From dd85b4a1f85705e8f2dc45c20dc1a39b63ec52d0 Mon Sep 17 00:00:00 2001 From: Michael Vorburger Date: Sat, 30 Sep 2023 01:53:41 +0200 Subject: [PATCH] feat (cli): Classpath Duplicate Detection Test --- MODULE.bazel | 1 + cli/BUILD | 1 + .../ClasspathHellDuplicatesCheckerTest.java | 86 +++++++++++++++++++ maven_install.json | 28 +++++- 4 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 cli/src/test/java/dev/enola/cli/ClasspathHellDuplicatesCheckerTest.java diff --git a/MODULE.bazel b/MODULE.bazel index e671c9609..c2a7c59e4 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -44,6 +44,7 @@ maven.install( "org.snakeyaml:snakeyaml-engine:2.7", "org.slf4j:slf4j-jdk14:2.0.9", "org.slf4j:slf4j-simple:2.0.9", + "io.github.classgraph:classgraph:4.8.162" ], fetch_sources = True, lock_file = "//:maven_install.json", diff --git a/cli/BUILD b/cli/BUILD index 7ce113de6..7d2a5dd76 100644 --- a/cli/BUILD +++ b/cli/BUILD @@ -53,6 +53,7 @@ java_binary( "@maven//:com_google_guava_guava", "@maven//:com_google_truth_truth", "@maven//:info_picocli_picocli", + "@maven//:io_github_classgraph_classgraph", "@maven//:junit_junit", ], ) for name in glob([ diff --git a/cli/src/test/java/dev/enola/cli/ClasspathHellDuplicatesCheckerTest.java b/cli/src/test/java/dev/enola/cli/ClasspathHellDuplicatesCheckerTest.java new file mode 100644 index 000000000..fc9a40d0c --- /dev/null +++ b/cli/src/test/java/dev/enola/cli/ClasspathHellDuplicatesCheckerTest.java @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2023 The Enola Authors + * + * Licensed 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 + * + * https://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 dev.enola.cli; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ScanResult; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class ClasspathHellDuplicatesCheckerTest { + + @Test + public void testIfThereAreAnyDuplicateJARsOnTheClasspath() throws Exception { + var problems = new HashMap>(); + try (ScanResult scanResult = new ClassGraph().scan()) { + var results = scanResult.getAllResources().classFilesOnly().findDuplicatePaths(); + System.out.println("ClassGraph duplicate results: " + results); + for (var duplicate : results) { + String resourceName = duplicate.getKey(); + if (!isHarmlessDuplicate(resourceName)) { + boolean addIt = true; + List jars = + duplicate.getValue().stream() + .map(resource -> resource.getURL().toExternalForm()) + .collect(Collectors.toList()); + for (String jar : jars) { + if (skipJAR(jar)) { + addIt = false; + break; + } + } + if (addIt) { + problems.put(resourceName, jars); + } + } + } + } + if (!problems.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (Map.Entry> entry : problems.entrySet()) { + sb.append(entry.getKey()); + sb.append('\n'); + for (String location : entry.getValue()) { + sb.append(" "); + sb.append(location); + sb.append('\n'); + } + } + Assert.fail(sb.toString()); + } + } + + private boolean skipJAR(String jarPath) { + // Bazel Rules etc. not runtime classpath + return jarPath.contains("java_tools/Runner_deploy.jar") + + // TODO: Fix the sad mess :( of duplicate Protobuf & gRPC JARs! + || jarPath.contains("protobuf") + || jarPath.contains("grpc"); + } + + protected boolean isHarmlessDuplicate(String resourcePath) { + return resourcePath.equals("module-info.class"); + } +} diff --git a/maven_install.json b/maven_install.json index d9a7ad7d1..ea52b2e04 100644 --- a/maven_install.json +++ b/maven_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 1511132585, - "__RESOLVED_ARTIFACTS_HASH": -1683949065, + "__INPUT_ARTIFACTS_HASH": 521153455, + "__RESOLVED_ARTIFACTS_HASH": -171608629, "artifacts": { "aopalliance:aopalliance": { "shasums": { @@ -241,6 +241,13 @@ }, "version": "4.7.5" }, + "io.github.classgraph:classgraph": { + "shasums": { + "jar": "ea30b2d5e29e89d52706bcecf7a6ae3b44682d4a1566a5f22b9453f9be2a970c", + "sources": "543151480066884e68090d7a8889f7a7b584b3760201af280cd577eaf1f9ecfd" + }, + "version": "4.8.162" + }, "io.grpc:grpc-api": { "shasums": { "jar": "7fbc4beb922f40e6a84987c8dfbc833ee12e332849161f13f32928a728bb5597", @@ -1198,6 +1205,21 @@ "info.picocli:picocli": [ "picocli" ], + "io.github.classgraph:classgraph": [ + "io.github.classgraph", + "nonapi.io.github.classgraph.classloaderhandler", + "nonapi.io.github.classgraph.classpath", + "nonapi.io.github.classgraph.concurrency", + "nonapi.io.github.classgraph.fastzipfilereader", + "nonapi.io.github.classgraph.fileslice", + "nonapi.io.github.classgraph.fileslice.reader", + "nonapi.io.github.classgraph.json", + "nonapi.io.github.classgraph.recycler", + "nonapi.io.github.classgraph.reflection", + "nonapi.io.github.classgraph.scanspec", + "nonapi.io.github.classgraph.types", + "nonapi.io.github.classgraph.utils" + ], "io.grpc:grpc-api": [ "io.grpc" ], @@ -2020,6 +2042,8 @@ "commons-io:commons-io:jar:sources", "info.picocli:picocli", "info.picocli:picocli:jar:sources", + "io.github.classgraph:classgraph", + "io.github.classgraph:classgraph:jar:sources", "io.grpc:grpc-api", "io.grpc:grpc-api:jar:sources", "io.grpc:grpc-context",