diff --git a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java index 6b8d24a6..4165b597 100644 --- a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java +++ b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java @@ -19,6 +19,8 @@ * under the License. */ +import static java.util.Collections.singletonList; + import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.execution.MavenSession; @@ -40,7 +42,9 @@ import org.apache.maven.plugins.shade.pom.PomWriter; import org.apache.maven.plugins.shade.relocation.Relocator; import org.apache.maven.plugins.shade.relocation.SimpleRelocator; +import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer; import org.apache.maven.plugins.shade.resource.ResourceTransformer; +import org.apache.maven.plugins.shade.resource.ServicesResourceTransformer; import org.apache.maven.project.DefaultProjectBuildingRequest; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; @@ -76,6 +80,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -764,53 +769,40 @@ private List getResourceTransformers() { if ( transformers == null ) { - return Collections.emptyList(); + return getDefaultResourceTransformers(); } return Arrays.asList( transformers ); } + private List getDefaultResourceTransformers() + { + final List transformers = new LinkedList<>(); + + getLog().debug( "Adding ServicesResourceTransformer transformer" ); + transformers.add( new ServicesResourceTransformer() ); + + getLog().debug( "Adding ManifestResourceTransformer transformer" ); + transformers.add( new ManifestResourceTransformer() ); + + return transformers; + } + private List getFilters() throws MojoExecutionException { - List filters = new ArrayList(); - List simpleFilters = new ArrayList(); + List filters = new LinkedList(); + List simpleFilters = new LinkedList<>(); if ( this.filters != null && this.filters.length > 0 ) { - Map artifacts = new HashMap(); - - artifacts.put( project.getArtifact(), new ArtifactId( project.getArtifact() ) ); - - for ( Artifact artifact : project.getArtifacts() ) - { - artifacts.put( artifact, new ArtifactId( artifact ) ); - } + Map artifacts = getArtifactIds(); for ( ArchiveFilter filter : this.filters ) { ArtifactId pattern = new ArtifactId( filter.getArtifact() ); - Set jars = new HashSet(); - - for ( Map.Entry entry : artifacts.entrySet() ) - { - if ( entry.getValue().matches( pattern ) ) - { - Artifact artifact = entry.getKey(); - - jars.add( artifact.getFile() ); - - if ( createSourcesJar ) - { - File file = resolveArtifactSources( artifact ); - if ( file != null ) - { - jars.add( file ); - } - } - } - } + Set jars = getMatchingJars( artifacts, pattern ); if ( jars.isEmpty() ) { @@ -822,6 +814,10 @@ private List getFilters() simpleFilters.add( new SimpleFilter( jars, filter.getIncludes(), filter.getExcludes() ) ); } } + else if ( this.filters == null ) + { + simpleFilters.addAll( getDefaultFilters() ); + } filters.addAll( simpleFilters ); @@ -842,6 +838,55 @@ private List getFilters() return filters; } + private List getDefaultFilters() + { + getLog().debug( "Adding META-INF/*.SF, META-INF/*.DSA and META-INF/*.RSA exclusions" ); + + Map artifacts = getArtifactIds(); + return singletonList( new SimpleFilter( + getMatchingJars( artifacts , new ArtifactId( "*:*" ) ), + Collections.emptySet(), + new HashSet<>( Arrays.asList( "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA" ) ) ) ); + } + + private Set getMatchingJars( final Map artifacts, final ArtifactId pattern ) + { + final Set jars = new HashSet(); + + for ( final Map.Entry entry : artifacts.entrySet() ) + { + if ( entry.getValue().matches( pattern ) ) + { + final Artifact artifact = entry.getKey(); + + jars.add( artifact.getFile() ); + + if ( createSourcesJar ) + { + final File file = resolveArtifactSources( artifact ); + if ( file != null ) + { + jars.add( file ); + } + } + } + } + return jars; + } + + private Map getArtifactIds() + { + final Map artifacts = new HashMap(); + + artifacts.put( project.getArtifact(), new ArtifactId( project.getArtifact() ) ); + + for ( final Artifact artifact : project.getArtifacts() ) + { + artifacts.put( artifact, new ArtifactId( artifact ) ); + } + return artifacts; + } + private File shadedArtifactFileWithClassifier() { Artifact artifact = project.getArtifact(); diff --git a/src/site/apt/examples/includes-excludes.apt.vm b/src/site/apt/examples/includes-excludes.apt.vm index e83126f9..f6a7b9a4 100644 --- a/src/site/apt/examples/includes-excludes.apt.vm +++ b/src/site/apt/examples/includes-excludes.apt.vm @@ -122,6 +122,19 @@ Selecting Contents for Uber JAR was introduced in plugin version 1.3. It excludes all signature related files from every artifact, regardless of its group or artifact id. + Note that if you didn't specify a not null <<>> value then the following setup will be done: + ++---- + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + ++---- + Besides user-specified filters, the plugin can also be configured to automatically remove all classes of dependencies that are not used by the project, thereby minimizing the resulting uber JAR: diff --git a/src/site/apt/examples/resource-transformers.apt.vm b/src/site/apt/examples/resource-transformers.apt.vm index 1a098333..9f1d4eca 100644 --- a/src/site/apt/examples/resource-transformers.apt.vm +++ b/src/site/apt/examples/resource-transformers.apt.vm @@ -538,3 +538,10 @@ Transformers in <<>> ... +----- + +* Default transformers + + If no transformer is configured (which means you didn't opened/closed <<>> + because an empty list is considered valid) then a default setup is done. It adds <<>> + and <<>>. + diff --git a/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoTest.java b/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoTest.java index fa869c8f..5c6f8aa7 100644 --- a/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/mojo/ShadeMojoTest.java @@ -29,6 +29,7 @@ import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -41,10 +42,13 @@ import org.apache.maven.plugins.shade.ShadeRequest; import org.apache.maven.plugins.shade.Shader; import org.apache.maven.plugins.shade.filter.Filter; +import org.apache.maven.plugins.shade.filter.SimpleFilter; import org.apache.maven.plugins.shade.relocation.Relocator; import org.apache.maven.plugins.shade.relocation.SimpleRelocator; import org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer; +import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer; import org.apache.maven.plugins.shade.resource.ResourceTransformer; +import org.apache.maven.plugins.shade.resource.ServicesResourceTransformer; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectBuildingRequest; import org.apache.maven.shared.transfer.artifact.ArtifactCoordinate; @@ -60,6 +64,50 @@ public class ShadeMojoTest extends PlexusTestCase { + public void testDefaultConfiguration() throws Exception + { + final ShadeMojo shadeMojo = new ShadeMojo(); + setProject(shadeMojo); + + // default transformers are present + final Method getResourceTransformers = ShadeMojo.class.getDeclaredMethod("getResourceTransformers"); + getResourceTransformers.setAccessible(true); + final List transformers = + List.class.cast(getResourceTransformers.invoke(shadeMojo)); + assertEquals(2, transformers.size()); + assertTrue(ServicesResourceTransformer.class.isInstance(transformers.get(0))); + assertTrue(ManifestResourceTransformer.class.isInstance(transformers.get(1))); + + // default exclusion is present + final Method getFilters = ShadeMojo.class.getDeclaredMethod("getFilters"); + getFilters.setAccessible(true); + final List filters = + List.class.cast(getFilters.invoke(shadeMojo)); + assertEquals(1, filters.size()); + + final Filter filter = filters.iterator().next(); + assertTrue(SimpleFilter.class.isInstance(filter)); + + final Field jars = filter.getClass().getDeclaredField("jars"); + jars.setAccessible(true); + assertEquals(1, Collection.class.cast(jars.get(filter)).size()); + + final Field excludes = filter.getClass().getDeclaredField("excludes"); + excludes.setAccessible(true); + final Collection excludesValues = Collection.class.cast(excludes.get(filter)); + final String debugExcludes = excludesValues.toString(); + assertEquals(debugExcludes, 3, excludesValues.size()); + for ( final String exclude : Arrays.asList( "META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA" ) ) + { + assertTrue(exclude + " is not in " + debugExcludes, + excludesValues.contains( exclude.replace( '/', File.separatorChar ) ) ); + } + + final Field includes = filter.getClass().getDeclaredField("includes"); + includes.setAccessible(true); + assertTrue(Collection.class.cast(includes.get(filter)).isEmpty()); + } + public void testShaderWithDefaultShadedPattern() throws Exception { @@ -124,67 +172,8 @@ public void testShadeWithFilter() createSourcesJar.setAccessible( true ); createSourcesJar.set( mojo, Boolean.TRUE ); - // configure artifactResolver (mocked) for mojo - ArtifactResolver mockArtifactResolver = new ArtifactResolver() - { - @Override - public ArtifactResult resolveArtifact( ProjectBuildingRequest req, final Artifact art ) - throws ArtifactResolverException - { - return new ArtifactResult() - { - - @Override - public Artifact getArtifact() - { - art.setResolved( true ); - String fileName = art.getArtifactId() + "-" + art.getVersion() - + ( art.getClassifier() != null ? "-" + art.getClassifier() : "" ) + ".jar"; - art.setFile( new File( fileName ) ); - return art; - } - }; - } - - @Override - public ArtifactResult resolveArtifact( ProjectBuildingRequest req, final ArtifactCoordinate coordinate ) - throws ArtifactResolverException - { - return new ArtifactResult() - { - - @Override - public Artifact getArtifact() - { - Artifact art = mock( Artifact.class ); - when( art.getGroupId() ).thenReturn( coordinate.getGroupId() ); - when( art.getArtifactId() ).thenReturn( coordinate.getArtifactId() ); - when( art.getType() ).thenReturn( coordinate.getExtension() ); - when( art.getClassifier() ).thenReturn( coordinate.getClassifier() ); - when( art.isResolved() ).thenReturn( true ); - String fileName = coordinate.getArtifactId() + "-" + coordinate.getVersion() - + ( coordinate.getClassifier() != null ? "-" + coordinate.getClassifier() : "" ) + ".jar"; - when( art.getFile() ).thenReturn( new File( fileName ) ); - return art; - } - }; - } - }; - Field artifactResolverField = ShadeMojo.class.getDeclaredField( "artifactResolver" ); - artifactResolverField.setAccessible( true ); - artifactResolverField.set( mojo, mockArtifactResolver ); - - // create and configure MavenProject - MavenProject project = new MavenProject(); - ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE ); - Artifact artifact = new DefaultArtifact( "org.apache.myfaces.core", "myfaces-impl", - VersionRange.createFromVersion( "2.0.1-SNAPSHOT" ), "compile", "jar", - null, artifactHandler ); - artifact = mockArtifactResolver.resolveArtifact( null, artifact ).getArtifact(); // setFile and setResolved - project.setArtifact( artifact ); - Field projectField = ShadeMojo.class.getDeclaredField( "project" ); - projectField.setAccessible( true ); - projectField.set( mojo, project ); + // setup a project + setProject(mojo); // create and configure the ArchiveFilter ArchiveFilter archiveFilter = new ArchiveFilter(); @@ -247,4 +236,69 @@ public void shaderWithPattern( String shadedPattern, File jar ) s.shade( shadeRequest ); } + private void setProject(final ShadeMojo mojo) throws Exception + { + // configure artifactResolver (mocked) for mojo + ArtifactResolver mockArtifactResolver = new ArtifactResolver() + { + @Override + public ArtifactResult resolveArtifact(ProjectBuildingRequest req, final Artifact art ) + throws ArtifactResolverException + { + return new ArtifactResult() + { + + @Override + public Artifact getArtifact() + { + art.setResolved( true ); + String fileName = art.getArtifactId() + "-" + art.getVersion() + + ( art.getClassifier() != null ? "-" + art.getClassifier() : "" ) + ".jar"; + art.setFile( new File( fileName ) ); + return art; + } + }; + } + + @Override + public ArtifactResult resolveArtifact( ProjectBuildingRequest req, final ArtifactCoordinate coordinate ) + throws ArtifactResolverException + { + return new ArtifactResult() + { + + @Override + public Artifact getArtifact() + { + Artifact art = mock( Artifact.class ); + when( art.getGroupId() ).thenReturn( coordinate.getGroupId() ); + when( art.getArtifactId() ).thenReturn( coordinate.getArtifactId() ); + when( art.getType() ).thenReturn( coordinate.getExtension() ); + when( art.getClassifier() ).thenReturn( coordinate.getClassifier() ); + when( art.isResolved() ).thenReturn( true ); + String fileName = coordinate.getArtifactId() + "-" + coordinate.getVersion() + + ( coordinate.getClassifier() != null ? "-" + coordinate.getClassifier() : "" ) + ".jar"; + when( art.getFile() ).thenReturn( new File( fileName ) ); + return art; + } + }; + } + }; + Field artifactResolverField = ShadeMojo.class.getDeclaredField( "artifactResolver" ); + artifactResolverField.setAccessible( true ); + artifactResolverField.set( mojo, mockArtifactResolver ); + + // create and configure MavenProject + MavenProject project = new MavenProject(); + ArtifactHandler artifactHandler = (ArtifactHandler) lookup( ArtifactHandler.ROLE ); + Artifact artifact = new DefaultArtifact( "org.apache.myfaces.core", "myfaces-impl", + VersionRange.createFromVersion( "2.0.1-SNAPSHOT" ), "compile", "jar", + null, artifactHandler ); + artifact = mockArtifactResolver.resolveArtifact( null, artifact ).getArtifact(); // setFile and setResolved + project.setArtifact( artifact ); + Field projectField = ShadeMojo.class.getDeclaredField( "project" ); + projectField.setAccessible( true ); + projectField.set( mojo, project ); + } + }