JMXManager.java 5.45 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
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() {
107

108 109 110
		setContainer(new MBeanContainer(ManagementFactory.getPlatformMBeanServer()));
		int jmxPort = JMXManager.getPort();
		String jmxUrl = "/jndi/rmi://localhost:" + jmxPort + "/jmxrmi";
111
		Map<String, Object> env = new HashMap<>();
112 113
		if (JMXManager.isSecure()) {
    		env.put("jmx.remote.authenticator", new JMXAuthenticator() {
114 115
				@Override
				public Subject authenticate(Object credentials) {
116 117 118 119 120 121 122 123 124 125
    	            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");
    	            }
126 127
    	            String username = aCredentials[0];
    	            String password = aCredentials[1];
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

    	            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;
	}
}