diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 00db9fce4ce..3ccd694972a 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -121,6 +121,9 @@ jobs:
##############
- name: java
run: mvn --batch-mode --update-snapshots verify -Pprintingbundle
+ # The printing bundle is unpacked over the WAR, so the two are checked together.
+ - name: "checking duplicated libraries"
+ run: python3 utility/build/check_duplicate_jars.py product/target/mapstore.war java/printing/target/mapstore-printing.zip
build:
runs-on: ubuntu-latest
strategy:
diff --git a/binary/bin-war/pom.xml b/binary/bin-war/pom.xml
index 710d1a6fa87..55cf7c10306 100644
--- a/binary/bin-war/pom.xml
+++ b/binary/bin-war/pom.xml
@@ -36,17 +36,6 @@
maven-war-plugin
${maven-war-plugin.version}
-
- WEB-INF/lib/*spring*5.3.18*.jar,
- WEB-INF/lib/json-lib-2.4-jdk15.jar,
- WEB-INF/lib/msg-simple-1.1.jar,
- WEB-INF/lib/btf-1.2.jar,
- WEB-INF/lib/commons-io-2.1.jar,
- WEB-INF/lib/commons-beanutils-1.8.0.jar,
- WEB-INF/lib/commons-logging-1.1.1.jar,
- WEB-INF/lib/jackson-coreutils-1.6.jar,
- WEB-INF/lib/stax-ex-1.8.jar
-
it.geosolutions.mapstore
diff --git a/docs/developer-guide/mapstore-migration-guide.md b/docs/developer-guide/mapstore-migration-guide.md
index 2e470429221..8926906f05a 100644
--- a/docs/developer-guide/mapstore-migration-guide.md
+++ b/docs/developer-guide/mapstore-migration-guide.md
@@ -124,7 +124,7 @@ The following changes must be applied to your project (see the updated [project
- 9.0.116
- 2.16.1
+ 10.1.55
-+ 2.19.4
++ 2.22.3
- 5.3.39
- 5.7.13
@@ -236,6 +236,178 @@ Copy the `logging.properties` file from the MapStore repository (`product/cargo/
**CI/CD**: update your build infrastructure to use JDK 17 as the build JDK.
+### Duplicated libraries in the WAR
+
+The GeoStore and http-proxy WARs are added to your project as *overlays*: their `WEB-INF/lib` folder is copied as it is into your WAR. Some of those libraries (Jackson, Log4j, SLF4J, Micrometer, commons-io, ...) are also brought by MapStore, often in a different version, so the final WAR contains two versions of the same library, and which one is loaded depends on the servlet container.
+
+The steps below remove the duplicates. They change only two files of your project: `pom.xml` and `web/pom.xml`. The [project templates](https://github.com/geosolutions-it/MapStore2/tree/master/project/standard/templates) contain the final result, if you want to compare.
+
+#### 1. `pom.xml` (in the root folder of your project)
+
+**1.1** In the `` section, add these two lines right after the `jackson.version` line (updated to `2.22.3` in the previous section):
+
+```diff
+ 2.22.3
++ 32.0.0-jre
++ 3.0.2
+```
+
+**1.2** In the `` section, find the `jackson-bom` dependency and add the two dependencies below right after it (after its closing `` tag):
+
+```diff
+
+ com.fasterxml.jackson
+ jackson-bom
+ ${jackson.version}
+ pom
+ import
+
++
++ com.google.guava
++ guava
++ ${guava.version}
++
++
++ com.google.code.findbugs
++ jsr305
++ ${jsr305.version}
++
+```
+
+#### 2. `web/pom.xml`
+
+**2.1** The servlet API is provided by Tomcat and must not be packaged in the WAR. In the main `` section (at the top of the file), find the `jakarta.servlet-api` dependency and add the `` line:
+
+```diff
+
+ jakarta.servlet
+ jakarta.servlet-api
+ ${jakarta.servlet-api.version}
++ provided
+
+```
+
+**2.2** Find the `maven-war-plugin` plugin. Inside its ``, delete the whole `` block: it lists versions that are not used anymore.
+
+```diff
+
+-
+- WEB-INF/lib/*spring*5.3.18*.jar,
+- WEB-INF/lib/json-lib-2.4-jdk15.jar,
+- WEB-INF/lib/msg-simple-1.1.jar,
+- WEB-INF/lib/btf-1.2.jar,
+- WEB-INF/lib/commons-io-2.1.jar,
+- WEB-INF/lib/commons-beanutils-1.8.0.jar,
+- WEB-INF/lib/commons-logging-1.1.1.jar,
+- WEB-INF/lib/jackson-coreutils-1.6.jar,
+- WEB-INF/lib/stax-ex-1.8.jar
+-
+
+```
+
+**2.3** In the same ``, inside ``, add an `` block to each of the two overlays, right after their `` line. If an overlay already has an `` block, keep the `` lines it has and add the new ones to it.
+
+```diff
+
+
+ it.geosolutions.geostore
+ geostore-webapp
++
++ WEB-INF/lib/commons-io-*.jar
++ WEB-INF/lib/jackson-annotations-*.jar
++ WEB-INF/lib/jackson-core-*.jar
++ WEB-INF/lib/jackson-databind-*.jar
++ WEB-INF/lib/log4j-api-*.jar
++ WEB-INF/lib/log4j-core-*.jar
++ WEB-INF/lib/log4j-slf4j-impl-*.jar
++ WEB-INF/lib/slf4j-api-*.jar
++ WEB-INF/lib/micrometer-commons-*.jar
++ WEB-INF/lib/micrometer-observation-*.jar
++ WEB-INF/lib/asm-2.*.jar
++
+
+
+ proxy
+ http_proxy
++
++ WEB-INF/lib/commons-io-*.jar
++ WEB-INF/lib/log4j-api-*.jar
++ WEB-INF/lib/log4j-core-*.jar
++ WEB-INF/lib/log4j-slf4j2-impl-*.jar
++ WEB-INF/lib/slf4j-api-*.jar
++ WEB-INF/lib/commons-codec-*.jar
++ WEB-INF/lib/jetty-*.jar
++
+
+
+```
+
+**2.4** Find the `` with `printing` (near the end of the file). Inside it there is a `` section with another `jakarta.servlet-api` dependency: add the same `` line as in step 2.1.
+
+```diff
+
+ jakarta.servlet
+ jakarta.servlet-api
+ ${jakarta.servlet-api.version}
++ provided
+
+```
+
+**2.5** In the same `printing` profile, find the `print-lib` dependency. In its ``:
+
+- delete the `commons-codec` exclusion:
+
+```diff
+-
+- commons-codec
+- commons-codec
+-
+```
+
+- add these five exclusions before the closing `` tag (JAXB is already shipped by GeoStore, HTTP Client 5 by http-proxy):
+
+```diff
++
++ org.glassfish.jaxb
++ jaxb-runtime
++
++
++ jakarta.xml.bind
++ jakarta.xml.bind-api
++
++
++ org.apache.httpcomponents.client5
++ httpclient5
++
++
++ org.apache.httpcomponents.core5
++ httpcore5
++
++
++ org.apache.httpcomponents.core5
++ httpcore5-h2
++
+
+```
+
+#### 3. Check the result
+
+From the root folder of your project, build the WAR and run the check script shipped with MapStore (Python 3 is required):
+
+```bash
+mvn clean install
+python3 MapStore2/utility/build/check_duplicate_jars.py web/target/*.war
+```
+
+If you use the printing module, repeat the check with the `printing` profile:
+
+```bash
+mvn clean install -Pprinting
+python3 MapStore2/utility/build/check_duplicate_jars.py web/target/*.war
+```
+
+The script must print `No duplicated libraries`. Otherwise it lists the libraries found in more than one version: if they come from a dependency you added to your project, exclude the older version.
+
### New `viewFramingFilter` in `web.xml`
Add the following filter to your project's `web/src/main/webapp/WEB-INF/web.xml`, mapped on `/index.html` (see the updated [project template](https://github.com/geosolutions-it/MapStore2/blob/master/project/standard/templates/web/src/main/webapp/WEB-INF/web.xml) for reference). It relies on `org.apache.catalina.filters.HttpHeaderSecurityFilter`, already available with the Tomcat 10.1 dependency required by the upgrade above:
diff --git a/java/printing/assembly/mapstore-printing.xml b/java/printing/assembly/mapstore-printing.xml
index cdd424ee7dc..a47df766eb7 100644
--- a/java/printing/assembly/mapstore-printing.xml
+++ b/java/printing/assembly/mapstore-printing.xml
@@ -13,67 +13,39 @@
${project.build.directory}/mapstore-printing/WEB-INF
**/*.xml
- **/print-lib*.jar
- **/aircompressor-*.jar
- **/apiguardian-api-*.jar
- **/avalon-*.jar
- **/batik-*.jar
- **/bcmail-*.jar
- **/bcprov-*.jar
- **/bigint-*.jar
- **/commons-lang*.jar
- **/commons-httpclient-*.jar
- **/commons-io-*.jar
- **/commons-text-*.jar
- **/disruptor-*.jar
- **/ejml-*.jar
- **/fontbox-*.jar
- **/fop-*.jar
- **/GeographicLib-*.jar
- **/gt-*.jar
- **/guava-*.jar
- **/httpclient-*.jar
- **/httpcore-*.jar
- **/imageio-*.jar
- **/indriya-*.jar
- **/hsqldb-*.jar
- **/itext-*.jar
- **/itextpdf-*.jar
- **/jai*.jar
- **/jaxb-*.jar
- **/jaxb-api-*.jar
- **/jdom2-*.jar
- **/jempbox-*.jar
- **/jgridshift-*.jar
- **/json-20*.jar
- **/jackson-dataformat-yaml-*.jar
- **/javax.inject-*.jar
- **/jts-core-*.jar
- **/jt-*.jar
- **/jyaml-*.jar
- **/metrics-*.jar
- **/net.opengis.ows-*.jar
- **/org.eclipse.emf.common-*.jar
- **/org.eclipse.emf.ecore-*.jar
- **/org.eclipse.emf.ecore.xmi-*.jar
- **/org.w3.xlink-*.jar
- **/openpdf-*.jar
- **/pdfbox-*.jar
- **/profiler-*.jar
- **/re2j-*.jar
- **/si-*.jar
- **/slf4j-api-*.jar
- **/snakeyaml-*.jar
- **/systems-common-*.jar
- **/txw2-*.jar
- **/unit-*.jar
- **/uom-*.jar
- xalan-*.jar
- **/xml-apis-*.jar
- **/xml-commons-resolver*.jar
- **/xmlgraphics-commons-*.jar
- **/xercesImpl-*.jar
+
+
+
+ /WEB-INF/lib
+
+ ${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}
+ false
+ runtime
+
+ com.fasterxml.jackson.core:*
+ com.google.code.findbugs:*
+ com.google.errorprone:*
+ com.google.guava:*
+ com.google.j2objc:*
+ commons-io:*
+ commons-logging:*
+ io.micrometer:*
+ jakarta.activation:*
+ jakarta.annotation:*
+ jakarta.inject:*
+ org.apache.commons:commons-lang3
+ org.apache.commons:commons-text
+ org.apache.logging.log4j:*
+ org.jspecify:*
+ org.slf4j:*
+ org.springframework:*
+
+
+
diff --git a/java/printing/pom.xml b/java/printing/pom.xml
index 50fdb6aa709..a980d6f4413 100644
--- a/java/printing/pom.xml
+++ b/java/printing/pom.xml
@@ -14,41 +14,16 @@
http://www.geosolutionsgroup.com
-
+
org.mapfish.print
print-lib
-
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.httpcomponents
- httpcore
-
-
mapstore-printing
-
- maven-dependency-plugin
-
-
- process-classes
-
- copy-dependencies
-
-
- ${project.build.directory}/mapstore-printing/WEB-INF/lib
-
-
-
-
maven-resources-plugin
${maven-resources-plugin.version}
diff --git a/java/web/pom.xml b/java/web/pom.xml
index 6f2053debeb..5b2dd757eca 100644
--- a/java/web/pom.xml
+++ b/java/web/pom.xml
@@ -67,26 +67,47 @@
maven-war-plugin
${maven-war-plugin.version}
-
- WEB-INF/lib/*spring*5.3.18*.jar,
- WEB-INF/lib/json-lib-2.4-jdk15.jar,
- WEB-INF/lib/msg-simple-1.1.jar,
- WEB-INF/lib/btf-1.2.jar,
- WEB-INF/lib/commons-io-2.1.jar,
- WEB-INF/lib/commons-beanutils-1.8.0.jar,
- WEB-INF/lib/commons-logging-1.1.1.jar,
- WEB-INF/lib/jackson-coreutils-1.6.jar,
- WEB-INF/lib/stax-ex-1.8.jar
-
+
it.geosolutions.geostore
geostore-webapp
- WEB-INF/classes/geostore-spring-security.xml
+
+ WEB-INF/classes/geostore-spring-security.xml
+ WEB-INF/lib/commons-io-*.jar
+ WEB-INF/lib/jackson-annotations-*.jar
+ WEB-INF/lib/jackson-core-*.jar
+ WEB-INF/lib/jackson-databind-*.jar
+ WEB-INF/lib/log4j-api-*.jar
+ WEB-INF/lib/log4j-core-*.jar
+ WEB-INF/lib/log4j-slf4j-impl-*.jar
+ WEB-INF/lib/slf4j-api-*.jar
+
+ WEB-INF/lib/micrometer-commons-*.jar
+ WEB-INF/lib/micrometer-observation-*.jar
+
+ WEB-INF/lib/asm-2.*.jar
+
proxy
http_proxy
+
+ WEB-INF/lib/commons-io-*.jar
+ WEB-INF/lib/log4j-api-*.jar
+ WEB-INF/lib/log4j-core-*.jar
+
+ WEB-INF/lib/log4j-slf4j2-impl-*.jar
+ WEB-INF/lib/slf4j-api-*.jar
+
+ WEB-INF/lib/commons-codec-*.jar
+
+ WEB-INF/lib/jetty-*.jar
+
diff --git a/pom.xml b/pom.xml
index 1872c59eb22..e8dcecf2e8a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,12 +26,13 @@
3.2.2
- 2.14.0
+ 2.22.0
1.2
6.0.0
4.0.3
2.4.2-geoserver
1.13
+ 3.0.2
4.13.2
2.26.1
2.1.3
@@ -88,11 +89,12 @@
pom
import
-
+
com.google.guava
guava
+ ${guava.version}
org.checkerframework
@@ -100,6 +102,11 @@
+
+ com.google.code.findbugs
+ jsr305
+ ${jsr305.version}
+
@@ -147,10 +154,12 @@
${mockito-core.version}
test
+
jakarta.servlet
jakarta.servlet-api
${jakarta.servlet-api.version}
+ provided
org.apache.commons
@@ -248,55 +257,14 @@
-
+
it.geosolutions.geostore
geostore-webapp
${geostore-webapp.version}
war
runtime
-
-
- org.springframework
- spring-aop
-
-
- org.springframework
- spring-beans
-
-
- org.springframework
- spring-context
-
-
- org.springframework
- spring-core
-
-
- org.springframework
- spring-expression
-
-
- org.springframework
- spring-jcl
-
-
- org.springframework
- spring-jdbc
-
-
- org.springframework
- spring-orm
-
-
- org.springframework
- spring-tx
-
-
- org.springframework
- spring-web
-
-
@@ -306,33 +274,20 @@
war
runtime
-
+
org.mapfish.print
print-lib
${print-lib.version}
-
-
- javax.xml.bind
- jaxb-api
-
-
- com.sun.xml.bind
- jaxb-impl
-
org.glassfish.jaxb
jaxb-runtime
- org.glassfish.jaxb
- txw2
-
-
-
- com.sun.xml.fastinfoset
- FastInfoset
+ jakarta.xml.bind
+ jakarta.xml.bind-api
@@ -344,8 +299,16 @@
httpcore
- jakarta.xml.bind
- jakarta.xml.bind-api
+ org.apache.httpcomponents.client5
+ httpclient5
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+
+
+ org.apache.httpcomponents.core5
+ httpcore5-h2
@@ -372,12 +335,6 @@
org.apache.logging.log4j
log4j-slf4j-impl
${log4j.version}
-
-
- org.slf4j
- slf4j-api
-
-
@@ -488,28 +445,12 @@
maven-antrun-plugin
3.1.0
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ ${maven-war-plugin.version}
+
-
-
-
- org.apache.maven.plugins
- maven-war-plugin
- 3.4.0
-
-
- WEB-INF/lib/*spring*5.3.18*.jar,
- WEB-INF/lib/json-lib-2.4-jdk15.jar,
- WEB-INF/lib/msg-simple-1.1.jar,
- WEB-INF/lib/btf-1.2.jar,
- WEB-INF/lib/commons-io-2.1.jar,
- WEB-INF/lib/commons-beanutils-1.8.0.jar,
- WEB-INF/lib/commons-logging-1.1.1.jar,
- WEB-INF/lib/jackson-coreutils-1.6.jar,
- WEB-INF/lib/stax-ex-1.8.jar
-
-
-
-
diff --git a/product/pom.xml b/product/pom.xml
index b18c751322d..9d17544a56a 100644
--- a/product/pom.xml
+++ b/product/pom.xml
@@ -251,17 +251,6 @@
maven-war-plugin
${maven-war-plugin.version}
-
- WEB-INF/lib/*spring*5.3.18*.jar,
- WEB-INF/lib/json-lib-2.4-jdk15.jar,
- WEB-INF/lib/msg-simple-1.1.jar,
- WEB-INF/lib/btf-1.2.jar,
- WEB-INF/lib/commons-io-2.1.jar,
- WEB-INF/lib/commons-beanutils-1.8.0.jar,
- WEB-INF/lib/commons-logging-1.1.1.jar,
- WEB-INF/lib/jackson-coreutils-1.6.jar,
- WEB-INF/lib/stax-ex-1.8.jar
-
it.geosolutions.mapstore
@@ -362,15 +351,11 @@
-
+
org.mapfish.print
print-lib
-
- commons-codec
- commons-codec
-
com.fasterxml.jackson.core
jackson-annotations
@@ -391,15 +376,6 @@
org.springframework
spring-context
-
-
- org.apache.httpcomponents
- httpclient
-
-
- org.apache.httpcomponents
- httpcore
-
diff --git a/project/standard/templates/pom.xml b/project/standard/templates/pom.xml
index 19a73a052e7..d5481b93cb1 100644
--- a/project/standard/templates/pom.xml
+++ b/project/standard/templates/pom.xml
@@ -13,7 +13,9 @@
8080
10.1.55
- 2.19.4
+ 2.22.3
+ 32.0.0-jre
+ 3.0.2
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8989
2.6
3.4.0
@@ -48,6 +50,17 @@
pom
import
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ com.google.code.findbugs
+ jsr305
+ ${jsr305.version}
+
diff --git a/project/standard/templates/web/pom.xml b/project/standard/templates/web/pom.xml
index b05476a443e..b43317774f9 100644
--- a/project/standard/templates/web/pom.xml
+++ b/project/standard/templates/web/pom.xml
@@ -57,11 +57,12 @@
${mockito-core.version}
test
-
+
jakarta.servlet
jakarta.servlet-api
${jakarta.servlet-api.version}
+ provided
@@ -351,25 +352,46 @@
maven-war-plugin
${maven-war-plugin.version}
-
- WEB-INF/lib/*spring*5.3.18*.jar,
- WEB-INF/lib/json-lib-2.4-jdk15.jar,
- WEB-INF/lib/msg-simple-1.1.jar,
- WEB-INF/lib/btf-1.2.jar,
- WEB-INF/lib/commons-io-2.1.jar,
- WEB-INF/lib/commons-beanutils-1.8.0.jar,
- WEB-INF/lib/commons-logging-1.1.1.jar,
- WEB-INF/lib/jackson-coreutils-1.6.jar,
- WEB-INF/lib/stax-ex-1.8.jar
-
+
it.geosolutions.geostore
geostore-webapp
+
+ WEB-INF/lib/commons-io-*.jar
+ WEB-INF/lib/jackson-annotations-*.jar
+ WEB-INF/lib/jackson-core-*.jar
+ WEB-INF/lib/jackson-databind-*.jar
+ WEB-INF/lib/log4j-api-*.jar
+ WEB-INF/lib/log4j-core-*.jar
+ WEB-INF/lib/log4j-slf4j-impl-*.jar
+ WEB-INF/lib/slf4j-api-*.jar
+
+ WEB-INF/lib/micrometer-commons-*.jar
+ WEB-INF/lib/micrometer-observation-*.jar
+
+ WEB-INF/lib/asm-2.*.jar
+
proxy
http_proxy
+
+ WEB-INF/lib/commons-io-*.jar
+ WEB-INF/lib/log4j-api-*.jar
+ WEB-INF/lib/log4j-core-*.jar
+
+ WEB-INF/lib/log4j-slf4j2-impl-*.jar
+ WEB-INF/lib/slf4j-api-*.jar
+
+ WEB-INF/lib/commons-codec-*.jar
+
+ WEB-INF/lib/jetty-*.jar
+
@@ -479,6 +501,7 @@
jakarta.servlet
jakarta.servlet-api
${jakarta.servlet-api.version}
+ provided
jakarta.xml.ws
@@ -488,16 +511,12 @@
-
+
org.mapfish.print
print-lib
${print-lib.version}
-
- commons-codec
- commons-codec
-
com.fasterxml.jackson.core
jackson-annotations
@@ -518,6 +537,14 @@
org.springframework
spring-context
+
+ org.glassfish.jaxb
+ jaxb-runtime
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
org.apache.httpcomponents
@@ -527,6 +554,18 @@
org.apache.httpcomponents
httpcore
+
+ org.apache.httpcomponents.client5
+ httpclient5
+
+
+ org.apache.httpcomponents.core5
+ httpcore5
+
+
+ org.apache.httpcomponents.core5
+ httpcore5-h2
+
diff --git a/utility/build/check_duplicate_jars.py b/utility/build/check_duplicate_jars.py
new file mode 100644
index 00000000000..abfb5c8e9e1
--- /dev/null
+++ b/utility/build/check_duplicate_jars.py
@@ -0,0 +1,118 @@
+"""
+check_duplicate_jars.py
+
+Fails when the given WAR or ZIP archives, deployed together, put two versions of
+the same library in WEB-INF/lib. Which of the two the servlet container loads
+first depends on the environment, so such a WAR must not be released.
+
+The archives are merged the way they are deployed: the printing bundle is
+unpacked over the MapStore WAR, so a jar with the same file name in both is
+the same file, not a duplicate.
+
+A library is identified by the jar file name without its version
+(e.g. log4j-api-2.26.1.jar -> log4j-api). Two jars with the same name are
+reported only when they contain at least one class in common: this keeps apart
+libraries that share an artifactId but not their packages, such as Jackson 2
+(com.fasterxml.jackson.core:jackson-core) and Jackson 3
+(tools.jackson.core:jackson-core).
+
+Usage example:
+ python utility/build/check_duplicate_jars.py \\
+ product/target/mapstore.war \\
+ java/printing/target/mapstore-printing.zip
+
+Exit status: 0 when no library is duplicated, 1 otherwise.
+"""
+
+import argparse
+import io
+import os
+import re
+import sys
+import zipfile
+from collections import defaultdict
+
+LIB_ENTRY = re.compile(r"^WEB-INF/lib/([^/]+\.jar)$")
+# the name ends before the first "-": guava-32.0.0-jre.jar -> guava
+JAR_NAME = re.compile(r"^(.+?)-\d.*\.jar$")
+
+
+def library_name(jar_file_name):
+ match = JAR_NAME.match(jar_file_name)
+ return match.group(1) if match else jar_file_name[: -len(".jar")]
+
+
+def class_entries(jar_bytes):
+ with zipfile.ZipFile(io.BytesIO(jar_bytes)) as jar:
+ return {
+ name
+ for name in jar.namelist()
+ if name.endswith(".class")
+ and not name.startswith("META-INF/")
+ and not name.endswith("module-info.class")
+ }
+
+
+def collect_jars(archives):
+ """Returns {jar file name: (archive path, jar bytes)}, merged as deployed."""
+ jars = {}
+ for archive in archives:
+ with zipfile.ZipFile(archive) as zipped:
+ for entry in zipped.namelist():
+ match = LIB_ENTRY.match(entry)
+ if match:
+ jars[match.group(1)] = (archive, zipped.read(entry))
+ return jars
+
+
+def find_duplicates(jars):
+ by_name = defaultdict(list)
+ for jar_file_name in jars:
+ by_name[library_name(jar_file_name)].append(jar_file_name)
+
+ duplicates = []
+ for name, jar_file_names in sorted(by_name.items()):
+ if len(jar_file_names) < 2:
+ continue
+ classes = {jar: class_entries(jars[jar][1]) for jar in jar_file_names}
+ clashing = sorted(
+ jar
+ for jar in jar_file_names
+ if any(classes[jar] & classes[other] for other in jar_file_names if other != jar)
+ )
+ if clashing:
+ duplicates.append((name, clashing))
+ return duplicates
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Fail when WAR/ZIP archives deployed together contain two versions of the same jar."
+ )
+ parser.add_argument("archives", nargs="+", help="WAR or ZIP archives, in deploy order")
+ args = parser.parse_args()
+
+ for archive in args.archives:
+ if not os.path.isfile(archive):
+ parser.error(f"archive not found: {archive}")
+
+ jars = collect_jars(args.archives)
+ duplicates = find_duplicates(jars)
+ checked = ", ".join(args.archives)
+
+ if not duplicates:
+ print(f"No duplicated libraries among {len(jars)} jars in {checked}")
+ return 0
+
+ in_github_actions = os.environ.get("GITHUB_ACTIONS") == "true"
+ print(f"Libraries packaged in more than one version in {checked}:")
+ for name, jar_file_names in duplicates:
+ found = ", ".join(f"{jar} ({jars[jar][0]})" for jar in jar_file_names)
+ print(f" {name}: {found}")
+ if in_github_actions:
+ print(f"::error title=Duplicated library {name}::{found}")
+ return 1
+
+
+if __name__ == "__main__":
+ sys.exit(main())