Index: core/src/main/java/org/quickfixj/jmx/JmxExporter.java =================================================================== --- core/src/main/java/org/quickfixj/jmx/JmxExporter.java (revision 899) +++ core/src/main/java/org/quickfixj/jmx/JmxExporter.java (working copy) @@ -17,30 +17,73 @@ package org.quickfixj.jmx; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; +import javax.management.ObjectName; import org.quickfixj.jmx.mbean.connector.ConnectorJmxExporter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import quickfix.Connector; import quickfix.mina.SessionConnector; public class JmxExporter { + /** + * Constant indicating that registration should fail when + * attempting to register an MBean under a name that already exists. + *

This is the default registration behaviour. + */ + public static final int REGISTRATION_FAIL_ON_EXISTING = 0; + /** + * Constant indicating that registration should ignore the affected MBean + * when attempting to register an MBean under a name that already exists. + */ + public static final int REGISTRATION_IGNORE_EXISTING = 1; + /** + * Constant indicating that registration should replace the affected MBean + * when attempting to register an MBean under a name that already exists. + */ + public static final int REGISTRATION_REPLACE_EXISTING = 2; + + private int registrationBehaviour; + + private final Logger log = LoggerFactory.getLogger(getClass()); + private final MBeanServer mbeanServer; private ConnectorJmxExporter connectorExporter = new ConnectorJmxExporter(); - public JmxExporter(MBeanServer mbeanServer) { + public JmxExporter(MBeanServer mbeanServer, int registrationBehaviour) { this.mbeanServer = mbeanServer; + this.registrationBehaviour = registrationBehaviour; } + public JmxExporter(MBeanServer mbeanServer) { + this(mbeanServer, REGISTRATION_FAIL_ON_EXISTING); + } + public JmxExporter() throws JMException { - this.mbeanServer = createMBeanServer(); + this(createMBeanServer()); } - private MBeanServer createMBeanServer() throws JMException { + /** + * Specify what action should be taken when attempting to register an MBean + * under an {@link javax.management.ObjectName} that already exists. + *

Default is REGISTRATION_FAIL_ON_EXISTING. + * @see #REGISTRATION_FAIL_ON_EXISTING + * @see #REGISTRATION_IGNORE_EXISTING + * @see #REGISTRATION_REPLACE_EXISTING + */ + public void setRegistrationBehavior(int registrationBehaviour) { + this.registrationBehaviour = registrationBehaviour; + } + + private static MBeanServer createMBeanServer() throws JMException { try { Class factoryClass = Class.forName("java.lang.management.ManagementFactory"); try { @@ -63,4 +106,42 @@ public MBeanServer getMBeanServer() { return mbeanServer; } + + /** + * Actually register the MBean with the server. The behaviour when encountering + * an existing MBean can be configured using the registrationBehaviour constructor or {@link #setRegistrationBehavior(int)}. + * This design (and also code) was unashamedly cribbed from Spring's + * MBeanRegistrationSupport + * class. Thanks Spring team! + * @see #REGISTRATION_FAIL_ON_EXISTING + * @see #REGISTRATION_IGNORE_EXISTING + * @see #REGISTRATION_REPLACE_EXISTING + * @param mbean + * @param objectName + * @throws JMException + */ + public void registerMBean(Object mbean, ObjectName objectName) throws JMException { + try { + mbeanServer.registerMBean(mbean, objectName); + } catch (InstanceAlreadyExistsException ex) { + if (this.registrationBehaviour == REGISTRATION_IGNORE_EXISTING) { + if (log.isDebugEnabled()) { + log.debug("Ignoring existing MBean at [" + objectName + "]"); + } + } else if (this.registrationBehaviour == REGISTRATION_REPLACE_EXISTING) { + try { + if (log.isDebugEnabled()) { + log.debug("Replacing existing MBean at [" + objectName + "]"); + } + mbeanServer.unregisterMBean(objectName); + mbeanServer.registerMBean(mbean, objectName); + } catch (InstanceNotFoundException ex2) { + log.error("Unable to replace existing MBean at [" + objectName + "]", ex2); + throw ex; + } + } else { + throw ex; + } + } + } } Index: core/src/main/java/org/quickfixj/jmx/mbean/connector/ConnectorJmxExporter.java =================================================================== --- core/src/main/java/org/quickfixj/jmx/mbean/connector/ConnectorJmxExporter.java (revision 899) +++ core/src/main/java/org/quickfixj/jmx/mbean/connector/ConnectorJmxExporter.java (working copy) @@ -20,11 +20,11 @@ import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; -import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.quickfixj.QFJException; +import org.quickfixj.jmx.JmxExporter; import org.quickfixj.jmx.mbean.ObjectNameFactory; import org.quickfixj.jmx.mbean.session.SessionJmxExporter; import org.slf4j.Logger; @@ -42,11 +42,11 @@ private final SessionJmxExporter sessionExporter = new SessionJmxExporter(); private final static AtomicInteger connectorIdCounter = new AtomicInteger(); - public void export(MBeanServer mbeanServer, SessionConnector connector) { - export(mbeanServer, connector, Integer.toString(connectorIdCounter.incrementAndGet())); + public void export(JmxExporter jmxExporter, SessionConnector connector) { + export(jmxExporter, connector, Integer.toString(connectorIdCounter.incrementAndGet())); } - public void export(MBeanServer mbeanServer, SessionConnector connector, String connectorId) { + public void export(JmxExporter jmxExporter, SessionConnector connector, String connectorId) { try { ConnectorAdmin connectorAdmin; if (connector instanceof AbstractSocketAcceptor) { @@ -59,11 +59,11 @@ ObjectName connectorName = getConnectorName(connector, connectorId); - mbeanServer.registerMBean(connectorAdmin, connectorName); + jmxExporter.registerMBean(connectorAdmin, connectorName); ArrayList sessionIDs = connector.getSessions(); for (int i = 0; i < sessionIDs.size(); i++) { SessionID sessionID = (SessionID) sessionIDs.get(i); - sessionExporter.export(mbeanServer, Session.lookupSession(sessionID), + sessionExporter.export(jmxExporter, Session.lookupSession(sessionID), connectorName, connector.getSettings()); } } catch (Exception e) { Index: core/src/main/java/org/quickfixj/jmx/mbean/session/SessionJmxExporter.java =================================================================== --- core/src/main/java/org/quickfixj/jmx/mbean/session/SessionJmxExporter.java (revision 899) +++ core/src/main/java/org/quickfixj/jmx/mbean/session/SessionJmxExporter.java (working copy) @@ -7,10 +7,10 @@ import java.util.TreeMap; import javax.management.JMException; -import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import org.quickfixj.jmx.JmxExporter; import org.quickfixj.jmx.mbean.ObjectNameFactory; import quickfix.ConfigError; @@ -21,17 +21,17 @@ public class SessionJmxExporter { private Map sessionObjectNames = new HashMap(); - public void export(MBeanServer mbeanServer, Session session, ObjectName connectorName, + public void export(JmxExporter jmxExporter, Session session, ObjectName connectorName, SessionSettings settings) throws JMException, ConfigError { ObjectName sessionName = createSessionName(session.getSessionID()); sessionObjectNames.put(session.getSessionID(), sessionName); SessionAdmin sessionAdmin = new SessionAdmin(session, connectorName); session.addStateListener(sessionAdmin); - mbeanServer.registerMBean(sessionAdmin, sessionName); + jmxExporter.registerMBean(sessionAdmin, sessionName); ObjectNameFactory settingsNameFactory = new ObjectNameFactory(); settingsNameFactory.addProperty("type", "Settings"); addSessionIdProperties(session.getSessionID(), settingsNameFactory); - mbeanServer.registerMBean(new SessionSettingsAdmin(session.getSessionID(), settings), + jmxExporter.registerMBean(new SessionSettingsAdmin(session.getSessionID(), settings), settingsNameFactory.createName()); }