package org.jivesoftware.openfire; import java.lang.management.ManagementFactory; import java.rmi.registry.Registry; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.management.remote.JMXAuthenticator; import javax.management.remote.JMXPrincipal; import javax.management.remote.JMXServiceURL; import javax.security.auth.Subject; import org.eclipse.jetty.jmx.ConnectorServer; import org.eclipse.jetty.jmx.MBeanContainer; import org.jivesoftware.openfire.admin.AdminManager; import org.jivesoftware.openfire.auth.AuthFactory; import org.jivesoftware.util.JiveGlobals; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manages the JMX configuration for Openfire. * * @author Tom Evans */ public class JMXManager { private static final Logger Log = LoggerFactory.getLogger(JMXManager.class); private static final String XMPP_JMX_ENABLED = "xmpp.jmx.enabled"; private static final String XMPP_JMX_SECURE = "xmpp.jmx.secure"; private static final String XMPP_JMX_PORT = "xmpp.jmx.port"; public static final int DEFAULT_PORT = Registry.REGISTRY_PORT; private static JMXManager instance = null; private MBeanContainer mbContainer; private ConnectorServer jmxServer; /** * Returns true if the JMX connector is configured to require * Openfire admin credentials. This option can be configured via * the admin console or by setting the following system property: * <pre> * xmpp.jmx.secure=false (default: true) * </pre> * * @return true if the JMX connector requires authentication */ public static boolean isSecure() { return JiveGlobals.getBooleanProperty(XMPP_JMX_SECURE, true); } public static void setSecure(boolean secure) { JiveGlobals.setProperty("xmpp.jmx.secure", String.valueOf(secure)); } /** * Returns the port number for the JMX connector. This option can * be configured via the admin console or by setting the following * system property: * <pre> * xmpp.jmx.port=[port] (default: 1099) * </pre> * * @return Port number for the JMX connector */ public static int getPort() { return JiveGlobals.getIntProperty(XMPP_JMX_PORT, DEFAULT_PORT); } public static void setPort(int port) { JiveGlobals.setProperty("xmpp.jmx.port", String.valueOf(port)); } /** * Returns true if JMX support is enabled. This option can be * configured via the admin console or by setting the following * system property: * <pre> * xmpp.jmx.enabled=true (default: false) * </pre> * * @return true if JMX support is enabled */ public static boolean isEnabled() { return JiveGlobals.getBooleanProperty(XMPP_JMX_ENABLED, false); } public static void setEnabled(boolean enabled) { JiveGlobals.setProperty("xmpp.jmx.enabled", String.valueOf(enabled)); } public static JMXManager getInstance() { if (instance == null) { instance = new JMXManager(); if (isEnabled()) { instance.start(); } } return instance; } private void start() { setContainer(new MBeanContainer(ManagementFactory.getPlatformMBeanServer())); int jmxPort = JMXManager.getPort(); String jmxUrl = "/jndi/rmi://localhost:" + jmxPort + "/jmxrmi"; Map<String, Object> env = new HashMap<>(); if (JMXManager.isSecure()) { env.put("jmx.remote.authenticator", new JMXAuthenticator() { @Override public Subject authenticate(Object credentials) { if (!(credentials instanceof String[])) { if (credentials == null) { throw new SecurityException("Credentials required"); } throw new SecurityException("Credentials should be String[]"); } final String[] aCredentials = (String[]) credentials; if (aCredentials.length < 2) { throw new SecurityException("Credentials should have at least two elements"); } String username = aCredentials[0]; String password = aCredentials[1]; try { AuthFactory.authenticate(username, password); } catch (Exception ex) { Log.error("Authentication failed for " + username); throw new SecurityException(); } if (AdminManager.getInstance().isUserAdmin(username, true)) { return new Subject(true, Collections.singleton(new JMXPrincipal(username)), Collections.EMPTY_SET, Collections.EMPTY_SET); } else { Log.error("Authorization failed for " + username); throw new SecurityException(); } } }); } try { jmxServer = new ConnectorServer(new JMXServiceURL("rmi", null, jmxPort, jmxUrl), env, "org.eclipse.jetty.jmx:name=rmiconnectorserver"); jmxServer.start(); } catch (Exception e) { Log.error("Failed to start JMX connector", e); } } public MBeanContainer getContainer() { return mbContainer; } public void setContainer(MBeanContainer mbContainer) { this.mbContainer = mbContainer; } }