Save This Page
Home » openjdk-7 » javax » management » remote » rmi » [javadoc | source]
    1   /*
    2    * Copyright 2002-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   package javax.management.remote.rmi;
   27   
   28   import com.sun.jmx.remote.internal.ArrayNotificationBuffer;
   29   import com.sun.jmx.remote.internal.NotificationBuffer;
   30   import com.sun.jmx.remote.security.JMXPluggableAuthenticator;
   31   import com.sun.jmx.remote.util.ClassLogger;
   32   
   33   import java.io.Closeable;
   34   import java.io.IOException;
   35   import java.lang.ref.WeakReference;
   36   import java.rmi.Remote;
   37   import java.rmi.server.RemoteServer;
   38   import java.rmi.server.ServerNotActiveException;
   39   import java.security.Principal;
   40   import java.util.ArrayList;
   41   import java.util.Collections;
   42   import java.util.Iterator;
   43   import java.util.List;
   44   import java.util.Map;
   45   import java.util.Set;
   46   
   47   import javax.management.MBeanServer;
   48   import javax.management.remote.JMXAuthenticator;
   49   import javax.management.remote.JMXConnectorServer;
   50   import javax.security.auth.Subject;
   51   
   52   /**
   53    * <p>An RMI object representing a connector server.  Remote clients
   54    * can make connections using the {@link #newClient(Object)} method.  This
   55    * method returns an RMI object representing the connection.</p>
   56    *
   57    * <p>User code does not usually reference this class directly.
   58    * RMI connection servers are usually created with the class {@link
   59    * RMIConnectorServer}.  Remote clients usually create connections
   60    * either with {@link javax.management.remote.JMXConnectorFactory}
   61    * or by instantiating {@link RMIConnector}.</p>
   62    *
   63    * <p>This is an abstract class.  Concrete subclasses define the
   64    * details of the client connection objects, such as whether they use
   65    * JRMP or IIOP.</p>
   66    *
   67    * @since 1.5
   68    */
   69   public abstract class RMIServerImpl implements Closeable, RMIServer {
   70       /**
   71        * <p>Constructs a new <code>RMIServerImpl</code>.</p>
   72        *
   73        * @param env the environment containing attributes for the new
   74        * <code>RMIServerImpl</code>.  Can be null, which is equivalent
   75        * to an empty Map.
   76        */
   77       public RMIServerImpl(Map<String,?> env) {
   78           this.env = (env == null) ? Collections.EMPTY_MAP : env;
   79       }
   80   
   81       void setRMIConnectorServer(RMIConnectorServer connServer)
   82               throws IOException {
   83           this.connServer = connServer;
   84       }
   85   
   86       /**
   87        * <p>Exports this RMI object.</p>
   88        *
   89        * @exception IOException if this RMI object cannot be exported.
   90        */
   91       protected abstract void export() throws IOException;
   92   
   93       /**
   94        * Returns a remotable stub for this server object.
   95        * @return a remotable stub.
   96        * @exception IOException if the stub cannot be obtained - e.g the
   97        *            RMIServerImpl has not been exported yet.
   98        **/
   99       public abstract Remote toStub() throws IOException;
  100   
  101       /**
  102        * <p>Sets the default <code>ClassLoader</code> for this connector
  103        * server. New client connections will use this classloader.
  104        * Existing client connections are unaffected.</p>
  105        *
  106        * @param cl the new <code>ClassLoader</code> to be used by this
  107        * connector server.
  108        *
  109        * @see #getDefaultClassLoader
  110        */
  111       public synchronized void setDefaultClassLoader(ClassLoader cl) {
  112           this.cl = cl;
  113       }
  114   
  115       /**
  116        * <p>Gets the default <code>ClassLoader</code> used by this connector
  117        * server.</p>
  118        *
  119        * @return the default <code>ClassLoader</code> used by this
  120        * connector server.</p>
  121        *
  122        * @see #setDefaultClassLoader
  123        */
  124       public synchronized ClassLoader getDefaultClassLoader() {
  125           return cl;
  126       }
  127   
  128       /**
  129        * <p>Sets the <code>MBeanServer</code> to which this connector
  130        * server is attached. New client connections will interact
  131        * with this <code>MBeanServer</code>. Existing client connections are
  132        * unaffected.</p>
  133        *
  134        * @param mbs the new <code>MBeanServer</code>.  Can be null, but
  135        * new client connections will be refused as long as it is.
  136        *
  137        * @see #getMBeanServer
  138        */
  139       public synchronized void setMBeanServer(MBeanServer mbs) {
  140           this.mbeanServer = mbs;
  141       }
  142   
  143       /**
  144        * <p>The <code>MBeanServer</code> to which this connector server
  145        * is attached.  This is the last value passed to {@link
  146        * #setMBeanServer} on this object, or null if that method has
  147        * never been called.</p>
  148        *
  149        * @return the <code>MBeanServer</code> to which this connector
  150        * is attached.
  151        *
  152        * @see #setMBeanServer
  153        */
  154       public synchronized MBeanServer getMBeanServer() {
  155           return mbeanServer;
  156       }
  157   
  158       public String getVersion() {
  159           // Expected format is: "protocol-version implementation-name"
  160           try {
  161               return "1.0 java_runtime_" +
  162                       System.getProperty("java.runtime.version");
  163           } catch (SecurityException e) {
  164               return "1.0 ";
  165           }
  166       }
  167   
  168       /**
  169        * <p>Creates a new client connection.  This method calls {@link
  170        * #makeClient makeClient} and adds the returned client connection
  171        * object to an internal list.  When this
  172        * <code>RMIServerImpl</code> is shut down via its {@link
  173        * #close()} method, the {@link RMIConnection#close() close()}
  174        * method of each object remaining in the list is called.</p>
  175        *
  176        * <p>The fact that a client connection object is in this internal
  177        * list does not prevent it from being garbage collected.</p>
  178        *
  179        * @param credentials this object specifies the user-defined
  180        * credentials to be passed in to the server in order to
  181        * authenticate the caller before creating the
  182        * <code>RMIConnection</code>.  Can be null.
  183        *
  184        * @return the newly-created <code>RMIConnection</code>.  This is
  185        * usually the object created by <code>makeClient</code>, though
  186        * an implementation may choose to wrap that object in another
  187        * object implementing <code>RMIConnection</code>.
  188        *
  189        * @exception IOException if the new client object cannot be
  190        * created or exported.
  191        *
  192        * @exception SecurityException if the given credentials do not allow
  193        * the server to authenticate the user successfully.
  194        *
  195        * @exception IllegalStateException if {@link #getMBeanServer()}
  196        * is null.
  197        */
  198       public RMIConnection newClient(Object credentials) throws IOException {
  199           return doNewClient(credentials);
  200       }
  201   
  202       /**
  203        * This method could be overridden by subclasses defined in this package
  204        * to perform additional operations specific to the underlying transport
  205        * before creating the new client connection.
  206        */
  207       RMIConnection doNewClient(Object credentials) throws IOException {
  208           final boolean tracing = logger.traceOn();
  209   
  210           if (tracing) logger.trace("newClient","making new client");
  211   
  212           if (getMBeanServer() == null)
  213               throw new IllegalStateException("Not attached to an MBean server");
  214   
  215           Subject subject = null;
  216           JMXAuthenticator authenticator =
  217               (JMXAuthenticator) env.get(JMXConnectorServer.AUTHENTICATOR);
  218           if (authenticator == null) {
  219               /*
  220                * Create the JAAS-based authenticator only if authentication
  221                * has been enabled
  222                */
  223               if (env.get("jmx.remote.x.password.file") != null ||
  224                   env.get("jmx.remote.x.login.config") != null) {
  225                   authenticator = new JMXPluggableAuthenticator(env);
  226               }
  227           }
  228           if (authenticator != null) {
  229               if (tracing) logger.trace("newClient","got authenticator: " +
  230                                  authenticator.getClass().getName());
  231               try {
  232                   subject = authenticator.authenticate(credentials);
  233               } catch (SecurityException e) {
  234                   logger.trace("newClient", "Authentication failed: " + e);
  235                   throw e;
  236               }
  237           }
  238   
  239           if (tracing) {
  240               if (subject != null)
  241                   logger.trace("newClient","subject is not null");
  242               else logger.trace("newClient","no subject");
  243           }
  244   
  245           final String connectionId = makeConnectionId(getProtocol(), subject);
  246   
  247           if (tracing)
  248               logger.trace("newClient","making new connection: " + connectionId);
  249   
  250           RMIConnection client = makeClient(connectionId, subject);
  251   
  252           connServer.connectionOpened(connectionId, "Connection opened", null);
  253   
  254           dropDeadReferences();
  255           WeakReference<RMIConnection> wr = new WeakReference<RMIConnection>(client);
  256           synchronized (clientList) {
  257               clientList.add(wr);
  258           }
  259   
  260           if (tracing)
  261               logger.trace("newClient","new connection done: " + connectionId );
  262   
  263           return client;
  264       }
  265   
  266       /**
  267        * <p>Creates a new client connection.  This method is called by
  268        * the public method {@link #newClient(Object)}.</p>
  269        *
  270        * @param connectionId the ID of the new connection.  Every
  271        * connection opened by this connector server will have a
  272        * different ID.  The behavior is unspecified if this parameter is
  273        * null.
  274        *
  275        * @param subject the authenticated subject.  Can be null.
  276        *
  277        * @return the newly-created <code>RMIConnection</code>.
  278        *
  279        * @exception IOException if the new client object cannot be
  280        * created or exported.
  281        */
  282       protected abstract RMIConnection makeClient(String connectionId,
  283                                                   Subject subject)
  284               throws IOException;
  285   
  286       /**
  287        * <p>Closes a client connection made by {@link #makeClient makeClient}.
  288        *
  289        * @param client a connection previously returned by
  290        * <code>makeClient</code> on which the <code>closeClient</code>
  291        * method has not previously been called.  The behavior is
  292        * unspecified if these conditions are violated, including the
  293        * case where <code>client</code> is null.
  294        *
  295        * @exception IOException if the client connection cannot be
  296        * closed.
  297        */
  298       protected abstract void closeClient(RMIConnection client)
  299               throws IOException;
  300   
  301       /**
  302        * <p>Returns the protocol string for this object.  The string is
  303        * <code>rmi</code> for RMI/JRMP and <code>iiop</code> for RMI/IIOP.
  304        *
  305        * @return the protocol string for this object.
  306        */
  307       protected abstract String getProtocol();
  308   
  309       /**
  310        * <p>Method called when a client connection created by {@link
  311        * #makeClient makeClient} is closed.  A subclass that defines
  312        * <code>makeClient</code> must arrange for this method to be
  313        * called when the resultant object's {@link RMIConnection#close()
  314        * close} method is called.  This enables it to be removed from
  315        * the <code>RMIServerImpl</code>'s list of connections.  It is
  316        * not an error for <code>client</code> not to be in that
  317        * list.</p>
  318        *
  319        * <p>After removing <code>client</code> from the list of
  320        * connections, this method calls {@link #closeClient
  321        * closeClient(client)}.</p>
  322        *
  323        * @param client the client connection that has been closed.
  324        *
  325        * @exception IOException if {@link #closeClient} throws this
  326        * exception.
  327        *
  328        * @exception NullPointerException if <code>client</code> is null.
  329        */
  330       protected void clientClosed(RMIConnection client) throws IOException {
  331           final boolean debug = logger.debugOn();
  332   
  333           if (debug) logger.trace("clientClosed","client="+client);
  334   
  335           if (client == null)
  336               throw new NullPointerException("Null client");
  337   
  338           synchronized (clientList) {
  339               dropDeadReferences();
  340               for (Iterator it = clientList.iterator(); it.hasNext(); ) {
  341                   WeakReference wr = (WeakReference) it.next();
  342                   if (wr.get() == client) {
  343                       it.remove();
  344                       break;
  345                   }
  346               }
  347               /* It is not a bug for this loop not to find the client.  In
  348                  our close() method, we remove a client from the list before
  349                  calling its close() method.  */
  350           }
  351   
  352           if (debug) logger.trace("clientClosed", "closing client.");
  353           closeClient(client);
  354   
  355           if (debug) logger.trace("clientClosed", "sending notif");
  356           connServer.connectionClosed(client.getConnectionId(),
  357                                       "Client connection closed", null);
  358   
  359           if (debug) logger.trace("clientClosed","done");
  360       }
  361   
  362       /**
  363        * <p>Closes this connection server.  This method first calls the
  364        * {@link #closeServer()} method so that no new client connections
  365        * will be accepted.  Then, for each remaining {@link
  366        * RMIConnection} object returned by {@link #makeClient
  367        * makeClient}, its {@link RMIConnection#close() close} method is
  368        * called.</p>
  369        *
  370        * <p>The behavior when this method is called more than once is
  371        * unspecified.</p>
  372        *
  373        * <p>If {@link #closeServer()} throws an
  374        * <code>IOException</code>, the individual connections are
  375        * nevertheless closed, and then the <code>IOException</code> is
  376        * thrown from this method.</p>
  377        *
  378        * <p>If {@link #closeServer()} returns normally but one or more
  379        * of the individual connections throws an
  380        * <code>IOException</code>, then, after closing all the
  381        * connections, one of those <code>IOException</code>s is thrown
  382        * from this method.  If more than one connection throws an
  383        * <code>IOException</code>, it is unspecified which one is thrown
  384        * from this method.</p>
  385        *
  386        * @exception IOException if {@link #closeServer()} or one of the
  387        * {@link RMIConnection#close()} calls threw
  388        * <code>IOException</code>.
  389        */
  390       public synchronized void close() throws IOException {
  391           final boolean tracing = logger.traceOn();
  392           final boolean debug   = logger.debugOn();
  393   
  394           if (tracing) logger.trace("close","closing");
  395   
  396           IOException ioException = null;
  397           try {
  398               if (debug)   logger.debug("close","closing Server");
  399               closeServer();
  400           } catch (IOException e) {
  401               if (tracing) logger.trace("close","Failed to close server: " + e);
  402               if (debug)   logger.debug("close",e);
  403               ioException = e;
  404           }
  405   
  406           if (debug)   logger.debug("close","closing Clients");
  407           // Loop to close all clients
  408           while (true) {
  409               synchronized (clientList) {
  410                   if (debug) logger.debug("close","droping dead references");
  411                   dropDeadReferences();
  412   
  413                   if (debug) logger.debug("close","client count: "+clientList.size());
  414                   if (clientList.size() == 0)
  415                       break;
  416                   /* Loop until we find a non-null client.  Because we called
  417                      dropDeadReferences(), this will usually be the first
  418                      element of the list, but a garbage collection could have
  419                      happened in between.  */
  420                   for (Iterator it = clientList.iterator(); it.hasNext(); ) {
  421                       WeakReference wr = (WeakReference) it.next();
  422                       RMIConnection client = (RMIConnection) wr.get();
  423                       it.remove();
  424                       if (client != null) {
  425                           try {
  426                               client.close();
  427                           } catch (IOException e) {
  428                               if (tracing)
  429                                   logger.trace("close","Failed to close client: " + e);
  430                               if (debug) logger.debug("close",e);
  431                               if (ioException == null)
  432                                   ioException = e;
  433                           }
  434                           break;
  435                       }
  436                   }
  437               }
  438           }
  439   
  440           if(notifBuffer != null)
  441               notifBuffer.dispose();
  442   
  443           if (ioException != null) {
  444               if (tracing) logger.trace("close","close failed.");
  445               throw ioException;
  446           }
  447   
  448           if (tracing) logger.trace("close","closed.");
  449       }
  450   
  451       /**
  452        * <p>Called by {@link #close()} to close the connector server.
  453        * After returning from this method, the connector server must
  454        * not accept any new connections.</p>
  455        *
  456        * @exception IOException if the attempt to close the connector
  457        * server failed.
  458        */
  459       protected abstract void closeServer() throws IOException;
  460   
  461       private static synchronized String makeConnectionId(String protocol,
  462                                                           Subject subject) {
  463           connectionIdNumber++;
  464   
  465           String clientHost = "";
  466           try {
  467               clientHost = RemoteServer.getClientHost();
  468           } catch (ServerNotActiveException e) {
  469               logger.trace("makeConnectionId", "getClientHost", e);
  470           }
  471   
  472           final StringBuilder buf = new StringBuilder();
  473           buf.append(protocol).append(":");
  474           if (clientHost.length() > 0)
  475               buf.append("//").append(clientHost);
  476           buf.append(" ");
  477           if (subject != null) {
  478               Set principals = subject.getPrincipals();
  479               String sep = "";
  480               for (Iterator it = principals.iterator(); it.hasNext(); ) {
  481                   Principal p = (Principal) it.next();
  482                   String name = p.getName().replace(' ', '_').replace(';', ':');
  483                   buf.append(sep).append(name);
  484                   sep = ";";
  485               }
  486           }
  487           buf.append(" ").append(connectionIdNumber);
  488           if (logger.traceOn())
  489               logger.trace("newConnectionId","connectionId="+buf);
  490           return buf.toString();
  491       }
  492   
  493       private void dropDeadReferences() {
  494           synchronized (clientList) {
  495               for (Iterator it = clientList.iterator(); it.hasNext(); ) {
  496                   WeakReference wr = (WeakReference) it.next();
  497                   if (wr.get() == null)
  498                       it.remove();
  499               }
  500           }
  501       }
  502   
  503       synchronized NotificationBuffer getNotifBuffer() {
  504           //Notification buffer is lazily created when the first client connects
  505           if(notifBuffer == null)
  506               notifBuffer =
  507                   ArrayNotificationBuffer.getNotificationBuffer(mbeanServer,
  508                                                                 env);
  509           return notifBuffer;
  510       }
  511   
  512       private static final ClassLogger logger =
  513           new ClassLogger("javax.management.remote.rmi", "RMIServerImpl");
  514   
  515       /** List of WeakReference values.  Each one references an
  516           RMIConnection created by this object, or null if the
  517           RMIConnection has been garbage-collected.  */
  518       private final List<WeakReference<RMIConnection>> clientList =
  519               new ArrayList<WeakReference<RMIConnection>>();
  520   
  521       private ClassLoader cl;
  522   
  523       private MBeanServer mbeanServer;
  524   
  525       private final Map env;
  526   
  527       private RMIConnectorServer connServer;
  528   
  529       private static int connectionIdNumber;
  530   
  531       private NotificationBuffer notifBuffer;
  532   }

Save This Page
Home » openjdk-7 » javax » management » remote » rmi » [javadoc | source]