From aabd918871c99ff5653f59b48ac2860101f04b46 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 25 Aug 2023 14:57:09 +0200 Subject: [PATCH] use faster gen with JDK methods --- terminator-commons/pom.xml | 5 -- .../commons/fingerprint/JdkClass.java | 8 +++ .../commons/fingerprint/JdkIndexer.java | 56 ++++++++++++++----- .../java/io/github/algomaster99/Options.java | 24 +++++--- 4 files changed, 68 insertions(+), 25 deletions(-) create mode 100644 terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkClass.java diff --git a/terminator-commons/pom.xml b/terminator-commons/pom.xml index 1d7b709d..99552a27 100644 --- a/terminator-commons/pom.xml +++ b/terminator-commons/pom.xml @@ -44,11 +44,6 @@ org.apache.logging.log4j log4j-core - - io.github.classgraph - classgraph - 4.8.162 - diff --git a/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkClass.java b/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkClass.java new file mode 100644 index 00000000..2e96125a --- /dev/null +++ b/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkClass.java @@ -0,0 +1,8 @@ +package io.github.algomaster99.terminator.commons.fingerprint; + +import java.nio.ByteBuffer; + +/** + * A class that represents a JDK class. It contains the name of the class and the bytes of the class as a {@link ByteBuffer}. + */ +public record JdkClass(String name, ByteBuffer bytes) {} diff --git a/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkIndexer.java b/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkIndexer.java index 93a0fa3a..7399f823 100644 --- a/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkIndexer.java +++ b/terminator-commons/src/main/java/io/github/algomaster99/terminator/commons/fingerprint/JdkIndexer.java @@ -1,23 +1,53 @@ package io.github.algomaster99.terminator.commons.fingerprint; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.Resource; -import io.github.classgraph.ScanResult; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; +import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +/** + * The JdkIndexer class provides a utility to list all JDK classes by scanning the JDK used for the execution of the application. + */ public class JdkIndexer { + private static final Logger logger = LoggerFactory.getLogger(JdkIndexer.class); + /** - * Returns a list of all jdk classes. The list is populated by scanning the jdk used for the execution of this application. - * @return a list of all jdk classes, never null. + * Returns a list of all JDK classes. The list is populated by scanning the JDK used for the execution of this application. + * + * @return a list of all JDK classes, never null. */ - public static List listJdkClasses() { - try (ScanResult scanResult = new ClassGraph() - .enableSystemJarsAndModules() - .acceptPackages("java.*", "jdk.*", "oracle.*", "sun.*") - .scan()) { - return scanResult.getAllClasses().stream().map(v -> v.getResource()).collect(Collectors.toList()); - } + public static List listJdkClasses() { + List jdkClasses = new ArrayList<>(); + ModuleFinder.ofSystem().findAll().forEach(mr -> { + try { + try (ModuleReader reader = mr.open()) { + for (String resource : reader.list().toList()) { + if (!resource.endsWith(".class")) { + continue; + } + Optional contents = reader.read(resource); + if (contents.isPresent()) { + jdkClasses.add(new JdkClass(resource, contents.get())); + } else { + logger.atWarn() + .log( + "Could not read resource {} from module {}", + resource, + mr.descriptor().name()); + } + } + } + } catch (Throwable e) { + logger.atError() + .setCause(e) + .log("Error while reading module {}", mr.descriptor().name()); + } + }); + return jdkClasses; } } diff --git a/watchdog-agent/src/main/java/io/github/algomaster99/Options.java b/watchdog-agent/src/main/java/io/github/algomaster99/Options.java index b50f2619..6cbe1ba9 100644 --- a/watchdog-agent/src/main/java/io/github/algomaster99/Options.java +++ b/watchdog-agent/src/main/java/io/github/algomaster99/Options.java @@ -16,6 +16,7 @@ import io.github.algomaster99.terminator.commons.jar.JarDownloader; import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.security.NoSuchAlgorithmException; @@ -155,25 +156,34 @@ private void processAllComponents(Bom14Schema sbom) { private void processJdk() { JdkIndexer.listJdkClasses().forEach(resource -> { try { - - byte[] classfileBytes = resource.load(); + byte[] classfileBytes = toArray(resource.bytes()); ClassReader classReader = new ClassReader(classfileBytes); - String classfileVersion = ClassfileVersion.getVersion(classfileBytes); String hash = HashComputer.computeHash(classfileBytes, algorithm); jdkFingerprints.put( classReader.getClassName(), List.of(new Jdk(new ClassFileAttributes(classfileVersion, hash, algorithm)))); - } catch (IOException e) { - LOGGER.error("Failed to load classfile bytes for: " + resource, e); - throw new RuntimeException(e); } catch (NoSuchAlgorithmException e) { LOGGER.error("Failed to compute hash with algorithm: " + algorithm, e); throw new RuntimeException(e); } catch (Exception e) { LOGGER.error("Failed to compute hash for: " + resource, e); - System.out.println("Failed to compute hash for: " + resource.getPath()); } }); } + + /** + * Converts a bytebuffer to a byte array. If the buffer has an array, it returns it, otherwise it copies the bytes. This is needed because the buffer is not guaranteed to have an array. + * See {@link java.nio.ByteBuffer#hasArray()} and {@link java.nio.DirectByteBuffer}. + * @param buffer the buffer to convert + * @return the byte array + */ + private byte[] toArray(ByteBuffer buffer) { + if (buffer.hasArray()) { + return buffer.array(); + } + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + return bytes; + } }