[Git][java-team/maven-shade-plugin][upstream] New upstream version 3.4.1
Emmanuel Bourg (@ebourg)
gitlab at salsa.debian.org
Fri Jan 6 12:56:18 GMT 2023
Emmanuel Bourg pushed to branch upstream at Debian Java Maintainers / maven-shade-plugin
Commits:
9cff8cb2 by Emmanuel Bourg at 2023-01-06T13:52:26+01:00
New upstream version 3.4.1
- - - - -
13 changed files:
- pom.xml
- + src/it/projects/MSHADE-413-parallel/invoker.properties
- + src/it/projects/MSHADE-413-parallel/p1/pom.xml
- + src/it/projects/MSHADE-413-parallel/p2/pom.xml
- + src/it/projects/MSHADE-413-parallel/pom.xml
- src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
- src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java
- src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java
- src/main/java/org/apache/maven/plugins/shade/pom/MavenJDOMWriter.java
- src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java
- src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
- src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java
- src/test/java/org/apache/maven/plugins/shade/resource/ServiceResourceTransformerTest.java
Changes:
=====================================
pom.xml
=====================================
@@ -30,7 +30,7 @@
</parent>
<artifactId>maven-shade-plugin</artifactId>
- <version>3.4.0</version>
+ <version>3.4.1</version>
<packaging>maven-plugin</packaging>
<name>Apache Maven Shade Plugin</name>
@@ -47,7 +47,7 @@
<connection>scm:git:https://gitbox.apache.org/repos/asf/maven-shade-plugin.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/maven-shade-plugin.git</developerConnection>
<url>https://github.com/apache/maven-shade-plugin/tree/${project.scm.tag}</url>
- <tag>maven-shade-plugin-3.4.0</tag>
+ <tag>maven-shade-plugin-3.4.1</tag>
</scm>
<issueManagement>
<system>jira</system>
@@ -69,9 +69,9 @@
<javaVersion>8</javaVersion>
<sisu.version>0.3.5</sisu.version>
<currentVersion>${project.version}</currentVersion>
- <asmVersion>9.3</asmVersion>
+ <asmVersion>9.4</asmVersion>
<slf4j.version>1.7.32</slf4j.version>
- <project.build.outputTimestamp>2022-09-11T10:17:50Z</project.build.outputTimestamp>
+ <project.build.outputTimestamp>2022-10-21T20:02:32Z</project.build.outputTimestamp>
</properties>
<contributors>
@@ -149,7 +149,7 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
- <version>3.3.0</version>
+ <version>3.4.2</version>
</dependency>
<!-- DI -->
@@ -197,7 +197,7 @@
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-dependency-tree</artifactId>
- <version>3.0.1</version>
+ <version>3.2.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
=====================================
src/it/projects/MSHADE-413-parallel/invoker.properties
=====================================
@@ -0,0 +1,19 @@
+# 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.
+
+invoker.timeoutInSeconds=60
+invoker.goals = clean install -T2 -pl p1/ -pl p2/
=====================================
src/it/projects/MSHADE-413-parallel/p1/pom.xml
=====================================
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.maven.its.shade.parallel</groupId>
+ <artifactId>mshade413-parent</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>mshade413-p1</artifactId>
+ <version>1.0</version>
+
+ <name>MSHADE-413-p1</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.projectnessie</groupId>
+ <artifactId>nessie-spark-extensions-base</artifactId>
+ <version>0.22.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectnessie</groupId>
+ <artifactId>nessie-spark-extensions-base</artifactId>
+ <version>0.22.0</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.iceberg</groupId>
+ <artifactId>iceberg-spark3-runtime</artifactId>
+ <version>0.13.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>5.8.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>@project.version@</version>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>org.projectnessie</include>
+ </includes>
+ </artifactSet>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
=====================================
src/it/projects/MSHADE-413-parallel/p2/pom.xml
=====================================
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.maven.its.shade.parallel</groupId>
+ <artifactId>mshade413-parent</artifactId>
+ <version>1.0</version>
+ </parent>
+
+ <artifactId>mshade413-p2</artifactId>
+ <version>1.0</version>
+
+ <name>MSHADE-413-p2</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.projectnessie</groupId>
+ <artifactId>nessie-spark-extensions-base</artifactId>
+ <version>0.22.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.projectnessie</groupId>
+ <artifactId>nessie-spark-extensions-base</artifactId>
+ <version>0.22.0</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.iceberg</groupId>
+ <artifactId>iceberg-spark3-runtime</artifactId>
+ <version>0.13.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ <version>5.8.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>@project.version@</version>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>org.projectnessie</include>
+ </includes>
+ </artifactSet>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
=====================================
src/it/projects/MSHADE-413-parallel/pom.xml
=====================================
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.maven.its.shade.parallel</groupId>
+ <artifactId>mshade413-parent</artifactId>
+ <version>1.0</version>
+ <packaging>pom</packaging>
+
+ <name>MSHADE-413</name>
+ <description>
+ Test that shade works in two parallel project builds.
+ </description>
+
+ <modules>
+ <module>p1</module>
+ <module>p2</module>
+ </modules>
+</project>
=====================================
src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
=====================================
@@ -22,7 +22,6 @@ package org.apache.maven.plugins.shade;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -62,6 +61,7 @@ import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer;
import org.apache.maven.plugins.shade.resource.ReproducibleResourceTransformer;
import org.apache.maven.plugins.shade.resource.ResourceTransformer;
import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.io.CachingOutputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
@@ -116,7 +116,8 @@ public class DefaultShader
shadeRequest.getUberJar().getParentFile().mkdirs();
try ( JarOutputStream out =
- new JarOutputStream( new BufferedOutputStream( new FileOutputStream( shadeRequest.getUberJar() ) ) ) )
+ new JarOutputStream( new BufferedOutputStream(
+ new CachingOutputStream( shadeRequest.getUberJar() ) ) ) )
{
goThroughAllJarEntriesForManifestTransformer( shadeRequest, resources, manifestTransformer, out );
@@ -180,8 +181,11 @@ public class DefaultShader
public boolean hasZipHeader() throws IOException
{
final byte[] header = new byte[HEADER_LEN];
- super.read( header, 0, HEADER_LEN );
- super.unread( header );
+ int len = super.read( header, 0, HEADER_LEN );
+ if ( len != -1 )
+ {
+ super.unread( header, 0, len );
+ }
return Arrays.equals( header, ZIP_HEADER );
}
}
=====================================
src/main/java/org/apache/maven/plugins/shade/filter/MinijarFilter.java
=====================================
@@ -129,58 +129,22 @@ public class MinijarFilter
neededClasses.removeAll( removable );
try
{
+ // getRuntimeClasspathElements returns a list of
+ // - the build output directory
+ // - all the paths to the dependencies' jars
+ // We thereby need to ignore the build directory because we don't want
+ // to remove anything from it, as it's the starting point of the
+ // minification process.
for ( final String fileName : project.getRuntimeClasspathElements() )
{
- try ( final JarFile jar = new JarFile( fileName ) )
+ // Ignore the build directory from this project
+ if ( fileName.equals( project.getBuild().getOutputDirectory() ) )
{
- for ( final Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); )
- {
- final JarEntry jarEntry = entries.nextElement();
- if ( jarEntry.isDirectory() || !jarEntry.getName().startsWith( "META-INF/services/" ) )
- {
- continue;
- }
-
- final String serviceClassName =
- jarEntry.getName().substring( "META-INF/services/".length() );
- final boolean isNeededClass = neededClasses.contains( cp.getClazz( serviceClassName ) );
- if ( !isNeededClass )
- {
- continue;
- }
-
- try ( final BufferedReader bufferedReader =
- new BufferedReader( new InputStreamReader( jar.getInputStream( jarEntry ), UTF_8 ) ) )
- {
- for ( String line = bufferedReader.readLine(); line != null;
- line = bufferedReader.readLine() )
- {
- final String className = line.split( "#", 2 )[0].trim();
- if ( className.isEmpty() )
- {
- continue;
- }
-
- final Clazz clazz = cp.getClazz( className );
- if ( clazz == null || !removable.contains( clazz ) )
- {
- continue;
- }
-
- log.debug( className + " was not removed because it is a service" );
- removeClass( clazz );
- repeatScan = true; // check whether the found classes use services in turn
- }
- }
- catch ( final IOException e )
- {
- log.warn( e.getMessage() );
- }
- }
+ continue;
}
- catch ( final IOException e )
+ if ( removeServicesFromJar( cp, neededClasses, fileName ) )
{
- log.warn( e.getMessage() );
+ repeatScan = true;
}
}
}
@@ -192,6 +156,69 @@ public class MinijarFilter
while ( repeatScan );
}
+ private boolean removeServicesFromJar( Clazzpath cp, Set<Clazz> neededClasses, String fileName )
+ {
+ boolean repeatScan = false;
+ try ( final JarFile jar = new JarFile( fileName ) )
+ {
+ for ( final Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); )
+ {
+ final JarEntry jarEntry = entries.nextElement();
+ if ( jarEntry.isDirectory() || !jarEntry.getName().startsWith( "META-INF/services/" ) )
+ {
+ continue;
+ }
+
+ final String serviceClassName = jarEntry.getName().substring( "META-INF/services/".length() );
+ final boolean isNeededClass = neededClasses.contains( cp.getClazz( serviceClassName ) );
+ if ( !isNeededClass )
+ {
+ continue;
+ }
+
+ try ( final BufferedReader configFileReader = new BufferedReader(
+ new InputStreamReader( jar.getInputStream( jarEntry ), UTF_8 ) ) )
+ {
+ // check whether the found classes use services in turn
+ repeatScan = scanServiceProviderConfigFile( cp, configFileReader );
+ }
+ catch ( final IOException e )
+ {
+ log.warn( e.getMessage() );
+ }
+ }
+ }
+ catch ( final IOException e )
+ {
+ log.warn( "Not a JAR file candidate. Ignoring classpath element '" + fileName + "' (" + e + ")." );
+ }
+ return repeatScan;
+ }
+
+ private boolean scanServiceProviderConfigFile( Clazzpath cp, BufferedReader configFileReader ) throws IOException
+ {
+ boolean serviceClassFound = false;
+ for ( String line = configFileReader.readLine(); line != null; line = configFileReader.readLine() )
+ {
+ final String className = line.split( "#", 2 )[0].trim();
+ if ( className.isEmpty() )
+ {
+ continue;
+ }
+
+ final Clazz clazz = cp.getClazz( className );
+ if ( clazz == null || !removable.contains( clazz ) )
+ {
+ continue;
+ }
+
+ log.debug( className + " was not removed because it is a service" );
+ removeClass( clazz );
+ serviceClassFound = true;
+ }
+ return serviceClassFound;
+ }
+
private void removeClass( final Clazz clazz )
{
removable.remove( clazz );
=====================================
src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java
=====================================
@@ -1061,10 +1061,6 @@ public class ShadeMojo
private void createDependencyReducedPom( Set<String> artifactsToRemove )
throws IOException, DependencyGraphBuilderException, ProjectBuildingException
{
- List<Dependency> dependencies = new ArrayList<>();
-
- boolean modified = false;
-
List<Dependency> transitiveDeps = new ArrayList<>();
// NOTE: By using the getArtifacts() we get the completely evaluated artifacts
@@ -1083,39 +1079,48 @@ public class ShadeMojo
// we'll figure out the exclusions in a bit.
transitiveDeps.add( dep );
}
- List<Dependency> origDeps = project.getDependencies();
- if ( promoteTransitiveDependencies )
+ Model model = project.getOriginalModel();
+
+ // MSHADE-413: Must not use objects (for example `Model` or `Dependency`) that are "owned
+ // by Maven" and being used by other projects/plugins. Modifying those will break the
+ // correctness of the build - or cause an endless loop.
+ List<Dependency> origDeps = new ArrayList<>();
+ List<Dependency> source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
+ for ( Dependency d : source )
{
- origDeps = transitiveDeps;
+ origDeps.add( d.clone() );
}
+ model = model.clone();
- Model model = project.getOriginalModel();
// MSHADE-185: We will remove all system scoped dependencies which usually
// have some kind of property usage. At this time the properties within
// such things are already evaluated.
List<Dependency> originalDependencies = model.getDependencies();
removeSystemScopedDependencies( artifactsToRemove, originalDependencies );
+ List<Dependency> dependencies = new ArrayList<>();
+ boolean modified = false;
for ( Dependency d : origDeps )
{
- dependencies.add( d );
-
- String id = getId( d );
-
- if ( artifactsToRemove.contains( id ) )
+ if ( artifactsToRemove.contains( getId( d ) ) )
{
- modified = true;
-
if ( keepDependenciesWithProvidedScope )
{
- d.setScope( "provided" );
+ if ( !"provided".equals( d.getScope() ) )
+ {
+ modified = true;
+ d.setScope( "provided" );
+ }
}
else
{
- dependencies.remove( d );
+ modified = true;
+ continue;
}
}
+
+ dependencies.add( d );
}
// MSHADE-155
@@ -1299,8 +1304,13 @@ public class ShadeMojo
boolean modified = false;
for ( DependencyNode n2 : node.getChildren() )
{
+ String artifactId2 = getId( n2.getArtifact() );
+
for ( DependencyNode n3 : n2.getChildren() )
{
+ Artifact artifact3 = n3.getArtifact();
+ String artifactId3 = getId( artifact3 );
+
// check if it really isn't in the list of original dependencies. Maven
// prior to 2.0.8 may grab versions from transients instead of
// from the direct deps in which case they would be marked included
@@ -1310,7 +1320,7 @@ public class ShadeMojo
boolean found = false;
for ( Dependency dep : transitiveDeps )
{
- if ( getId( dep ).equals( getId( n3.getArtifact() ) ) )
+ if ( getId( dep ).equals( artifactId3 ) )
{
found = true;
break;
@@ -1321,18 +1331,31 @@ public class ShadeMojo
// note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true,
// but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps,
// which makes this fix even possible (see also MSHADE-181)
- if ( !found && !"provided".equals( n3.getArtifact().getScope() ) )
+ if ( !found && !"provided".equals( artifact3.getScope() ) )
{
+ getLog().debug( String.format( "dependency %s (scope %s) not found in transitive dependencies",
+ artifactId3, artifact3.getScope() ) );
for ( Dependency dep : dependencies )
{
- if ( getId( dep ).equals( getId( n2.getArtifact() ) ) )
+ if ( getId( dep ).equals( artifactId2 ) )
{
- Exclusion exclusion = new Exclusion();
- exclusion.setArtifactId( n3.getArtifact().getArtifactId() );
- exclusion.setGroupId( n3.getArtifact().getGroupId() );
- dep.addExclusion( exclusion );
- modified = true;
- break;
+ // MSHADE-413: First check whether the exclusion has already been added,
+ // because it's meaningless to add it more than once. Certain cases
+ // can end up adding the exclusion "forever" and cause an endless loop
+ // rewriting the whole dependency-reduced-pom.xml file.
+ if ( !dependencyHasExclusion( dep, artifact3 ) )
+ {
+ getLog().debug( String.format( "Adding exclusion for dependency %s (scope %s) "
+ + "to %s (scope %s)",
+ artifactId3, artifact3.getScope(),
+ getId( dep ), dep.getScope() ) );
+ Exclusion exclusion = new Exclusion();
+ exclusion.setArtifactId( artifact3.getArtifactId() );
+ exclusion.setGroupId( artifact3.getGroupId() );
+ dep.addExclusion( exclusion );
+ modified = true;
+ break;
+ }
}
}
}
@@ -1347,6 +1370,21 @@ public class ShadeMojo
}
}
+ private boolean dependencyHasExclusion( Dependency dep, Artifact exclusionToCheck )
+ {
+ boolean containsExclusion = false;
+ for ( Exclusion existingExclusion : dep.getExclusions() )
+ {
+ if ( existingExclusion.getGroupId().equals( exclusionToCheck.getGroupId() )
+ && existingExclusion.getArtifactId().equals( exclusionToCheck.getArtifactId() ) )
+ {
+ containsExclusion = true;
+ break;
+ }
+ }
+ return containsExclusion;
+ }
+
private List<ResourceTransformer> toResourceTransformers(
String shade, List<ResourceTransformer> resourceTransformers )
{
=====================================
src/main/java/org/apache/maven/plugins/shade/pom/MavenJDOMWriter.java
=====================================
@@ -1630,7 +1630,6 @@ public class MavenJDOMWriter
iterateRepository( innerCount, root, value.getRepositories(), "repositories", "repository" );
iterateRepository( innerCount, root, value.getPluginRepositories(), "pluginRepositories", "pluginRepository" );
iterateDependency( innerCount, root, value.getDependencies(), "dependencies", "dependency" );
- findAndReplaceXpp3DOM( innerCount, root, "reports", (Xpp3Dom) value.getReports() );
updateReporting( value.getReporting(), "reporting", innerCount, root );
updateDependencyManagement( value.getDependencyManagement(), "dependencyManagement", innerCount, root );
updateDistributionManagement( value.getDistributionManagement(), "distributionManagement", innerCount, root );
@@ -1658,7 +1657,6 @@ public class MavenJDOMWriter
iterateRepository( innerCount, root, value.getPluginRepositories(), "pluginRepositories",
"pluginRepository" );
iterateDependency( innerCount, root, value.getDependencies(), "dependencies", "dependency" );
- findAndReplaceXpp3DOM( innerCount, root, "reports", (Xpp3Dom) value.getReports() );
updateReporting( value.getReporting(), "reporting", innerCount, root );
updateDependencyManagement( value.getDependencyManagement(), "dependencyManagement", innerCount, root );
updateDistributionManagement( value.getDistributionManagement(), "distributionManagement", innerCount, root );
@@ -1775,7 +1773,6 @@ public class MavenJDOMWriter
!value.isExtensions() ? null : String.valueOf( value.isExtensions() ), "false" );
iteratePluginExecution( innerCount, root, value.getExecutions(), "executions", "execution" );
iterateDependency( innerCount, root, value.getDependencies(), "dependencies", "dependency" );
- findAndReplaceXpp3DOM( innerCount, root, "goals", (Xpp3Dom) value.getGoals() );
findAndReplaceSimpleElement( innerCount, root, "inherited", value.getInherited(), null );
findAndReplaceXpp3DOM( innerCount, root, "configuration", (Xpp3Dom) value.getConfiguration() );
}
@@ -1897,7 +1894,6 @@ public class MavenJDOMWriter
iterateRepository( innerCount, root, value.getRepositories(), "repositories", "repository" );
iterateRepository( innerCount, root, value.getPluginRepositories(), "pluginRepositories", "pluginRepository" );
iterateDependency( innerCount, root, value.getDependencies(), "dependencies", "dependency" );
- findAndReplaceXpp3DOM( innerCount, root, "reports", (Xpp3Dom) value.getReports() );
updateReporting( value.getReporting(), "reporting", innerCount, root );
updateDependencyManagement( value.getDependencyManagement(), "dependencyManagement", innerCount, root );
updateDistributionManagement( value.getDistributionManagement(), "distributionManagement", innerCount, root );
=====================================
src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java
=====================================
@@ -22,11 +22,12 @@ package org.apache.maven.plugins.shade.resource;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
@@ -45,7 +46,7 @@ public class ServicesResourceTransformer
{
private static final String SERVICES_PATH = "META-INF/services";
- private final Map<String, ArrayList<String>> serviceEntries = new HashMap<>();
+ private final Map<String, Set<String>> serviceEntries = new HashMap<>();
private long time = Long.MIN_VALUE;
@@ -68,7 +69,7 @@ public class ServicesResourceTransformer
}
resource = SERVICES_PATH + '/' + resource;
- ArrayList<String> out = serviceEntries.computeIfAbsent( resource, k -> new ArrayList<>() );
+ Set<String> out = serviceEntries.computeIfAbsent( resource, k -> new LinkedHashSet<>() );
Scanner scanner = new Scanner( is, StandardCharsets.UTF_8.name() );
while ( scanner.hasNextLine() )
@@ -98,10 +99,10 @@ public class ServicesResourceTransformer
public void modifyOutputStream( JarOutputStream jos )
throws IOException
{
- for ( Map.Entry<String, ArrayList<String>> entry : serviceEntries.entrySet() )
+ for ( Map.Entry<String, Set<String>> entry : serviceEntries.entrySet() )
{
String key = entry.getKey();
- ArrayList<String> data = entry.getValue();
+ Set<String> data = entry.getValue();
JarEntry jarEntry = new JarEntry( key );
jarEntry.setTime( time );
=====================================
src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
=====================================
@@ -19,15 +19,20 @@ package org.apache.maven.plugins.shade;
* under the License.
*/
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -37,6 +42,7 @@ import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
+import java.util.stream.Collectors;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
@@ -47,6 +53,7 @@ import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
import org.apache.maven.plugins.shade.resource.AppendingTransformer;
import org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer;
import org.apache.maven.plugins.shade.resource.ResourceTransformer;
+import org.apache.maven.plugins.shade.resource.ServicesResourceTransformer;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.Os;
import org.junit.Assert;
@@ -80,6 +87,8 @@ public class DefaultShaderTest
private static final String[] EXCLUDES = new String[] { "org/codehaus/plexus/util/xml/Xpp3Dom",
"org/codehaus/plexus/util/xml/pull.*" };
+ private final String NEWLINE = "\n";
+
@Test
public void testNoopWhenNotRelocated() throws IOException, MojoExecutionException {
final File plexusJar = new File("src/test/jars/plexus-utils-1.4.1.jar" );
@@ -343,6 +352,44 @@ public class DefaultShaderTest
final String innerJarFileName = "inner.jar";
+ temporaryFolder.create();
+ File innerJar = temporaryFolder.newFile( innerJarFileName );
+ try ( JarOutputStream jos = new JarOutputStream( Files.newOutputStream( innerJar.toPath() ) ) )
+ {
+ jos.putNextEntry( new JarEntry( "foo.txt" ) );
+ jos.write( "c1".getBytes( StandardCharsets.UTF_8 ) );
+ jos.closeEntry();
+ }
+
+ ShadeRequest shadeRequest = new ShadeRequest();
+ shadeRequest.setJars( new LinkedHashSet<>( Collections.singleton( innerJar ) ) );
+ shadeRequest.setFilters( Collections.emptyList() );
+ shadeRequest.setRelocators( Collections.emptyList() );
+ shadeRequest.setResourceTransformers( Collections.emptyList() );
+ File shadedFile = temporaryFolder.newFile( "shaded.jar" );
+ shadeRequest.setUberJar( shadedFile );
+
+ DefaultShader shader = newShader();
+ shader.shade( shadeRequest );
+
+ FileTime lastModified = FileTime.from( Files.getLastModifiedTime( shadedFile.toPath() ).toInstant()
+ .minus( 5, ChronoUnit.SECONDS ) );
+
+ Files.setLastModifiedTime( shadedFile.toPath(), lastModified );
+
+ shader.shade(shadeRequest);
+ assertEquals( lastModified, Files.getLastModifiedTime( shadedFile.toPath() ) );
+
+ temporaryFolder.delete();
+ }
+
+ @Test
+ public void testShaderNoOverwrite() throws Exception
+ {
+ TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ final String innerJarFileName = "inner.jar";
+
temporaryFolder.create();
File innerJar = temporaryFolder.newFile( innerJarFileName );
try ( JarOutputStream jos = new JarOutputStream( new FileOutputStream( innerJar ) ) )
@@ -382,6 +429,93 @@ public class DefaultShaderTest
temporaryFolder.delete();
}
+ @Test
+ public void testShaderWithDuplicateService() throws Exception
+ {
+ TemporaryFolder temporaryFolder = new TemporaryFolder();
+ temporaryFolder.create();
+
+ String serviceEntryName = "META-INF/services/my.foo.Service";
+ String serviceEntryValue = "my.foo.impl.Service1";
+
+ File innerJar1 = temporaryFolder.newFile( "inner1.jar" );
+ try (JarOutputStream jos = new JarOutputStream(Files.newOutputStream( innerJar1.toPath() ) ) )
+ {
+ jos.putNextEntry( new JarEntry(serviceEntryName) );
+ jos.write( ( serviceEntryValue + NEWLINE ).getBytes( StandardCharsets.UTF_8 ) );
+ jos.closeEntry();
+ }
+
+ File innerJar2 = temporaryFolder.newFile( "inner2.jar" );
+ try ( JarOutputStream jos = new JarOutputStream( Files.newOutputStream( innerJar2.toPath() ) ) )
+ {
+ jos.putNextEntry( new JarEntry(serviceEntryName) );
+ jos.write( ( serviceEntryValue + NEWLINE ).getBytes( StandardCharsets.UTF_8 ) );
+ jos.closeEntry();
+ }
+
+ ShadeRequest shadeRequest = new ShadeRequest();
+ shadeRequest.setJars( new LinkedHashSet<>( Arrays.asList( innerJar1, innerJar2 ) ) );
+ shadeRequest.setFilters( Collections.emptyList() );
+ shadeRequest.setRelocators( Collections.emptyList() );
+ shadeRequest.setResourceTransformers( Collections.singletonList( new ServicesResourceTransformer() ) );
+ File shadedFile = temporaryFolder.newFile( "shaded.jar" );
+ shadeRequest.setUberJar( shadedFile );
+
+ DefaultShader shader = newShader();
+ shader.shade( shadeRequest );
+
+ JarFile shadedJarFile = new JarFile( shadedFile );
+ JarEntry entry = shadedJarFile.getJarEntry(serviceEntryName);
+
+ List<String> lines = new BufferedReader( new InputStreamReader( shadedJarFile.getInputStream( entry ), StandardCharsets.UTF_8 ) )
+ .lines().collect( Collectors.toList() );
+
+ //After shading, there should be a single input
+ Assert.assertEquals( Collections.singletonList( serviceEntryValue ), lines );
+
+ temporaryFolder.delete();
+ }
+
+ @Test
+ public void testShaderWithSmallEntries() throws Exception
+ {
+ TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ final String innerJarFileName = "inner.jar";
+ int len;
+
+ temporaryFolder.create();
+ File innerJar = temporaryFolder.newFile( innerJarFileName );
+ try ( JarOutputStream jos = new JarOutputStream( new FileOutputStream( innerJar ) ) )
+ {
+ jos.putNextEntry( new JarEntry( "foo.txt" ) );
+ byte[] bytes = "c1".getBytes(StandardCharsets.UTF_8);
+ len = bytes.length;
+ jos.write( bytes );
+ jos.closeEntry();
+ }
+
+ ShadeRequest shadeRequest = new ShadeRequest();
+ shadeRequest.setJars( new LinkedHashSet<>( Collections.singleton( innerJar ) ) );
+ shadeRequest.setFilters( new ArrayList<Filter>() );
+ shadeRequest.setRelocators( new ArrayList<Relocator>() );
+ shadeRequest.setResourceTransformers( new ArrayList<ResourceTransformer>() );
+ File shadedFile = temporaryFolder.newFile( "shaded.jar" );
+ shadeRequest.setUberJar( shadedFile );
+
+ DefaultShader shader = newShader();
+ shader.shade( shadeRequest );
+
+ JarFile shadedJarFile = new JarFile( shadedFile );
+ JarEntry entry = shadedJarFile.getJarEntry( "foo.txt" );
+
+ //After shading, entry compression method should not be changed.
+ Assert.assertEquals( entry.getSize(), len );
+
+ temporaryFolder.delete();
+ }
+
private void writeEntryWithoutCompression( String entryName, byte[] entryBytes, JarOutputStream jos ) throws IOException
{
final JarEntry entry = new JarEntry( entryName );
=====================================
src/test/java/org/apache/maven/plugins/shade/filter/MinijarFilterTest.java
=====================================
@@ -19,7 +19,10 @@ package org.apache.maven.plugins.shade.filter;
* under the License.
*/
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -27,15 +30,23 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Set;
import java.util.TreeSet;
+import java.util.jar.JarOutputStream;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.DependencyResolutionRequiredException;
+import org.apache.maven.model.Build;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
@@ -43,16 +54,25 @@ import org.mockito.ArgumentCaptor;
public class MinijarFilterTest
{
+ @Rule
+ public TemporaryFolder tempFolder = TemporaryFolder.builder().assureDeletion().build();
+
+ private File outputDirectory;
private File emptyFile;
+ private File jarFile;
+ private Log log;
+ private ArgumentCaptor<CharSequence> logCaptor;
@Before
public void init()
throws IOException
{
- TemporaryFolder tempFolder = new TemporaryFolder();
- tempFolder.create();
+ this.outputDirectory = tempFolder.newFolder();
this.emptyFile = tempFolder.newFile();
-
+ this.jarFile = tempFolder.newFile();
+ new JarOutputStream( new FileOutputStream( this.jarFile ) ).close();
+ this.log = mock(Log.class);
+ logCaptor = ArgumentCaptor.forClass(CharSequence.class);
}
/**
@@ -64,11 +84,7 @@ public class MinijarFilterTest
{
assumeFalse( "Expected to run under JDK8+", System.getProperty("java.version").startsWith("1.7") );
- ArgumentCaptor<CharSequence> logCaptor = ArgumentCaptor.forClass( CharSequence.class );
-
- MavenProject mavenProject = mockProject( emptyFile );
-
- Log log = mock( Log.class );
+ MavenProject mavenProject = mockProject( outputDirectory, emptyFile );
MinijarFilter mf = new MinijarFilter( mavenProject, log );
@@ -84,14 +100,10 @@ public class MinijarFilterTest
public void testWithPomProject()
throws IOException
{
- ArgumentCaptor<CharSequence> logCaptor = ArgumentCaptor.forClass( CharSequence.class );
-
// project with pom packaging and no artifact.
- MavenProject mavenProject = mockProject( null );
+ MavenProject mavenProject = mockProject( outputDirectory, null );
mavenProject.setPackaging( "pom" );
- Log log = mock( Log.class );
-
MinijarFilter mf = new MinijarFilter( mavenProject, log );
mf.finished();
@@ -105,7 +117,7 @@ public class MinijarFilterTest
}
- private MavenProject mockProject( File file )
+ private MavenProject mockProject( File outputDirectory, File file, String... classPathElements )
{
MavenProject mavenProject = mock( MavenProject.class );
@@ -129,17 +141,29 @@ public class MinijarFilterTest
when( mavenProject.getArtifact().getFile() ).thenReturn( file );
- return mavenProject;
+ Build build = new Build();
+ build.setOutputDirectory( outputDirectory.toString() );
+
+ List<String> classpath = new ArrayList<>();
+ classpath.add( outputDirectory.toString() );
+ if ( file != null )
+ {
+ classpath.add(file.toString());
+ }
+ classpath.addAll( Arrays.asList( classPathElements ) );
+ when( mavenProject.getBuild() ).thenReturn( build );
+ try {
+ when(mavenProject.getRuntimeClasspathElements()).thenReturn(classpath);
+ } catch (DependencyResolutionRequiredException e) {
+ fail("Encountered unexpected exception: " + e.getClass().getSimpleName() + ": " + e.getMessage());
+ }
+ return mavenProject;
}
@Test
public void finsishedShouldProduceMessageForClassesTotalNonZero()
{
- ArgumentCaptor<CharSequence> logCaptor = ArgumentCaptor.forClass( CharSequence.class );
-
- Log log = mock( Log.class );
-
MinijarFilter m = new MinijarFilter( 1, 50, log );
m.finished();
@@ -153,10 +177,6 @@ public class MinijarFilterTest
@Test
public void finishedShouldProduceMessageForClassesTotalZero()
{
- ArgumentCaptor<CharSequence> logCaptor = ArgumentCaptor.forClass( CharSequence.class );
-
- Log log = mock( Log.class );
-
MinijarFilter m = new MinijarFilter( 0, 0, log );
m.finished();
@@ -166,4 +186,24 @@ public class MinijarFilterTest
assertEquals( "Minimized 0 -> 0", logCaptor.getValue() );
}
+
+ /**
+ * Verify that directories are ignored when scanning the classpath for JARs containing services,
+ * but warnings are logged instead
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/MSHADE-366">MSHADE-366</a>
+ */
+ @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 + "' (" ) );
+ }
+
}
=====================================
src/test/java/org/apache/maven/plugins/shade/resource/ServiceResourceTransformerTest.java
=====================================
@@ -121,7 +121,7 @@ public class ServiceResourceTransformerTest {
assertNotNull( jarEntry );
try ( InputStream entryStream = jarFile.getInputStream( jarEntry ) ) {
String xformedContent = IOUtils.toString( entryStream, StandardCharsets.UTF_8);
- assertEquals( contentShaded + contentShaded, xformedContent );
+ assertEquals( contentShaded, xformedContent );
} finally {
jarFile.close();
}
View it on GitLab: https://salsa.debian.org/java-team/maven-shade-plugin/-/commit/9cff8cb2abd4c77944a27bc40bca22d7ebbebe51
--
View it on GitLab: https://salsa.debian.org/java-team/maven-shade-plugin/-/commit/9cff8cb2abd4c77944a27bc40bca22d7ebbebe51
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20230106/5cbc6e16/attachment.htm>
More information about the pkg-java-commits
mailing list