You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by no...@apache.org on 2022/11/16 02:07:13 UTC

[solr] branch jira/solr16547 created (now 2d07feac9a4)

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

noble pushed a change to branch jira/solr16547
in repository https://gitbox.apache.org/repos/asf/solr.git


      at 2d07feac9a4 refactor QueryResponseWriters

This branch includes the following new commits:

     new 2d07feac9a4 refactor QueryResponseWriters

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[solr] 01/01: refactor QueryResponseWriters

Posted by no...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

noble pushed a commit to branch jira/solr16547
in repository https://gitbox.apache.org/repos/asf/solr.git

commit 2d07feac9a48be3690bbbc75f08574a5947e39bb
Author: Noble Paul <no...@gmail.com>
AuthorDate: Wed Nov 16 13:06:50 2022 +1100

    refactor QueryResponseWriters
---
 .../java/org/apache/solr/core/CoreContainer.java   |   4 +-
 .../src/java/org/apache/solr/core/PluginBag.java   | 141 ++++-----------------
 .../org/apache/solr/core/RequestHandlerBag.java    | 125 ++++++++++++++++++
 .../java/org/apache/solr/core/RequestHandlers.java |   6 +-
 .../java/org/apache/solr/core/ResponseWriters.java | 117 +++++++++++++++++
 .../src/java/org/apache/solr/core/SolrCore.java    |  73 +----------
 .../org/apache/solr/filestore/PackageStoreAPI.java |   4 +-
 .../java/org/apache/solr/handler/BlobHandler.java  |  33 +++--
 .../java/org/apache/solr/handler/ClusterAPI.java   |  10 ++
 .../apache/solr/handler/ReplicationHandler.java    |   9 +-
 .../apache/solr/handler/export/ExportWriter.java   |   3 +-
 .../org/apache/solr/jersey/JerseyApplications.java |   9 +-
 .../org/apache/solr/jersey/MetricBeanFactory.java  |  14 +-
 .../apache/solr/jersey/RequestMetricHandling.java  |   7 +-
 .../java/org/apache/solr/servlet/HttpSolrCall.java |   6 +-
 .../processor/UpdateRequestProcessorChain.java     |   2 +-
 .../src/test/org/apache/solr/OutputWriterTest.java |   4 +-
 .../test/org/apache/solr/core/PluginBagTest.java   |  20 +--
 .../org/apache/solr/core/RequestHandlersTest.java  |   2 +-
 19 files changed, 342 insertions(+), 247 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index 00eb7454b8d..e47475103b0 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -187,8 +187,8 @@ public class CoreContainer {
     }
   }
 
-  private volatile PluginBag<SolrRequestHandler> containerHandlers =
-      new PluginBag<>(SolrRequestHandler.class, null);
+  private volatile RequestHandlerBag containerHandlers =
+      new RequestHandlerBag( null);
 
   private volatile ApplicationHandler jerseyAppHandler;
 
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 5ecf681ad62..2cbc1a13a3f 100644
--- a/solr/core/src/java/org/apache/solr/core/PluginBag.java
+++ b/solr/core/src/java/org/apache/solr/core/PluginBag.java
@@ -16,9 +16,6 @@
  */
 package org.apache.solr.core;
 
-import static java.util.Collections.singletonMap;
-import static org.apache.solr.api.ApiBag.HANDLER_NAME;
-
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.Collection;
@@ -32,24 +29,17 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
-import org.apache.commons.collections4.CollectionUtils;
+
 import org.apache.lucene.util.ResourceLoader;
 import org.apache.lucene.util.ResourceLoaderAware;
-import org.apache.solr.api.Api;
-import org.apache.solr.api.ApiBag;
-import org.apache.solr.api.ApiSupport;
-import org.apache.solr.api.JerseyResource;
 import org.apache.solr.common.SolrException;
-import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.handler.component.SearchComponent;
-import org.apache.solr.jersey.JerseyApplications;
 import org.apache.solr.pkg.PackagePluginHolder;
 import org.apache.solr.request.SolrRequestHandler;
 import org.apache.solr.util.plugin.NamedListInitializedPlugin;
 import org.apache.solr.util.plugin.PluginInfoInitialized;
 import org.apache.solr.util.plugin.SolrCoreAware;
-import org.glassfish.jersey.server.ResourceConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,34 +47,16 @@ import org.slf4j.LoggerFactory;
 public class PluginBag<T> implements AutoCloseable {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private final Map<String, PluginHolder<T>> registry;
-  private final Map<String, PluginHolder<T>> immutableRegistry;
+  protected Map<String, PluginHolder<T>> registry;
   private String def;
   private final Class<T> klass;
-  private SolrCore core;
+  protected SolrCore core;
   private final SolrConfig.SolrPluginInfo meta;
-  private final ApiBag apiBag;
-  private final ResourceConfig jerseyResources;
 
-  public static class JerseyMetricsLookupRegistry
-      extends HashMap<Class<? extends JerseyResource>, RequestHandlerBase> {}
-
-  private final JerseyMetricsLookupRegistry infoBeanByResource;
+  private Map<String, PluginHolder<T>> defaults;
 
   /** Pass needThreadSafety=true if plugins can be added and removed concurrently with lookups. */
   public PluginBag(Class<T> klass, SolrCore core, boolean needThreadSafety) {
-    if (klass == SolrRequestHandler.class) {
-      this.apiBag = new ApiBag(core != null);
-      this.infoBeanByResource = new JerseyMetricsLookupRegistry();
-      this.jerseyResources =
-          (core == null)
-              ? new JerseyApplications.CoreContainerApp(infoBeanByResource)
-              : new JerseyApplications.SolrCoreApp(core, infoBeanByResource);
-    } else {
-      this.apiBag = null;
-      this.jerseyResources = null;
-      this.infoBeanByResource = null;
-    }
     this.core = core;
     this.klass = klass;
     // TODO: since reads will dominate writes, we could also think about creating a new instance of
@@ -93,7 +65,6 @@ public class PluginBag<T> implements AutoCloseable {
     // We could also perhaps make this constructor into a factory method to return different
     // implementations depending on thread safety needs.
     this.registry = needThreadSafety ? new ConcurrentHashMap<>() : new HashMap<>();
-    this.immutableRegistry = Collections.unmodifiableMap(registry);
     meta = SolrConfig.classVsSolrPluginInfo.get(klass.getName());
     if (meta == null) {
       throw new SolrException(
@@ -101,6 +72,15 @@ public class PluginBag<T> implements AutoCloseable {
     }
   }
 
+  public PluginBag(Class<T> klass, SolrCore core, boolean needThreadSafety, Map<String,
+          PluginHolder<T>> defaults,SolrConfig.SolrPluginInfo pluginMetaData) {
+    this.core = core;
+    this.klass = klass;
+    this.registry = needThreadSafety ? new ConcurrentHashMap<>(0) : new HashMap<>(0);
+    this.meta = pluginMetaData;
+    this.defaults = defaults;
+  }
+
   /** Constructs a non-threadsafe plugin registry */
   public PluginBag(Class<T> klass, SolrCore core) {
     this(klass, core, false);
@@ -177,9 +157,14 @@ public class PluginBag<T> implements AutoCloseable {
   /** Get a plugin by name. If the plugin is not already instantiated, it is done here */
   public T get(String name) {
     PluginHolder<T> result = registry.get(name);
+    if(result == null && defaults != null) result = defaults.get(name);
     return result == null ? null : result.get();
   }
 
+  public PluginHolder<T> getHolder(String name) {
+    return registry.get(name);
+  }
+
   /**
    * Fetches a plugin by name , or the default
    *
@@ -193,7 +178,7 @@ public class PluginBag<T> implements AutoCloseable {
   }
 
   public Set<String> keySet() {
-    return immutableRegistry.keySet();
+    return registry.keySet();
   }
 
   /** register a plugin by a name */
@@ -207,61 +192,8 @@ public class PluginBag<T> implements AutoCloseable {
 
   @SuppressWarnings({"unchecked"})
   public PluginHolder<T> put(String name, PluginHolder<T> plugin) {
-    Boolean registerApi = null;
-    Boolean disableHandler = null;
-    if (plugin.pluginInfo != null) {
-      String registerAt = plugin.pluginInfo.attributes.get("registerPath");
-      if (registerAt != null) {
-        List<String> strs = StrUtils.splitSmart(registerAt, ',');
-        disableHandler = !strs.contains("/solr");
-        registerApi = strs.contains("/v2");
-      }
-    }
-
-    if (apiBag != null) {
-      if (plugin.isLoaded()) {
-        T inst = plugin.get();
-        if (inst instanceof ApiSupport) {
-          ApiSupport apiSupport = (ApiSupport) inst;
-          if (registerApi == null) registerApi = apiSupport.registerV2();
-          if (disableHandler == null) disableHandler = !apiSupport.registerV1();
-
-          if (registerApi) {
-            Collection<Api> apis = apiSupport.getApis();
-            if (apis != null) {
-              Map<String, String> nameSubstitutes = singletonMap(HANDLER_NAME, name);
-              for (Api api : apis) {
-                apiBag.register(api, nameSubstitutes);
-              }
-            }
-
-            // TODO Should we use <requestHandler name="/blah"> to override the path that each
-            //  resource registers under?
-            Collection<Class<? extends JerseyResource>> jerseyApis =
-                apiSupport.getJerseyResources();
-            if (!CollectionUtils.isEmpty(jerseyApis)) {
-              for (Class<? extends JerseyResource> jerseyClazz : jerseyApis) {
-                if (log.isDebugEnabled()) {
-                  log.debug("Registering jersey resource class: {}", jerseyClazz.getName());
-                }
-                jerseyResources.register(jerseyClazz);
-                // See MetricsBeanFactory javadocs for a better understanding of this resource->RH
-                // mapping
-                if (inst instanceof RequestHandlerBase) {
-                  infoBeanByResource.put(jerseyClazz, (RequestHandlerBase) inst);
-                }
-              }
-            }
-          }
-        }
-      } else {
-        if (registerApi != null && registerApi)
-          apiBag.registerLazy((PluginHolder<SolrRequestHandler>) plugin, plugin.pluginInfo);
-      }
-    }
-    if (disableHandler == null) disableHandler = Boolean.FALSE;
     PluginHolder<T> old = null;
-    if (!disableHandler) old = registry.put(name, plugin);
+    old = registry.put(name, plugin);
     if (plugin.pluginInfo != null && plugin.pluginInfo.isDefault()) setDefault(name);
     if (plugin.isLoaded()) registerMBean(plugin.get(), core, name);
     // old instance has been replaced - close it to prevent mem leaks
@@ -279,10 +211,6 @@ public class PluginBag<T> implements AutoCloseable {
     this.def = def;
   }
 
-  public Map<String, PluginHolder<T>> getRegistry() {
-    return immutableRegistry;
-  }
-
   public boolean contains(String name) {
     return registry.containsKey(name);
   }
@@ -327,9 +255,11 @@ public class PluginBag<T> implements AutoCloseable {
             infos.stream().map(i -> i.name).collect(Collectors.toList()));
       }
     }
-    for (Map.Entry<String, T> e : defaults.entrySet()) {
-      if (!contains(e.getKey())) {
-        put(e.getKey(), new PluginHolder<>(null, e.getValue()));
+    if(defaults != null) {
+      for (Map.Entry<String, T> e : defaults.entrySet()) {
+        if (!contains(e.getKey())) {
+          put(e.getKey(), new PluginHolder<>(null, e.getValue()));
+        }
       }
     }
   }
@@ -341,7 +271,7 @@ public class PluginBag<T> implements AutoCloseable {
     return result.isLoaded();
   }
 
-  private void registerMBean(Object inst, SolrCore core, String pluginKey) {
+  protected void registerMBean(Object inst, SolrCore core, String pluginKey) {
     if (core == null) return;
     if (inst instanceof SolrInfoBean) {
       SolrInfoBean mBean = (SolrInfoBean) inst;
@@ -398,7 +328,6 @@ public class PluginBag<T> implements AutoCloseable {
       return Optional.ofNullable(inst);
     }
 
-    @Override
     public T get() {
       return inst;
     }
@@ -436,7 +365,6 @@ public class PluginBag<T> implements AutoCloseable {
       return pluginInfo;
     }
 
-    @Override
     public String toString() {
       return String.valueOf(inst);
     }
@@ -518,21 +446,4 @@ public class PluginBag<T> implements AutoCloseable {
       return true;
     }
   }
-
-  public Api v2lookup(String path, String method, Map<String, String> parts) {
-    if (apiBag == null) {
-      throw new SolrException(
-          SolrException.ErrorCode.SERVER_ERROR,
-          "this should not happen, looking up for v2 API at the wrong place");
-    }
-    return apiBag.lookup(path, method, parts);
-  }
-
-  public ApiBag getApiBag() {
-    return apiBag;
-  }
-
-  public ResourceConfig getJerseyEndpoints() {
-    return jerseyResources;
-  }
 }
diff --git a/solr/core/src/java/org/apache/solr/core/RequestHandlerBag.java b/solr/core/src/java/org/apache/solr/core/RequestHandlerBag.java
new file mode 100644
index 00000000000..cfddb08e4a7
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/RequestHandlerBag.java
@@ -0,0 +1,125 @@
+package org.apache.solr.core;
+
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.solr.api.Api;
+import org.apache.solr.api.ApiBag;
+import org.apache.solr.api.ApiSupport;
+import org.apache.solr.api.JerseyResource;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.handler.RequestHandlerBase;
+import org.apache.solr.request.SolrRequestHandler;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.api.ApiBag.HANDLER_NAME;
+
+public class RequestHandlerBag extends PluginBag<SolrRequestHandler> {
+    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private  ApiBag apiBag;
+    private  ResourceConfig jerseyResources;
+    private  JerseyMetricsLookupRegistry infoBeanByResource;
+
+    public RequestHandlerBag(SolrCore core) {
+        super(SolrRequestHandler.class, core, true);
+        this.apiBag = new ApiBag(core != null);
+    }
+
+    @Override
+    public PluginHolder<SolrRequestHandler> put(String name, PluginHolder<SolrRequestHandler> plugin) {
+        Boolean registerApi = null;
+        Boolean disableHandler = null;
+        if (plugin.pluginInfo != null) {
+            String registerAt = plugin.pluginInfo.attributes.get("registerPath");
+            if (registerAt != null) {
+                List<String> strs = StrUtils.splitSmart(registerAt, ',');
+                disableHandler = !strs.contains("/solr");
+                registerApi = strs.contains("/v2");
+            }
+        }
+
+        if (apiBag != null) {
+            if (plugin.isLoaded()) {
+                SolrRequestHandler inst = plugin.get();
+                if (inst instanceof ApiSupport) {
+                    ApiSupport apiSupport = (ApiSupport) inst;
+                    if (registerApi == null) registerApi = apiSupport.registerV2();
+                    if (disableHandler == null) disableHandler = !apiSupport.registerV1();
+
+                    if (registerApi) {
+                        Collection<Api> apis = apiSupport.getApis();
+                        if (apis != null) {
+                            Map<String, String> nameSubstitutes = singletonMap(HANDLER_NAME, name);
+                            for (Api api : apis) {
+                                apiBag.register(api, nameSubstitutes);
+                            }
+                        }
+
+                        // TODO Should we use <requestHandler name="/blah"> to override the path that each
+                        //  resource registers under?
+                        Collection<Class<? extends JerseyResource>> jerseyApis =
+                                apiSupport.getJerseyResources();
+                        if (!CollectionUtils.isEmpty(jerseyApis)) {
+                            for (Class<? extends JerseyResource> jerseyClazz : jerseyApis) {
+                                if (log.isDebugEnabled()) {
+                                    log.debug("Registering jersey resource class: {}", jerseyClazz.getName());
+                                }
+                                jerseyResources.register(jerseyClazz);
+                                // See MetricsBeanFactory javadocs for a better understanding of this resource->RH
+                                // mapping
+                                if (inst instanceof RequestHandlerBase) {
+                                    infoBeanByResource.put(jerseyClazz, (RequestHandlerBase) inst);
+                                }
+                            }
+                        }
+                    }
+                }
+            } else {
+                if (registerApi != null && registerApi)
+                    apiBag.registerLazy((PluginHolder<SolrRequestHandler>) plugin, plugin.pluginInfo);
+            }
+        }
+        PluginHolder<SolrRequestHandler> old = null;
+        if (disableHandler == null) disableHandler = Boolean.FALSE;
+        if (!disableHandler) old = registry.put(name, plugin);
+        if (plugin.pluginInfo != null && plugin.pluginInfo.isDefault()) setDefault(name);
+        if (plugin.isLoaded()) registerMBean(plugin.get(), core, name);
+        // old instance has been replaced - close it to prevent mem leaks
+        if (old != null && old != plugin) {
+            closeQuietly(old);
+        }
+        return old;
+    }
+
+    public Api v2lookup(String path, String method, Map<String, String> parts) {
+        if (apiBag == null) {
+            throw new SolrException(
+                    SolrException.ErrorCode.SERVER_ERROR,
+                    "this should not happen, looking up for v2 API at the wrong place");
+        }
+        return apiBag.lookup(path, method, parts);
+    }
+
+
+
+    public ApiBag getApiBag() {
+        return apiBag;
+    }
+
+    public ResourceConfig getJerseyEndpoints() {
+        return jerseyResources;
+    }
+
+    public static class JerseyMetricsLookupRegistry
+        extends HashMap<Class<? extends JerseyResource>, RequestHandlerBase> {}
+}
diff --git a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java
index dca7c832e3f..4c2e7d15041 100644
--- a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java
+++ b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java
@@ -33,7 +33,7 @@ public final class RequestHandlers {
 
   private final SolrCore core;
 
-  final PluginBag<SolrRequestHandler> handlers;
+  final RequestHandlerBag handlers;
 
   /**
    * Trim the trailing '/' if it's there, and convert null to empty string.
@@ -51,7 +51,7 @@ public final class RequestHandlers {
     this.core = core;
     // we need a thread safe registry since methods like register are currently documented to be
     // thread safe.
-    handlers = new PluginBag<>(SolrRequestHandler.class, core, true);
+    handlers = new RequestHandlerBag( core);
   }
 
   /**
@@ -79,7 +79,7 @@ public final class RequestHandlers {
   }
 
   /** Returns an unmodifiable Map containing the registered handlers */
-  public PluginBag<SolrRequestHandler> getRequestHandlers() {
+  public RequestHandlerBag getRequestHandlers() {
     return handlers;
   }
 
diff --git a/solr/core/src/java/org/apache/solr/core/ResponseWriters.java b/solr/core/src/java/org/apache/solr/core/ResponseWriters.java
new file mode 100644
index 00000000000..8a11bb3390f
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/core/ResponseWriters.java
@@ -0,0 +1,117 @@
+/*
+ * 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.solr.core;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.solr.client.solrj.impl.BinaryResponseParser;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.handler.ReplicationHandler;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.*;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ResponseWriters {
+    public static final SolrConfig.SolrPluginInfo info =  SolrConfig.classVsSolrPluginInfo.get(QueryResponseWriter.class.getName());
+    private static QueryResponseWriter standard;
+
+    public static QueryResponseWriter get(String name) {
+        return DEFAULT_RESPONSE_WRITERS.get(name);
+    }
+    public static QueryResponseWriter getOrDefault(String name){
+        QueryResponseWriter result = DEFAULT_RESPONSE_WRITERS.get(name);
+        return result==null? standard: result;
+
+    }
+
+    public static final Map<String, QueryResponseWriter> DEFAULT_RESPONSE_WRITERS;
+    public static final Map<String, PluginBag.PluginHolder<QueryResponseWriter>> DEFAULT_RESPONSE_WRITER_HOLDERS ;
+
+
+    static {
+        HashMap<String, QueryResponseWriter> m = new HashMap<>(15, 1);
+        m.put("xml", new XMLResponseWriter());
+        m.put(CommonParams.JSON, new JSONResponseWriter());
+        m.put("standard", m.get(CommonParams.JSON));
+        m.put("geojson", new GeoJSONResponseWriter());
+        m.put("graphml", new GraphMLResponseWriter());
+        m.put("python", new PythonResponseWriter());
+        m.put("php", new PHPResponseWriter());
+        m.put("phps", new PHPSerializedResponseWriter());
+        m.put("ruby", new RubyResponseWriter());
+        m.put("raw", new RawResponseWriter());
+        m.put(CommonParams.JAVABIN, new BinaryResponseWriter());
+        m.put("csv", new CSVResponseWriter());
+        m.put("schema.xml", new SchemaXmlResponseWriter());
+        m.put("smile", new SmileResponseWriter());
+        standard = m.get("standard");
+        m.put(ReplicationHandler.FILE_STREAM, getFileStreamWriter());
+        DEFAULT_RESPONSE_WRITERS = Collections.unmodifiableMap(m);
+        try {
+            m.put(
+                    "xlsx",
+                    (QueryResponseWriter)
+                            Class.forName("org.apache.solr.handler.extraction.XLSXResponseWriter")
+                                    .getConstructor()
+                                    .newInstance());
+        } catch (Exception e) {
+            // don't worry; extraction module not in class path
+        }
+        ImmutableMap.Builder<String, PluginBag.PluginHolder<QueryResponseWriter>> b = ImmutableMap.builder();
+        DEFAULT_RESPONSE_WRITERS.forEach((k, v) -> b.put(k, new PluginBag.PluginHolder<>(v, info)));
+        DEFAULT_RESPONSE_WRITER_HOLDERS = b.build();
+    }
+
+    private static BinaryResponseWriter getFileStreamWriter() {
+        return new BinaryResponseWriter() {
+            @Override
+            public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response)
+                    throws IOException {
+                RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM);
+                if (rawWriter != null) {
+                    rawWriter.write(out);
+                    if (rawWriter instanceof Closeable) ((Closeable) rawWriter).close();
+                }
+            }
+
+            @Override
+            public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
+                RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM);
+                if (rawWriter != null) {
+                    return rawWriter.getContentType();
+                } else {
+                    return BinaryResponseParser.BINARY_CONTENT_TYPE;
+                }
+            }
+        };
+    }
+
+
+
+    public interface RawWriter {
+        default String getContentType() {
+            return BinaryResponseParser.BINARY_CONTENT_TYPE;
+        }
+
+        void write(OutputStream os) throws IOException;
+    }
+}
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 cf6e7d51cc9..8ff9255d221 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -17,6 +17,7 @@
 package org.apache.solr.core;
 
 import static org.apache.solr.common.params.CommonParams.PATH;
+import static org.apache.solr.core.ResponseWriters.DEFAULT_RESPONSE_WRITERS;
 
 import com.codahale.metrics.Counter;
 import com.codahale.metrics.Timer;
@@ -1997,7 +1998,7 @@ public class SolrCore implements SolrInfoBean, Closeable {
           && searchComponents.get(name) instanceof HighlightComponent) {
         if (!HighlightComponent.COMPONENT_NAME.equals(name)) {
           searchComponents.put(
-              HighlightComponent.COMPONENT_NAME, searchComponents.getRegistry().get(name));
+              HighlightComponent.COMPONENT_NAME, searchComponents.getHolder(name));
         }
         break;
       }
@@ -2991,84 +2992,20 @@ public class SolrCore implements SolrInfoBean, Closeable {
   public PluginBag<QueryResponseWriter> getResponseWriters() {
     return responseWriters;
   }
-
   private final PluginBag<QueryResponseWriter> responseWriters =
-      new PluginBag<>(QueryResponseWriter.class, this);
-  public static final Map<String, QueryResponseWriter> DEFAULT_RESPONSE_WRITERS;
-
-  static {
-    HashMap<String, QueryResponseWriter> m = new HashMap<>(15, 1);
-    m.put("xml", new XMLResponseWriter());
-    m.put(CommonParams.JSON, new JSONResponseWriter());
-    m.put("standard", m.get(CommonParams.JSON));
-    m.put("geojson", new GeoJSONResponseWriter());
-    m.put("graphml", new GraphMLResponseWriter());
-    m.put("python", new PythonResponseWriter());
-    m.put("php", new PHPResponseWriter());
-    m.put("phps", new PHPSerializedResponseWriter());
-    m.put("ruby", new RubyResponseWriter());
-    m.put("raw", new RawResponseWriter());
-    m.put(CommonParams.JAVABIN, new BinaryResponseWriter());
-    m.put("csv", new CSVResponseWriter());
-    m.put("schema.xml", new SchemaXmlResponseWriter());
-    m.put("smile", new SmileResponseWriter());
-    m.put(ReplicationHandler.FILE_STREAM, getFileStreamWriter());
-    DEFAULT_RESPONSE_WRITERS = Collections.unmodifiableMap(m);
-    try {
-      m.put(
-          "xlsx",
-          (QueryResponseWriter)
-              Class.forName("org.apache.solr.handler.extraction.XLSXResponseWriter")
-                  .getConstructor()
-                  .newInstance());
-    } catch (Exception e) {
-      // don't worry; extraction module not in class path
-    }
-  }
-
-  private static BinaryResponseWriter getFileStreamWriter() {
-    return new BinaryResponseWriter() {
-      @Override
-      public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response)
-          throws IOException {
-        RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM);
-        if (rawWriter != null) {
-          rawWriter.write(out);
-          if (rawWriter instanceof Closeable) ((Closeable) rawWriter).close();
-        }
-      }
-
-      @Override
-      public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
-        RawWriter rawWriter = (RawWriter) response.getValues().get(ReplicationHandler.FILE_STREAM);
-        if (rawWriter != null) {
-          return rawWriter.getContentType();
-        } else {
-          return BinaryResponseParser.BINARY_CONTENT_TYPE;
-        }
-      }
-    };
-  }
-
+          new PluginBag<>(QueryResponseWriter.class, this, false,
+                  ResponseWriters.DEFAULT_RESPONSE_WRITER_HOLDERS, ResponseWriters.info);
   public void fetchLatestSchema() {
     IndexSchema schema = configSet.getIndexSchema(true);
     setLatestSchema(schema);
   }
 
-  public interface RawWriter {
-    default String getContentType() {
-      return BinaryResponseParser.BINARY_CONTENT_TYPE;
-    }
-
-    void write(OutputStream os) throws IOException;
-  }
-
   /**
    * Configure the query response writers. There will always be a default writer; additional writers
    * may also be configured.
    */
   private void initWriters() {
-    responseWriters.init(DEFAULT_RESPONSE_WRITERS, this);
+    responseWriters.init(null, this);
     // configure the default response writer; this one should never be null
     if (responseWriters.getDefault() == null) responseWriters.setDefault("standard");
   }
diff --git a/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java b/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java
index 0a98bba9a2f..d5985e06f2c 100644
--- a/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java
+++ b/solr/core/src/java/org/apache/solr/filestore/PackageStoreAPI.java
@@ -46,7 +46,7 @@ import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.util.Utils;
 import org.apache.solr.core.BlobRepository;
 import org.apache.solr.core.CoreContainer;
-import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.ResponseWriters;
 import org.apache.solr.pkg.PackageAPI;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
@@ -348,7 +348,7 @@ public class PackageStoreAPI {
       req.setParams(SolrParams.wrapDefaults(solrParams, req.getParams()));
       rsp.add(
           FILE_STREAM,
-          (SolrCore.RawWriter)
+          (ResponseWriters.RawWriter)
               os ->
                   packageStore.get(
                       path,
diff --git a/solr/core/src/java/org/apache/solr/handler/BlobHandler.java b/solr/core/src/java/org/apache/solr/handler/BlobHandler.java
index 3bb14b19ed4..789be7117be 100644
--- a/solr/core/src/java/org/apache/solr/handler/BlobHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/BlobHandler.java
@@ -53,6 +53,7 @@ import org.apache.solr.common.util.ContentStream;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.ResponseWriters;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.admin.api.GetBlobInfoAPI;
 import org.apache.solr.handler.admin.api.UploadBlobAPI;
@@ -210,24 +211,20 @@ public class BlobHandler extends RequestHandlerBase
           if (docs.totalHits.value > 0) {
             rsp.add(
                 ReplicationHandler.FILE_STREAM,
-                new SolrCore.RawWriter() {
-
-                  @Override
-                  public void write(OutputStream os) throws IOException {
-                    Document doc = req.getSearcher().doc(docs.scoreDocs[0].doc);
-                    IndexableField sf = doc.getField("blob");
-                    FieldType fieldType = req.getSchema().getField("blob").getType();
-                    ByteBuffer buf = (ByteBuffer) fieldType.toObject(sf);
-                    if (buf == null) {
-                      // should never happen unless a user wrote this document directly
-                      throw new SolrException(
-                          SolrException.ErrorCode.NOT_FOUND,
-                          "Invalid document . No field called blob");
-                    } else {
-                      os.write(buf.array(), buf.arrayOffset(), buf.limit());
-                    }
-                  }
-                });
+                    (ResponseWriters.RawWriter) os -> {
+                      Document doc = req.getSearcher().doc(docs.scoreDocs[0].doc);
+                      IndexableField sf = doc.getField("blob");
+                      FieldType fieldType = req.getSchema().getField("blob").getType();
+                      ByteBuffer buf = (ByteBuffer) fieldType.toObject(sf);
+                      if (buf == null) {
+                        // should never happen unless a user wrote this document directly
+                        throw new SolrException(
+                            SolrException.ErrorCode.NOT_FOUND,
+                            "Invalid document . No field called blob");
+                      } else {
+                        os.write(buf.array(), buf.arrayOffset(), buf.limit());
+                      }
+                    });
 
           } else {
             throw new SolrException(
diff --git a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
index 1a56f7980d5..236159bbe0a 100644
--- a/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/ClusterAPI.java
@@ -77,6 +77,16 @@ public class ClusterAPI {
     this.configSetsHandler = configSetsHandler;
   }
 
+  @EndPoint(method = GET, path = "/node/heap", permission = COLL_READ_PERM)
+  public void heap(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+    Runtime rt = Runtime.getRuntime();
+    if(req.getParams().getBool("gc", false)) {
+      rt.gc();
+    }
+    rsp.add("heap",  rt.totalMemory() - rt.freeMemory());
+  }
+
+
   @EndPoint(method = GET, path = "/cluster/node-roles", permission = COLL_READ_PERM)
   public void roles(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
     rsp.add(
diff --git a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java
index 7cae4115598..29b0e47aaa7 100644
--- a/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/ReplicationHandler.java
@@ -83,13 +83,8 @@ import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.SolrNamedThreadFactory;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.common.util.SuppressForbidden;
-import org.apache.solr.core.CloseHook;
-import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.*;
 import org.apache.solr.core.DirectoryFactory.DirContext;
-import org.apache.solr.core.IndexDeletionPolicyWrapper;
-import org.apache.solr.core.SolrCore;
-import org.apache.solr.core.SolrDeletionPolicy;
-import org.apache.solr.core.SolrEventListener;
 import org.apache.solr.core.backup.repository.BackupRepository;
 import org.apache.solr.core.backup.repository.LocalFileSystemRepository;
 import org.apache.solr.handler.IndexFetcher.IndexFetchResult;
@@ -1582,7 +1577,7 @@ public class ReplicationHandler extends RequestHandlerBase implements SolrCoreAw
   }
 
   /** This class is used to read and send files in the lucene index */
-  private class DirectoryFileStream implements SolrCore.RawWriter {
+  private class DirectoryFileStream implements ResponseWriters.RawWriter {
     protected SolrParams params;
 
     protected FastOutputStream fos;
diff --git a/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java b/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java
index cc1599562a3..d3764c1088b 100644
--- a/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java
+++ b/solr/core/src/java/org/apache/solr/handler/export/ExportWriter.java
@@ -55,6 +55,7 @@ import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.params.StreamParams;
 import org.apache.solr.common.util.JavaBinCodec;
+import org.apache.solr.core.ResponseWriters;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.metrics.SolrMetricsContext;
 import org.apache.solr.request.SolrQueryRequest;
@@ -91,7 +92,7 @@ import org.slf4j.LoggerFactory;
  * across the wire) and marked as sent (unset in the bitmap). This process repeats until all
  * matching documents have been sent.
  */
-public class ExportWriter implements SolrCore.RawWriter, Closeable {
+public class ExportWriter implements ResponseWriters.RawWriter, Closeable {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   public static final String BATCH_SIZE_PARAM = "batchSize";
diff --git a/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java b/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java
index 7a4bb484e4b..dc613dbe958 100644
--- a/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java
+++ b/solr/core/src/java/org/apache/solr/jersey/JerseyApplications.java
@@ -21,7 +21,8 @@ import io.swagger.v3.oas.annotations.OpenAPIDefinition;
 import io.swagger.v3.oas.annotations.info.Info;
 import io.swagger.v3.oas.annotations.info.License;
 import javax.inject.Singleton;
-import org.apache.solr.core.PluginBag;
+
+import org.apache.solr.core.RequestHandlerBag;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.SolrQueryResponse;
@@ -44,7 +45,7 @@ import org.glassfish.jersey.server.ResourceConfig;
 public class JerseyApplications {
 
   public static class CoreContainerApp extends ResourceConfig {
-    public CoreContainerApp(PluginBag.JerseyMetricsLookupRegistry beanRegistry) {
+    public CoreContainerApp(RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry) {
       super();
 
       // Authentication and authorization
@@ -68,7 +69,7 @@ public class JerseyApplications {
             @Override
             protected void configure() {
               bindFactory(new MetricBeanFactory(beanRegistry))
-                  .to(PluginBag.JerseyMetricsLookupRegistry.class)
+                  .to(RequestHandlerBag.JerseyMetricsLookupRegistry.class)
                   .in(Singleton.class);
             }
           });
@@ -102,7 +103,7 @@ public class JerseyApplications {
 
   public static class SolrCoreApp extends CoreContainerApp {
 
-    public SolrCoreApp(SolrCore solrCore, PluginBag.JerseyMetricsLookupRegistry beanRegistry) {
+    public SolrCoreApp(SolrCore solrCore, RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry) {
       super(beanRegistry);
 
       // Dependency Injection for Jersey resources
diff --git a/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java b/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java
index c23851359d9..9821cd51f87 100644
--- a/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java
+++ b/solr/core/src/java/org/apache/solr/jersey/MetricBeanFactory.java
@@ -17,7 +17,7 @@
 
 package org.apache.solr.jersey;
 
-import org.apache.solr.core.PluginBag;
+import org.apache.solr.core.RequestHandlerBag;
 import org.glassfish.hk2.api.Factory;
 
 /**
@@ -26,7 +26,7 @@ import org.glassfish.hk2.api.Factory;
  * <p>Currently, Jersey resources that have a corresponding v1 API produce the same metrics as their
  * v1 equivalent and rely on the v1 requestHandler instance to do so. Solr facilitates this by
  * building a map of the Jersey resource to requestHandler mapping (a {@link
- * org.apache.solr.core.PluginBag.JerseyMetricsLookupRegistry}), and injecting it into the pre- and
+ * RequestHandlerBag.JerseyMetricsLookupRegistry}), and injecting it into the pre- and
  * post- Jersey filters that handle metrics.
  *
  * <p>This isn't ideal, as requestHandler's don't really "fit" conceptually here. But it's
@@ -35,21 +35,21 @@ import org.glassfish.hk2.api.Factory;
  * @see RequestMetricHandling.PreRequestMetricsFilter
  * @see RequestMetricHandling.PostRequestMetricsFilter
  */
-public class MetricBeanFactory implements Factory<PluginBag.JerseyMetricsLookupRegistry> {
+public class MetricBeanFactory implements Factory<RequestHandlerBag.JerseyMetricsLookupRegistry> {
 
-  private final PluginBag.JerseyMetricsLookupRegistry metricsLookupRegistry;
+  private final RequestHandlerBag.JerseyMetricsLookupRegistry metricsLookupRegistry;
 
-  public MetricBeanFactory(PluginBag.JerseyMetricsLookupRegistry metricsLookupRegistry) {
+  public MetricBeanFactory(RequestHandlerBag.JerseyMetricsLookupRegistry metricsLookupRegistry) {
     this.metricsLookupRegistry = metricsLookupRegistry;
   }
 
   @Override
-  public PluginBag.JerseyMetricsLookupRegistry provide() {
+  public RequestHandlerBag.JerseyMetricsLookupRegistry provide() {
     return metricsLookupRegistry;
   }
 
   @Override
-  public void dispose(PluginBag.JerseyMetricsLookupRegistry instance) {
+  public void dispose(RequestHandlerBag.JerseyMetricsLookupRegistry instance) {
     /* No-op */
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java b/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java
index 595027005ad..b4543a2084b 100644
--- a/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java
+++ b/solr/core/src/java/org/apache/solr/jersey/RequestMetricHandling.java
@@ -31,7 +31,8 @@ import javax.ws.rs.container.ContainerResponseContext;
 import javax.ws.rs.container.ContainerResponseFilter;
 import javax.ws.rs.container.ResourceInfo;
 import javax.ws.rs.core.Context;
-import org.apache.solr.core.PluginBag;
+
+import org.apache.solr.core.RequestHandlerBag;
 import org.apache.solr.handler.RequestHandlerBase;
 import org.apache.solr.request.SolrQueryRequest;
 import org.slf4j.Logger;
@@ -58,10 +59,10 @@ public class RequestMetricHandling {
 
     @Context private ResourceInfo resourceInfo;
 
-    private PluginBag.JerseyMetricsLookupRegistry beanRegistry;
+    private RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry;
 
     @Inject
-    public PreRequestMetricsFilter(PluginBag.JerseyMetricsLookupRegistry beanRegistry) {
+    public PreRequestMetricsFilter(RequestHandlerBag.JerseyMetricsLookupRegistry beanRegistry) {
       this.beanRegistry = beanRegistry;
     }
 
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index debd481a9a3..692dde2f8d6 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -101,6 +101,7 @@ import org.apache.solr.common.util.TimeSource;
 import org.apache.solr.common.util.Utils;
 import org.apache.solr.common.util.ValidatingJsonMap;
 import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.ResponseWriters;
 import org.apache.solr.core.SolrConfig;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.handler.ContentStreamHandlerBase;
@@ -876,7 +877,7 @@ public class HttpSolrCall {
       }
     }
     QueryResponseWriter respWriter =
-        SolrCore.DEFAULT_RESPONSE_WRITERS.get(solrReq.getParams().get(CommonParams.WT));
+        ResponseWriters.get(solrReq.getParams().get(CommonParams.WT));
     if (respWriter == null) respWriter = getResponseWriter();
     writeResponse(solrResp, respWriter, Method.getMethod(req.getMethod()));
     if (shouldAudit()) {
@@ -905,8 +906,7 @@ public class HttpSolrCall {
     if (core != null) {
       return core.getQueryResponseWriter(wt);
     } else {
-      return SolrCore.DEFAULT_RESPONSE_WRITERS.getOrDefault(
-          wt, SolrCore.DEFAULT_RESPONSE_WRITERS.get("standard"));
+      return ResponseWriters.getOrDefault(wt);
     }
   }
 
diff --git a/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java b/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java
index 1f5476632f8..e0bfc06c371 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/UpdateRequestProcessorChain.java
@@ -302,7 +302,7 @@ public final class UpdateRequestProcessorChain implements PluginInfoInitialized
       if (s.isEmpty()) continue;
       UpdateRequestProcessorFactory p = null;
       PluginBag.PluginHolder<UpdateRequestProcessorFactory> holder =
-          core.getUpdateProcessors().getRegistry().get(s);
+          core.getUpdateProcessors().getHolder(s);
       if (holder instanceof PackagePluginHolder) {
         p = new LazyUpdateRequestProcessorFactory(holder);
       } else {
diff --git a/solr/core/src/test/org/apache/solr/OutputWriterTest.java b/solr/core/src/test/org/apache/solr/OutputWriterTest.java
index 13eb69d9014..471471b3e41 100644
--- a/solr/core/src/test/org/apache/solr/OutputWriterTest.java
+++ b/solr/core/src/test/org/apache/solr/OutputWriterTest.java
@@ -67,10 +67,10 @@ public class OutputWriterTest extends SolrTestCaseJ4 {
 
   public void testLazy() {
     PluginBag.PluginHolder<QueryResponseWriter> qrw =
-        h.getCore().getResponseWriters().getRegistry().get("useless");
+        h.getCore().getResponseWriters().getHolder("useless");
     assertTrue("Should be a lazy class", qrw instanceof PluginBag.LazyPluginHolder);
 
-    qrw = h.getCore().getResponseWriters().getRegistry().get("xml");
+    qrw = h.getCore().getResponseWriters().getHolder("xml");
     assertTrue("Should not be a lazy class", qrw.isLoaded());
     assertSame("Should not be a lazy class", qrw.getClass(), PluginBag.PluginHolder.class);
   }
diff --git a/solr/core/src/test/org/apache/solr/core/PluginBagTest.java b/solr/core/src/test/org/apache/solr/core/PluginBagTest.java
index f0281038197..c0a011bc67d 100644
--- a/solr/core/src/test/org/apache/solr/core/PluginBagTest.java
+++ b/solr/core/src/test/org/apache/solr/core/PluginBagTest.java
@@ -49,14 +49,14 @@ public class PluginBagTest extends SolrTestCaseJ4 {
 
   @Test
   public void testOnlyInitsJerseyIfHoldingRequestHandlers() {
-    final PluginBag<SearchComponent> nonRequestHandlerBag =
-        new PluginBag<>(SearchComponent.class, null);
+    final RequestHandlerBag nonRequestHandlerBag =
+        new RequestHandlerBag(null);
     assertNull(
         "Jersey app should not be created for plugin bags that aren't managing RequestHandler's",
         nonRequestHandlerBag.getJerseyEndpoints());
 
-    final PluginBag<SolrRequestHandler> handlerPluginBag =
-        new PluginBag<>(SolrRequestHandler.class, null);
+    final RequestHandlerBag handlerPluginBag =
+        new RequestHandlerBag(null);
     assertNotNull(
         "Jersey app should be created for plugin bags that manage RequestHandlers",
         handlerPluginBag.getJerseyEndpoints());
@@ -64,16 +64,16 @@ public class PluginBagTest extends SolrTestCaseJ4 {
 
   @Test
   public void testCreatesCoreSpecificJerseyAppIfCoreProvided() {
-    final PluginBag<SolrRequestHandler> handlerPluginBag =
-        new PluginBag<>(SolrRequestHandler.class, solrCore);
+    final RequestHandlerBag handlerPluginBag =
+        new RequestHandlerBag(solrCore);
     assertEquals(
         JerseyApplications.SolrCoreApp.class, handlerPluginBag.getJerseyEndpoints().getClass());
   }
 
   @Test
   public void testCreatesContainerSpecificJerseyAppIfNoCoreProvided() {
-    final PluginBag<SolrRequestHandler> handlerPluginBag =
-        new PluginBag<>(SolrRequestHandler.class, null);
+    final RequestHandlerBag handlerPluginBag =
+        new RequestHandlerBag(null);
     assertEquals(
         JerseyApplications.CoreContainerApp.class,
         handlerPluginBag.getJerseyEndpoints().getClass());
@@ -81,8 +81,8 @@ public class PluginBagTest extends SolrTestCaseJ4 {
 
   @Test
   public void testRegistersJerseyResourcesAssociatedWithRequestHandlers() {
-    final PluginBag<SolrRequestHandler> handlerPluginBag =
-        new PluginBag<>(SolrRequestHandler.class, null);
+    final RequestHandlerBag handlerPluginBag =
+        new RequestHandlerBag(null);
     assertFalse(handlerPluginBag.getJerseyEndpoints().isRegistered(ListConfigSetsAPI.class));
 
     handlerPluginBag.put("/foo", new ConfigSetsHandler(coreContainer));
diff --git a/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java b/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java
index 7ac9d6a107e..42fd4a466ab 100644
--- a/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java
+++ b/solr/core/src/test/org/apache/solr/core/RequestHandlersTest.java
@@ -52,7 +52,7 @@ public class RequestHandlersTest extends SolrTestCaseJ4 {
   public void testLazyLoading() {
     SolrCore core = h.getCore();
     PluginBag.PluginHolder<SolrRequestHandler> handler =
-        core.getRequestHandlers().getRegistry().get("/lazy");
+        core.getRequestHandlers().getHolder("/lazy");
     assertFalse(handler.isLoaded());
 
     assertU(