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 dc7ab7992a..6d330d1693 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 3b59850ac9..42a89f8e22 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.File; 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.CtClass; 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 getCommonJars(File prevDir, File currDir) { - Set prevJars = getJars(prevDir); - Set currJars = getJars(currDir); + static List> getCommonJars(File prev, File curr) { + if (prev.isFile() && curr.isFile()) { + return Arrays.asList(Pair.of(prev, curr)); + } + + Map prevJars = getJars(prev); + Map currJars = getJars(curr); + + List> retMapping = new ArrayList<>(); - Set commonJars = new HashSet<>(prevJars); - commonJars.retainAll(currJars); - - // TODO how to handle different - return commonJars.stream().sorted().collect(Collectors.toList()); + for (String prevKey: (Iterable) 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 @@ public class APIDiff { * @param dir The directory. * @return The jar file names. */ - private static Set getJars(File dir) { - return Stream.of(dir.listFiles(JAR_FILTER)) - .map(f -> f.getName()) - .collect(Collectors.toSet()); + private static Map 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 @@ public class APIDiff { private static Set 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 @@ public class APIDiff { // scope only to previous or current public packages Set prevPublicApiPackages = getPublicPackages(prevJar); Set curPublicApiPackages = getPublicPackages(curJar); - + + boolean filterToPublicPackages = (prevPublicApiPackages == null && curPublicApiPackages == null) ? false : true; + prevPublicApiPackages = prevPublicApiPackages == null ? Collections.emptySet() : prevPublicApiPackages; + curPublicApiPackages = curPublicApiPackages == null ? Collections.emptySet() : curPublicApiPackages; + Set allPublicApiPackages = new HashSet<>(); allPublicApiPackages.addAll(prevPublicApiPackages); allPublicApiPackages.addAll(curPublicApiPackages); @@ -161,7 +184,10 @@ public class APIDiff { 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 8422ed2039..9198974036 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 @@ public class CLIProcessor { 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 86b6174b4b..78853c4a69 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.Map; 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 class Main { Map newVersionNumMapping = new HashMap<>(); - for (String commonJarFileName : APIDiff.getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) { + for (Pair 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 8ae574308d..16e149f854 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 @@ public class ModuleUpdates { 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 @@ public class ModuleUpdates { } 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 @@ public class ModuleUpdates { 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 class ModuleUpdates { 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));