package org.apache.maven.plugins.javadoc.resolver; | |
/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
*/ | |
import static org.codehaus.plexus.util.IOUtil.close; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.IOException; | |
import java.util.AbstractMap; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.HashMap; | |
import java.util.LinkedHashMap; | |
import java.util.LinkedHashSet; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.Set; | |
import org.apache.maven.artifact.Artifact; | |
import org.apache.maven.artifact.DefaultArtifact; | |
import org.apache.maven.artifact.factory.ArtifactFactory; | |
import org.apache.maven.artifact.metadata.ArtifactMetadataSource; | |
import org.apache.maven.artifact.resolver.ArtifactNotFoundException; | |
import org.apache.maven.artifact.resolver.ArtifactResolutionException; | |
import org.apache.maven.artifact.resolver.filter.ArtifactFilter; | |
import org.apache.maven.plugins.javadoc.AbstractJavadocMojo; | |
import org.apache.maven.plugins.javadoc.JavadocUtil; | |
import org.apache.maven.plugins.javadoc.ResourcesBundleMojo; | |
import org.apache.maven.plugins.javadoc.options.JavadocOptions; | |
import org.apache.maven.plugins.javadoc.options.io.xpp3.JavadocOptionsXpp3Reader; | |
import org.apache.maven.project.MavenProject; | |
import org.apache.maven.shared.artifact.filter.resolve.transform.ArtifactIncludeFilterTransformer; | |
import org.apache.maven.shared.artifact.resolve.ArtifactResolver; | |
import org.apache.maven.shared.artifact.resolve.ArtifactResolverException; | |
import org.apache.maven.shared.dependencies.resolve.DependencyResolver; | |
import org.codehaus.plexus.archiver.ArchiverException; | |
import org.codehaus.plexus.archiver.UnArchiver; | |
import org.codehaus.plexus.archiver.manager.ArchiverManager; | |
import org.codehaus.plexus.archiver.manager.NoSuchArchiverException; | |
import org.codehaus.plexus.component.annotations.Component; | |
import org.codehaus.plexus.component.annotations.Requirement; | |
import org.codehaus.plexus.logging.AbstractLogEnabled; | |
import org.codehaus.plexus.util.xml.pull.XmlPullParserException; | |
/** | |
* | |
*/ | |
@Component( role = ResourceResolver.class ) | |
public final class ResourceResolver extends AbstractLogEnabled | |
{ | |
@Requirement | |
private ArtifactFactory artifactFactory; | |
@Requirement | |
private ArtifactResolver resolver; | |
@Requirement | |
private DependencyResolver dependencyResolver; | |
@Requirement | |
private ArtifactMetadataSource artifactMetadataSource; | |
@Requirement | |
private ArchiverManager archiverManager; | |
/** | |
* The classifier for sources. | |
*/ | |
public static final String SOURCES_CLASSIFIER = "sources"; | |
/** | |
* The classifier for test sources | |
*/ | |
public static final String TEST_SOURCES_CLASSIFIER = "test-sources"; | |
private static final List<String> SOURCE_VALID_CLASSIFIERS = Arrays.asList( SOURCES_CLASSIFIER, | |
TEST_SOURCES_CLASSIFIER ); | |
private static final List<String> RESOURCE_VALID_CLASSIFIERS = | |
Arrays.asList( AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER, | |
AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER ); | |
/** | |
* @param config {@link SourceResolverConfig} | |
* @return list of {@link JavadocBundle}. | |
* @throws IOException {@link IOException} | |
*/ | |
public List<JavadocBundle> resolveDependencyJavadocBundles( final SourceResolverConfig config ) | |
throws IOException | |
{ | |
final List<JavadocBundle> bundles = new ArrayList<>(); | |
final Map<String, MavenProject> projectMap = new HashMap<>(); | |
if ( config.reactorProjects() != null ) | |
{ | |
for ( final MavenProject p : config.reactorProjects() ) | |
{ | |
projectMap.put( key( p.getGroupId(), p.getArtifactId() ), p ); | |
} | |
} | |
final List<Artifact> artifacts = config.project().getTestArtifacts(); | |
final List<Artifact> forResourceResolution = new ArrayList<>( artifacts.size() ); | |
for ( final Artifact artifact : artifacts ) | |
{ | |
final String key = key( artifact.getGroupId(), artifact.getArtifactId() ); | |
final MavenProject p = projectMap.get( key ); | |
if ( p != null ) | |
{ | |
bundles.addAll( resolveBundleFromProject( config, p, artifact ) ); | |
} | |
else | |
{ | |
forResourceResolution.add( artifact ); | |
} | |
} | |
bundles.addAll( resolveBundlesFromArtifacts( config, forResourceResolution ) ); | |
return bundles; | |
} | |
/** | |
* @param config {@link SourceResolverConfig} | |
* @return The list of resolved dependencies. | |
* @throws ArtifactResolutionException {@link ArtifactResolutionException} | |
* @throws ArtifactNotFoundException {@link ArtifactNotFoundException} | |
*/ | |
public Map<String, Collection<String>> resolveDependencySourcePaths( final SourceResolverConfig config ) | |
throws ArtifactResolutionException, ArtifactNotFoundException | |
{ | |
final Map<String, Collection<String>> mappedDirs = new LinkedHashMap<>(); | |
final Map<String, MavenProject> projectMap = new HashMap<>(); | |
if ( config.reactorProjects() != null ) | |
{ | |
for ( final MavenProject p : config.reactorProjects() ) | |
{ | |
projectMap.put( key( p.getGroupId(), p.getArtifactId() ), p ); | |
} | |
} | |
final List<Artifact> artifacts = config.project().getTestArtifacts(); | |
final List<Artifact> forResourceResolution = new ArrayList<>( artifacts.size() ); | |
for ( final Artifact artifact : artifacts ) | |
{ | |
final String key = key( artifact.getGroupId(), artifact.getArtifactId() ); | |
final MavenProject p = projectMap.get( key ); | |
if ( p != null ) | |
{ | |
mappedDirs.put( key, resolveFromProject( config, p, artifact ) ); | |
} | |
else | |
{ | |
forResourceResolution.add( artifact ); | |
} | |
} | |
for ( Map.Entry<String, String> entry : resolveFromArtifacts( config, forResourceResolution ) ) | |
{ | |
mappedDirs.put( entry.getKey(), Collections.singletonList( entry.getValue() ) ); | |
} | |
return mappedDirs; | |
} | |
private static List<JavadocBundle> resolveBundleFromProject( SourceResolverConfig config, MavenProject project, | |
Artifact artifact ) throws IOException | |
{ | |
List<JavadocBundle> bundles = new ArrayList<>(); | |
List<String> classifiers = new ArrayList<>(); | |
if ( config.includeCompileSources() ) | |
{ | |
classifiers.add( AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER ); | |
} | |
if ( config.includeTestSources() ) | |
{ | |
classifiers.add( AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER ); | |
} | |
for ( String classifier : classifiers ) | |
{ | |
File optionsFile = | |
new File( project.getBuild().getDirectory(), "javadoc-bundle-options/javadoc-options-" + classifier | |
+ ".xml" ); | |
if ( !optionsFile.exists() ) | |
{ | |
continue; | |
} | |
FileInputStream stream = null; | |
try | |
{ | |
stream = new FileInputStream( optionsFile ); | |
JavadocOptions options = new JavadocOptionsXpp3Reader().read( stream ); | |
stream.close(); | |
stream = null; | |
bundles.add( new JavadocBundle( options, new File( project.getBasedir(), | |
options.getJavadocResourcesDirectory() ) ) ); | |
} | |
catch ( XmlPullParserException e ) | |
{ | |
IOException error = | |
new IOException( "Failed to read javadoc options from: " + optionsFile + "\nReason: " | |
+ e.getMessage() ); | |
error.initCause( e ); | |
throw error; | |
} | |
finally | |
{ | |
close( stream ); | |
} | |
} | |
return bundles; | |
} | |
private List<JavadocBundle> resolveBundlesFromArtifacts( final SourceResolverConfig config, | |
final List<Artifact> artifacts ) | |
throws IOException | |
{ | |
final List<Artifact> toResolve = new ArrayList<>( artifacts.size() ); | |
for ( final Artifact artifact : artifacts ) | |
{ | |
if ( config.filter() != null | |
&& !new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) ) | |
{ | |
continue; | |
} | |
if ( config.includeCompileSources() ) | |
{ | |
toResolve.add( createResourceArtifact( artifact, | |
AbstractJavadocMojo.JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER, | |
config ) ); | |
} | |
if ( config.includeTestSources() ) | |
{ | |
toResolve.add( createResourceArtifact( artifact, | |
AbstractJavadocMojo.TEST_JAVADOC_RESOURCES_ATTACHMENT_CLASSIFIER, | |
config ) ); | |
} | |
} | |
List<String> dirs = new ArrayList<>( toResolve.size() ); | |
try | |
{ | |
for ( Map.Entry<String, String> entry : resolveAndUnpack( toResolve, config, RESOURCE_VALID_CLASSIFIERS, | |
false ) ) | |
{ | |
dirs.add( entry.getValue() ); | |
} | |
} | |
catch ( ArtifactResolutionException e ) | |
{ | |
if ( getLogger().isDebugEnabled() ) | |
{ | |
getLogger().debug( e.getMessage(), e ); | |
} | |
} | |
catch ( ArtifactNotFoundException e ) | |
{ | |
if ( getLogger().isDebugEnabled() ) | |
{ | |
getLogger().debug( e.getMessage(), e ); | |
} | |
} | |
List<JavadocBundle> result = new ArrayList<>(); | |
for ( String d : dirs ) | |
{ | |
File dir = new File( d ); | |
File resources = new File( dir, ResourcesBundleMojo.RESOURCES_DIR_PATH ); | |
JavadocOptions options = null; | |
File javadocOptions = new File( dir, ResourcesBundleMojo.BUNDLE_OPTIONS_PATH ); | |
if ( javadocOptions.exists() ) | |
{ | |
try ( FileInputStream reader = new FileInputStream( javadocOptions ) ) | |
{ | |
options = new JavadocOptionsXpp3Reader().read( reader ); | |
} | |
catch ( XmlPullParserException e ) | |
{ | |
IOException error = new IOException( "Failed to parse javadoc options: " + e.getMessage() ); | |
error.initCause( e ); | |
throw error; | |
} | |
} | |
result.add( new JavadocBundle( options, resources ) ); | |
} | |
return result; | |
} | |
private Collection<Entry<String, String>> resolveFromArtifacts( final SourceResolverConfig config, | |
final List<Artifact> artifacts ) | |
throws ArtifactResolutionException, ArtifactNotFoundException | |
{ | |
final List<Artifact> toResolve = new ArrayList<>( artifacts.size() ); | |
for ( final Artifact artifact : artifacts ) | |
{ | |
if ( config.filter() != null | |
&& !new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) ) | |
{ | |
continue; | |
} | |
if ( config.includeCompileSources() ) | |
{ | |
toResolve.add( createResourceArtifact( artifact, SOURCES_CLASSIFIER, config ) ); | |
} | |
if ( config.includeTestSources() ) | |
{ | |
toResolve.add( createResourceArtifact( artifact, TEST_SOURCES_CLASSIFIER, config ) ); | |
} | |
} | |
return resolveAndUnpack( toResolve, config, SOURCE_VALID_CLASSIFIERS, true ); | |
} | |
private Artifact createResourceArtifact( final Artifact artifact, final String classifier, | |
final SourceResolverConfig config ) | |
{ | |
final DefaultArtifact a = | |
(DefaultArtifact) artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), | |
artifact.getArtifactId(), | |
artifact.getVersion(), "jar", | |
classifier ); | |
a.setRepository( artifact.getRepository() ); | |
return a; | |
} | |
/** | |
* | |
* @param artifacts the artifacts to resolve | |
* @param config the configuration | |
* @param validClassifiers | |
* @param propagateErrors | |
* @return list of <dependencyConflictId, absolutePath> | |
* @throws ArtifactResolutionException if an exception occurs | |
* @throws ArtifactNotFoundException if an exception occurs | |
*/ | |
private Collection<Map.Entry<String, String>> resolveAndUnpack( final List<Artifact> artifacts, | |
final SourceResolverConfig config, | |
final List<String> validClassifiers, | |
final boolean propagateErrors ) | |
throws ArtifactResolutionException, ArtifactNotFoundException | |
{ | |
// NOTE: Since these are '-sources' and '-test-sources' artifacts, they won't actually | |
// resolve transitively...this is just used to aggregate resolution failures into a single | |
// exception. | |
final Set<Artifact> artifactSet = new LinkedHashSet<>( artifacts ); | |
final ArtifactFilter filter; | |
if ( config.filter() != null ) | |
{ | |
filter = new ArtifactIncludeFilterTransformer().transform( config.filter() ); | |
} | |
else | |
{ | |
filter = null; | |
} | |
final List<Map.Entry<String, String>> result = new ArrayList<>( artifacts.size() ); | |
for ( final Artifact a : artifactSet ) | |
{ | |
if ( !validClassifiers.contains( a.getClassifier() ) || ( filter != null && !filter.include( a ) ) ) | |
{ | |
continue; | |
} | |
Artifact resolvedArtifact; | |
try | |
{ | |
resolvedArtifact = resolver.resolveArtifact( config.getBuildingRequest(), a ).getArtifact(); | |
} | |
catch ( ArtifactResolverException e1 ) | |
{ | |
continue; | |
} | |
final File d = | |
new File( config.outputBasedir(), a.getArtifactId() + "-" + a.getVersion() + "-" + a.getClassifier() ); | |
if ( !d.exists() ) | |
{ | |
d.mkdirs(); | |
} | |
try | |
{ | |
final UnArchiver unArchiver = archiverManager.getUnArchiver( a.getType() ); | |
unArchiver.setDestDirectory( d ); | |
unArchiver.setSourceFile( resolvedArtifact.getFile() ); | |
unArchiver.extract(); | |
result.add( new AbstractMap.SimpleEntry<String, String>( a.getDependencyConflictId(), | |
d.getAbsolutePath() ) ); | |
} | |
catch ( final NoSuchArchiverException e ) | |
{ | |
if ( propagateErrors ) | |
{ | |
throw new ArtifactResolutionException( "Failed to retrieve valid un-archiver component: " | |
+ a.getType(), a, e ); | |
} | |
} | |
catch ( final ArchiverException e ) | |
{ | |
if ( propagateErrors ) | |
{ | |
throw new ArtifactResolutionException( "Failed to unpack: " + a.getId(), a, e ); | |
} | |
} | |
} | |
return result; | |
} | |
private static Collection<String> resolveFromProject( final SourceResolverConfig config, | |
final MavenProject reactorProject, final Artifact artifact ) | |
{ | |
final List<String> dirs = new ArrayList<>(); | |
if ( config.filter() == null | |
|| new ArtifactIncludeFilterTransformer().transform( config.filter() ).include( artifact ) ) | |
{ | |
if ( config.includeCompileSources() ) | |
{ | |
final List<String> srcRoots = reactorProject.getCompileSourceRoots(); | |
for ( final String root : srcRoots ) | |
{ | |
dirs.add( root ); | |
} | |
} | |
if ( config.includeTestSources() ) | |
{ | |
final List<String> srcRoots = reactorProject.getTestCompileSourceRoots(); | |
for ( final String root : srcRoots ) | |
{ | |
dirs.add( root ); | |
} | |
} | |
} | |
return JavadocUtil.pruneDirs( reactorProject, dirs ); | |
} | |
private static String key( final String gid, final String aid ) | |
{ | |
return gid + ":" + aid; | |
} | |
} |