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