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