diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 1928df3c57a..73ad4d78a23 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -410,6 +410,8 @@ New Features * SOLR-6233: Provide basic command line tools for checking Solr status and health. (Timothy Potter) +* SOLR-6585: RequestHandlers can optionaly handle sub paths as well (Noble Paul) + Bug Fixes ---------------------- 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 21ad2cd5ec2..d4b65ec6570 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -22,6 +22,8 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.io.File; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -35,11 +37,13 @@ import org.apache.solr.cloud.ZkController; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.util.ExecutorUtil; +import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.admin.CollectionsHandler; import org.apache.solr.handler.admin.CoreAdminHandler; import org.apache.solr.handler.admin.InfoHandler; import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.logging.LogWatcher; +import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.update.UpdateShardHandler; import org.apache.solr.util.DefaultSolrThreadFactory; import org.apache.solr.util.FileUtils; @@ -99,6 +103,17 @@ public class CoreContainer { private String hostName; + private Map containerHandlers = new HashMap<>(); + + public SolrRequestHandler getRequestHandler(String path) { + return RequestHandlerBase.getRequestHandler(path, containerHandlers); + } + + public Map getRequestHandlers(){ + return this.containerHandlers; + + } + // private ClientConnectionManager clientConnectionManager = new PoolingClientConnectionManager(); { @@ -222,8 +237,11 @@ public class CoreContainer { zkSys.initZooKeeper(this, solrHome, cfg); collectionsHandler = createHandler(cfg.getCollectionsHandlerClass(), CollectionsHandler.class); + containerHandlers.put("/admin/collections" , collectionsHandler); infoHandler = createHandler(cfg.getInfoHandlerClass(), InfoHandler.class); + containerHandlers.put("/admin/info" , infoHandler); coreAdminHandler = createHandler(cfg.getCoreAdminHandlerClass(), CoreAdminHandler.class); + containerHandlers.put(cfg.getAdminPath() , coreAdminHandler); coreConfigService = cfg.createCoreConfigService(loader, zkSys.getZkController()); @@ -919,5 +937,5 @@ class CloserThread extends Thread { } } } - + } 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 4e8b9d5404b..e53dca3be6a 100644 --- a/solr/core/src/java/org/apache/solr/core/RequestHandlers.java +++ b/solr/core/src/java/org/apache/solr/core/RequestHandlers.java @@ -49,6 +49,7 @@ public final class RequestHandlers { // the map implementation should be thread safe private final Map handlers = new ConcurrentHashMap<>() ; + private final Map immutableHandlers = Collections.unmodifiableMap(handlers) ; /** * Trim the trailing '/' if its there, and convert null to empty string. @@ -59,7 +60,7 @@ public final class RequestHandlers { * to map to the same handler * */ - private static String normalize( String p ) + public static String normalize( String p ) { if(p == null) return ""; if( p.endsWith( "/" ) && p.length() > 1 ) @@ -114,7 +115,7 @@ public final class RequestHandlers { * Returns an unmodifiable Map containing the registered handlers */ public Map getRequestHandlers() { - return Collections.unmodifiableMap( handlers ); + return immutableHandlers; } 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 7445b0275af..395741bd6b9 100644 --- a/solr/core/src/java/org/apache/solr/core/SolrCore.java +++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java @@ -38,6 +38,7 @@ 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.core.DirectoryFactory.DirContext; +import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.handler.SnapPuller; import org.apache.solr.handler.UpdateRequestHandler; import org.apache.solr.handler.admin.ShowFileRequestHandler; @@ -1217,7 +1218,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable { * This function is thread safe. */ public SolrRequestHandler getRequestHandler(String handlerName) { - return reqHandlers.get(handlerName); + return RequestHandlerBase.getRequestHandler(RequestHandlers.normalize(handlerName), reqHandlers.getRequestHandlers()); } /** diff --git a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java index 773af54198b..7d68785c8ca 100644 --- a/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/DumpRequestHandler.java @@ -20,13 +20,18 @@ package org.apache.solr.handler; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; +import java.util.List; import org.apache.commons.io.IOUtils; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.InitParams; +import org.apache.solr.core.PluginInfo; import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.util.plugin.PluginInfoInitialized; public class DumpRequestHandler extends RequestHandlerBase { @@ -81,4 +86,20 @@ public class DumpRequestHandler extends RequestHandlerBase public String getDescription() { return "Dump handler (debug)"; } + + @Override + public SolrRequestHandler getSubHandler(String path) { + if(subpaths !=null && subpaths.contains(path)) return this; + return null; + } + private List subpaths; + + @Override + public void init(NamedList args) { + super.init(args); + if(args !=null) { + NamedList nl = (NamedList) args.get(PluginInfo.DEFAULTS); + if(nl!=null) subpaths = nl.getAll("subpath"); + } + } } diff --git a/solr/core/src/java/org/apache/solr/handler/NestedRequestHandler.java b/solr/core/src/java/org/apache/solr/handler/NestedRequestHandler.java new file mode 100644 index 00000000000..e4faba2c4c3 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/NestedRequestHandler.java @@ -0,0 +1,28 @@ +package org.apache.solr.handler; + +/* + * 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. + */ + +import org.apache.solr.request.SolrRequestHandler; + + +/**An interface for RequestHandlers need to handle all paths under its registered path + */ +public interface NestedRequestHandler { + + public SolrRequestHandler getSubHandler(String path); +} diff --git a/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java b/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java index f2aaee25035..9d7a0dae330 100644 --- a/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java +++ b/solr/core/src/java/org/apache/solr/handler/RequestHandlerBase.java @@ -33,12 +33,13 @@ import org.apache.solr.util.stats.Timer; import org.apache.solr.util.stats.TimerContext; import java.net.URL; +import java.util.Map; import java.util.concurrent.atomic.AtomicLong; /** * */ -public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoMBean { +public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfoMBean, NestedRequestHandler { protected NamedList initArgs = null; protected SolrParams defaults; @@ -194,6 +195,41 @@ public abstract class RequestHandlerBase implements SolrRequestHandler, SolrInfo return null; // this can be overridden, but not required } + + @Override + public SolrRequestHandler getSubHandler(String path) { + return null; + } + + + /** + * Get the request handler registered to a given name. + * + * This function is thread safe. + */ + public static SolrRequestHandler getRequestHandler(String handlerName, Map reqHandlers) { + if(handlerName == null) return null; + SolrRequestHandler handler = reqHandlers.get(handlerName); + int idx = 0; + if(handler == null) { + for (; ; ) { + idx = handlerName.indexOf('/', idx+1); + if (idx > 0) { + String firstPart = handlerName.substring(0, idx); + handler = reqHandlers.get(firstPart); + if (handler == null) continue; + if (handler instanceof NestedRequestHandler) { + return ((NestedRequestHandler) handler).getSubHandler(handlerName.substring(idx)); + } + } else { + break; + } + } + } + return handler; + } + + @Override public NamedList getStatistics() { NamedList lst = new SimpleOrderedMap<>(); diff --git a/solr/core/src/java/org/apache/solr/request/SolrRequestHandler.java b/solr/core/src/java/org/apache/solr/request/SolrRequestHandler.java index 9479f0e0703..1c61092860c 100644 --- a/solr/core/src/java/org/apache/solr/request/SolrRequestHandler.java +++ b/solr/core/src/java/org/apache/solr/request/SolrRequestHandler.java @@ -60,5 +60,7 @@ public interface SolrRequestHandler extends SolrInfoMBean { * all interface obligations. */ public void handleRequest(SolrQueryRequest req, SolrQueryResponse rsp); + + public static final String TYPE = "requestHandler"; } 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 ff4959dd4a1..60890fb8b14 100644 --- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java +++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java @@ -256,20 +256,12 @@ public class SolrDispatchFilter extends BaseSolrFilter { boolean usingAliases = false; List collectionsList = null; // Check for the core admin collections url - if( path.equals( "/admin/collections" ) ) { - handler = cores.getCollectionsHandler(); + handler = cores.getRequestHandler(path); + if( handler!= null ) { solrReq = SolrRequestParsers.DEFAULT.parse(null,path, req); handleAdminRequest(req, response, handler, solrReq); return; - } - // Check for the core admin info url - if( path.startsWith( "/admin/info" ) ) { - handler = cores.getInfoHandler(); - solrReq = SolrRequestParsers.DEFAULT.parse(null,path, req); - handleAdminRequest(req, response, handler, solrReq); - return; - } - else { + } else { //otherwise, we should find a core from the path idx = path.indexOf( "/", 1 ); if( idx > 1 ) { diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java index f3fa91d78c8..203bbba3441 100644 --- a/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java +++ b/solr/core/src/java/org/apache/solr/servlet/SolrRequestParsers.java @@ -52,6 +52,7 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.util.FastInputStream; +import org.apache.solr.core.CoreContainer; import org.apache.solr.core.SolrConfig; import org.apache.solr.core.SolrCore; import org.apache.solr.request.SolrQueryRequest; @@ -134,7 +135,7 @@ public class SolrRequestParsers parsers.put( STANDARD, standard ); parsers.put( "", standard ); } - + public SolrQueryRequest parse( SolrCore core, String path, HttpServletRequest req ) throws Exception { SolrRequestParser parser = standard; @@ -149,7 +150,8 @@ public class SolrRequestParsers // Handlers and login will want to know the path. If it contains a ':' // the handler could use it for RESTful URLs sreq.getContext().put( "path", path ); - + sreq.getContext().put("httpMethod", req.getMethod()); + if(addHttpRequestToContext) { sreq.getContext().put("httpRequest", req); } diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-paramset.xml b/solr/core/src/test-files/solr/collection1/conf/solrconfig-paramset.xml index 3dde8b2cef9..60c896e4616 100644 --- a/solr/core/src/test-files/solr/collection1/conf/solrconfig-paramset.xml +++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-paramset.xml @@ -60,5 +60,13 @@ + + + /some/path + /some/other/path + + + + diff --git a/solr/core/src/test/org/apache/solr/core/TestInitParams.java b/solr/core/src/test/org/apache/solr/core/TestInitParams.java index 65279db60bd..377dbd814e1 100644 --- a/solr/core/src/test/org/apache/solr/core/TestInitParams.java +++ b/solr/core/src/test/org/apache/solr/core/TestInitParams.java @@ -90,6 +90,13 @@ public class TestInitParams extends SolrTestCaseJ4 { assertEquals(Arrays.asList("C1","C") ,def.getAll("c")); } + public void testNestedRequestHandler() { + assertNotNull(h.getCore().getRequestHandler("/greedypath")); + assertNotNull(h.getCore().getRequestHandler("/greedypath/some/path")); + assertNotNull( h.getCore().getRequestHandler("/greedypath/some/other/path")); + assertNull(h.getCore().getRequestHandler("/greedypath/unknownpath")); + } +