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