You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2021/01/14 06:03:54 UTC

[lucene-solr] branch branch_8x updated: SOLR-14155: Load all other SolrCore plugins from packages

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

noble pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/branch_8x by this push:
     new cdf4e31  SOLR-14155: Load all other SolrCore plugins from packages
cdf4e31 is described below

commit cdf4e31d86d0f5f70aa728d0e5b41ccfd786ca00
Author: noblepaul <no...@gmail.com>
AuthorDate: Thu Jan 14 16:53:57 2021 +1100

    SOLR-14155: Load all other SolrCore plugins from packages
---
 solr/CHANGES.txt                                   |   2 +
 .../org/apache/solr/core/DirectoryFactory.java     |   2 +-
 .../src/java/org/apache/solr/core/PluginBag.java   |   1 -
 .../src/java/org/apache/solr/core/SolrCore.java    |  46 +++++++--
 .../org/apache/solr/core/SolrResourceLoader.java   | 114 ++++++++++++++++-----
 .../org/apache/solr/handler/SolrConfigHandler.java |  29 +++---
 .../solr/handler/component/SearchHandler.java      |  26 ++---
 .../java/org/apache/solr/pkg/PackageListeners.java |  15 ++-
 .../solr/pkg/PackageListeningClassLoader.java      |  73 ++++++++-----
 .../org/apache/solr/pkg/PackagePluginHolder.java   |  16 ++-
 .../java/org/apache/solr/search/CacheConfig.java   |  42 ++++++--
 .../java/org/apache/solr/update/UpdateHandler.java |   6 +-
 .../solr/configsets/conf2/conf/schema.xml          |  43 ++++++++
 .../solr/configsets/conf2/conf/solrconfig.xml      |  68 ++++++++++++
 .../src/test/org/apache/solr/pkg/TestPackages.java |  81 +++++++++++++--
 15 files changed, 449 insertions(+), 115 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index e02e5ae..8e3201f 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -86,6 +86,8 @@ Improvements
 
 * SOLR-15040: Improvements to postlogs timestamp handling (Joel Bernstein)
 
+* SOLR-14155: Load all other SolrCore plugins from packages (noble)
+
 Optimizations
 ---------------------
 * SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)
diff --git a/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java b/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java
index 5736692..619e7c9 100644
--- a/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java
+++ b/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java
@@ -419,7 +419,7 @@ public abstract class DirectoryFactory implements NamedListInitializedPlugin,
     final DirectoryFactory dirFactory;
     if (info != null) {
       log.debug(info.className);
-      dirFactory = config.getResourceLoader().newInstance(info.className, DirectoryFactory.class);
+      dirFactory = config.getResourceLoader().newInstance (info, DirectoryFactory.class, true);
       // allow DirectoryFactory instances to access the CoreContainer
       dirFactory.initCoreContainer(cc);
       dirFactory.init(info.initArgs);
diff --git a/solr/core/src/java/org/apache/solr/core/PluginBag.java b/solr/core/src/java/org/apache/solr/core/PluginBag.java
index d2442a1..b7c07f7 100644
--- a/solr/core/src/java/org/apache/solr/core/PluginBag.java
+++ b/solr/core/src/java/org/apache/solr/core/PluginBag.java
@@ -413,7 +413,6 @@ public class PluginBag<T> implements AutoCloseable {
       if (pluginInfo != null) return pluginInfo.className;
       return null;
     }
-
     public PluginInfo getPluginInfo() {
       return pluginInfo;
     }
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index bde2fd5..7ac37f8 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -57,6 +57,7 @@ import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Consumer;
 
 import com.codahale.metrics.Counter;
 import com.codahale.metrics.MetricRegistry;
@@ -280,6 +281,8 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
 
   private ExecutorService coreAsyncTaskExecutor = ExecutorUtil.newMDCAwareCachedThreadPool("Core Async Task");
 
+  public  final SolrCore.Provider coreProvider;
+
   /**
    * The SolrResourceLoader used to load all resources for this core.
    *
@@ -745,7 +748,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
     IndexReaderFactory indexReaderFactory;
     PluginInfo info = solrConfig.getPluginInfo(IndexReaderFactory.class.getName());
     if (info != null) {
-      indexReaderFactory = resourceLoader.newInstance(info.className, IndexReaderFactory.class);
+      indexReaderFactory = resourceLoader.newInstance(info, IndexReaderFactory.class, true);
       indexReaderFactory.init(info.initArgs);
     } else {
       indexReaderFactory = new StandardIndexReaderFactory();
@@ -941,9 +944,11 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
       this.configSet = configSet;
       this.coreDescriptor = Objects.requireNonNull(coreDescriptor, "coreDescriptor cannot be null");
       setName(coreDescriptor.getName());
+      coreProvider = new Provider(coreContainer, getName(), uniqueId);
 
       this.solrConfig = configSet.getSolrConfig();
       this.resourceLoader = configSet.getSolrConfig().getResourceLoader();
+      this.resourceLoader.initCore(this);
       IndexSchema schema = configSet.getIndexSchema();
 
       this.configSetProperties = configSet.getProperties();
@@ -1247,7 +1252,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
 
   private void checkVersionFieldExistsInSchema(IndexSchema schema, CoreDescriptor coreDescriptor) {
     if (null != coreDescriptor.getCloudDescriptor()) {
-      // we are evidently running in cloud mode.  
+      // we are evidently running in cloud mode.
       //
       // In cloud mode, version field is required for correct consistency
       // ideally this check would be more fine grained, and individual features
@@ -1411,7 +1416,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
     final PluginInfo info = solrConfig.getPluginInfo(CodecFactory.class.getName());
     final CodecFactory factory;
     if (info != null) {
-      factory = resourceLoader.newInstance(info.className, CodecFactory.class);
+      factory = resourceLoader.newInstance( info, CodecFactory.class, true);
       factory.init(info.initArgs);
     } else {
       factory = new CodecFactory() {
@@ -1449,8 +1454,8 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
     final StatsCache cache;
     PluginInfo pluginInfo = solrConfig.getPluginInfo(StatsCache.class.getName());
     if (pluginInfo != null && pluginInfo.className != null && pluginInfo.className.length() > 0) {
-      cache = createInitInstance(pluginInfo, StatsCache.class, null,
-          LocalStatsCache.class.getName());
+      cache = resourceLoader.newInstance( pluginInfo, StatsCache.class, true);
+      initPlugin(pluginInfo ,cache);
       if (log.isDebugEnabled()) {
         log.debug("Using statsCache impl: {}", cache.getClass().getName());
       }
@@ -2159,7 +2164,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
           newReader = currentReader;
         }
 
-        // for now, turn off caches if this is for a realtime reader 
+        // for now, turn off caches if this is for a realtime reader
         // (caches take a little while to instantiate)
         final boolean useCaches = !realtime;
         final String newName = realtime ? "realtime" : "main";
@@ -3212,7 +3217,7 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
   public void cleanupOldIndexDirectories(boolean reload) {
     final DirectoryFactory myDirFactory = getDirectoryFactory();
     final String myDataDir = getDataDir();
-    final String myIndexDir = getNewIndexDir(); // ensure the latest replicated index is protected 
+    final String myIndexDir = getNewIndexDir(); // ensure the latest replicated index is protected
     final String coreName = getName();
     if (myDirFactory != null && myDataDir != null && myIndexDir != null) {
       Thread cleanupThread = new Thread(() -> {
@@ -3299,4 +3304,31 @@ public final class SolrCore implements SolrInfoBean, SolrMetricProducer, Closeab
   public void runAsync(Runnable r) {
     coreAsyncTaskExecutor.submit(r);
   }
+
+  /**Provides the core instance if the core instance is still alive.
+   * This helps to not hold on to a live {@link SolrCore} instance
+   * even after it's unloaded
+   *
+   */
+  public static class Provider {
+    private final CoreContainer coreContainer;
+    private final String coreName;
+    private final UUID coreId;
+
+    public Provider(CoreContainer coreContainer, String coreName, UUID coreId) {
+      this.coreContainer = coreContainer;
+      this.coreName = coreName;
+      this.coreId = coreId;
+    }
+    public void reload() {
+      coreContainer.reload(coreName, coreId);
+    }
+
+    public void withCore(Consumer<SolrCore> r) {
+      try(SolrCore core = coreContainer.getCore(coreName, coreId)) {
+        if(core == null) return;
+        r.accept(core);
+      }
+    }
+  }
 }
diff --git a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
index 17d0132..b58c8d9 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrResourceLoader.java
@@ -31,6 +31,7 @@ import java.nio.file.Path;
 import java.nio.file.PathMatcher;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -48,6 +49,7 @@ import org.apache.solr.handler.component.SearchComponent;
 import org.apache.solr.handler.component.ShardHandlerFactory;
 import org.apache.solr.logging.DeprecationLog;
 import org.apache.solr.pkg.PackageListeningClassLoader;
+import org.apache.solr.pkg.PackageLoader;
 import org.apache.solr.request.SolrRequestHandler;
 import org.apache.solr.response.QueryResponseWriter;
 import org.apache.solr.rest.RestManager;
@@ -86,6 +88,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
   private CoreContainer coreContainer;
   private PackageListeningClassLoader schemaLoader ;
 
+  private PackageListeningClassLoader coreReloadingClassLoader ;
   private final List<SolrCoreAware> waitingForCore = Collections.synchronizedList(new ArrayList<SolrCoreAware>());
   private final List<SolrInfoBean> infoMBeans = Collections.synchronizedList(new ArrayList<SolrInfoBean>());
   private final List<ResourceLoaderAware> waitingForResources = Collections.synchronizedList(new ArrayList<ResourceLoaderAware>());
@@ -452,7 +455,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
    * @throws IOException If there is a low-level I/O error.
    */
   public List<String> getLines(String resource,
-      String encoding) throws IOException {
+                               String encoding) throws IOException {
     return getLines(resource, Charset.forName(encoding));
   }
 
@@ -462,7 +465,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
       return WordlistLoader.getLines(openResource(resource), charset);
     } catch (CharacterCodingException ex) {
       throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
-         "Error loading resource (wrong encoding?): " + resource, ex);
+          "Error loading resource (wrong encoding?): " + resource, ex);
     }
   }
 
@@ -556,8 +559,8 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
         //cache the shortname vs FQN if it is loaded by the webapp classloader  and it is loaded
         // using a shortname
         if (clazz.getClassLoader() == SolrResourceLoader.class.getClassLoader() &&
-              !cname.equals(clazz.getName()) &&
-              (subpackages.length == 0 || subpackages == packages)) {
+            !cname.equals(clazz.getName()) &&
+            (subpackages.length == 0 || subpackages == packages)) {
           //store in the cache
           classNameCache.put(cname, clazz.getName());
         }
@@ -565,7 +568,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
         // print warning if class is deprecated
         if (clazz.isAnnotationPresent(Deprecated.class)) {
           DeprecationLog.log(cname,
-            "Solr loaded a deprecated plugin/analysis class [" + cname + "]. Please consult documentation how to replace it accordingly.");
+              "Solr loaded a deprecated plugin/analysis class [" + cname + "]. Please consult documentation how to replace it accordingly.");
         }
       }
     }
@@ -682,6 +685,24 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
     }
   }
 
+  void initCore(SolrCore core) {
+    this.coreName = core.getName();
+    this.config = core.getSolrConfig();
+    this.coreId = core.uniqueId;
+    this.coreContainer = core.getCoreContainer();
+    SolrCore.Provider coreProvider = core.coreProvider;
+
+    this.coreReloadingClassLoader = new PackageListeningClassLoader(core.getCoreContainer(),
+        this, s -> config.maxPackageVersion(s), null){
+      @Override
+      protected void doReloadAction(Ctx ctx) {
+        log.info("Core reloading classloader issued reload for: {}/{} ", coreName, coreId);
+        coreProvider.reload();
+      }
+    };
+    core.getPackageListeners().addListener(coreReloadingClassLoader, true);
+
+  }
 
   /**
    * Tell all {@link SolrCoreAware} instances about the SolrCore
@@ -720,7 +741,7 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
   public void inform( ResourceLoader loader ) throws IOException
   {
 
-     // make a copy to avoid potential deadlock of a callback adding to the list
+    // make a copy to avoid potential deadlock of a callback adding to the list
     ResourceLoaderAware[] arr;
 
     while (waitingForResources.size() > 0) {
@@ -801,30 +822,30 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
     awareCompatibility = new HashMap<>();
     awareCompatibility.put(
         SolrCoreAware.class, new Class<?>[]{
-        // DO NOT ADD THINGS TO THIS LIST -- ESPECIALLY THINGS THAT CAN BE CREATED DYNAMICALLY
-        // VIA RUNTIME APIS -- UNTILL CAREFULLY CONSIDERING THE ISSUES MENTIONED IN SOLR-8311
-        CodecFactory.class,
-        DirectoryFactory.class,
-        ManagedIndexSchemaFactory.class,
-        QueryResponseWriter.class,
-        SearchComponent.class,
-        ShardHandlerFactory.class,
-        SimilarityFactory.class,
-        SolrRequestHandler.class,
-        UpdateRequestProcessorFactory.class
-      }
+            // DO NOT ADD THINGS TO THIS LIST -- ESPECIALLY THINGS THAT CAN BE CREATED DYNAMICALLY
+            // VIA RUNTIME APIS -- UNTILL CAREFULLY CONSIDERING THE ISSUES MENTIONED IN SOLR-8311
+            CodecFactory.class,
+            DirectoryFactory.class,
+            ManagedIndexSchemaFactory.class,
+            QueryResponseWriter.class,
+            SearchComponent.class,
+            ShardHandlerFactory.class,
+            SimilarityFactory.class,
+            SolrRequestHandler.class,
+            UpdateRequestProcessorFactory.class
+        }
     );
 
     awareCompatibility.put(
         ResourceLoaderAware.class, new Class<?>[]{
-        // DO NOT ADD THINGS TO THIS LIST -- ESPECIALLY THINGS THAT CAN BE CREATED DYNAMICALLY
-        // VIA RUNTIME APIS -- UNTILL CAREFULLY CONSIDERING THE ISSUES MENTIONED IN SOLR-8311
-        CharFilterFactory.class,
-        TokenFilterFactory.class,
-        TokenizerFactory.class,
-        QParserPlugin.class,
-        FieldType.class
-      }
+            // DO NOT ADD THINGS TO THIS LIST -- ESPECIALLY THINGS THAT CAN BE CREATED DYNAMICALLY
+            // VIA RUNTIME APIS -- UNTILL CAREFULLY CONSIDERING THE ISSUES MENTIONED IN SOLR-8311
+            CharFilterFactory.class,
+            TokenFilterFactory.class,
+            TokenizerFactory.class,
+            QParserPlugin.class,
+            FieldType.class
+        }
     );
   }
 
@@ -878,6 +899,47 @@ public class SolrResourceLoader implements ResourceLoader, Closeable, SolrClassL
   public List<SolrInfoBean> getInfoMBeans(){
     return Collections.unmodifiableList(infoMBeans);
   }
+  /**
+   * Load a class using an appropriate {@link SolrResourceLoader} depending of the package on that class
+   * @param registerCoreReloadListener register a listener for the package and reload the core if the package is changed.
+   *                                   Use this sparingly. This will result in core reloads across all the cores in
+   *                                   all collections using this configset
+   */
+  public  <T> Class<? extends T> findClass( PluginInfo info, Class<T>  type, boolean registerCoreReloadListener) {
+    if(info.cName.pkg == null) return findClass(info.className, type);
+    return _classLookup(info,
+        (Function<PackageLoader.Package.Version, Class<? extends T>>) ver -> ver.getLoader().findClass(info.cName.className, type), registerCoreReloadListener);
+
+  }
+
+
+  private  <T> T _classLookup(PluginInfo info, Function<PackageLoader.Package.Version, T> fun, boolean registerCoreReloadListener ) {
+    PluginInfo.ClassName cName = info.cName;
+    if (registerCoreReloadListener) {
+      if (coreReloadingClassLoader == null) {
+        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Core not set");
+      }
+      return fun.apply(coreReloadingClassLoader.findPackageVersion(cName, true));
+    } else {
+      return fun.apply(coreReloadingClassLoader.findPackageVersion(cName, false));
+    }
+  }
+
+  /**
+   *Create a n instance of a class using an appropriate {@link SolrResourceLoader} depending on the package of that class
+   * @param registerCoreReloadListener register a listener for the package and reload the core if the package is changed.
+   *                                   Use this sparingly. This will result in core reloads across all the cores in
+   *                                   all collections using this configset
+   */
+  public <T> T newInstance(PluginInfo info, Class<T> type, boolean registerCoreReloadListener) {
+    if(info.cName.pkg == null) {
+      return newInstance(info.cName.className == null?
+              type.getName():
+              info.cName.className ,
+          type);
+    }
+    return _classLookup( info, version -> version.getLoader().newInstance(info.cName.className, type), registerCoreReloadListener);
+  }
 
   private PackageListeningClassLoader createSchemaLoader() {
     CoreContainer cc = getCoreContainer();
diff --git a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
index 7f48b63..03579c6 100644
--- a/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/SolrConfigHandler.java
@@ -48,6 +48,7 @@ import org.apache.solr.client.solrj.impl.HttpSolrClient;
 import org.apache.solr.client.solrj.io.stream.expr.Expressible;
 import org.apache.solr.cloud.ZkController;
 import org.apache.solr.cloud.ZkSolrResourceLoader;
+import org.apache.solr.common.MapSerializable;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.DocCollection;
@@ -70,6 +71,7 @@ import org.apache.solr.core.RequestParams;
 import org.apache.solr.core.SolrConfig;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.pkg.PackageAPI;
 import org.apache.solr.pkg.PackageListeners;
 import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryRequest;
@@ -251,25 +253,28 @@ public class SolrConfigHandler extends RequestHandlerBase implements SolrCoreAwa
             String componentName = req.getParams().get("componentName");
             if (componentName != null) {
               @SuppressWarnings({"rawtypes"})
-              Map map = (Map) val.get(parts.get(1));
-              if (map != null) {
-                Object o = map.get(componentName);
-                val.put(parts.get(1), makeMap(componentName, o));
+              Map pluginNameVsPluginInfo = (Map) val.get(parts.get(1));
+              if (pluginNameVsPluginInfo != null) {
+                @SuppressWarnings({"rawtypes"})
+                Object o = pluginNameVsPluginInfo instanceof MapSerializable ?
+                       pluginNameVsPluginInfo:
+                        pluginNameVsPluginInfo.get(componentName);
+                @SuppressWarnings({"rawtypes"})
+                Map pluginInfo = o instanceof  MapSerializable? ((MapSerializable) o).toMap(new LinkedHashMap<>()): (Map) o;
+                val.put(parts.get(1),pluginNameVsPluginInfo instanceof PluginInfo? pluginInfo :  makeMap(componentName, pluginInfo));
                 if (req.getParams().getBool("meta", false)) {
                   // meta=true is asking for the package info of the plugin
                   // We go through all the listeners and see if there is one registered for this plugin
                   List<PackageListeners.Listener> listeners = req.getCore().getPackageListeners().getListeners();
                   for (PackageListeners.Listener listener :
                       listeners) {
-                    PluginInfo info = listener.pluginInfo();
-                    if(info == null) continue;
-                    if (info.type.equals(parts.get(1)) && info.name.equals(componentName)) {
-                      if (o instanceof Map) {
-                        @SuppressWarnings({"rawtypes"})
-                        Map m1 = (Map) o;
-                        m1.put("_packageinfo_", listener.getPackageVersion(info.cName));
+                    Map<String, PackageAPI.PkgVersion> infos = listener.packageDetails();
+                    if(infos == null || infos.isEmpty()) continue;
+                    infos.forEach((s, mapWriter) -> {
+                      if(s.equals(pluginInfo.get("class"))) {
+                        (pluginInfo).put("_packageinfo_", mapWriter);
                       }
-                    }
+                    });
                   }
                 }
               }
diff --git a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
index f5c2474..c0c536f 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
@@ -16,16 +16,6 @@
  */
 package org.apache.solr.handler.component;
 
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-
 import org.apache.commons.lang3.StringUtils;
 import org.apache.lucene.index.ExitableDirectoryReader;
 import org.apache.lucene.search.TotalHits;
@@ -44,6 +34,7 @@ import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.PluginInfo;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.RequestHandlerBase;
+import org.apache.solr.pkg.PackageAPI;
 import org.apache.solr.pkg.PackageListeners;
 import org.apache.solr.pkg.PackageLoader;
 import org.apache.solr.request.SolrQueryRequest;
@@ -61,10 +52,13 @@ import org.apache.solr.util.plugin.SolrCoreAware;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.apache.solr.common.params.CommonParams.DISTRIB;
-import static org.apache.solr.common.params.CommonParams.FAILURE;
-import static org.apache.solr.common.params.CommonParams.PATH;
-import static org.apache.solr.common.params.CommonParams.STATUS;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.invoke.MethodHandles;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.apache.solr.common.params.CommonParams.*;
 
 
 /**
@@ -162,8 +156,8 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware,
         }
 
         @Override
-        public PluginInfo pluginInfo() {
-          return null;
+        public Map<String , PackageAPI.PkgVersion> packageDetails() {
+          return Collections.emptyMap();
         }
 
         @Override
diff --git a/solr/core/src/java/org/apache/solr/pkg/PackageListeners.java b/solr/core/src/java/org/apache/solr/pkg/PackageListeners.java
index 1895b6d..1281d99 100644
--- a/solr/core/src/java/org/apache/solr/pkg/PackageListeners.java
+++ b/solr/core/src/java/org/apache/solr/pkg/PackageListeners.java
@@ -47,6 +47,14 @@ public class PackageListeners {
 
   public synchronized void addListener(Listener listener) {
     listeners.add(new SoftReference<>(listener));
+  }
+
+  public synchronized void addListener(Listener listener, boolean addFirst) {
+    if(addFirst) {
+      listeners.add(0, new SoftReference<>(listener));
+    } else {
+      addListener(listener);
+    }
 
   }
 
@@ -71,7 +79,7 @@ public class PackageListeners {
         invokeListeners(pkgInfo, ctx);
       }
     } finally {
-      ctx.runLaterTasks(core::runAsync);
+      ctx.runLaterTasks(r -> core.getCoreContainer().runAsync(r));
       MDCLoggingContext.clear();
     }
   }
@@ -102,7 +110,10 @@ public class PackageListeners {
     /**Name of the package or null to listen to all package changes */
     String packageName();
 
-    PluginInfo pluginInfo();
+    /** fetch the package versions of class names
+     *
+     */
+    Map<String, PackageAPI.PkgVersion> packageDetails();
 
     /**A callback when the package is updated */
     void changed(PackageLoader.Package pkg, Ctx ctx);
diff --git a/solr/core/src/java/org/apache/solr/pkg/PackageListeningClassLoader.java b/solr/core/src/java/org/apache/solr/pkg/PackageListeningClassLoader.java
index c10af0c..0a90162 100644
--- a/solr/core/src/java/org/apache/solr/pkg/PackageListeningClassLoader.java
+++ b/solr/core/src/java/org/apache/solr/pkg/PackageListeningClassLoader.java
@@ -26,9 +26,8 @@ import org.apache.solr.common.cloud.SolrClassLoader;
 import org.apache.solr.core.SolrResourceLoader;
 
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 /**
  * A {@link SolrClassLoader} that is designed to listen to a set of packages.
@@ -37,22 +36,29 @@ import java.util.function.Function;
  * */
 public class PackageListeningClassLoader implements SolrClassLoader , PackageListeners.Listener {
     private final CoreContainer coreContainer;
-    private final SolrResourceLoader coreResourceLoader;
+    private final SolrClassLoader fallbackClassLoader;
     private final Function<String, String> pkgVersionSupplier;
     /** package name and the versions that we are tracking
      */
-    private Map<String ,PackageAPI.PkgVersion> packageVersions =  new HashMap<>(1);
+    private Map<String ,PackageAPI.PkgVersion> packageVersions =  new ConcurrentHashMap<>(1);
+    private Map<String, String> classNameVsPackageName = new ConcurrentHashMap<>();
     private final Runnable onReload;
 
+    /**
+     * @param fallbackClassLoader The {@link SolrClassLoader} to use if no package is specified
+     * @param pkgVersionSupplier Get the version configured for a given package
+     * @param onReload The callback function that should be run if a package is updated
+     */
     public PackageListeningClassLoader(CoreContainer coreContainer,
-                                       SolrResourceLoader coreResourceLoader,
+                                       SolrClassLoader fallbackClassLoader,
                                        Function<String, String> pkgVersionSupplier,
                                        Runnable onReload) {
         this.coreContainer = coreContainer;
-        this.coreResourceLoader = coreResourceLoader;
+        this.fallbackClassLoader = fallbackClassLoader;
         this.pkgVersionSupplier = pkgVersionSupplier;
         this.onReload = () -> {
-            packageVersions = new HashMap<>();
+            packageVersions = new ConcurrentHashMap<>();
+            classNameVsPackageName = new ConcurrentHashMap<>();
             onReload.run();
         };
     }
@@ -61,18 +67,28 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
     @Override
     public <T> T newInstance(String cname, Class<T> expectedType, String... subpackages) {
         PluginInfo.ClassName cName = new PluginInfo.ClassName(cname);
-        if(cName.pkg == null){
-            return coreResourceLoader.newInstance(cname, expectedType, subpackages);
+        if(cName.pkg == null) {
+            return fallbackClassLoader.newInstance(cname, expectedType, subpackages);
         } else {
-            PackageLoader.Package.Version version = findPkgVersion(cName);
-            return applyResourceLoaderAware(version, version.getLoader().newInstance(cName.className, expectedType, subpackages));
-
+            PackageLoader.Package.Version version = findPackageVersion(cName, true);
+            T obj = version.getLoader().newInstance(cName.className, expectedType, subpackages);
+            classNameVsPackageName.put(cName.original, cName.pkg);
+            return applyResourceLoaderAware(version, obj);
         }
     }
 
-    private PackageLoader.Package.Version findPkgVersion(PluginInfo.ClassName cName) {
+
+    /**
+     * This looks up for package and also listens for that package if required
+     * @param cName The class name
+     */
+    public PackageLoader.Package.Version findPackageVersion(PluginInfo.ClassName cName, boolean registerListener) {
         PackageLoader.Package.Version theVersion = coreContainer.getPackageLoader().getPackage(cName.pkg).getLatest(pkgVersionSupplier.apply(cName.pkg));
-        packageVersions.put(cName.pkg, theVersion.getPkgVersion());
+        if(registerListener) {
+            classNameVsPackageName.put(cName.original, cName.pkg);
+            PackageAPI.PkgVersion pkgVersion = theVersion.getPkgVersion();
+            if(pkgVersion !=null) packageVersions.put(cName.pkg, pkgVersion);
+        }
         return theVersion;
     }
 
@@ -101,10 +117,12 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
     public <T> T newInstance(String cname, Class<T> expectedType, String[] subPackages, Class[] params, Object[] args) {
         PluginInfo.ClassName cName = new PluginInfo.ClassName(cname);
         if (cName.pkg == null) {
-            return coreResourceLoader.newInstance(cname, expectedType, subPackages, params, args);
+            return fallbackClassLoader.newInstance(cname, expectedType, subPackages, params, args);
         } else {
-            PackageLoader.Package.Version version = findPkgVersion(cName);
-            return applyResourceLoaderAware(version, version.getLoader().newInstance(cName.className, expectedType, subPackages, params, args));
+            PackageLoader.Package.Version version = findPackageVersion(cName, true);
+            T obj = version.getLoader().newInstance(cName.className, expectedType, subPackages, params, args);
+            classNameVsPackageName.put(cName.original, cName.pkg);
+            return applyResourceLoaderAware(version, obj);
         }
     }
 
@@ -112,10 +130,12 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
     public <T> Class<? extends T> findClass(String cname, Class<T> expectedType) {
         PluginInfo.ClassName cName = new PluginInfo.ClassName(cname);
         if (cName.pkg == null) {
-            return coreResourceLoader.findClass(cname, expectedType);
+            return fallbackClassLoader.findClass(cname, expectedType);
         } else {
-            PackageLoader.Package.Version version = findPkgVersion(cName);
-            return version.getLoader().findClass(cName.className, expectedType);
+            PackageLoader.Package.Version version = findPackageVersion(cName, true);
+            Class<? extends T> klas = version.getLoader().findClass(cName.className, expectedType);
+            classNameVsPackageName.put(cName.original, cName.pkg);
+            return klas;
 
         }
     }
@@ -126,8 +146,10 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
     }
 
     @Override
-    public PluginInfo pluginInfo() {
-        return null;
+    public Map<String, PackageAPI.PkgVersion> packageDetails() {
+        Map<String, PackageAPI.PkgVersion> result = new LinkedHashMap<>();
+        classNameVsPackageName.forEach((k, v) -> result.put(k, packageVersions.get(v)));
+        return result;
     }
 
     @Override
@@ -146,6 +168,11 @@ public class PackageListeningClassLoader implements SolrClassLoader , PackageLis
             //no need to update
             return;
         }
+        doReloadAction(ctx);
+    }
+
+    protected void doReloadAction(Ctx ctx) {
+        if(onReload == null) return;
         ctx.runLater(null, onReload);
     }
 }
diff --git a/solr/core/src/java/org/apache/solr/pkg/PackagePluginHolder.java b/solr/core/src/java/org/apache/solr/pkg/PackagePluginHolder.java
index 0c6fd80..978f707 100644
--- a/solr/core/src/java/org/apache/solr/pkg/PackagePluginHolder.java
+++ b/solr/core/src/java/org/apache/solr/pkg/PackagePluginHolder.java
@@ -19,6 +19,8 @@ package org.apache.solr.pkg;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.Collections;
+import java.util.Map;
 
 import org.apache.lucene.analysis.util.ResourceLoaderAware;
 import org.apache.solr.common.MapWriter;
@@ -57,8 +59,8 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
       }
 
       @Override
-      public PluginInfo pluginInfo() {
-        return info;
+      public Map<String, PackageAPI.PkgVersion> packageDetails() {
+        return Collections.singletonMap(info.cName.original, pkgVersion.getPkgVersion());
       }
 
       @Override
@@ -75,6 +77,12 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
     });
   }
 
+  public static <T> PluginBag.PluginHolder<T> createHolder(T inst,  Class<T> type) {
+    SolrConfig.SolrPluginInfo plugin = SolrConfig.classVsSolrPluginInfo.get(type.getName());
+    PluginInfo info = new PluginInfo(plugin.tag, Collections.singletonMap("class", inst.getClass().getName()));
+    return new PluginBag.PluginHolder<T>(info,inst);
+  }
+
   public static <T> PluginBag.PluginHolder<T> createHolder(PluginInfo info, SolrCore core, Class<T> type, String msg) {
     if(info.cName.pkg == null) {
       return new PluginBag.PluginHolder<T>(info, core.createInitInstance(info, type,msg, null));
@@ -108,7 +116,7 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
 
     if (log.isInfoEnabled()) {
       log.info("loading plugin: {} -> {} using  package {}:{}",
-          pluginInfo.type, pluginInfo.name, pkg.name(), newest.getVersion());
+              pluginInfo.type, pluginInfo.name, pkg.name(), newest.getVersion());
     }
 
     initNewInstance(newest);
@@ -119,7 +127,7 @@ public class PackagePluginHolder<T> extends PluginBag.PluginHolder<T> {
   @SuppressWarnings({"unchecked"})
   protected Object initNewInstance(PackageLoader.Package.Version newest) {
     Object instance = SolrCore.createInstance(pluginInfo.className,
-        pluginMeta.clazz, pluginMeta.getCleanTag(), core, newest.getLoader());
+            pluginMeta.clazz, pluginMeta.getCleanTag(), core, newest.getLoader());
     PluginBag.initInstance(instance, pluginInfo);
     handleAwareCallbacks(newest.getLoader(), instance);
     T old = inst;
diff --git a/solr/core/src/java/org/apache/solr/search/CacheConfig.java b/solr/core/src/java/org/apache/solr/search/CacheConfig.java
index b4f4706..9b1c297 100644
--- a/solr/core/src/java/org/apache/solr/search/CacheConfig.java
+++ b/solr/core/src/java/org/apache/solr/search/CacheConfig.java
@@ -23,10 +23,14 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.MapSerializable;
+
+import org.apache.solr.core.PluginInfo;
+
 import org.apache.solr.core.SolrConfig;
 import org.apache.solr.core.SolrResourceLoader;
 import org.apache.solr.common.util.DOMUtil;
@@ -46,11 +50,17 @@ import static org.apache.solr.common.params.CommonParams.NAME;
  */
 public class CacheConfig implements MapSerializable{
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  
+
   private String nodeName;
 
+
+  /**
+   * When this object is created, the core is not yet available . So, if the class is to be
+   * loaded from a package we should have a corresponding core
+   *
+   */
   @SuppressWarnings({"rawtypes"})
-  private Class<? extends SolrCache> clazz;
+  private Supplier<Class<? extends SolrCache>> clazz;
   private Map<String,String> args;
   private CacheRegenerator regenerator;
 
@@ -64,9 +74,10 @@ public class CacheConfig implements MapSerializable{
 
   @SuppressWarnings({"rawtypes"})
   public CacheConfig(Class<? extends SolrCache> clazz, Map<String,String> args, CacheRegenerator regenerator) {
-    this.clazz = clazz;
+    this.clazz = () -> clazz;
     this.args = args;
     this.regenerator = regenerator;
+    this.nodeName = args.get(NAME);
   }
 
   public CacheRegenerator getRegenerator() {
@@ -85,7 +96,7 @@ public class CacheConfig implements MapSerializable{
       Node node = nodes.item(i);
       if ("true".equals(DOMUtil.getAttrOrDefault(node, "enabled", "true"))) {
         CacheConfig config = getConfig(solrConfig, node.getNodeName(),
-                                       DOMUtil.toMap(node.getAttributes()), configPath);
+            DOMUtil.toMap(node.getAttributes()), configPath);
         result.put(config.args.get(NAME), config);
       }
     }
@@ -134,19 +145,31 @@ public class CacheConfig implements MapSerializable{
     SolrResourceLoader loader = solrConfig.getResourceLoader();
     config.cacheImpl = config.args.get("class");
     if (config.cacheImpl == null) config.cacheImpl = "solr.CaffeineCache";
+    config.clazz = new Supplier() {
+      @SuppressWarnings("rawtypes")
+      Class<SolrCache> loadedClass;
+
+      @Override
+      @SuppressWarnings("rawtypes")
+      public Class<? extends SolrCache> get() {
+        if (loadedClass != null) return loadedClass;
+        return loadedClass = (Class<SolrCache>) loader.findClass(
+            new PluginInfo("cache", Collections.singletonMap("class", config.cacheImpl)),
+            SolrCache.class, true);
+      }
+    };
     config.regenImpl = config.args.get("regenerator");
-    config.clazz = loader.findClass(config.cacheImpl, SolrCache.class);
     if (config.regenImpl != null) {
       config.regenerator = loader.newInstance(config.regenImpl, CacheRegenerator.class);
     }
-    
+
     return config;
   }
 
   @SuppressWarnings({"rawtypes"})
   public SolrCache newInstance() {
     try {
-      SolrCache cache = clazz.getConstructor().newInstance();
+      SolrCache cache = clazz.get().getConstructor().newInstance();
       persistence[0] = cache.init(args, persistence[0], regenerator);
       return cache;
     } catch (Exception e) {
@@ -158,11 +181,8 @@ public class CacheConfig implements MapSerializable{
   }
 
   @Override
-  @SuppressWarnings({"unchecked"})
   public Map<String, Object> toMap(Map<String, Object> map) {
-    @SuppressWarnings({"rawtypes"})
-    Map result = Collections.unmodifiableMap(args);
-    return result;
+    return new HashMap<>(args);
   }
 
   public String getNodeName() {
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateHandler.java b/solr/core/src/java/org/apache/solr/update/UpdateHandler.java
index a2ee786..13cdfbb 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateHandler.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateHandler.java
@@ -111,7 +111,7 @@ public abstract class UpdateHandler implements SolrInfoBean {
   public UpdateHandler(SolrCore core)  {
     this(core, null);
   }
-  
+
   public UpdateHandler(SolrCore core, UpdateLog updateLog)  {
     this.core=core;
     idField = core.getLatestSchema().getUniqueKeyField();
@@ -126,8 +126,8 @@ public abstract class UpdateHandler implements SolrInfoBean {
       if (dirFactory instanceof HdfsDirectoryFactory) {
         ulog = new HdfsUpdateLog(((HdfsDirectoryFactory)dirFactory).getConfDir());
       } else {
-        String className = ulogPluginInfo.className == null ? UpdateLog.class.getName() : ulogPluginInfo.className;
-        ulog = core.getResourceLoader().newInstance(className, UpdateLog.class);
+        ulog = ulogPluginInfo.className == null ? new UpdateLog():
+            core.getResourceLoader().newInstance(ulogPluginInfo, UpdateLog.class, true);
       }
 
       if (!core.isReloaded() && !dirFactory.isPersistent()) {
diff --git a/solr/core/src/test-files/solr/configsets/conf2/conf/schema.xml b/solr/core/src/test-files/solr/configsets/conf2/conf/schema.xml
new file mode 100644
index 0000000..85f7ed3
--- /dev/null
+++ b/solr/core/src/test-files/solr/configsets/conf2/conf/schema.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ 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.
+-->
+<schema name="minimal" version="1.1">
+  <fieldType name="string" class="solr.StrField"/>
+  <fieldType name="int" class="${solr.tests.IntegerFieldType}" docValues="${solr.tests.numeric.dv}" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+  <fieldType name="long" class="${solr.tests.LongFieldType}" docValues="${solr.tests.numeric.dv}" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+  <dynamicField name="*" type="string" indexed="true" stored="true"/>
+  <!-- for versioning -->
+  <field name="_version_" type="long" indexed="true" stored="true"/>
+  <field name="_root_" type="string" indexed="true" stored="true" multiValued="false" required="false"/>
+  <field name="id" type="string" indexed="true" stored="true"/>
+  <dynamicField name="*_s"  type="string"  indexed="true"  stored="true" />
+  <uniqueKey>id</uniqueKey>
+  <query>
+    <filterCache
+            size="0"
+            initialSize="0"
+            autowarmCount="0"/>
+    <queryResultCache
+            size="0"
+            initialSize="0"
+            autowarmCount="0"/>
+    <documentCache
+            size="0"
+            initialSize="0"
+            autowarmCount="0"/>
+  </query>
+</schema>
diff --git a/solr/core/src/test-files/solr/configsets/conf2/conf/solrconfig.xml b/solr/core/src/test-files/solr/configsets/conf2/conf/solrconfig.xml
new file mode 100644
index 0000000..e69d16f
--- /dev/null
+++ b/solr/core/src/test-files/solr/configsets/conf2/conf/solrconfig.xml
@@ -0,0 +1,68 @@
+<?xml version="1.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.
+-->
+
+<!-- Minimal solrconfig.xml with /select, /admin and /update only -->
+
+<config>
+
+  <dataDir>${solr.data.dir:}</dataDir>
+
+  <directoryFactory name="DirectoryFactory"
+                    class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+
+  <updateHandler class="solr.DirectUpdateHandler2">
+    <commitWithin>
+      <softCommit>${solr.commitwithin.softcommit:true}</softCommit>
+    </commitWithin>
+    <updateLog class="${solr.ulog:solr.UpdateLog}"></updateLog>
+  </updateHandler>
+
+  <requestHandler name="/select" class="solr.SearchHandler">
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+      <str name="indent">true</str>
+      <str name="df">text</str>
+    </lst>
+
+  </requestHandler>
+  <query>
+    <filterCache
+            class = "mypkg:org.apache.solr.search.CaffeineCache"
+            size="512"
+            initialSize="512"
+            autowarmCount="0" />
+
+    <queryResultCache
+            size="512"
+            initialSize="512"
+            autowarmCount="0" />
+
+    <documentCache
+            size="512"
+            initialSize="512"
+            autowarmCount="0" />
+  </query>
+  <indexConfig>
+    <mergeScheduler class="${solr.mscheduler:org.apache.lucene.index.ConcurrentMergeScheduler}"/>
+  </indexConfig>
+</config>
+
diff --git a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
index 9b30753..dc90769 100644
--- a/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
+++ b/solr/core/src/test/org/apache/solr/pkg/TestPackages.java
@@ -102,6 +102,69 @@ public class TestPackages extends SolrCloudTestCase {
     @JsonProperty("class")
     public String klass;
   }
+
+
+  public void testCoreReloadingPlugin() throws Exception {
+    MiniSolrCloudCluster cluster =
+        configureCluster(4)
+            .withJettyConfig(jetty -> jetty.enableV2(true))
+            .addConfig("conf", configset("conf2"))
+            .configure();
+    try {
+      String FILE1 = "/mypkg/runtimelibs.jar";
+      String COLLECTION_NAME = "testCoreReloadingPluginColl";
+      byte[] derFile = readFile("cryptokeys/pub_key512.der");
+      uploadKey(derFile, PackageStoreAPI.KEYS_DIR+"/pub_key512.der", cluster);
+      postFileAndWait(cluster, "runtimecode/runtimelibs.jar.bin", FILE1,
+          "L3q/qIGs4NaF6JiO0ZkMUFa88j0OmYc+I6O7BOdNuMct/xoZ4h73aZHZGc0+nmI1f/U3bOlMPINlSOM6LK3JpQ==");
+
+      Package.AddVersion add = new Package.AddVersion();
+      add.version = "1.0";
+      add.pkg = "mypkg";
+      add.files = Arrays.asList(new String[]{FILE1});
+      V2Request req = new V2Request.Builder("/cluster/package")
+          .forceV2(true)
+          .withMethod(SolrRequest.METHOD.POST)
+          .withPayload(Collections.singletonMap("add", add))
+          .build();
+
+      req.process(cluster.getSolrClient());
+      TestDistribPackageStore.assertResponseValues(10,
+          () -> new V2Request.Builder("/cluster/package").
+              withMethod(SolrRequest.METHOD.GET)
+              .build().process(cluster.getSolrClient()),
+          Utils.makeMap(
+              ":result:packages:mypkg[0]:version", "1.0",
+              ":result:packages:mypkg[0]:files[0]", FILE1
+          ));
+
+      CollectionAdminRequest
+          .createCollection(COLLECTION_NAME, "conf", 2, 2)
+          .process(cluster.getSolrClient());
+      cluster.waitForActiveCollection(COLLECTION_NAME, 2, 4);
+
+      verifyComponent(cluster.getSolrClient(), COLLECTION_NAME, "query", "filterCache", add.pkg, add.version);
+
+
+      add.version = "2.0";
+      req.process(cluster.getSolrClient());
+      TestDistribPackageStore.assertResponseValues(10,
+          () -> new V2Request.Builder("/cluster/package").
+              withMethod(SolrRequest.METHOD.GET)
+              .build().process(cluster.getSolrClient()),
+          Utils.makeMap(
+              ":result:packages:mypkg[1]:version", "2.0",
+              ":result:packages:mypkg[1]:files[0]", FILE1
+          ));
+      new UpdateRequest().commit(cluster.getSolrClient(), COLLECTION_NAME);
+
+      verifyComponent(cluster.getSolrClient(),
+          COLLECTION_NAME, "query", "filterCache",
+          "mypkg", "2.0" );
+    } finally {
+      cluster.shutdown();
+    }
+  }
   @Test
   @SuppressWarnings({"unchecked"})
   public void testPluginLoading() throws Exception {
@@ -650,10 +713,10 @@ public class TestPackages extends SolrCloudTestCase {
     System.setProperty("managed.schema.mutable", "true");
 
     MiniSolrCloudCluster cluster =
-            configureCluster(4)
-                    .withJettyConfig(jetty -> jetty.enableV2(true))
-                    .addConfig("conf1", configset("schema-package"))
-                    .configure();
+        configureCluster(4)
+            .withJettyConfig(jetty -> jetty.enableV2(true))
+            .addConfig("conf1", configset("schema-package"))
+            .configure();
     try {
       String FILE1 = "/schemapkg/schema-plugins.jar";
       byte[] derFile = readFile("cryptokeys/pub_key512.der");
@@ -686,14 +749,14 @@ public class TestPackages extends SolrCloudTestCase {
           ));
 
       CollectionAdminRequest
-              .createCollection(COLLECTION_NAME, "conf1", 2, 2)
-              .process(cluster.getSolrClient());
+          .createCollection(COLLECTION_NAME, "conf1", 2, 2)
+          .process(cluster.getSolrClient());
       cluster.waitForActiveCollection(COLLECTION_NAME, 2, 4);
 
       verifySchemaComponent(cluster.getSolrClient(), COLLECTION_NAME, "/schema/fieldtypes/myNewTextFieldWithAnalyzerClass",
-              Utils.makeMap(":fieldType:analyzer:charFilters[0]:_packageinfo_:version" ,"1.0",
-                      ":fieldType:analyzer:tokenizer:_packageinfo_:version","1.0",
-                      ":fieldType:_packageinfo_:version","1.0"));
+          Utils.makeMap(":fieldType:analyzer:charFilters[0]:_packageinfo_:version" ,"1.0",
+              ":fieldType:analyzer:tokenizer:_packageinfo_:version","1.0",
+              ":fieldType:_packageinfo_:version","1.0"));
 
       add = new Package.AddVersion();
       add.version = "2.0";