Created
March 24, 2025 09:24
-
-
Save Shawyeok/f41995b69839adb46cf5c231f04a9e1c to your computer and use it in GitHub Desktop.
JarConflictFinder: A Java tool to identify duplicate classes across multiple JAR files in your project. This tool helps detect potential classpath conflicts by scanning directories or individual JAR files and reporting classes that appear in multiple JARs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.File; | |
import java.io.IOException; | |
import java.util.*; | |
import java.util.jar.JarEntry; | |
import java.util.jar.JarFile; | |
import java.util.stream.Collectors; | |
public class JarConflictFinder { | |
private Map<String, List<String>> classLocations = new HashMap<>(); | |
public void scanDirectory(String directoryPath) { | |
File directory = new File(directoryPath); | |
if (!directory.isDirectory()) { | |
System.err.println("Not a valid directory: " + directoryPath); | |
return; | |
} | |
File[] jarFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".jar")); | |
if (jarFiles != null) { | |
for (File jarFile : jarFiles) { | |
scanJarFile(jarFile.getAbsolutePath()); | |
} | |
} | |
} | |
public void scanJarFile(String jarPath) { | |
try (JarFile jarFile = new JarFile(jarPath)) { | |
Enumeration<JarEntry> entries = jarFile.entries(); | |
while (entries.hasMoreElements()) { | |
JarEntry entry = entries.nextElement(); | |
if (entry.getName().endsWith(".class")) { | |
// Convert path to class name | |
String className = entry.getName() | |
.replace('/', '.') | |
.replace(".class", ""); | |
classLocations.computeIfAbsent(className, k -> new ArrayList<>()) | |
.add(jarFile.getName()); | |
} | |
} | |
} catch (IOException e) { | |
System.err.println("Error processing JAR file: " + jarPath); | |
e.printStackTrace(); | |
} | |
} | |
public void printDuplicates() { | |
System.out.println("Found duplicate classes:"); | |
System.out.println("------------------------"); | |
boolean foundDuplicates = false; | |
for (Map.Entry<String, List<String>> entry : classLocations.entrySet()) { | |
if (entry.getValue().size() > 1) { | |
foundDuplicates = true; | |
System.out.println("\nClass: " + entry.getKey()); | |
System.out.println("Found in:"); | |
for (String jarPath : entry.getValue()) { | |
System.out.println(" - " + jarPath); | |
} | |
} | |
} | |
if (!foundDuplicates) { | |
System.out.println("No duplicate classes found."); | |
} | |
// Print statistics after showing duplicates | |
printStatistics(); | |
} | |
private void printStatistics() { | |
System.out.println("\nDuplicate Class Statistics:"); | |
System.out.println("=========================="); | |
// 1. Total number of duplicate classes | |
long totalDuplicates = classLocations.values().stream() | |
.filter(list -> list.size() > 1) | |
.count(); | |
System.out.println("Total duplicate classes found: " + totalDuplicates); | |
// 2. Conflicts per JAR pair | |
Map<String, Integer> jarPairConflicts = new HashMap<>(); | |
for (List<String> jars : classLocations.values()) { | |
if (jars.size() > 1) { | |
for (int i = 0; i < jars.size(); i++) { | |
for (int j = i + 1; j < jars.size(); j++) { | |
String pair = jars.get(i) + " <-> " + jars.get(j); | |
jarPairConflicts.merge(pair, 1, Integer::sum); | |
} | |
} | |
} | |
} | |
System.out.println("\nConflicts per JAR pair:"); | |
jarPairConflicts.entrySet().stream() | |
.sorted((e1, e2) -> e2.getValue().compareTo(e1.getValue())) | |
.limit(10) // Show top 10 conflicting pairs | |
.forEach(e -> System.out.println(String.format(" %s: %d conflicts", e.getKey(), e.getValue()))); | |
// 3. Distribution of duplicate counts | |
Map<Integer, Long> duplicateDistribution = classLocations.values().stream() | |
.filter(list -> list.size() > 1) | |
.collect(Collectors.groupingBy( | |
List::size, | |
Collectors.counting() | |
)); | |
System.out.println("\nDistribution of duplicates:"); | |
duplicateDistribution.entrySet().stream() | |
.sorted(Map.Entry.comparingByKey()) | |
.forEach(e -> System.out.println(String.format(" Classes found in %d JARs: %d", e.getKey(), e.getValue()))); | |
} | |
public static void main(String[] args) { | |
if (args.length == 0) { | |
System.out.println("Usage: java JarConflictFinder <directory-or-jar-path> [additional-jar-paths...]"); | |
return; | |
} | |
JarConflictFinder finder = new JarConflictFinder(); | |
for (String path : args) { | |
File file = new File(path); | |
if (file.isDirectory()) { | |
finder.scanDirectory(path); | |
} else if (path.toLowerCase().endsWith(".jar")) { | |
finder.scanJarFile(path); | |
} else { | |
System.err.println("Skipping invalid path: " + path); | |
} | |
} | |
finder.printDuplicates(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
JarConflictFinder
A Java tool to identify duplicate classes across multiple JAR files in your project. This tool helps detect potential classpath conflicts by scanning directories or individual JAR files and reporting classes that appear in multiple JARs.
Features
Prerequisites
Installation
JarConflictFinder.java
Usage
The tool can be used in several ways:
1. Scan a Directory
To scan all JAR files in a directory:
2. Scan Specific JAR Files
To scan specific JAR files:
3. Mixed Scanning
You can combine directory and file scanning:
Output Format
The tool provides output in three sections:
Duplicate Classes List
Conflict Statistics
Distribution Analysis
Example Output
Best Practices
Troubleshooting
If you encounter any issues:
License
This tool is provided as-is under the MIT License.