mirror of https://github.com/apache/lucene.git
SOLR-256 -- Support exposing Solr statistics through JMX
git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@680795 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ed366c71ad
commit
2d13d9eab4
|
@ -325,6 +325,8 @@ New Features
|
||||||
|
|
||||||
62. SOLR-611: Expose sort_values returned by QueryComponent in SolrJ's QueryResponse (Dan Rosher via shalin)
|
62. SOLR-611: Expose sort_values returned by QueryComponent in SolrJ's QueryResponse (Dan Rosher via shalin)
|
||||||
|
|
||||||
|
63. SOLR-256: Support exposing Solr statistics through JMX (Sharad Agrawal, shalin)
|
||||||
|
|
||||||
|
|
||||||
Changes in runtime behavior
|
Changes in runtime behavior
|
||||||
1. SOLR-559: use Lucene updateDocument, deleteDocuments methods. This
|
1. SOLR-559: use Lucene updateDocument, deleteDocuments methods. This
|
||||||
|
|
|
@ -116,6 +116,20 @@
|
||||||
-->
|
-->
|
||||||
<unlockOnStartup>false</unlockOnStartup>
|
<unlockOnStartup>false</unlockOnStartup>
|
||||||
</mainIndex>
|
</mainIndex>
|
||||||
|
|
||||||
|
<!-- Enables JMX if and only if an existing MBeanServer is found, use
|
||||||
|
this if you want to configure JMX through JVM parameters. Remove
|
||||||
|
this to disable exposing Solr configuration and statistics to JMX.
|
||||||
|
|
||||||
|
If you want to connect to a particular server, specify the agentId
|
||||||
|
e.g. <jmx agentId="myAgent" />
|
||||||
|
|
||||||
|
If you want to start a new MBeanServer, specify the serviceUrl
|
||||||
|
e.g <jmx serviceurl="service:jmx:rmi:///jndi/rmi://localhost:9999/solr" />
|
||||||
|
|
||||||
|
For more details see http://wiki.apache.org/solr/SolrJmx
|
||||||
|
-->
|
||||||
|
<jmx />
|
||||||
|
|
||||||
<!-- the default high-performance update handler -->
|
<!-- the default high-performance update handler -->
|
||||||
<updateHandler class="solr.DirectUpdateHandler2">
|
<updateHandler class="solr.DirectUpdateHandler2">
|
||||||
|
|
|
@ -0,0 +1,295 @@
|
||||||
|
/**
|
||||||
|
* 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 org.apache.solr.common.SolrException;
|
||||||
|
import org.apache.solr.common.util.NamedList;
|
||||||
|
import org.apache.solr.core.SolrConfig.JmxConfiguration;
|
||||||
|
|
||||||
|
import javax.management.*;
|
||||||
|
import javax.management.remote.JMXConnectorServer;
|
||||||
|
import javax.management.remote.JMXConnectorServerFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Responsible for finding (or creating) a MBeanServer from given configuration
|
||||||
|
* and registering all SolrInfoMBean objects with JMX.
|
||||||
|
* </p>
|
||||||
|
* <p/>
|
||||||
|
* <p>
|
||||||
|
* Please see http://wiki.apache.org/solr/SolrJmx for instructions on usage and configuration
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @see org.apache.solr.core.SolrConfig.JmxConfiguration
|
||||||
|
* @since solr 1.3
|
||||||
|
*/
|
||||||
|
public class JmxMonitoredMap<K, V> extends
|
||||||
|
ConcurrentHashMap<String, SolrInfoMBean> {
|
||||||
|
private static final Logger LOG = Logger.getLogger(JmxMonitoredMap.class
|
||||||
|
.getName());
|
||||||
|
|
||||||
|
private MBeanServer server = null;
|
||||||
|
|
||||||
|
private String jmxRootName;
|
||||||
|
|
||||||
|
public JmxMonitoredMap(String coreName, JmxConfiguration jmxConfig) {
|
||||||
|
jmxRootName = "solr" + (coreName == null ? "" : "/" + coreName);
|
||||||
|
|
||||||
|
if (jmxConfig.agentId != null && jmxConfig.serviceUrl != null) {
|
||||||
|
throw new SolrException(
|
||||||
|
SolrException.ErrorCode.SERVER_ERROR,
|
||||||
|
"Incorrect JMX Configuration in solrconfig.xml, both agentId and serviceUrl cannot be specified at the same time");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jmxConfig.serviceUrl == null) {
|
||||||
|
List<MBeanServer> servers = null;
|
||||||
|
|
||||||
|
if (jmxConfig.agentId == null) {
|
||||||
|
// Try to find the first MBeanServer
|
||||||
|
servers = MBeanServerFactory.findMBeanServer(null);
|
||||||
|
} else if (jmxConfig.agentId != null) {
|
||||||
|
// Try to find the first MBean server with the given agentId
|
||||||
|
servers = MBeanServerFactory.findMBeanServer(jmxConfig.agentId);
|
||||||
|
// throw Exception if no servers were found with the given agentId
|
||||||
|
if (servers == null || servers.isEmpty())
|
||||||
|
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||||
|
"No JMX Servers found with agentId: " + jmxConfig.agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (servers == null || servers.isEmpty()) {
|
||||||
|
LOG
|
||||||
|
.info("No JMX servers found, not exposing Solr information with JMX.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
server = servers.get(0);
|
||||||
|
LOG.info("JMX monitoring is enabled. Adding Solr mbeans to JMX Server: "
|
||||||
|
+ server);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
// Create a new MBeanServer with the given serviceUrl
|
||||||
|
server = MBeanServerFactory.newMBeanServer();
|
||||||
|
JMXConnectorServer connector = JMXConnectorServerFactory
|
||||||
|
.newJMXConnectorServer(new JMXServiceURL(jmxConfig.serviceUrl),
|
||||||
|
null, server);
|
||||||
|
connector.start();
|
||||||
|
LOG.info("JMX monitoring is enabled at " + jmxConfig.serviceUrl);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Release the reference
|
||||||
|
server = null;
|
||||||
|
throw new RuntimeException("Could not start JMX monitoring ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the map and unregisters all SolrInfoMBeans in the map from
|
||||||
|
* MBeanServer
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
if (server != null) {
|
||||||
|
for (Map.Entry<String, SolrInfoMBean> entry : entrySet()) {
|
||||||
|
unregister(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the SolrInfoMBean to the map and registers the given SolrInfoMBean
|
||||||
|
* instance with the MBeanServer defined for this core. If a SolrInfoMBean is
|
||||||
|
* already registered with the MBeanServer then it is unregistered and then
|
||||||
|
* re-registered.
|
||||||
|
*
|
||||||
|
* @param key the JMX type name for this SolrInfoMBean
|
||||||
|
* @param infoBean the SolrInfoMBean instance to be registered
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SolrInfoMBean put(String key, SolrInfoMBean infoBean) {
|
||||||
|
if (server != null && infoBean != null) {
|
||||||
|
try {
|
||||||
|
ObjectName name = getObjectName(key, infoBean);
|
||||||
|
if (server.isRegistered(name))
|
||||||
|
server.unregisterMBean(name);
|
||||||
|
SolrDynamicMBean mbean = new SolrDynamicMBean(infoBean);
|
||||||
|
server.registerMBean(mbean, name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.log(Level.WARNING, "Failed to register info bean: " + key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.put(key, infoBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the SolrInfoMBean object at the given key and unregisters it from
|
||||||
|
* MBeanServer
|
||||||
|
*
|
||||||
|
* @param key the JMX type name for this SolrInfoMBean
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SolrInfoMBean remove(Object key) {
|
||||||
|
SolrInfoMBean infoBean = get(key);
|
||||||
|
if (infoBean != null) {
|
||||||
|
try {
|
||||||
|
unregister((String) key, infoBean);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
LOG.log(Level.WARNING, "Failed to unregister info bean: " + key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregister(String key, SolrInfoMBean infoBean) {
|
||||||
|
if (server == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectName name = getObjectName(key, infoBean);
|
||||||
|
if (server.isRegistered(name)) {
|
||||||
|
server.unregisterMBean(name);
|
||||||
|
} else {
|
||||||
|
LOG.info("Failed to unregister mbean: " + key
|
||||||
|
+ " because it was not registered");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||||
|
"Failed to unregister info bean: " + key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectName getObjectName(String key, SolrInfoMBean infoBean)
|
||||||
|
throws MalformedObjectNameException {
|
||||||
|
Hashtable<String, String> map = new Hashtable<String, String>();
|
||||||
|
map.put("type", key);
|
||||||
|
map.put("id", infoBean.getName());
|
||||||
|
return ObjectName.getInstance(jmxRootName, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DynamicMBean is used to dynamically expose all SolrInfoMBean
|
||||||
|
* getStatistics() NameList keys as String getters.
|
||||||
|
*/
|
||||||
|
static class SolrDynamicMBean implements DynamicMBean {
|
||||||
|
private SolrInfoMBean infoBean;
|
||||||
|
|
||||||
|
private HashSet<String> staticStats;
|
||||||
|
|
||||||
|
public SolrDynamicMBean(SolrInfoMBean managedResource) {
|
||||||
|
this.infoBean = managedResource;
|
||||||
|
staticStats = new HashSet<String>();
|
||||||
|
|
||||||
|
// For which getters are already available in SolrInfoMBean
|
||||||
|
staticStats.add("name");
|
||||||
|
staticStats.add("version");
|
||||||
|
staticStats.add("description");
|
||||||
|
staticStats.add("category");
|
||||||
|
staticStats.add("sourceId");
|
||||||
|
staticStats.add("source");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MBeanInfo getMBeanInfo() {
|
||||||
|
ArrayList<MBeanAttributeInfo> attrInfoList = new ArrayList<MBeanAttributeInfo>();
|
||||||
|
|
||||||
|
for (String stat : staticStats) {
|
||||||
|
attrInfoList.add(new MBeanAttributeInfo(stat, String.class.getName(),
|
||||||
|
null, true, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
NamedList dynamicStats = infoBean.getStatistics();
|
||||||
|
if (dynamicStats != null) {
|
||||||
|
for (int i = 0; i < dynamicStats.size(); i++) {
|
||||||
|
String name = dynamicStats.getName(i);
|
||||||
|
if (!staticStats.contains(name))
|
||||||
|
attrInfoList.add(new MBeanAttributeInfo(dynamicStats.getName(i),
|
||||||
|
String.class.getName(), null, true, false, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.log(Level.WARNING, "Could not getStatistics on info bean "
|
||||||
|
+ infoBean.getName(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
MBeanAttributeInfo[] attrInfoArr = attrInfoList
|
||||||
|
.toArray(new MBeanAttributeInfo[attrInfoList.size()]);
|
||||||
|
return new MBeanInfo(getClass().getName(), infoBean
|
||||||
|
.getDescription(), attrInfoArr, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getAttribute(String attribute)
|
||||||
|
throws AttributeNotFoundException, MBeanException, ReflectionException {
|
||||||
|
Object val;
|
||||||
|
if (staticStats.contains(attribute) && attribute != null
|
||||||
|
& attribute.length() > 0) {
|
||||||
|
try {
|
||||||
|
String getter = "get" + attribute.substring(0, 1).toUpperCase()
|
||||||
|
+ attribute.substring(1);
|
||||||
|
Method meth = infoBean.getClass().getMethod(getter);
|
||||||
|
val = meth.invoke(infoBean);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AttributeNotFoundException(attribute);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NamedList list = infoBean.getStatistics();
|
||||||
|
val = list.get(attribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val != null)
|
||||||
|
return val.toString();
|
||||||
|
else
|
||||||
|
return val;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttributeList getAttributes(String[] attributes) {
|
||||||
|
AttributeList list = new AttributeList();
|
||||||
|
for (String attribute : attributes) {
|
||||||
|
try {
|
||||||
|
list.add(new Attribute(attribute, getAttribute(attribute)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warning("Could not get attibute " + attribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttribute(Attribute attribute)
|
||||||
|
throws AttributeNotFoundException, InvalidAttributeValueException,
|
||||||
|
MBeanException, ReflectionException {
|
||||||
|
throw new UnsupportedOperationException("Operation not Supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttributeList setAttributes(AttributeList attributes) {
|
||||||
|
throw new UnsupportedOperationException("Operation not Supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object invoke(String actionName, Object[] params, String[] signature)
|
||||||
|
throws MBeanException, ReflectionException {
|
||||||
|
throw new UnsupportedOperationException("Operation not Supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import org.apache.solr.search.CacheConfig;
|
||||||
import org.apache.solr.update.SolrIndexConfig;
|
import org.apache.solr.update.SolrIndexConfig;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
|
|
||||||
|
import org.w3c.dom.Node;
|
||||||
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
@ -133,6 +134,15 @@ public class SolrConfig extends Config {
|
||||||
pingQueryParams = readPingQueryParams(this);
|
pingQueryParams = readPingQueryParams(this);
|
||||||
|
|
||||||
httpCachingConfig = new HttpCachingConfig(this);
|
httpCachingConfig = new HttpCachingConfig(this);
|
||||||
|
|
||||||
|
Node jmx = (Node) getNode("jmx", false);
|
||||||
|
if (jmx != null) {
|
||||||
|
jmxConfig = new JmxConfiguration(true, get("jmx/@agentId", null), get(
|
||||||
|
"jmx/@serviceUrl", null));
|
||||||
|
} else {
|
||||||
|
jmxConfig = new JmxConfiguration(false, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
Config.log.info("Loaded SolrConfig: " + name);
|
Config.log.info("Loaded SolrConfig: " + name);
|
||||||
|
|
||||||
// TODO -- at solr 2.0. this should go away
|
// TODO -- at solr 2.0. this should go away
|
||||||
|
@ -162,6 +172,9 @@ public class SolrConfig extends Config {
|
||||||
public final SolrIndexConfig defaultIndexConfig;
|
public final SolrIndexConfig defaultIndexConfig;
|
||||||
public final SolrIndexConfig mainIndexConfig;
|
public final SolrIndexConfig mainIndexConfig;
|
||||||
|
|
||||||
|
//JMX configuration
|
||||||
|
public final JmxConfiguration jmxConfig;
|
||||||
|
|
||||||
private final HttpCachingConfig httpCachingConfig;
|
private final HttpCachingConfig httpCachingConfig;
|
||||||
public HttpCachingConfig getHttpCachingConfig() {
|
public HttpCachingConfig getHttpCachingConfig() {
|
||||||
return httpCachingConfig;
|
return httpCachingConfig;
|
||||||
|
@ -197,6 +210,19 @@ public class SolrConfig extends Config {
|
||||||
return new LocalSolrQueryRequest(core, pingQueryParams);
|
return new LocalSolrQueryRequest(core, pingQueryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class JmxConfiguration {
|
||||||
|
public boolean enabled = false;
|
||||||
|
|
||||||
|
public String agentId;
|
||||||
|
|
||||||
|
public String serviceUrl;
|
||||||
|
|
||||||
|
public JmxConfiguration(boolean enabled, String agentId, String serviceUrl) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
this.agentId = agentId;
|
||||||
|
this.serviceUrl = serviceUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class HttpCachingConfig {
|
public static class HttpCachingConfig {
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ public final class SolrCore {
|
||||||
private final SolrHighlighter highlighter;
|
private final SolrHighlighter highlighter;
|
||||||
private final Map<String,SearchComponent> searchComponents;
|
private final Map<String,SearchComponent> searchComponents;
|
||||||
private final Map<String,UpdateRequestProcessorChain> updateProcessorChains;
|
private final Map<String,UpdateRequestProcessorChain> updateProcessorChains;
|
||||||
private final Map<String,SolrInfoMBean> infoRegistry = new java.util.HashMap<String,SolrInfoMBean>();
|
private final Map<String, SolrInfoMBean> infoRegistry;
|
||||||
|
|
||||||
public long getStartTime() { return startTime; }
|
public long getStartTime() { return startTime; }
|
||||||
|
|
||||||
|
@ -182,15 +182,16 @@ public final class SolrCore {
|
||||||
{
|
{
|
||||||
return this.logid;
|
return this.logid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @return the Info Registry map which contains SolrInfoMBean objects keyed by name
|
||||||
* @since solr 1.3
|
* @since solr 1.3
|
||||||
*/
|
*/
|
||||||
public Map<String,SolrInfoMBean> getInfoRegistry() {
|
public Map<String, SolrInfoMBean> getInfoRegistry() {
|
||||||
return infoRegistry;
|
return infoRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<SolrEventListener> parseListener(String path) {
|
public List<SolrEventListener> parseListener(String path) {
|
||||||
List<SolrEventListener> lst = new ArrayList<SolrEventListener>();
|
List<SolrEventListener> lst = new ArrayList<SolrEventListener>();
|
||||||
log.info( logid+"Searching for listeners: " +path);
|
log.info( logid+"Searching for listeners: " +path);
|
||||||
|
@ -393,6 +394,14 @@ public final class SolrCore {
|
||||||
if (schema==null) {
|
if (schema==null) {
|
||||||
schema = new IndexSchema(config, IndexSchema.DEFAULT_SCHEMA_FILE, null);
|
schema = new IndexSchema(config, IndexSchema.DEFAULT_SCHEMA_FILE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Initialize JMX
|
||||||
|
if (config.jmxConfig.enabled) {
|
||||||
|
infoRegistry = new JmxMonitoredMap<String, SolrInfoMBean>(name, config.jmxConfig);
|
||||||
|
} else {
|
||||||
|
log.info("JMX monitoring not detected for core: " + name);
|
||||||
|
infoRegistry = new LinkedHashMap<String, SolrInfoMBean>();
|
||||||
|
}
|
||||||
|
|
||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
this.dataDir = dataDir;
|
this.dataDir = dataDir;
|
||||||
|
@ -444,6 +453,7 @@ public final class SolrCore {
|
||||||
solrConfig.get("updateHandler/@class", DirectUpdateHandler.class.getName())
|
solrConfig.get("updateHandler/@class", DirectUpdateHandler.class.getName())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
infoRegistry.put("updateHandler", updateHandler);
|
||||||
|
|
||||||
// Finally tell anyone who wants to know
|
// Finally tell anyone who wants to know
|
||||||
loader.inform( loader );
|
loader.inform( loader );
|
||||||
|
@ -552,6 +562,7 @@ public final class SolrCore {
|
||||||
* 1. searcher
|
* 1. searcher
|
||||||
* 2. updateHandler
|
* 2. updateHandler
|
||||||
* 3. all CloseHooks will be notified
|
* 3. all CloseHooks will be notified
|
||||||
|
* 4. All MBeans will be unregistered from MBeanServer if JMX was enabled
|
||||||
*/
|
*/
|
||||||
public void close() {
|
public void close() {
|
||||||
log.info(logid+" CLOSING SolrCore!");
|
log.info(logid+" CLOSING SolrCore!");
|
||||||
|
@ -574,7 +585,12 @@ public final class SolrCore {
|
||||||
for( CloseHook hook : closeHooks ) {
|
for( CloseHook hook : closeHooks ) {
|
||||||
hook.close( this );
|
hook.close( this );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
infoRegistry.clear();
|
||||||
|
} catch (Exception e) {
|
||||||
|
SolrException.log(log, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
|
|
|
@ -158,8 +158,6 @@ public class SolrIndexSearcher extends Searcher implements SolrInfoMBean {
|
||||||
// for DocSets
|
// for DocSets
|
||||||
HASHSET_INVERSE_LOAD_FACTOR = solrConfig.hashSetInverseLoadFactor;
|
HASHSET_INVERSE_LOAD_FACTOR = solrConfig.hashSetInverseLoadFactor;
|
||||||
HASHDOCSET_MAXSIZE = solrConfig.hashDocSetMaxSize;
|
HASHDOCSET_MAXSIZE = solrConfig.hashDocSetMaxSize;
|
||||||
// register self
|
|
||||||
core.getInfoRegistry().put(this.name, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,6 +169,9 @@ public class SolrIndexSearcher extends Searcher implements SolrInfoMBean {
|
||||||
/** Register sub-objects such as caches
|
/** Register sub-objects such as caches
|
||||||
*/
|
*/
|
||||||
public void register() {
|
public void register() {
|
||||||
|
// register self
|
||||||
|
core.getInfoRegistry().put("searcher", this);
|
||||||
|
core.getInfoRegistry().put(name, this);
|
||||||
for (SolrCache cache : cacheList) {
|
for (SolrCache cache : cacheList) {
|
||||||
cache.setState(SolrCache.State.LIVE);
|
cache.setState(SolrCache.State.LIVE);
|
||||||
core.getInfoRegistry().put(cache.name(), cache);
|
core.getInfoRegistry().put(cache.name(), cache);
|
||||||
|
@ -184,9 +185,6 @@ public class SolrIndexSearcher extends Searcher implements SolrInfoMBean {
|
||||||
* In particular, the underlying reader and any cache's in use are closed.
|
* In particular, the underlying reader and any cache's in use are closed.
|
||||||
*/
|
*/
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
// unregister first, so no management actions are tried on a closing searcher.
|
|
||||||
core.getInfoRegistry().remove(name);
|
|
||||||
|
|
||||||
if (cachingEnabled) {
|
if (cachingEnabled) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Closing ").append(name);
|
sb.append("Closing ").append(name);
|
||||||
|
@ -198,6 +196,7 @@ public class SolrIndexSearcher extends Searcher implements SolrInfoMBean {
|
||||||
} else {
|
} else {
|
||||||
log.fine("Closing " + name);
|
log.fine("Closing " + name);
|
||||||
}
|
}
|
||||||
|
core.getInfoRegistry().remove(name);
|
||||||
try {
|
try {
|
||||||
searcher.close();
|
searcher.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,6 @@ public abstract class UpdateHandler implements SolrInfoMBean {
|
||||||
idFieldType = idField!=null ? idField.getType() : null;
|
idFieldType = idField!=null ? idField.getType() : null;
|
||||||
idTerm = idField!=null ? new Term(idField.getName(),"") : null;
|
idTerm = idField!=null ? new Term(idField.getName(),"") : null;
|
||||||
parseEventListeners();
|
parseEventListeners();
|
||||||
core.getInfoRegistry().put("updateHandler", this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SolrIndexWriter createMainIndexWriter(String name, boolean removeAllExisting) throws IOException {
|
protected SolrIndexWriter createMainIndexWriter(String name, boolean removeAllExisting) throws IOException {
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* 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 org.apache.solr.core.JmxMonitoredMap.SolrDynamicMBean;
|
||||||
|
import org.apache.solr.util.AbstractSolrTestCase;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.management.*;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for JMX Integration
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @since solr 1.3
|
||||||
|
*/
|
||||||
|
public class TestJmxIntegration extends AbstractSolrTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSchemaFile() {
|
||||||
|
return "schema.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSolrConfigFile() {
|
||||||
|
return "solrconfig.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
// Make sure that at least one MBeanServer is available
|
||||||
|
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
super.setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJmxRegistration() throws Exception {
|
||||||
|
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
|
||||||
|
System.out.println("Servers: " + servers);
|
||||||
|
assertNotNull("MBeanServers were null", servers);
|
||||||
|
assertFalse("No MBeanServer was found", servers.isEmpty());
|
||||||
|
|
||||||
|
MBeanServer mbeanServer = servers.get(0);
|
||||||
|
assertTrue("No MBeans found in server", mbeanServer.getMBeanCount() > 0);
|
||||||
|
|
||||||
|
Set<ObjectInstance> objects = mbeanServer.queryMBeans(null, null);
|
||||||
|
assertFalse("No SolrInfoMBean objects found in mbean server", objects
|
||||||
|
.isEmpty());
|
||||||
|
for (ObjectInstance o : objects) {
|
||||||
|
MBeanInfo mbeanInfo = mbeanServer.getMBeanInfo(o.getObjectName());
|
||||||
|
if (mbeanInfo.getClassName().endsWith(SolrDynamicMBean.class.getName())) {
|
||||||
|
assertTrue("No Attributes found for mbean: " + mbeanInfo, mbeanInfo
|
||||||
|
.getAttributes().length > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJmxUpdate() throws Exception {
|
||||||
|
List<MBeanServer> servers = MBeanServerFactory.findMBeanServer(null);
|
||||||
|
MBeanServer mbeanServer = servers.get(0);
|
||||||
|
|
||||||
|
Set<ObjectInstance> objects = mbeanServer.queryMBeans(null, Query.match(
|
||||||
|
Query.attr("numDocs"), Query.value("[0-9]")));
|
||||||
|
assertFalse("No MBean for SolrIndexSearcher found in MBeanServer", objects
|
||||||
|
.isEmpty());
|
||||||
|
|
||||||
|
int oldNumDocs = Integer.valueOf((String) mbeanServer.getAttribute(objects
|
||||||
|
.iterator().next().getObjectName(), "numDocs"));
|
||||||
|
|
||||||
|
assertU(adoc("id", "1"));
|
||||||
|
assertU(commit());
|
||||||
|
|
||||||
|
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("numDocs"),
|
||||||
|
Query.value("[0-9]")));
|
||||||
|
assertFalse("No MBean for SolrIndexSearcher found in MBeanServer", objects
|
||||||
|
.isEmpty());
|
||||||
|
|
||||||
|
int numDocs = Integer.valueOf((String) mbeanServer.getAttribute(objects
|
||||||
|
.iterator().next().getObjectName(), "numDocs"));
|
||||||
|
assertTrue("New numDocs is same as old numDocs as reported by JMX",
|
||||||
|
numDocs > oldNumDocs);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/**
|
||||||
|
* 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 org.apache.solr.common.util.NamedList;
|
||||||
|
import org.apache.solr.core.SolrConfig.JmxConfiguration;
|
||||||
|
import org.junit.After;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import javax.management.MBeanServerConnection;
|
||||||
|
import javax.management.ObjectInstance;
|
||||||
|
import javax.management.Query;
|
||||||
|
import javax.management.remote.JMXConnector;
|
||||||
|
import javax.management.remote.JMXConnectorFactory;
|
||||||
|
import javax.management.remote.JMXServiceURL;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.rmi.RemoteException;
|
||||||
|
import java.rmi.registry.LocateRegistry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for JmxMonitoredMap
|
||||||
|
*
|
||||||
|
* @version $Id$
|
||||||
|
* @since solr 1.3
|
||||||
|
*/
|
||||||
|
public class TestJmxMonitoredMap {
|
||||||
|
|
||||||
|
private int port = 0;
|
||||||
|
|
||||||
|
private JMXConnector connector;
|
||||||
|
|
||||||
|
private MBeanServerConnection mbeanServer;
|
||||||
|
|
||||||
|
private JmxMonitoredMap<String, SolrInfoMBean> monitoredMap;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
try {
|
||||||
|
ServerSocket server = new ServerSocket(0);
|
||||||
|
port = server.getLocalPort();
|
||||||
|
server.close();
|
||||||
|
System.out.println("Using port: " + port);
|
||||||
|
try {
|
||||||
|
LocateRegistry.createRegistry(port);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
String url = "service:jmx:rmi:///jndi/rmi://:" + port + "/solrjmx";
|
||||||
|
JmxConfiguration config = new JmxConfiguration(true, null, url);
|
||||||
|
monitoredMap = new JmxMonitoredMap<String, SolrInfoMBean>(null, config);
|
||||||
|
JMXServiceURL u = new JMXServiceURL(url);
|
||||||
|
connector = JMXConnectorFactory.connect(u);
|
||||||
|
mbeanServer = connector.getMBeanServerConnection();
|
||||||
|
break;
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
try {
|
||||||
|
connector.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPutRemoveClear() throws Exception {
|
||||||
|
MockInfoMBean mock = new MockInfoMBean();
|
||||||
|
monitoredMap.put("mock", mock);
|
||||||
|
|
||||||
|
Set<ObjectInstance> objects = mbeanServer.queryMBeans(null, Query.match(
|
||||||
|
Query.attr("name"), Query.value("mock")));
|
||||||
|
assertFalse("No MBean for mock object found in MBeanServer", objects
|
||||||
|
.isEmpty());
|
||||||
|
|
||||||
|
monitoredMap.remove("mock");
|
||||||
|
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("name"),
|
||||||
|
Query.value("mock")));
|
||||||
|
assertTrue("MBean for mock object found in MBeanServer even after removal",
|
||||||
|
objects.isEmpty());
|
||||||
|
|
||||||
|
monitoredMap.put("mock", mock);
|
||||||
|
monitoredMap.put("mock2", mock);
|
||||||
|
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("name"),
|
||||||
|
Query.value("mock")));
|
||||||
|
assertFalse("No MBean for mock object found in MBeanServer", objects
|
||||||
|
.isEmpty());
|
||||||
|
|
||||||
|
monitoredMap.clear();
|
||||||
|
objects = mbeanServer.queryMBeans(null, Query.match(Query.attr("name"),
|
||||||
|
Query.value("mock")));
|
||||||
|
assertTrue(
|
||||||
|
"MBean for mock object found in MBeanServer even after clear has been called",
|
||||||
|
objects.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockInfoMBean implements SolrInfoMBean {
|
||||||
|
public String getName() {
|
||||||
|
return "mock";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category getCategory() {
|
||||||
|
return Category.OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return "mock";
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL[] getDocs() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion() {
|
||||||
|
return "mock";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return "mock";
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public NamedList getStatistics() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceId() {
|
||||||
|
return "mock";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
<config>
|
<config>
|
||||||
|
|
||||||
|
<jmx />
|
||||||
|
|
||||||
<!-- Used to specify an alternate directory to hold all index data.
|
<!-- Used to specify an alternate directory to hold all index data.
|
||||||
It defaults to "index" if not present, and should probably
|
It defaults to "index" if not present, and should probably
|
||||||
not be changed if replication is in use. -->
|
not be changed if replication is in use. -->
|
||||||
|
|
Loading…
Reference in New Issue