1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.jmx;
20
21import java.lang.management.ManagementFactory;
22import java.net.InetAddress;
23import java.net.ServerSocket;
24import java.rmi.registry.LocateRegistry;
25import java.rmi.registry.Registry;
26import java.rmi.server.UnicastRemoteObject;
27import java.util.Map;
28
29import javax.management.MBeanServer;
30import javax.management.ObjectName;
31import javax.management.remote.JMXConnectorServer;
32import javax.management.remote.JMXConnectorServerFactory;
33import javax.management.remote.JMXServiceURL;
34
35import org.eclipse.jetty.util.component.AbstractLifeCycle;
36import org.eclipse.jetty.util.log.Log;
37import org.eclipse.jetty.util.log.Logger;
38import org.eclipse.jetty.util.thread.ShutdownThread;
39
40
41/* ------------------------------------------------------------ */
42/**
43 * AbstractLifeCycle wrapper for JMXConnector Server
44 */
45public class ConnectorServer extends AbstractLifeCycle
46{
47    private static final Logger LOG = Log.getLogger(ConnectorServer.class);
48
49    JMXConnectorServer _connectorServer;
50    Registry _registry;
51
52    /* ------------------------------------------------------------ */
53    /**
54     * Constructs connector server
55     *
56     * @param serviceURL the address of the new connector server.
57     * The actual address of the new connector server, as returned
58     * by its getAddress method, will not necessarily be exactly the same.
59     * @param name object name string to be assigned to connector server bean
60     * @throws Exception
61     */
62    public ConnectorServer(JMXServiceURL serviceURL, String name)
63        throws Exception
64    {
65        this(serviceURL, null, name);
66    }
67
68    /* ------------------------------------------------------------ */
69    /**
70     * Constructs connector server
71     *
72     * @param svcUrl the address of the new connector server.
73     * The actual address of the new connector server, as returned
74     * by its getAddress method, will not necessarily be exactly the same.
75     * @param environment  a set of attributes to control the new connector
76     * server's behavior. This parameter can be null. Keys in this map must
77     * be Strings. The appropriate type of each associated value depends on
78     * the attribute. The contents of environment are not changed by this call.
79     * @param name object name string to be assigned to connector server bean
80     * @throws Exception
81     */
82    public ConnectorServer(JMXServiceURL svcUrl, Map<String,?> environment, String name)
83         throws Exception
84    {
85    	String urlPath = svcUrl.getURLPath();
86    	int idx = urlPath.indexOf("rmi://");
87    	if (idx > 0)
88    	{
89    	    String hostPort = urlPath.substring(idx+6, urlPath.indexOf('/', idx+6));
90    	    String regHostPort = startRegistry(hostPort);
91    	    if (regHostPort != null) {
92    	        urlPath = urlPath.replace(hostPort,regHostPort);
93    	        svcUrl = new JMXServiceURL(svcUrl.getProtocol(), svcUrl.getHost(), svcUrl.getPort(), urlPath);
94    	    }
95    	}
96        MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
97        _connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(svcUrl, environment, mbeanServer);
98        mbeanServer.registerMBean(_connectorServer,new ObjectName(name));
99    }
100
101	/* ------------------------------------------------------------ */
102    /**
103     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
104     */
105    @Override
106    public void doStart()
107        throws Exception
108    {
109        _connectorServer.start();
110        ShutdownThread.register(0, this);
111
112        LOG.info("JMX Remote URL: {}", _connectorServer.getAddress().toString());
113    }
114
115    /* ------------------------------------------------------------ */
116    /**
117     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
118     */
119    @Override
120    public void doStop()
121        throws Exception
122    {
123        ShutdownThread.deregister(this);
124        _connectorServer.stop();
125        stopRegistry();
126    }
127
128    /**
129     * Check that local RMI registry is used, and ensure it is started. If local RMI registry is being used and not started, start it.
130     *
131     * @param hostPath
132     *            hostname and port number of RMI registry
133     * @throws Exception
134     */
135    private String startRegistry(String hostPath) throws Exception
136    {
137        int rmiPort = 1099; // default RMI registry port
138        String rmiHost = hostPath;
139
140        int idx = hostPath.indexOf(':');
141        if (idx > 0)
142        {
143            rmiPort = Integer.parseInt(hostPath.substring(idx + 1));
144            rmiHost = hostPath.substring(0,idx);
145        }
146
147        // Verify that local registry is being used
148        InetAddress hostAddress = InetAddress.getByName(rmiHost);
149        if(hostAddress.isLoopbackAddress())
150        {
151            if (rmiPort == 0)
152            {
153                ServerSocket socket = new ServerSocket(0);
154                rmiPort = socket.getLocalPort();
155                socket.close();
156            }
157            else
158            {
159                try
160                {
161                    // Check if a local registry is already running
162                    LocateRegistry.getRegistry(rmiPort).list();
163                    return null;
164                }
165                catch (Exception ex)
166                {
167                    LOG.ignore(ex);
168                }
169            }
170
171            _registry = LocateRegistry.createRegistry(rmiPort);
172            Thread.sleep(1000);
173
174            rmiHost = InetAddress.getLocalHost().getCanonicalHostName();
175            return rmiHost + ':' + Integer.toString(rmiPort);
176        }
177
178        return null;
179    }
180
181    private void stopRegistry()
182    {
183        if (_registry != null)
184        {
185            try
186            {
187                UnicastRemoteObject.unexportObject(_registry,true);
188            }
189            catch (Exception ex)
190            {
191                LOG.ignore(ex);
192            }
193        }
194    }
195}
196