blob: 240bc3bdc0ebae8053a7f399d574afda5a962781 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package javax.management.remote;
28
29import java.io.IOException;
30import java.util.ArrayList;
31import java.util.List;
32import java.util.Map;
33
34import javax.management.MBeanNotificationInfo;
35import javax.management.MBeanRegistration;
36import javax.management.MBeanServer;
37import javax.management.Notification;
38import javax.management.NotificationBroadcasterSupport;
39import javax.management.ObjectName;
40
41/**
42 * <p>Superclass of every connector server. A connector server is
43 * attached to an MBean server. It listens for client connection
44 * requests and creates a connection for each one.</p>
45 *
46 * <p>A connector server is associated with an MBean server either by
47 * registering it in that MBean server, or by passing the MBean server
48 * to its constructor.</p>
49 *
50 * <p>A connector server is inactive when created. It only starts
51 * listening for client connections when the {@link #start() start}
52 * method is called. A connector server stops listening for client
53 * connections when the {@link #stop() stop} method is called or when
54 * the connector server is unregistered from its MBean server.</p>
55 *
56 * <p>Stopping a connector server does not unregister it from its
57 * MBean server. A connector server once stopped cannot be
58 * restarted.</p>
59 *
60 * <p>Each time a client connection is made or broken, a notification
61 * of class {@link JMXConnectionNotification} is emitted.</p>
62 *
63 * @since 1.5
64 */
65public abstract class JMXConnectorServer
66 extends NotificationBroadcasterSupport
67 implements JMXConnectorServerMBean, MBeanRegistration, JMXAddressable {
68
69 /**
70 * <p>Name of the attribute that specifies the authenticator for a
71 * connector server. The value associated with this attribute, if
72 * any, must be an object that implements the interface {@link
73 * JMXAuthenticator}.</p>
74 */
75 public static final String AUTHENTICATOR =
76 "jmx.remote.authenticator";
77
78 /**
79 * <p>Constructs a connector server that will be registered as an
80 * MBean in the MBean server it is attached to. This constructor
81 * is typically called by one of the <code>createMBean</code>
82 * methods when creating, within an MBean server, a connector
83 * server that makes it available remotely.</p>
84 */
85 public JMXConnectorServer() {
86 this(null);
87 }
88
89 /**
90 * <p>Constructs a connector server that is attached to the given
91 * MBean server. A connector server that is created in this way
92 * can be registered in a different MBean server.</p>
93 *
94 * @param mbeanServer the MBean server that this connector server
95 * is attached to. Null if this connector server will be attached
96 * to an MBean server by being registered in it.
97 */
98 public JMXConnectorServer(MBeanServer mbeanServer) {
99 this.mbeanServer = mbeanServer;
100 }
101
102 /**
103 * <p>Returns the MBean server that this connector server is
104 * attached to.</p>
105 *
106 * @return the MBean server that this connector server is attached
107 * to, or null if it is not yet attached to an MBean server.
108 */
109 public synchronized MBeanServer getMBeanServer() {
110 return mbeanServer;
111 }
112
113 public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf)
114 {
115 if (mbsf == null)
116 throw new IllegalArgumentException("Invalid null argument: mbsf");
117
118 if (mbeanServer != null) mbsf.setMBeanServer(mbeanServer);
119 mbeanServer = mbsf;
120 }
121
122 public String[] getConnectionIds() {
123 synchronized (connectionIds) {
124 return connectionIds.toArray(new String[connectionIds.size()]);
125 }
126 }
127
128 /**
129 * <p>Returns a client stub for this connector server. A client
130 * stub is a serializable object whose {@link
131 * JMXConnector#connect(Map) connect} method can be used to make
132 * one new connection to this connector server.</p>
133 *
134 * <p>A given connector need not support the generation of client
135 * stubs. However, the connectors specified by the JMX Remote API do
136 * (JMXMP Connector and RMI Connector).</p>
137 *
138 * <p>The default implementation of this method uses {@link
139 * #getAddress} and {@link JMXConnectorFactory} to generate the
140 * stub, with code equivalent to the following:</p>
141 *
142 * <pre>
143 * JMXServiceURL addr = {@link #getAddress() getAddress()};
144 * return {@link JMXConnectorFactory#newJMXConnector(JMXServiceURL, Map)
145 * JMXConnectorFactory.newJMXConnector(addr, env)};
146 * </pre>
147 *
148 * <p>A connector server for which this is inappropriate must
149 * override this method so that it either implements the
150 * appropriate logic or throws {@link
151 * UnsupportedOperationException}.</p>
152 *
153 * @param env client connection parameters of the same sort that
154 * could be provided to {@link JMXConnector#connect(Map)
155 * JMXConnector.connect(Map)}. Can be null, which is equivalent
156 * to an empty map.
157 *
158 * @return a client stub that can be used to make a new connection
159 * to this connector server.
160 *
161 * @exception UnsupportedOperationException if this connector
162 * server does not support the generation of client stubs.
163 *
164 * @exception IllegalStateException if the JMXConnectorServer is
165 * not started (see {@link JMXConnectorServerMBean#isActive()}).
166 *
167 * @exception IOException if a communications problem means that a
168 * stub cannot be created.
169 **/
170 public JMXConnector toJMXConnector(Map<String,?> env)
171 throws IOException
172 {
173 if (!isActive()) throw new
174 IllegalStateException("Connector is not active");
175 JMXServiceURL addr = getAddress();
176 return JMXConnectorFactory.newJMXConnector(addr, env);
177 }
178
179 /**
180 * <p>Returns an array indicating the notifications that this MBean
181 * sends. The implementation in <code>JMXConnectorServer</code>
182 * returns an array with one element, indicating that it can emit
183 * notifications of class {@link JMXConnectionNotification} with
184 * the types defined in that class. A subclass that can emit other
185 * notifications should return an array that contains this element
186 * plus descriptions of the other notifications.</p>
187 *
188 * @return the array of possible notifications.
189 */
190 public MBeanNotificationInfo[] getNotificationInfo() {
191 final String[] types = {
192 JMXConnectionNotification.OPENED,
193 JMXConnectionNotification.CLOSED,
194 JMXConnectionNotification.FAILED,
195 };
196 final String className = JMXConnectionNotification.class.getName();
197 final String description =
198 "A client connection has been opened or closed";
199 return new MBeanNotificationInfo[] {
200 new MBeanNotificationInfo(types, className, description),
201 };
202 }
203
204 /**
205 * <p>Called by a subclass when a new client connection is opened.
206 * Adds <code>connectionId</code> to the list returned by {@link
207 * #getConnectionIds()}, then emits a {@link
208 * JMXConnectionNotification} with type {@link
209 * JMXConnectionNotification#OPENED}.</p>
210 *
211 * @param connectionId the ID of the new connection. This must be
212 * different from the ID of any connection previously opened by
213 * this connector server.
214 *
215 * @param message the message for the emitted {@link
216 * JMXConnectionNotification}. Can be null. See {@link
217 * Notification#getMessage()}.
218 *
219 * @param userData the <code>userData</code> for the emitted
220 * {@link JMXConnectionNotification}. Can be null. See {@link
221 * Notification#getUserData()}.
222 *
223 * @exception NullPointerException if <code>connectionId</code> is
224 * null.
225 */
226 protected void connectionOpened(String connectionId,
227 String message,
228 Object userData) {
229
230 if (connectionId == null)
231 throw new NullPointerException("Illegal null argument");
232
233 synchronized (connectionIds) {
234 connectionIds.add(connectionId);
235 }
236
237 sendNotification(JMXConnectionNotification.OPENED, connectionId,
238 message, userData);
239 }
240
241 /**
242 * <p>Called by a subclass when a client connection is closed
243 * normally. Removes <code>connectionId</code> from the list returned
244 * by {@link #getConnectionIds()}, then emits a {@link
245 * JMXConnectionNotification} with type {@link
246 * JMXConnectionNotification#CLOSED}.</p>
247 *
248 * @param connectionId the ID of the closed connection.
249 *
250 * @param message the message for the emitted {@link
251 * JMXConnectionNotification}. Can be null. See {@link
252 * Notification#getMessage()}.
253 *
254 * @param userData the <code>userData</code> for the emitted
255 * {@link JMXConnectionNotification}. Can be null. See {@link
256 * Notification#getUserData()}.
257 *
258 * @exception NullPointerException if <code>connectionId</code>
259 * is null.
260 */
261 protected void connectionClosed(String connectionId,
262 String message,
263 Object userData) {
264
265 if (connectionId == null)
266 throw new NullPointerException("Illegal null argument");
267
268 synchronized (connectionIds) {
269 connectionIds.remove(connectionId);
270 }
271
272 sendNotification(JMXConnectionNotification.CLOSED, connectionId,
273 message, userData);
274 }
275
276 /**
277 * <p>Called by a subclass when a client connection fails.
278 * Removes <code>connectionId</code> from the list returned by
279 * {@link #getConnectionIds()}, then emits a {@link
280 * JMXConnectionNotification} with type {@link
281 * JMXConnectionNotification#FAILED}.</p>
282 *
283 * @param connectionId the ID of the failed connection.
284 *
285 * @param message the message for the emitted {@link
286 * JMXConnectionNotification}. Can be null. See {@link
287 * Notification#getMessage()}.
288 *
289 * @param userData the <code>userData</code> for the emitted
290 * {@link JMXConnectionNotification}. Can be null. See {@link
291 * Notification#getUserData()}.
292 *
293 * @exception NullPointerException if <code>connectionId</code> is
294 * null.
295 */
296 protected void connectionFailed(String connectionId,
297 String message,
298 Object userData) {
299
300 if (connectionId == null)
301 throw new NullPointerException("Illegal null argument");
302
303 synchronized (connectionIds) {
304 connectionIds.remove(connectionId);
305 }
306
307 sendNotification(JMXConnectionNotification.FAILED, connectionId,
308 message, userData);
309 }
310
311 private void sendNotification(String type, String connectionId,
312 String message, Object userData) {
313 Notification notif =
314 new JMXConnectionNotification(type,
315 getNotificationSource(),
316 connectionId,
317 nextSequenceNumber(),
318 message,
319 userData);
320 sendNotification(notif);
321 }
322
323 private synchronized Object getNotificationSource() {
324 if (myName != null)
325 return myName;
326 else
327 return this;
328 }
329
330 private static long nextSequenceNumber() {
331 synchronized (sequenceNumberLock) {
332 return sequenceNumber++;
333 }
334 }
335
336 // implements MBeanRegistration
337 /**
338 * <p>Called by an MBean server when this connector server is
339 * registered in that MBean server. This connector server becomes
340 * attached to the MBean server and its {@link #getMBeanServer()}
341 * method will return <code>mbs</code>.</p>
342 *
343 * <p>If this connector server is already attached to an MBean
344 * server, this method has no effect. The MBean server it is
345 * attached to is not necessarily the one it is being registered
346 * in.</p>
347 *
348 * @param mbs the MBean server in which this connection server is
349 * being registered.
350 *
351 * @param name The object name of the MBean.
352 *
353 * @return The name under which the MBean is to be registered.
354 *
355 * @exception NullPointerException if <code>mbs</code> or
356 * <code>name</code> is null.
357 */
358 public synchronized ObjectName preRegister(MBeanServer mbs,
359 ObjectName name) {
360 if (mbs == null || name == null)
361 throw new NullPointerException("Null MBeanServer or ObjectName");
362 if (mbeanServer == null) {
363 mbeanServer = mbs;
364 myName = name;
365 }
366 return name;
367 }
368
369 public void postRegister(Boolean registrationDone) {
370 // do nothing
371 }
372
373 /**
374 * <p>Called by an MBean server when this connector server is
375 * unregistered from that MBean server. If this connector server
376 * was attached to that MBean server by being registered in it,
377 * and if the connector server is still active,
378 * then unregistering it will call the {@link #stop stop} method.
379 * If the <code>stop</code> method throws an exception, the
380 * unregistration attempt will fail. It is recommended to call
381 * the <code>stop</code> method explicitly before unregistering
382 * the MBean.</p>
383 *
384 * @exception IOException if thrown by the {@link #stop stop} method.
385 */
386 public synchronized void preDeregister() throws Exception {
387 if (myName != null && isActive()) {
388 stop();
389 myName = null; // just in case stop is buggy and doesn't stop
390 }
391 }
392
393 public void postDeregister() {
394 myName = null;
395 }
396
397 /**
398 * The MBeanServer used by this server to execute a client request.
399 */
400 private MBeanServer mbeanServer = null;
401
402 /**
403 * The name used to registered this server in an MBeanServer.
404 * It is null if the this server is not registered or has been unregistered.
405 */
406 private ObjectName myName;
407
408 private List<String> connectionIds = new ArrayList<String>();
409
410 private static final int[] sequenceNumberLock = new int[0];
411 private static long sequenceNumber;
412}