groupId is equivalent to groupId:*:*:*, groupId:artifactId is
* equivalent to groupId:artifactId:*:* and groupId:artifactId:classifier is equivalent to
* groupId:artifactId:*:classifier. For example:
- *
+ *
*
* <artifactSet>
* <includes>
@@ -128,7 +128,7 @@ public class ShadeMojo
/**
* Packages to be relocated. For example:
- *
+ *
*
* <relocations>
* <relocation>
@@ -143,7 +143,7 @@ public class ShadeMojo
* </relocation>
* </relocations>
*
- *
+ *
* Note: Support for includes exists only since version 1.4.
*/
@SuppressWarnings( "MismatchedReadAndWriteOfArray" )
@@ -164,7 +164,7 @@ public class ShadeMojo
* to use an include to collect a set of files from the archive then use excludes to further reduce the set. By
* default, all files are included and no files are excluded. If multiple filters apply to an artifact, the
* intersection of the matched files will be included in the final JAR. For example:
- *
+ *
*
* <filters>
* <filter>
@@ -310,13 +310,41 @@ public class ShadeMojo
/**
* When true, dependencies will be stripped down on the class level to only the transitive hull required for the
- * artifact. Note: Usage of this feature requires Java 1.5 or higher.
+ * artifact. See also {@link #entryPoints}, if you wish to further optimize JAR minimization.
+ *
+ * Note: This feature requires Java 1.8 or higher due to its use of
+ * jdependency. Its accuracy therefore also depends on
+ * jdependency's limitations.
*
* @since 1.4
*/
@Parameter
private boolean minimizeJar;
+ /**
+ * Use this option in order to fine-tune {@link #minimizeJar}: By default, all of the target module's classes are
+ * kept and used as entry points for JAR minimization. By explicitly limiting the set of entry points, you can
+ * further minimize the set of classes kept in the shaded JAR. This affects both classes in the module itself and
+ * dependency classes. If {@link #minimizeJar} is inactive, this option has no effect either.
+ *
+ * Note: This feature requires Java 1.8 or higher due to its use of
+ * jdependency. Its accuracy therefore also depends on
+ * jdependency's limitations.
+ *
+ * Configuration example:
+ *
{@code
+ * true
+ *
+ * org.acme.Application
+ * org.acme.OtherEntryPoint
+ *
+ * }
+ *
+ * @since 3.5.0
+ */
+ @Parameter
+ private Set entryPoints;
+
/**
* The path to the output file for the shaded artifact. When this parameter is set, the created archive will neither
* replace the project's main artifact nor will it be attached. Hence, this parameter causes the parameters
@@ -551,7 +579,7 @@ public void execute()
replaceFile( finalFile, testSourcesJar );
testSourcesJar = finalFile;
}
-
+
renamed = true;
}
@@ -964,11 +992,16 @@ private List getFilters()
if ( minimizeJar )
{
- getLog().info( "Minimizing jar " + project.getArtifact() );
+ if ( entryPoints == null )
+ {
+ entryPoints = new HashSet<>();
+ }
+ getLog().info( "Minimizing jar " + project.getArtifact()
+ + ( entryPoints.isEmpty() ? "" : " with entry points" ) );
try
{
- filters.add( new MinijarFilter( project, getLog(), simpleFilters ) );
+ filters.add( new MinijarFilter( project, getLog(), simpleFilters, entryPoints ) );
}
catch ( IOException e )
{
@@ -1153,7 +1186,7 @@ private void rewriteDependencyReducedPomIfWeHaveReduction( List depe
}
File f = dependencyReducedPomLocation;
- // MSHADE-225
+ // MSHADE-225
// Works for now, maybe there's a better algorithm where no for-loop is required
if ( loopCounter == 0 )
{
diff --git a/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java b/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
index 14786ecc..960cf8d1 100644
--- a/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
+++ b/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
@@ -25,6 +25,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.URL;
@@ -36,11 +37,13 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.stream.Collectors;
import java.util.zip.CRC32;
@@ -57,6 +60,7 @@
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.Os;
import org.junit.Assert;
+import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
@@ -65,6 +69,9 @@
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
+import static java.util.Arrays.asList;
+import static java.util.Collections.singleton;
+import static org.codehaus.plexus.util.FileUtils.forceMkdir;
import static java.util.Objects.requireNonNull;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.hasItem;
@@ -87,6 +94,9 @@ public class DefaultShaderTest
private static final String[] EXCLUDES = new String[] { "org/codehaus/plexus/util/xml/Xpp3Dom",
"org/codehaus/plexus/util/xml/pull.*" };
+ @ClassRule
+ public static final TemporaryFolder tmp = new TemporaryFolder();
+
private final String NEWLINE = "\n";
@Test
@@ -119,7 +129,7 @@ public void testNoopWhenNotRelocated() throws IOException, MojoExecutionExceptio
// Before MSHADE-391, the processed files were written to the uber JAR, which did no harm, but made it
// difficult to find out by simple file comparison, if a file was actually relocated or not. Now, Shade
// makes sure to always write the original file if the class neither was relocated itself nor references
- // other, relocated classes. So we are checking for regressions here.
+ // other, relocated classes. So we are checking for regressions here.
assertTrue( areEqual( originalJar, shadedJar,
"org/codehaus/plexus/util/Expand.class" ) );
@@ -289,6 +299,60 @@ public void testShaderWithoutExcludesShouldRemoveReferencesOfOriginalPattern()
new String[] {} );
}
+ @Test
+ public void testHandleDirectory()
+ throws Exception
+ {
+ final File dir = tmp.getRoot();
+ // explode src/test/jars/test-artifact-1.0-SNAPSHOT.jar in this temp dir
+ try ( final JarInputStream in = new JarInputStream(
+ new FileInputStream( "src/test/jars/test-artifact-1.0-SNAPSHOT.jar" ) ) )
+ {
+ JarEntry nextJarEntry;
+ while ( (nextJarEntry = in.getNextJarEntry()) != null )
+ {
+ if ( nextJarEntry.isDirectory() )
+ {
+ continue;
+ }
+ final File out = new File( dir, nextJarEntry.getName() );
+ forceMkdir( out.getParentFile() );
+ try ( final OutputStream outputStream = new FileOutputStream( out ) )
+ {
+ IOUtil.copy( in, outputStream, (int) Math.max( nextJarEntry.getSize(), 512 ) );
+ }
+ }
+ }
+
+ // do shade
+ final File shade = new File( "target/testHandleDirectory.jar" );
+ shaderWithPattern( "org/shaded/plexus/util", shade, new String[0], singleton( dir ) );
+
+ // ensure directory was shaded properly
+ try ( final JarFile jar = new JarFile( shade ) )
+ {
+ final List entries = new ArrayList<>();
+ final Enumeration jarEntryEnumeration = jar.entries();
+ while ( jarEntryEnumeration.hasMoreElements() )
+ {
+ final JarEntry jarEntry = jarEntryEnumeration.nextElement();
+ if ( jarEntry.isDirectory() )
+ {
+ continue;
+ }
+ entries.add( jarEntry.getName() );
+ }
+ Collections.sort( entries );
+ assertEquals(
+ asList(
+ "META-INF/maven/org.apache.maven.plugins.shade/test-artifact/pom.properties",
+ "META-INF/maven/org.apache.maven.plugins.shade/test-artifact/pom.xml",
+ "org/apache/maven/plugins/shade/Lib.class"
+ ),
+ entries );
+ }
+ }
+
@Test
public void testShaderWithRelocatedClassname()
throws Exception
@@ -531,16 +595,18 @@ private void writeEntryWithoutCompression( String entryName, byte[] entryBytes,
jos.closeEntry();
}
- private void shaderWithPattern( String shadedPattern, File jar, String[] excludes )
- throws Exception
+ private void shaderWithPattern( String shadedPattern, File jar, String[] excludes ) throws Exception
{
- DefaultShader s = newShader();
-
Set set = new LinkedHashSet<>();
-
set.add( new File( "src/test/jars/test-project-1.0-SNAPSHOT.jar" ) );
-
set.add( new File( "src/test/jars/plexus-utils-1.4.1.jar" ) );
+ shaderWithPattern( shadedPattern, jar, excludes, set );
+ }
+
+ private void shaderWithPattern( String shadedPattern, File jar, String[] excludes, Set set )
+ throws Exception
+ {
+ DefaultShader s = newShader();
List relocators = new ArrayList<>();
diff --git a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java
index 25be4d8a..9f13a12d 100644
--- a/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java
+++ b/src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java
@@ -32,6 +32,7 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -70,7 +71,7 @@ public void init()
this.outputDirectory = tempFolder.newFolder();
this.emptyFile = tempFolder.newFile();
this.jarFile = tempFolder.newFile();
- new JarOutputStream( new FileOutputStream( this.jarFile ) ).close();
+ new JarOutputStream(Files.newOutputStream(this.jarFile.toPath())).close();
this.log = mock(Log.class);
logCaptor = ArgumentCaptor.forClass(CharSequence.class);
}
@@ -187,23 +188,4 @@ public void finishedShouldProduceMessageForClassesTotalZero()
}
- /**
- * Verify that directories are ignored when scanning the classpath for JARs containing services,
- * but warnings are logged instead
- *
- * @see MSHADE-366
- */
- @Test
- public void removeServicesShouldIgnoreDirectories() throws Exception {
- String classPathElementToIgnore = tempFolder.newFolder().getAbsolutePath();
- MavenProject mockedProject = mockProject( outputDirectory, jarFile, classPathElementToIgnore );
-
- new MinijarFilter(mockedProject, log);
-
- verify( log, times( 1 ) ).warn( logCaptor.capture() );
-
- assertThat( logCaptor.getValue().toString(), startsWith(
- "Not a JAR file candidate. Ignoring classpath element '" + classPathElementToIgnore + "' (" ) );
- }
-
}