1 /* 2 * Copyright (c) 2003, 2008, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 27 package javax.management.remote; 28 29 import java.io.IOException; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.Map; 33 34 import javax.management.MBeanNotificationInfo; 35 import javax.management.MBeanRegistration; 36 import javax.management.MBeanServer; 37 import javax.management.Notification; 38 import javax.management.NotificationBroadcasterSupport; 39 import 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 */ 65 public 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 */ JMXConnectorServer()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, or not registered 93 * in any MBean server.</p> 94 * 95 * @param mbeanServer the MBean server that this connector server 96 * is attached to. Null if this connector server will be attached 97 * to an MBean server by being registered in it. 98 */ JMXConnectorServer(MBeanServer mbeanServer)99 public JMXConnectorServer(MBeanServer mbeanServer) { 100 this.mbeanServer = mbeanServer; 101 } 102 103 /** 104 * <p>Returns the MBean server that this connector server is 105 * attached to.</p> 106 * 107 * @return the MBean server that this connector server is attached 108 * to, or null if it is not yet attached to an MBean server. 109 */ getMBeanServer()110 public synchronized MBeanServer getMBeanServer() { 111 return mbeanServer; 112 } 113 setMBeanServerForwarder(MBeanServerForwarder mbsf)114 public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) 115 { 116 if (mbsf == null) 117 throw new IllegalArgumentException("Invalid null argument: mbsf"); 118 119 if (mbeanServer != null) mbsf.setMBeanServer(mbeanServer); 120 mbeanServer = mbsf; 121 } 122 getConnectionIds()123 public String[] getConnectionIds() { 124 synchronized (connectionIds) { 125 return connectionIds.toArray(new String[connectionIds.size()]); 126 } 127 } 128 129 /** 130 * <p>Returns a client stub for this connector server. A client 131 * stub is a serializable object whose {@link 132 * JMXConnector#connect(Map) connect} method can be used to make 133 * one new connection to this connector server.</p> 134 * 135 * <p>A given connector need not support the generation of client 136 * stubs. However, the connectors specified by the JMX Remote API do 137 * (JMXMP Connector and RMI Connector).</p> 138 * 139 * <p>The default implementation of this method uses {@link 140 * #getAddress} and {@link JMXConnectorFactory} to generate the 141 * stub, with code equivalent to the following:</p> 142 * 143 * <pre> 144 * JMXServiceURL addr = {@link #getAddress() getAddress()}; 145 * return {@link JMXConnectorFactory#newJMXConnector(JMXServiceURL, Map) 146 * JMXConnectorFactory.newJMXConnector(addr, env)}; 147 * </pre> 148 * 149 * <p>A connector server for which this is inappropriate must 150 * override this method so that it either implements the 151 * appropriate logic or throws {@link 152 * UnsupportedOperationException}.</p> 153 * 154 * @param env client connection parameters of the same sort that 155 * could be provided to {@link JMXConnector#connect(Map) 156 * JMXConnector.connect(Map)}. Can be null, which is equivalent 157 * to an empty map. 158 * 159 * @return a client stub that can be used to make a new connection 160 * to this connector server. 161 * 162 * @exception UnsupportedOperationException if this connector 163 * server does not support the generation of client stubs. 164 * 165 * @exception IllegalStateException if the JMXConnectorServer is 166 * not started (see {@link JMXConnectorServerMBean#isActive()}). 167 * 168 * @exception IOException if a communications problem means that a 169 * stub cannot be created. 170 **/ toJMXConnector(Map<String,?> env)171 public JMXConnector toJMXConnector(Map<String,?> env) 172 throws IOException 173 { 174 if (!isActive()) throw new 175 IllegalStateException("Connector is not active"); 176 JMXServiceURL addr = getAddress(); 177 return JMXConnectorFactory.newJMXConnector(addr, env); 178 } 179 180 /** 181 * <p>Returns an array indicating the notifications that this MBean 182 * sends. The implementation in <code>JMXConnectorServer</code> 183 * returns an array with one element, indicating that it can emit 184 * notifications of class {@link JMXConnectionNotification} with 185 * the types defined in that class. A subclass that can emit other 186 * notifications should return an array that contains this element 187 * plus descriptions of the other notifications.</p> 188 * 189 * @return the array of possible notifications. 190 */ 191 @Override getNotificationInfo()192 public MBeanNotificationInfo[] getNotificationInfo() { 193 final String[] types = { 194 JMXConnectionNotification.OPENED, 195 JMXConnectionNotification.CLOSED, 196 JMXConnectionNotification.FAILED, 197 }; 198 final String className = JMXConnectionNotification.class.getName(); 199 final String description = 200 "A client connection has been opened or closed"; 201 return new MBeanNotificationInfo[] { 202 new MBeanNotificationInfo(types, className, description), 203 }; 204 } 205 206 /** 207 * <p>Called by a subclass when a new client connection is opened. 208 * Adds <code>connectionId</code> to the list returned by {@link 209 * #getConnectionIds()}, then emits a {@link 210 * JMXConnectionNotification} with type {@link 211 * JMXConnectionNotification#OPENED}.</p> 212 * 213 * @param connectionId the ID of the new connection. This must be 214 * different from the ID of any connection previously opened by 215 * this connector server. 216 * 217 * @param message the message for the emitted {@link 218 * JMXConnectionNotification}. Can be null. See {@link 219 * Notification#getMessage()}. 220 * 221 * @param userData the <code>userData</code> for the emitted 222 * {@link JMXConnectionNotification}. Can be null. See {@link 223 * Notification#getUserData()}. 224 * 225 * @exception NullPointerException if <code>connectionId</code> is 226 * null. 227 */ connectionOpened(String connectionId, String message, Object userData)228 protected void connectionOpened(String connectionId, 229 String message, 230 Object userData) { 231 232 if (connectionId == null) 233 throw new NullPointerException("Illegal null argument"); 234 235 synchronized (connectionIds) { 236 connectionIds.add(connectionId); 237 } 238 239 sendNotification(JMXConnectionNotification.OPENED, connectionId, 240 message, userData); 241 } 242 243 /** 244 * <p>Called by a subclass when a client connection is closed 245 * normally. Removes <code>connectionId</code> from the list returned 246 * by {@link #getConnectionIds()}, then emits a {@link 247 * JMXConnectionNotification} with type {@link 248 * JMXConnectionNotification#CLOSED}.</p> 249 * 250 * @param connectionId the ID of the closed connection. 251 * 252 * @param message the message for the emitted {@link 253 * JMXConnectionNotification}. Can be null. See {@link 254 * Notification#getMessage()}. 255 * 256 * @param userData the <code>userData</code> for the emitted 257 * {@link JMXConnectionNotification}. Can be null. See {@link 258 * Notification#getUserData()}. 259 * 260 * @exception NullPointerException if <code>connectionId</code> 261 * is null. 262 */ connectionClosed(String connectionId, String message, Object userData)263 protected void connectionClosed(String connectionId, 264 String message, 265 Object userData) { 266 267 if (connectionId == null) 268 throw new NullPointerException("Illegal null argument"); 269 270 synchronized (connectionIds) { 271 connectionIds.remove(connectionId); 272 } 273 274 sendNotification(JMXConnectionNotification.CLOSED, connectionId, 275 message, userData); 276 } 277 278 /** 279 * <p>Called by a subclass when a client connection fails. 280 * Removes <code>connectionId</code> from the list returned by 281 * {@link #getConnectionIds()}, then emits a {@link 282 * JMXConnectionNotification} with type {@link 283 * JMXConnectionNotification#FAILED}.</p> 284 * 285 * @param connectionId the ID of the failed connection. 286 * 287 * @param message the message for the emitted {@link 288 * JMXConnectionNotification}. Can be null. See {@link 289 * Notification#getMessage()}. 290 * 291 * @param userData the <code>userData</code> for the emitted 292 * {@link JMXConnectionNotification}. Can be null. See {@link 293 * Notification#getUserData()}. 294 * 295 * @exception NullPointerException if <code>connectionId</code> is 296 * null. 297 */ connectionFailed(String connectionId, String message, Object userData)298 protected void connectionFailed(String connectionId, 299 String message, 300 Object userData) { 301 302 if (connectionId == null) 303 throw new NullPointerException("Illegal null argument"); 304 305 synchronized (connectionIds) { 306 connectionIds.remove(connectionId); 307 } 308 309 sendNotification(JMXConnectionNotification.FAILED, connectionId, 310 message, userData); 311 } 312 sendNotification(String type, String connectionId, String message, Object userData)313 private void sendNotification(String type, String connectionId, 314 String message, Object userData) { 315 Notification notif = 316 new JMXConnectionNotification(type, 317 getNotificationSource(), 318 connectionId, 319 nextSequenceNumber(), 320 message, 321 userData); 322 sendNotification(notif); 323 } 324 getNotificationSource()325 private synchronized Object getNotificationSource() { 326 if (myName != null) 327 return myName; 328 else 329 return this; 330 } 331 nextSequenceNumber()332 private static long nextSequenceNumber() { 333 synchronized (sequenceNumberLock) { 334 return sequenceNumber++; 335 } 336 } 337 338 // implements MBeanRegistration 339 /** 340 * <p>Called by an MBean server when this connector server is 341 * registered in that MBean server. This connector server becomes 342 * attached to the MBean server and its {@link #getMBeanServer()} 343 * method will return <code>mbs</code>.</p> 344 * 345 * <p>If this connector server is already attached to an MBean 346 * server, this method has no effect. The MBean server it is 347 * attached to is not necessarily the one it is being registered 348 * in.</p> 349 * 350 * @param mbs the MBean server in which this connection server is 351 * being registered. 352 * 353 * @param name The object name of the MBean. 354 * 355 * @return The name under which the MBean is to be registered. 356 * 357 * @exception NullPointerException if <code>mbs</code> or 358 * <code>name</code> is null. 359 */ preRegister(MBeanServer mbs, ObjectName name)360 public synchronized ObjectName preRegister(MBeanServer mbs, 361 ObjectName name) { 362 if (mbs == null || name == null) 363 throw new NullPointerException("Null MBeanServer or ObjectName"); 364 if (mbeanServer == null) { 365 mbeanServer = mbs; 366 myName = name; 367 } 368 return name; 369 } 370 postRegister(Boolean registrationDone)371 public void postRegister(Boolean registrationDone) { 372 // do nothing 373 } 374 375 /** 376 * <p>Called by an MBean server when this connector server is 377 * unregistered from that MBean server. If this connector server 378 * was attached to that MBean server by being registered in it, 379 * and if the connector server is still active, 380 * then unregistering it will call the {@link #stop stop} method. 381 * If the <code>stop</code> method throws an exception, the 382 * unregistration attempt will fail. It is recommended to call 383 * the <code>stop</code> method explicitly before unregistering 384 * the MBean.</p> 385 * 386 * @exception IOException if thrown by the {@link #stop stop} method. 387 */ preDeregister()388 public synchronized void preDeregister() throws Exception { 389 if (myName != null && isActive()) { 390 stop(); 391 myName = null; // just in case stop is buggy and doesn't stop 392 } 393 } 394 postDeregister()395 public void postDeregister() { 396 myName = null; 397 } 398 399 /** 400 * The MBeanServer used by this server to execute a client request. 401 */ 402 private MBeanServer mbeanServer = null; 403 404 /** 405 * The name used to registered this server in an MBeanServer. 406 * It is null if the this server is not registered or has been unregistered. 407 */ 408 private ObjectName myName; 409 410 private final List<String> connectionIds = new ArrayList<String>(); 411 412 private static final int[] sequenceNumberLock = new int[0]; 413 private static long sequenceNumber; 414 } 415