From 84814d7f8bff2c06b9386ab165366199d2630e51 Mon Sep 17 00:00:00 2001
From: Greg DiCristofaro <gregd@basistech.com>
Date: Mon, 28 Aug 2023 21:09:56 -0400
Subject: [PATCH] beginnings of APIUpdate jar

---
 release_scripts/APIUpdate/.gitignore          |   1 +
 release_scripts/APIUpdate/nbactions.xml       |  55 ++++++
 release_scripts/APIUpdate/pom.xml             |  28 +++
 .../apiupdate/APIUpdate.java                  | 137 +++++++++++++++
 .../apiupdate/CLIProcessor.java               | 166 ++++++++++++++++++
 5 files changed, 387 insertions(+)
 create mode 100644 release_scripts/APIUpdate/.gitignore
 create mode 100644 release_scripts/APIUpdate/nbactions.xml
 create mode 100644 release_scripts/APIUpdate/pom.xml
 create mode 100644 release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java
 create mode 100644 release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java

diff --git a/release_scripts/APIUpdate/.gitignore b/release_scripts/APIUpdate/.gitignore
new file mode 100644
index 0000000000..c41cc9e35e
--- /dev/null
+++ b/release_scripts/APIUpdate/.gitignore
@@ -0,0 +1 @@
+/target
\ No newline at end of file
diff --git a/release_scripts/APIUpdate/nbactions.xml b/release_scripts/APIUpdate/nbactions.xml
new file mode 100644
index 0000000000..e3b484bf66
--- /dev/null
+++ b/release_scripts/APIUpdate/nbactions.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<actions>
+        <action>
+            <actionName>run</actionName>
+            <packagings>
+                <packaging>jar</packaging>
+            </packagings>
+            <goals>
+                <goal>process-classes</goal>
+                <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
+            </goals>
+            <properties>
+                <exec.vmArgs></exec.vmArgs>
+                <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+                <exec.appArgs>--help</exec.appArgs>
+                <exec.mainClass>${packageClassName}</exec.mainClass>
+                <exec.executable>java</exec.executable>
+            </properties>
+        </action>
+        <action>
+            <actionName>debug</actionName>
+            <packagings>
+                <packaging>jar</packaging>
+            </packagings>
+            <goals>
+                <goal>process-classes</goal>
+                <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
+            </goals>
+            <properties>
+                <exec.vmArgs>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}</exec.vmArgs>
+                <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+                <exec.appArgs>--help</exec.appArgs>
+                <exec.mainClass>${packageClassName}</exec.mainClass>
+                <exec.executable>java</exec.executable>
+                <jpda.listen>true</jpda.listen>
+            </properties>
+        </action>
+        <action>
+            <actionName>profile</actionName>
+            <packagings>
+                <packaging>jar</packaging>
+            </packagings>
+            <goals>
+                <goal>process-classes</goal>
+                <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
+            </goals>
+            <properties>
+                <exec.vmArgs></exec.vmArgs>
+                <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+                <exec.mainClass>${packageClassName}</exec.mainClass>
+                <exec.executable>java</exec.executable>
+                <exec.appArgs>--help</exec.appArgs>
+            </properties>
+        </action>
+    </actions>
diff --git a/release_scripts/APIUpdate/pom.xml b/release_scripts/APIUpdate/pom.xml
new file mode 100644
index 0000000000..1d38e4f5e2
--- /dev/null
+++ b/release_scripts/APIUpdate/pom.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.sleuthkit.autopsy.classpathsimplication</groupId>
+    <artifactId>APIUpdate</artifactId>
+    <version>1.0</version>
+    <packaging>jar</packaging>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.source>17</maven.compiler.source>
+        <maven.compiler.target>17</maven.compiler.target>
+        <exec.mainClass>org.sleuthkit.autopsy.classpathsimplication.apiupdate.APIUpdate</exec.mainClass>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>com.github.siom79.japicmp</groupId>
+            <artifactId>japicmp</artifactId>
+            <version>0.17.2</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>1.5.0</version>
+        </dependency>
+
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java
new file mode 100644
index 0000000000..e387fd686c
--- /dev/null
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java
@@ -0,0 +1,137 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ */
+package org.sleuthkit.autopsy.classpathsimplication.apiupdate;
+
+import japicmp.cmp.JApiCmpArchive;
+import japicmp.cmp.JarArchiveComparator;
+import japicmp.cmp.JarArchiveComparatorOptions;
+import japicmp.model.JApiClass;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.commons.cli.ParseException;
+import org.sleuthkit.autopsy.classpathsimplication.apiupdate.CLIProcessor.CLIArgs;
+
+/**
+ *
+ * @author gregd
+ */
+public class APIUpdate {
+
+    public static void main(String[] args) {
+        args = "-c C:\\Users\\gregd\\Documents\\Source\\autopsy\\build\\cluster\\modules -p C:\\Users\\gregd\\Desktop\\prevVers -cv 4.21.0 -pv 4.20.0".split(" ");
+        CLIArgs cliArgs;
+        try {
+            cliArgs = CLIProcessor.parseCli(args);
+            if (cliArgs.isIsHelp()) {
+                CLIProcessor.printHelp(null);
+                System.exit(0);
+            }
+        } catch (ParseException ex) {
+            CLIProcessor.printHelp(ex);
+            System.exit(-1);
+            return;
+        }
+
+        for (String commonJarFileName : getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
+//            getComparison(
+//                    cliArgs.getPreviousVersion(),
+//                    cliArgs.getCurrentVersion(),
+//                    cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(),
+//                    cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile());
+            try {
+                Set<String> pubPackages = getPublicPackages(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
+                System.out.println(pubPackages);
+            } catch (IOException ex) {
+                Logger.getLogger(APIUpdate.class.getName()).log(Level.SEVERE, null, ex);
+            } catch (IllegalStateException ex) {
+                Logger.getLogger(APIUpdate.class.getName()).log(Level.SEVERE, null, ex);
+            }
+        }
+
+    }
+
+    private static final FileFilter JAR_FILTER
+            = (File f) -> f.isFile() && (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".nbm"));
+
+    private static List<String> getCommonJars(File prevDir, File currDir) {
+        Set<String> prevJars = getJars(prevDir);
+        Set<String> currJars = getJars(currDir);
+
+        Set<String> commonJars = new HashSet<>(prevJars);
+        commonJars.retainAll(currJars);
+
+        // TODO how to handle different
+        return commonJars.stream().sorted().collect(Collectors.toList());
+    }
+
+    private static Set<String> getJars(File dir) {
+        return Stream.of(dir.listFiles(JAR_FILTER))
+                .map(f -> f.getName())
+                .collect(Collectors.toSet());
+    }
+
+    private static Set<String> getPublicPackages(File jarFile) throws IOException, IllegalStateException {
+        ZipFile zipFile = new ZipFile(jarFile);
+
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = entries.nextElement();
+            if ("META-INF/MANIFEST.MF".equalsIgnoreCase(entry.getName())) {
+                InputStream stream = zipFile.getInputStream(entry);
+                Manifest manifest = new Manifest(stream);
+                Attributes attributes = manifest.getMainAttributes();
+                String publicPackageStr = attributes.getValue("OpenIDE-Module-Public-Packages");
+                if (publicPackageStr == null) {
+                    throw new IllegalStateException(MessageFormat.format("Manifest for {0} does not have key of 'OpenIDE-Module-Public-Packages'", jarFile.getAbsolutePath()));
+                } else {
+                    return Stream.of(publicPackageStr.split(","))
+                            .map(String::trim)
+                            .map(str -> str.endsWith(".*") ? str.substring(0, str.length() - 2) : str)
+                            .collect(Collectors.toSet());
+                }
+            }
+        }
+
+        throw new FileNotFoundException("Could not find MANIFEST.MF in " + jarFile.getAbsolutePath());
+    }
+
+    private static void getComparison(String prevVersion, String curVersion, File prevJar, File curJar) {
+        JarArchiveComparatorOptions comparatorOptions = new JarArchiveComparatorOptions();
+        comparatorOptions.getIgnoreMissingClasses().setIgnoreAllMissingClasses(true);
+        JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(comparatorOptions);
+        List<JApiClass> jApiClasses = jarArchiveComparator.compare(
+                new JApiCmpArchive(prevJar, prevVersion),
+                new JApiCmpArchive(curJar, curVersion)
+        );
+        System.out.println("Comparing " + prevJar.getName());
+        System.out.println(jApiClasses);
+    }
+
+    private static void mainRun() {
+
+        // get public API diff's, for each jar
+        // limit to public packages
+        // one of the following:
+        // generate text output of difference
+        // update version numbers in manifest file/references accordingly
+    }
+
+}
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java
new file mode 100644
index 0000000000..0ac99e73ed
--- /dev/null
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java
@@ -0,0 +1,166 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package org.sleuthkit.autopsy.classpathsimplication.apiupdate;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+/**
+ *
+ * @author gregd
+ */
+public class CLIProcessor {
+
+    static Option PREV_VERS_PATH_OPT = Option.builder()
+            .argName("path")
+            .desc("The path to the previous version jar files")
+            .hasArg(true)
+            .longOpt("prev-path")
+            .option("p")
+            .required(true)
+            .build();
+
+    static Option CUR_VERS_PATH_OPT = Option.builder()
+            .argName("path")
+            .desc("The path to the current version jar files")
+            .hasArg(true)
+            .longOpt("curr-path")
+            .option("c")
+            .required(true)
+            .build();
+
+    static Option PREV_VERS_OPT = Option.builder()
+            .argName("version")
+            .desc("The previous version number")
+            .hasArg(true)
+            .longOpt("prev-version")
+            .option("pv")
+            .required(true)
+            .build();
+
+    static Option CUR_VERS_OPT = Option.builder()
+            .argName("version")
+            .desc("The current version number")
+            .hasArg(true)
+            .longOpt("curr-version")
+            .option("cv")
+            .required(true)
+            .build();
+
+    static List<Option> ALL_OPTIONS = Arrays.asList(
+            PREV_VERS_PATH_OPT,
+            CUR_VERS_PATH_OPT,
+            PREV_VERS_OPT,
+            CUR_VERS_OPT
+    );
+
+    static Options CLI_OPTIONS = getCliOptions(ALL_OPTIONS);
+
+    private static Options getCliOptions(List<Option> opts) {
+        Options toRet = new Options();
+        for (Option opt : opts) {
+            toRet.addOption(opt);
+        }
+
+        return toRet;
+    }
+
+    static Option HELP_OPT = Option.builder()
+            .desc("Print help message")
+            .hasArg(false)
+            .longOpt("help")
+            .option("h")
+            .required(false)
+            .build();
+
+    static Options HELP_OPTIONS = getCliOptions(Collections.singletonList(HELP_OPT));
+
+    private static CommandLineParser parser = new DefaultParser();
+    
+   
+    private static HelpFormatter helpFormatter = new HelpFormatter();
+
+    static void printHelp(Exception ex) {
+        if (ex != null && ex.getMessage() != null && !ex.getMessage().isBlank()) {
+            System.out.println(ex.getMessage());
+        }
+
+        helpFormatter.printHelp("APIUpdate", CLI_OPTIONS);
+    }
+
+    static CLIArgs parseCli(String[] args) throws ParseException {
+        CommandLine helpCmd = parser.parse(HELP_OPTIONS, args, true);
+        boolean isHelp = helpCmd.hasOption(HELP_OPT);
+        if (isHelp) {
+            return new CLIArgs(null, null, null, null, true);
+        }
+
+        CommandLine cmd = parser.parse(CLI_OPTIONS, args);
+        String curVers = cmd.getOptionValue(CUR_VERS_OPT);
+        String prevVers = cmd.getOptionValue(PREV_VERS_OPT);
+        String curVersPath = cmd.getOptionValue(CUR_VERS_PATH_OPT);
+        String prevVersPath = cmd.getOptionValue(PREV_VERS_PATH_OPT);
+        File curVersFile = new File(curVersPath);
+        File prevVersFile = new File(prevVersPath);
+
+        if (!curVersFile.isDirectory()) {
+            throw new ParseException("No directory found at " + curVersFile.getAbsolutePath());
+        }
+
+        if (!prevVersFile.isDirectory()) {
+            throw new ParseException("No directory found at " + prevVersFile.getAbsolutePath());
+        }
+
+        return new CLIArgs(curVers, prevVers, curVersFile, prevVersFile, false);
+    }
+
+
+    public static class CLIArgs {
+
+        private final String currentVersion;
+        private final String previousVersion;
+        private final File currentVersPath;
+        private final File previousVersPath;
+        private final boolean isHelp;
+
+        public String getCurrentVersion() {
+            return currentVersion;
+        }
+
+        public String getPreviousVersion() {
+            return previousVersion;
+        }
+
+        public File getCurrentVersPath() {
+            return currentVersPath;
+        }
+
+        public File getPreviousVersPath() {
+            return previousVersPath;
+        }
+
+        public boolean isIsHelp() {
+            return isHelp;
+        }
+
+        public CLIArgs(String currentVersion, String previousVersion, File currentVersPath, File previousVersPath, boolean isHelp) {
+            this.currentVersion = currentVersion;
+            this.previousVersion = previousVersion;
+            this.currentVersPath = currentVersPath;
+            this.previousVersPath = previousVersPath;
+            this.isHelp = isHelp;
+        }
+
+    }
+}
-- 
GitLab