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