diff --git a/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar b/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar index dc7ab7992aea6c551c9e1c4eb1ae72eb234570f8..6d330d16936409b799dccb1f1917a0bb52bc8614 100644 Binary files a/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar and b/release_scripts/APIUpdate/APIUpdate-1.0-jar-with-dependencies.jar differ diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java index 3b59850ac952a7923846a7eb8c35c92efa52005f..42a89f8e221eedd5db41942500d45d34bc2b7f6e 100644 --- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java +++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java @@ -40,12 +40,17 @@ import java.io.FileFilter; import java.io.IOException; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; import javassist.CtBehavior; @@ -53,11 +58,14 @@ import javassist.CtField; import javassist.CtMember; import javassist.Modifier; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; /** * Handles diffing the public API between two jar files. */ public class APIDiff { + private static final Logger LOGGER = Logger.getLogger(APIDiff.class.getName()); // filters to a jar or nbm file private static final FileFilter JAR_FILTER @@ -67,19 +75,29 @@ public class APIDiff { * Identifies common jar files between two directories. Only files listed in * the directory are considered. This method does not recurse. * - * @param prevDir The previous version directory. - * @param currDir The current version directory. + * @param prev The previous version directory. + * @param curr The current version directory. * @return The jar file names. */ - 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()); + static List<Pair<File, File>> getCommonJars(File prev, File curr) { + if (prev.isFile() && curr.isFile()) { + return Arrays.asList(Pair.of(prev, curr)); + } + + Map<String, File> prevJars = getJars(prev); + Map<String, File> currJars = getJars(curr); + + List<Pair<File, File>> retMapping = new ArrayList<>(); + + for (String prevKey: (Iterable<String>) prevJars.keySet().stream().sorted(StringUtils::compareIgnoreCase)::iterator) { + File prevFile = prevJars.get(prevKey); + File curFile = currJars.get(prevKey); + if (prevFile != null && curFile != null) { + retMapping.add(Pair.of(prevFile, curFile)); + } + } + + return retMapping; } /** @@ -88,10 +106,10 @@ static List<String> getCommonJars(File prevDir, File currDir) { * @param dir The directory. * @return The jar file names. */ - private static Set<String> getJars(File dir) { - return Stream.of(dir.listFiles(JAR_FILTER)) - .map(f -> f.getName()) - .collect(Collectors.toSet()); + private static Map<String, File> getJars(File dir) { + File[] files = dir.isDirectory() ? dir.listFiles(JAR_FILTER) : new File[]{dir}; + files = files == null ? new File[0] : files; + return Stream.of(files).collect(Collectors.toMap(f -> f.getName(), f -> f, (f1, f2) -> f1)); } /** @@ -106,7 +124,8 @@ private static Set<String> getJars(File dir) { private static Set<String> getPublicPackages(File jarFile) throws IOException, IllegalStateException { String publicPackageStr = ManifestLoader.loadFromJar(jarFile).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())); + LOGGER.log(Level.WARNING, MessageFormat.format("Manifest for {0} does not have key of 'OpenIDE-Module-Public-Packages'", jarFile.getAbsolutePath())); + return null; } else { return Stream.of(publicPackageStr.split(",")) .map(String::trim) @@ -139,7 +158,11 @@ static ComparisonRecord getComparison(String prevVersion, String curVersion, Fil // scope only to previous or current public packages Set<String> prevPublicApiPackages = getPublicPackages(prevJar); Set<String> curPublicApiPackages = getPublicPackages(curJar); - + + boolean filterToPublicPackages = (prevPublicApiPackages == null && curPublicApiPackages == null) ? false : true; + prevPublicApiPackages = prevPublicApiPackages == null ? Collections.emptySet() : prevPublicApiPackages; + curPublicApiPackages = curPublicApiPackages == null ? Collections.emptySet() : curPublicApiPackages; + Set<String> allPublicApiPackages = new HashSet<>(); allPublicApiPackages.addAll(prevPublicApiPackages); allPublicApiPackages.addAll(curPublicApiPackages); @@ -161,7 +184,10 @@ static ComparisonRecord getComparison(String prevVersion, String curVersion, Fil JarArchiveComparatorOptions comparatorOptions = new JarArchiveComparatorOptions(); // only classes in prev or current public api - comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !allPublicApiPackages.contains(ctClass.getPackageName())); + if (filterToPublicPackages) { + comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !allPublicApiPackages.contains(ctClass.getPackageName())); + } + // only public classes comparatorOptions.getFilters().getExcludes().add((ClassFilter) (CtClass ctClass) -> !Modifier.isPublic(ctClass.getModifiers())); // only fields, methods that are public or protected and class is not final diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java index 8422ed20395ac8cc1bf1a87ec5d7c744481be82d..919897403605c47dcea7cedbada70d58cff00a95 100644 --- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java +++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java @@ -189,13 +189,17 @@ static CLIArgs parseCli(String[] args) throws ParseException { File prevVersFile = new File(prevVersPath); File srcPathFile = new File(srcPath); - if (!curVersFile.isDirectory()) { + if (!curVersFile.exists()) { throw new ParseException("No directory found at " + curVersFile.getAbsolutePath()); } - if (!prevVersFile.isDirectory()) { + if (!prevVersFile.exists()) { throw new ParseException("No directory found at " + prevVersFile.getAbsolutePath()); } + + if (curVersFile.isDirectory() != prevVersFile.isDirectory()) { + throw new ParseException("Current and previous paths must both be directories or files"); + } if (!srcPathFile.isDirectory()) { throw new ParseException("No directory found at " + srcPathFile.getAbsolutePath()); diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java index 86b6174b4bcb846c641fcb8a855a1e4d9769424f..78853c4a69c0f23cb6f5efb74cf34cc2846bc427 100644 --- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java +++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.apiupdate; +import java.io.File; import java.io.IOException; import java.text.MessageFormat; import java.util.HashMap; @@ -25,6 +26,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.tuple.Pair; import org.sleuthkit.autopsy.apiupdate.APIDiff.ComparisonRecord; import org.sleuthkit.autopsy.apiupdate.CLIProcessor.CLIArgs; import org.sleuthkit.autopsy.apiupdate.ModuleUpdates.ModuleVersionNumbers; @@ -52,19 +54,26 @@ public static void main(String[] args) { Map<String, ModuleVersionNumbers> newVersionNumMapping = new HashMap<>(); - for (String commonJarFileName : APIDiff.getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) { + for (Pair<File, File> prevCurJars : APIDiff.getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) { try { - ModuleVersionNumbers prevVersionNums = ModuleUpdates.getVersionsFromJar(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile()); + ModuleVersionNumbers prevVersionNums = ModuleUpdates.getVersionsFromJar(prevCurJars.getLeft()); ComparisonRecord record = APIDiff.getComparison( cliArgs.getPreviousVersion(), cliArgs.getCurrentVersion(), - cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(), - cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile()); + prevCurJars.getLeft(), + prevCurJars.getRight()); ModuleVersionNumbers projectedVersionNums = ModuleUpdates.getModuleVersionUpdate(prevVersionNums, record.getChangeType()); + + String jarFileName; + if (prevCurJars.getLeft().getName().equalsIgnoreCase(prevCurJars.getRight().getName())) { + jarFileName = prevCurJars.getLeft().getName(); + } else { + jarFileName = MessageFormat.format("[previous: {0}, current: {1}]", prevCurJars.getLeft().getName(), prevCurJars.getRight().getName()); + } - outputDiff(commonJarFileName, record, prevVersionNums, projectedVersionNums); + outputDiff(jarFileName, record, prevVersionNums, projectedVersionNums); newVersionNumMapping.put(projectedVersionNums.getRelease().getModuleName(), projectedVersionNums); } catch (IOException ex) { diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java index 8ae574308d8f2184b21619395b96a0fbad1857f2..16e149f854278ff32bc2a25396bd7294c8a0ec8d 100644 --- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java +++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java @@ -110,7 +110,7 @@ public class ModuleUpdates { */ private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String resourceForLogging) { if (StringUtils.isBlank(semVerStr)) { - LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver for empty string in {0}", resourceForLogging)); + LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse semver for empty string in {0}", resourceForLogging)); return defaultSemVer; } @@ -127,7 +127,7 @@ private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging), ex); } } else { - LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging)); + LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging)); } return defaultSemVer; @@ -154,7 +154,7 @@ private static ReleaseVal parseReleaseVers(String releaseStr, String resourceFor } return new ReleaseVal(releaseName, releaseNum); } else { - LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse release version string {0} for {1}", releaseStr, resourceForLogging)); + LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse release version string {0} for {1}", releaseStr, resourceForLogging)); } return new ReleaseVal("", null); @@ -175,7 +175,7 @@ private static int tryParse(String str, int defaultVal, String resourceForLoggin try { return Integer.parseInt(str); } catch (NullPointerException | NumberFormatException ex) { - LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse version string {0} for {1}", str, resourceForLogging), ex); + LOGGER.log(Level.WARNING, MessageFormat.format("Unable to parse version string {0} for {1}", str, resourceForLogging), ex); return defaultVal; } } @@ -193,8 +193,15 @@ public static ModuleVersionNumbers getVersionsFromJar(File jarFile) throws IOExc SemVer specSemVer = parseSemVer(spec, DEFAULT_SEMVER, MessageFormat.format("{0} in manifest for {1}", MANIFEST_SPEC_KEY, jarFile)); - int implementation = tryParse(manifest.getValue(MANIFEST_IMPL_KEY), DEFAULT_VERS_VAL, - MessageFormat.format("{0} in manifest for {1}", MANIFEST_IMPL_KEY, jarFile)); + String implStr = manifest.getValue(MANIFEST_IMPL_KEY); + int implementation; + if (StringUtils.isBlank(implStr)) { + LOGGER.log(Level.WARNING, MessageFormat.format("No {0} for implementation found in {1}. Using default of {2}.", MANIFEST_IMPL_KEY, jarFile.getName(), DEFAULT_VERS_VAL)); + implementation = DEFAULT_VERS_VAL; + } else { + implementation = tryParse(implStr, DEFAULT_VERS_VAL, + MessageFormat.format("{0} in manifest for {1}", MANIFEST_IMPL_KEY, jarFile)); + } ReleaseVal release = parseReleaseVers(manifest.getValue(MANIFEST_RELEASE_KEY), MessageFormat.format("{0} in manifest for {1}", MANIFEST_RELEASE_KEY, jarFile));