You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by sh...@apache.org on 2008/07/29 20:13:58 UTC
svn commit: r680795 - in /lucene/solr/trunk: ./ example/solr/conf/
src/java/org/apache/solr/core/ src/java/org/apache/solr/search/
src/java/org/apache/solr/update/ src/test/org/apache/solr/core/
src/test/test-files/solr/conf/
Author: shalin
Date: Tue Jul 29 11:13:57 2008
New Revision: 680795
URL: http://svn.apache.org/viewvc?rev=680795&view=rev
Log:
SOLR-256 -- Support exposing Solr statistics through JMX
Added:
lucene/solr/trunk/src/java/org/apache/solr/core/JmxMonitoredMap.java
lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxIntegration.java
lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxMonitoredMap.java
Modified:
lucene/solr/trunk/CHANGES.txt
lucene/solr/trunk/example/solr/conf/solrconfig.xml
lucene/solr/trunk/src/java/org/apache/solr/core/SolrConfig.java
lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java
lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java
lucene/solr/trunk/src/java/org/apache/solr/update/UpdateHandler.java
lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml
Modified: lucene/solr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/CHANGES.txt?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/CHANGES.txt (original)
+++ lucene/solr/trunk/CHANGES.txt Tue Jul 29 11:13:57 2008
@@ -325,6 +325,8 @@
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
Modified: lucene/solr/trunk/example/solr/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/example/solr/conf/solrconfig.xml?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/example/solr/conf/solrconfig.xml (original)
+++ lucene/solr/trunk/example/solr/conf/solrconfig.xml Tue Jul 29 11:13:57 2008
@@ -116,6 +116,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">
Added: lucene/solr/trunk/src/java/org/apache/solr/core/JmxMonitoredMap.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/core/JmxMonitoredMap.java?rev=680795&view=auto
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/core/JmxMonitoredMap.java (added)
+++ lucene/solr/trunk/src/java/org/apache/solr/core/JmxMonitoredMap.java Tue Jul 29 11:13:57 2008
@@ -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");
+ }
+ }
+}
Modified: lucene/solr/trunk/src/java/org/apache/solr/core/SolrConfig.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/core/SolrConfig.java?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/core/SolrConfig.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/core/SolrConfig.java Tue Jul 29 11:13:57 2008
@@ -26,6 +26,7 @@
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 @@
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 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 @@
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 {
Modified: lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/core/SolrCore.java Tue Jul 29 11:13:57 2008
@@ -85,7 +85,7 @@
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; }
@@ -182,15 +182,16 @@
{
return this.logid;
}
-
+
/**
+ * @return the Info Registry map which contains SolrInfoMBean objects keyed by name
* @since solr 1.3
*/
- public Map<String,SolrInfoMBean> getInfoRegistry() {
+ public Map<String, SolrInfoMBean> getInfoRegistry() {
return infoRegistry;
}
-
-
+
+
public List<SolrEventListener> parseListener(String path) {
List<SolrEventListener> lst = new ArrayList<SolrEventListener>();
log.info( logid+"Searching for listeners: " +path);
@@ -393,6 +394,14 @@
if (schema==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.dataDir = dataDir;
@@ -444,6 +453,7 @@
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 @@
* 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 @@
for( CloseHook hook : closeHooks ) {
hook.close( this );
}
- }
+ }
+ try {
+ infoRegistry.clear();
+ } catch (Exception e) {
+ SolrException.log(log, e);
+ }
}
public boolean isClosed() {
Modified: lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/search/SolrIndexSearcher.java Tue Jul 29 11:13:57 2008
@@ -158,8 +158,6 @@
// for DocSets
HASHSET_INVERSE_LOAD_FACTOR = solrConfig.hashSetInverseLoadFactor;
HASHDOCSET_MAXSIZE = solrConfig.hashDocSetMaxSize;
- // register self
- core.getInfoRegistry().put(this.name, this);
}
@@ -171,6 +169,9 @@
/** 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 @@
* 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 @@
} else {
log.fine("Closing " + name);
}
+ core.getInfoRegistry().remove(name);
try {
searcher.close();
}
Modified: lucene/solr/trunk/src/java/org/apache/solr/update/UpdateHandler.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/update/UpdateHandler.java?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/update/UpdateHandler.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/update/UpdateHandler.java Tue Jul 29 11:13:57 2008
@@ -116,7 +116,6 @@
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 {
Added: lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxIntegration.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxIntegration.java?rev=680795&view=auto
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxIntegration.java (added)
+++ lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxIntegration.java Tue Jul 29 11:13:57 2008
@@ -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);
+ }
+}
Added: lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxMonitoredMap.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxMonitoredMap.java?rev=680795&view=auto
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxMonitoredMap.java (added)
+++ lucene/solr/trunk/src/test/org/apache/solr/core/TestJmxMonitoredMap.java Tue Jul 29 11:13:57 2008
@@ -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";
+ }
+ }
+
+}
Modified: lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml?rev=680795&r1=680794&r2=680795&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml (original)
+++ lucene/solr/trunk/src/test/test-files/solr/conf/solrconfig.xml Tue Jul 29 11:13:57 2008
@@ -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. -->