From 6455dec1e03413105dd4d8ee77445680762cc8c8 Mon Sep 17 00:00:00 2001 From: Mark Robert Miller Date: Wed, 10 Jul 2013 18:28:00 +0000 Subject: [PATCH] SOLR-4943: Add a new system wide info admin handler that exposes the system info that could previously only be retrieved using a SolrCore. git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1501898 13f79535-47bb-0310-9956-ffa450edef68 --- solr/CHANGES.txt | 3 + .../org/apache/solr/core/CoreContainer.java | 8 ++ .../solr/handler/admin/InfoHandler.java | 112 ++++++++++++++++++ .../solr/handler/admin/LoggingHandler.java | 15 ++- .../solr/handler/admin/SystemInfoHandler.java | 28 ++++- .../solr/servlet/SolrDispatchFilter.java | 7 ++ .../solr/handler/admin/InfoHandlerTest.java | 80 +++++++++++++ .../solr/client/solrj/SolrExampleTests.java | 12 ++ 8 files changed, 261 insertions(+), 4 deletions(-) create mode 100644 solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java create mode 100644 solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 82b377cc6fc..53de9ba8ba3 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -179,6 +179,9 @@ New Features * SOLR-4991: Register QParserPlugins as SolrInfoMBeans (ehatcher) +* SOLR-4943: Add a new system wide info admin handler that exposes the system info + that could previously only be retrieved using a SolrCore. (Mark Miller) + 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 59bddeb59d3..7cb6497da46 100644 --- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java +++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java @@ -25,6 +25,7 @@ import org.apache.solr.common.cloud.ZooKeeperException; import org.apache.solr.common.util.ExecutorUtil; 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.HttpShardHandlerFactory; import org.apache.solr.handler.component.ShardHandlerFactory; import org.apache.solr.logging.LogWatcher; @@ -41,6 +42,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import javax.xml.xpath.XPathExpressionException; + import java.io.File; import java.text.SimpleDateFormat; import java.util.Collection; @@ -118,6 +120,7 @@ public class CoreContainer protected final ConfigSolr cfg; protected final SolrResourceLoader loader; protected final String solrHome; + private InfoHandler infoHandler; { log.info("New CoreContainer " + System.identityHashCode(this)); @@ -269,6 +272,7 @@ public class CoreContainer } collectionsHandler = new CollectionsHandler(this); + infoHandler = new InfoHandler(this); containerProperties = cfg.getSolrProperties("solr"); // setup executor to load cores in parallel @@ -959,6 +963,10 @@ public class CoreContainer return collectionsHandler; } + public InfoHandler getInfoHandler() { + return infoHandler; + } + /** * the default core name, or null if there is no default core name */ diff --git a/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java new file mode 100644 index 00000000000..6ce5a5f6c1e --- /dev/null +++ b/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java @@ -0,0 +1,112 @@ +package org.apache.solr.handler.admin; + +/* + * 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.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.handler.RequestHandlerBase; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class InfoHandler extends RequestHandlerBase { + protected static Logger log = LoggerFactory.getLogger(InfoHandler.class); + protected final CoreContainer coreContainer; + + private ThreadDumpHandler threadDumpHandler = new ThreadDumpHandler(); + private PropertiesRequestHandler propertiesHandler = new PropertiesRequestHandler(); + private LoggingHandler loggingHandler; + private SystemInfoHandler systemInfoHandler; + + /** + * Overloaded ctor to inject CoreContainer into the handler. + * + * @param coreContainer Core Container of the solr webapp installed. + */ + public InfoHandler(final CoreContainer coreContainer) { + this.coreContainer = coreContainer; + systemInfoHandler = new SystemInfoHandler(coreContainer); + loggingHandler = new LoggingHandler(coreContainer); + + } + + + @Override + final public void init(NamedList args) { + + } + + /** + * The instance of CoreContainer this handler handles. This should be the CoreContainer instance that created this + * handler. + * + * @return a CoreContainer instance + */ + public CoreContainer getCoreContainer() { + return this.coreContainer; + } + + @Override + public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { + // Make sure the cores is enabled + CoreContainer cores = getCoreContainer(); + if (cores == null) { + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, + "Core container instance missing"); + } + + String path = (String) req.getContext().get("path"); + int i = path.lastIndexOf('/'); + String name = path.substring(i + 1, path.length()); + + if (name.equalsIgnoreCase("properties")) { + propertiesHandler.handleRequest(req, rsp); + } else if (name.equalsIgnoreCase("threads")) { + threadDumpHandler.handleRequest(req, rsp); + } else if (name.equalsIgnoreCase("logging")) { + loggingHandler.handleRequest(req, rsp); + } else if (name.equalsIgnoreCase("system")) { + systemInfoHandler.handleRequest(req, rsp); + } else { + if (name.equalsIgnoreCase("info")) name = ""; + throw new SolrException(ErrorCode.NOT_FOUND, "Info Handler not found: " + name); + } + + rsp.setHttpCaching(false); + } + + + + + + //////////////////////// SolrInfoMBeans methods ////////////////////// + + @Override + public String getDescription() { + return "System Information"; + } + + @Override + public String getSource() { + return "$URL: https://svn.apache.org/repos/asf/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/admin/InfoHandler.java $"; + } +} diff --git a/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java index f95f03c30ab..6e26e869e34 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/LoggingHandler.java @@ -27,6 +27,7 @@ import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.CoreContainer; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.logging.LogWatcher; @@ -44,12 +45,22 @@ import org.slf4j.LoggerFactory; */ public class LoggingHandler extends RequestHandlerBase implements SolrCoreAware { static final org.slf4j.Logger log = LoggerFactory.getLogger(LoggingHandler.class); + + private LogWatcher watcher; - LogWatcher watcher = null; + public LoggingHandler(CoreContainer cc) { + this.watcher = cc.getLogging(); + } + + public LoggingHandler() { + + } @Override public void inform(SolrCore core) { - watcher = core.getCoreDescriptor().getCoreContainer().getLogging(); + if (watcher == null) { + watcher = core.getCoreDescriptor().getCoreContainer().getLogging(); + } } @Override diff --git a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java index 7483721386b..2e36e5eef34 100644 --- a/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java +++ b/solr/core/src/java/org/apache/solr/handler/admin/SystemInfoHandler.java @@ -37,6 +37,7 @@ import org.apache.commons.io.IOUtils; import org.apache.lucene.LucenePackage; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; +import org.apache.solr.core.CoreContainer; import org.apache.solr.core.SolrCore; import org.apache.solr.handler.RequestHandlerBase; import org.apache.solr.request.SolrQueryRequest; @@ -62,8 +63,20 @@ public class SystemInfoHandler extends RequestHandlerBase //(ie: not static, so core reload will refresh) private String hostname = null; + private CoreContainer cc; + public SystemInfoHandler() { super(); + init(); + } + + public SystemInfoHandler(CoreContainer cc) { + super(); + this.cc = cc; + init(); + } + + private void init() { try { InetAddress addr = InetAddress.getLocalHost(); hostname = addr.getCanonicalHostName(); @@ -75,14 +88,25 @@ public class SystemInfoHandler extends RequestHandlerBase @Override public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception { - rsp.add( "core", getCoreInfo( req.getCore(), req.getSchema() ) ); - boolean solrCloudMode = req.getCore().getCoreDescriptor().getCoreContainer().isZooKeeperAware(); + SolrCore core = req.getCore(); + if (core != null) rsp.add( "core", getCoreInfo( core, req.getSchema() ) ); + boolean solrCloudMode = getCoreContainer(req, core).isZooKeeperAware(); rsp.add( "mode", solrCloudMode ? "solrcloud" : "std"); rsp.add( "lucene", getLuceneInfo() ); rsp.add( "jvm", getJvmInfo() ); rsp.add( "system", getSystemInfo() ); rsp.setHttpCaching(false); } + + private CoreContainer getCoreContainer(SolrQueryRequest req, SolrCore core) { + CoreContainer coreContainer; + if (core != null) { + coreContainer = req.getCore().getCoreDescriptor().getCoreContainer(); + } else { + coreContainer = cc; + } + return coreContainer; + } /** * Get system info 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 72067fd79f4..218d30e44dc 100644 --- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java +++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java @@ -218,6 +218,13 @@ public class SolrDispatchFilter implements Filter 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 { //otherwise, we should find a core from the path idx = path.indexOf( "/", 1 ); diff --git a/solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java new file mode 100644 index 00000000000..306a99d1d85 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/handler/admin/InfoHandlerTest.java @@ -0,0 +1,80 @@ +/* + * 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.handler.admin; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.core.CoreContainer; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; +import org.junit.BeforeClass; +import org.junit.Test; + +public class InfoHandlerTest extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml", "schema.xml"); + } + + @Test + public void testCoreAdminHandler() throws Exception { + + final CoreContainer cores = h.getCoreContainer(); + InfoHandler infoHandler = cores.getInfoHandler(); + SolrQueryResponse rsp = handleRequest(infoHandler, "properties"); + + assertNotNull(rsp.getValues().get("system.properties")); + + + rsp = handleRequest(infoHandler, "threads"); + + assertNotNull(rsp.getValues().get("system")); + + rsp = handleRequest(infoHandler, "logging"); + + assertNotNull(rsp.getValues().get("watcher")); + + try { + rsp = handleRequest(infoHandler, "info"); + fail("Should have failed with not found"); + } catch(SolrException e) { + assertEquals(404, e.code()); + } + + try { + rsp = handleRequest(infoHandler, ""); + fail("Should have failed with not found"); + } catch(SolrException e) { + assertEquals(404, e.code()); + } + + + } + + private SolrQueryResponse handleRequest(InfoHandler infoHandler, String path) + throws Exception { + SolrQueryResponse rsp = new SolrQueryResponse(); + SolrQueryRequest req = req(); + req.getContext().put("path", path); + infoHandler.handleRequestBody(req, rsp); + return rsp; + } + +} diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java index d73038f793f..d8b1f9853c9 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java @@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicInteger; import com.google.common.collect.Lists; import com.google.common.collect.Maps; + import junit.framework.Assert; import org.apache.lucene.util._TestUtil; @@ -209,6 +210,17 @@ abstract public class SolrExampleTests extends SolrJettyTestBase Assert.assertEquals("price:[* TO 2]", values.get(0)); Assert.assertEquals("price:[2 TO 4]", values.get(1)); + + if (jetty != null) { + // check system wide system handler + "/admin/info/system" + String url = jetty.getBaseUrl().toString(); + HttpSolrServer client = new HttpSolrServer(url); + SolrQuery q = new SolrQuery(); + q.set("qt", "/admin/info/system"); + QueryResponse rsp = client.query(q); + assertNotNull(rsp.getResponse().get("mode")); + assertNotNull(rsp.getResponse().get("lucene")); + } }