001/*
002 * #%L
003 * Netarchivesuite - common
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023
024package dk.netarkivet.common.management;
025
026import java.io.IOException;
027import java.io.Serializable;
028import java.lang.management.ManagementFactory;
029import java.rmi.registry.LocateRegistry;
030import java.text.MessageFormat;
031import java.util.HashMap;
032import java.util.Map;
033
034import javax.management.MBeanServer;
035import javax.management.remote.JMXConnectorServer;
036import javax.management.remote.JMXConnectorServerFactory;
037import javax.management.remote.JMXServiceURL;
038
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042import dk.netarkivet.common.CommonSettings;
043import dk.netarkivet.common.distribute.monitorregistry.MonitorRegistryClientFactory;
044import dk.netarkivet.common.exceptions.IOFailure;
045import dk.netarkivet.common.utils.Settings;
046import dk.netarkivet.common.utils.SystemUtils;
047
048/**
049 * Utility class that handles exposing the platform mbean server using rmi, and using specified ports and password
050 * files. <br/>
051 * <br/>
052 * <p>
053 * See http://java.sun.com/j2se/1.5.0/docs/guide/jmx/tutorial/security.html <br/>
054 * <br/>
055 * <p>
056 * TODO This implementation is not robust and could be improved. <br/>
057 * TODO For instance: - Singleton behaviour <br/>
058 * TODO -Reuse of already created registry <br/>
059 * TODO Usage of access rights (for read-only mbeans) (see reference above)
060 */
061public class MBeanConnectorCreator {
062
063    private static final Logger log = LoggerFactory.getLogger(MBeanConnectorCreator.class);
064
065    public static boolean isExposed = false;
066
067    private static final String SERVICE_JMX_RMI_URL = "service:jmx:rmi://{0}:{1}/jndi/rmi://{0}:{2}/jmxrmi";
068
069    private static final String ENVIRONMENT_PASSWORD_FILE_PROPERTY = "jmx.remote.x.password.file";
070
071    /**
072     * Registers an RMI connector to the local mbean server in a private RMI registry, under the name "jmxrmi". The port
073     * for the registry is read from settings, and the RMI port used for exposing the connector is also read from
074     * settings. Access to the mbean server is restricted by the rules set in the password file, likewise read from
075     * settings.
076     *
077     * @throws IOFailure on trouble exposing the server.
078     */
079    public static synchronized void exposeJMXMBeanServer() {
080        try {
081            if (!isExposed) {
082                int jmxPort = Settings.getInt(CommonSettings.JMX_PORT);
083                int rmiPort = Settings.getInt(CommonSettings.JMX_RMI_PORT);
084                String passwordFile = Settings.get(CommonSettings.JMX_PASSWORD_FILE);
085
086                // Create a private registry for the exposing the JMX connector.
087                LocateRegistry.createRegistry(jmxPort);
088                // Create a URL that signifies that we wish to use the local
089                // registry created above, and listen for rmi callbacks on the
090                // RMI port of this machine, exposing the mbeanserver with the
091                // name "jmxrmi".
092                String canonicalHostName = SystemUtils.getLocalHostName();
093                JMXServiceURL url = new JMXServiceURL(MessageFormat.format(SERVICE_JMX_RMI_URL, canonicalHostName,
094                        Integer.toString(rmiPort), Integer.toString(jmxPort)));
095                // Insert the password file into environment used when creating
096                // the connector server.
097                Map<String, Serializable> env = new HashMap<String, Serializable>();
098                env.put(ENVIRONMENT_PASSWORD_FILE_PROPERTY, passwordFile);
099                // Register the connector to the local mbean server in this
100                // registry under that URL, using the created environment
101                // settings.
102                MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
103                JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
104                // Start the connector server.
105                cs.start();
106                isExposed = true;
107                // Register the JMX server at the registry.
108                MonitorRegistryClientFactory.getInstance().register(canonicalHostName,
109                        Settings.getInt(CommonSettings.JMX_PORT), Settings.getInt(CommonSettings.JMX_RMI_PORT));
110
111                if (log.isInfoEnabled()) {
112                    log.info("Registered mbean server in registry on port {} communicating on port {} "
113                            + "using password file '{}'." + "\nService URL is {}", jmxPort, rmiPort, passwordFile,
114                            url.toString());
115                }
116            }
117        } catch (IOException e) {
118            throw new IOFailure("Error creating and registering an RMIConnector to the platform mbean server.", e);
119        }
120    }
121
122}