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