1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugins.javadoc.resolver;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.IOException;
28 import java.nio.file.Path;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import org.apache.maven.RepositoryUtils;
40 import org.apache.maven.artifact.Artifact;
41 import org.apache.maven.artifact.ArtifactUtils;
42 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
43 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
44 import org.apache.maven.plugins.javadoc.AbstractJavadocMojo;
45 import org.apache.maven.plugins.javadoc.JavadocModule;
46 import org.apache.maven.plugins.javadoc.JavadocUtil;
47 import org.apache.maven.plugins.javadoc.ResourcesBundleMojo;
48 import org.apache.maven.plugins.javadoc.options.JavadocOptions;
49 import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Reader;
50 import org.apache.maven.project.MavenProject;
51 import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer;
52 import org.apache.maven.shared.artifact.filter.resolve.transform.EclipseAetherFilterTransformer;
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.util.xml.pull.XmlPullParserException;
58 import org.eclipse.aether.RepositorySystem;
59 import org.eclipse.aether.RepositorySystemSession;
60 import org.eclipse.aether.graph.DefaultDependencyNode;
61 import org.eclipse.aether.graph.DependencyFilter;
62 import org.eclipse.aether.resolution.ArtifactRequest;
63 import org.eclipse.aether.resolution.ArtifactResult;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67
68
69
70 @Named
71 @Singleton
72 public final class ResourceResolver {
73 private static final Logger LOGGER = LoggerFactory.getLogger(ResourceResolver.class);
74
75 @Inject
76 private RepositorySystem repoSystem;
77
78 @Inject
79 private ArchiverManager archiverManager;
80
81
82
83
84 public static final String SOURCES_CLASSIFIER = "sources";
85
86
87
88
89 public static final String TEST_SOURCES_CLASSIFIER = "test-sources";
90
91 private static final List<String> SOURCE_VALID_CLASSIFIERS =
92 Arrays.asList(SOURCES_CLASSIFIER, TEST_SOURCES_CLASSIFIER);
93
94 private static final List<String> RESOURCE_VALID_CLASSIFIERS = Arrays.asList(
95 AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER,
96 AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER);
97
98
99
100
101
102
103 public List<JavadocBundle> resolveDependencyJavadocBundles(final SourceResolverConfig config) throws IOException {
104 final List<JavadocBundle> bundles = new ArrayList<>();
105
106 final Map<String, MavenProject> projectMap = new HashMap<>();
107 if (config.reactorProjects() != null) {
108 for (final MavenProject p : config.reactorProjects()) {
109 projectMap.put(ArtifactUtils.key(p.getGroupId(), p.getArtifactId(), p.getVersion()), p);
110 }
111 }
112
113 final List<Artifact> artifacts = config.project().getTestArtifacts();
114
115 final List<Artifact> forResourceResolution = new ArrayList<>(artifacts.size());
116 for (final Artifact artifact : artifacts) {
117 final MavenProject p = projectMap.get(ArtifactUtils.key(artifact));
118 if (p != null) {
119 bundles.addAll(resolveBundleFromProject(config, p, artifact));
120 } else {
121 forResourceResolution.add(artifact);
122 }
123 }
124
125 bundles.addAll(resolveBundlesFromArtifacts(config, forResourceResolution));
126
127 return bundles;
128 }
129
130
131
132
133
134
135
136 public Collection<JavadocModule> resolveDependencySourcePaths(final SourceResolverConfig config)
137 throws ArtifactResolutionException, ArtifactNotFoundException {
138 final Collection<JavadocModule> mappedDirs = new ArrayList<>();
139
140 final Map<String, MavenProject> projectMap = new HashMap<>();
141 if (config.reactorProjects() != null) {
142 for (final MavenProject p : config.reactorProjects()) {
143 projectMap.put(ArtifactUtils.key(p.getGroupId(), p.getArtifactId(), p.getVersion()), p);
144 }
145 }
146
147 final List<Artifact> artifacts = config.project().getTestArtifacts();
148
149 for (final Artifact artifact : artifacts) {
150 final String key = ArtifactUtils.key(artifact);
151 final MavenProject p = projectMap.get(key);
152 if (p != null) {
153 mappedDirs.add(new JavadocModule(key, artifact.getFile(), resolveFromProject(config, p, artifact)));
154 } else {
155 JavadocModule m = resolveFromArtifact(config, artifact);
156 if (m != null) {
157 mappedDirs.add(m);
158 }
159 }
160 }
161
162 return mappedDirs;
163 }
164
165 private static List<JavadocBundle> resolveBundleFromProject(
166 SourceResolverConfig config, MavenProject project, Artifact artifact) throws IOException {
167 List<JavadocBundle> bundles = new ArrayList<>();
168
169 List<String> classifiers = new ArrayList<>();
170 if (config.includeCompileSources()) {
171 classifiers.add(AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER);
172 }
173
174 if (config.includeTestSources()) {
175 classifiers.add(AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER);
176 }
177
178 for (String classifier : classifiers) {
179 File optionsFile = new File(
180 project.getBuild().getDirectory(), "javadoc-bundle-options/javadoc-options-" + classifier + ".xml");
181 if (!optionsFile.exists()) {
182 continue;
183 }
184
185 try (FileInputStream stream = new FileInputStream(optionsFile)) {
186 JavadocOptions options = new JavadocOptionsXpp3Reader().read(stream);
187 bundles.add(new JavadocBundle(
188 options, new File(project.getBasedir(), options.getJavadocResourcesDirectory())));
189 } catch (XmlPullParserException e) {
190 IOException error = new IOException(
191 "Failed to read javadoc options from: " + optionsFile + "\nReason: " + e.getMessage(), e);
192 throw error;
193 }
194 }
195
196 return bundles;
197 }
198
199 private List<JavadocBundle> resolveBundlesFromArtifacts(
200 final SourceResolverConfig config, final List<Artifact> artifacts) throws IOException {
201 final List<org.eclipse.aether.artifact.Artifact> toResolve = new ArrayList<>(artifacts.size());
202
203 for (final Artifact artifact : artifacts) {
204 if (config.filter() != null
205 && !new ArtifactIncludeFilterTransformer()
206 .transform(config.filter())
207 .include(artifact)) {
208 continue;
209 }
210
211 if (config.includeCompileSources()) {
212 toResolve.add(createResourceArtifact(
213 artifact, AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER, config));
214 }
215
216 if (config.includeTestSources()) {
217 toResolve.add(createResourceArtifact(
218 artifact, AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER, config));
219 }
220 }
221
222 Collection<Path> dirs = new ArrayList<>(toResolve.size());
223 try {
224 dirs = resolveAndUnpack(toResolve, config, RESOURCE_VALID_CLASSIFIERS, false);
225 } catch (ArtifactResolutionException | ArtifactNotFoundException e) {
226 if (LOGGER.isDebugEnabled()) {
227 LOGGER.debug(e.getMessage(), e);
228 }
229 }
230
231 List<JavadocBundle> result = new ArrayList<>();
232
233 for (Path d : dirs) {
234 File dir = d.toFile();
235 File resources = new File(dir, ResourcesBundleMojo.RESOURCES_DIR_PATH);
236 JavadocOptions options = null;
237
238 File javadocOptions = new File(dir, ResourcesBundleMojo.BUNDLE_OPTIONS_PATH);
239 if (javadocOptions.exists()) {
240 try (FileInputStream reader = new FileInputStream(javadocOptions)) {
241 options = new JavadocOptionsXpp3Reader().read(reader);
242 } catch (XmlPullParserException e) {
243 IOException error = new IOException("Failed to parse javadoc options: " + e.getMessage(), e);
244 throw error;
245 }
246 }
247
248 result.add(new JavadocBundle(options, resources));
249 }
250
251 return result;
252 }
253
254 private JavadocModule resolveFromArtifact(final SourceResolverConfig config, final Artifact artifact)
255 throws ArtifactResolutionException, ArtifactNotFoundException {
256 final List<org.eclipse.aether.artifact.Artifact> toResolve = new ArrayList<>(2);
257
258 if (config.filter() != null
259 && !new ArtifactIncludeFilterTransformer()
260 .transform(config.filter())
261 .include(artifact)) {
262 return null;
263 }
264
265 if (config.includeCompileSources()) {
266 toResolve.add(createResourceArtifact(artifact, SOURCES_CLASSIFIER, config));
267 }
268
269 if (config.includeTestSources()) {
270 toResolve.add(createResourceArtifact(artifact, TEST_SOURCES_CLASSIFIER, config));
271 }
272
273 Collection<Path> sourcePaths = resolveAndUnpack(toResolve, config, SOURCE_VALID_CLASSIFIERS, true);
274
275 return new JavadocModule(ArtifactUtils.key(artifact), artifact.getFile(), sourcePaths);
276 }
277
278 private org.eclipse.aether.artifact.Artifact createResourceArtifact(
279 final Artifact artifact, final String classifier, final SourceResolverConfig config) {
280 return new org.eclipse.aether.artifact.DefaultArtifact(
281 artifact.getGroupId(), artifact.getArtifactId(), classifier, "jar", artifact.getVersion());
282 }
283
284
285
286
287
288
289
290
291
292
293
294 private Collection<Path> resolveAndUnpack(
295 final List<org.eclipse.aether.artifact.Artifact> artifacts,
296 final SourceResolverConfig config,
297 final List<String> validClassifiers,
298 final boolean propagateErrors)
299 throws ArtifactResolutionException, ArtifactNotFoundException {
300
301
302
303 final Set<org.eclipse.aether.artifact.Artifact> artifactSet = new LinkedHashSet<>(artifacts);
304
305 final DependencyFilter filter;
306 if (config.filter() != null) {
307 filter = new EclipseAetherFilterTransformer().transform(config.filter());
308 } else {
309 filter = null;
310 }
311
312 final List<Path> result = new ArrayList<>(artifacts.size());
313 for (final org.eclipse.aether.artifact.Artifact a : artifactSet) {
314 if (!validClassifiers.contains(a.getClassifier())
315 || (filter != null && !filter.accept(new DefaultDependencyNode(a), Collections.emptyList()))) {
316 continue;
317 }
318
319 Artifact resolvedArtifact;
320 ArtifactRequest req = new ArtifactRequest(a, config.project().getRemoteProjectRepositories(), null);
321 try {
322 RepositorySystemSession repoSession =
323 config.getBuildingRequest().getRepositorySession();
324 ArtifactResult resolutionResult = repoSystem.resolveArtifact(repoSession, req);
325 resolvedArtifact = RepositoryUtils.toArtifact(resolutionResult.getArtifact());
326 } catch (org.eclipse.aether.resolution.ArtifactResolutionException e) {
327 continue;
328 }
329 final File d = new File(
330 config.outputBasedir(), a.getArtifactId() + "-" + a.getVersion() + "-" + a.getClassifier());
331
332 if (!d.exists()) {
333 d.mkdirs();
334 }
335
336 try {
337 final UnArchiver unArchiver = archiverManager.getUnArchiver(a.getExtension());
338
339 unArchiver.setDestDirectory(d);
340 unArchiver.setSourceFile(resolvedArtifact.getFile());
341
342 unArchiver.extract();
343
344 result.add(d.toPath().toAbsolutePath());
345 } catch (final NoSuchArchiverException e) {
346 if (propagateErrors) {
347 throw new ArtifactResolutionException(
348 "Failed to retrieve valid un-archiver component: " + a.getExtension(),
349 RepositoryUtils.toArtifact(a),
350 e);
351 }
352 } catch (final ArchiverException e) {
353 if (propagateErrors) {
354 throw new ArtifactResolutionException("Failed to unpack: " + a, RepositoryUtils.toArtifact(a), e);
355 }
356 }
357 }
358
359 return result;
360 }
361
362 private static Collection<Path> resolveFromProject(
363 final SourceResolverConfig config, final MavenProject reactorProject, final Artifact artifact) {
364 final List<String> dirs = new ArrayList<>();
365
366 if (config.filter() == null
367 || new ArtifactIncludeFilterTransformer()
368 .transform(config.filter())
369 .include(artifact)) {
370 if (config.includeCompileSources()) {
371 final List<String> srcRoots = reactorProject.getCompileSourceRoots();
372 dirs.addAll(srcRoots);
373 }
374
375 if (config.includeTestSources()) {
376 final List<String> srcRoots = reactorProject.getTestCompileSourceRoots();
377 dirs.addAll(srcRoots);
378 }
379 }
380
381 return JavadocUtil.pruneDirs(reactorProject, dirs);
382 }
383 }