From 2d13d9eab44f8029467cb8b2ca470f01ef5239fd Mon Sep 17 00:00:00 2001 From: Shalin Shekhar Mangar Date: Tue, 29 Jul 2008 18:13:57 +0000 Subject: [PATCH] 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 --- CHANGES.txt | 2 + example/solr/conf/solrconfig.xml | 14 + .../org/apache/solr/core/JmxMonitoredMap.java | 295 ++++++++++++++++++ src/java/org/apache/solr/core/SolrConfig.java | 26 ++ src/java/org/apache/solr/core/SolrCore.java | 28 +- .../apache/solr/search/SolrIndexSearcher.java | 9 +- .../org/apache/solr/update/UpdateHandler.java | 1 - .../apache/solr/core/TestJmxIntegration.java | 108 +++++++ .../apache/solr/core/TestJmxMonitoredMap.java | 155 +++++++++ src/test/test-files/solr/conf/solrconfig.xml | 2 + 10 files changed, 628 insertions(+), 12 deletions(-) create mode 100644 src/java/org/apache/solr/core/JmxMonitoredMap.java create mode 100644 src/test/org/apache/solr/core/TestJmxIntegration.java create mode 100644 src/test/org/apache/solr/core/TestJmxMonitoredMap.java diff --git a/CHANGES.txt b/CHANGES.txt index 9781716a028..4032d1c0e40 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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 diff --git a/example/solr/conf/solrconfig.xml b/example/solr/conf/solrconfig.xml index bc4e4139d4b..34f27639c7a 100755 --- a/example/solr/conf/solrconfig.xml +++ b/example/solr/conf/solrconfig.xml @@ -116,6 +116,20 @@ --> false + + + diff --git a/src/java/org/apache/solr/core/JmxMonitoredMap.java b/src/java/org/apache/solr/core/JmxMonitoredMap.java new file mode 100644 index 00000000000..be6319ef4fb --- /dev/null +++ b/src/java/org/apache/solr/core/JmxMonitoredMap.java @@ -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; + +/** + *

+ * Responsible for finding (or creating) a MBeanServer from given configuration + * and registering all SolrInfoMBean objects with JMX. + *

+ *

+ *

+ * Please see http://wiki.apache.org/solr/SolrJmx for instructions on usage and configuration + *

+ * + * @version $Id$ + * @see org.apache.solr.core.SolrConfig.JmxConfiguration + * @since solr 1.3 + */ +public class JmxMonitoredMap extends + ConcurrentHashMap { + 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 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 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 map = new Hashtable(); + 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 staticStats; + + public SolrDynamicMBean(SolrInfoMBean managedResource) { + this.infoBean = managedResource; + staticStats = new HashSet(); + + // 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 attrInfoList = new ArrayList(); + + 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"); + } + } +} diff --git a/src/java/org/apache/solr/core/SolrConfig.java b/src/java/org/apache/solr/core/SolrConfig.java index 12b8aa2a6a8..9b691df4dc4 100644 --- a/src/java/org/apache/solr/core/SolrConfig.java +++ b/src/java/org/apache/solr/core/SolrConfig.java @@ -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 { diff --git a/src/java/org/apache/solr/core/SolrCore.java b/src/java/org/apache/solr/core/SolrCore.java index 9dfdd3c4fae..2e91e67c388 100644 --- a/src/java/org/apache/solr/core/SolrCore.java +++ b/src/java/org/apache/solr/core/SolrCore.java @@ -85,7 +85,7 @@ public final class SolrCore { private final SolrHighlighter highlighter; private final Map searchComponents; private final Map updateProcessorChains; - private final Map infoRegistry = new java.util.HashMap(); + private final Map infoRegistry; public long getStartTime() { return startTime; } @@ -182,15 +182,16 @@ public final class SolrCore { { return this.logid; } - + /** + * @return the Info Registry map which contains SolrInfoMBean objects keyed by name * @since solr 1.3 */ - public Map getInfoRegistry() { + public Map getInfoRegistry() { return infoRegistry; } - - + + public List parseListener(String path) { List lst = new ArrayList(); log.info( logid+"Searching for listeners: " +path); @@ -393,6 +394,14 @@ public final class SolrCore { if (schema==null) { schema = new IndexSchema(config, IndexSchema.DEFAULT_SCHEMA_FILE, null); } + + //Initialize JMX + if (config.jmxConfig.enabled) { + infoRegistry = new JmxMonitoredMap(name, config.jmxConfig); + } else { + log.info("JMX monitoring not detected for core: " + name); + infoRegistry = new LinkedHashMap(); + } this.schema = schema; this.dataDir = dataDir; @@ -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!"); @@ -574,7 +585,12 @@ public final class SolrCore { for( CloseHook hook : closeHooks ) { hook.close( this ); } - } + } + try { + infoRegistry.clear(); + } catch (Exception e) { + SolrException.log(log, e); + } } public boolean isClosed() { diff --git a/src/java/org/apache/solr/search/SolrIndexSearcher.java b/src/java/org/apache/solr/search/SolrIndexSearcher.java index d3077689bbb..a188cc19753 100644 --- a/src/java/org/apache/solr/search/SolrIndexSearcher.java +++ b/src/java/org/apache/solr/search/SolrIndexSearcher.java @@ -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(); } diff --git a/src/java/org/apache/solr/update/UpdateHandler.java b/src/java/org/apache/solr/update/UpdateHandler.java index 94a9886a0a2..5a95628fb93 100644 --- a/src/java/org/apache/solr/update/UpdateHandler.java +++ b/src/java/org/apache/solr/update/UpdateHandler.java @@ -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 { diff --git a/src/test/org/apache/solr/core/TestJmxIntegration.java b/src/test/org/apache/solr/core/TestJmxIntegration.java new file mode 100644 index 00000000000..019bc8db86e --- /dev/null +++ b/src/test/org/apache/solr/core/TestJmxIntegration.java @@ -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 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 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 servers = MBeanServerFactory.findMBeanServer(null); + MBeanServer mbeanServer = servers.get(0); + + Set 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); + } +} diff --git a/src/test/org/apache/solr/core/TestJmxMonitoredMap.java b/src/test/org/apache/solr/core/TestJmxMonitoredMap.java new file mode 100644 index 00000000000..09fee57cbcc --- /dev/null +++ b/src/test/org/apache/solr/core/TestJmxMonitoredMap.java @@ -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 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(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 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"; + } + } + +} diff --git a/src/test/test-files/solr/conf/solrconfig.xml b/src/test/test-files/solr/conf/solrconfig.xml index ab712b44187..723109e5b5a 100644 --- a/src/test/test-files/solr/conf/solrconfig.xml +++ b/src/test/test-files/solr/conf/solrconfig.xml @@ -24,6 +24,8 @@ + +