View Javadoc
1   package org.apache.maven.plugins.javadoc.resolver;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static org.codehaus.plexus.util.IOUtil.close;
23  
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.LinkedHashSet;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  
35  import org.apache.maven.artifact.Artifact;
36  import org.apache.maven.artifact.DefaultArtifact;
37  import org.apache.maven.artifact.factory.ArtifactFactory;
38  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
39  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
40  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
41  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
42  import org.apache.maven.plugins.javadoc.options.JavadocOptions;
43  import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Reader;
44  import org.apache.maven.plugins.javadoc.AbstractJavadocMojo;
45  import org.apache.maven.plugins.javadoc.JavadocUtil;
46  import org.apache.maven.plugins.javadoc.ResourcesBundleMojo;
47  import org.apache.maven.project.MavenProject;
48  import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer;
49  import org.apache.maven.shared.artifact.resolve.ArtifactResolver;
50  import org.apache.maven.shared.artifact.resolve.ArtifactResolverException;
51  import org.apache.maven.shared.dependencies.resolve.DependencyResolver;
52  import org.codehaus.plexus.archiver.ArchiverException;
53  import org.codehaus.plexus.archiver.UnArchiver;
54  import org.codehaus.plexus.archiver.manager.ArchiverManager;
55  import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
56  import org.codehaus.plexus.component.annotations.Component;
57  import org.codehaus.plexus.component.annotations.Requirement;
58  import org.codehaus.plexus.logging.AbstractLogEnabled;
59  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
60  
61  /**
62   * 
63   */
64  @Component( role = ResourceResolver.class )
65  public final class ResourceResolver extends AbstractLogEnabled
66  {
67      @Requirement
68      private ArtifactFactory artifactFactory;
69      
70      @Requirement
71      private ArtifactResolver resolver;
72      
73      @Requirement
74      private DependencyResolver dependencyResolver;
75  
76      @Requirement
77      private ArtifactMetadataSource artifactMetadataSource;
78      
79      @Requirement
80      private ArchiverManager archiverManager;
81  
82      /**
83       * The classifier for sources.
84       */
85      public static final String SOURCES_CLASSIFIER = "sources";
86  
87      /**
88       * The classifier for test sources 
89       */
90      public static final String TEST_SOURCES_CLASSIFIER = "test-sources";
91  
92      private static final List<String> SOURCE_VALID_CLASSIFIERS = Arrays.asList( SOURCES_CLASSIFIER,
93                                                                                  TEST_SOURCES_CLASSIFIER );
94  
95      private static final List<String> RESOURCE_VALID_CLASSIFIERS =
96          Arrays.asList( AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER,
97                         AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER );
98  
99      /**
100      * @param config {@link SourceResolverConfig}
101      * @return list of {@link JavadocBundle}.
102      * @throws IOException {@link IOException}
103      */
104     public List<JavadocBundle> resolveDependencyJavadocBundles( final SourceResolverConfig config )
105         throws IOException
106     {
107         final List<JavadocBundle> bundles = new ArrayList<JavadocBundle>();
108 
109         final Map<String, MavenProject> projectMap = new HashMap<String, MavenProject>();
110         if ( config.reactorProjects() != null )
111         {
112             for ( final MavenProject p : config.reactorProjects() )
113             {
114                 projectMap.put( key( p.getGroupId(), p.getArtifactId() ), p );
115             }
116         }
117 
118         final List<Artifact> artifacts = config.project().getTestArtifacts();
119 
120         final List<Artifact> forResourceResolution = new ArrayList<Artifact>( artifacts.size() );
121         for ( final Artifact artifact : artifacts )
122         {
123             final String key = key( artifact.getGroupId(), artifact.getArtifactId() );
124             final MavenProject p = projectMap.get( key );
125             if ( p != null )
126             {
127                 bundles.addAll( resolveBundleFromProject( config, p, artifact ) );
128             }
129             else
130             {
131                 forResourceResolution.add( artifact );
132             }
133         }
134 
135         bundles.addAll( resolveBundlesFromArtifacts( config, forResourceResolution ) );
136 
137         return bundles;
138     }
139 
140     /**
141      * @param config {@link SourceResolverConfig}
142      * @return The list of resolved dependencies.
143      * @throws ArtifactResolutionException {@link ArtifactResolutionException}
144      * @throws ArtifactNotFoundException {@link ArtifactNotFoundException}
145      */
146     public List<String> resolveDependencySourcePaths( final SourceResolverConfig config )
147         throws ArtifactResolutionException, ArtifactNotFoundException
148     {
149         final List<String> dirs = new ArrayList<String>();
150 
151         final Map<String, MavenProject> projectMap = new HashMap<String, MavenProject>();
152         if ( config.reactorProjects() != null )
153         {
154             for ( final MavenProject p : config.reactorProjects() )
155             {
156                 projectMap.put( key( p.getGroupId(), p.getArtifactId() ), p );
157             }
158         }
159 
160         final List<Artifact> artifacts = config.project().getTestArtifacts();
161 
162         final List<Artifact> forResourceResolution = new ArrayList<Artifact>( artifacts.size() );
163         for ( final Artifact artifact : artifacts )
164         {
165             final String key = key( artifact.getGroupId(), artifact.getArtifactId() );
166             final MavenProject p = projectMap.get( key );
167             if ( p != null )
168             {
169                 dirs.addAll( resolveFromProject( config, p, artifact ) );
170             }
171             else
172             {
173                 forResourceResolution.add( artifact );
174             }
175         }
176 
177         dirs.addAll( resolveFromArtifacts( config, forResourceResolution ) );
178 
179         return dirs;
180     }
181 
182     private static List<JavadocBundle> resolveBundleFromProject( SourceResolverConfig config, MavenProject project,
183                                                            Artifact artifact ) throws IOException
184     {
185         List<JavadocBundle> bundles = new ArrayList<JavadocBundle>();
186         
187         List<String> classifiers = new ArrayList<String>();
188         if ( config.includeCompileSources() )
189         {
190             classifiers.add( AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER );
191         }
192         
193         if ( config.includeTestSources() )
194         {
195             classifiers.add( AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER );
196         }
197         
198         for ( String classifier : classifiers )
199         {
200             File optionsFile =
201                 new File( project.getBuild().getDirectory(), "javadoc-bundle-options/javadoc-options-" + classifier
202                     + ".xml" );
203             if ( !optionsFile.exists() )
204             {
205                 continue;
206             }
207             
208             FileInputStream stream = null;
209             try
210             {
211                 stream = new FileInputStream( optionsFile );
212                 JavadocOptions options = new JavadocOptionsXpp3Reader().read( stream );
213                 stream.close();
214                 stream = null;
215                 bundles.add( new JavadocBundle( options, new File( project.getBasedir(),
216                                                                    options.getJavadocResourcesDirectory() ) ) );
217             }
218             catch ( XmlPullParserException e )
219             {
220                 IOException error =
221                     new IOException( "Failed to read javadoc options from: " + optionsFile + "\nReason: "
222                         + e.getMessage() );
223                 error.initCause( e );
224                 
225                 throw error;
226             }
227             finally
228             {
229                 close( stream );
230             }
231         }
232 
233         return bundles;
234     }
235 
236     private List<JavadocBundle> resolveBundlesFromArtifacts( final SourceResolverConfig config,
237                                                                     final List<Artifact> artifacts )
238         throws IOException
239     {
240         final List<Artifact> toResolve = new ArrayList<Artifact>( artifacts.size() );
241 
242         for ( final Artifact artifact : artifacts )
243         {
244             if ( config.filter() != null
245                 && !new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) )
246             {
247                 continue;
248             }
249 
250             if ( config.includeCompileSources() )
251             {
252                 toResolve.add( createResourceArtifact( artifact,
253                                                        AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER,
254                                                        config ) );
255             }
256 
257             if ( config.includeTestSources() )
258             {
259                 toResolve.add( createResourceArtifact( artifact,
260                                                        AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER,
261                                                        config ) );
262             }
263         }
264 
265         List<String> dirs = null;
266         try
267         {
268             dirs = resolveAndUnpack( toResolve, config, RESOURCE_VALID_CLASSIFIERS, false );
269         }
270         catch ( ArtifactResolutionException e )
271         {
272             if ( getLogger().isDebugEnabled() )
273             {
274                 getLogger().debug( e.getMessage(), e );
275             }
276         }
277         catch ( ArtifactNotFoundException e )
278         {
279             if ( getLogger().isDebugEnabled() )
280             {
281                 getLogger().debug( e.getMessage(), e );
282             }
283         }
284         
285         List<JavadocBundle> result = new ArrayList<JavadocBundle>();
286 
287         if ( dirs != null )
288         {
289             for ( String d : dirs )
290             {
291                 File dir = new File( d );
292                 File resources = new File( dir, ResourcesBundleMojo.RESOURCES_DIR_PATH );
293                 JavadocOptions options = null;
294 
295                 File javadocOptions = new File( dir, ResourcesBundleMojo.BUNDLE_OPTIONS_PATH );
296                 if ( javadocOptions.exists() )
297                 {
298                     FileInputStream reader = null;
299                     try
300                     {
301                         reader = new FileInputStream( javadocOptions );
302                         options = new JavadocOptionsXpp3Reader().read( reader );
303                     }
304                     catch ( XmlPullParserException e )
305                     {
306                         IOException error = new IOException( "Failed to parse javadoc options: " + e.getMessage() );
307                         error.initCause( e );
308                         
309                         throw error;
310                     }
311                     finally
312                     {
313                         close( reader );
314                     }
315                 }
316                 
317                 result.add( new JavadocBundle( options, resources ) );
318             }
319         }
320         
321         return result;
322     }
323 
324     private List<String> resolveFromArtifacts( final SourceResolverConfig config,
325                                                       final List<Artifact> artifacts )
326         throws ArtifactResolutionException, ArtifactNotFoundException
327     {
328         final List<Artifact> toResolve = new ArrayList<Artifact>( artifacts.size() );
329 
330         for ( final Artifact artifact : artifacts )
331         {
332             if ( config.filter() != null
333                 && !new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) )
334             {
335                 continue;
336             }
337 
338             if ( config.includeCompileSources() )
339             {
340                 toResolve.add( createResourceArtifact( artifact, SOURCES_CLASSIFIER, config ) );
341             }
342 
343             if ( config.includeTestSources() )
344             {
345                 toResolve.add( createResourceArtifact( artifact, TEST_SOURCES_CLASSIFIER, config ) );
346             }
347         }
348 
349         return resolveAndUnpack( toResolve, config, SOURCE_VALID_CLASSIFIERS, true );
350     }
351 
352     private Artifact createResourceArtifact( final Artifact artifact, final String classifier,
353                                                     final SourceResolverConfig config )
354     {
355         final DefaultArtifact a =
356             (DefaultArtifact) artifactFactory.createArtifactWithClassifier( artifact.getGroupId(),
357                                                                                      artifact.getArtifactId(),
358                                                                                      artifact.getVersion(), "jar",
359                                                                                      classifier );
360 
361         a.setRepository( artifact.getRepository() );
362 
363         return a;
364     }
365 
366     private List<String> resolveAndUnpack( final List<Artifact> artifacts, final SourceResolverConfig config,
367                                                   final List<String> validClassifiers, final boolean propagateErrors )
368         throws ArtifactResolutionException, ArtifactNotFoundException
369     {
370         // NOTE: Since these are '-sources' and '-test-sources' artifacts, they won't actually 
371         // resolve transitively...this is just used to aggregate resolution failures into a single 
372         // exception.
373         final Set<Artifact> artifactSet = new LinkedHashSet<Artifact>( artifacts );
374 
375         final ArtifactFilter filter;
376         if ( config.filter() != null )
377         {
378             filter = new ArtifactIncludeFilterTransformer().transform( config.filter() );
379         }
380         else
381         {
382             filter = null;
383         }
384         
385         final List<String> result = new ArrayList<String>( artifacts.size() );
386         for ( final Artifact a : artifactSet )
387         {
388             if ( !validClassifiers.contains( a.getClassifier() ) || ( filter != null && !filter.include( a ) ) )
389             {
390                 continue;
391             }
392             
393             Artifact resolvedArtifact;
394             try
395             {
396                 resolvedArtifact = resolver.resolveArtifact( config.getBuildingRequest(), a ).getArtifact();
397             }
398             catch ( ArtifactResolverException e1 )
399             {
400                 continue;
401             }
402 
403             final File d =
404                 new File( config.outputBasedir(), a.getArtifactId() + "-" + a.getVersion() + "-" + a.getClassifier() );
405 
406             if ( !d.exists() )
407             {
408                 d.mkdirs();
409             }
410 
411             try
412             {
413                 final UnArchiver unArchiver = archiverManager.getUnArchiver( a.getType() );
414 
415                 unArchiver.setDestDirectory( d );
416                 unArchiver.setSourceFile( resolvedArtifact.getFile() );
417 
418                 unArchiver.extract();
419 
420                 result.add( d.getAbsolutePath() );
421             }
422             catch ( final NoSuchArchiverException e )
423             {
424                 if ( propagateErrors )
425                 {
426                     throw new ArtifactResolutionException( "Failed to retrieve valid un-archiver component: "
427                         + a.getType(), a, e );
428                 }
429             }
430             catch ( final ArchiverException e )
431             {
432                 if ( propagateErrors )
433                 {
434                     throw new ArtifactResolutionException( "Failed to unpack: " + a.getId(), a, e );
435                 }
436             }
437         }
438 
439         return result;
440     }
441 
442     private static List<String> resolveFromProject( final SourceResolverConfig config,
443                                                     final MavenProject reactorProject, final Artifact artifact )
444     {
445         final List<String> dirs = new ArrayList<String>();
446 
447         if ( config.filter() == null
448             || new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) )
449         {
450             if ( config.includeCompileSources() )
451             {
452                 final List<String> srcRoots = reactorProject.getCompileSourceRoots();
453                 for ( final String root : srcRoots )
454                 {
455                     dirs.add( root );
456                 }
457             }
458 
459             if ( config.includeTestSources() )
460             {
461                 final List<String> srcRoots = reactorProject.getTestCompileSourceRoots();
462                 for ( final String root : srcRoots )
463                 {
464                     dirs.add( root );
465                 }
466             }
467         }
468 
469         return JavadocUtil.pruneDirs( reactorProject, dirs );
470     }
471 
472     private static String key( final String gid, final String aid )
473     {
474         return gid + ":" + aid;
475     }
476 
477 }