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 2019/08/29 04:07:04 UTC

[lucene-solr] 01/02: SOLR-13723: JettySolrRunner should support /api/* (the v2 end point)

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

commit 7d026f803d03467e9152de07381bdc246b26c8ce
Author: Noble Paul <no...@users.noreply.github.com>
AuthorDate: Thu Aug 29 14:01:52 2019 +1000

    SOLR-13723: JettySolrRunner should support /api/* (the v2 end point)
---
 .../solr/client/solrj/embedded/JettyConfig.java    |  14 ++-
 .../client/solrj/embedded/JettySolrRunner.java     | 109 +++++++++++----------
 .../solr/client/solrj/impl/HttpSolrClient.java     |   2 +-
 .../solr/client/solrj/request/V2Request.java       |  17 ++++
 .../solr/client/solrj/request/TestV2Request.java   |  11 +++
 .../apache/solr/cloud/MiniSolrCloudCluster.java    |   2 +-
 .../org/apache/solr/cloud/SolrCloudTestCase.java   |  10 +-
 7 files changed, 106 insertions(+), 59 deletions(-)

diff --git a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettyConfig.java b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettyConfig.java
index bff63e1..e4a0547 100644
--- a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettyConfig.java
+++ b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettyConfig.java
@@ -32,6 +32,9 @@ public class JettyConfig {
 
   public final String context;
 
+  public final boolean enableV2;
+
+
   public final boolean stopAtShutdown;
   
   public final Long waitForLoadingCoresToFinishMs;
@@ -46,7 +49,7 @@ public class JettyConfig {
 
   private JettyConfig(boolean onlyHttp1, int port, int portRetryTime , String context, boolean stopAtShutdown,
                       Long waitForLoadingCoresToFinishMs, Map<ServletHolder, String> extraServlets,
-                      Map<Class<? extends Filter>, String> extraFilters, SSLConfig sslConfig) {
+                      Map<Class<? extends Filter>, String> extraFilters, SSLConfig sslConfig, boolean enableV2) {
     this.onlyHttp1 = onlyHttp1;
     this.port = port;
     this.context = context;
@@ -56,6 +59,7 @@ public class JettyConfig {
     this.extraFilters = extraFilters;
     this.sslConfig = sslConfig;
     this.portRetryTime = portRetryTime;
+    this.enableV2 = enableV2;
   }
 
   public static Builder builder() {
@@ -78,6 +82,7 @@ public class JettyConfig {
     boolean onlyHttp1 = false;
     int port = 0;
     String context = "/solr";
+    boolean enableV2 = true;
     boolean stopAtShutdown = true;
     Long waitForLoadingCoresToFinishMs = 300000L;
     Map<ServletHolder, String> extraServlets = new TreeMap<>();
@@ -89,6 +94,10 @@ public class JettyConfig {
       this.onlyHttp1 = useOnlyHttp1;
       return this;
     }
+    public Builder enableV2(boolean flag){
+      this.enableV2 = flag;
+      return this;
+    }
 
     public Builder setPort(int port) {
       this.port = port;
@@ -144,7 +153,8 @@ public class JettyConfig {
 
 
     public JettyConfig build() {
-      return new JettyConfig(onlyHttp1, port, portRetryTime, context, stopAtShutdown, waitForLoadingCoresToFinishMs, extraServlets, extraFilters, sslConfig);
+      return new JettyConfig(onlyHttp1, port, portRetryTime, context, stopAtShutdown,
+          waitForLoadingCoresToFinishMs, extraServlets, extraFilters, sslConfig, enableV2);
     }
 
   }
diff --git a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
index ba94104..c98bbb4 100644
--- a/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
+++ b/solr/core/src/java/org/apache/solr/client/solrj/embedded/JettySolrRunner.java
@@ -59,6 +59,8 @@ import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
 import org.eclipse.jetty.http2.HTTP2Cipher;
 import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
 import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.rewrite.handler.RewriteHandler;
+import org.eclipse.jetty.rewrite.handler.RewritePatternRule;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.HttpConfiguration;
 import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -83,7 +85,7 @@ import org.slf4j.MDC;
 
 /**
  * Run solr using jetty
- * 
+ *
  * @since solr 1.3
  */
 public class JettySolrRunner {
@@ -93,7 +95,7 @@ public class JettySolrRunner {
   private static final int THREAD_POOL_MAX_THREADS = 10000;
   // NOTE: needs to be larger than SolrHttpClient.threadPoolSweeperMaxIdleTime
   private static final int THREAD_POOL_MAX_IDLE_TIME_MS = 260000;
-  
+
   Server server;
 
   volatile FilterHolder dispatchFilter;
@@ -128,14 +130,14 @@ public class JettySolrRunner {
     private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
     private AtomicLong nRequests = new AtomicLong();
-    
+
     List<Delay> delays = new ArrayList<>();
 
     public long getTotalRequests() {
       return nRequests.get();
 
     }
-    
+
     /**
      * Introduce a delay of specified milliseconds for the specified request.
      *
@@ -146,7 +148,7 @@ public class JettySolrRunner {
     public void addDelay(String reason, int count, int delay) {
       delays.add(new Delay(reason, count, delay));
     }
-    
+
     /**
      * Remove any delay introduced before.
      */
@@ -167,14 +169,14 @@ public class JettySolrRunner {
 
     @Override
     public void destroy() { }
-    
+
     private void executeDelay() {
       int delayMs = 0;
       for (Delay delay: delays) {
         this.log.info("Delaying "+delay.delayValue+", for reason: "+delay.reason);
         if (delay.counter.decrementAndGet() == 0) {
           delayMs += delay.delayValue;
-        }        
+        }
       }
 
       if (delayMs > 0) {
@@ -215,7 +217,7 @@ public class JettySolrRunner {
   public JettySolrRunner(String solrHome, JettyConfig config) {
     this(solrHome, new Properties(), config);
   }
-  
+
   /**
    * Construct a JettySolrRunner
    *
@@ -244,7 +246,7 @@ public class JettySolrRunner {
     this.solrHome = solrHome;
     this.config = config;
     this.nodeProperties = nodeProperties;
-    
+
     if (enableProxy) {
       try {
         proxy = new SocketProxy(0, config.sslConfig != null && config.sslConfig.isSSLMode());
@@ -256,7 +258,7 @@ public class JettySolrRunner {
 
     this.init(this.config.port);
   }
-  
+
   private void init(int port) {
 
     QueuedThreadPool qtp = new QueuedThreadPool();
@@ -275,7 +277,7 @@ public class JettySolrRunner {
       //
       // This means we will use the same truststore, keystore (and keys) for
       // the server as well as any client actions taken by this JVM in
-      // talking to that server, but for the purposes of testing that should 
+      // talking to that server, but for the purposes of testing that should
       // be good enough
       final SslContextFactory sslcontext = SSLConfig.createContextFactory(config.sslConfig);
 
@@ -382,7 +384,7 @@ public class JettySolrRunner {
         dispatchFilter.setHeldClass(SolrDispatchFilter.class);
         dispatchFilter.setInitParameter("excludePatterns", excludePatterns);
         root.addFilter(dispatchFilter, "*", EnumSet.of(DispatcherType.REQUEST));
-        
+
         synchronized (JettySolrRunner.this) {
           waitOnSolr = true;
           JettySolrRunner.this.notify();
@@ -400,7 +402,16 @@ public class JettySolrRunner {
     }
 
     chain = injectJettyHandlers(chain);
-    
+
+    if(config.enableV2) {
+      RewriteHandler rwh = new RewriteHandler();
+      rwh.setHandler(chain);
+      rwh.setRewriteRequestURI(true);
+      rwh.setRewritePathInfo(false);
+      rwh.setOriginalPathAttribute("requestedPath");
+      rwh.addRule(new RewritePatternRule("/api/*", "/solr/____v2"));
+      chain = rwh;
+    }
     GzipHandler gzipHandler = new GzipHandler();
     gzipHandler.setHandler(chain);
 
@@ -413,7 +424,7 @@ public class JettySolrRunner {
     server.setHandler(gzipHandler);
   }
 
-  /** descendants may inject own handler chaining it to the given root 
+  /** descendants may inject own handler chaining it to the given root
    * and then returning that own one*/
   protected HandlerWrapper injectJettyHandlers(HandlerWrapper chain) {
     return chain;
@@ -445,7 +456,7 @@ public class JettySolrRunner {
   public boolean isRunning() {
     return server.isRunning() && dispatchFilter != null && dispatchFilter.isRunning();
   }
-  
+
   public boolean isStopped() {
     return (server.isStopped() && dispatchFilter == null) || (server.isStopped() && dispatchFilter.isStopped()
         && ((QueuedThreadPool) server.getThreadPool()).isStopped());
@@ -478,12 +489,12 @@ public class JettySolrRunner {
     // Do not let Jetty/Solr pollute the MDC for this thread
     Map<String, String> prevContext = MDC.getCopyOfContextMap();
     MDC.clear();
-    
+
     log.info("Start Jetty (original configured port={})", this.config.port);
-    
+
     try {
       int port = reusePort && jettyPort != -1 ? jettyPort : this.config.port;
-      
+
       // if started before, make a new server
       if (startedBefore) {
         waitOnSolr = false;
@@ -508,21 +519,21 @@ public class JettySolrRunner {
           }
         }
       }
-      
+
       if (config.waitForLoadingCoresToFinishMs != null && config.waitForLoadingCoresToFinishMs > 0L) {
         waitForLoadingCoresToFinish(config.waitForLoadingCoresToFinishMs);
       }
-      
+
       setProtocolAndHost();
-      
+
       if (enableProxy) {
         if (started) {
           proxy.reopen();
         } else {
           proxy.open(getBaseUrl().toURI());
         }
-      }    
-      
+      }
+
     } finally {
       started  = true;
       if (prevContext != null)  {
@@ -548,7 +559,7 @@ public class JettySolrRunner {
     this.protocol = protocol;
     this.host = c.getHost();
   }
-  
+
   private void retryOnPortBindFailure(int portRetryTime, int port) throws Exception, InterruptedException {
     TimeOut timeout = new TimeOut(portRetryTime, TimeUnit.SECONDS, TimeSource.NANO_TIME);
     int tryCnt = 1;
@@ -567,7 +578,7 @@ public class JettySolrRunner {
             continue;
           }
         }
-        
+
         throw e;
       }
     }
@@ -628,7 +639,7 @@ public class JettySolrRunner {
 
       QueuedThreadPool qtp = (QueuedThreadPool) server.getThreadPool();
       ReservedThreadExecutor rte = qtp.getBean(ReservedThreadExecutor.class);
-      
+
       server.stop();
 
       if (server.getState().equals(Server.FAILED)) {
@@ -647,18 +658,18 @@ public class JettySolrRunner {
           Thread.sleep(50);
         }
       }
-      
+
       // we tried to kill everything, now we wait for executor to stop
       qtp.setStopTimeout(Integer.MAX_VALUE);
       qtp.stop();
       qtp.join();
-      
+
       if (rte != null) {
         // we try and wait for the reserved thread executor, but it doesn't always seem to work
         // so we actually set 0 reserved threads at creation
-        
+
         rte.stop();
-        
+
         TimeOut timeout = new TimeOut(30, TimeUnit.SECONDS, TimeSource.NANO_TIME);
         timeout.waitFor("Timeout waiting for reserved executor to stop.", ()
             -> rte.isStopped());
@@ -675,12 +686,12 @@ public class JettySolrRunner {
           // ignore
         }
       } while (!server.isStopped());
-      
+
     } finally {
       if (enableProxy) {
         proxy.close();
       }
-      
+
       if (prevContext != null) {
         MDC.setContextMap(prevContext);
       } else {
@@ -691,7 +702,7 @@ public class JettySolrRunner {
 
   /**
    * Returns the Local Port of the jetty Server.
-   * 
+   *
    * @exception RuntimeException if there is no Connector
    */
   private int getFirstConnectorPort() {
@@ -701,22 +712,22 @@ public class JettySolrRunner {
     }
     return ((ServerConnector) conns[0]).getLocalPort();
   }
-  
-  
+
+
   /**
    * Returns the Local Port of the jetty Server.
-   * 
+   *
    * @exception RuntimeException if there is no Connector
    */
   public int getLocalPort() {
     return getLocalPort(false);
   }
-  
+
   /**
    * Returns the Local Port of the jetty Server.
-   * 
+   *
    * @param internalPort pass true to get the true jetty port rather than the proxy port if configured
-   * 
+   *
    * @exception RuntimeException if there is no Connector
    */
   public int getLocalPort(boolean internalPort) {
@@ -728,7 +739,7 @@ public class JettySolrRunner {
     }
     return (proxyPort != -1) ? proxyPort : jettyPort;
   }
-  
+
   /**
    * Sets the port of a local socket proxy that sits infront of this server; if set
    * then all client traffic will flow through the proxy, giving us the ability to
@@ -737,7 +748,7 @@ public class JettySolrRunner {
   public void setProxyPort(int proxyPort) {
     this.proxyPort = proxyPort;
   }
-  
+
   /**
    * Returns a base URL consisting of the protocol, host, and port for a
    * Connector in use by the Jetty Server contained in this runner.
@@ -764,7 +775,7 @@ public class JettySolrRunner {
   public SolrClient newClient() {
     return new HttpSolrClient.Builder(getBaseUrl().toString()).build();
   }
-  
+
   public SolrClient newClient(int connectionTimeoutMillis, int socketTimeoutMillis) {
     return new HttpSolrClient.Builder(getBaseUrl().toString())
         .withConnectionTimeout(connectionTimeoutMillis)
@@ -793,13 +804,9 @@ public class JettySolrRunner {
   /**
    * A main class that starts jetty+solr This is useful for debugging
    */
-  public static void main(String[] args) {
-    try {
-      JettySolrRunner jetty = new JettySolrRunner(".", "/solr", 8983);
-      jetty.start();
-    } catch (Exception ex) {
-      ex.printStackTrace();
-    }
+  public static void main(String[] args) throws Exception {
+    JettySolrRunner jetty = new JettySolrRunner(".", "/solr", 8983);
+    jetty.start();
   }
 
   /**
@@ -829,12 +836,12 @@ public class JettySolrRunner {
       throw new IllegalStateException("The dispatchFilter is not set!");
     }
   }
-  
+
   static class Delay {
     final AtomicInteger counter;
     final int delayValue;
     final String reason;
-    
+
     public Delay(String reason, int counter, int delay) {
       this.reason = reason;
       this.counter = new AtomicInteger(counter);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
index 75db5a1..7498075 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/HttpSolrClient.java
@@ -364,7 +364,7 @@ public class HttpSolrClient extends BaseHttpSolrClient {
       basePath += "/" + collection;
 
     if (request instanceof V2Request) {
-      if (System.getProperty("solr.v2RealPath") == null) {
+      if (System.getProperty("solr.v2RealPath") == null || ((V2Request) request).isForceV2()) {
         basePath = baseUrl.replace("/solr", "/api");
       } else {
         basePath = baseUrl + "/____v2";
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/V2Request.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/V2Request.java
index 17e3c9b..4236177 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/V2Request.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/V2Request.java
@@ -42,6 +42,7 @@ public class V2Request extends SolrRequest<V2Response> implements MapWriter {
   private SolrParams solrParams;
   public final boolean useBinary;
   private String collection;
+  private boolean forceV2 = false;
   private boolean isPerCollectionRequest = false;
 
   private V2Request(METHOD m, String resource, boolean useBinary) {
@@ -55,6 +56,10 @@ public class V2Request extends SolrRequest<V2Response> implements MapWriter {
 
   }
 
+  public boolean isForceV2(){
+    return forceV2;
+  }
+
   @Override
   public SolrParams getParams() {
     return solrParams;
@@ -113,6 +118,8 @@ public class V2Request extends SolrRequest<V2Response> implements MapWriter {
     private SolrParams params;
     private boolean useBinary = false;
 
+    private boolean forceV2EndPoint = false;
+
     /**
      * Create a Builder object based on the provided resource.
      * The default method is GET.
@@ -130,7 +137,16 @@ public class V2Request extends SolrRequest<V2Response> implements MapWriter {
     }
 
     /**
+     * Only for testing. It's always true otherwise
+     */
+    public Builder forceV2(boolean flag) {
+      forceV2EndPoint = flag;
+      return this;
+    }
+
+    /**
      * Set payload for request.
+     *
      * @param payload as UTF-8 String
      * @return builder object
      */
@@ -161,6 +177,7 @@ public class V2Request extends SolrRequest<V2Response> implements MapWriter {
       V2Request v2Request = new V2Request(method, resource, useBinary);
       v2Request.solrParams = params;
       v2Request.payload = payload;
+      v2Request.forceV2 = forceV2EndPoint;
       return v2Request;
     }
   }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestV2Request.java b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestV2Request.java
index 300b6c5..fa34267 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestV2Request.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestV2Request.java
@@ -42,9 +42,20 @@ public class TestV2Request extends SolrCloudTestCase {
   @Before
   public void setupCluster() throws Exception {
     configureCluster(4)
+        .withJettyConfig(jettyCfg -> jettyCfg.enableV2(true))
         .addConfig("config", getFile("solrj/solr/collection1/conf").toPath())
         .configure();
   }
+
+  public void testApiPathAvailability() throws Exception {
+    V2Response rsp = new V2Request.Builder("/cluster/nodes")
+        .forceV2(true)
+        .withMethod(SolrRequest.METHOD.GET).build()
+        .process(cluster.getSolrClient());
+    List l = (List) rsp._get("nodes",null);
+    assertNotNull(l);
+    assertFalse(l.isEmpty());
+  }
   
   @After
   public void afterTest() throws Exception {
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
index eca5235..7c6d120 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/MiniSolrCloudCluster.java
@@ -786,7 +786,7 @@ public class MiniSolrCloudCluster {
       if (activeReplicas == expectedReplicas) {
         return true;
       }
-      
+
       return false;
     };
   }
diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java
index d8d2472..23283cc 100644
--- a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java
@@ -32,6 +32,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 import org.apache.solr.SolrTestCaseJ4;
@@ -106,7 +107,7 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
     private final int nodeCount;
     private final Path baseDir;
     private String solrxml = MiniSolrCloudCluster.DEFAULT_CLOUD_SOLR_XML;
-    private JettyConfig jettyConfig = buildJettyConfig("/solr");
+    private JettyConfig.Builder jettyConfigBuilder = JettyConfig.builder().setContext("/solr").withSSLConfig(sslConfig.buildServerSSLConfig());
     private Optional<String> securityJson = Optional.empty();
 
     private List<Config> configs = new ArrayList<>();
@@ -126,10 +127,10 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
     }
 
     /**
-     * Use a {@link JettyConfig} to configure the cluster's jetty servers
+     * Use a JettyConfig.Builder to configure the cluster's jetty servers
      */
-    public Builder withJettyConfig(JettyConfig jettyConfig) {
-      this.jettyConfig = jettyConfig;
+    public Builder withJettyConfig(Consumer<JettyConfig.Builder> fun) {
+      fun.accept(jettyConfigBuilder);
       return this;
     }
 
@@ -226,6 +227,7 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
      * @throws Exception if an error occurs on startup
      */
     public MiniSolrCloudCluster build() throws Exception {
+      JettyConfig jettyConfig = jettyConfigBuilder.build();
       MiniSolrCloudCluster cluster = new MiniSolrCloudCluster(nodeCount, baseDir, solrxml, jettyConfig,
           null, securityJson, trackJettyMetrics);
       CloudSolrClient client = cluster.getSolrClient();