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.git


The following commit(s) were added to refs/heads/main by this push:
     new 68acf9f  Fix warnings leftover from VFS deprecation (#1804)
68acf9f is described below

commit 68acf9f101efeaf3a612b39e898a3a58090470fa
Author: Christopher Tubbs <ctubb...@apache.org>
AuthorDate: Thu Nov 26 14:17:23 2020 -0500

    Fix warnings leftover from VFS deprecation (#1804)
    
    * Fix warnings leftover from VFS deprecation
    
    This addresses warnings introduced recently and generally improves the
    overall context class loader feature and related code.
    
    Fixing warnings:
    * Deprecate some classes that exist only to support deprecated code
    * Reduce references to deprecated classes by putting them behind more
      utility methods (especially in ClassLoaderUtil)
    * Remove references to deprecated classes in javadoc `@see` tags
    * Remove unused variables
    * Remove Deprecated annotation when it would propagate and create too
      many warnings for internal classes that aren't user-facing and don't
      need to advertise their deprecation status
    * Suppress unavoidable compiler warnings due to deprecations
    * Prevent new compiler warnings from being introduced, by adding a
      flag for the compiler plugin to fail the build on warnings. Since we
      keep untriaged (unsuppressed) warnings at `0`, this should not cause a
      problem for most builds, but can be suppressed during development with
      `-Dmaven.compiler.failOnWarning=false`.
    
    Other improvements:
    * Deprecate old table.classpath.context in favor of new
      table.class.loader.context, and update Property docs
    * Update ContextClassLoaderFactory javadocs and remove declared
      RuntimeExceptions from interface definition
    * Consolidate ContextClassLoaders and ClassLoaderUtil utility classes
    * Rename LegacyVFSContextClassLoaderFactory to
      DefaultContextClassLoaderFactory
    * Use more functional and streams, wherever possible, and use generics
      where previously rawtypes or excessive casting was used
    * Remove unneeded initialize method from factory interface
    * Push down IOException handling into utility methods in
      AccumuloVFSClassLoader that were previously propagated and handled
      outside the class to simplify calling code
    
    * Fix tests
    
    Add implementation for isPropertySet in ConfigurationCopy, because it is
    used for per-table context lookup in some test code paths.
    
    * Add deprecated annotation to a few missed classes
---
 .../accumulo/core/classloader/ClassLoaderUtil.java | 81 +++++++++++++++++--
 .../core/classloader/ContextClassLoaders.java      | 88 --------------------
 .../DefaultContextClassLoaderFactory.java          | 82 +++++++++++++++++++
 .../LegacyVFSContextClassLoaderFactory.java        | 94 ----------------------
 .../accumulo/core/conf/ConfigurationCopy.java      |  5 ++
 .../apache/accumulo/core/conf/IterConfigUtil.java  |  2 +-
 .../org/apache/accumulo/core/conf/Property.java    | 42 ++++++----
 .../apache/accumulo/core/conf/PropertyType.java    |  1 +
 .../accumulo/core/file/BloomFilterLayer.java       |  4 +-
 .../cache/impl/BlockCacheManagerFactory.java       |  2 +-
 .../core/iterators/TypedValueCombiner.java         |  4 +-
 .../accumulo/core/sample/impl/SamplerFactory.java  |  3 +-
 .../core/spi/common/ContextClassLoaderFactory.java | 55 +++++++------
 .../accumulo/core/summary/SummarizerFactory.java   |  3 +-
 .../classloader/ContextClassLoaderFactoryTest.java | 14 ++--
 .../core/classloader/URLClassLoaderFactory.java    | 20 ++---
 .../core/conf/AccumuloConfigurationTest.java       | 21 ++---
 .../apache/accumulo/core/crypto/CryptoTest.java    |  2 +-
 .../minicluster/MiniAccumuloClusterTest.java       | 12 ++-
 pom.xml                                            |  2 +
 .../org/apache/accumulo/server/AbstractServer.java |  2 +
 .../accumulo/server/ServiceEnvironmentImpl.java    |  4 +-
 .../server/client/ClientServiceHandler.java        |  9 +--
 .../accumulo/server/conf/TableConfiguration.java   |  4 +-
 .../server/master/balancer/TableLoadBalancer.java  |  2 +-
 .../accumulo/server/util/LoginProperties.java      |  8 +-
 .../server/master/balancer/GroupBalancerTest.java  |  3 -
 .../master/balancer/TableLoadBalancerTest.java     |  6 +-
 .../java/org/apache/accumulo/master/Master.java    | 11 ---
 .../org/apache/accumulo/tracer/TraceServer.java    | 17 ++--
 .../org/apache/accumulo/tserver/TabletServer.java  |  7 --
 .../tserver/constraints/ConstraintChecker.java     |  2 +-
 .../accumulo/tserver/tablet/CompactableUtils.java  |  3 +-
 .../main/java/org/apache/accumulo/shell/Shell.java | 63 +++++++++------
 .../accumulo/shell/commands/ClasspathCommand.java  |  4 +-
 .../accumulo/shell/commands/ScanCommand.java       | 10 +--
 .../shell/commands/SetIterCommandTest.java         |  4 +-
 .../main/java/org/apache/accumulo/start/Main.java  | 15 ++--
 .../start/classloader/AccumuloClassLoader.java     |  2 +-
 .../vfs/AccumuloReloadingVFSClassLoader.java       |  2 +-
 .../classloader/vfs/AccumuloVFSClassLoader.java    | 63 ++++++++++-----
 .../start/classloader/vfs/ContextManager.java      | 17 ++--
 .../vfs/PostDelegatingVFSClassLoader.java          |  2 +-
 .../classloader/vfs/ReloadingClassLoader.java      |  2 +-
 .../classloader/vfs/UniqueFileReplicator.java      |  2 +-
 .../classloader/vfs/AccumuloClasspathTest.java     |  6 +-
 .../vfs/AccumuloReloadingVFSClassLoaderTest.java   |  1 +
 .../vfs/AccumuloVFSClassLoaderTest.java            |  2 +
 .../start/classloader/vfs/ContextManagerTest.java  | 10 +--
 .../org/apache/accumulo/test/ShellServerIT.java    | 34 ++++----
 .../accumulo/test/UserCompactionStrategyIT.java    |  3 +-
 .../test/functional/ConfigurableCompactionIT.java  |  2 +-
 .../accumulo/test/functional/ScannerContextIT.java | 10 ++-
 53 files changed, 451 insertions(+), 418 deletions(-)

diff --git 
a/core/src/main/java/org/apache/accumulo/core/classloader/ClassLoaderUtil.java 
b/core/src/main/java/org/apache/accumulo/core/classloader/ClassLoaderUtil.java
index 89bf835..1ac0474 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/classloader/ClassLoaderUtil.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/classloader/ClassLoaderUtil.java
@@ -18,17 +18,84 @@
  */
 package org.apache.accumulo.core.classloader;
 
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
+import org.apache.accumulo.core.conf.AccumuloConfiguration;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.common.ContextClassLoaderFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ClassLoaderUtil {
 
-  public static <U> Class<? extends U> loadClass(String contextName, String 
className,
+  private static final Logger LOG = 
LoggerFactory.getLogger(ClassLoaderUtil.class);
+  private static ContextClassLoaderFactory FACTORY;
+
+  private ClassLoaderUtil() {
+    // cannot construct; static utilities only
+  }
+
+  /**
+   * Initialize the ContextClassLoaderFactory
+   */
+  public static synchronized void initContextFactory(AccumuloConfiguration 
conf) {
+    if (FACTORY == null) {
+      LOG.debug("Creating {}", ContextClassLoaderFactory.class.getName());
+      String factoryName = 
conf.get(Property.GENERAL_CONTEXT_CLASSLOADER_FACTORY.getKey());
+      if (factoryName == null || factoryName.isEmpty()) {
+        // load the default implementation
+        LOG.info("Using default {}, which is subject to change in a future 
release",
+            ContextClassLoaderFactory.class.getName());
+        FACTORY = new DefaultContextClassLoaderFactory(conf);
+      } else {
+        // load user's selected implementation
+        try {
+          var factoryClass = 
Class.forName(factoryName).asSubclass(ContextClassLoaderFactory.class);
+          LOG.info("Creating {}: {}", 
ContextClassLoaderFactory.class.getName(), factoryName);
+          FACTORY = factoryClass.getDeclaredConstructor().newInstance();
+        } catch (ReflectiveOperationException e) {
+          throw new IllegalStateException("Unable to load and initialize 
class: " + factoryName, e);
+        }
+      }
+    } else {
+      LOG.debug("{} already initialized with {}.", 
ContextClassLoaderFactory.class.getName(),
+          FACTORY.getClass().getName());
+    }
+  }
+
+  // for testing
+  static ContextClassLoaderFactory getContextFactory() {
+    return FACTORY;
+  }
+
+  // for testing
+  static synchronized void resetContextFactoryForTests() {
+    FACTORY = null;
+  }
+
+  @SuppressWarnings("deprecation")
+  public static ClassLoader getClassLoader(String context) {
+    if (context != null && !context.isEmpty()) {
+      return FACTORY.getClassLoader(context);
+    } else {
+      return 
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.getClassLoader();
+    }
+  }
+
+  public static <U> Class<? extends U> loadClass(String context, String 
className,
       Class<U> extension) throws ClassNotFoundException {
-    if (contextName != null && !contextName.isEmpty())
-      return 
ContextClassLoaders.getContextClassLoaderFactory().getClassLoader(contextName)
-          .loadClass(className).asSubclass(extension);
-    else
-      return AccumuloVFSClassLoader.loadClass(className, extension);
+    return getClassLoader(context).loadClass(className).asSubclass(extension);
+  }
 
+  public static <U> Class<? extends U> loadClass(String className, Class<U> 
extension)
+      throws ClassNotFoundException {
+    return loadClass(null, className, extension);
   }
+
+  /**
+   * Retrieve the classloader context from a table's configuration.
+   */
+  @SuppressWarnings("removal")
+  public static String tableContext(AccumuloConfiguration conf) {
+    return conf.get(conf.resolve(Property.TABLE_CLASSLOADER_CONTEXT, 
Property.TABLE_CLASSPATH));
+  }
+
 }
diff --git 
a/core/src/main/java/org/apache/accumulo/core/classloader/ContextClassLoaders.java
 
b/core/src/main/java/org/apache/accumulo/core/classloader/ContextClassLoaders.java
deleted file mode 100644
index 92f30ff..0000000
--- 
a/core/src/main/java/org/apache/accumulo/core/classloader/ContextClassLoaders.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.accumulo.core.classloader;
-
-import java.lang.reflect.InvocationTargetException;
-
-import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.Property;
-import org.apache.accumulo.core.spi.common.ContextClassLoaderFactory;
-import org.apache.accumulo.core.util.ConfigurationImpl;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ContextClassLoaders {
-
-  private static final Logger LOG = 
LoggerFactory.getLogger(ContextClassLoaders.class);
-
-  private static ContextClassLoaderFactory FACTORY;
-  private static AccumuloConfiguration CONF;
-
-  /**
-   * Initialize the ContextClassLoaderFactory
-   *
-   * @param conf
-   *          AccumuloConfiguration object
-   */
-  public static synchronized void initialize(AccumuloConfiguration conf) 
throws Exception {
-    if (null == CONF) {
-      CONF = conf;
-      LOG.info("Creating ContextClassLoaderFactory");
-      var factoryName = 
CONF.get(Property.GENERAL_CONTEXT_CLASSLOADER_FACTORY.toString());
-      if (null == factoryName || factoryName.isEmpty()) {
-        LOG.info("No ClassLoaderFactory specified");
-        return;
-      }
-      try {
-        var factoryClass = Class.forName(factoryName);
-        if (ContextClassLoaderFactory.class.isAssignableFrom(factoryClass)) {
-          LOG.info("Creating ContextClassLoaderFactory: {}", factoryName);
-          FACTORY = ((Class<? extends ContextClassLoaderFactory>) factoryClass)
-              .getDeclaredConstructor().newInstance();
-          FACTORY.initialize(new ConfigurationImpl(CONF));
-        } else {
-          throw new RuntimeException(factoryName + " does not implement 
ContextClassLoaderFactory");
-        }
-      } catch (ClassNotFoundException | InstantiationException | 
IllegalAccessException
-          | IllegalArgumentException | InvocationTargetException | 
NoSuchMethodException
-          | SecurityException e) {
-        LOG.error(
-            "Unable to load and initialize class: {}. Ensure that the jar 
containing the ContextClassLoaderFactory is on the classpath",
-            factoryName);
-        throw e;
-      }
-    } else {
-      LOG.debug("ContextClassLoaderFactory already initialized.");
-    }
-  }
-
-  /**
-   * Get the ContextClassLoaderFactory
-   *
-   * @return the configured context classloader factory
-   */
-  public static ContextClassLoaderFactory getContextClassLoaderFactory() {
-    return FACTORY;
-  }
-
-  public static void resetForTests() {
-    CONF = null;
-  }
-
-}
diff --git 
a/core/src/main/java/org/apache/accumulo/core/classloader/DefaultContextClassLoaderFactory.java
 
b/core/src/main/java/org/apache/accumulo/core/classloader/DefaultContextClassLoaderFactory.java
new file mode 100644
index 0000000..3613842
--- /dev/null
+++ 
b/core/src/main/java/org/apache/accumulo/core/classloader/DefaultContextClassLoaderFactory.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.accumulo.core.classloader;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import org.apache.accumulo.core.conf.AccumuloConfiguration;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.common.ContextClassLoaderFactory;
+import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The default implementation of ContextClassLoaderFactory. This 
implementation is subject to change
+ * over time. It currently implements the legacy context class loading 
behavior based on Accumulo's
+ * custom class loaders and commons-vfs2. In future, it may simply return the 
system class loader
+ * for all requested contexts. This class is used internally to Accumulo only, 
and should not be
+ * used by users in their configuration.
+ */
+@SuppressWarnings({"deprecation", "removal"})
+public class DefaultContextClassLoaderFactory implements 
ContextClassLoaderFactory {
+
+  private static final AtomicBoolean isInstantiated = new AtomicBoolean(false);
+  private static final Logger LOG = 
LoggerFactory.getLogger(DefaultContextClassLoaderFactory.class);
+  private static final String className = 
DefaultContextClassLoaderFactory.class.getName();
+
+  public DefaultContextClassLoaderFactory(final AccumuloConfiguration accConf) 
{
+    if (!isInstantiated.compareAndSet(false, true)) {
+      throw new IllegalStateException("Can only instantiate " + className + " 
once");
+    }
+    Supplier<Map<String,String>> contextConfigSupplier =
+        () -> 
accConf.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
+    AccumuloVFSClassLoader.setContextConfig(contextConfigSupplier);
+    LOG.debug("ContextManager configuration set");
+    startCleanupThread(contextConfigSupplier);
+  }
+
+  private static void startCleanupThread(final Supplier<Map<String,String>> 
contextConfigSupplier) {
+    new Timer(className + "-cleanup", true).scheduleAtFixedRate(new 
TimerTask() {
+      @Override
+      public void run() {
+        Map<String,String> contextConfigs = contextConfigSupplier.get();
+        LOG.trace("{}-cleanup thread, properties: {}", className, 
contextConfigs);
+        int prefixlen = 
Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey().length();
+        Set<String> contextsInUse = contextConfigs.keySet().stream()
+            .map(k -> k.substring(prefixlen)).collect(Collectors.toSet());
+        LOG.trace("{}-cleanup thread, contexts in use: {}", className, 
contextsInUse);
+        AccumuloVFSClassLoader.removeUnusedContexts(contextsInUse);
+      }
+    }, 60_000, 60_000);
+    LOG.debug("Context cleanup timer started at 60s intervals");
+  }
+
+  @Override
+  public ClassLoader getClassLoader(String contextName) {
+    return AccumuloVFSClassLoader.getContextClassLoader(contextName);
+  }
+
+}
diff --git 
a/core/src/main/java/org/apache/accumulo/core/classloader/LegacyVFSContextClassLoaderFactory.java
 
b/core/src/main/java/org/apache/accumulo/core/classloader/LegacyVFSContextClassLoaderFactory.java
deleted file mode 100644
index 6578e49..0000000
--- 
a/core/src/main/java/org/apache/accumulo/core/classloader/LegacyVFSContextClassLoaderFactory.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.accumulo.core.classloader;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import org.apache.accumulo.core.client.PluginEnvironment.Configuration;
-import org.apache.accumulo.core.conf.Property;
-import org.apache.accumulo.core.spi.common.ContextClassLoaderFactory;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
-import org.apache.accumulo.start.classloader.vfs.ContextManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Deprecated(since = "2.1.0", forRemoval = true)
-public class LegacyVFSContextClassLoaderFactory implements 
ContextClassLoaderFactory {
-
-  private static final Logger LOG =
-      LoggerFactory.getLogger(LegacyVFSContextClassLoaderFactory.class);
-
-  public void initialize(Configuration contextProperties) {
-    try {
-      AccumuloVFSClassLoader.getContextManager()
-          .setContextConfig(new ContextManager.DefaultContextsConfig() {
-            @Override
-            public Map<String,String> getVfsContextClasspathProperties() {
-              return contextProperties
-                  
.getWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey());
-            }
-          });
-      LOG.debug("ContextManager configuration set");
-      new Timer("LegacyVFSContextClassLoaderFactory-cleanup", true)
-          .scheduleAtFixedRate(new TimerTask() {
-            @Override
-            public void run() {
-              try {
-                if (LOG.isTraceEnabled()) {
-                  LOG.trace("LegacyVFSContextClassLoaderFactory-cleanup 
thread, properties: {}",
-                      contextProperties);
-                }
-                Set<String> configuredContexts = new HashSet<>();
-                
contextProperties.getWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey())
-                    .forEach((k, v) -> {
-                      configuredContexts.add(
-                          
k.substring(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey().length()));
-                    });
-                LOG.trace("LegacyVFSContextClassLoaderFactory-cleanup thread, 
contexts in use: {}",
-                    configuredContexts);
-                
AccumuloVFSClassLoader.getContextManager().removeUnusedContexts(configuredContexts);
-              } catch (IOException e) {
-                LOG.warn("{}", e.getMessage(), e);
-              }
-            }
-          }, 60000, 60000);
-      LOG.debug("Context cleanup timer started at 60s intervals");
-    } catch (IOException e) {
-      throw new UncheckedIOException(e);
-    }
-
-  }
-
-  @Override
-  public ClassLoader getClassLoader(String contextName) throws 
IllegalArgumentException {
-    try {
-      return 
AccumuloVFSClassLoader.getContextManager().getClassLoader(contextName);
-    } catch (IOException e) {
-      throw new UncheckedIOException(
-          "Error getting context class loader for context: " + contextName, e);
-    }
-  }
-
-}
diff --git 
a/core/src/main/java/org/apache/accumulo/core/conf/ConfigurationCopy.java 
b/core/src/main/java/org/apache/accumulo/core/conf/ConfigurationCopy.java
index 6336af5..3f0162b 100644
--- a/core/src/main/java/org/apache/accumulo/core/conf/ConfigurationCopy.java
+++ b/core/src/main/java/org/apache/accumulo/core/conf/ConfigurationCopy.java
@@ -111,4 +111,9 @@ public class ConfigurationCopy extends 
AccumuloConfiguration {
       return updateCount;
     }
   }
+
+  @Override
+  public boolean isPropertySet(Property prop, boolean cacheAndWatch) {
+    return copy.containsKey(prop.getKey());
+  }
 }
diff --git 
a/core/src/main/java/org/apache/accumulo/core/conf/IterConfigUtil.java 
b/core/src/main/java/org/apache/accumulo/core/conf/IterConfigUtil.java
index 82c1290..605e69f 100644
--- a/core/src/main/java/org/apache/accumulo/core/conf/IterConfigUtil.java
+++ b/core/src/main/java/org/apache/accumulo/core/conf/IterConfigUtil.java
@@ -184,7 +184,7 @@ public class IterConfigUtil {
     }
 
     IterLoad il = loadIterConf(scope, ssiList, ssio, conf);
-    il = 
il.iterEnv(env).useAccumuloClassLoader(true).context(conf.get(Property.TABLE_CLASSPATH));
+    il = 
il.iterEnv(env).useAccumuloClassLoader(true).context(ClassLoaderUtil.tableContext(conf));
     return loadIterators(source, il);
   }
 
diff --git a/core/src/main/java/org/apache/accumulo/core/conf/Property.java 
b/core/src/main/java/org/apache/accumulo/core/conf/Property.java
index f9bd878..bbc956d 100644
--- a/core/src/main/java/org/apache/accumulo/core/conf/Property.java
+++ b/core/src/main/java/org/apache/accumulo/core/conf/Property.java
@@ -26,7 +26,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 import org.apache.accumulo.core.Constants;
-import org.apache.accumulo.core.classloader.LegacyVFSContextClassLoaderFactory;
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.security.tokens.PasswordToken;
 import org.apache.accumulo.core.constraints.NoDeleteConstraint;
 import org.apache.accumulo.core.file.rfile.RFile;
@@ -40,8 +40,6 @@ import org.apache.accumulo.core.spi.scan.ScanPrioritizer;
 import org.apache.accumulo.core.spi.scan.SimpleScanDispatcher;
 import org.apache.accumulo.core.util.format.DefaultFormatter;
 import org.apache.accumulo.core.util.interpret.DefaultScanInterpreter;
-import org.apache.accumulo.start.classloader.AccumuloClassLoader;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -187,8 +185,10 @@ public enum Property {
       "Properties in this category affect the behavior of accumulo overall, 
but"
           + " do not have to be consistent throughout a cloud."),
   @Deprecated(since = "2.0.0")
-  
GENERAL_DYNAMIC_CLASSPATHS(AccumuloVFSClassLoader.DYNAMIC_CLASSPATH_PROPERTY_NAME,
-      AccumuloVFSClassLoader.DEFAULT_DYNAMIC_CLASSPATH_VALUE, 
PropertyType.STRING,
+  GENERAL_DYNAMIC_CLASSPATHS(
+      
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.DYNAMIC_CLASSPATH_PROPERTY_NAME,
+      
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.DEFAULT_DYNAMIC_CLASSPATH_VALUE,
+      PropertyType.STRING,
       "This property is deprecated since 2.0.0. A list of all of the places 
where changes "
           + "in jars or classes will force a reload of the classloader. 
Built-in dynamic class "
           + "loading will be removed in a future version. If this is needed, 
consider overriding "
@@ -197,9 +197,10 @@ public enum Property {
           + "Additionally, this property no longer does property interpolation 
of environment "
           + "variables, such as '$ACCUMULO_HOME'. Use commons-configuration 
syntax,"
           + "'${env:ACCUMULO_HOME}' instead."),
-  GENERAL_CONTEXT_CLASSLOADER_FACTORY("general.context.class.loader.factory",
-      LegacyVFSContextClassLoaderFactory.class.getName(), PropertyType.STRING,
-      "Name of classloader factory to be used to create classloaders for named 
contexts."),
+  GENERAL_CONTEXT_CLASSLOADER_FACTORY("general.context.class.loader.factory", 
"",
+      PropertyType.CLASSNAME,
+      "Name of classloader factory to be used to create classloaders for named 
contexts,"
+          + " such as per-table contexts set by 
`table.class.loader.context`."),
   GENERAL_RPC_TIMEOUT("general.rpc.timeout", "120s", PropertyType.TIMEDURATION,
       "Time to wait on I/O for simple, short RPC calls"),
   @Experimental
@@ -879,6 +880,11 @@ public enum Property {
       "The Formatter class to apply on results in the shell"),
   TABLE_INTERPRETER_CLASS("table.interepreter", 
DefaultScanInterpreter.class.getName(),
       PropertyType.STRING, "The ScanInterpreter class to apply on scan 
arguments in the shell"),
+  TABLE_CLASSLOADER_CONTEXT("table.class.loader.context", "", 
PropertyType.STRING,
+      "The context to use for loading per-table resources, such as iterators"
+          + " from the configured factory in 
`general.context.class.loader.factory`."),
+  @Deprecated(since = "2.1.0", forRemoval = true)
+  @ReplacedBy(property = TABLE_CLASSLOADER_CONTEXT)
   TABLE_CLASSPATH("table.classpath.context", "", PropertyType.STRING,
       "Per table classpath context"),
   TABLE_REPLICATION("table.replication", "false", PropertyType.BOOLEAN,
@@ -929,12 +935,14 @@ public enum Property {
   // defined by its use in AccumuloVFSClassLoader when generating the property 
documentation
   @Deprecated(since = "2.1.0", forRemoval = true)
   VFS_CLASSLOADER_SYSTEM_CLASSPATH_PROPERTY(
-      AccumuloVFSClassLoader.VFS_CLASSLOADER_SYSTEM_CLASSPATH_PROPERTY, "", 
PropertyType.STRING,
+      
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.VFS_CLASSLOADER_SYSTEM_CLASSPATH_PROPERTY,
+      "", PropertyType.STRING,
       "Configuration for a system level vfs classloader. Accumulo jar can be"
           + " configured here and loaded out of HDFS."),
   @Deprecated(since = "2.1.0", forRemoval = true)
-  
VFS_CONTEXT_CLASSPATH_PROPERTY(AccumuloVFSClassLoader.VFS_CONTEXT_CLASSPATH_PROPERTY,
 null,
-      PropertyType.PREFIX,
+  VFS_CONTEXT_CLASSPATH_PROPERTY(
+      
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.VFS_CONTEXT_CLASSPATH_PROPERTY,
+      null, PropertyType.PREFIX,
       "Properties in this category are define a classpath. These properties"
           + " start  with the category prefix, followed by a context name. The 
value is"
           + " a comma separated list of URIs. Supports full regex on filename 
alone."
@@ -948,8 +956,9 @@ public enum Property {
   // this property shouldn't be used directly; it exists solely to document 
the default value
   // defined by its use in AccumuloVFSClassLoader when generating the property 
documentation
   @Deprecated(since = "2.1.0", forRemoval = true)
-  VFS_CLASSLOADER_CACHE_DIR(AccumuloVFSClassLoader.VFS_CACHE_DIR, 
"${java.io.tmpdir}",
-      PropertyType.ABSOLUTEPATH,
+  VFS_CLASSLOADER_CACHE_DIR(
+      
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.VFS_CACHE_DIR,
+      "${java.io.tmpdir}", PropertyType.ABSOLUTEPATH,
       "The base directory to use for the vfs cache. The actual cached files 
will be located"
           + " in a subdirectory, 
`accumulo-vfs-cache-<jvmProcessName>-${user.name}`, where"
           + " `<jvmProcessName>` is determined by the JVM's internal 
management engine."
@@ -1025,7 +1034,8 @@ public enum Property {
           + "HDFS directory in which accumulo instance will run. "
           + "Do not change after accumulo is initialized."),
   @Deprecated(since = "2.0.0")
-  GENERAL_CLASSPATHS(AccumuloClassLoader.GENERAL_CLASSPATHS, "", 
PropertyType.STRING,
+  
GENERAL_CLASSPATHS(org.apache.accumulo.start.classloader.AccumuloClassLoader.GENERAL_CLASSPATHS,
+      "", PropertyType.STRING,
       "This property is deprecated since 2.0.0. The class path should instead 
be configured"
           + " by the launch environment (for example, accumulo-env.sh). A list 
of all"
           + " of the places to look for a class. Order does matter, as it will 
look for"
@@ -1371,12 +1381,11 @@ public enum Property {
    * @param defaultInstance
    *          instance to use if creation fails
    * @return new class instance, or default instance if creation failed
-   * @see AccumuloVFSClassLoader
    */
   public static <T> T 
createTableInstanceFromPropertyName(AccumuloConfiguration conf,
       Property property, Class<T> base, T defaultInstance) {
     String clazzName = conf.get(property);
-    String context = conf.get(TABLE_CLASSPATH);
+    String context = ClassLoaderUtil.tableContext(conf);
     return ConfigurationTypeHelper.getClassInstance(context, clazzName, base, 
defaultInstance);
   }
 
@@ -1392,7 +1401,6 @@ public enum Property {
    * @param defaultInstance
    *          instance to use if creation fails
    * @return new class instance, or default instance if creation failed
-   * @see AccumuloVFSClassLoader
    */
   public static <T> T createInstanceFromPropertyName(AccumuloConfiguration 
conf, Property property,
       Class<T> base, T defaultInstance) {
diff --git a/core/src/main/java/org/apache/accumulo/core/conf/PropertyType.java 
b/core/src/main/java/org/apache/accumulo/core/conf/PropertyType.java
index ce45ae9..0b607ce 100644
--- a/core/src/main/java/org/apache/accumulo/core/conf/PropertyType.java
+++ b/core/src/main/java/org/apache/accumulo/core/conf/PropertyType.java
@@ -107,6 +107,7 @@ public enum PropertyType {
           + "config file using '${env:ACCUMULO_HOME}' or similar."),
 
   // VFS_CLASSLOADER_CACHE_DIR's default value is a special case, for 
documentation purposes
+  @SuppressWarnings("removal")
   ABSOLUTEPATH("absolute path",
       x -> x == null || x.trim().isEmpty() || new Path(x.trim()).isAbsolute()
           || x.equals(Property.VFS_CLASSLOADER_CACHE_DIR.getDefaultValue()),
diff --git 
a/core/src/main/java/org/apache/accumulo/core/file/BloomFilterLayer.java 
b/core/src/main/java/org/apache/accumulo/core/file/BloomFilterLayer.java
index 8f9333e..dcc96c2 100644
--- a/core/src/main/java/org/apache/accumulo/core/file/BloomFilterLayer.java
+++ b/core/src/main/java/org/apache/accumulo/core/file/BloomFilterLayer.java
@@ -122,7 +122,7 @@ public class BloomFilterLayer {
        * load KeyFunctor
        */
       try {
-        String context = acuconf.get(Property.TABLE_CLASSPATH);
+        String context = ClassLoaderUtil.tableContext(acuconf);
         String classname = acuconf.get(Property.TABLE_BLOOM_KEY_FUNCTOR);
         Class<? extends KeyFunctor> clazz;
         if (!useAccumuloStart)
@@ -213,7 +213,7 @@ public class BloomFilterLayer {
 
       loadThreshold = acuconf.getCount(Property.TABLE_BLOOM_LOAD_THRESHOLD);
 
-      final String context = acuconf.get(Property.TABLE_CLASSPATH);
+      final String context = ClassLoaderUtil.tableContext(acuconf);
 
       loadTask = () -> {
         // no need to load the bloom filter if the map file is closed
diff --git 
a/core/src/main/java/org/apache/accumulo/core/file/blockfile/cache/impl/BlockCacheManagerFactory.java
 
b/core/src/main/java/org/apache/accumulo/core/file/blockfile/cache/impl/BlockCacheManagerFactory.java
index 2ee6b1d..5ba4b0f 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/file/blockfile/cache/impl/BlockCacheManagerFactory.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/file/blockfile/cache/impl/BlockCacheManagerFactory.java
@@ -43,7 +43,7 @@ public class BlockCacheManagerFactory {
       throws Exception {
     String impl = conf.get(Property.TSERV_CACHE_MANAGER_IMPL);
     Class<? extends BlockCacheManager> clazz =
-        ClassLoaderUtil.loadClass(null, impl, BlockCacheManager.class);
+        ClassLoaderUtil.loadClass(impl, BlockCacheManager.class);
     LOG.info("Created new block cache manager of type: {}", 
clazz.getSimpleName());
     return clazz.getDeclaredConstructor().newInstance();
   }
diff --git 
a/core/src/main/java/org/apache/accumulo/core/iterators/TypedValueCombiner.java 
b/core/src/main/java/org/apache/accumulo/core/iterators/TypedValueCombiner.java
index ba904ee..85a6640 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/iterators/TypedValueCombiner.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/iterators/TypedValueCombiner.java
@@ -128,8 +128,8 @@ public abstract class TypedValueCombiner<V> extends 
Combiner {
   protected void setEncoder(String encoderClass) {
     try {
       @SuppressWarnings("unchecked")
-      Class<? extends Encoder<V>> clazz = (Class<? extends Encoder<V>>) 
ClassLoaderUtil
-          .loadClass(null, encoderClass, Encoder.class);
+      Class<? extends Encoder<V>> clazz =
+          (Class<? extends Encoder<V>>) 
ClassLoaderUtil.loadClass(encoderClass, Encoder.class);
       encoder = clazz.getDeclaredConstructor().newInstance();
     } catch (ReflectiveOperationException e) {
       throw new IllegalArgumentException(e);
diff --git 
a/core/src/main/java/org/apache/accumulo/core/sample/impl/SamplerFactory.java 
b/core/src/main/java/org/apache/accumulo/core/sample/impl/SamplerFactory.java
index e28656d..f35d005 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/sample/impl/SamplerFactory.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/sample/impl/SamplerFactory.java
@@ -23,12 +23,11 @@ import java.io.IOException;
 import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.sample.Sampler;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.Property;
 
 public class SamplerFactory {
   public static Sampler newSampler(SamplerConfigurationImpl config, 
AccumuloConfiguration acuconf,
       boolean useAccumuloStart) throws IOException {
-    String context = acuconf.get(Property.TABLE_CLASSPATH);
+    String context = ClassLoaderUtil.tableContext(acuconf);
 
     Class<? extends Sampler> clazz;
     try {
diff --git 
a/core/src/main/java/org/apache/accumulo/core/spi/common/ContextClassLoaderFactory.java
 
b/core/src/main/java/org/apache/accumulo/core/spi/common/ContextClassLoaderFactory.java
index 9ac7862..104cfa3 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/spi/common/ContextClassLoaderFactory.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/spi/common/ContextClassLoaderFactory.java
@@ -18,39 +18,44 @@
  */
 package org.apache.accumulo.core.spi.common;
 
-import org.apache.accumulo.core.client.PluginEnvironment.Configuration;
-
 /**
- * The ClassLoaderFactory is defined by the property general.context.factory. 
The factory
- * implementation is configured externally to Accumulo and will return a 
ClassLoader for a given
- * contextName.
+ * The ContextClassLoaderFactory provides a mechanism for various Accumulo 
components to use a
+ * custom ClassLoader for specific contexts, such as loading table iterators. 
This factory is
+ * initialized at startup and supplies ClassLoaders when provided a context.
  *
- * @since 2.1.0
+ * <p>
+ * This factory can be configured using the 
<code>general.context.class.loader.factory</code>
+ * property. All implementations of this factory must have a default 
(no-argument) public
+ * constructor.
+ *
+ * <p>
+ * A default implementation is provided for Accumulo 2.x to retain existing 
context class loader
+ * behavior based on per-table configuration. However, after Accumulo 2.x, the 
default is expected
+ * to change to a simpler implementation, and users will need to provide their 
own implementation to
+ * support advanced context class loading features. Some implementations may 
be maintained by the
+ * Accumulo developers in a separate package. Check the Accumulo website or 
contact the developers
+ * for more details on the status of these implementations.
  *
+ * <p>
+ * Because this factory is expected to be instantiated early in the 
application startup process,
+ * configuration is expected to be provided within the environment (such as in 
Java system
+ * properties or process environment variables), and is 
implementation-specific.
+ *
+ * @since 2.1.0
  */
 public interface ContextClassLoaderFactory {
 
   /**
-   * Initialize the ClassLoaderFactory. Implementations may need a reference 
to the configuration so
-   * that it can clean up contexts that are no longer being used.
-   *
-   * @param contextProperties
-   *          Accumulo configuration properties
-   * @throws Exception
-   *           if error initializing ClassLoaderFactory
-   */
-  void initialize(Configuration contextProperties) throws Exception;
-
-  /**
-   * Get the classloader for the context name. Callers should not cache the 
ClassLoader result as it
-   * may change if/when the ClassLoader reloads
+   * Get the class loader for the given context. Callers should not cache the 
ClassLoader result as
+   * it may change if/when the ClassLoader reloads. Implementations should 
throw a RuntimeException
+   * of some type (such as IllegalArgumentException) if the provided context 
is not supported or
+   * fails to be constructed.
    *
-   * @param contextName
-   *          name of classloader context
-   * @return classloader configured for the context
-   * @throws IllegalArgumentException
-   *           if contextName is not supported
+   * @param context
+   *          the context that represents a class loader that is managed by 
this factory (can be
+   *          null)
+   * @return the class loader for the given context
    */
-  ClassLoader getClassLoader(String contextName) throws 
IllegalArgumentException;
+  ClassLoader getClassLoader(String context);
 
 }
diff --git 
a/core/src/main/java/org/apache/accumulo/core/summary/SummarizerFactory.java 
b/core/src/main/java/org/apache/accumulo/core/summary/SummarizerFactory.java
index 0c1a8f2..3b79694 100644
--- a/core/src/main/java/org/apache/accumulo/core/summary/SummarizerFactory.java
+++ b/core/src/main/java/org/apache/accumulo/core/summary/SummarizerFactory.java
@@ -24,7 +24,6 @@ import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.summary.Summarizer;
 import org.apache.accumulo.core.client.summary.SummarizerConfiguration;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.Property;
 
 public class SummarizerFactory {
   private ClassLoader classloader;
@@ -39,7 +38,7 @@ public class SummarizerFactory {
   }
 
   public SummarizerFactory(AccumuloConfiguration tableConfig) {
-    this.context = tableConfig.get(Property.TABLE_CLASSPATH);
+    this.context = ClassLoaderUtil.tableContext(tableConfig);
   }
 
   private Summarizer newSummarizer(String classname)
diff --git 
a/core/src/test/java/org/apache/accumulo/core/classloader/ContextClassLoaderFactoryTest.java
 
b/core/src/test/java/org/apache/accumulo/core/classloader/ContextClassLoaderFactoryTest.java
index 40fdd9b..54f9924 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/classloader/ContextClassLoaderFactoryTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/classloader/ContextClassLoaderFactoryTest.java
@@ -24,6 +24,7 @@ import java.io.File;
 import java.net.URLClassLoader;
 
 import org.apache.accumulo.core.conf.ConfigurationCopy;
+import org.apache.accumulo.core.conf.Property;
 import org.apache.commons.io.FileUtils;
 import org.junit.Before;
 import org.junit.Rule;
@@ -63,18 +64,17 @@ public class ContextClassLoaderFactoryTest {
   public void differentContexts() throws Exception {
 
     ConfigurationCopy cc = new ConfigurationCopy();
-    cc.set("general.context.class.loader.factory", 
URLClassLoaderFactory.class.getName());
-    ContextClassLoaders.resetForTests();
-    ContextClassLoaders.initialize(cc);
+    cc.set(Property.GENERAL_CONTEXT_CLASSLOADER_FACTORY.getKey(),
+        URLClassLoaderFactory.class.getName());
+    ClassLoaderUtil.resetContextFactoryForTests();
+    ClassLoaderUtil.initContextFactory(cc);
 
-    URLClassLoader cl1 =
-        (URLClassLoader) 
ContextClassLoaders.getContextClassLoaderFactory().getClassLoader(uri1);
+    URLClassLoader cl1 = (URLClassLoader) 
ClassLoaderUtil.getContextFactory().getClassLoader(uri1);
     var urls1 = cl1.getURLs();
     assertEquals(1, urls1.length);
     assertEquals(uri1, urls1[0].toString());
 
-    URLClassLoader cl2 =
-        (URLClassLoader) 
ContextClassLoaders.getContextClassLoaderFactory().getClassLoader(uri2);
+    URLClassLoader cl2 = (URLClassLoader) 
ClassLoaderUtil.getContextFactory().getClassLoader(uri2);
     var urls2 = cl2.getURLs();
     assertEquals(1, urls2.length);
     assertEquals(uri2, urls2[0].toString());
diff --git 
a/core/src/test/java/org/apache/accumulo/core/classloader/URLClassLoaderFactory.java
 
b/core/src/test/java/org/apache/accumulo/core/classloader/URLClassLoaderFactory.java
index 23bfda1..beece5f 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/classloader/URLClassLoaderFactory.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/classloader/URLClassLoaderFactory.java
@@ -21,31 +21,27 @@ package org.apache.accumulo.core.classloader;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
-import java.util.ArrayList;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
-import org.apache.accumulo.core.client.PluginEnvironment.Configuration;
 import org.apache.accumulo.core.spi.common.ContextClassLoaderFactory;
 
+// test implementation
 public class URLClassLoaderFactory implements ContextClassLoaderFactory {
 
   private static final String COMMA = ",";
 
   @Override
-  public void initialize(Configuration contextProperties) throws Exception {}
-
-  @Override
-  public ClassLoader getClassLoader(String contextName) throws 
IllegalArgumentException {
+  public ClassLoader getClassLoader(String contextName) {
     // The context name is the classpath.
-    var parts = contextName.split(COMMA);
-    var urls = new ArrayList<URL>();
-    for (String p : parts) {
+    URL[] urls = Stream.of(contextName.split(COMMA)).map(p -> {
       try {
-        urls.add(new URL(p));
+        return new URL(p);
       } catch (MalformedURLException e) {
         throw new IllegalArgumentException("Error creating URL from classpath 
segment: " + p, e);
       }
-    }
-    return URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]));
+    }).collect(Collectors.toList()).toArray(new URL[0]);
+    return URLClassLoader.newInstance(urls);
   }
 
 }
diff --git 
a/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
 
b/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
index 55f0af7..28ee628 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
@@ -37,6 +37,10 @@ import org.junit.Test;
 
 public class AccumuloConfigurationTest {
 
+  @SuppressWarnings("removal")
+  private static final Property VFS_CONTEXT_CLASSPATH_PROPERTY =
+      Property.VFS_CONTEXT_CLASSPATH_PROPERTY;
+
   @Test
   public void testGetPropertyByString() {
     AccumuloConfiguration c = DefaultConfiguration.getInstance();
@@ -227,8 +231,8 @@ public class AccumuloConfigurationTest {
     expected2.put(Property.TABLE_ITERATOR_SCAN_PREFIX.getKey() + "i1.opt", 
"o99");
     assertEquals(expected2, pm3);
 
-    Map<String,String> pm5 = 
tc.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
-    Map<String,String> pm6 = 
tc.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
+    Map<String,String> pm5 = 
tc.getAllPropertiesWithPrefix(VFS_CONTEXT_CLASSPATH_PROPERTY);
+    Map<String,String> pm6 = 
tc.getAllPropertiesWithPrefix(VFS_CONTEXT_CLASSPATH_PROPERTY);
     assertSame(pm5, pm6);
     assertEquals(0, pm5.size());
 
@@ -239,7 +243,7 @@ public class AccumuloConfigurationTest {
     Map<String,String> pm8 = 
tc.getAllPropertiesWithPrefix(Property.TABLE_ITERATOR_SCAN_PREFIX);
     assertSame(pm3, pm8);
 
-    Map<String,String> pm9 = 
tc.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
+    Map<String,String> pm9 = 
tc.getAllPropertiesWithPrefix(VFS_CONTEXT_CLASSPATH_PROPERTY);
     assertSame(pm5, pm9);
 
     tc.set(Property.TABLE_ITERATOR_SCAN_PREFIX.getKey() + "i2", "class42");
@@ -260,14 +264,13 @@ public class AccumuloConfigurationTest {
     assertSame(pmC, pmD);
     assertEquals(expected1, pmC);
 
-    tc.set(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "ctx123", 
"hdfs://ib/p1");
+    tc.set(VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "ctx123", "hdfs://ib/p1");
 
-    Map<String,String> pmE = 
tc.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
-    Map<String,String> pmF = 
tc.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
+    Map<String,String> pmE = 
tc.getAllPropertiesWithPrefix(VFS_CONTEXT_CLASSPATH_PROPERTY);
+    Map<String,String> pmF = 
tc.getAllPropertiesWithPrefix(VFS_CONTEXT_CLASSPATH_PROPERTY);
     assertSame(pmE, pmF);
     assertNotSame(pm5, pmE);
-    assertEquals(
-        Map.of(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "ctx123", 
"hdfs://ib/p1"), pmE);
+    assertEquals(Map.of(VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "ctx123", 
"hdfs://ib/p1"), pmE);
 
     Map<String,String> pmG = 
tc.getAllPropertiesWithPrefix(Property.TABLE_ITERATOR_SCAN_PREFIX);
     Map<String,String> pmH = 
tc.getAllPropertiesWithPrefix(Property.TABLE_ITERATOR_SCAN_PREFIX);
@@ -281,7 +284,7 @@ public class AccumuloConfigurationTest {
     assertSame(pmI, pmJ);
     assertEquals(expected1, pmI);
 
-    Map<String,String> pmK = 
tc.getAllPropertiesWithPrefix(Property.VFS_CONTEXT_CLASSPATH_PROPERTY);
+    Map<String,String> pmK = 
tc.getAllPropertiesWithPrefix(VFS_CONTEXT_CLASSPATH_PROPERTY);
     assertSame(pmE, pmK);
 
     Map<String,String> pmL = 
tc.getAllPropertiesWithPrefix(Property.TABLE_ITERATOR_SCAN_PREFIX);
diff --git a/core/src/test/java/org/apache/accumulo/core/crypto/CryptoTest.java 
b/core/src/test/java/org/apache/accumulo/core/crypto/CryptoTest.java
index f96730d..b0d75f4 100644
--- a/core/src/test/java/org/apache/accumulo/core/crypto/CryptoTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/crypto/CryptoTest.java
@@ -271,7 +271,7 @@ public class CryptoTest {
         "org.apache.accumulo.core.cryptoImpl.AESCryptoService");
     String configuredClass = 
aconf.get(Property.INSTANCE_CRYPTO_SERVICE.getKey());
     Class<? extends CryptoService> clazz =
-        ClassLoaderUtil.loadClass(null, configuredClass, CryptoService.class);
+        ClassLoaderUtil.loadClass(configuredClass, CryptoService.class);
     CryptoService cs = clazz.getDeclaredConstructor().newInstance();
 
     assertEquals(AESCryptoService.class, cs.getClass());
diff --git 
a/minicluster/src/test/java/org/apache/accumulo/minicluster/MiniAccumuloClusterTest.java
 
b/minicluster/src/test/java/org/apache/accumulo/minicluster/MiniAccumuloClusterTest.java
index 968de66..398322e 100644
--- 
a/minicluster/src/test/java/org/apache/accumulo/minicluster/MiniAccumuloClusterTest.java
+++ 
b/minicluster/src/test/java/org/apache/accumulo/minicluster/MiniAccumuloClusterTest.java
@@ -63,6 +63,10 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "paths not 
set by user input")
 public class MiniAccumuloClusterTest {
 
+  @SuppressWarnings("removal")
+  private static final Property VFS_CONTEXT_CLASSPATH_PROPERTY =
+      Property.VFS_CONTEXT_CLASSPATH_PROPERTY;
+
   public static File testDir;
 
   private static MiniAccumuloCluster accumulo;
@@ -180,9 +184,10 @@ public class MiniAccumuloClusterTest {
     File jarFile = folder.newFile("iterator.jar");
     FileUtils.copyURLToFile(this.getClass().getResource("/FooFilter.jar"), 
jarFile);
 
-    
conn.instanceOperations().setProperty(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey()
 + "cx1",
+    
conn.instanceOperations().setProperty(VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + 
"cx1",
         jarFile.toURI().toString());
-    conn.tableOperations().setProperty("table2", 
Property.TABLE_CLASSPATH.getKey(), "cx1");
+    conn.tableOperations().setProperty("table2", 
Property.TABLE_CLASSLOADER_CONTEXT.getKey(),
+        "cx1");
     conn.tableOperations().attachIterator("table2",
         new IteratorSetting(100, "foocensor", 
"org.apache.accumulo.test.FooFilter"));
 
@@ -212,8 +217,7 @@ public class MiniAccumuloClusterTest {
 
     assertEquals(2, count);
 
-    conn.instanceOperations()
-        .removeProperty(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + 
"cx1");
+    
conn.instanceOperations().removeProperty(VFS_CONTEXT_CLASSPATH_PROPERTY.getKey()
 + "cx1");
     conn.tableOperations().delete("table2");
   }
 
diff --git a/pom.xml b/pom.xml
index b0b4497..7a4123e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -133,6 +133,8 @@
     <jaxb.version>2.3.0.1</jaxb.version>
     <jersey.version>2.30.1</jersey.version>
     <jetty.version>9.4.27.v20200227</jetty.version>
+    <!-- prevent introduction of new compiler warnings -->
+    <maven.compiler.failOnWarning>true</maven.compiler.failOnWarning>
     <maven.compiler.release>11</maven.compiler.release>
     <maven.compiler.source>11</maven.compiler.source>
     <maven.compiler.target>11</maven.compiler.target>
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/AbstractServer.java 
b/server/base/src/main/java/org/apache/accumulo/server/AbstractServer.java
index 9ac39df..f6609b0 100644
--- a/server/base/src/main/java/org/apache/accumulo/server/AbstractServer.java
+++ b/server/base/src/main/java/org/apache/accumulo/server/AbstractServer.java
@@ -22,6 +22,7 @@ import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
 import org.apache.accumulo.core.trace.TraceUtil;
 import org.apache.accumulo.server.metrics.Metrics;
@@ -49,6 +50,7 @@ public abstract class AbstractServer implements 
AutoCloseable, Runnable {
     log.info("Version " + Constants.VERSION);
     log.info("Instance " + context.getInstanceID());
     ServerUtil.init(context, appName);
+    ClassLoaderUtil.initContextFactory(context.getConfiguration());
     this.metricsSystem = Metrics.initSystem(getClass().getSimpleName());
     TraceUtil.enableServerTraces(hostname, appName, 
context.getConfiguration());
     if (context.getSaslParams() != null) {
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/ServiceEnvironmentImpl.java
 
b/server/base/src/main/java/org/apache/accumulo/server/ServiceEnvironmentImpl.java
index 6b74454..b0845f3 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/ServiceEnvironmentImpl.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/ServiceEnvironmentImpl.java
@@ -20,10 +20,10 @@ package org.apache.accumulo.server;
 
 import java.io.IOException;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.TableNotFoundException;
 import org.apache.accumulo.core.clientImpl.Tables;
 import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
-import org.apache.accumulo.core.conf.Property;
 import org.apache.accumulo.core.data.TableId;
 import org.apache.accumulo.core.spi.common.ServiceEnvironment;
 import org.apache.accumulo.core.util.ConfigurationImpl;
@@ -62,7 +62,7 @@ public class ServiceEnvironmentImpl implements 
ServiceEnvironment {
   @Override
   public <T> T instantiate(TableId tableId, String className, Class<T> base)
       throws ReflectiveOperationException, IOException {
-    String ctx = 
srvCtx.getTableConfiguration(tableId).get(Property.TABLE_CLASSPATH);
+    String ctx = 
ClassLoaderUtil.tableContext(srvCtx.getTableConfiguration(tableId));
     return ConfigurationTypeHelper.getClassInstance(ctx, className, base);
   }
 }
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java
 
b/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java
index 34255af..8ac9f48 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/client/ClientServiceHandler.java
@@ -353,17 +353,16 @@ public class ClientServiceHandler implements 
ClientService.Iface {
     return transactionWatcher.isActive(tid);
   }
 
-  @SuppressWarnings({"rawtypes", "unchecked"})
   @Override
   public boolean checkClass(TInfo tinfo, TCredentials credentials, String 
className,
       String interfaceMatch) throws TException {
     security.authenticateUser(credentials, credentials);
 
     ClassLoader loader = getClass().getClassLoader();
-    Class shouldMatch;
+    Class<?> shouldMatch;
     try {
       shouldMatch = loader.loadClass(interfaceMatch);
-      Class test = ClassLoaderUtil.loadClass(null, className, shouldMatch);
+      Class<?> test = ClassLoaderUtil.loadClass(className, shouldMatch);
       test.getDeclaredConstructor().newInstance();
       return true;
     } catch (ClassCastException | ReflectiveOperationException e) {
@@ -386,7 +385,7 @@ public class ClientServiceHandler implements 
ClientService.Iface {
     try {
       shouldMatch = loader.loadClass(interfaceMatch);
       AccumuloConfiguration conf = context.getTableConfiguration(tableId);
-      String context = conf.get(Property.TABLE_CLASSPATH);
+      String context = ClassLoaderUtil.tableContext(conf);
       Class<?> test = ClassLoaderUtil.loadClass(context, className, 
shouldMatch);
       test.getDeclaredConstructor().newInstance();
       return true;
@@ -410,7 +409,7 @@ public class ClientServiceHandler implements 
ClientService.Iface {
     try {
       shouldMatch = loader.loadClass(interfaceMatch);
       AccumuloConfiguration conf = 
context.getNamespaceConfiguration(namespaceId);
-      String context = conf.get(Property.TABLE_CLASSPATH);
+      String context = ClassLoaderUtil.tableContext(conf);
       Class<?> test = ClassLoaderUtil.loadClass(context, className, 
shouldMatch);
       test.getDeclaredConstructor().newInstance();
       return true;
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/conf/TableConfiguration.java
 
b/server/base/src/main/java/org/apache/accumulo/server/conf/TableConfiguration.java
index eb3164a..8e24224 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/conf/TableConfiguration.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/conf/TableConfiguration.java
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Predicate;
 
 import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
 import org.apache.accumulo.core.conf.IterConfigUtil;
 import org.apache.accumulo.core.conf.Property;
@@ -75,8 +76,7 @@ public class TableConfiguration extends AccumuloConfiguration 
{
         Map<String,Map<String,String>> allOpts = new HashMap<>();
         List<IterInfo> iters =
             IterConfigUtil.parseIterConf(scope, Collections.emptyList(), 
allOpts, conf);
-        return new ParsedIteratorConfig(iters, allOpts, 
conf.get(Property.TABLE_CLASSPATH));
-
+        return new ParsedIteratorConfig(iters, allOpts, 
ClassLoaderUtil.tableContext(conf));
       }));
     }
 
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/master/balancer/TableLoadBalancer.java
 
b/server/base/src/main/java/org/apache/accumulo/server/master/balancer/TableLoadBalancer.java
index c8c9289..b13670a 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/master/balancer/TableLoadBalancer.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/master/balancer/TableLoadBalancer.java
@@ -48,7 +48,7 @@ public class TableLoadBalancer extends TabletBalancer {
   private TabletBalancer constructNewBalancerForTable(String clazzName, 
TableId tableId)
       throws Exception {
     String context = null;
-    context = 
this.context.getTableConfiguration(tableId).get(Property.TABLE_CLASSPATH);
+    context = 
ClassLoaderUtil.tableContext(this.context.getTableConfiguration(tableId));
     Class<? extends TabletBalancer> clazz =
         ClassLoaderUtil.loadClass(context, clazzName, TabletBalancer.class);
     Constructor<? extends TabletBalancer> constructor = 
clazz.getConstructor(TableId.class);
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
 
b/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
index 3b0633d..e12a588 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
@@ -18,6 +18,7 @@
  */
 package org.apache.accumulo.server.util;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
 import 
org.apache.accumulo.core.client.security.tokens.AuthenticationToken.TokenProperty;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
@@ -25,7 +26,6 @@ import org.apache.accumulo.core.conf.Property;
 import org.apache.accumulo.core.conf.SiteConfiguration;
 import org.apache.accumulo.server.ServerContext;
 import org.apache.accumulo.server.security.handler.Authenticator;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.apache.accumulo.start.spi.KeywordExecutable;
 
 import com.google.auto.service.AutoService;
@@ -47,9 +47,9 @@ public class LoginProperties implements KeywordExecutable {
   public void execute(String[] args) throws Exception {
     try (var context = new ServerContext(SiteConfiguration.auto())) {
       AccumuloConfiguration config = context.getConfiguration();
-      Authenticator authenticator = AccumuloVFSClassLoader.getClassLoader()
-          .loadClass(config.get(Property.INSTANCE_SECURITY_AUTHENTICATOR))
-          
.asSubclass(Authenticator.class).getDeclaredConstructor().newInstance();
+      Authenticator authenticator = ClassLoaderUtil
+          .loadClass(config.get(Property.INSTANCE_SECURITY_AUTHENTICATOR), 
Authenticator.class)
+          .getDeclaredConstructor().newInstance();
 
       System.out
           .println("Supported token types for " + 
authenticator.getClass().getName() + " are : ");
diff --git 
a/server/base/src/test/java/org/apache/accumulo/server/master/balancer/GroupBalancerTest.java
 
b/server/base/src/test/java/org/apache/accumulo/server/master/balancer/GroupBalancerTest.java
index 5cec0e9..f09a68f 100644
--- 
a/server/base/src/test/java/org/apache/accumulo/server/master/balancer/GroupBalancerTest.java
+++ 
b/server/base/src/test/java/org/apache/accumulo/server/master/balancer/GroupBalancerTest.java
@@ -38,11 +38,9 @@ import org.apache.accumulo.core.data.TableId;
 import org.apache.accumulo.core.dataImpl.KeyExtent;
 import org.apache.accumulo.core.master.thrift.TabletServerStatus;
 import org.apache.accumulo.core.metadata.TServerInstance;
-import org.apache.accumulo.core.metadata.schema.TabletsMetadata;
 import org.apache.accumulo.core.util.MapCounter;
 import org.apache.accumulo.server.master.state.TabletMigration;
 import org.apache.hadoop.io.Text;
-import org.easymock.EasyMock;
 import org.junit.Test;
 
 public class GroupBalancerTest {
@@ -59,7 +57,6 @@ public class GroupBalancerTest {
   public static class TabletServers {
     private final Set<TServerInstance> tservers = new HashSet<>();
     private final Map<KeyExtent,TServerInstance> tabletLocs = new HashMap<>();
-    private final TabletsMetadata mockMetadata = 
EasyMock.createMock(TabletsMetadata.class);
 
     public void addTservers(String... locs) {
       for (String loc : locs) {
diff --git 
a/server/base/src/test/java/org/apache/accumulo/server/master/balancer/TableLoadBalancerTest.java
 
b/server/base/src/test/java/org/apache/accumulo/server/master/balancer/TableLoadBalancerTest.java
index 7d3be08..b2b1c48 100644
--- 
a/server/base/src/test/java/org/apache/accumulo/server/master/balancer/TableLoadBalancerTest.java
+++ 
b/server/base/src/test/java/org/apache/accumulo/server/master/balancer/TableLoadBalancerTest.java
@@ -148,7 +148,11 @@ public class TableLoadBalancerTest {
   public void test() {
     final ServerContext context = createMockContext();
     TableConfiguration conf = createMock(TableConfiguration.class);
-    expect(conf.get(Property.TABLE_CLASSPATH)).andReturn("").anyTimes();
+    @SuppressWarnings("removal")
+    Property TABLE_CLASSPATH = Property.TABLE_CLASSPATH;
+    expect(conf.resolve(Property.TABLE_CLASSLOADER_CONTEXT, TABLE_CLASSPATH))
+        .andReturn(Property.TABLE_CLASSLOADER_CONTEXT).anyTimes();
+    
expect(conf.get(Property.TABLE_CLASSLOADER_CONTEXT)).andReturn("").anyTimes();
     
expect(context.getTableConfiguration(EasyMock.anyObject())).andReturn(conf).anyTimes();
     replay(context, conf);
 
diff --git 
a/server/manager/src/main/java/org/apache/accumulo/master/Master.java 
b/server/manager/src/main/java/org/apache/accumulo/master/Master.java
index a6b7f15..708e5c1 100644
--- a/server/manager/src/main/java/org/apache/accumulo/master/Master.java
+++ b/server/manager/src/main/java/org/apache/accumulo/master/Master.java
@@ -46,7 +46,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.accumulo.core.Constants;
-import org.apache.accumulo.core.classloader.ContextClassLoaders;
 import org.apache.accumulo.core.client.AccumuloClient;
 import org.apache.accumulo.core.client.Scanner;
 import org.apache.accumulo.core.client.TableNotFoundException;
@@ -377,16 +376,6 @@ public class Master extends AbstractServer
         Property.MASTER_TABLET_BALANCER, TabletBalancer.class, new 
DefaultLoadBalancer());
     this.tabletBalancer.init(context);
 
-    try {
-      ContextClassLoaders.initialize(aconf);
-    } catch (Exception e1) {
-      log.error("Error configuring ContextClassLoaderFactory", e1);
-      if (e1 instanceof RuntimeException) {
-        throw (RuntimeException) e1;
-      }
-      throw new RuntimeException("Error configuring 
ContextClassLoaderFactory", e1);
-    }
-
     this.security = AuditedSecurityOperation.getInstance(context);
 
     // Create the secret manager (can generate and verify delegation tokens)
diff --git 
a/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceServer.java 
b/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceServer.java
index d3859a8..1c46948 100644
--- a/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceServer.java
+++ b/server/tracer/src/main/java/org/apache/accumulo/tracer/TraceServer.java
@@ -21,7 +21,6 @@ package org.apache.accumulo.tracer;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static 
org.apache.accumulo.fate.util.UtilWaitThread.sleepUninterruptibly;
 
-import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
@@ -34,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
 import org.apache.accumulo.core.Constants;
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.Accumulo;
 import org.apache.accumulo.core.client.AccumuloClient;
 import org.apache.accumulo.core.client.AccumuloException;
@@ -60,7 +60,6 @@ import org.apache.accumulo.server.ServerOpts;
 import org.apache.accumulo.server.ServerUtil;
 import org.apache.accumulo.server.security.SecurityUtil;
 import org.apache.accumulo.server.util.time.SimpleTimer;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.apache.accumulo.tracer.thrift.RemoteSpan;
 import org.apache.accumulo.tracer.thrift.SpanReceiver.Iface;
 import org.apache.accumulo.tracer.thrift.SpanReceiver.Processor;
@@ -265,9 +264,9 @@ public class TraceServer implements Watcher, AutoCloseable {
           at = new PasswordToken(conf.get(p).getBytes(UTF_8));
         } else {
           Properties props = new Properties();
-          AuthenticationToken token =
-              
AccumuloVFSClassLoader.getClassLoader().loadClass(conf.get(Property.TRACE_TOKEN_TYPE))
-                  
.asSubclass(AuthenticationToken.class).getDeclaredConstructor().newInstance();
+          AuthenticationToken token = ClassLoaderUtil
+              .loadClass(conf.get(Property.TRACE_TOKEN_TYPE), 
AuthenticationToken.class)
+              .getDeclaredConstructor().newInstance();
 
           int prefixLength = 
Property.TRACE_TOKEN_PROPERTY_PREFIX.getKey().length();
           for (Entry<String,String> entry : loginMap.entrySet()) {
@@ -289,7 +288,7 @@ public class TraceServer implements Watcher, AutoCloseable {
         accumuloClient.tableOperations().setProperty(tableName,
             Property.TABLE_FORMATTER_CLASS.getKey(), 
TraceFormatter.class.getName());
         break;
-      } catch (AccumuloException | TableExistsException | 
TableNotFoundException | IOException
+      } catch (AccumuloException | TableExistsException | 
TableNotFoundException
           | RuntimeException ex) {
         log.info("Waiting to checking/create the trace table.", ex);
         sleepUninterruptibly(1, TimeUnit.SECONDS);
@@ -362,8 +361,8 @@ public class TraceServer implements Watcher, AutoCloseable {
 
   private static void loginTracer(AccumuloConfiguration acuConf) {
     try {
-      Class<? extends AuthenticationToken> traceTokenType = 
AccumuloVFSClassLoader.getClassLoader()
-          
.loadClass(acuConf.get(Property.TRACE_TOKEN_TYPE)).asSubclass(AuthenticationToken.class);
+      Class<? extends AuthenticationToken> traceTokenType = ClassLoaderUtil
+          .loadClass(acuConf.get(Property.TRACE_TOKEN_TYPE), 
AuthenticationToken.class);
 
       if (KerberosToken.class.isAssignableFrom(traceTokenType)) {
         // We're using Kerberos to talk to Accumulo, so check for trace user 
specific auth details.
@@ -395,7 +394,7 @@ public class TraceServer implements Watcher, AutoCloseable {
         log.info("Handling login under the assumption that Accumulo users are 
not using Kerberos.");
         SecurityUtil.serverLogin(acuConf);
       }
-    } catch (IOException | ClassNotFoundException exception) {
+    } catch (ClassNotFoundException exception) {
       final String msg =
           String.format("Failed to retrieve trace user token information based 
on property %1s.",
               Property.TRACE_TOKEN_TYPE);
diff --git 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
index 93900a2..d16d4bb 100644
--- a/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
+++ b/server/tserver/src/main/java/org/apache/accumulo/tserver/TabletServer.java
@@ -53,7 +53,6 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.accumulo.core.Constants;
-import org.apache.accumulo.core.classloader.ContextClassLoaders;
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.Durability;
 import org.apache.accumulo.core.clientImpl.DurabilityImpl;
@@ -997,12 +996,6 @@ public class TabletServer extends AbstractServer {
     clientAddress = HostAndPort.fromParts(getHostname(), 0);
 
     final AccumuloConfiguration aconf = getConfiguration();
-    try {
-      ContextClassLoaders.initialize(aconf);
-    } catch (Exception e1) {
-      log.error("Error configuring ContextClassLoaderFactory", e1);
-      throw new RuntimeException("Error configuring 
ContextClassLoaderFactory", e1);
-    }
 
     FileSystemMonitor.start(aconf, Property.TSERV_MONITOR_FS);
 
diff --git 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/constraints/ConstraintChecker.java
 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/constraints/ConstraintChecker.java
index 5a2588d..51e4cc6 100644
--- 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/constraints/ConstraintChecker.java
+++ 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/constraints/ConstraintChecker.java
@@ -47,7 +47,7 @@ public class ConstraintChecker {
     constrains = new ArrayList<>();
 
     try {
-      String context = conf.get(Property.TABLE_CLASSPATH);
+      String context = ClassLoaderUtil.tableContext(conf);
 
       for (Entry<String,String> entry : conf
           
.getAllPropertiesWithPrefix(Property.TABLE_CONSTRAINT_PREFIX).entrySet()) {
diff --git 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/CompactableUtils.java
 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/CompactableUtils.java
index 78845d6..cc6ffe8 100644
--- 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/CompactableUtils.java
+++ 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/tablet/CompactableUtils.java
@@ -33,6 +33,7 @@ import java.util.concurrent.ExecutionException;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.IteratorSetting;
 import org.apache.accumulo.core.client.PluginEnvironment;
 import org.apache.accumulo.core.client.admin.CompactionConfig;
@@ -269,7 +270,7 @@ public class CompactableUtils {
 
   static <T> T newInstance(AccumuloConfiguration tableConfig, String className,
       Class<T> baseClass) {
-    String context = tableConfig.get(Property.TABLE_CLASSPATH);
+    String context = ClassLoaderUtil.tableContext(tableConfig);
     try {
       return ConfigurationTypeHelper.getClassInstance(context, className, 
baseClass);
     } catch (IOException | ReflectiveOperationException e) {
diff --git a/shell/src/main/java/org/apache/accumulo/shell/Shell.java 
b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
index 62d4203..dc6327b 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/Shell.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
@@ -44,9 +44,11 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 import org.apache.accumulo.core.Constants;
-import org.apache.accumulo.core.classloader.ContextClassLoaders;
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.cli.ClientOpts.PasswordConverter;
 import org.apache.accumulo.core.client.Accumulo;
 import org.apache.accumulo.core.client.AccumuloClient;
@@ -161,7 +163,6 @@ import org.apache.accumulo.shell.commands.UserCommand;
 import org.apache.accumulo.shell.commands.UserPermissionsCommand;
 import org.apache.accumulo.shell.commands.UsersCommand;
 import org.apache.accumulo.shell.commands.WhoAmICommand;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.DefaultParser;
@@ -192,6 +193,8 @@ public class Shell extends ShellOptions implements 
KeywordExecutable {
   public static final Logger log = LoggerFactory.getLogger(Shell.class);
   private static final Logger audit = 
LoggerFactory.getLogger(Shell.class.getName() + ".audit");
 
+  private static final Predicate<String> IS_HELP_OPT =
+      s -> s != null && (s.equals("-" + helpOption) || s.equals("--" + 
helpLongOption));
   public static final Charset CHARSET = ISO_8859_1;
   public static final int NO_FIXED_ARG_LENGTH_CHECK = -1;
   public static final String COMMENT_PREFIX = "#";
@@ -433,14 +436,13 @@ public class Shell extends ShellOptions implements 
KeywordExecutable {
   }
 
   public ClassLoader getClassLoader(final CommandLine cl, final Shell 
shellState)
-      throws AccumuloException, TableNotFoundException, 
AccumuloSecurityException, IOException,
+      throws AccumuloException, TableNotFoundException, 
AccumuloSecurityException,
       FileSystemException {
 
     boolean tables =
         cl.hasOption(OptUtil.tableOpt().getOpt()) || 
!shellState.getTableName().isEmpty();
     boolean namespaces = cl.hasOption(OptUtil.namespaceOpt().getOpt());
 
-    String tableContext = null;
     Iterable<Entry<String,String>> tableProps;
 
     if (namespaces) {
@@ -456,27 +458,37 @@ public class Shell extends ShellOptions implements 
KeywordExecutable {
     } else {
       throw new IllegalArgumentException("No table or namespace specified");
     }
-    for (Entry<String,String> entry : tableProps) {
-      if (entry.getKey().equals(Property.TABLE_CLASSPATH.getKey())) {
-        tableContext = entry.getValue();
-      }
-    }
+    String tableContext = getTableContextFromProps(tableProps);
 
-    ClassLoader classloader;
+    if (tableContext != null && !tableContext.isEmpty()) {
+      ClassLoaderUtil.initContextFactory(new ConfigurationCopy(
+          
shellState.getAccumuloClient().instanceOperations().getSystemConfiguration()));
+    }
+    return ClassLoaderUtil.getClassLoader(tableContext);
+  }
 
-    if (tableContext != null && !tableContext.equals("")) {
-      try {
-        ContextClassLoaders.initialize(new ConfigurationCopy(
-            
shellState.getAccumuloClient().instanceOperations().getSystemConfiguration()));
-      } catch (Exception e1) {
-        log.error("Error configuring ContextClassLoaderFactory", e1);
-        throw new RuntimeException("Error configuring 
ContextClassLoaderFactory", e1);
+  private static String 
getTableContextFromProps(Iterable<Entry<String,String>> props) {
+    String tableContext = null;
+    for (Entry<String,String> entry : props) {
+      // look for either the old property or the new one, but
+      // if the new one is set, stop looking and let it take precedence
+      if (entry.getKey().equals(Property.TABLE_CLASSLOADER_CONTEXT.getKey())
+          && entry.getValue() != null && !entry.getKey().isEmpty()) {
+        return entry.getValue();
+      }
+      @SuppressWarnings("removal")
+      Property TABLE_CLASSPATH = Property.TABLE_CLASSPATH;
+      if (entry.getKey().equals(TABLE_CLASSPATH.getKey())) {
+        // don't return even if this is set; instead,
+        // keep looking, in case we find the newer property set
+        tableContext = entry.getValue();
+        if (tableContext != null && !tableContext.isEmpty()) {
+          log.warn("Deprecated table context property detected. '{}' should be 
replaced by '{}'",
+              TABLE_CLASSPATH.getKey(), 
Property.TABLE_CLASSLOADER_CONTEXT.getKey());
+        }
       }
-      classloader = 
ContextClassLoaders.getContextClassLoaderFactory().getClassLoader(tableContext);
-    } else {
-      classloader = AccumuloVFSClassLoader.getClassLoader();
     }
-    return classloader;
+    return tableContext;
   }
 
   @Override
@@ -774,11 +786,10 @@ public class Shell extends ShellOptions implements 
KeywordExecutable {
         }
         printException(e);
       } catch (ParseException e) {
-        // not really an error if the exception is a missing required
-        // option when the user is asking for help
-        if (!(e instanceof MissingOptionException
-            && (Arrays.asList(fields).contains("-" + helpOption)
-                || Arrays.asList(fields).contains("--" + helpLongOption)))) {
+        if (e instanceof MissingOptionException && 
Stream.of(fields).anyMatch(IS_HELP_OPT)) {
+          // not really an error if the exception shows a required option is 
missing
+          // and the user is asking for help
+        } else {
           ++exitCode;
           printException(e);
         }
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/commands/ClasspathCommand.java 
b/shell/src/main/java/org/apache/accumulo/shell/commands/ClasspathCommand.java
index 165da34..d0195b1 100644
--- 
a/shell/src/main/java/org/apache/accumulo/shell/commands/ClasspathCommand.java
+++ 
b/shell/src/main/java/org/apache/accumulo/shell/commands/ClasspathCommand.java
@@ -23,16 +23,16 @@ import java.io.UncheckedIOException;
 
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.shell.Shell.Command;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.apache.commons.cli.CommandLine;
 
 import jline.console.ConsoleReader;
 
 public class ClasspathCommand extends Command {
+  @SuppressWarnings("deprecation")
   @Override
   public int execute(final String fullCommand, final CommandLine cl, final 
Shell shellState) {
     final ConsoleReader reader = shellState.getReader();
-    AccumuloVFSClassLoader.printClassPath(s -> {
+    
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.printClassPath(s
 -> {
       try {
         reader.print(s);
       } catch (IOException ex) {
diff --git 
a/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java 
b/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
index 986f24b..8f5fe5f 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/commands/ScanCommand.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Map.Entry;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.AccumuloException;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
 import org.apache.accumulo.core.client.IteratorSetting;
@@ -50,7 +51,6 @@ import org.apache.accumulo.shell.Shell.PrintFile;
 import org.apache.accumulo.shell.ShellCommandException;
 import org.apache.accumulo.shell.ShellCommandException.ErrorCode;
 import org.apache.accumulo.shell.ShellUtil;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.Option;
 import org.apache.commons.cli.Options;
@@ -223,13 +223,13 @@ public class ScanCommand extends Command {
         Shell.log
             .warn("Scan Interpreter option is deprecated and will be removed 
in a future version.");
 
-        clazz = 
AccumuloVFSClassLoader.loadClass(cl.getOptionValue(interpreterOpt.getOpt()),
+        clazz = 
ClassLoaderUtil.loadClass(cl.getOptionValue(interpreterOpt.getOpt()),
             ScanInterpreter.class);
       } else if (cl.hasOption(formatterInterpeterOpt.getOpt())) {
         Shell.log
             .warn("Scan Interpreter option is deprecated and will be removed 
in a future version.");
 
-        clazz = 
AccumuloVFSClassLoader.loadClass(cl.getOptionValue(formatterInterpeterOpt.getOpt()),
+        clazz = 
ClassLoaderUtil.loadClass(cl.getOptionValue(formatterInterpeterOpt.getOpt()),
             ScanInterpreter.class);
       }
     } catch (ClassNotFoundException e) {
@@ -398,8 +398,8 @@ public class ScanCommand extends Command {
     o.addOption(interpreterOpt);
     o.addOption(formatterInterpeterOpt);
     o.addOption(timeoutOption);
-    if (Arrays.asList(new String[] {ScanCommand.class.getName(), 
GrepCommand.class.getName(),
-        EGrepCommand.class.getName()}).contains(this.getClass().getName())) {
+    if (Arrays.asList(ScanCommand.class.getName(), GrepCommand.class.getName(),
+        EGrepCommand.class.getName()).contains(this.getClass().getName())) {
       // supported subclasses must handle the output file option properly
       // only add this option to commands which handle it correctly
       o.addOption(outputFileOpt);
diff --git 
a/shell/src/test/java/org/apache/accumulo/shell/commands/SetIterCommandTest.java
 
b/shell/src/test/java/org/apache/accumulo/shell/commands/SetIterCommandTest.java
index 169ae78..3e41020 100644
--- 
a/shell/src/test/java/org/apache/accumulo/shell/commands/SetIterCommandTest.java
+++ 
b/shell/src/test/java/org/apache/accumulo/shell/commands/SetIterCommandTest.java
@@ -23,13 +23,13 @@ import java.io.Writer;
 import java.lang.reflect.Field;
 import java.util.EnumSet;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.client.AccumuloClient;
 import org.apache.accumulo.core.client.IteratorSetting;
 import org.apache.accumulo.core.client.admin.TableOperations;
 import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope;
 import org.apache.accumulo.core.iterators.SortedKeyValueIterator;
 import org.apache.accumulo.shell.Shell;
-import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 import org.apache.commons.cli.CommandLine;
 import org.easymock.EasyMock;
 import org.junit.Before;
@@ -76,7 +76,7 @@ public class SetIterCommandTest {
 
     // Loading the class
     EasyMock.expect(shellState.getClassLoader(cli, shellState))
-        .andReturn(AccumuloVFSClassLoader.getClassLoader());
+        .andReturn(ClassLoaderUtil.getClassLoader(null));
 
     // Set the output object
     Field field = reader.getClass().getSuperclass().getDeclaredField("out");
diff --git a/start/src/main/java/org/apache/accumulo/start/Main.java 
b/start/src/main/java/org/apache/accumulo/start/Main.java
index 6e04a72..f48f4ee 100644
--- a/start/src/main/java/org/apache/accumulo/start/Main.java
+++ b/start/src/main/java/org/apache/accumulo/start/Main.java
@@ -29,7 +29,6 @@ import java.util.ServiceLoader;
 import java.util.TreeMap;
 import java.util.TreeSet;
 
-import org.apache.accumulo.start.classloader.AccumuloClassLoader;
 import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.accumulo.start.spi.KeywordExecutable.UsageGroup;
 import org.slf4j.Logger;
@@ -49,8 +48,10 @@ public class Main {
       ClassLoader loader = getClassLoader();
       Class<?> confClass = null;
       try {
-        confClass =
-            
AccumuloClassLoader.getClassLoader().loadClass("org.apache.hadoop.conf.Configuration");
+        @SuppressWarnings("deprecation")
+        var deprecatedConfClass = 
org.apache.accumulo.start.classloader.AccumuloClassLoader
+            
.getClassLoader().loadClass("org.apache.hadoop.conf.Configuration");
+        confClass = deprecatedConfClass;
       } catch (ClassNotFoundException e) {
         log.error("Unable to find Hadoop Configuration class on classpath, 
check configuration.",
             e);
@@ -113,11 +114,13 @@ public class Main {
     return classLoader;
   }
 
-  public static synchronized Class<?> getVFSClassLoader()
+  @Deprecated
+  private static synchronized Class<?> getVFSClassLoader()
       throws IOException, ClassNotFoundException {
     if (vfsClassLoader == null) {
-      
Thread.currentThread().setContextClassLoader(AccumuloClassLoader.getClassLoader());
-      vfsClassLoader = AccumuloClassLoader.getClassLoader()
+      Thread.currentThread().setContextClassLoader(
+          
org.apache.accumulo.start.classloader.AccumuloClassLoader.getClassLoader());
+      vfsClassLoader = 
org.apache.accumulo.start.classloader.AccumuloClassLoader.getClassLoader()
           
.loadClass("org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader");
     }
     return vfsClassLoader;
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java
index 3dd5bdc..77b4b0c 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/AccumuloClassLoader.java
@@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
-@Deprecated(since = "2.1.0", forRemoval = true)
+@Deprecated
 public class AccumuloClassLoader {
 
   public static final String GENERAL_CLASSPATHS = "general.classpaths";
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoader.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoader.java
index 3c1904c..39a2fda 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoader.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoader.java
@@ -45,7 +45,7 @@ import org.slf4j.LoggerFactory;
  * changes in any of the files/directories that are in the classpath and will 
recreate the delegate
  * object if there is any change in the classpath.
  */
-@Deprecated(since = "2.1.0", forRemoval = true)
+@Deprecated
 public class AccumuloReloadingVFSClassLoader implements FileListener, 
ReloadingClassLoader {
 
   private static final Logger log = 
LoggerFactory.getLogger(AccumuloReloadingVFSClassLoader.class);
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoader.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoader.java
index e1a9a1f..4a1390a 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoader.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoader.java
@@ -20,6 +20,7 @@ package org.apache.accumulo.start.classloader.vfs;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.lang.management.ManagementFactory;
 import java.lang.ref.WeakReference;
 import java.net.URL;
@@ -27,6 +28,9 @@ import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
 
 import org.apache.accumulo.start.classloader.AccumuloClassLoader;
 import org.apache.commons.io.FileUtils;
@@ -68,7 +72,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  * Used to load jar dynamically.
  * </pre>
  */
-@Deprecated(since = "2.1.0", forRemoval = true)
+@Deprecated
 public class AccumuloVFSClassLoader {
 
   public static class AccumuloVFSClassLoaderShutdownThread implements Runnable 
{
@@ -110,19 +114,6 @@ public class AccumuloVFSClassLoader {
     Runtime.getRuntime().addShutdownHook(new Thread(new 
AccumuloVFSClassLoaderShutdownThread()));
   }
 
-  public static synchronized <U> Class<? extends U> loadClass(String 
classname, Class<U> extension)
-      throws ClassNotFoundException {
-    try {
-      return getClassLoader().loadClass(classname).asSubclass(extension);
-    } catch (IOException e) {
-      throw new ClassNotFoundException("IO Error loading class " + classname, 
e);
-    }
-  }
-
-  public static Class<?> loadClass(String classname) throws 
ClassNotFoundException {
-    return loadClass(classname, Object.class).asSubclass(Object.class);
-  }
-
   static FileObject[] resolve(FileSystemManager vfs, String uris) throws 
FileSystemException {
     return resolve(vfs, uris, new ArrayList<>());
   }
@@ -202,7 +193,15 @@ public class AccumuloVFSClassLoader {
     return new AccumuloReloadingVFSClassLoader(dynamicCPath, generateVfs(), 
wrapper, 1000, true);
   }
 
-  public static ClassLoader getClassLoader() throws IOException {
+  public static ClassLoader getClassLoader() {
+    try {
+      return getClassLoader_Internal();
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  private static ClassLoader getClassLoader_Internal() throws IOException {
     ReloadingClassLoader localLoader = loader;
     while (localLoader == null) {
       synchronized (lock) {
@@ -397,15 +396,37 @@ public class AccumuloVFSClassLoader {
     }
   }
 
-  public static synchronized ContextManager getContextManager() throws 
IOException {
+  public static void setContextConfig(Supplier<Map<String,String>> 
contextConfigSupplier) {
+    var config = new 
ContextManager.DefaultContextsConfig(contextConfigSupplier);
+    try {
+      getContextManager().setContextConfig(config);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  public static void removeUnusedContexts(Set<String> contextsInUse) {
+    try {
+      getContextManager().removeUnusedContexts(contextsInUse);
+    } catch (IOException e) {
+      log.warn("{}", e.getMessage(), e);
+    }
+  }
+
+  public static ClassLoader getContextClassLoader(String context) {
+    try {
+      return getContextManager().getClassLoader(context);
+    } catch (IOException e) {
+      throw new UncheckedIOException("Error getting context class loader for 
context: " + context,
+          e);
+    }
+  }
+
+  private static synchronized ContextManager getContextManager() throws 
IOException {
     if (contextManager == null) {
       getClassLoader();
       contextManager = new ContextManager(generateVfs(), () -> {
-        try {
-          return getClassLoader();
-        } catch (IOException e) {
-          throw new RuntimeException(e);
-        }
+        return getClassLoader();
       });
     }
 
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ContextManager.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ContextManager.java
index b9fd161..8c4db84 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ContextManager.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ContextManager.java
@@ -23,14 +23,15 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.function.Supplier;
 
 import org.apache.commons.vfs2.FileSystemException;
 import org.apache.commons.vfs2.FileSystemManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@Deprecated(since = "2.1.0", forRemoval = true)
-public class ContextManager {
+@Deprecated
+class ContextManager {
 
   private static final Logger log = 
LoggerFactory.getLogger(ContextManager.class);
 
@@ -79,6 +80,7 @@ public class ContextManager {
     this.parent = parent;
   }
 
+  @Deprecated
   public static class ContextConfig {
     final String name;
     final String uris;
@@ -112,15 +114,20 @@ public class ContextManager {
     ContextConfig getContextConfig(String context);
   }
 
-  public abstract static class DefaultContextsConfig implements ContextsConfig 
{
+  public static class DefaultContextsConfig implements ContextsConfig {
 
-    public abstract Map<String,String> getVfsContextClasspathProperties();
+    private final Supplier<Map<String,String>> 
vfsContextClasspathPropertiesProvider;
+
+    public DefaultContextsConfig(
+        Supplier<Map<String,String>> vfsContextClasspathPropertiesProvider) {
+      this.vfsContextClasspathPropertiesProvider = 
vfsContextClasspathPropertiesProvider;
+    }
 
     @Override
     public ContextConfig getContextConfig(String context) {
 
       String prop = AccumuloVFSClassLoader.VFS_CONTEXT_CLASSPATH_PROPERTY + 
context;
-      Map<String,String> props = getVfsContextClasspathProperties();
+      Map<String,String> props = vfsContextClasspathPropertiesProvider.get();
 
       String uris = props.get(prop);
 
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/PostDelegatingVFSClassLoader.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/PostDelegatingVFSClassLoader.java
index 0eb2ef6..4fed6cd 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/PostDelegatingVFSClassLoader.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/PostDelegatingVFSClassLoader.java
@@ -23,7 +23,7 @@ import org.apache.commons.vfs2.FileSystemException;
 import org.apache.commons.vfs2.FileSystemManager;
 import org.apache.commons.vfs2.impl.VFSClassLoader;
 
-@Deprecated(since = "2.1.0", forRemoval = true)
+@Deprecated
 public class PostDelegatingVFSClassLoader extends VFSClassLoader {
 
   public PostDelegatingVFSClassLoader(FileObject[] files, FileSystemManager 
manager,
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ReloadingClassLoader.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ReloadingClassLoader.java
index 8dae67e..ac1b807 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ReloadingClassLoader.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/ReloadingClassLoader.java
@@ -18,7 +18,7 @@
  */
 package org.apache.accumulo.start.classloader.vfs;
 
-@Deprecated(since = "2.1.0", forRemoval = true)
+@Deprecated
 public interface ReloadingClassLoader {
   ClassLoader getClassLoader();
 }
diff --git 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/UniqueFileReplicator.java
 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/UniqueFileReplicator.java
index c23bda0..e75d6c2 100644
--- 
a/start/src/main/java/org/apache/accumulo/start/classloader/vfs/UniqueFileReplicator.java
+++ 
b/start/src/main/java/org/apache/accumulo/start/classloader/vfs/UniqueFileReplicator.java
@@ -37,7 +37,7 @@ import org.slf4j.LoggerFactory;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
-@Deprecated(since = "2.1.0", forRemoval = true)
+@Deprecated
 public class UniqueFileReplicator implements VfsComponent, FileReplicator {
 
   private static final char[] TMP_RESERVED_CHARS =
diff --git 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloClasspathTest.java
 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloClasspathTest.java
index fe46cad..c1e5e2f 100644
--- 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloClasspathTest.java
+++ 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloClasspathTest.java
@@ -18,7 +18,6 @@
  */
 package org.apache.accumulo.start.classloader.vfs;
 
-import static 
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.getClassPath;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -48,4 +47,9 @@ public class AccumuloClasspathTest {
     assertFalse(getClassPath(false).contains("app"));
     assertFalse(getClassPath(false).contains("Level"));
   }
+
+  @SuppressWarnings("deprecation")
+  private String getClassPath(boolean b) {
+    return 
org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader.getClassPath(b);
+  }
 }
diff --git 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoaderTest.java
 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoaderTest.java
index 4c434b5..50206f3 100644
--- 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoaderTest.java
+++ 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloReloadingVFSClassLoaderTest.java
@@ -38,6 +38,7 @@ import org.junit.rules.TemporaryFolder;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
+@Deprecated
 @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "paths not 
set by user input")
 public class AccumuloReloadingVFSClassLoaderTest {
 
diff --git 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoaderTest.java
 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoaderTest.java
index 52b143a..93c97b3 100644
--- 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoaderTest.java
+++ 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/AccumuloVFSClassLoaderTest.java
@@ -43,6 +43,8 @@ import org.powermock.reflect.Whitebox;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
+@SuppressWarnings("deprecation")
+@Deprecated
 @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "paths not 
set by user input")
 @RunWith(PowerMockRunner.class)
 @PrepareForTest(AccumuloVFSClassLoader.class)
diff --git 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/ContextManagerTest.java
 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/ContextManagerTest.java
index 0794ecf..636a16c 100644
--- 
a/start/src/test/java/org/apache/accumulo/start/classloader/vfs/ContextManagerTest.java
+++ 
b/start/src/test/java/org/apache/accumulo/start/classloader/vfs/ContextManagerTest.java
@@ -27,7 +27,6 @@ import static org.junit.Assert.assertSame;
 import java.io.File;
 import java.util.HashSet;
 
-import org.apache.accumulo.start.classloader.vfs.ContextManager.ContextConfig;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.vfs2.FileObject;
 import org.apache.commons.vfs2.FileSystemException;
@@ -40,6 +39,7 @@ import org.junit.rules.TemporaryFolder;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
+@Deprecated
 @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "paths not 
set by user input")
 public class ContextManagerTest {
 
@@ -97,9 +97,9 @@ public class ContextManagerTest {
 
     cm.setContextConfig(context -> {
       if (context.equals("CX1")) {
-        return new ContextConfig("CX1", uri1, true);
+        return new ContextManager.ContextConfig("CX1", uri1, true);
       } else if (context.equals("CX2")) {
-        return new ContextConfig("CX2", uri2, true);
+        return new ContextManager.ContextConfig("CX2", uri2, true);
       }
       return null;
     });
@@ -139,9 +139,9 @@ public class ContextManagerTest {
 
     cm.setContextConfig(context -> {
       if (context.equals("CX1")) {
-        return new ContextConfig("CX1", uri1.toString(), true);
+        return new ContextManager.ContextConfig("CX1", uri1.toString(), true);
       } else if (context.equals("CX2")) {
-        return new ContextConfig("CX2", uri2.toString(), false);
+        return new ContextManager.ContextConfig("CX2", uri2.toString(), false);
       }
       return null;
     });
diff --git a/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java 
b/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
index 1631f50..c33d354 100644
--- a/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/ShellServerIT.java
@@ -122,6 +122,11 @@ import jline.console.ConsoleReader;
 
 @Category({MiniClusterOnlyTests.class, SunnyDayTests.class})
 public class ShellServerIT extends SharedMiniClusterBase {
+
+  @SuppressWarnings("removal")
+  private static final Property VFS_CONTEXT_CLASSPATH_PROPERTY =
+      Property.VFS_CONTEXT_CLASSPATH_PROPERTY;
+
   public static class TestOutputStream extends OutputStream {
     StringBuilder sb = new StringBuilder();
 
@@ -1667,11 +1672,12 @@ public class ShellServerIT extends 
SharedMiniClusterBase {
         fooConstraintJar);
     fooConstraintJar.deleteOnExit();
 
-    ts.exec("config -s " + Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + 
"cx1="
-        + fooFilterJar.toURI() + "," + fooConstraintJar.toURI(), true);
+    ts.exec("config -s " + VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "cx1=" + 
fooFilterJar.toURI()
+        + "," + fooConstraintJar.toURI(), true);
 
     ts.exec("createtable " + table, true);
-    ts.exec("config -t " + table + " -s " + Property.TABLE_CLASSPATH.getKey() 
+ "=cx1", true);
+    ts.exec("config -t " + table + " -s " + 
Property.TABLE_CLASSLOADER_CONTEXT.getKey() + "=cx1",
+        true);
 
     sleepUninterruptibly(200, TimeUnit.MILLISECONDS);
 
@@ -1697,7 +1703,7 @@ public class ShellServerIT extends SharedMiniClusterBase {
     ts.exec("insert ok foo q v", true);
 
     ts.exec("deletetable -f " + table, true);
-    ts.exec("config -d " + Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + 
"cx1");
+    ts.exec("config -d " + VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "cx1");
 
   }
 
@@ -1842,15 +1848,15 @@ public class ShellServerIT extends 
SharedMiniClusterBase {
     make10();
     setupFakeContextPath();
     // Add the context to the table so that setiter works.
-    result = ts.exec("config -s " + Property.VFS_CONTEXT_CLASSPATH_PROPERTY + 
FAKE_CONTEXT + "="
+    result = ts.exec("config -s " + VFS_CONTEXT_CLASSPATH_PROPERTY + 
FAKE_CONTEXT + "="
         + FAKE_CONTEXT_CLASSPATH);
-    assertEquals("root@miniInstance t> config -s " + 
Property.VFS_CONTEXT_CLASSPATH_PROPERTY
-        + FAKE_CONTEXT + "=" + FAKE_CONTEXT_CLASSPATH + "\n", result);
+    assertEquals("root@miniInstance t> config -s " + 
VFS_CONTEXT_CLASSPATH_PROPERTY + FAKE_CONTEXT
+        + "=" + FAKE_CONTEXT_CLASSPATH + "\n", result);
 
-    result = ts.exec("config -t t -s table.classpath.context=" + FAKE_CONTEXT);
-    assertEquals(
-        "root@miniInstance t> config -t t -s table.classpath.context=" + 
FAKE_CONTEXT + "\n",
-        result);
+    result = ts
+        .exec("config -t t -s " + Property.TABLE_CLASSLOADER_CONTEXT.getKey() 
+ "=" + FAKE_CONTEXT);
+    assertEquals("root@miniInstance t> config -t t -s "
+        + Property.TABLE_CLASSLOADER_CONTEXT.getKey() + "=" + FAKE_CONTEXT + 
"\n", result);
 
     result = ts.exec("setshelliter -pn baz -n reverse -p 21 -class " + 
VALUE_REVERSING_ITERATOR);
     assertTrue(result.contains("The iterator class does not implement 
OptionDescriber"));
@@ -1877,10 +1883,10 @@ public class ShellServerIT extends 
SharedMiniClusterBase {
 
     setupRealContextPath();
     // Define a new classloader context, but don't set it on the table
-    result = ts.exec("config -s " + Property.VFS_CONTEXT_CLASSPATH_PROPERTY + 
REAL_CONTEXT + "="
+    result = ts.exec("config -s " + VFS_CONTEXT_CLASSPATH_PROPERTY + 
REAL_CONTEXT + "="
         + REAL_CONTEXT_CLASSPATH);
-    assertEquals("root@miniInstance t> config -s " + 
Property.VFS_CONTEXT_CLASSPATH_PROPERTY
-        + REAL_CONTEXT + "=" + REAL_CONTEXT_CLASSPATH + "\n", result);
+    assertEquals("root@miniInstance t> config -s " + 
VFS_CONTEXT_CLASSPATH_PROPERTY + REAL_CONTEXT
+        + "=" + REAL_CONTEXT_CLASSPATH + "\n", result);
     // Override the table classloader context with the REAL implementation of
     // ValueReversingIterator, which does reverse the value.
     result = ts.exec("scan -pn baz -np -b row1 -e row1 -cc " + REAL_CONTEXT);
diff --git 
a/test/src/main/java/org/apache/accumulo/test/UserCompactionStrategyIT.java 
b/test/src/main/java/org/apache/accumulo/test/UserCompactionStrategyIT.java
index ed198f7..b8dbe11 100644
--- a/test/src/main/java/org/apache/accumulo/test/UserCompactionStrategyIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/UserCompactionStrategyIT.java
@@ -164,7 +164,8 @@ public class UserCompactionStrategyIT extends 
AccumuloClusterHarness {
       c.tableOperations().create(tableName);
       c.instanceOperations().setProperty(
           Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "context1", 
destFile.toString());
-      c.tableOperations().setProperty(tableName, 
Property.TABLE_CLASSPATH.getKey(), "context1");
+      c.tableOperations().setProperty(tableName, 
Property.TABLE_CLASSLOADER_CONTEXT.getKey(),
+          "context1");
 
       c.tableOperations().addSplits(tableName, new TreeSet<>(Arrays.asList(new 
Text("efg"))));
 
diff --git 
a/test/src/main/java/org/apache/accumulo/test/functional/ConfigurableCompactionIT.java
 
b/test/src/main/java/org/apache/accumulo/test/functional/ConfigurableCompactionIT.java
index 9efca32..155b214 100644
--- 
a/test/src/main/java/org/apache/accumulo/test/functional/ConfigurableCompactionIT.java
+++ 
b/test/src/main/java/org/apache/accumulo/test/functional/ConfigurableCompactionIT.java
@@ -121,7 +121,7 @@ public class ConfigurableCompactionIT extends 
ConfigurableMacBase {
           Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + "context1", 
destFile.toString());
       Map<String,String> props = new HashMap<>();
       props.put(Property.TABLE_MAJC_RATIO.getKey(), "10");
-      props.put(Property.TABLE_CLASSPATH.getKey(), "context1");
+      props.put(Property.TABLE_CLASSLOADER_CONTEXT.getKey(), "context1");
       // EfgCompactionStrat will only compact a tablet w/ end row of 'efg'. No 
other tablets are
       // compacted.
       props.put(Property.TABLE_COMPACTION_STRATEGY.getKey(),
diff --git 
a/test/src/main/java/org/apache/accumulo/test/functional/ScannerContextIT.java 
b/test/src/main/java/org/apache/accumulo/test/functional/ScannerContextIT.java
index fb41529..beb9fd6 100644
--- 
a/test/src/main/java/org/apache/accumulo/test/functional/ScannerContextIT.java
+++ 
b/test/src/main/java/org/apache/accumulo/test/functional/ScannerContextIT.java
@@ -52,7 +52,10 @@ import org.junit.Test;
 public class ScannerContextIT extends AccumuloClusterHarness {
 
   private static final String CONTEXT = ScannerContextIT.class.getSimpleName();
-  private static final String CONTEXT_PROPERTY = 
Property.VFS_CONTEXT_CLASSPATH_PROPERTY + CONTEXT;
+  @SuppressWarnings("removal")
+  private static final Property VFS_CONTEXT_CLASSPATH_PROPERTY =
+      Property.VFS_CONTEXT_CLASSPATH_PROPERTY;
+  private static final String CONTEXT_PROPERTY = 
VFS_CONTEXT_CLASSPATH_PROPERTY + CONTEXT;
   private static final String CONTEXT_DIR = "file://" + 
System.getProperty("user.dir") + "/target";
   private static final String CONTEXT_CLASSPATH = CONTEXT_DIR + "/Test.jar";
   private static int ITERATIONS = 10;
@@ -140,7 +143,7 @@ public class ScannerContextIT extends 
AccumuloClusterHarness {
       // Create two contexts FOO and ScanContextIT. The FOO context will point 
to a classpath
       // that contains nothing. The ScanContextIT context will point to the 
test iterators jar
       String tableContext = "FOO";
-      String tableContextProperty = Property.VFS_CONTEXT_CLASSPATH_PROPERTY + 
tableContext;
+      String tableContextProperty = VFS_CONTEXT_CLASSPATH_PROPERTY + 
tableContext;
       String tableContextDir = "file://" + System.getProperty("user.dir") + 
"/target";
       String tableContextClasspath = tableContextDir + "/TestFoo.jar";
       // Define both contexts
@@ -150,7 +153,8 @@ public class ScannerContextIT extends 
AccumuloClusterHarness {
       String tableName = getUniqueNames(1)[0];
       c.tableOperations().create(tableName);
       // Set the FOO context on the table
-      c.tableOperations().setProperty(tableName, 
Property.TABLE_CLASSPATH.getKey(), tableContext);
+      c.tableOperations().setProperty(tableName, 
Property.TABLE_CLASSLOADER_CONTEXT.getKey(),
+          tableContext);
       try (BatchWriter bw = c.createBatchWriter(tableName)) {
         for (int i = 0; i < ITERATIONS; i++) {
           Mutation m = new Mutation("row" + i);

Reply via email to