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
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.nio.file.Path;
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.transfer.artifact.resolve.ArtifactResolver;
55 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
56 import org.apache.maven.shared.transfer.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<Path>> resolveDependencySourcePaths( final SourceResolverConfig config )
152 throws ArtifactResolutionException, ArtifactNotFoundException
153 {
154 final Map<String, Collection<Path>> 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, Path> 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
217 try ( FileInputStream stream = new FileInputStream( optionsFile ) )
218 {
219 JavadocOptions options = new JavadocOptionsXpp3Reader().read( stream );
220 bundles.add( new JavadocBundle( options, new File( project.getBasedir(),
221 options.getJavadocResourcesDirectory() ) ) );
222 }
223 catch ( XmlPullParserException e )
224 {
225 IOException error =
226 new IOException( "Failed to read javadoc options from: " + optionsFile + "\nReason: "
227 + e.getMessage(), e );
228 throw error;
229 }
230 }
231
232 return bundles;
233 }
234
235 private List<JavadocBundle> resolveBundlesFromArtifacts( final SourceResolverConfig config,
236 final List<Artifact> artifacts )
237 throws IOException
238 {
239 final List<Artifact> toResolve = new ArrayList<>( artifacts.size() );
240
241 for ( final Artifact artifact : artifacts )
242 {
243 if ( config.filter() != null
244 && !new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) )
245 {
246 continue;
247 }
248
249 if ( config.includeCompileSources() )
250 {
251 toResolve.add( createResourceArtifact( artifact,
252 AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER,
253 config ) );
254 }
255
256 if ( config.includeTestSources() )
257 {
258 toResolve.add( createResourceArtifact( artifact,
259 AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER,
260 config ) );
261 }
262 }
263
264 List<String> dirs = new ArrayList<>( toResolve.size() );
265 try
266 {
267 for ( Map.Entry<String, Path> entry : resolveAndUnpack( toResolve, config, RESOURCE_VALID_CLASSIFIERS,
268 false ) )
269 {
270 dirs.add( entry.getValue().toString() );
271 }
272 }
273 catch ( ArtifactResolutionException | ArtifactNotFoundException e )
274 {
275 if ( getLogger().isDebugEnabled() )
276 {
277 getLogger().debug( e.getMessage(), e );
278 }
279 }
280
281 List<JavadocBundle> result = new ArrayList<>();
282
283 for ( String d : dirs )
284 {
285 File dir = new File( d );
286 File resources = new File( dir, ResourcesBundleMojo.RESOURCES_DIR_PATH );
287 JavadocOptions options = null;
288
289 File javadocOptions = new File( dir, ResourcesBundleMojo.BUNDLE_OPTIONS_PATH );
290 if ( javadocOptions.exists() )
291 {
292 try ( FileInputStream reader = new FileInputStream( javadocOptions ) )
293 {
294 options = new JavadocOptionsXpp3Reader().read( reader );
295 }
296 catch ( XmlPullParserException e )
297 {
298 IOException error = new IOException( "Failed to parse javadoc options: " + e.getMessage(), e );
299 throw error;
300 }
301 }
302
303 result.add( new JavadocBundle( options, resources ) );
304 }
305
306 return result;
307 }
308
309 private Collection<Entry<String, Path>> resolveFromArtifacts( final SourceResolverConfig config,
310 final List<Artifact> artifacts )
311 throws ArtifactResolutionException, ArtifactNotFoundException
312 {
313 final List<Artifact> toResolve = new ArrayList<>( artifacts.size() );
314
315 for ( final Artifact artifact : artifacts )
316 {
317 if ( config.filter() != null
318 && !new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) )
319 {
320 continue;
321 }
322
323 if ( config.includeCompileSources() )
324 {
325 toResolve.add( createResourceArtifact( artifact, SOURCES_CLASSIFIER, config ) );
326 }
327
328 if ( config.includeTestSources() )
329 {
330 toResolve.add( createResourceArtifact( artifact, TEST_SOURCES_CLASSIFIER, config ) );
331 }
332 }
333
334 return resolveAndUnpack( toResolve, config, SOURCE_VALID_CLASSIFIERS, true );
335 }
336
337 private Artifact createResourceArtifact( final Artifact artifact, final String classifier,
338 final SourceResolverConfig config )
339 {
340 final DefaultArtifact a =
341 (DefaultArtifact) artifactFactory.createArtifactWithClassifier( artifact.getGroupId(),
342 artifact.getArtifactId(),
343 artifact.getVersion(), "jar",
344 classifier );
345
346 a.setRepository( artifact.getRepository() );
347
348 return a;
349 }
350
351
352
353
354
355
356
357
358
359
360
361 private Collection<Map.Entry<String, Path>> resolveAndUnpack( final List<Artifact> artifacts,
362 final SourceResolverConfig config,
363 final List<String> validClassifiers,
364 final boolean propagateErrors )
365 throws ArtifactResolutionException, ArtifactNotFoundException
366 {
367
368
369
370 final Set<Artifact> artifactSet = new LinkedHashSet<>( artifacts );
371
372 final ArtifactFilter filter;
373 if ( config.filter() != null )
374 {
375 filter = new ArtifactIncludeFilterTransformer().transform( config.filter() );
376 }
377 else
378 {
379 filter = null;
380 }
381
382 final List<Map.Entry<String, Path>> result = new ArrayList<>( artifacts.size() );
383 for ( final Artifact a : artifactSet )
384 {
385 if ( !validClassifiers.contains( a.getClassifier() ) || ( filter != null && !filter.include( a ) ) )
386 {
387 continue;
388 }
389
390 Artifact resolvedArtifact;
391 try
392 {
393 resolvedArtifact = resolver.resolveArtifact( config.getBuildingRequest(), a ).getArtifact();
394 }
395 catch ( ArtifactResolverException e1 )
396 {
397 continue;
398 }
399
400 final File d =
401 new File( config.outputBasedir(), a.getArtifactId() + "-" + a.getVersion() + "-" + a.getClassifier() );
402
403 if ( !d.exists() )
404 {
405 d.mkdirs();
406 }
407
408 try
409 {
410 final UnArchiver unArchiver = archiverManager.getUnArchiver( a.getType() );
411
412 unArchiver.setDestDirectory( d );
413 unArchiver.setSourceFile( resolvedArtifact.getFile() );
414
415 unArchiver.extract();
416
417 result.add( new AbstractMap.SimpleEntry<>( key( a.getGroupId(), a.getArtifactId() ),
418 d.toPath().toAbsolutePath() ) );
419 }
420 catch ( final NoSuchArchiverException e )
421 {
422 if ( propagateErrors )
423 {
424 throw new ArtifactResolutionException( "Failed to retrieve valid un-archiver component: "
425 + a.getType(), a, e );
426 }
427 }
428 catch ( final ArchiverException e )
429 {
430 if ( propagateErrors )
431 {
432 throw new ArtifactResolutionException( "Failed to unpack: " + a.getId(), a, e );
433 }
434 }
435 }
436
437 return result;
438 }
439
440 private static Collection<Path> resolveFromProject( final SourceResolverConfig config,
441 final MavenProject reactorProject, final Artifact artifact )
442 {
443 final List<String> dirs = new ArrayList<>();
444
445 if ( config.filter() == null
446 || new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) )
447 {
448 if ( config.includeCompileSources() )
449 {
450 final List<String> srcRoots = reactorProject.getCompileSourceRoots();
451 dirs.addAll( srcRoots );
452 }
453
454 if ( config.includeTestSources() )
455 {
456 final List<String> srcRoots = reactorProject.getTestCompileSourceRoots();
457 dirs.addAll( srcRoots );
458 }
459 }
460
461 return JavadocUtil.pruneDirs( reactorProject, dirs );
462 }
463
464 private static String key( final String gid, final String aid )
465 {
466 return gid + ":" + aid;
467 }
468
469 }