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)
|
||||
|
||||
63. SOLR-256: Support exposing Solr statistics through JMX (Sharad Agrawal, shalin)
|
||||
|
||||
|
||||
Changes in runtime behavior
|
||||
1. SOLR-559: use Lucene updateDocument, deleteDocuments methods. This
|
||||
|
|
|
@ -117,6 +117,20 @@
|
|||
<unlockOnStartup>false</unlockOnStartup>
|
||||
</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 -->
|
||||
<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.lucene.search.BooleanQuery;
|
||||
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
@ -133,6 +134,15 @@ public class SolrConfig extends Config {
|
|||
pingQueryParams = readPingQueryParams(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);
|
||||
|
||||
// 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 mainIndexConfig;
|
||||
|
||||
//JMX configuration
|
||||
public final JmxConfiguration jmxConfig;
|
||||
|
||||
private final HttpCachingConfig httpCachingConfig;
|
||||
public HttpCachingConfig getHttpCachingConfig() {
|
||||
return httpCachingConfig;
|
||||
|
@ -197,6 +210,19 @@ public class SolrConfig extends Config {
|
|||
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 {
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ public final class SolrCore {
|
|||
private final SolrHighlighter highlighter;
|
||||
private final Map<String,SearchComponent> searchComponents;
|
||||
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; }
|
||||
|
||||
|
@ -184,6 +184,7 @@ public final class SolrCore {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return the Info Registry map which contains SolrInfoMBean objects keyed by name
|
||||
* @since solr 1.3
|
||||
*/
|
||||
public Map<String, SolrInfoMBean> getInfoRegistry() {
|
||||
|
@ -394,6 +395,14 @@ public final class SolrCore {
|
|||
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.dataDir = dataDir;
|
||||
this.solrConfig = config;
|
||||
|
@ -444,6 +453,7 @@ public final class SolrCore {
|
|||
solrConfig.get("updateHandler/@class", DirectUpdateHandler.class.getName())
|
||||
);
|
||||
|
||||
infoRegistry.put("updateHandler", updateHandler);
|
||||
|
||||
// Finally tell anyone who wants to know
|
||||
loader.inform( loader );
|
||||
|
@ -552,6 +562,7 @@ public final class SolrCore {
|
|||
* 1. searcher
|
||||
* 2. updateHandler
|
||||
* 3. all CloseHooks will be notified
|
||||
* 4. All MBeans will be unregistered from MBeanServer if JMX was enabled
|
||||
*/
|
||||
public void close() {
|
||||
log.info(logid+" CLOSING SolrCore!");
|
||||
|
@ -575,6 +586,11 @@ public final class SolrCore {
|
|||
hook.close( this );
|
||||
}
|
||||
}
|
||||
try {
|
||||
infoRegistry.clear();
|
||||
} catch (Exception e) {
|
||||
SolrException.log(log, e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
|
|
|
@ -158,8 +158,6 @@ public class SolrIndexSearcher extends Searcher implements SolrInfoMBean {
|
|||
// for DocSets
|
||||
HASHSET_INVERSE_LOAD_FACTOR = solrConfig.hashSetInverseLoadFactor;
|
||||
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
|
||||
*/
|
||||
public void register() {
|
||||
// register self
|
||||
core.getInfoRegistry().put("searcher", this);
|
||||
core.getInfoRegistry().put(name, this);
|
||||
for (SolrCache cache : cacheList) {
|
||||
cache.setState(SolrCache.State.LIVE);
|
||||
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.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
// unregister first, so no management actions are tried on a closing searcher.
|
||||
core.getInfoRegistry().remove(name);
|
||||
|
||||
if (cachingEnabled) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Closing ").append(name);
|
||||
|
@ -198,6 +196,7 @@ public class SolrIndexSearcher extends Searcher implements SolrInfoMBean {
|
|||
} else {
|
||||
log.fine("Closing " + name);
|
||||
}
|
||||
core.getInfoRegistry().remove(name);
|
||||
try {
|
||||
searcher.close();
|
||||
}
|
||||
|
|
|
@ -116,7 +116,6 @@ public abstract class UpdateHandler implements SolrInfoMBean {
|
|||
idFieldType = idField!=null ? idField.getType() : null;
|
||||
idTerm = idField!=null ? new Term(idField.getName(),"") : null;
|
||||
parseEventListeners();
|
||||
core.getInfoRegistry().put("updateHandler", this);
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<jmx />
|
||||
|
||||
<!-- Used to specify an alternate directory to hold all index data.
|
||||
It defaults to "index" if not present, and should probably
|
||||
not be changed if replication is in use. -->
|
||||
|
|
Loading…
Reference in New Issue