This is an automated email from the ASF dual-hosted git repository.

ctubbsii pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo-classloaders.git


The following commit(s) were added to refs/heads/main by this push:
     new 947441f  Support additional checksum algorithms (#53)
947441f is described below

commit 947441fdff0624cad67d1d99644ac61fec29c690
Author: Christopher Tubbs <[email protected]>
AuthorDate: Thu Jan 29 12:18:53 2026 -0500

    Support additional checksum algorithms (#53)
    
    * Use checksum algorithms defined in the Resource to verify the checksum
    * Support any algorithm supported by the JVM
    * Use SHA-512 for the ContextDefinition change detection and filenames
    * Use algorithm name in filenames and cache key
    * Update javadocs and README
    * Incorporate algorithm name normalization checks in filename tests
    * Use a variety of algorithms in tests
    
    Also:
    
    * Remove use of non-public Accumulo APIs
    * Add a checkstyle rule for the non-test code to prevent new uses of
      non-public Accumulo APIs
    * Use a BufferedInputStream when downloading resources for
      ContextDefinition.create()
---
 modules/local-caching-classloader/README.md        |  32 ++--
 .../lcc/LocalCachingContextClassLoaderFactory.java |  28 ++-
 .../lcc/definition/ContextDefinition.java          |  70 ++++++--
 .../classloader/lcc/definition/Resource.java       |  30 +++-
 .../accumulo/classloader/lcc/util/LccUtils.java    |   8 +-
 .../accumulo/classloader/lcc/util/LocalStore.java  |  14 +-
 .../LocalCachingContextClassLoaderFactoryTest.java |  56 +++---
 .../MiniAccumuloClusterClassLoaderFactoryTest.java |   8 +-
 .../apache/accumulo/classloader/lcc/TestUtils.java |   7 +-
 .../classloader/lcc/util/LocalStoreTest.java       |  85 +++++----
 pom.xml                                            | 196 ++++++++++++---------
 11 files changed, 333 insertions(+), 201 deletions(-)

diff --git a/modules/local-caching-classloader/README.md 
b/modules/local-caching-classloader/README.md
index 8aecacf..a624386 100644
--- a/modules/local-caching-classloader/README.md
+++ b/modules/local-caching-classloader/README.md
@@ -62,15 +62,23 @@ Here is an example context definition file:
   "resources": [
     {
       "location": "file:/home/user/ClassLoaderTestA/TestA.jar",
-      "checksum": "a10883244d70d971ec25cbfa69b6f08f"
+      "algorithm": "MD5",
+      "checksum": "ae5e8248a9243751d60dbcaaedeb93ba"
     },
     {
       "location": "hdfs://localhost:8020/contextB/TestB.jar",
-      "checksum": "a02a3b7026528156fb782dcdecaaa097"
+      "algorithm": "SHA-256",
+      "checksum": 
"ed95fe130090fd64c2caddc3e37555ada8ba49a91bfd8ec1fd5c989d340ad0e0"
     },
     {
       "location": "http://localhost:80/TestC.jar";,
-      "checksum": "f464e66f6d07a41c656e8f4679509215"
+      "algorithm": "SHA3-224",
+      "checksum": "958f12ddc5acf87c2fe0ceed645327bb0c92e268acf915c4a374c14b"
+    },
+    {
+      "location": "https://localhost:80/TestD.jar";,
+      "algorithm": "SHA-512/224",
+      "checksum": "f7f982521ceb8ca97662973ada9b92b86de6bbaf233f14fd47efd792"
     }
   ]
 }
@@ -116,8 +124,8 @@ and processes, so be very careful when removing old 
contents to ensure that
 they are no longer needed. If a resource file is deleted from the local storage
 cache while a `ClassLoader` exists that references it, that `ClassLoader` may,
 and probably will, stop working correctly. Similarly, files that have been
-downloaded should not be modified, because checksums are only verified on first
-download, and any modification will likely cause unexpected behavior.
+downloaded should not be modified, because any modification will likely cause
+unexpected behavior to classloaders still using the file.
 
 * Do **NOT** use a temporary directory for the local storage cache location.
 * The local storage cache location **MUST** use a filesystem that supports
@@ -125,10 +133,11 @@ download, and any modification will likely cause 
unexpected behavior.
 
 ## Creating a ContextDefinition file
 
-Users may take advantage of the `ContextDefinition.create(int,URL[])` method to
-construct a `ContextDefinition` object, programmatically. This will calculate
-the checksums of the classpath elements. `ContextDefinition.toJson()` can be
-used to serialize the `ContextDefinition` to a `String` to store in a file.
+Users may take advantage of the `ContextDefinition.create(int,String,URL[])`
+method to construct a `ContextDefinition` object, programmatically. This will
+calculate the checksums of the classpath elements. `ContextDefinition.toJson()`
+can be used to serialize the `ContextDefinition` to a `String` to store in a
+file.
 
 Alternatively, if this library's jar is built and placed onto Accumulo's
 `CLASSPATH`, then one can run `bin/accumulo create-context-definition` to
@@ -136,8 +145,9 @@ create the ContextDefinition json file using the 
command-line. The resulting
 json is printed to stdout and can be redirected to a file. The command takes
 two arguments:
 
-1. the monitor interval, in seconds (e.g. `-i 300`), and
-2. a list of file URLs (e.g. `hdfs://host:port/path/to/one.jar 
file://host/path/to/two.jar`)
+1. the monitor interval, in seconds (e.g. `-i 300`),
+2. an optional checksum algorithm to use (e.g. `-a 'SHA3-512'`), and
+3. a list of file URLs (e.g. `hdfs://host:port/path/to/one.jar 
file://host/path/to/two.jar`)
 
 ## Updating a ContextDefinition file
 
diff --git 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
index 13d6a38..dd232c1 100644
--- 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
+++ 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactory.java
@@ -52,7 +52,6 @@ import 
org.apache.accumulo.classloader.lcc.jmx.ContextClassLoadersMXBean;
 import org.apache.accumulo.classloader.lcc.util.DeduplicationCache;
 import org.apache.accumulo.classloader.lcc.util.LccUtils;
 import org.apache.accumulo.classloader.lcc.util.LocalStore;
-import org.apache.accumulo.core.conf.Property;
 import org.apache.accumulo.core.spi.common.ContextClassLoaderEnvironment;
 import org.apache.accumulo.core.spi.common.ContextClassLoaderFactory;
 import org.slf4j.Logger;
@@ -68,8 +67,8 @@ import com.google.common.base.Stopwatch;
  * {@link ContextDefinition} file. The file contains an interval at which this 
class should monitor
  * the file for changes and a list of {@link Resource} objects. If the 
monitoring fails for a period
  * configurable with the {@link #UPDATE_FAILURE_GRACE_PERIOD_MINS} property, 
then monitoring will
- * discontinue until the next use of that context. Each resource is defined by 
a URL to the file and
- * an expected SHA-256 checksum.
+ * discontinue until the next use of that context. Each resource is defined by 
a URL to the file, a
+ * checksum algorithm, and a checksum.
  * <p>
  * The URLs supplied for the context definition file and for the resources may 
use any URL type with
  * a registered provider in your application, such as: file, http, https, or 
hdfs.
@@ -90,11 +89,10 @@ import com.google.common.base.Stopwatch;
  */
 public class LocalCachingContextClassLoaderFactory implements 
ContextClassLoaderFactory {
 
-  public static final String CACHE_DIR_PROPERTY =
-      Property.GENERAL_ARBITRARY_PROP_PREFIX.getKey() + 
"classloader.lcc.cache.dir";
+  public static final String CACHE_DIR_PROPERTY = 
"general.custom.classloader.lcc.cache.dir";
 
   public static final String UPDATE_FAILURE_GRACE_PERIOD_MINS =
-      Property.GENERAL_ARBITRARY_PROP_PREFIX.getKey() + 
"classloader.lcc.update.grace.minutes";
+      "general.custom.classloader.lcc.update.grace.minutes";
 
   private static final Logger LOG =
       LoggerFactory.getLogger(LocalCachingContextClassLoaderFactory.class);
@@ -215,8 +213,8 @@ public class LocalCachingContextClassLoaderFactory 
implements ContextClassLoader
     } else {
       computedDefinition = previousDefinition;
     }
-    final URLClassLoader classloader = classloaders.computeIfAbsent(
-        newCacheKey(contextLocation, computedDefinition.getChecksum()), 
(Supplier<URL[]>) () -> {
+    final URLClassLoader classloader = classloaders
+        .computeIfAbsent(newCacheKey(contextLocation, computedDefinition), 
(Supplier<URL[]>) () -> {
           try {
             return localStore.get().storeContextResources(computedDefinition);
           } catch (IOException e) {
@@ -233,16 +231,16 @@ public class LocalCachingContextClassLoaderFactory 
implements ContextClassLoader
     return ContextDefinition.fromRemoteURL(url);
   }
 
-  private static String newCacheKey(String contextLocation, String 
contextChecksum) {
-    // the checksum can't contain '-', so everything before the last one is 
the location
-    return contextLocation + "-" + contextChecksum;
+  private static String newCacheKey(String contextLocation, ContextDefinition 
contextDefinition) {
+    // the location is between the first left parenthesis and the last right 
parenthesis
+    return contextDefinition.getChecksumAlgorithm() + " (" + contextLocation + 
") = "
+        + contextDefinition.getChecksum();
   }
 
   private static boolean cacheKeyMatchesContextLocation(String cacheKey, 
String contextLocation) {
-    // the checksum can't contain '-', so everything before the last one is 
the location
-    // we can't just use startsWith(contextLocation) because there may be 
other contextLocations
-    // that contain this contextLocation as a prefix, so we do an exact match 
on the location part
-    return cacheKey.substring(0, 
cacheKey.lastIndexOf('-')).equals(contextLocation);
+    // extract the location from the parentheses in the cacheKey
+    return cacheKey.substring(cacheKey.indexOf('(') + 1, 
cacheKey.lastIndexOf(')'))
+        .equals(contextLocation);
   }
 
   private void checkMonitoredLocation(String contextLocation, long interval) {
diff --git 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/ContextDefinition.java
 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/ContextDefinition.java
index 4278e25..2e42cec 100644
--- 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/ContextDefinition.java
+++ 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/ContextDefinition.java
@@ -21,8 +21,9 @@ package org.apache.accumulo.classloader.lcc.definition;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Objects.hash;
 import static java.util.Objects.requireNonNull;
-import static org.apache.accumulo.classloader.lcc.util.LccUtils.DIGESTER;
+import static org.apache.accumulo.classloader.lcc.util.LccUtils.getDigester;
 
+import java.io.BufferedInputStream;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -34,12 +35,14 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
-import org.apache.accumulo.core.cli.Help;
 import org.apache.accumulo.start.spi.KeywordExecutable;
 
+import com.beust.jcommander.JCommander;
 import com.beust.jcommander.Parameter;
+import com.beust.jcommander.ParameterException;
 import com.google.auto.service.AutoService;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Suppliers;
@@ -51,14 +54,55 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 @AutoService(KeywordExecutable.class)
 public class ContextDefinition implements KeywordExecutable {
 
-  static class Opts extends Help {
+  static class Opts {
     @Parameter(names = {"-i", "--interval"}, required = true,
         description = "monitor interval (in seconds)", arity = 1, order = 1)
     int monitorInterval;
 
+    @Parameter(names = {"-a", "--algorithm"}, required = false,
+        description = "checksum algorithm to use (default: " + SHA_512 + ")", 
arity = 1, order = 2)
+    String algorithm = SHA_512;
+
     @Parameter(required = true, description = "classpath element URL (<url>[ 
<url>...])",
-        arity = -1, order = 2)
+        arity = -1, order = 3)
     public List<String> files = new ArrayList<>();
+
+    @Parameter(names = {"-h", "-?", "--help", "-help"}, help = true)
+    public boolean help = false;
+
+    void parseArgs(Consumer<JCommander> jcConsumer, String programName, 
String[] args,
+        Object... others) {
+      JCommander commander = new JCommander();
+      jcConsumer.accept(commander);
+      commander.addObject(this);
+      for (Object other : others) {
+        commander.addObject(other);
+      }
+      commander.setProgramName(programName);
+      try {
+        commander.parse(args);
+      } catch (ParameterException ex) {
+        commander.usage();
+        exitWithError(ex.getMessage(), 1);
+      }
+      if (help) {
+        commander.usage();
+        exit(0);
+      }
+    }
+
+    void parseArgs(String programName, String[] args, Object... others) {
+      parseArgs(jCommander -> {}, programName, args, others);
+    }
+
+    void exit(int status) {
+      System.exit(status);
+    }
+
+    void exitWithError(String message, int status) {
+      System.err.println(message);
+      exit(status);
+    }
   }
 
   // pretty-print uses Unix newline
@@ -67,13 +111,13 @@ public class ContextDefinition implements 
KeywordExecutable {
 
   @SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD",
       justification = "user-supplied URL is the intended functionality")
-  public static ContextDefinition create(int monitorIntervalSecs, URL... 
sources)
+  public static ContextDefinition create(int monitorIntervalSecs, String 
algorithm, URL... sources)
       throws IOException {
     LinkedHashSet<Resource> resources = new LinkedHashSet<>();
     for (URL u : sources) {
-      try (InputStream is = u.openStream()) {
-        String checksum = DIGESTER.digestAsHex(is);
-        resources.add(new Resource(u, checksum));
+      try (InputStream is = new BufferedInputStream(u.openStream())) {
+        String checksum = getDigester(algorithm).digestAsHex(is);
+        resources.add(new Resource(u, algorithm, checksum));
       }
     }
     return new ContextDefinition(monitorIntervalSecs, resources);
@@ -91,9 +135,11 @@ public class ContextDefinition implements KeywordExecutable 
{
     }
   }
 
+  private static final String SHA_512 = "SHA-512";
+
   // transient fields that don't go in the json
   private final transient Supplier<String> checksum =
-      Suppliers.memoize(() -> DIGESTER.digestAsHex(toJson()));
+      Suppliers.memoize(() -> 
getDigester(getChecksumAlgorithm()).digestAsHex(toJson()));
 
   // serialized fields for json
   // use a LinkedHashSet to preserve the order specified in the context file
@@ -138,6 +184,10 @@ public class ContextDefinition implements 
KeywordExecutable {
         && Objects.equals(resources, other.resources);
   }
 
+  public String getChecksumAlgorithm() {
+    return SHA_512;
+  }
+
   public String getChecksum() {
     return checksum.get();
   }
@@ -168,7 +218,7 @@ public class ContextDefinition implements KeywordExecutable 
{
     for (String f : opts.files) {
       urls[count++] = new URL(f);
     }
-    ContextDefinition def = create(opts.monitorInterval, urls);
+    ContextDefinition def = create(opts.monitorInterval, opts.algorithm, urls);
     System.out.print(def.toJson());
   }
 }
diff --git 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/Resource.java
 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/Resource.java
index 70afdde..9aef499 100644
--- 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/Resource.java
+++ 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/definition/Resource.java
@@ -20,24 +20,48 @@ package org.apache.accumulo.classloader.lcc.definition;
 
 import java.net.URL;
 import java.nio.file.Path;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.Objects;
 
 public class Resource {
 
   private URL location;
+  private String algorithm;
   private String checksum;
 
   public Resource() {}
 
-  public Resource(URL location, String checksum) {
+  public Resource(URL location, String algorithm, String checksum) {
     this.location = location;
+    this.algorithm = normalizeAlgorithm(algorithm);
     this.checksum = checksum;
   }
 
+  protected static String normalizeAlgorithm(String algorithm) {
+    try {
+      // try to normalize the algorithm name by finding the provider, then 
getting the MessageDigest
+      // service for that algorithm, and asking that service for the canonical 
algorithm name
+      return MessageDigest.getInstance(algorithm).getProvider()
+          .getService("MessageDigest", algorithm).getAlgorithm();
+    } catch (NoSuchAlgorithmException e) {
+      // just keep the provided name if we can't find a provider for that 
algorithm
+      return algorithm;
+    }
+  }
+
   public URL getLocation() {
     return location;
   }
 
+  public String getAlgorithm() {
+    return algorithm;
+  }
+
+  public String getChecksum() {
+    return checksum;
+  }
+
   public String getFileName() {
     var nameAsPath = Path.of(location.getPath()).getFileName();
     if (nameAsPath == null) {
@@ -50,10 +74,6 @@ public class Resource {
     return name;
   }
 
-  public String getChecksum() {
-    return checksum;
-  }
-
   @Override
   public int hashCode() {
     return Objects.hash(location, checksum);
diff --git 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LccUtils.java
 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LccUtils.java
index 22a8f1d..a971e81 100644
--- 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LccUtils.java
+++ 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LccUtils.java
@@ -20,6 +20,7 @@ package org.apache.accumulo.classloader.lcc.util;
 
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.concurrent.ConcurrentHashMap;
 
 import 
org.apache.accumulo.classloader.lcc.LocalCachingContextClassLoaderFactory;
 import org.apache.commons.codec.digest.DigestUtils;
@@ -31,7 +32,12 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 public class LccUtils {
   private static final Logger LOG = LoggerFactory.getLogger(LccUtils.class);
 
-  public static final DigestUtils DIGESTER = new 
DigestUtils(DigestUtils.getSha256Digest());
+  private static final ConcurrentHashMap<String,DigestUtils> DIGESTERS = new 
ConcurrentHashMap<>();
+
+  // keep at most one DigestUtils instance for each algorithm
+  public static DigestUtils getDigester(String algorithm) {
+    return DIGESTERS.computeIfAbsent(algorithm, DigestUtils::new);
+  }
 
   @SuppressFBWarnings(value = "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED",
       justification = "doPrivileged is deprecated without replacement and 
removed in newer Java")
diff --git 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LocalStore.java
 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LocalStore.java
index 9d533bb..81cedad 100644
--- 
a/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LocalStore.java
+++ 
b/modules/local-caching-classloader/src/main/java/org/apache/accumulo/classloader/lcc/util/LocalStore.java
@@ -25,7 +25,7 @@ import static java.nio.file.StandardOpenOption.SYNC;
 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
 import static java.nio.file.StandardOpenOption.WRITE;
 import static java.util.Objects.requireNonNull;
-import static org.apache.accumulo.classloader.lcc.util.LccUtils.DIGESTER;
+import static org.apache.accumulo.classloader.lcc.util.LccUtils.getDigester;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -116,7 +116,7 @@ public final class LocalStore {
   public static String localResourceName(Resource r) {
     requireNonNull(r);
     String remoteFileName = r.getFileName();
-    String checksum = r.getChecksum();
+    String checksum = checksumForFileName(r.getAlgorithm(), r.getChecksum());
     var matcher = fileNamesWithExtensionPattern.matcher(remoteFileName);
     if (matcher.matches()) {
       return String.format("%s-%s.%s", matcher.group(1), checksum, 
matcher.group(2));
@@ -128,6 +128,10 @@ public final class LocalStore {
     return "." + requireNonNull(baseName) + "_PID" + PID + "_" + 
UUID.randomUUID() + ".tmp";
   }
 
+  static String checksumForFileName(String algorithm, String checksum) {
+    return algorithm.replace('/', '_') + "-" + checksum;
+  }
+
   /**
    * Save the {@link ContextDefinition} to the contexts directory, and all of 
its resources to the
    * resources directory.
@@ -136,7 +140,8 @@ public final class LocalStore {
     requireNonNull(contextDefinition, "definition must be supplied");
     // use a LinkedHashSet to preserve the order of the context resources
     final Set<Path> localFiles = new LinkedHashSet<>();
-    final String destinationName = contextDefinition.getChecksum() + ".json";
+    final String destinationName = 
checksumForFileName(contextDefinition.getChecksumAlgorithm(),
+        contextDefinition.getChecksum()) + ".json";
     try {
       storeContextDefinition(contextDefinition, destinationName);
       boolean successful = false;
@@ -310,9 +315,10 @@ public final class LocalStore {
   }
 
   private void verifyDownload(Resource resource, Path downloadPath, Closeable 
cleanUpAction) {
+    final String algorithm = resource.getAlgorithm();
     final String checksum;
     try {
-      checksum = DIGESTER.digestAsHex(downloadPath);
+      checksum = getDigester(algorithm).digestAsHex(downloadPath);
     } catch (IOException e) {
       throw new UncheckedIOException("Unable to perform checksum verification 
on " + downloadPath
           + " for resource " + resource.getLocation(), e);
diff --git 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
index 8a0cb31..72d8824 100644
--- 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
+++ 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/LocalCachingContextClassLoaderFactoryTest.java
@@ -70,6 +70,8 @@ import com.google.gson.JsonSyntaxException;
 public class LocalCachingContextClassLoaderFactoryTest {
 
   protected static final int MONITOR_INTERVAL_SECS = 5;
+  // MD5 sum for "bad"
+  private static final String BAD_MD5 = "bae60998ffe4923b131e3d6e4c19993e";
   private static MiniDFSCluster hdfs;
   private static FileSystem fs;
   private static Server jetty;
@@ -128,7 +130,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     final URL jarCJettyLocation = jetty.getURI().resolve("TestC.jar").toURL();
 
     // ContextDefinition with all jars
-    var allJarsDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation,
+    var allJarsDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
"SHA-512", jarAOrigLocation,
         jarBHdfsLocation, jarCJettyLocation, jarDOrigLocation);
     String allJarsDefJson = allJarsDef.toJson();
 
@@ -225,7 +227,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
   @Test
   public void testInitialInvalidJson() throws Exception {
     // Create a new context definition file in HDFS, but with invalid content
-    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     // write out invalid json
     final var invalid = createContextDefinitionFile(fs, 
"InvalidContextDefinitionFile.json",
         def.toJson().substring(0, 4));
@@ -239,7 +241,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testInitial() throws Exception {
-    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var initial =
         createContextDefinitionFile(fs, "InitialContextDefinitionFile.json", 
def.toJson());
     final URL initialDefUrl = new URL(fs.getUri().toString() + 
initial.toUri().toString());
@@ -263,7 +265,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     Files.copy(jarAPath, jarACopy, StandardCopyOption.REPLACE_EXISTING);
     assertTrue(Files.exists(jarACopy));
 
-    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarACopy.toUri().toURL());
+    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarACopy.toUri().toURL());
 
     Files.delete(jarACopy);
     assertTrue(!Files.exists(jarACopy));
@@ -289,7 +291,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
   @Test
   public void testInitialBadResourceURL() throws Exception {
     LinkedHashSet<Resource> resources = new LinkedHashSet<>();
-    resources.add(new Resource(jarAOrigLocation, "1234"));
+    resources.add(new Resource(jarAOrigLocation, "MD5", BAD_MD5));
 
     // remove the file:// prefix from the URL
     String goodJson = new ContextDefinition(MONITOR_INTERVAL_SECS, 
resources).toJson();
@@ -313,7 +315,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testInitialBadResourceChecksum() throws Exception {
-    Resource r = new Resource(jarAOrigLocation, "1234");
+    Resource r = new Resource(jarAOrigLocation, "MD5", BAD_MD5);
     LinkedHashSet<Resource> resources = new LinkedHashSet<>();
     resources.add(r);
 
@@ -340,7 +342,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdate() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, "UpdateContextDefinitionFile.json", 
def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -353,7 +355,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     testClassFailsToLoad(cl, classD);
 
     // Update the contents of the context definition json file
-    var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarDOrigLocation);
+    var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarDOrigLocation);
     updateContextDefinitionFile(fs, defFilePath, updateDef.toJson());
 
     // wait 2x the monitor interval
@@ -371,7 +373,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdateSameClassNameDifferentContent() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, "UpdateContextDefinitionFile.json", 
def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -384,7 +386,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     testClassFailsToLoad(cl, classD);
 
     // Update the contents of the context definition json file
-    var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarEOrigLocation);
+    var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarEOrigLocation);
     updateContextDefinitionFile(fs, defFilePath, updateDef.toJson());
 
     // wait 2x the monitor interval
@@ -404,7 +406,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdateContextDefinitionEmpty() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, 
"UpdateEmptyContextDefinitionFile.json", def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -435,7 +437,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdateNonExistentResource() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, "UpdateNonExistentResource.json", 
def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -457,7 +459,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     assertTrue(!Files.exists(jarACopy));
     Files.copy(jarAPath, jarACopy, StandardCopyOption.REPLACE_EXISTING);
     assertTrue(Files.exists(jarACopy));
-    var def2 = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarACopy.toUri().toURL());
+    var def2 = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarACopy.toUri().toURL());
     Files.delete(jarACopy);
     assertTrue(!Files.exists(jarACopy));
 
@@ -478,7 +480,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdateBadResourceChecksum() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, "UpdateBadResourceChecksum.json", 
def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -490,7 +492,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     testClassFailsToLoad(cl, classC);
     testClassFailsToLoad(cl, classD);
 
-    Resource r = new Resource(jarAOrigLocation, "1234");
+    Resource r = new Resource(jarAOrigLocation, "MD5", BAD_MD5);
     LinkedHashSet<Resource> resources = new LinkedHashSet<>();
     resources.add(r);
 
@@ -513,7 +515,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdateBadResourceURL() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, "UpdateBadResourceChecksum.json", 
def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -527,7 +529,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
     // remove the file:// prefix from the URL
     LinkedHashSet<Resource> resources = new LinkedHashSet<>();
-    resources.add(new Resource(jarAOrigLocation, "1234"));
+    resources.add(new Resource(jarAOrigLocation, "MD5", BAD_MD5));
     String goodJson = new ContextDefinition(MONITOR_INTERVAL_SECS, 
resources).toJson();
     String badJson =
         goodJson.replace(jarAOrigLocation.toString(), 
jarAOrigLocation.toString().substring(6));
@@ -550,7 +552,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testUpdateInvalidJson() throws Exception {
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, 
"UpdateInvalidContextDefinitionFile.json", def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -562,7 +564,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     testClassFailsToLoad(cl, classC);
     testClassFailsToLoad(cl, classD);
 
-    var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarDOrigLocation);
+    var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarDOrigLocation);
     updateContextDefinitionFile(fs, defFilePath, 
updateDef.toJson().substring(0, 4));
 
     // wait 2x the monitor interval
@@ -595,8 +597,8 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testChangingContext() throws Exception {
-    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation, jarBOrigLocation,
-        jarCOrigLocation, jarDOrigLocation);
+    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation,
+        jarBOrigLocation, jarCOrigLocation, jarDOrigLocation);
     final var update =
         createContextDefinitionFile(fs, 
"UpdateChangingContextDefinition.json", def.toJson());
     final URL updatedDefUrl = new URL(fs.getUri().toString() + 
update.toUri().toString());
@@ -622,8 +624,8 @@ public class LocalCachingContextClassLoaderFactoryTest {
       final URL removed = updatedList.remove(0);
 
       // Update the contents of the context definition json file
-      var updateDef =
-          ContextDefinition.create(MONITOR_INTERVAL_SECS, 
updatedList.toArray(new URL[0]));
+      var updateDef = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
"SHA-512",
+          updatedList.toArray(new URL[0]));
       updateContextDefinitionFile(fs, update, updateDef.toJson());
 
       // wait 2x the monitor interval
@@ -675,7 +677,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
         Map.of(CACHE_DIR_PROPERTY, baseCacheDir, 
UPDATE_FAILURE_GRACE_PERIOD_MINS, "1"));
     localFactory.init(() -> new ConfigurationImpl(acuConf));
 
-    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation);
+    final var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation);
     final var defFilePath =
         createContextDefinitionFile(fs, "UpdateNonExistentResource.json", 
def.toJson());
     final URL updateDefUrl = new URL(fs.getUri().toString() + 
defFilePath.toUri().toString());
@@ -697,7 +699,7 @@ public class LocalCachingContextClassLoaderFactoryTest {
     assertTrue(!Files.exists(jarACopy));
     Files.copy(jarAPath, jarACopy, StandardCopyOption.REPLACE_EXISTING);
     assertTrue(Files.exists(jarACopy));
-    var def2 = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarACopy.toUri().toURL());
+    var def2 = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarACopy.toUri().toURL());
     Files.delete(jarACopy);
     assertTrue(!Files.exists(jarACopy));
 
@@ -734,8 +736,8 @@ public class LocalCachingContextClassLoaderFactoryTest {
 
   @Test
   public void testExternalFileModification() throws Exception {
-    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarAOrigLocation, jarBOrigLocation,
-        jarCOrigLocation, jarDOrigLocation);
+    var def = ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarAOrigLocation,
+        jarBOrigLocation, jarCOrigLocation, jarDOrigLocation);
     final var update =
         createContextDefinitionFile(fs, 
"UpdateChangingContextDefinition.json", def.toJson());
     final URL updatedDefUrl = new URL(fs.getUri().toString() + 
update.toUri().toString());
diff --git 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/MiniAccumuloClusterClassLoaderFactoryTest.java
 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/MiniAccumuloClusterClassLoaderFactoryTest.java
index b364354..8ebad35 100644
--- 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/MiniAccumuloClusterClassLoaderFactoryTest.java
+++ 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/MiniAccumuloClusterClassLoaderFactoryTest.java
@@ -158,8 +158,8 @@ public class MiniAccumuloClusterClassLoaderFactoryTest 
extends SharedMiniCluster
     Files.createDirectory(jsonDirPath, PERMISSIONS);
 
     // Create a context definition that only references jar A
-    final ContextDefinition testContextDef =
-        ContextDefinition.create(MONITOR_INTERVAL_SECS, jarAOrigLocation);
+    final var testContextDef =
+        ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-256", 
jarAOrigLocation);
     final String testContextDefJson = testContextDef.toJson();
     final File testContextDefFile = 
jsonDirPath.resolve("testContextDefinition.json").toFile();
     Files.writeString(testContextDefFile.toPath(), testContextDefJson, 
StandardOpenOption.CREATE);
@@ -245,7 +245,7 @@ public class MiniAccumuloClusterClassLoaderFactoryTest 
extends SharedMiniCluster
 
       // Update the context definition to point to jar B
       final ContextDefinition testContextDefUpdate =
-          ContextDefinition.create(MONITOR_INTERVAL_SECS, jarBOrigLocation);
+          ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarBOrigLocation);
       final String testContextDefUpdateJson = testContextDefUpdate.toJson();
       Files.writeString(testContextDefFile.toPath(), testContextDefUpdateJson,
           StandardOpenOption.TRUNCATE_EXISTING);
@@ -281,7 +281,7 @@ public class MiniAccumuloClusterClassLoaderFactoryTest 
extends SharedMiniCluster
       assertTrue(Files.exists(jarACopy));
 
       final ContextDefinition testContextDefUpdate2 =
-          ContextDefinition.create(MONITOR_INTERVAL_SECS, 
jarACopy.toUri().toURL());
+          ContextDefinition.create(MONITOR_INTERVAL_SECS, "SHA-512", 
jarACopy.toUri().toURL());
       Files.delete(jarACopy);
       assertTrue(!Files.exists(jarACopy));
 
diff --git 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/TestUtils.java
 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/TestUtils.java
index d625a68..9811f4e 100644
--- 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/TestUtils.java
+++ 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/TestUtils.java
@@ -19,7 +19,7 @@
 package org.apache.accumulo.classloader.lcc;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.apache.accumulo.classloader.lcc.util.LccUtils.DIGESTER;
+import static org.apache.accumulo.classloader.lcc.util.LccUtils.getDigester;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -170,9 +170,10 @@ public class TestUtils {
 
   @SuppressFBWarnings(value = "URLCONNECTION_SSRF_FD",
       justification = "user-supplied URL is the intended functionality")
-  public static String computeResourceChecksum(URL resourceLocation) throws 
IOException {
+  public static String computeResourceChecksum(String algorithm, URL 
resourceLocation)
+      throws IOException {
     try (InputStream is = resourceLocation.openStream()) {
-      return DIGESTER.digestAsHex(is);
+      return getDigester(algorithm).digestAsHex(is);
     }
   }
 
diff --git 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/util/LocalStoreTest.java
 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/util/LocalStoreTest.java
index 86e6d94..3d637ab 100644
--- 
a/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/util/LocalStoreTest.java
+++ 
b/modules/local-caching-classloader/src/test/java/org/apache/accumulo/classloader/lcc/util/LocalStoreTest.java
@@ -20,6 +20,7 @@ package org.apache.accumulo.classloader.lcc.util;
 
 import static 
org.apache.accumulo.classloader.lcc.TestUtils.testClassFailsToLoad;
 import static org.apache.accumulo.classloader.lcc.TestUtils.testClassLoads;
+import static 
org.apache.accumulo.classloader.lcc.util.LocalStore.localResourceName;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
@@ -91,12 +92,12 @@ public class LocalStoreTest {
 
     // Create ContextDefinition with all three resources
     final LinkedHashSet<Resource> resources = new LinkedHashSet<>();
-    resources
-        .add(new Resource(jarAOrigLocation, 
TestUtils.computeResourceChecksum(jarAOrigLocation)));
-    resources
-        .add(new Resource(jarBNewLocation, 
TestUtils.computeResourceChecksum(jarBOrigLocation)));
-    resources
-        .add(new Resource(jarCNewLocation, 
TestUtils.computeResourceChecksum(jarCOrigLocation)));
+    resources.add(new Resource(jarAOrigLocation, "SHA-256",
+        TestUtils.computeResourceChecksum("SHA-256", jarAOrigLocation)));
+    resources.add(new Resource(jarBNewLocation, "SHA-512",
+        TestUtils.computeResourceChecksum("SHA-512", jarBOrigLocation)));
+    resources.add(new Resource(jarCNewLocation, "SHA-1",
+        TestUtils.computeResourceChecksum("SHA-1", jarCOrigLocation)));
 
     def = new ContextDefinition(MONITOR_INTERVAL_SECS, resources);
     classA = new TestClassInfo("test.TestObjectA", "Hello from A");
@@ -151,13 +152,18 @@ public class LocalStoreTest {
     assertTrue(Files.exists(baseCacheDir));
   }
 
-  private static Resource rsrc(String filename, String checksum) {
+  private static Resource rsrc(String filename, String algorithm, String 
checksum) {
     return new Resource() {
       @Override
       public String getFileName() {
         return filename;
       }
 
+      @Override
+      public String getAlgorithm() {
+        return normalizeAlgorithm(algorithm);
+      }
+
       @Override
       public String getChecksum() {
         return checksum;
@@ -167,31 +173,42 @@ public class LocalStoreTest {
 
   @Test
   public void testLocalFileName() {
+    // regular jar, test various algorithm name normalizations
+    assertEquals("f0-MD5-chk0.jar", localResourceName(rsrc("f0.jar", "md5", 
"chk0")));
+    assertEquals("f0-MD5-chk0.jar", localResourceName(rsrc("f0.jar", "MD5", 
"chk0")));
+    assertEquals("f0-SHA-1-chk0.jar", localResourceName(rsrc("f0.jar", "sha1", 
"chk0")));
+    assertEquals("f0-SHA-1-chk0.jar", localResourceName(rsrc("f0.jar", 
"Sha-1", "chk0")));
+    assertEquals("f0-SHA-1-chk0.jar", localResourceName(rsrc("f0.jar", 
"SHA-1", "chk0")));
+    assertEquals("f0-SHA-512-chk0.jar", localResourceName(rsrc("f0.jar", 
"sha512", "chk0")));
+    assertEquals("f0-SHA-512_224-chk0.jar",
+        localResourceName(rsrc("f0.jar", "sha512/224", "chk0")));
+    assertEquals("f0-SHA3-224-chk0.jar", localResourceName(rsrc("f0.jar", 
"sha3-224", "chk0")));
+
     // regular war
-    assertEquals("f1-chk1.war", LocalStore.localResourceName(rsrc("f1.war", 
"chk1")));
+    assertEquals("f1-mock-chk1.war", localResourceName(rsrc("f1.war", "mock", 
"chk1")));
     // dotfile war
-    assertEquals(".f1-chk1.war", LocalStore.localResourceName(rsrc(".f1.war", 
"chk1")));
+    assertEquals(".f1-mock-chk1.war", localResourceName(rsrc(".f1.war", 
"mock", "chk1")));
     // regular jar (has multiple dots)
-    assertEquals("f2-1.0-chk2.jar", 
LocalStore.localResourceName(rsrc("f2-1.0.jar", "chk2")));
+    assertEquals("f2-1.0-mock-chk2.jar", localResourceName(rsrc("f2-1.0.jar", 
"mock", "chk2")));
     // dotfile jar (has multiple dots)
-    assertEquals(".f2-1.0-chk2.jar", 
LocalStore.localResourceName(rsrc(".f2-1.0.jar", "chk2")));
+    assertEquals(".f2-1.0-mock-chk2.jar", 
localResourceName(rsrc(".f2-1.0.jar", "mock", "chk2")));
     // regular file with no suffix
-    assertEquals("f3-chk3", LocalStore.localResourceName(rsrc("f3", "chk3")));
+    assertEquals("f3-mock-chk3", localResourceName(rsrc("f3", "mock", 
"chk3")));
 
     // weird files with trailing dots and no file suffix
-    assertEquals("f4.-chk4", LocalStore.localResourceName(rsrc("f4.", 
"chk4")));
-    assertEquals("f4..-chk4", LocalStore.localResourceName(rsrc("f4..", 
"chk4")));
-    assertEquals("f4...-chk4", LocalStore.localResourceName(rsrc("f4...", 
"chk4")));
+    assertEquals("f4.-mock-chk4", localResourceName(rsrc("f4.", "mock", 
"chk4")));
+    assertEquals("f4..-mock-chk4", localResourceName(rsrc("f4..", "mock", 
"chk4")));
+    assertEquals("f4...-mock-chk4", localResourceName(rsrc("f4...", "mock", 
"chk4")));
     // weird dotfiles that don't really have a suffix
-    assertEquals(".f5-chk5", LocalStore.localResourceName(rsrc(".f5", 
"chk5")));
-    assertEquals("..f5-chk5", LocalStore.localResourceName(rsrc("..f5", 
"chk5")));
+    assertEquals(".f5-mock-chk5", localResourceName(rsrc(".f5", "mock", 
"chk5")));
+    assertEquals("..f5-mock-chk5", localResourceName(rsrc("..f5", "mock", 
"chk5")));
     // weird files with weird dots, but do have a valid suffix
-    assertEquals("f6.-chk6.jar", LocalStore.localResourceName(rsrc("f6..jar", 
"chk6")));
-    assertEquals("f6..-chk6.jar", 
LocalStore.localResourceName(rsrc("f6...jar", "chk6")));
-    assertEquals(".f6-chk6.jar", LocalStore.localResourceName(rsrc(".f6.jar", 
"chk6")));
-    assertEquals("..f6-chk6.jar", 
LocalStore.localResourceName(rsrc("..f6.jar", "chk6")));
-    assertEquals(".f6.-chk6.jar", 
LocalStore.localResourceName(rsrc(".f6..jar", "chk6")));
-    assertEquals("..f6.-chk6.jar", 
LocalStore.localResourceName(rsrc("..f6..jar", "chk6")));
+    assertEquals("f6.-mock-chk6.jar", localResourceName(rsrc("f6..jar", 
"mock", "chk6")));
+    assertEquals("f6..-mock-chk6.jar", localResourceName(rsrc("f6...jar", 
"mock", "chk6")));
+    assertEquals(".f6-mock-chk6.jar", localResourceName(rsrc(".f6.jar", 
"mock", "chk6")));
+    assertEquals("..f6-mock-chk6.jar", localResourceName(rsrc("..f6.jar", 
"mock", "chk6")));
+    assertEquals(".f6.-mock-chk6.jar", localResourceName(rsrc(".f6..jar", 
"mock", "chk6")));
+    assertEquals("..f6.-mock-chk6.jar", localResourceName(rsrc("..f6..jar", 
"mock", "chk6")));
   }
 
   @Test
@@ -201,10 +218,10 @@ public class LocalStoreTest {
 
     // Confirm the 3 jars are cached locally
     assertTrue(Files.exists(baseCacheDir));
-    
assertTrue(Files.exists(baseCacheDir.resolve("contexts").resolve(def.getChecksum()
 + ".json")));
+    assertTrue(Files.exists(baseCacheDir.resolve("contexts").resolve(
+        LocalStore.checksumForFileName(def.getChecksumAlgorithm(), 
def.getChecksum()) + ".json")));
     for (Resource r : def.getResources()) {
-      assertTrue(
-          
Files.exists(baseCacheDir.resolve("resources").resolve(LocalStore.localResourceName(r))));
+      
assertTrue(Files.exists(baseCacheDir.resolve("resources").resolve(localResourceName(r))));
     }
   }
 
@@ -237,25 +254,25 @@ public class LocalStoreTest {
     // Add D
     final URL jarDOrigLocation = 
LocalStoreTest.class.getResource("/ClassLoaderTestD/TestD.jar");
     assertNotNull(jarDOrigLocation);
-    updatedResources
-        .add(new Resource(jarDOrigLocation, 
TestUtils.computeResourceChecksum(jarDOrigLocation)));
+    updatedResources.add(new Resource(jarDOrigLocation, "SHA-512",
+        TestUtils.computeResourceChecksum("SHA-512", jarDOrigLocation)));
 
     var updatedDef = new ContextDefinition(MONITOR_INTERVAL_SECS, 
updatedResources);
     urls = localStore.storeContextResources(updatedDef);
 
     // Confirm the 3 jars are cached locally
-    assertTrue(
-        
Files.exists(baseCacheDir.resolve("contexts").resolve(updatedDef.getChecksum() 
+ ".json")));
+    assertTrue(Files.exists(baseCacheDir.resolve("contexts").resolve(
+        LocalStore.checksumForFileName(updatedDef.getChecksumAlgorithm(), 
updatedDef.getChecksum())
+            + ".json")));
     for (Resource r : updatedDef.getResources()) {
       assertFalse(r.getFileName().contains("C"));
-      assertTrue(
-          
Files.exists(baseCacheDir.resolve("resources").resolve(LocalStore.localResourceName(r))));
+      
assertTrue(Files.exists(baseCacheDir.resolve("resources").resolve(localResourceName(r))));
     }
 
     assertTrue(removedResource.getFileName().contains("C"),
         "cache location should still contain 'C'");
-    assertTrue(Files.exists(
-        
baseCacheDir.resolve("resources").resolve(LocalStore.localResourceName(removedResource))));
+    assertTrue(Files
+        
.exists(baseCacheDir.resolve("resources").resolve(localResourceName(removedResource))));
 
     final var updatedContextClassLoader = LccUtils.createClassLoader("url", 
urls);
 
diff --git a/pom.xml b/pom.xml
index 16766a5..934a65c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -472,93 +472,6 @@ under the License.
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
-        <configuration>
-          <checkstyleRules>
-            <module name="Checker">
-              <property name="charset" value="UTF-8" />
-              <property name="severity" value="warning" />
-              <!-- Checks for whitespace                               -->
-              <!-- See 
https://checkstyle.sourceforge.io/config_whitespace.html -->
-              <module name="FileTabCharacter" />
-              <module name="TreeWalker">
-                <module name="OneTopLevelClass" />
-                <module name="RegexpSinglelineJava">
-                  <property name="format" value="\s+$" />
-                  <property name="message" value="Line has trailing 
whitespace." />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" 
value="[@]Deprecated([^)]*forRemoval[^)]*)" />
-                  <property name="message" value="forRemoval should not be 
used." />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" value="[@]see\s+[{][@]link" />
-                  <property name="message" value="Javadoc @see does not need 
@link: pick one or the other." />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" 
value="jline[.]internal[.]Preconditions" />
-                  <property name="message" value="Please use Guava 
Preconditions not JLine" />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" 
value="org[.]apache[.]commons[.]math[.]" />
-                  <property name="message" value="Use commons-math3 
(org.apache.commons.math3.*)" />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" 
value="org[.]junit[.]jupiter[.]api[.]Assertions;" />
-                  <property name="message" value="Use static imports for 
Assertions.* methods for consistency" />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" 
value="org[.]junit[.]jupiter[.]api[.]Assumptions;" />
-                  <property name="message" value="Use static imports for 
Assumptions.* methods for consistency" />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" value="import 
java[.]nio[.]charset[.]StandardCharsets;" />
-                  <property name="message" value="Use static imports for 
StandardCharsets.* constants for consistency" />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <!-- double escape quotes because checkstyle passes these 
through another xml parser -->
-                  <property name="format" value="&amp;quot; [+] &amp;quot;" />
-                  <property name="message" value="Unnecessary concatenation of 
string literals" />
-                </module>
-                <module name="RegexpSinglelineJava">
-                  <property name="format" 
value="com[.]google[.]common[.]cache[.]" />
-                  <property name="message" value="Please use Caffeine Cache, 
not Guava" />
-                </module>
-                <module name="OuterTypeFilename" />
-                <module name="AvoidStarImport" />
-                <module name="NoLineWrap" />
-                <module name="LeftCurly" />
-                <module name="RightCurly">
-                  <property name="tokens" value="LITERAL_TRY, LITERAL_CATCH, 
LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, CLASS_DEF, METHOD_DEF, CTOR_DEF, 
LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT" />
-                </module>
-                <module name="SeparatorWrap">
-                  <property name="tokens" value="DOT" />
-                  <property name="option" value="nl" />
-                </module>
-                <module name="SeparatorWrap">
-                  <property name="tokens" value="COMMA" />
-                  <property name="option" value="EOL" />
-                </module>
-                <module name="PackageName">
-                  <property name="format" 
value="^[a-z]+(\.[a-z][a-zA-Z0-9]*)*$" />
-                </module>
-                <module name="MethodTypeParameterName">
-                  <property name="format" 
value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
-                </module>
-                <module name="NonEmptyAtclauseDescription" />
-                <module name="JavadocMethod">
-                  <property name="allowMissingParamTags" value="true" />
-                  <property name="allowMissingReturnTag" value="true" />
-                  <property name="allowedAnnotations" 
value="Override,Test,BeforeClass,AfterClass,Before,After,BeforeAll,AfterAll,BeforeEach,AfterEach"
 />
-                </module>
-                <module name="MissingOverrideCheck" />
-                <!--Require braces for all control statements -->
-                <module name="NeedBraces" />
-              </module>
-            </module>
-          </checkstyleRules>
-          <violationSeverity>warning</violationSeverity>
-          <includeTestSourceDirectory>true</includeTestSourceDirectory>
-        </configuration>
         <dependencies>
           <dependency>
             <groupId>com.puppycrawl.tools</groupId>
@@ -572,6 +485,115 @@ under the License.
             <goals>
               <goal>check</goal>
             </goals>
+            <configuration>
+              <checkstyleRules>
+                <module name="Checker">
+                  <property name="charset" value="UTF-8" />
+                  <property name="severity" value="warning" />
+                  <!-- Checks for whitespace                               -->
+                  <!-- See 
https://checkstyle.sourceforge.io/config_whitespace.html -->
+                  <module name="FileTabCharacter" />
+                  <module name="TreeWalker">
+                    <module name="OneTopLevelClass" />
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" value="\s+$" />
+                      <property name="message" value="Line has trailing 
whitespace." />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="[@]Deprecated([^)]*forRemoval[^)]*)" />
+                      <property name="message" value="forRemoval should not be 
used." />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" value="[@]see\s+[{][@]link" />
+                      <property name="message" value="Javadoc @see does not 
need @link: pick one or the other." />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="jline[.]internal[.]Preconditions" />
+                      <property name="message" value="Please use Guava 
Preconditions not JLine" />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="org[.]apache[.]commons[.]math[.]" />
+                      <property name="message" value="Use commons-math3 
(org.apache.commons.math3.*)" />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="org[.]junit[.]jupiter[.]api[.]Assertions;" />
+                      <property name="message" value="Use static imports for 
Assertions.* methods for consistency" />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="org[.]junit[.]jupiter[.]api[.]Assumptions;" />
+                      <property name="message" value="Use static imports for 
Assumptions.* methods for consistency" />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" value="import 
java[.]nio[.]charset[.]StandardCharsets;" />
+                      <property name="message" value="Use static imports for 
StandardCharsets.* constants for consistency" />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <!-- double escape quotes because checkstyle passes 
these through another xml parser -->
+                      <property name="format" value="&amp;quot; [+] 
&amp;quot;" />
+                      <property name="message" value="Unnecessary 
concatenation of string literals" />
+                    </module>
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="com[.]google[.]common[.]cache[.]" />
+                      <property name="message" value="Please use Caffeine 
Cache, not Guava" />
+                    </module>
+                    <module name="OuterTypeFilename" />
+                    <module name="AvoidStarImport" />
+                    <module name="NoLineWrap" />
+                    <module name="LeftCurly" />
+                    <module name="RightCurly">
+                      <property name="tokens" value="LITERAL_TRY, 
LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, CLASS_DEF, 
METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, 
INSTANCE_INIT" />
+                    </module>
+                    <module name="SeparatorWrap">
+                      <property name="tokens" value="DOT" />
+                      <property name="option" value="nl" />
+                    </module>
+                    <module name="SeparatorWrap">
+                      <property name="tokens" value="COMMA" />
+                      <property name="option" value="EOL" />
+                    </module>
+                    <module name="PackageName">
+                      <property name="format" 
value="^[a-z]+([.][a-z][a-zA-Z0-9]*)*$" />
+                    </module>
+                    <module name="MethodTypeParameterName">
+                      <property name="format" 
value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" />
+                    </module>
+                    <module name="NonEmptyAtclauseDescription" />
+                    <module name="JavadocMethod">
+                      <property name="allowMissingParamTags" value="true" />
+                      <property name="allowMissingReturnTag" value="true" />
+                      <property name="allowedAnnotations" 
value="Override,Test,BeforeClass,AfterClass,Before,After,BeforeAll,AfterAll,BeforeEach,AfterEach"
 />
+                    </module>
+                    <module name="MissingOverrideCheck" />
+                    <!--Require braces for all control statements -->
+                    <module name="NeedBraces" />
+                  </module>
+                </module>
+              </checkstyleRules>
+              <violationSeverity>warning</violationSeverity>
+              <includeTestSourceDirectory>true</includeTestSourceDirectory>
+            </configuration>
+          </execution>
+          <execution>
+            <id>check-public-api</id>
+            <goals>
+              <goal>check</goal>
+            </goals>
+            <configuration>
+              <checkstyleRules>
+                <module name="Checker">
+                  <property name="charset" value="UTF-8" />
+                  <property name="severity" value="warning" />
+                  <module name="TreeWalker">
+                    <module name="RegexpSinglelineJava">
+                      <property name="format" 
value="import\s+org[.]apache[.]accumulo[.](?!(core[.](client|data|iterators|security|spi)|minicluster|hadoop|classloader|start[.]spi[.]KeywordExecutable)[.;]).*"
 />
+                      <property name="message" value="Found reference to 
non-public Accumulo API" />
+                    </module>
+                  </module>
+                </module>
+              </checkstyleRules>
+              <violationSeverity>warning</violationSeverity>
+              <includeTestSourceDirectory>false</includeTestSourceDirectory>
+            </configuration>
           </execution>
         </executions>
       </plugin>

Reply via email to