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