001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package javax.management.remote.rmi;
010:
011: import java.io.IOException;
012: import java.io.InvalidObjectException;
013: import java.io.ObjectInputStream;
014: import java.io.ObjectOutputStream;
015: import java.io.Serializable;
016: import java.util.HashMap;
017: import java.util.Map;
018: import javax.management.ListenerNotFoundException;
019: import javax.management.MBeanServerConnection;
020: import javax.management.NotificationFilter;
021: import javax.management.NotificationListener;
022: import javax.management.remote.JMXConnector;
023: import javax.management.remote.JMXConnectorFactory;
024: import javax.management.remote.JMXServiceURL;
025: import javax.security.auth.Subject;
026:
027: import mx4j.remote.ConnectionNotificationEmitter;
028: import mx4j.remote.ConnectionResolver;
029: import mx4j.remote.HeartBeat;
030: import mx4j.remote.RemoteNotificationClientHandler;
031: import mx4j.remote.rmi.ClientExceptionCatcher;
032: import mx4j.remote.rmi.ClientInvoker;
033: import mx4j.remote.rmi.ClientUnmarshaller;
034: import mx4j.remote.rmi.RMIHeartBeat;
035: import mx4j.remote.rmi.RMIRemoteNotificationClientHandler;
036:
037: /**
038: * @version $Revision: 1.25 $
039: */
040: public class RMIConnector implements JMXConnector, Serializable {
041: private static final long serialVersionUID = 817323035842634473l;
042:
043: /**
044: * @serial
045: */
046: private final JMXServiceURL jmxServiceURL;
047: /**
048: * @serial
049: */
050: private RMIServer rmiServer;
051:
052: private transient RMIConnection connection;
053: private transient boolean connected;
054: private transient boolean closed;
055: private transient ClassLoader defaultClassLoader;
056: private transient String connectionId;
057: private transient ConnectionNotificationEmitter emitter;
058: private transient HeartBeat heartbeat;
059: private transient RemoteNotificationClientHandler notificationHandler;
060:
061: public RMIConnector(JMXServiceURL url, Map environment) {
062: if (url == null)
063: throw new IllegalArgumentException(
064: "JMXServiceURL cannot be null");
065: this .jmxServiceURL = url;
066: this .rmiServer = null;
067: initialize(environment);
068: }
069:
070: public RMIConnector(RMIServer server, Map environment) {
071: if (server == null)
072: throw new IllegalArgumentException(
073: "RMIServer cannot be null");
074: this .jmxServiceURL = null;
075: this .rmiServer = server;
076: initialize(environment);
077: }
078:
079: private void initialize(Map environment) {
080: this .defaultClassLoader = findDefaultClassLoader(environment);
081: this .emitter = new ConnectionNotificationEmitter(this );
082: }
083:
084: private ClassLoader findDefaultClassLoader(Map environment) {
085: if (environment == null)
086: return null;
087: Object loader = environment
088: .get(JMXConnectorFactory.DEFAULT_CLASS_LOADER);
089: if (loader != null && !(loader instanceof ClassLoader))
090: throw new IllegalArgumentException("Environment property "
091: + JMXConnectorFactory.DEFAULT_CLASS_LOADER
092: + " must be a ClassLoader");
093: return (ClassLoader) loader;
094: }
095:
096: public void connect() throws IOException, SecurityException {
097: connect(null);
098: }
099:
100: public void connect(Map environment) throws IOException,
101: SecurityException {
102: synchronized (this ) {
103: if (connected)
104: return;
105: if (closed)
106: throw new IOException(
107: "This RMIConnector has already been closed");
108:
109: // Spec says, about default ClassLoader, to look here:
110: // 1. Environment at connect(); if null,
111: // 2. Environment at creation; if null,
112: // 3. Context classloader at connect().
113: ClassLoader loader = findDefaultClassLoader(environment);
114: if (loader != null)
115: defaultClassLoader = loader;
116: else if (defaultClassLoader == null)
117: defaultClassLoader = Thread.currentThread()
118: .getContextClassLoader();
119:
120: Map env = environment == null ? new HashMap() : environment;
121:
122: String protocol = jmxServiceURL.getProtocol();
123: ConnectionResolver resolver = ConnectionResolver
124: .newConnectionResolver(protocol, env);
125: if (resolver == null)
126: throw new IOException("Unsupported protocol: "
127: + protocol);
128: if (rmiServer == null)
129: rmiServer = (RMIServer) resolver.lookupClient(
130: jmxServiceURL, env);
131: rmiServer = (RMIServer) resolver.bindClient(rmiServer, env);
132:
133: Object credentials = env.get(CREDENTIALS);
134: this .connection = rmiServer.newClient(credentials);
135:
136: connected = true;
137: this .connectionId = connection.getConnectionId();
138:
139: this .heartbeat = new RMIHeartBeat(connection, emitter, env);
140: this .notificationHandler = new RMIRemoteNotificationClientHandler(
141: connection, emitter, heartbeat, env);
142:
143: this .heartbeat.start();
144: this .notificationHandler.start();
145: }
146:
147: emitter.sendConnectionNotificationOpened();
148: }
149:
150: public void close() throws IOException {
151: synchronized (this ) {
152: if (closed)
153: return;
154: connected = false;
155: closed = true;
156:
157: if (notificationHandler != null)
158: notificationHandler.stop();
159: if (heartbeat != null)
160: heartbeat.stop();
161: if (connection != null)
162: connection.close();
163:
164: connection = null;
165: rmiServer = null;
166: }
167:
168: emitter.sendConnectionNotificationClosed();
169: }
170:
171: public String getConnectionId() throws IOException {
172: return connectionId;
173: }
174:
175: public MBeanServerConnection getMBeanServerConnection()
176: throws IOException {
177: return getMBeanServerConnection(null);
178: }
179:
180: public MBeanServerConnection getMBeanServerConnection(
181: Subject delegate) throws IOException {
182: if (!connected)
183: throw new IOException("Connection has not been established");
184:
185: // TODO: here we hardcode the client invocation chain. Maybe worth remove this hardcoding ?
186: ClientInvoker invoker = new ClientInvoker(connection,
187: notificationHandler, delegate);
188: MBeanServerConnection unmarshaller = ClientUnmarshaller
189: .newInstance(invoker, defaultClassLoader);
190: MBeanServerConnection catcher = ClientExceptionCatcher
191: .newInstance(unmarshaller);
192:
193: return catcher;
194: }
195:
196: public void addConnectionNotificationListener(
197: NotificationListener listener, NotificationFilter filter,
198: Object handback) {
199: emitter.addNotificationListener(listener, filter, handback);
200: }
201:
202: public void removeConnectionNotificationListener(
203: NotificationListener listener)
204: throws ListenerNotFoundException {
205: emitter.removeNotificationListener(listener);
206: }
207:
208: public void removeConnectionNotificationListener(
209: NotificationListener listener, NotificationFilter filter,
210: Object handback) throws ListenerNotFoundException {
211: emitter.removeNotificationListener(listener, filter, handback);
212: }
213:
214: private void readObject(ObjectInputStream ois) throws IOException,
215: ClassNotFoundException {
216: ois.defaultReadObject();
217: if (jmxServiceURL == null && rmiServer == null)
218: throw new InvalidObjectException(
219: "Nor the JMXServiceURL nor the RMIServer were specified for this RMIConnector");
220: initialize(null);
221: }
222:
223: private void writeObject(ObjectOutputStream oos) throws IOException {
224: if (jmxServiceURL == null && rmiServer == null)
225: throw new InvalidObjectException(
226: "Nor the JMXServiceURL nor the RMIServer were specified for this RMIConnector");
227: oos.defaultWriteObject();
228: }
229: }
|