[Git][java-team/maven-shade-plugin][master] 3 commits: New upstream version 3.4.1

Emmanuel Bourg (@ebourg) gitlab at salsa.debian.org
Fri Jan 6 12:56:04 GMT 2023



Emmanuel Bourg pushed to branch master 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
- - - - -
59e29a4f by Emmanuel Bourg at 2023-01-06T13:52:27+01:00
Update upstream source from tag 'upstream/3.4.1'

Update to upstream version '3.4.1'
with Debian dir 1c32d3e502f605908cfe77624cb348881dbd7f66
- - - - -
c9ee8634 by Emmanuel Bourg at 2023-01-06T13:55:24+01:00
New upstream release (3.4.1)

- - - - -


14 changed files:

- debian/changelog
- 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:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+maven-shade-plugin (3.4.1-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release
+
+ -- Emmanuel Bourg <ebourg at apache.org>  Fri, 06 Jan 2023 13:55:04 +0100
+
 maven-shade-plugin (3.4.0-1) unstable; urgency=medium
 
   * Team upload.


=====================================
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/-/compare/86f5d89bef99e945904e1e59c5db5bbe00d203d9...c9ee8634d739a6fd903bdefc78ae2b102eb07625

-- 
View it on GitLab: https://salsa.debian.org/java-team/maven-shade-plugin/-/compare/86f5d89bef99e945904e1e59c5db5bbe00d203d9...c9ee8634d739a6fd903bdefc78ae2b102eb07625
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/86ad6cdc/attachment.htm>


More information about the pkg-java-commits mailing list