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