KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > management > remote > rmi > RMIServerImpl


1 /*
2  * @(#)RMIServerImpl.java 1.58 07/08/14
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.management.remote.rmi;
9
10 import com.sun.jmx.remote.internal.ArrayNotificationBuffer;
11 import com.sun.jmx.remote.internal.NotificationBuffer;
12 import com.sun.jmx.remote.security.JMXPluggableAuthenticator;
13 import com.sun.jmx.remote.util.ClassLogger;
14 import java.io.IOException JavaDoc;
15 import java.lang.ref.WeakReference JavaDoc;
16 import java.rmi.Remote JavaDoc;
17 import java.rmi.server.RemoteServer JavaDoc;
18 import java.rmi.server.ServerNotActiveException JavaDoc;
19 import java.security.Principal JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collections JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Set JavaDoc;
26 import javax.management.MBeanServer JavaDoc;
27 import javax.management.remote.JMXAuthenticator JavaDoc;
28 import javax.management.remote.JMXConnectorServer JavaDoc;
29 import javax.security.auth.Subject JavaDoc;
30
31 /**
32  * <p>An RMI object representing a connector server. Remote clients
33  * can make connections using the {@link #newClient(Object)} method. This
34  * method returns an RMI object representing the connection.</p>
35  *
36  * <p>User code does not usually reference this class directly.
37  * RMI connection servers are usually created with the class {@link
38  * RMIConnectorServer}. Remote clients usually create connections
39  * either with {@link javax.management.remote.JMXConnectorFactory}
40  * or by instantiating {@link RMIConnector}.</p>
41  *
42  * <p>This is an abstract class. Concrete subclasses define the
43  * details of the client connection objects, such as whether they use
44  * JRMP or IIOP.</p>
45  *
46  * @since 1.5
47  * @since.unbundled 1.0
48  */

49 public abstract class RMIServerImpl implements RMIServer JavaDoc {
50     /**
51      * <p>Constructs a new <code>RMIServerImpl</code>.</p>
52      *
53      * @param env the environment containing attributes for the new
54      * <code>RMIServerImpl</code>. Can be null, which is equivalent
55      * to an empty Map.
56      */

57     public RMIServerImpl(Map JavaDoc<String JavaDoc,?> env) {
58         this.env = (env == null) ? Collections.EMPTY_MAP : env;
59     }
60
61     void setRMIConnectorServer(RMIConnectorServer JavaDoc connServer)
62         throws IOException JavaDoc {
63     this.connServer = connServer;
64     }
65
66     /**
67      * <p>Exports this RMI object.</p>
68      *
69      * @exception IOException if this RMI object cannot be exported.
70      */

71     protected abstract void export() throws IOException JavaDoc;
72
73     /**
74      * Returns a remotable stub for this server object.
75      * @return a remotable stub.
76      * @exception IOException if the stub cannot be obtained - e.g the
77      * RMIServerImpl has not been exported yet.
78      **/

79     public abstract Remote JavaDoc toStub() throws IOException JavaDoc;
80
81     /**
82      * <p>Sets the default <code>ClassLoader</code> for this connector
83      * server. New client connections will use this classloader.
84      * Existing client connections are unaffected.</p>
85      *
86      * @param cl the new <code>ClassLoader</code> to be used by this
87      * connector server.
88      *
89      * @see #getDefaultClassLoader
90      */

91     public synchronized void setDefaultClassLoader(ClassLoader JavaDoc cl) {
92         this.cl = cl;
93     }
94
95     /**
96      * <p>Gets the default <code>ClassLoader</code> used by this connector
97      * server.</p>
98      *
99      * @return the default <code>ClassLoader</code> used by this
100      * connector server.</p>
101      *
102      * @see #setDefaultClassLoader
103      */

104     public synchronized ClassLoader JavaDoc getDefaultClassLoader() {
105         return cl;
106     }
107
108     /**
109      * <p>Sets the <code>MBeanServer</code> to which this connector
110      * server is attached. New client connections will interact
111      * with this <code>MBeanServer</code>. Existing client connections are
112      * unaffected.</p>
113      *
114      * @param mbs the new <code>MBeanServer</code>. Can be null, but
115      * new client connections will be refused as long as it is.
116      *
117      * @see #getMBeanServer
118      */

119     public synchronized void setMBeanServer(MBeanServer JavaDoc mbs) {
120         this.mbeanServer = mbs;
121     }
122
123     /**
124      * <p>The <code>MBeanServer</code> to which this connector server
125      * is attached. This is the last value passed to {@link
126      * #setMBeanServer} on this object, or null if that method has
127      * never been called.</p>
128      *
129      * @return the <code>MBeanServer</code> to which this connector
130      * is attached.
131      *
132      * @see #setMBeanServer
133      */

134     public synchronized MBeanServer JavaDoc getMBeanServer() {
135         return mbeanServer;
136     }
137
138     public String JavaDoc getVersion() {
139         // Expected format is: "protocol-version implementation-name"
140
try {
141             return "1.0 java_runtime_" +
142                     System.getProperty("java.runtime.version");
143         } catch (SecurityException JavaDoc e) {
144             return "1.0 ";
145         }
146     }
147
148     /**
149      * <p>Creates a new client connection. This method calls {@link
150      * #makeClient makeClient} and adds the returned client connection
151      * object to an internal list. When this
152      * <code>RMIServerImpl</code> is shut down via its {@link
153      * #close()} method, the {@link RMIConnection#close() close()}
154      * method of each object remaining in the list is called.</p>
155      *
156      * <p>The fact that a client connection object is in this internal
157      * list does not prevent it from being garbage collected.</p>
158      *
159      * @param credentials this object specifies the user-defined
160      * credentials to be passed in to the server in order to
161      * authenticate the caller before creating the
162      * <code>RMIConnection</code>. Can be null.
163      *
164      * @return the newly-created <code>RMIConnection</code>. This is
165      * usually the object created by <code>makeClient</code>, though
166      * an implementation may choose to wrap that object in another
167      * object implementing <code>RMIConnection</code>.
168      *
169      * @exception IOException if the new client object cannot be
170      * created or exported.
171      *
172      * @exception SecurityException if the given credentials do not allow
173      * the server to authenticate the user successfully.
174      *
175      * @exception IllegalStateException if {@link #getMBeanServer()}
176      * is null.
177      */

178     public RMIConnection JavaDoc newClient(Object JavaDoc credentials) throws IOException JavaDoc {
179         return doNewClient(credentials);
180     }
181
182     /**
183      * This method could be overridden by subclasses defined in this package
184      * to perform additional operations specific to the underlying transport
185      * before creating the new client connection.
186      */

187     RMIConnection JavaDoc doNewClient(Object JavaDoc credentials) throws IOException JavaDoc {
188     final boolean tracing = logger.traceOn();
189
190     if (tracing) logger.trace("newClient","making new client");
191
192     if (getMBeanServer() == null)
193         throw new IllegalStateException JavaDoc("Not attached to an MBean server");
194
195         Subject JavaDoc subject = null;
196         JMXAuthenticator JavaDoc authenticator =
197             (JMXAuthenticator JavaDoc) env.get(JMXConnectorServer.AUTHENTICATOR);
198     if (authenticator == null) {
199         /*
200          * Create the JAAS-based authenticator only if authentication
201          * has been enabled
202          */

203         if (env.get("jmx.remote.x.password.file") != null ||
204         env.get("jmx.remote.x.login.config") != null) {
205         authenticator = new JMXPluggableAuthenticator(env);
206         }
207     }
208         if (authenticator != null) {
209         if (tracing) logger.trace("newClient","got authenticator: " +
210                    authenticator.getClass().getName());
211         try {
212         subject = authenticator.authenticate(credentials);
213         } catch (SecurityException JavaDoc e) {
214         logger.trace("newClient", "Authentication failed: " + e);
215         throw e;
216         }
217         }
218
219     if (tracing) {
220         if (subject != null)
221         logger.trace("newClient","subject is not null");
222         else logger.trace("newClient","no subject");
223     }
224
225     final String JavaDoc connectionId = makeConnectionId(getProtocol(), subject);
226
227     if (tracing)
228         logger.trace("newClient","making new connection: " + connectionId);
229
230         RMIConnection JavaDoc client = makeClient(connectionId, subject);
231
232     connServer.connectionOpened(connectionId, "Connection opened", null);
233
234         dropDeadReferences();
235         WeakReference JavaDoc wr = new WeakReference JavaDoc(client);
236         synchronized (clientList) {
237             clientList.add(wr);
238         }
239
240     if (tracing)
241         logger.trace("newClient","new connection done: " + connectionId );
242
243         return client;
244     }
245
246     /**
247      * <p>Creates a new client connection. This method is called by
248      * the public method {@link #newClient(Object)}.</p>
249      *
250      * @param connectionId the ID of the new connection. Every
251      * connection opened by this connector server will have a
252      * different ID. The behavior is unspecified if this parameter is
253      * null.
254      *
255      * @param subject the authenticated subject. Can be null.
256      *
257      * @return the newly-created <code>RMIConnection</code>.
258      *
259      * @exception IOException if the new client object cannot be
260      * created or exported.
261      */

262     protected abstract RMIConnection JavaDoc makeClient(String JavaDoc connectionId,
263                         Subject JavaDoc subject)
264         throws IOException JavaDoc;
265
266     /**
267      * <p>Closes a client connection made by {@link #makeClient makeClient}.
268      *
269      * @param client a connection previously returned by
270      * <code>makeClient</code> on which the <code>closeClient</code>
271      * method has not previously been called. The behavior is
272      * unspecified if these conditions are violated, including the
273      * case where <code>client</code> is null.
274      *
275      * @exception IOException if the client connection cannot be
276      * closed.
277      */

278     protected abstract void closeClient(RMIConnection JavaDoc client)
279         throws IOException JavaDoc;
280
281     /**
282      * <p>Returns the protocol string for this object. The string is
283      * <code>rmi</code> for RMI/JRMP and <code>iiop</code> for RMI/IIOP.
284      *
285      * @return the protocol string for this object.
286      */

287     protected abstract String JavaDoc getProtocol();
288
289     /**
290      * <p>Method called when a client connection created by {@link
291      * #makeClient makeClient} is closed. A subclass that defines
292      * <code>makeClient</code> must arrange for this method to be
293      * called when the resultant object's {@link RMIConnection#close()
294      * close} method is called. This enables it to be removed from
295      * the <code>RMIServerImpl</code>'s list of connections. It is
296      * not an error for <code>client</code> not to be in that
297      * list.</p>
298      *
299      * <p>After removing <code>client</code> from the list of
300      * connections, this method calls {@link #closeClient
301      * closeClient(client)}.</p>
302      *
303      * @param client the client connection that has been closed.
304      *
305      * @exception IOException if {@link #closeClient} throws this
306      * exception.
307      *
308      * @exception NullPointerException if <code>client</code> is null.
309      */

310     protected void clientClosed(RMIConnection JavaDoc client) throws IOException JavaDoc {
311     final boolean debug = logger.debugOn();
312
313     if (debug) logger.trace("clientClosed","client="+client);
314
315     if (client == null)
316         throw new NullPointerException JavaDoc("Null client");
317
318         synchronized (clientList) {
319             dropDeadReferences();
320             for (Iterator JavaDoc it = clientList.iterator(); it.hasNext(); ) {
321                 WeakReference JavaDoc wr = (WeakReference JavaDoc) it.next();
322                 if (wr.get() == client) {
323                     it.remove();
324                     break;
325                 }
326             }
327             /* It is not a bug for this loop not to find the client. In
328                our close() method, we remove a client from the list before
329                calling its close() method. */

330         }
331
332     if (debug) logger.trace("clientClosed", "closing client.");
333         closeClient(client);
334
335     if (debug) logger.trace("clientClosed", "sending notif");
336     connServer.connectionClosed(client.getConnectionId(),
337                     "Client connection closed", null);
338
339     if (debug) logger.trace("clientClosed","done");
340     }
341
342     /**
343      * <p>Closes this connection server. This method first calls the
344      * {@link #closeServer()} method so that no new client connections
345      * will be accepted. Then, for each remaining {@link
346      * RMIConnection} object returned by {@link #makeClient
347      * makeClient}, its {@link RMIConnection#close() close} method is
348      * called.</p>
349      *
350      * <p>The behavior when this method is called more than once is
351      * unspecified.</p>
352      *
353      * <p>If {@link #closeServer()} throws an
354      * <code>IOException</code>, the individual connections are
355      * nevertheless closed, and then the <code>IOException</code> is
356      * thrown from this method.</p>
357      *
358      * <p>If {@link #closeServer()} returns normally but one or more
359      * of the individual connections throws an
360      * <code>IOException</code>, then, after closing all the
361      * connections, one of those <code>IOException</code>s is thrown
362      * from this method. If more than one connection throws an
363      * <code>IOException</code>, it is unspecified which one is thrown
364      * from this method.</p>
365      *
366      * @exception IOException if {@link #closeServer()} or one of the
367      * {@link RMIConnection#close()} calls threw
368      * <code>IOException</code>.
369      */

370     public synchronized void close() throws IOException JavaDoc {
371     final boolean tracing = logger.traceOn();
372     final boolean debug = logger.debugOn();
373     
374     if (tracing) logger.trace("close","closing");
375
376         IOException JavaDoc ioException = null;
377         try {
378         if (debug) logger.debug("close","closing Server");
379             closeServer();
380         } catch (IOException JavaDoc e) {
381         if (tracing) logger.trace("close","Failed to close server: " + e);
382         if (debug) logger.debug("close",e);
383             ioException = e;
384         }
385
386     if (debug) logger.debug("close","closing Clients");
387         // Loop to close all clients
388
while (true) {
389             synchronized (clientList) {
390         if (debug) logger.debug("close","droping dead references");
391                 dropDeadReferences();
392
393         if (debug) logger.debug("close","client count: "+clientList.size());
394                 if (clientList.size() == 0)
395                     break;
396                 /* Loop until we find a non-null client. Because we called
397                    dropDeadReferences(), this will usually be the first
398                    element of the list, but a garbage collection could have
399                    happened in between. */

400                 for (Iterator JavaDoc it = clientList.iterator(); it.hasNext(); ) {
401                     WeakReference JavaDoc wr = (WeakReference JavaDoc) it.next();
402                     RMIConnection JavaDoc client = (RMIConnection JavaDoc) wr.get();
403                     it.remove();
404                     if (client != null) {
405                         try {
406                             client.close();
407                         } catch (IOException JavaDoc e) {
408                 if (tracing)
409                 logger.trace("close","Failed to close client: " + e);
410                 if (debug) logger.debug("close",e);
411                             if (ioException == null)
412                                 ioException = e;
413                         }
414                         break;
415                     }
416                 }
417             }
418         }
419
420     if(notifBuffer != null)
421         notifBuffer.dispose();
422     
423         if (ioException != null) {
424         if (tracing) logger.trace("close","close failed.");
425             throw ioException;
426     }
427
428     if (tracing) logger.trace("close","closed.");
429     }
430
431     /**
432      * <p>Called by {@link #close()} to close the connector server.
433      * After returning from this method, the connector server must
434      * not accept any new connections.</p>
435      *
436      * @exception IOException if the attempt to close the connector
437      * server failed.
438      */

439     protected abstract void closeServer() throws IOException JavaDoc;
440
441     private static synchronized String JavaDoc makeConnectionId(String JavaDoc protocol,
442                             Subject JavaDoc subject) {
443         connectionIdNumber++;
444
445     String JavaDoc clientHost = "";
446     try {
447         clientHost = RemoteServer.getClientHost();
448     } catch (ServerNotActiveException JavaDoc e) {
449         logger.trace("makeConnectionId", "getClientHost", e);
450     }
451
452     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
453     buf.append(protocol).append(":");
454     if (clientHost.length() > 0)
455         buf.append("//").append(clientHost);
456     buf.append(" ");
457     if (subject != null) {
458         Set JavaDoc principals = subject.getPrincipals();
459         String JavaDoc sep = "";
460         for (Iterator JavaDoc it = principals.iterator(); it.hasNext(); ) {
461         Principal JavaDoc p = (Principal JavaDoc) it.next();
462         String JavaDoc name = p.getName().replace(' ', '_').replace(';', ':');
463         buf.append(sep).append(name);
464         sep = ";";
465         }
466     }
467     buf.append(" ").append(connectionIdNumber);
468     if (logger.traceOn())
469         logger.trace("newConnectionId","connectionId="+buf);
470         return buf.toString();
471     }
472
473     private void dropDeadReferences() {
474         synchronized (clientList) {
475             for (Iterator JavaDoc it = clientList.iterator(); it.hasNext(); ) {
476                 WeakReference JavaDoc wr = (WeakReference JavaDoc) it.next();
477                 if (wr.get() == null)
478                     it.remove();
479             }
480         }
481     }
482     
483     synchronized NotificationBuffer getNotifBuffer() {
484     //Notification buffer is lazily created when the first client connects
485
if(notifBuffer == null)
486         notifBuffer =
487         ArrayNotificationBuffer.getNotificationBuffer(mbeanServer,
488                                   env);
489     return notifBuffer;
490     }
491     
492     private static final ClassLogger logger =
493     new ClassLogger("javax.management.remote.rmi", "RMIServerImpl");
494
495     /** List of WeakReference values. Each one references an
496         RMIConnection created by this object, or null if the
497         RMIConnection has been garbage-collected. */

498     private final List JavaDoc clientList = new ArrayList JavaDoc();
499
500     private ClassLoader JavaDoc cl;
501
502     private MBeanServer JavaDoc mbeanServer;
503
504     private final Map JavaDoc env;
505
506     private RMIConnectorServer JavaDoc connServer;
507
508     private static int connectionIdNumber;
509
510     private NotificationBuffer notifBuffer;
511 }
512
Popular Tags