Декомпилирование .class файлов внутри jar файлов для сравнения этих самых jar файлов. Использует cfr для декомпилирования. Build docker build -t decompiler . Usage docker run --rm -v /path/to/jars:/jars decompiler /jars/SomeJar.jar Dockerfile #### # Build image #### FROM openjdk:21 AS build LABEL maintainer=avvero RUN microdnf install findutils COPY gradlew /app/ COPY gradle /app/gradle WORKDIR /app RUN ./gradlew --version WORKDIR /app COPY . . RUN ./gradlew build --no-daemon #### # Runtime image #### FROM openjdk:21 COPY --from=build /app/build/classes/java/main/Decompiler.class /app/Decompiler.class COPY --from=build /app/cfr.jar /app/cfr.jar WORKDIR /app ENTRYPOINT ["java", "Decompiler"] Code import java.io.*; import java.nio.file.*; import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class Decompiler { private static final Set<String> IGNORED_FILES = new HashSet<>(Arrays.asList( "META-INF/MANIFEST.MF" )); public static void main(String[] args) throws IOException { if (args.length == 0) { System.out.println("Usage: java Decompiler <path-to-jar1> <path-to-jar2> ..."); System.exit(1); } for (String jarFilePath : args) { File jarFile = new File(jarFilePath); if (!jarFile.exists() || !jarFile.isFile()) { System.err.println("File not found or not a file: " + jarFilePath); continue; } String baseName = jarFile.getName().substring(0, jarFile.getName().lastIndexOf('.')); Path outputDir = jarFile.getParentFile().toPath().resolve(baseName + "_decompiled"); Files.createDirectories(outputDir); try (JarFile jar = new JarFile(jarFile)) { decompileJar(jar, outputDir); } catch (IOException e) { System.err.println("Failed to decompile JAR: " + jarFilePath); e.printStackTrace(); } } } private static void decompileJar(JarFile jarFile, Path outputDir) throws IOException { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (IGNORED_FILES.contains(entry.getName())) { continue; } File entryFile = new File(outputDir.toFile(), entry.getName()); if (entry.isDirectory()) { entryFile.mkdirs(); } else if (entry.getName().endsWith(".class")) { entryFile.getParentFile().mkdirs(); Files.copy(jarFile.getInputStream(entry), entryFile.toPath(), StandardCopyOption.REPLACE_EXISTING); decompileClassFile(entryFile.toPath()); Files.delete(entryFile.toPath()); } else { entryFile.getParentFile().mkdirs(); Files.copy(jarFile.getInputStream(entry), entryFile.toPath(), StandardCopyOption.REPLACE_EXISTING); } } } private static void decompileClassFile(Path classFile) throws IOException { Path outputDir = classFile.getParent(); ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", "cfr.jar", classFile.toString(), "--outputdir", outputDir.toString()); processBuilder.redirectErrorStream(true); Process process = processBuilder.start(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } process.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IOException("Decompilation interrupted", e); } } } javajardecompiledocker