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

dsmiley pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 53a2a36df2f SOLR-17118: Simplify/fix CoreContainerProvider 
initialization (#2474)
53a2a36df2f is described below

commit 53a2a36df2fb843df640ce51ead4a6e0f65d8e74
Author: David Smiley <[email protected]>
AuthorDate: Sat Jun 1 04:04:21 2024 +0100

    SOLR-17118: Simplify/fix CoreContainerProvider initialization (#2474)
    
    Thereby fixing a rare occurrence of Solr hanging on startup.
    
    CoreContainerProvider:  Don't need any CountDownLatch (x2), the 
synchronized WeakHashMap of "services", the ServiceHolder, the 
ContextInitializationKey.  No looping to wait for initialization.
    
    JettySolrRunner: incorporate the CoreContainerProvider and various servlet 
filters in a normal way -- add all this before starting, not after.  Thus Jetty 
will shut them down properly so we don't have to.  Removed some needless 
synchronized wait/notify and other needless stuff.
    
    HealthCheckHandlerTest was shutting down CoreContainer improperly; this 
subset of the test was removed.
    
    (cherry picked from commit 1177796e32631c62d8f00e7df4341c92b75e1617)
---
 solr/CHANGES.txt                                   |   2 +
 .../java/org/apache/solr/core/CoreContainer.java   |   1 +
 .../apache/solr/servlet/CoreContainerProvider.java | 173 +++++----------------
 .../apache/solr/servlet/SolrDispatchFilter.java    |  27 +---
 .../test/org/apache/solr/cloud/ZkFailoverTest.java |   2 +-
 .../solr/handler/admin/HealthCheckHandlerTest.java |  30 ----
 .../org/apache/solr/embedded/JettySolrRunner.java  | 129 +++++----------
 7 files changed, 85 insertions(+), 279 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index ac68379d1be..a6489e78012 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -85,6 +85,8 @@ Bug Fixes
 
 * SOLR-17255: Fix bugs in SolrParams.toLocalParamsString() (hossman)
 
+* SOLR-17118: Simplify CoreContainerProvider initialization and 
JettySolrRunner. Avoid deadlock/hang. (David Smiley)
+
 Dependency Upgrades
 ---------------------
 (No changes)
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 b9a13cb19d9..0285b56e5d9 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -1259,6 +1259,7 @@ public class CoreContainer {
     return isShutDown;
   }
 
+  /** Close / shut down. Only called by {@link 
org.apache.solr.servlet.CoreContainerProvider}. */
   public void shutdown() {
 
     ZkController zkController = getZkController();
diff --git 
a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java 
b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
index 6cd261bc0f2..8b8bc3c927d 100644
--- a/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
+++ b/solr/core/src/java/org/apache/solr/servlet/CoreContainerProvider.java
@@ -36,16 +36,10 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.time.Instant;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.Properties;
-import java.util.WeakHashMap;
-import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
 import java.util.stream.Stream;
 import javax.naming.Context;
 import javax.naming.InitialContext;
@@ -93,72 +87,63 @@ public class CoreContainerProvider implements 
ServletContextListener {
   private HttpClient httpClient;
   private SolrMetricManager metricManager;
   private RateLimitManager rateLimitManager;
-  private final CountDownLatch init = new CountDownLatch(1);
   private String registryName;
-  // AFAIK the only reason we need this is to support JettySolrRunner for 
tests. In tests we might
-  // have multiple CoreContainers in the same JVM, but I *think* that doesn't 
happen in a real
-  // server.
-  private static final Map<ContextInitializationKey, ServiceHolder> services =
-      Collections.synchronizedMap(new WeakHashMap<>());
-
-  // todo: dependency injection instead, but for now this method and the 
associated map will have
-  // to suffice.
-  // Note that this relies on ServletContext.equals() not implementing 
anything significantly
-  // different than Object.equals for its .equals method (I've found no 
implementation that even
-  // implements it).
-  public static ServiceHolder serviceForContext(ServletContext ctx) throws 
InterruptedException {
-    ContextInitializationKey key = new ContextInitializationKey(ctx);
-    return services.computeIfAbsent(key, ServiceHolder::new);
+
+  /**
+   * Acquires an instance from the context. Never null.
+   *
+   * @throws IllegalStateException if not present.
+   */
+  public static CoreContainerProvider serviceForContext(ServletContext ctx) {
+    var provider = (CoreContainerProvider) 
ctx.getAttribute(CoreContainerProvider.class.getName());
+    if (provider == null) {
+      throw new IllegalStateException("CoreContainer failed to initialize");
+    }
+    return provider;
   }
 
   @Override
-  public void contextInitialized(ServletContextEvent sce) {
-    init(sce.getServletContext());
+  public void contextInitialized(ServletContextEvent event) {
+    final var ctx = event.getServletContext();
+    init(ctx);
+    ctx.setAttribute(CoreContainerProvider.class.getName(), this);
   }
 
   @Override
   public void contextDestroyed(ServletContextEvent sce) {
     close();
+    // could remove ourselves from ctx but why bother
   }
 
+  /**
+   * @see SolrDispatchFilter#getCores()
+   */
   CoreContainer getCoreContainer() throws UnavailableException {
-    waitForCoreContainer(() -> cores, init);
+    checkReady();
     return cores;
   }
 
+  /**
+   * @see SolrDispatchFilter#getHttpClient()
+   */
   HttpClient getHttpClient() throws UnavailableException {
-    waitForCoreContainer(() -> cores, init);
+    checkReady();
     return httpClient;
   }
 
-  private static void waitForCoreContainer(Supplier<CoreContainer> provider, 
CountDownLatch latch)
-      throws UnavailableException {
-    CoreContainer cores = provider.get();
-    if (cores == null || cores.isShutDown()) {
-      long startWait = System.nanoTime();
-      try {
-        while (!latch.await(10, TimeUnit.SECONDS)) {
-          long now = System.nanoTime();
-          if (log.isInfoEnabled()) {
-            log.info(
-                "Still waiting for CoreContainerStartup ({} seconds elapsed)",
-                (now - startWait) / 1_000_000_000);
-          }
-        }
-      } catch (InterruptedException e) { // well, no wait then
-        Thread.currentThread().interrupt();
-      }
-      cores = provider.get();
-      if (cores == null || cores.isShutDown()) {
-        final String msg =
-            "Error processing the request. CoreContainer is either not 
initialized or shutting down.";
-        log.error(msg);
-        throw new UnavailableException(msg);
-      }
+  private void checkReady() throws UnavailableException {
+    // TODO throw AlreadyClosedException instead?
+    if (cores == null) {
+      // cores could be null if it didn't start properly or if it's completely 
shut down.
+      // It appears impossible that it'd be null if it didn't even try to 
start yet.
+      final String msg = "Error processing the request. CoreContainer has shut 
down.";
+      log.error(msg);
+      throw new UnavailableException(msg);
     }
+    assert !cores.isShutDown(); // shutdown sequence initiates *here*, thus 
will be nulled first
   }
 
-  public void close() {
+  private void close() {
     CoreContainer cc = cores;
 
     // Mark Miller suggested that we should be publishing that we are down 
before anything else
@@ -197,9 +182,9 @@ public class CoreContainerProvider implements 
ServletContextListener {
     }
   }
 
-  public void init(ServletContext servletContext) {
+  private void init(ServletContext servletContext) {
     if (log.isTraceEnabled()) {
-      log.trace("CoreService.init(): {}", this.getClass().getClassLoader());
+      log.trace("init(): {}", this.getClass().getClassLoader());
     }
     CoreContainer coresInit = null;
     try {
@@ -272,12 +257,8 @@ public class CoreContainerProvider implements 
ServletContextListener {
         throw (Error) t;
       }
     } finally {
-      log.trace("SolrDispatchFilter.init() done");
+      log.trace("init() done");
       this.cores = coresInit; // crucially final assignment
-      services
-          .computeIfAbsent(new ContextInitializationKey(servletContext), 
ServiceHolder::new)
-          .setService(this);
-      init.countDown();
     }
   }
 
@@ -496,82 +477,4 @@ public class CoreContainerProvider implements 
ServletContextListener {
   void setRateLimitManager(RateLimitManager rateLimitManager) {
     this.rateLimitManager = rateLimitManager;
   }
-
-  private static class ContextInitializationKey {
-    private final ServletContext ctx;
-    private final CountDownLatch initializing = new CountDownLatch(1);
-
-    private ContextInitializationKey(ServletContext ctx) {
-      if (ctx == null) {
-        throw new IllegalArgumentException("Context must not be null");
-      }
-      // if one of these is reachable both must be to avoid collection from 
weak hashmap, so
-      // set an attribute holding this object to ensure we never get collected 
until the
-      // ServletContext is eligible for collection too.
-      ctx.setAttribute(this.getClass().getName(), this);
-      this.ctx = ctx;
-    }
-
-    public synchronized ServletContext getCtx() {
-      return ctx;
-    }
-
-    synchronized void makeReady() {
-      this.initializing.countDown();
-    }
-
-    // NOT synchronized :)
-    public void waitForReadyService() throws InterruptedException {
-      initializing.await();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) return true;
-      if (!(o instanceof ContextInitializationKey)) return false;
-      ContextInitializationKey that = (ContextInitializationKey) o;
-      return ctx.equals(that.ctx);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(ctx);
-    }
-  }
-
-  static class ServiceHolder {
-    private volatile CoreContainerProvider service;
-    private volatile ContextInitializationKey key;
-
-    private ServiceHolder(ContextInitializationKey key) {
-      if (key == null) {
-        throw new IllegalArgumentException(
-            "Key for accessing this service holder must be supplied");
-      }
-      this.key = key;
-    }
-
-    public void setService(CoreContainerProvider service) {
-      this.service = service;
-      key.makeReady();
-      key = null; // be sure not to hold a reference to the context via the key
-    }
-
-    public CoreContainerProvider getService() {
-      try {
-        if (key != null) {
-          try {
-            key.waitForReadyService();
-          } catch (NullPointerException e) {
-            // ignore, means we raced with set service and lost, but that's 
fine since null implies
-            // we are ready.
-          }
-        }
-      } catch (InterruptedException e) {
-        throw new SolrException(
-            ErrorCode.SERVER_ERROR, "Interrupted while obtaining reference to 
CoreService");
-      }
-      return service;
-    }
-  }
 }
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java 
b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index f1b1513ce51..bd408f886cf 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -57,7 +57,6 @@ import org.apache.solr.security.AuditEvent;
 import org.apache.solr.security.AuthenticationPlugin;
 import org.apache.solr.security.PKIAuthenticationPlugin;
 import org.apache.solr.security.PublicKeyHandler;
-import org.apache.solr.servlet.CoreContainerProvider.ServiceHolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -76,10 +75,7 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
   public static final String ATTR_TRACING_SPAN = Span.class.getName();
   public static final String ATTR_TRACING_TRACER = Tracer.class.getName();
 
-  // TODO: see if we can get rid of the holder here (Servlet spec actually 
guarantees
-  // ContextListeners run before filter init, but JettySolrRunner that we use 
for tests is
-  // complicated)
-  private ServiceHolder coreService;
+  private CoreContainerProvider containerProvider;
 
   protected final CountDownLatch init = new CountDownLatch(1);
 
@@ -98,7 +94,7 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
 
   public HttpClient getHttpClient() {
     try {
-      return coreService.getService().getHttpClient();
+      return containerProvider.getHttpClient();
     } catch (UnavailableException e) {
       throw new SolrException(
           ErrorCode.SERVER_ERROR, "Internal Http Client Unavailable, startup 
may have failed");
@@ -140,14 +136,9 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
   @Override
   public void init(FilterConfig config) throws ServletException {
     try {
-      coreService = 
CoreContainerProvider.serviceForContext(config.getServletContext());
+      containerProvider = 
CoreContainerProvider.serviceForContext(config.getServletContext());
       boolean isCoordinator =
-          NodeRoles.MODE_ON.equals(
-              coreService
-                  .getService()
-                  .getCoreContainer()
-                  .nodeRoles
-                  .getRoleMode(NodeRoles.Role.COORDINATOR));
+          
NodeRoles.MODE_ON.equals(getCores().nodeRoles.getRoleMode(NodeRoles.Role.COORDINATOR));
       solrCallFactory =
           isCoordinator ? new CoordinatorHttpSolrCall.Factory() : new 
HttpSolrCallFactory() {};
       if (log.isTraceEnabled()) {
@@ -155,9 +146,6 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
       }
 
       configExcludes(this, config.getInitParameter("excludePatterns"));
-    } catch (InterruptedException e) {
-      throw new ServletException("Interrupted while fetching core service");
-
     } catch (Throwable t) {
       // catch this so our filter still works
       log.error("Could not start Dispatch Filter.", t);
@@ -170,8 +158,9 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
     }
   }
 
+  /** The CoreContainer. It's ready for use, albeit could shut down whenever. 
Never null. */
   public CoreContainer getCores() throws UnavailableException {
-    return coreService.getService().getCoreContainer();
+    return containerProvider.getCoreContainer();
   }
 
   @Override
@@ -208,7 +197,7 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
     }
     Tracer t = getCores() == null ? GlobalTracer.get() : 
getCores().getTracer();
     request.setAttribute(ATTR_TRACING_TRACER, t);
-    RateLimitManager rateLimitManager = 
coreService.getService().getRateLimitManager();
+    RateLimitManager rateLimitManager = 
containerProvider.getRateLimitManager();
     try {
       ServletUtils.rateLimitRequest(
           rateLimitManager,
@@ -412,7 +401,7 @@ public class SolrDispatchFilter extends BaseSolrFilter 
implements PathExcluder {
 
   @VisibleForTesting
   void replaceRateLimitManager(RateLimitManager rateLimitManager) {
-    coreService.getService().setRateLimitManager(rateLimitManager);
+    containerProvider.setRateLimitManager(rateLimitManager);
   }
 
   /** internal API */
diff --git a/solr/core/src/test/org/apache/solr/cloud/ZkFailoverTest.java 
b/solr/core/src/test/org/apache/solr/cloud/ZkFailoverTest.java
index f4251ad9b6f..d9dceb79614 100644
--- a/solr/core/src/test/org/apache/solr/cloud/ZkFailoverTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/ZkFailoverTest.java
@@ -65,7 +65,7 @@ public class ZkFailoverTest extends SolrCloudTestCase {
     // This attempt will succeed since there will be enough time to connect
     System.setProperty("waitForZk", "20");
     restartSolrAndZk();
-    waitForLiveNodes(2);
+    waitForLiveNodes(cluster.getJettySolrRunners().size());
     waitForState("Timeout waiting for " + coll, coll, clusterShape(2, 2));
     QueryResponse rsp =
         new QueryRequest(new 
SolrQuery("*:*")).process(cluster.getSolrClient(), coll);
diff --git 
a/solr/core/src/test/org/apache/solr/handler/admin/HealthCheckHandlerTest.java 
b/solr/core/src/test/org/apache/solr/handler/admin/HealthCheckHandlerTest.java
index 74019a02b35..2751cfb9950 100644
--- 
a/solr/core/src/test/org/apache/solr/handler/admin/HealthCheckHandlerTest.java
+++ 
b/solr/core/src/test/org/apache/solr/handler/admin/HealthCheckHandlerTest.java
@@ -115,36 +115,6 @@ public class HealthCheckHandlerTest extends 
SolrCloudTestCase {
       newJetty.stop();
     }
 
-    // add a new node for the purpose of negative testing
-    // negative check that if core container is not available at the node
-    newJetty = cluster.startJettySolrRunner();
-    try (SolrClient solrClient = 
getHttpSolrClient(newJetty.getBaseUrl().toString())) {
-
-      // positive check that our (new) "healthy" node works with direct http 
client
-      assertEquals(CommonParams.OK, 
runHealthcheckWithClient(solrClient).getNodeStatus());
-
-      // shutdown the core container of new node
-      newJetty.getCoreContainer().shutdown();
-
-      // api shouldn't unreachable
-      SolrException thrown =
-          expectThrows(
-              SolrException.class,
-              () -> {
-                runHealthcheckWithClient(solrClient).getNodeStatus();
-                fail("API shouldn't be available, and fail at above request");
-              });
-      assertEquals("Exception code should be 404", 404, thrown.code());
-      assertTrue(
-          "Should have seen an exception containing the an error",
-          thrown
-              .getMessage()
-              .contains(
-                  "Error processing the request. CoreContainer is either not 
initialized or shutting down."));
-    } finally {
-      newJetty.stop();
-    }
-
     // (redundant) positive check that our (previously) exiting "healthy" node 
(still) works
     // after getting negative results from our broken node and failed core 
container
     try (SolrClient solrClient =
diff --git 
a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java 
b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
index 55b7a45e285..12aa7134f15 100644
--- a/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
+++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettySolrRunner.java
@@ -39,7 +39,6 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.Random;
 import java.util.Set;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
@@ -47,6 +46,7 @@ import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.FilterChain;
 import javax.servlet.FilterConfig;
+import javax.servlet.ServletContextEvent;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
@@ -59,10 +59,8 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.cloud.SocketProxy;
 import org.apache.solr.client.solrj.embedded.SSLConfig;
 import org.apache.solr.client.solrj.impl.HttpSolrClient;
-import org.apache.solr.common.util.ExecutorUtil;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
-import org.apache.solr.common.util.SolrNamedThreadFactory;
 import org.apache.solr.common.util.TimeSource;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.SolrCore;
@@ -93,7 +91,6 @@ import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletContextHandler;
 import org.eclipse.jetty.servlet.ServletHolder;
 import org.eclipse.jetty.servlet.Source;
-import org.eclipse.jetty.util.component.LifeCycle;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jetty.util.thread.ReservedThreadExecutor;
@@ -146,8 +143,6 @@ public class JettySolrRunner {
 
   private volatile boolean started = false;
 
-  private CoreContainerProvider coreContainerProvider;
-
   public static class DebugFilter implements Filter {
     private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
@@ -376,24 +371,12 @@ public class JettySolrRunner {
           new ServletContextHandler(server, config.context, 
ServletContextHandler.SESSIONS);
       root.setResourceBase(".");
 
-      server.addEventListener(
-          new LifeCycle.Listener() {
-
-            @Override
-            public void lifeCycleStopping(LifeCycle arg0) {}
-
-            @Override
-            public synchronized void lifeCycleStopped(LifeCycle arg0) {
-              if (coreContainerProvider != null) {
-                coreContainerProvider.close();
-              }
-            }
-
+      root.addEventListener(
+          // Install CCP first.  Subclass CCP to do some pre-initialization
+          new CoreContainerProvider() {
             @Override
-            public void lifeCycleStarting(LifeCycle arg0) {}
-
-            @Override
-            public synchronized void lifeCycleStarted(LifeCycle arg0) {
+            public void contextInitialized(ServletContextEvent event) {
+              // awkwardly, parts of Solr want to know the port but we don't 
know that until now
               jettyPort = getFirstConnectorPort();
               int port = jettyPort;
               if (proxyPort != -1) port = proxyPort;
@@ -404,41 +387,31 @@ public class JettySolrRunner {
                   .setAttribute(SolrDispatchFilter.PROPERTIES_ATTRIBUTE, 
nodeProperties);
               root.getServletContext()
                   .setAttribute(SolrDispatchFilter.SOLRHOME_ATTRIBUTE, 
solrHome);
+
               SSLConfigurationsFactory.current().init(); // normally happens 
in jetty-ssl.xml
-              coreContainerProvider = new CoreContainerProvider();
-              coreContainerProvider.init(root.getServletContext());
+
               log.info("Jetty properties: {}", nodeProperties);
 
-              debugFilter =
-                  root.addFilter(DebugFilter.class, "/*", 
EnumSet.of(DispatcherType.REQUEST));
-              extraFilters = new ArrayList<>();
-              for (Map.Entry<Class<? extends Filter>, String> entry :
-                  config.extraFilters.entrySet()) {
-                extraFilters.add(
-                    root.addFilter(
-                        entry.getKey(), entry.getValue(), 
EnumSet.of(DispatcherType.REQUEST)));
-              }
-
-              for (Map.Entry<ServletHolder, String> entry : 
config.extraServlets.entrySet()) {
-                root.addServlet(entry.getKey(), entry.getValue());
-              }
-              dispatchFilter = 
root.getServletHandler().newFilterHolder(Source.EMBEDDED);
-              dispatchFilter.setHeldClass(SolrDispatchFilter.class);
-              dispatchFilter.setInitParameter("excludePatterns", 
excludePatterns);
-              // Map dispatchFilter in same path as in web.xml
-              root.addFilter(dispatchFilter, "/*", 
EnumSet.of(DispatcherType.REQUEST));
-
-              synchronized (JettySolrRunner.this) {
-                waitOnSolr = true;
-                JettySolrRunner.this.notify();
-              }
-            }
-
-            @Override
-            public void lifeCycleFailure(LifeCycle arg0, Throwable arg1) {
-              System.clearProperty("hostPort");
+              super.contextInitialized(event);
             }
           });
+
+      debugFilter = root.addFilter(DebugFilter.class, "/*", 
EnumSet.of(DispatcherType.REQUEST));
+      extraFilters = new ArrayList<>();
+      for (Map.Entry<Class<? extends Filter>, String> entry : 
config.extraFilters.entrySet()) {
+        extraFilters.add(
+            root.addFilter(entry.getKey(), entry.getValue(), 
EnumSet.of(DispatcherType.REQUEST)));
+      }
+
+      for (Map.Entry<ServletHolder, String> entry : 
config.extraServlets.entrySet()) {
+        root.addServlet(entry.getKey(), entry.getValue());
+      }
+      dispatchFilter = 
root.getServletHandler().newFilterHolder(Source.EMBEDDED);
+      dispatchFilter.setHeldClass(SolrDispatchFilter.class);
+      dispatchFilter.setInitParameter("excludePatterns", excludePatterns);
+      // Map dispatchFilter in same path as in web.xml
+      root.addFilter(dispatchFilter, "/*", EnumSet.of(DispatcherType.REQUEST));
+
       // Default servlet as a fall-through
       root.addServlet(Servlet404.class, "/");
       chain = root;
@@ -455,6 +428,7 @@ public class JettySolrRunner {
       rwh.addRule(new RewritePatternRule("/api/*", "/solr/____v2"));
       chain = rwh;
     }
+
     GzipHandler gzipHandler = new GzipHandler();
     gzipHandler.setHandler(chain);
 
@@ -483,15 +457,14 @@ public class JettySolrRunner {
    * @return the {@link CoreContainer} for this node
    */
   public CoreContainer getCoreContainer() {
+    final var solrDispatchFilter = getSolrDispatchFilter();
+    if (solrDispatchFilter == null) {
+      return null;
+    }
     try {
-      if (getSolrDispatchFilter() == null || 
getSolrDispatchFilter().getCores() == null) {
-        return null;
-      }
-      return getSolrDispatchFilter().getCores();
+      return solrDispatchFilter.getCores();
     } catch (UnavailableException e) {
-      // Since this is only used in tests, this is just a straight-up failure
-      // If this is converted for other use something else might be better here
-      throw new RuntimeException(e);
+      return null;
     }
   }
 
@@ -558,15 +531,7 @@ public class JettySolrRunner {
           server.start();
         }
       }
-      synchronized (JettySolrRunner.this) {
-        int cnt = 0;
-        while (!waitOnSolr || !dispatchFilter.isRunning()) {
-          this.wait(100);
-          if (cnt++ == 15) {
-            throw new RuntimeException("Jetty/Solr unresponsive");
-          }
-        }
-      }
+      assert dispatchFilter.isRunning();
 
       if (config.waitForLoadingCoresToFinishMs != null
           && config.waitForLoadingCoresToFinishMs > 0L) {
@@ -663,21 +628,11 @@ public class JettySolrRunner {
     Map<String, String> prevContext = MDC.getCopyOfContextMap();
     MDC.clear();
     try {
-      Filter filter = dispatchFilter.getFilter();
       QueuedThreadPool qtp = (QueuedThreadPool) server.getThreadPool();
       ReservedThreadExecutor rte = qtp.getBean(ReservedThreadExecutor.class);
 
       server.stop();
 
-      if (server.getState().equals(Server.FAILED)) {
-        filter.destroy();
-        if (extraFilters != null) {
-          for (FilterHolder f : extraFilters) {
-            f.getFilter().destroy();
-          }
-        }
-      }
-
       // stop timeout is 0, so we will interrupt right away
       while (!qtp.isStopped()) {
         qtp.stop();
@@ -701,12 +656,6 @@ public class JettySolrRunner {
         timeout.waitFor("Timeout waiting for reserved executor to stop.", 
rte::isStopped);
       }
 
-      // we want to shutdown outside of jetty cutting us off
-      SolrDispatchFilter sdf = getSolrDispatchFilter();
-      if (sdf != null) {
-        ExecutorUtil.shutdownAndAwaitTermination(getJettyShutDownThreadPool());
-      }
-
       do {
         try {
           server.join();
@@ -728,10 +677,6 @@ public class JettySolrRunner {
     }
   }
 
-  private ExecutorService getJettyShutDownThreadPool() {
-    return ExecutorUtil.newMDCAwareCachedThreadPool(new 
SolrNamedThreadFactory("jettyShutDown"));
-  }
-
   public void outputMetrics(File outputDirectory, String fileName) throws 
IOException {
     if (getCoreContainer() != null) {
 
@@ -920,11 +865,7 @@ public class JettySolrRunner {
       } catch (UnavailableException e) {
         throw new IllegalStateException("The CoreContainer is unavailable!");
       }
-      if (cores != null) {
-        cores.waitForLoadingCoresToFinish(timeoutMs);
-      } else {
-        throw new IllegalStateException("The CoreContainer is not set!");
-      }
+      cores.waitForLoadingCoresToFinish(timeoutMs);
     } else {
       throw new IllegalStateException("The dispatchFilter is not set!");
     }

Reply via email to