[Git][java-team/maven-shared-jar][master] 7 commits: New upstream version 3.1.0

Emmanuel Bourg (@ebourg) gitlab at salsa.debian.org
Thu Oct 24 15:08:31 BST 2024



Emmanuel Bourg pushed to branch master at Debian Java Maintainers / maven-shared-jar


Commits:
7c1d2350 by Emmanuel Bourg at 2024-10-24T15:57:51+02:00
New upstream version 3.1.0
- - - - -
278ba6e2 by Emmanuel Bourg at 2024-10-24T15:58:00+02:00
New upstream version 3.1.1
- - - - -
95057ae0 by Emmanuel Bourg at 2024-10-24T15:58:01+02:00
Update upstream source from tag 'upstream/3.1.1'

Update to upstream version '3.1.1'
with Debian dir 89914881f3e0ba18654957b74970e29430d791a6
- - - - -
b9925872 by Emmanuel Bourg at 2024-10-24T16:06:37+02:00
New dependency on libplexus-xml-java

- - - - -
3fff39b9 by Emmanuel Bourg at 2024-10-24T16:07:01+02:00
New dependency on libmodello-maven-plugin-java

- - - - -
68798e16 by Emmanuel Bourg at 2024-10-24T16:07:29+02:00
Updated the Maven rules

- - - - -
1a1ee8a4 by Emmanuel Bourg at 2024-10-24T16:08:02+02:00
Upload to unstable

- - - - -


20 changed files:

- debian/changelog
- debian/control
- debian/libmaven-shared-jar-java.poms
- debian/maven.rules
- pom.xml
- src/main/java/org/apache/maven/shared/jar/JarAnalyzer.java
- src/main/java/org/apache/maven/shared/jar/JarData.java
- src/main/java/org/apache/maven/shared/jar/classes/JarClassesAnalysis.java
- + src/main/java/org/apache/maven/shared/jar/classes/JarVersionedRuntime.java
- + src/main/java/org/apache/maven/shared/jar/classes/JarVersionedRuntimes.java
- src/main/java/org/apache/maven/shared/jar/identification/JarIdentificationAnalysis.java
- src/main/java/org/apache/maven/shared/jar/identification/exposers/TextFileExposer.java
- src/main/java/org/apache/maven/shared/jar/identification/exposers/TimestampExposer.java
- src/test/java/org/apache/maven/shared/jar/JarAnalyzerTest.java
- src/test/java/org/apache/maven/shared/jar/classes/JarClassesAnalyzerTest.java
- + src/test/resources/jars/module-info-only-test-0.0.1.jar
- + src/test/resources/jars/multi-release-resources-only-0.0.1.jar
- + src/test/resources/jars/multi-release-test-0.0.1.jar
- + src/test/resources/jars/multi-release-version-with-lower-jdk-revision-class-0.0.1.jar
- + src/test/resources/jars/tomcat-jni-9.0.75.jar


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,13 @@
+maven-shared-jar (3.1.1-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release
+    - New dependency on libplexus-xml-java
+    - New dependency on libmodello-maven-plugin-java
+    - Updated the Maven rules
+
+ -- Emmanuel Bourg <ebourg at apache.org>  Thu, 24 Oct 2024 16:07:56 +0200
+
 maven-shared-jar (3.0.0-1) unstable; urgency=medium
 
   * Team upload.


=====================================
debian/control
=====================================
@@ -10,10 +10,13 @@ Build-Depends:
  libbcel-java,
  libcommons-collections4-java,
  libeclipse-sisu-maven-plugin-java,
+ libmaven-parent-java,
  libmaven-shared-utils-java,
  libmaven3-core-java,
+ libmodello-maven-plugin-java,
  libplexus-digest-java,
  libplexus-testing-java,
+ libplexus-xml-java,
  maven-debian-helper (>= 1.4)
 Standards-Version: 4.7.0
 Vcs-Git: https://salsa.debian.org/java-team/maven-shared-jar.git


=====================================
debian/libmaven-shared-jar-java.poms
=====================================
@@ -25,4 +25,4 @@
 #   --site-xml=<location>: Optional, the location for site.xml if it needs to be installed.
 #     Empty by default. [mh_install]
 #
-pom.xml --no-parent --has-package-version
+pom.xml --has-package-version


=====================================
debian/maven.rules
=====================================
@@ -16,9 +16,5 @@
 # and version starting with 3., replacing the version with 3.x
 #   junit junit jar s/3\\..*/3.x/
 
-commons-collections commons-collections jar s/3\..*/3.x/ * *
-s/com.google.code.findbugs/org.apache.bcel/ s/bcel-findbugs/bcel/ jar s/.*/5.x/ * *
-org.apache.maven.shared maven-shared-jar jar s/.*/debian/ * *
-junit junit * s/.*/3.x/ * *
 org.apache.maven maven-* * s/.*/3.x/ * *
-org.codehaus.plexus plexus-container-default * s/.*/1.5.5/ * *
+org.apache.maven.shared maven-shared-components pom s/.*/debian/ * *


=====================================
pom.xml
=====================================
@@ -23,12 +23,12 @@
   <parent>
     <groupId>org.apache.maven.shared</groupId>
     <artifactId>maven-shared-components</artifactId>
-    <version>39</version>
+    <version>42</version>
     <relativePath />
   </parent>
 
   <artifactId>maven-shared-jar</artifactId>
-  <version>3.0.0</version>
+  <version>3.1.1</version>
 
   <name>Apache Maven JAR Utilities</name>
   <description>Utilities that help identify the contents of a JAR, including Java class analysis and Maven metadata analysis.</description>
@@ -36,7 +36,7 @@
   <scm>
     <connection>scm:git:https://gitbox.apache.org/repos/asf/maven-shared-jar.git</connection>
     <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/maven-shared-jar.git</developerConnection>
-    <tag>maven-shared-jar-3.0.0</tag>
+    <tag>maven-shared-jar-3.1.1</tag>
     <url>https://github.com/apache/maven-shared-jar/tree/${project.scm.tag}</url>
   </scm>
   <issueManagement>
@@ -56,10 +56,9 @@
 
   <properties>
     <javaVersion>8</javaVersion>
-    <mavenVersion>3.2.5</mavenVersion>
+    <mavenVersion>3.6.3</mavenVersion>
     <slf4jVersion>1.7.36</slf4jVersion>
-    <project.build.outputTimestamp>2023-04-15T15:52:19Z</project.build.outputTimestamp>
-    <junitVersion>5.9.2</junitVersion>
+    <project.build.outputTimestamp>2024-06-20T20:40:38Z</project.build.outputTimestamp>
   </properties>
 
   <dependencies>
@@ -88,18 +87,28 @@
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
-      <version>3.5.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-xml</artifactId>
     </dependency>
     <dependency>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
-      <version>1.15</version>
+      <version>1.17.0</version>
     </dependency>
 
     <dependency>
       <groupId>org.apache.bcel</groupId>
       <artifactId>bcel</artifactId>
-      <version>6.7.0</version>
+      <version>6.9.0</version>
+    </dependency>
+    <!-- Required by BCEL overwritten by maven-artifact -->
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
+      <version>3.14.0</version>
+      <scope>runtime</scope>
     </dependency>
 
     <dependency>
@@ -111,19 +120,17 @@
     <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-api</artifactId>
-      <version>${junitVersion}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-params</artifactId>
-      <version>${junitVersion}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-testing</artifactId>
-      <version>1.1.0</version>
+      <version>1.3.0</version>
       <scope>test</scope>
     </dependency>
     <dependency>


=====================================
src/main/java/org/apache/maven/shared/jar/JarAnalyzer.java
=====================================
@@ -151,9 +151,20 @@ public class JarAnalyzer {
      * @return the list of files found, in {@link java.util.jar.JarEntry} elements
      */
     public List<JarEntry> filterEntries(Pattern pattern) {
+        return filterEntries(pattern, getEntries());
+    }
+
+    /**
+     * Filter a list of JAR entries against the pattern.
+     *
+     * @param pattern the pattern to filter against.
+     * @param entryList the list of entries to filter from.
+     * @return the filtered list of JarEntry.
+     */
+    private List<JarEntry> filterEntries(Pattern pattern, List<JarEntry> entryList) {
         List<JarEntry> ret = new ArrayList<>();
 
-        for (JarEntry entry : getEntries()) {
+        for (JarEntry entry : entryList) {
             Matcher mat = pattern.matcher(entry.getName());
             if (mat.find()) {
                 ret.add(entry);
@@ -171,6 +182,18 @@ public class JarAnalyzer {
         return filterEntries(CLASS_FILTER);
     }
 
+    /**
+     * Get all the classes in the entry list.
+     *
+     * @param entryList the entry list.
+     * @return the filtered entry list.
+     *
+     * @since 3.1.0
+     */
+    public List<JarEntry> getClassEntries(List<JarEntry> entryList) {
+        return filterEntries(CLASS_FILTER, entryList);
+    }
+
     /**
      * Get all the Maven POM entries in the JAR.
      *


=====================================
src/main/java/org/apache/maven/shared/jar/JarData.java
=====================================
@@ -22,17 +22,21 @@ import java.io.File;
 import java.util.Collections;
 import java.util.List;
 import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
 import java.util.jar.JarEntry;
 import java.util.jar.Manifest;
 
 import org.apache.maven.shared.jar.classes.JarClasses;
+import org.apache.maven.shared.jar.classes.JarVersionedRuntimes;
 import org.apache.maven.shared.jar.identification.JarIdentification;
-import org.codehaus.plexus.util.StringUtils;
 
 /**
  * Class that contains details of a single JAR file and it's entries.
  */
 public final class JarData {
+
+    private static final Name ATTR_MULTI_RELEASE = new Attributes.Name("Multi-Release");
+
     /**
      * The JAR file.
      */
@@ -43,6 +47,11 @@ public final class JarData {
      */
     private final boolean aSealed;
 
+    /**
+     * Whether the JAR file is Multi-Release.
+     */
+    private boolean multiRelease;
+
     /**
      * The hashcode for the entire file's contents.
      */
@@ -68,11 +77,21 @@ public final class JarData {
      */
     private final List<JarEntry> entries;
 
+    /**
+     * The JAR entries of the root content, when it is a multi-release JAR
+     */
+    private List<JarEntry> rootEntries;
+
     /**
      * Information about the JAR's identifying features.
      */
     private JarIdentification jarIdentification;
 
+    /**
+     * Information about the JAR's Multi-Release entries
+     */
+    private JarVersionedRuntimes versionedRuntimes;
+
     /**
      * Constructor.
      *
@@ -87,20 +106,22 @@ public final class JarData {
 
         this.entries = Collections.unmodifiableList(entries);
 
-        boolean aSealed = false;
-        if (this.manifest != null) {
-            String sval = this.manifest.getMainAttributes().getValue(Attributes.Name.SEALED);
-            if (StringUtils.isNotEmpty(sval)) {
-                aSealed = "true".equalsIgnoreCase(sval.trim());
-            }
-        }
-        this.aSealed = aSealed;
+        this.aSealed = isAttributePresent(Attributes.Name.SEALED);
+        this.multiRelease = isAttributePresent(ATTR_MULTI_RELEASE);
     }
 
     public List<JarEntry> getEntries() {
         return entries;
     }
 
+    public List<JarEntry> getRootEntries() {
+        return rootEntries;
+    }
+
+    public void setRootEntries(List<JarEntry> rootEntries) {
+        this.rootEntries = rootEntries;
+    }
+
     public Manifest getManifest() {
         return manifest;
     }
@@ -113,6 +134,10 @@ public final class JarData {
         return aSealed;
     }
 
+    public boolean isMultiRelease() {
+        return multiRelease;
+    }
+
     public void setFileHash(String fileHash) {
         this.fileHash = fileHash;
     }
@@ -141,6 +166,10 @@ public final class JarData {
         return entries.size();
     }
 
+    public int getNumRootEntries() {
+        return rootEntries.size();
+    }
+
     public int getNumClasses() {
         return jarClasses.getClassNames().size();
     }
@@ -164,4 +193,20 @@ public final class JarData {
     public JarClasses getJarClasses() {
         return jarClasses;
     }
+
+    public void setVersionedRuntimes(JarVersionedRuntimes versionedRuntimes) {
+        this.versionedRuntimes = versionedRuntimes;
+    }
+
+    public JarVersionedRuntimes getVersionedRuntimes() {
+        return this.versionedRuntimes;
+    }
+
+    private boolean isAttributePresent(Attributes.Name attrName) {
+        if (this.manifest != null) {
+            String sval = this.manifest.getMainAttributes().getValue(attrName);
+            return sval != null && "true".equalsIgnoreCase(sval.trim());
+        }
+        return false;
+    }
 }


=====================================
src/main/java/org/apache/maven/shared/jar/classes/JarClassesAnalysis.java
=====================================
@@ -26,8 +26,13 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NavigableMap;
 import java.util.Optional;
+import java.util.TreeMap;
 import java.util.jar.JarEntry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import org.apache.bcel.classfile.ClassFormatException;
 import org.apache.bcel.classfile.ClassParser;
@@ -36,6 +41,7 @@ import org.apache.bcel.classfile.JavaClass;
 import org.apache.bcel.classfile.LineNumberTable;
 import org.apache.bcel.classfile.Method;
 import org.apache.maven.shared.jar.JarAnalyzer;
+import org.apache.maven.shared.jar.JarData;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,8 +57,17 @@ import org.slf4j.LoggerFactory;
 @Named
 @SuppressWarnings("checkstyle:MagicNumber")
 public class JarClassesAnalysis {
+
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
+    /**
+     * Constant representing the root content of a Multi-Release JAR file, thus outside of
+     * any given META-INF/versions/N/... entry.
+     */
+    private static final Integer ROOT = 0;
+
+    private static final Pattern ENTRY_FILTER_MULTI_RELEASE = Pattern.compile("^META-INF/versions/([1-9]\\d*)/.*$");
+
     private static final Map<Double, String> JAVA_CLASS_VERSIONS;
 
     static {
@@ -90,68 +105,134 @@ public class JarClassesAnalysis {
      * @return the details of the classes found
      */
     public JarClasses analyze(JarAnalyzer jarAnalyzer) {
-        JarClasses classes = jarAnalyzer.getJarData().getJarClasses();
+        JarData jarData = jarAnalyzer.getJarData();
+        JarClasses classes = jarData.getJarClasses();
         if (classes == null) {
-            String jarfilename = jarAnalyzer.getFile().getAbsolutePath();
-            classes = new JarClasses();
+            if (jarData.isMultiRelease()) {
+                classes = analyzeMultiRelease(jarAnalyzer);
+            } else {
+                classes = analyzeRoot(jarAnalyzer);
+            }
+        }
+        return classes;
+    }
+
+    private Integer jarEntryVersion(JarEntry entry) {
+        Matcher matcher = ENTRY_FILTER_MULTI_RELEASE.matcher(entry.getName());
+        if (matcher.matches()) {
+            return Integer.valueOf(matcher.group(1));
+        }
+        return ROOT;
+    }
 
-            List<JarEntry> classList = jarAnalyzer.getClassEntries();
+    private JarClasses analyzeMultiRelease(JarAnalyzer jarAnalyzer) {
+        String jarFilename = jarAnalyzer.getFile().getAbsolutePath();
 
-            classes.setDebugPresent(false);
+        Map<Integer, List<JarEntry>> mapEntries =
+                jarAnalyzer.getEntries().stream().collect(Collectors.groupingBy(this::jarEntryVersion));
 
-            double maxVersion = 0.0;
+        // ordered by increasing Java version
+        NavigableMap<Integer, JarVersionedRuntime> runtimeVersionsMap = new TreeMap<>();
 
-            for (JarEntry entry : classList) {
-                String classname = entry.getName();
+        for (Map.Entry<Integer, List<JarEntry>> mapEntry : mapEntries.entrySet()) {
+            Integer runtimeVersion = mapEntry.getKey();
+            List<JarEntry> runtimeVersionEntryList = mapEntry.getValue();
 
-                try {
-                    ClassParser classParser = new ClassParser(jarfilename, classname);
+            List<JarEntry> classList = jarAnalyzer.getClassEntries(runtimeVersionEntryList);
 
-                    JavaClass javaClass = classParser.parse();
+            JarClasses classes = analyze(jarFilename, classList);
 
-                    String classSignature = javaClass.getClassName();
+            runtimeVersionsMap.put(runtimeVersion, new JarVersionedRuntime(runtimeVersionEntryList, classes));
+        }
 
-                    if (!classes.isDebugPresent()) {
-                        if (hasDebugSymbols(javaClass)) {
-                            classes.setDebugPresent(true);
-                        }
-                    }
+        JarData jarData = jarAnalyzer.getJarData();
 
-                    double classVersion = javaClass.getMajor();
-                    if (javaClass.getMinor() > 0) {
-                        classVersion = classVersion + javaClass.getMinor() / 10.0;
-                    }
+        JarVersionedRuntime rootContentVersionedRuntime = runtimeVersionsMap.remove(ROOT);
+        jarData.setRootEntries(rootContentVersionedRuntime.getEntries());
+        JarClasses rootJarClasses = rootContentVersionedRuntime.getJarClasses();
+        jarData.setJarClasses(rootJarClasses);
 
-                    if (classVersion > maxVersion) {
-                        maxVersion = classVersion;
-                    }
+        jarData.setVersionedRuntimes(new JarVersionedRuntimes(runtimeVersionsMap));
 
-                    Method[] methods = javaClass.getMethods();
-                    for (Method method : methods) {
-                        classes.addMethod(classSignature + "." + method.getName() + method.getSignature());
-                    }
+        return rootJarClasses;
+    }
+
+    private JarClasses analyzeRoot(JarAnalyzer jarAnalyzer) {
+        String jarFilename = jarAnalyzer.getFile().getAbsolutePath();
+
+        List<JarEntry> classList = jarAnalyzer.getClassEntries();
 
-                    String classPackageName = javaClass.getPackageName();
+        JarClasses classes = analyze(jarFilename, classList);
 
-                    classes.addClassName(classSignature);
-                    classes.addPackage(classPackageName);
+        jarAnalyzer.getJarData().setJarClasses(classes);
+        return classes;
+    }
 
-                    ImportVisitor importVisitor = new ImportVisitor(javaClass);
-                    DescendingVisitor descVisitor = new DescendingVisitor(javaClass, importVisitor);
-                    javaClass.accept(descVisitor);
+    private JarClasses analyze(String jarFilename, List<JarEntry> classList) {
+        JarClasses classes = new JarClasses();
 
-                    classes.addImports(importVisitor.getImports());
-                } catch (ClassFormatException e) {
-                    logger.warn("Unable to process class " + classname + " in JarAnalyzer File " + jarfilename, e);
-                } catch (IOException e) {
-                    logger.warn("Unable to process JarAnalyzer File " + jarfilename, e);
+        classes.setDebugPresent(false);
+
+        double maxVersion = 0.0;
+        double moduleInfoVersion = 0.0;
+
+        for (JarEntry entry : classList) {
+            String classname = entry.getName();
+
+            try {
+                ClassParser classParser = new ClassParser(jarFilename, classname);
+
+                JavaClass javaClass = classParser.parse();
+
+                String classSignature = javaClass.getClassName();
+
+                if (!classes.isDebugPresent()) {
+                    if (hasDebugSymbols(javaClass)) {
+                        classes.setDebugPresent(true);
+                    }
                 }
-            }
 
-            Optional.ofNullable(JAVA_CLASS_VERSIONS.get(maxVersion)).ifPresent(classes::setJdkRevision);
+                double classVersion = javaClass.getMajor();
+                if (javaClass.getMinor() > 0) {
+                    classVersion = classVersion + javaClass.getMinor() / 10.0;
+                }
+
+                if ("module-info".equals(classSignature)) {
+                    // ignore the module-info.class for computing the maxVersion, since it will always be >= 9
+                    moduleInfoVersion = classVersion;
+                } else if (classVersion > maxVersion) {
+                    maxVersion = classVersion;
+                }
 
-            jarAnalyzer.getJarData().setJarClasses(classes);
+                Method[] methods = javaClass.getMethods();
+                for (Method method : methods) {
+                    classes.addMethod(classSignature + "." + method.getName() + method.getSignature());
+                }
+
+                String classPackageName = javaClass.getPackageName();
+
+                classes.addClassName(classSignature);
+                classes.addPackage(classPackageName);
+
+                ImportVisitor importVisitor = new ImportVisitor(javaClass);
+                DescendingVisitor descVisitor = new DescendingVisitor(javaClass, importVisitor);
+                javaClass.accept(descVisitor);
+
+                classes.addImports(importVisitor.getImports());
+            } catch (ClassFormatException e) {
+                logger.warn("Unable to process class " + classname + " in JarAnalyzer File " + jarFilename, e);
+            } catch (IOException e) {
+                logger.warn("Unable to process JarAnalyzer File " + jarFilename, e);
+            }
+        }
+
+        if (maxVersion == 0.0 && moduleInfoVersion > 0.0) {
+            // the one and only class file was module-info.class
+            maxVersion = moduleInfoVersion;
         }
+
+        Optional.ofNullable(JAVA_CLASS_VERSIONS.get(maxVersion)).ifPresent(classes::setJdkRevision);
+
         return classes;
     }
 


=====================================
src/main/java/org/apache/maven/shared/jar/classes/JarVersionedRuntime.java
=====================================
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+package org.apache.maven.shared.jar.classes;
+
+import java.util.List;
+import java.util.jar.JarEntry;
+
+public class JarVersionedRuntime {
+
+    /**
+     * The JAR entries.
+     */
+    private final List<JarEntry> entries;
+
+    private final JarClasses jarClasses;
+
+    public JarVersionedRuntime(List<JarEntry> entries, JarClasses jarClasses) {
+        this.entries = entries;
+        this.jarClasses = jarClasses;
+    }
+
+    /**
+     * @return the entries
+     */
+    public List<JarEntry> getEntries() {
+        return entries;
+    }
+
+    /**
+     * @return the jarClasses
+     */
+    public JarClasses getJarClasses() {
+        return jarClasses;
+    }
+
+    public int getNumEntries() {
+        return entries.size();
+    }
+}


=====================================
src/main/java/org/apache/maven/shared/jar/classes/JarVersionedRuntimes.java
=====================================
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+package org.apache.maven.shared.jar.classes;
+
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Gathered facts about the runtime versions contained within a Multi-Release JAR file.
+ *
+ * @see org.apache.maven.shared.jar.classes.JarClassesAnalysis#analyze(org.apache.maven.shared.jar.JarAnalyzer)
+ */
+public class JarVersionedRuntimes {
+
+    /**
+     * Information about the JAR's Multi-Release entries
+     */
+    private NavigableMap<Integer, JarVersionedRuntime> versionedRuntimeMap;
+
+    public JarVersionedRuntimes(NavigableMap<Integer, JarVersionedRuntime> versionedRuntimeMap) {
+        this.versionedRuntimeMap = versionedRuntimeMap;
+    }
+
+    /**
+     * @return the versionedRuntimeMap
+     */
+    public NavigableMap<Integer, JarVersionedRuntime> getVersionedRuntimeMap() {
+        return versionedRuntimeMap;
+    }
+
+    public JarVersionedRuntime getJarVersionedRuntime(Integer version) {
+        return versionedRuntimeMap.get(version);
+    }
+
+    /**
+     * Return the JarClasses associated to the release.
+     * @param version the release version.
+     * @return the JarClasses.
+     */
+    public JarClasses getJarClasses(Integer version) {
+        return versionedRuntimeMap.get(version).getJarClasses();
+    }
+
+    /**
+     * Get a set of release versions included in the JAR file.
+     * @return a set with the Java versions as Strings.
+     */
+    public Set<Integer> getRuntimeVersionsAsSet() {
+        return versionedRuntimeMap.keySet();
+    }
+
+    /**
+     * Return the highest the JarClasses of the Jdk version that would be executed if they would be executed on a JVM given by the release parameter.
+     * @param version the Jdk version number of the executing JVM.
+     * @return The fittest JarClasses object matching if found one, or null otherwise.
+     * @throws NullPointerException if release is null.
+     */
+    public JarVersionedRuntime getBestFitJarVersionedRuntime(Integer version) {
+        Objects.requireNonNull(version, "version cannot be null");
+        Entry<Integer, JarVersionedRuntime> entry = versionedRuntimeMap.floorEntry(version);
+        if (entry == null) {
+            return null;
+        }
+        return entry.getValue();
+    }
+
+    /**
+     * Return the highest the JarClasses of the Jdk version that would be executed if they would be executed given a System property.
+     * Example values: "java.version.specification" or "java.vm.specification.version".
+     * @param key the System property.
+     * @return The best fitting JarClasses object matching if found one, or null otherwise.
+     * @throws NullPointerException if key is null
+     * @throws IllegalStateException if system property value of key is null
+     * @throws IllegalStateException if system property cannot be converted to Integer
+     */
+    public JarVersionedRuntime getBestFitJarVersionedRuntimeBySystemProperty(String key) {
+        Objects.requireNonNull(key, "key cannot null");
+        String property = System.getProperty(key);
+        if (property == null) {
+            throw new IllegalStateException("The value of the system property '" + key + "' is null");
+        }
+        try {
+            Integer release = Integer.parseInt(property);
+            return getBestFitJarVersionedRuntime(release);
+        } catch (NumberFormatException e) {
+            throw new IllegalStateException(
+                    "The value of the system property '" + key + "' [" + property
+                            + "] cannot be converted to an Integer",
+                    e);
+        }
+    }
+}


=====================================
src/main/java/org/apache/maven/shared/jar/identification/JarIdentificationAnalysis.java
=====================================
@@ -105,7 +105,7 @@ public class JarIdentificationAnalysis {
 
         int size = Integer.MAX_VALUE;
         for (String val : list) {
-            if (StringUtils.isNotEmpty(val)) {
+            if (val != null && !val.isEmpty()) {
                 if (val.length() < size) {
                     smallest = val;
                     size = val.length();
@@ -120,7 +120,7 @@ public class JarIdentificationAnalysis {
         String largest = null;
         int size = Integer.MIN_VALUE;
         for (String val : list) {
-            if (StringUtils.isNotEmpty(val)) {
+            if (val != null && !val.isEmpty()) {
                 if (val.length() > size) {
                     largest = val;
                     size = val.length();


=====================================
src/main/java/org/apache/maven/shared/jar/identification/exposers/TextFileExposer.java
=====================================
@@ -32,7 +32,6 @@ import java.util.jar.JarEntry;
 import org.apache.maven.shared.jar.JarAnalyzer;
 import org.apache.maven.shared.jar.identification.JarIdentification;
 import org.apache.maven.shared.jar.identification.JarIdentificationExposer;
-import org.codehaus.plexus.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,7 +69,7 @@ public class TextFileExposer implements JarIdentificationExposer {
                     // TODO: maybe even for groupId entries.
 
                     logger.debug(line);
-                    if (StringUtils.isNotEmpty(line)) {
+                    if (line != null && !line.isEmpty()) {
                         textVersions.add(line);
                     }
                 } catch (IOException e) {


=====================================
src/main/java/org/apache/maven/shared/jar/identification/exposers/TimestampExposer.java
=====================================
@@ -32,7 +32,6 @@ import org.apache.commons.collections4.bag.HashBag;
 import org.apache.maven.shared.jar.JarAnalyzer;
 import org.apache.maven.shared.jar.identification.JarIdentification;
 import org.apache.maven.shared.jar.identification.JarIdentificationExposer;
-import org.codehaus.plexus.util.StringUtils;
 
 /**
  * Exposer that examines a a JAR and uses the most recent timestamp as a potential version.
@@ -61,7 +60,7 @@ public class TimestampExposer implements JarIdentificationExposer {
             }
         }
 
-        if (StringUtils.isNotEmpty(ts)) {
+        if (ts != null && !ts.isEmpty()) {
             identification.addVersion(ts);
         }
     }


=====================================
src/test/java/org/apache/maven/shared/jar/JarAnalyzerTest.java
=====================================
@@ -64,6 +64,18 @@ class JarAnalyzerTest extends AbstractJarAnalyzerTestCase {
         assertFalse(jarData.isSealed());
     }
 
+    @Test
+    void testMultiRelease() throws Exception {
+        JarData jarData = getJarData("multi-release-test-0.0.1.jar");
+        assertTrue(jarData.isMultiRelease());
+    }
+
+    @Test
+    void testNotMultiRelease() throws Exception {
+        JarData jarData = getJarData("codec.jar");
+        assertFalse(jarData.isMultiRelease());
+    }
+
     @Test
     void testMissingFile() {
         assertThrows(IOException.class, () -> new JarAnalyzer(new File("foo-bar-this-should-not-exist.jar")));


=====================================
src/test/java/org/apache/maven/shared/jar/classes/JarClassesAnalyzerTest.java
=====================================
@@ -21,9 +21,13 @@ package org.apache.maven.shared.jar.classes;
 import javax.inject.Inject;
 
 import java.io.File;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
 
 import org.apache.maven.shared.jar.AbstractJarAnalyzerTestCase;
 import org.apache.maven.shared.jar.JarAnalyzer;
+import org.apache.maven.shared.jar.JarData;
 import org.codehaus.plexus.testing.PlexusTest;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -34,6 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
  * JarAnalyzer Classes Test Case
@@ -137,6 +142,244 @@ class JarClassesAnalyzerTest extends AbstractJarAnalyzerTestCase {
         assertEquals(expectedRevision, jclass.getJdkRevision());
     }
 
+    @Test
+    void testAnalyzeJarWithModuleInfoClass() throws Exception {
+        JarData jarData = getJarData("tomcat-jni-9.0.75.jar");
+        JarClasses jclass = jarData.getJarClasses();
+        assertEquals("1.8", jclass.getJdkRevision());
+    }
+
+    @Test
+    void testAnalyzeJarWithOnlyModuleInfoClass() throws Exception {
+        JarData jarData = getJarData("module-info-only-test-0.0.1.jar");
+        assertEquals(10, jarData.getNumEntries());
+        // root level information
+        assertEquals(8, jarData.getNumRootEntries());
+        JarClasses jclass = jarData.getJarClasses();
+        assertTrue(jclass.getImports().isEmpty());
+        assertTrue(jclass.getPackages().isEmpty());
+        assertTrue(jclass.getClassNames().isEmpty());
+        assertTrue(jclass.getMethods().isEmpty());
+        assertNull(jclass.getJdkRevision());
+
+        JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
+        assertNotNull(jarVersionedRuntimes);
+        Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
+        assertNotNull(jarVersionedRuntimeMap);
+        assertEquals(1, jarVersionedRuntimeMap.size()); // 11
+
+        JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
+        JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
+        assertEquals("11", jarClasses11.getJdkRevision());
+        assertTrue(jarClasses11.getImports().isEmpty());
+        assertEquals(1, jarClasses11.getPackages().size());
+        assertEquals("", jarClasses11.getPackages().get(0));
+        assertEquals(1, jarClasses11.getClassNames().size());
+        assertTrue(jarClasses11.getMethods().isEmpty());
+        assertEquals(2, jarVersionedRuntime11.getNumEntries());
+        assertEntriesContains(jarVersionedRuntime11.getEntries(), "META-INF/versions/11/module-info.class");
+    }
+
+    @Test
+    void testAnalyzeMultiReleaseJarVersion() throws Exception {
+        JarData jarData = getJarData("multi-release-test-0.0.1.jar");
+        assertEquals(37, jarData.getNumEntries());
+        // root level information
+        assertEquals(17, jarData.getNumRootEntries());
+        JarClasses jclass = jarData.getJarClasses();
+        assertEquals("1.8", jclass.getJdkRevision());
+        assertFalse(jclass.getImports().isEmpty());
+        assertEquals(1, jclass.getPackages().size());
+        assertEquals(1, jclass.getClassNames().size());
+        assertFalse(jclass.getMethods().isEmpty());
+        assertEntriesContains(jarData.getEntries(), "resource.txt");
+
+        JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
+        assertNotNull(jarVersionedRuntimes);
+        Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
+        assertNotNull(jarVersionedRuntimeMap);
+        assertEquals(2, jarVersionedRuntimeMap.size()); // 9 and 11
+
+        JarVersionedRuntime jarVersionedRuntime9 = jarVersionedRuntimes.getJarVersionedRuntime(9);
+        JarClasses jarClasses9 = jarVersionedRuntime9.getJarClasses();
+        assertEquals("9", jarClasses9.getJdkRevision());
+        assertFalse(jarClasses9.getImports().isEmpty());
+        assertEquals(1, jarClasses9.getPackages().size());
+        assertEquals(1, jarClasses9.getClassNames().size());
+        assertFalse(jarClasses9.getMethods().isEmpty());
+        assertEquals(10, jarVersionedRuntime9.getNumEntries());
+        assertEntriesContains(jarVersionedRuntime9.getEntries(), "META-INF/versions/9/resource.txt");
+
+        JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
+        JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
+        assertEquals("11", jarClasses11.getJdkRevision());
+        assertFalse(jarClasses11.getImports().isEmpty());
+        assertEquals(1, jarClasses11.getPackages().size());
+        assertEquals(1, jarClasses11.getClassNames().size());
+        assertFalse(jarClasses11.getMethods().isEmpty());
+        assertEquals(10, jarVersionedRuntime11.getNumEntries());
+        assertEntriesContains(jarVersionedRuntime11.getEntries(), "META-INF/versions/11/resource.txt");
+
+        // test ordering
+        assertEquals("[9, 11]", jarVersionedRuntimeMap.keySet().toString());
+
+        // test best fit
+        try {
+            jarVersionedRuntimes.getBestFitJarVersionedRuntime(null);
+            fail("It should throw an NPE");
+        } catch (NullPointerException e) {
+            assertTrue(true);
+        }
+        assertNull(jarVersionedRuntimes.getBestFitJarVersionedRuntime(8)); // unreal value but good just for testing
+        assertEquals(
+                "9",
+                jarVersionedRuntimes
+                        .getBestFitJarVersionedRuntime(9)
+                        .getJarClasses()
+                        .getJdkRevision());
+        assertEquals(
+                "9",
+                jarVersionedRuntimes
+                        .getBestFitJarVersionedRuntime(10)
+                        .getJarClasses()
+                        .getJdkRevision());
+        assertEquals(
+                "11",
+                jarVersionedRuntimes
+                        .getBestFitJarVersionedRuntime(11)
+                        .getJarClasses()
+                        .getJdkRevision());
+        assertEquals(
+                "11",
+                jarVersionedRuntimes
+                        .getBestFitJarVersionedRuntime(20)
+                        .getJarClasses()
+                        .getJdkRevision());
+
+        try {
+            jarVersionedRuntimes.getBestFitJarVersionedRuntimeBySystemProperty(null);
+            fail("It should throw an NPE");
+        } catch (NullPointerException e) {
+            assertTrue(true);
+        }
+
+        try {
+            getBestFitReleaseBySystemProperty(jarVersionedRuntimes, null);
+            fail("It should throw an NPE");
+        } catch (NullPointerException e) {
+            assertTrue(true);
+        }
+
+        try {
+            getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "xxx");
+            fail("It should throw an ISE");
+        } catch (IllegalStateException e) {
+            assertTrue(true);
+        }
+
+        assertNull(getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "8"));
+        assertEquals(
+                "9",
+                getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "9")
+                        .getJarClasses()
+                        .getJdkRevision());
+        assertEquals(
+                "9",
+                getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "10")
+                        .getJarClasses()
+                        .getJdkRevision());
+        assertEquals(
+                "11",
+                getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "11")
+                        .getJarClasses()
+                        .getJdkRevision());
+        assertEquals(
+                "11",
+                getBestFitReleaseBySystemProperty(jarVersionedRuntimes, "20")
+                        .getJarClasses()
+                        .getJdkRevision());
+    }
+
+    /**
+     * Exposes issue MSHARED-1413
+     */
+    @Test
+    public void testAnalyzeMultiReleaseJarWithVersion11HasALowerJdkRevisionClass() {
+        try {
+            // Version 11 has one class compiled to target Java 1.8
+            JarData jarData = getJarData("multi-release-version-with-lower-jdk-revision-class-0.0.1.jar");
+            JarClasses jclass = jarData.getJarClasses();
+
+            assertNull(jclass.getJdkRevision());
+
+            JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
+            assertNotNull(jarVersionedRuntimes);
+            Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
+            assertNotNull(jarVersionedRuntimeMap);
+            assertEquals(1, jarVersionedRuntimeMap.size()); // 11
+
+            JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
+            JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
+            assertEquals("1.8", jarClasses11.getJdkRevision());
+        } catch (Exception e) {
+            fail("It should not raise an exception", e);
+        }
+    }
+
+    /**
+     * Ensures no exceptions are raised when versioned content does not contain classes (just resources)
+     */
+    @Test
+    public void testAnalyzeMultiReleaseJarResourcesOnly() {
+        try {
+            JarData jarData = getJarData("multi-release-resources-only-0.0.1.jar");
+            JarClasses jclass = jarData.getJarClasses();
+
+            assertEquals("1.8", jclass.getJdkRevision());
+
+            JarVersionedRuntimes jarVersionedRuntimes = jarData.getVersionedRuntimes();
+            assertNotNull(jarVersionedRuntimes);
+            Map<Integer, JarVersionedRuntime> jarVersionedRuntimeMap = jarVersionedRuntimes.getVersionedRuntimeMap();
+            assertNotNull(jarVersionedRuntimeMap);
+            assertEquals(2, jarVersionedRuntimeMap.size()); // 9 and 11
+
+            JarVersionedRuntime jarVersionedRuntime9 = jarVersionedRuntimes.getJarVersionedRuntime(9);
+            JarClasses jarClasses9 = jarVersionedRuntime9.getJarClasses();
+            // no classes found
+            assertNull(jarClasses9.getJdkRevision());
+
+            JarVersionedRuntime jarVersionedRuntime11 = jarVersionedRuntimes.getJarVersionedRuntime(11);
+            JarClasses jarClasses11 = jarVersionedRuntime11.getJarClasses();
+            // no classes found
+            assertNull(jarClasses11.getJdkRevision());
+        } catch (Exception e) {
+            fail("It should not raise an exception", e);
+        }
+    }
+
+    private void assertEntriesContains(List<JarEntry> list, final String entryToFind) {
+        assertTrue(list.stream().anyMatch(entry -> entry.getName().equals(entryToFind)));
+    }
+
+    private JarVersionedRuntime getBestFitReleaseBySystemProperty(
+            JarVersionedRuntimes jarVersionedRuntimes, String value) {
+        String key = "maven.shared.jar.test.vm";
+        System.setProperty(key, value);
+        return jarVersionedRuntimes.getBestFitJarVersionedRuntimeBySystemProperty(key);
+    }
+
+    private JarData getJarData(String filename) throws Exception {
+        File file = getSampleJar(filename);
+
+        JarAnalyzer jarAnalyzer = new JarAnalyzer(file);
+        JarClasses jclass = analyzer.analyze(jarAnalyzer);
+        JarData jarData = jarAnalyzer.getJarData();
+        assertNotNull(jclass, "JarClasses");
+        assertNotNull(jarData, "JarData");
+
+        return jarData;
+    }
+
     private JarClasses getJarClasses(String filename) throws Exception {
         File file = getSampleJar(filename);
 


=====================================
src/test/resources/jars/module-info-only-test-0.0.1.jar
=====================================
Binary files /dev/null and b/src/test/resources/jars/module-info-only-test-0.0.1.jar differ


=====================================
src/test/resources/jars/multi-release-resources-only-0.0.1.jar
=====================================
Binary files /dev/null and b/src/test/resources/jars/multi-release-resources-only-0.0.1.jar differ


=====================================
src/test/resources/jars/multi-release-test-0.0.1.jar
=====================================
Binary files /dev/null and b/src/test/resources/jars/multi-release-test-0.0.1.jar differ


=====================================
src/test/resources/jars/multi-release-version-with-lower-jdk-revision-class-0.0.1.jar
=====================================
Binary files /dev/null and b/src/test/resources/jars/multi-release-version-with-lower-jdk-revision-class-0.0.1.jar differ


=====================================
src/test/resources/jars/tomcat-jni-9.0.75.jar
=====================================
Binary files /dev/null and b/src/test/resources/jars/tomcat-jni-9.0.75.jar differ



View it on GitLab: https://salsa.debian.org/java-team/maven-shared-jar/-/compare/3528475ed2af16c9a17233605f91b51de6b72afd...1a1ee8a4bb47b858dedf8fc9e710f62d31857efc

-- 
View it on GitLab: https://salsa.debian.org/java-team/maven-shared-jar/-/compare/3528475ed2af16c9a17233605f91b51de6b72afd...1a1ee8a4bb47b858dedf8fc9e710f62d31857efc
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/20241024/fc68dd95/attachment.htm>


More information about the pkg-java-commits mailing list