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   
   29   import com.sun.jmx.remote.security.MBeanServerFileAccessController;
   30   import com.sun.jmx.remote.util.ClassLogger;
   31   import com.sun.jmx.remote.util.EnvHelp;
   32   
   33   import java.io.ByteArrayOutputStream;
   34   import java.io.IOException;
   35   import java.io.ObjectOutputStream;
   36   import java.net.MalformedURLException;
   37   import java.rmi.server.RMIClientSocketFactory;
   38   import java.rmi.server.RMIServerSocketFactory;
   39   import java.util.Collections;
   40   import java.util.HashMap;
   41   import java.util.HashSet;
   42   import java.util.Hashtable;
   43   import java.util.Map;
   44   import java.util.Set;
   45   
   46   import javax.management.InstanceNotFoundException;
   47   import javax.management.MBeanServer;
   48   
   49   import javax.management.remote.JMXConnectionNotification;
   50   import javax.management.remote.JMXConnector;
   51   import javax.management.remote.JMXConnectorServer;
   52   import javax.management.remote.JMXServiceURL;
   53   import javax.management.remote.MBeanServerForwarder;
   54   
   55   import javax.naming.InitialContext;
   56   import javax.naming.NamingException;
   57   
   58   /**
   59    * <p>A JMX API connector server that creates RMI-based connections
   60    * from remote clients.  Usually, such connector servers are made
   61    * using {@link javax.management.remote.JMXConnectorServerFactory
   62    * JMXConnectorServerFactory}.  However, specialized applications can
   63    * use this class directly, for example with an {@link RMIServerImpl}
   64    * object.</p>
   65    *
   66    * @since 1.5
   67    */
   68   public class RMIConnectorServer extends JMXConnectorServer {
   69       /**
   70        * <p>Name of the attribute that specifies whether the {@link
   71        * RMIServer} stub that represents an RMI connector server should
   72        * override an existing stub at the same address.  The value
   73        * associated with this attribute, if any, should be a string that
   74        * is equal, ignoring case, to <code>"true"</code> or
   75        * <code>"false"</code>.  The default value is false.</p>
   76        */
   77       public static final String JNDI_REBIND_ATTRIBUTE =
   78           "jmx.remote.jndi.rebind";
   79   
   80       /**
   81        * <p>Name of the attribute that specifies the {@link
   82        * RMIClientSocketFactory} for the RMI objects created in
   83        * conjunction with this connector. The value associated with this
   84        * attribute must be of type <code>RMIClientSocketFactory</code> and can
   85        * only be specified in the <code>Map</code> argument supplied when
   86        * creating a connector server.</p>
   87        */
   88       public static final String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE =
   89           "jmx.remote.rmi.client.socket.factory";
   90   
   91       /**
   92        * <p>Name of the attribute that specifies the {@link
   93        * RMIServerSocketFactory} for the RMI objects created in
   94        * conjunction with this connector. The value associated with this
   95        * attribute must be of type <code>RMIServerSocketFactory</code> and can
   96        * only be specified in the <code>Map</code> argument supplied when
   97        * creating a connector server.</p>
   98        */
   99       public static final String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE =
  100           "jmx.remote.rmi.server.socket.factory";
  101   
  102       /**
  103        * <p>Makes an <code>RMIConnectorServer</code>.
  104        * This is equivalent to calling {@link #RMIConnectorServer(
  105        * JMXServiceURL,Map,RMIServerImpl,MBeanServer)
  106        * RMIConnectorServer(directoryURL,environment,null,null)}</p>
  107        *
  108        * @param url the URL defining how to create the connector server.
  109        * Cannot be null.
  110        *
  111        * @param environment attributes governing the creation and
  112        * storing of the RMI object.  Can be null, which is equivalent to
  113        * an empty Map.
  114        *
  115        * @exception IllegalArgumentException if <code>url</code> is null.
  116        *
  117        * @exception MalformedURLException if <code>url</code> does not
  118        * conform to the syntax for an RMI connector, or if its protocol
  119        * is not recognized by this implementation. Only "rmi" and "iiop"
  120        * are valid when this constructor is used.
  121        *
  122        * @exception IOException if the connector server cannot be created
  123        * for some reason or if it is inevitable that its {@link #start()
  124        * start} method will fail.
  125        */
  126       public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment)
  127               throws IOException {
  128           this(url, environment, (MBeanServer) null);
  129       }
  130   
  131       /**
  132        * <p>Makes an <code>RMIConnectorServer</code> for the given MBean
  133        * server.
  134        * This is equivalent to calling {@link #RMIConnectorServer(
  135        * JMXServiceURL,Map,RMIServerImpl,MBeanServer)
  136        * RMIConnectorServer(directoryURL,environment,null,mbeanServer)}</p>
  137        *
  138        * @param url the URL defining how to create the connector server.
  139        * Cannot be null.
  140        *
  141        * @param environment attributes governing the creation and
  142        * storing of the RMI object.  Can be null, which is equivalent to
  143        * an empty Map.
  144        *
  145        * @param mbeanServer the MBean server to which the new connector
  146        * server is attached, or null if it will be attached by being
  147        * registered as an MBean in the MBean server.
  148        *
  149        * @exception IllegalArgumentException if <code>url</code> is null.
  150        *
  151        * @exception MalformedURLException if <code>url</code> does not
  152        * conform to the syntax for an RMI connector, or if its protocol
  153        * is not recognized by this implementation. Only "rmi" and "iiop"
  154        * are valid when this constructor is used.
  155        *
  156        * @exception IOException if the connector server cannot be created
  157        * for some reason or if it is inevitable that its {@link #start()
  158        * start} method will fail.
  159        */
  160       public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
  161                                 MBeanServer mbeanServer)
  162               throws IOException {
  163           this(url, environment, (RMIServerImpl) null, mbeanServer);
  164       }
  165   
  166       /**
  167        * <p>Makes an <code>RMIConnectorServer</code> for the given MBean
  168        * server.</p>
  169        *
  170        * @param url the URL defining how to create the connector server.
  171        * Cannot be null.
  172        *
  173        * @param environment attributes governing the creation and
  174        * storing of the RMI object.  Can be null, which is equivalent to
  175        * an empty Map.
  176        *
  177        * @param rmiServerImpl An implementation of the RMIServer interface,
  178        *  consistent with the protocol type specified in <var>url</var>.
  179        *  If this parameter is non null, the protocol type specified by
  180        *  <var>url</var> is not constrained, and is assumed to be valid.
  181        *  Otherwise, only "rmi" and "iiop" will be recognized.
  182        *
  183        * @param mbeanServer the MBean server to which the new connector
  184        * server is attached, or null if it will be attached by being
  185        * registered as an MBean in the MBean server.
  186        *
  187        * @exception IllegalArgumentException if <code>url</code> is null.
  188        *
  189        * @exception MalformedURLException if <code>url</code> does not
  190        * conform to the syntax for an RMI connector, or if its protocol
  191        * is not recognized by this implementation. Only "rmi" and "iiop"
  192        * are recognized when <var>rmiServerImpl</var> is null.
  193        *
  194        * @exception IOException if the connector server cannot be created
  195        * for some reason or if it is inevitable that its {@link #start()
  196        * start} method will fail.
  197        *
  198        * @see #start
  199        */
  200       public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
  201                                 RMIServerImpl rmiServerImpl,
  202                                 MBeanServer mbeanServer)
  203               throws IOException {
  204           super(mbeanServer);
  205   
  206           if (url == null) throw new
  207               IllegalArgumentException("Null JMXServiceURL");
  208           if (rmiServerImpl == null) {
  209               final String prt = url.getProtocol();
  210               if (prt == null || !(prt.equals("rmi") || prt.equals("iiop"))) {
  211                   final String msg = "Invalid protocol type: " + prt;
  212                   throw new MalformedURLException(msg);
  213               }
  214               final String urlPath = url.getURLPath();
  215               if (!urlPath.equals("")
  216                   && !urlPath.equals("/")
  217                   && !urlPath.startsWith("/jndi/")) {
  218                   final String msg = "URL path must be empty or start with " +
  219                       "/jndi/";
  220                   throw new MalformedURLException(msg);
  221               }
  222           }
  223   
  224           if (environment == null)
  225               this.attributes = Collections.emptyMap();
  226           else {
  227               EnvHelp.checkAttributes(environment);
  228               this.attributes = Collections.unmodifiableMap(environment);
  229           }
  230   
  231           this.address = url;
  232           this.rmiServerImpl = rmiServerImpl;
  233       }
  234   
  235       /**
  236        * <p>Returns a client stub for this connector server.  A client
  237        * stub is a serializable object whose {@link
  238        * JMXConnector#connect(Map) connect} method can be used to make
  239        * one new connection to this connector server.</p>
  240        *
  241        * @param env client connection parameters of the same sort that
  242        * could be provided to {@link JMXConnector#connect(Map)
  243        * JMXConnector.connect(Map)}.  Can be null, which is equivalent
  244        * to an empty map.
  245        *
  246        * @return a client stub that can be used to make a new connection
  247        * to this connector server.
  248        *
  249        * @exception UnsupportedOperationException if this connector
  250        * server does not support the generation of client stubs.
  251        *
  252        * @exception IllegalStateException if the JMXConnectorServer is
  253        * not started (see {@link #isActive()}).
  254        *
  255        * @exception IOException if a communications problem means that a
  256        * stub cannot be created.
  257        **/
  258       public JMXConnector toJMXConnector(Map<String,?> env) throws IOException {
  259           // The serialized for of rmiServerImpl is automatically
  260           // a RMI server stub.
  261           if (!isActive()) throw new
  262               IllegalStateException("Connector is not active");
  263   
  264           // Merge maps
  265           Map<String, Object> usemap = new HashMap<String, Object>(
  266                   (this.attributes==null)?Collections.<String, Object>emptyMap():
  267                       this.attributes);
  268   
  269           if (env != null) {
  270               EnvHelp.checkAttributes(env);
  271               usemap.putAll(env);
  272           }
  273   
  274           usemap = EnvHelp.filterAttributes(usemap);
  275   
  276           final RMIServer stub=(RMIServer)rmiServerImpl.toStub();
  277   
  278           return new RMIConnector(stub, usemap);
  279       }
  280   
  281       /**
  282        * <p>Activates the connector server, that is starts listening for
  283        * client connections.  Calling this method when the connector
  284        * server is already active has no effect.  Calling this method
  285        * when the connector server has been stopped will generate an
  286        * <code>IOException</code>.</p>
  287        *
  288        * <p>The behavior of this method when called for the first time
  289        * depends on the parameters that were supplied at construction,
  290        * as described below.</p>
  291        *
  292        * <p>First, an object of a subclass of {@link RMIServerImpl} is
  293        * required, to export the connector server through RMI:</p>
  294        *
  295        * <ul>
  296        *
  297        * <li>If an <code>RMIServerImpl</code> was supplied to the
  298        * constructor, it is used.
  299        *
  300        * <li>Otherwise, if the protocol part of the
  301        * <code>JMXServiceURL</code> supplied to the constructor was
  302        * <code>iiop</code>, an object of type {@link RMIIIOPServerImpl}
  303        * is created.
  304        *
  305        * <li>Otherwise, if the <code>JMXServiceURL</code>
  306        * was null, or its protocol part was <code>rmi</code>, an object
  307        * of type {@link RMIJRMPServerImpl} is created.
  308        *
  309        * <li>Otherwise, the implementation can create an
  310        * implementation-specific {@link RMIServerImpl} or it can throw
  311        * {@link MalformedURLException}.
  312        *
  313        * </ul>
  314        *
  315        * <p>If the given address includes a JNDI directory URL as
  316        * specified in the package documentation for {@link
  317        * javax.management.remote.rmi}, then this
  318        * <code>RMIConnectorServer</code> will bootstrap by binding the
  319        * <code>RMIServerImpl</code> to the given address.</p>
  320        *
  321        * <p>If the URL path part of the <code>JMXServiceURL</code> was
  322        * empty or a single slash (<code>/</code>), then the RMI object
  323        * will not be bound to a directory.  Instead, a reference to it
  324        * will be encoded in the URL path of the RMIConnectorServer
  325        * address (returned by {@link #getAddress()}).  The encodings for
  326        * <code>rmi</code> and <code>iiop</code> are described in the
  327        * package documentation for {@link
  328        * javax.management.remote.rmi}.</p>
  329        *
  330        * <p>The behavior when the URL path is neither empty nor a JNDI
  331        * directory URL, or when the protocol is neither <code>rmi</code>
  332        * nor <code>iiop</code>, is implementation defined, and may
  333        * include throwing {@link MalformedURLException} when the
  334        * connector server is created or when it is started.</p>
  335        *
  336        * @exception IllegalStateException if the connector server has
  337        * not been attached to an MBean server.
  338        * @exception IOException if the connector server cannot be
  339        * started.
  340        */
  341       public synchronized void start() throws IOException {
  342           final boolean tracing = logger.traceOn();
  343   
  344           if (state == STARTED) {
  345               if (tracing) logger.trace("start", "already started");
  346               return;
  347           } else if (state == STOPPED) {
  348               if (tracing) logger.trace("start", "already stopped");
  349               throw new IOException("The server has been stopped.");
  350           }
  351   
  352           if (getMBeanServer() == null)
  353               throw new IllegalStateException("This connector server is not " +
  354                                               "attached to an MBean server");
  355   
  356           // Check the internal access file property to see
  357           // if an MBeanServerForwarder is to be provided
  358           //
  359           if (attributes != null) {
  360               // Check if access file property is specified
  361               //
  362               String accessFile =
  363                   (String) attributes.get("jmx.remote.x.access.file");
  364               if (accessFile != null) {
  365                   // Access file property specified, create an instance
  366                   // of the MBeanServerFileAccessController class
  367                   //
  368                   MBeanServerForwarder mbsf;
  369                   try {
  370                       mbsf = new MBeanServerFileAccessController(accessFile);
  371                   } catch (IOException e) {
  372                       throw EnvHelp.initCause(
  373                           new IllegalArgumentException(e.getMessage()), e);
  374                   }
  375                   // Set the MBeanServerForwarder
  376                   //
  377                   setMBeanServerForwarder(mbsf);
  378               }
  379           }
  380   
  381           try {
  382               if (tracing) logger.trace("start", "setting default class loader");
  383               defaultClassLoader =
  384                   EnvHelp.resolveServerClassLoader(attributes, getMBeanServer());
  385           } catch (InstanceNotFoundException infc) {
  386               IllegalArgumentException x = new
  387                   IllegalArgumentException("ClassLoader not found: "+infc);
  388               throw EnvHelp.initCause(x,infc);
  389           }
  390   
  391           if (tracing) logger.trace("start", "setting RMIServer object");
  392           final RMIServerImpl rmiServer;
  393   
  394           if (rmiServerImpl != null)
  395               rmiServer = rmiServerImpl;
  396           else
  397               rmiServer = newServer();
  398   
  399           rmiServer.setMBeanServer(getMBeanServer());
  400           rmiServer.setDefaultClassLoader(defaultClassLoader);
  401           rmiServer.setRMIConnectorServer(this);
  402           rmiServer.export();
  403   
  404           try {
  405               if (tracing) logger.trace("start", "getting RMIServer object to export");
  406               final RMIServer objref = objectToBind(rmiServer, attributes);
  407   
  408               if (address != null && address.getURLPath().startsWith("/jndi/")) {
  409                   final String jndiUrl = address.getURLPath().substring(6);
  410   
  411                   if (tracing)
  412                       logger.trace("start", "Using external directory: " + jndiUrl);
  413   
  414                   final boolean rebind = EnvHelp.computeBooleanFromString(
  415                       attributes,
  416                       JNDI_REBIND_ATTRIBUTE);
  417   
  418                   if (tracing)
  419                       logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
  420   
  421                   try {
  422                       if (tracing) logger.trace("start", "binding to " + jndiUrl);
  423   
  424                       final Hashtable usemap = EnvHelp.mapToHashtable(attributes);
  425   
  426                       bind(jndiUrl, usemap, objref, rebind);
  427   
  428                       boundJndiUrl = jndiUrl;
  429                   } catch (NamingException e) {
  430                       // fit e in the nested exception if we are on 1.4
  431                       throw newIOException("Cannot bind to URL ["+jndiUrl+"]: "
  432                                            + e, e);
  433                   }
  434               } else {
  435                   // if jndiURL is null, we must encode the stub into the URL.
  436                   if (tracing) logger.trace("start", "Encoding URL");
  437   
  438                   encodeStubInAddress(objref, attributes);
  439   
  440                   if (tracing) logger.trace("start", "Encoded URL: " + this.address);
  441               }
  442           } catch (Exception e) {
  443               try {
  444                   rmiServer.close();
  445               } catch (Exception x) {
  446                   // OK: we are already throwing another exception
  447               }
  448               if (e instanceof RuntimeException)
  449                   throw (RuntimeException) e;
  450               else if (e instanceof IOException)
  451                   throw (IOException) e;
  452               else
  453                   throw newIOException("Got unexpected exception while " +
  454                                        "starting the connector server: "
  455                                        + e, e);
  456           }
  457   
  458           rmiServerImpl = rmiServer;
  459   
  460           synchronized(openedServers) {
  461               openedServers.add(this);
  462           }
  463   
  464           state = STARTED;
  465   
  466           if (tracing) {
  467               logger.trace("start", "Connector Server Address = " + address);
  468               logger.trace("start", "started.");
  469           }
  470       }
  471   
  472       /**
  473        * <p>Deactivates the connector server, that is, stops listening for
  474        * client connections.  Calling this method will also close all
  475        * client connections that were made by this server.  After this
  476        * method returns, whether normally or with an exception, the
  477        * connector server will not create any new client
  478        * connections.</p>
  479        *
  480        * <p>Once a connector server has been stopped, it cannot be started
  481        * again.</p>
  482        *
  483        * <p>Calling this method when the connector server has already
  484        * been stopped has no effect.  Calling this method when the
  485        * connector server has not yet been started will disable the
  486        * connector server object permanently.</p>
  487        *
  488        * <p>If closing a client connection produces an exception, that
  489        * exception is not thrown from this method.  A {@link
  490        * JMXConnectionNotification} is emitted from this MBean with the
  491        * connection ID of the connection that could not be closed.</p>
  492        *
  493        * <p>Closing a connector server is a potentially slow operation.
  494        * For example, if a client machine with an open connection has
  495        * crashed, the close operation might have to wait for a network
  496        * protocol timeout.  Callers that do not want to block in a close
  497        * operation should do it in a separate thread.</p>
  498        *
  499        * <p>This method calls the method {@link RMIServerImpl#close()
  500        * close} on the connector server's <code>RMIServerImpl</code>
  501        * object.</p>
  502        *
  503        * <p>If the <code>RMIServerImpl</code> was bound to a JNDI
  504        * directory by the {@link #start() start} method, it is unbound
  505        * from the directory by this method.</p>
  506        *
  507        * @exception IOException if the server cannot be closed cleanly,
  508        * or if the <code>RMIServerImpl</code> cannot be unbound from the
  509        * directory.  When this exception is thrown, the server has
  510        * already attempted to close all client connections, if
  511        * appropriate; to call {@link RMIServerImpl#close()}; and to
  512        * unbind the <code>RMIServerImpl</code> from its directory, if
  513        * appropriate.  All client connections are closed except possibly
  514        * those that generated exceptions when the server attempted to
  515        * close them.
  516        */
  517       public void stop() throws IOException {
  518           final boolean tracing = logger.traceOn();
  519   
  520           synchronized (this) {
  521               if (state == STOPPED) {
  522                   if (tracing) logger.trace("stop","already stopped.");
  523                   return;
  524               } else if (state == CREATED) {
  525                   if (tracing) logger.trace("stop","not started yet.");
  526               }
  527   
  528               if (tracing) logger.trace("stop", "stopping.");
  529               state = STOPPED;
  530           }
  531   
  532           synchronized(openedServers) {
  533               openedServers.remove(this);
  534           }
  535   
  536           IOException exception = null;
  537   
  538           // rmiServerImpl can be null if stop() called without start()
  539           if (rmiServerImpl != null) {
  540               try {
  541                   if (tracing) logger.trace("stop", "closing RMI server.");
  542                   rmiServerImpl.close();
  543               } catch (IOException e) {
  544                   if (tracing) logger.trace("stop", "failed to close RMI server: " + e);
  545                   if (logger.debugOn()) logger.debug("stop",e);
  546                   exception = e;
  547               }
  548           }
  549   
  550           if (boundJndiUrl != null) {
  551               try {
  552                   if (tracing)
  553                       logger.trace("stop",
  554                             "unbind from external directory: " + boundJndiUrl);
  555   
  556                   final Hashtable usemap = EnvHelp.mapToHashtable(attributes);
  557   
  558                   InitialContext ctx =
  559                       new InitialContext(usemap);
  560   
  561                   ctx.unbind(boundJndiUrl);
  562   
  563                   ctx.close();
  564               } catch (NamingException e) {
  565                   if (tracing) logger.trace("stop", "failed to unbind RMI server: "+e);
  566                   if (logger.debugOn()) logger.debug("stop",e);
  567                   // fit e in as the nested exception if we are on 1.4
  568                   if (exception == null)
  569                       exception = newIOException("Cannot bind to URL: " + e, e);
  570               }
  571           }
  572   
  573           if (exception != null) throw exception;
  574   
  575           if (tracing) logger.trace("stop", "stopped");
  576       }
  577   
  578       public synchronized boolean isActive() {
  579           return (state == STARTED);
  580       }
  581   
  582       public JMXServiceURL getAddress() {
  583           if (!isActive())
  584               return null;
  585           return address;
  586       }
  587   
  588       public Map<String,?> getAttributes() {
  589           Map<String, ?> map = EnvHelp.filterAttributes(attributes);
  590           return Collections.unmodifiableMap(map);
  591       }
  592   
  593       public synchronized
  594           void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
  595           super.setMBeanServerForwarder(mbsf);
  596           if (rmiServerImpl != null)
  597               rmiServerImpl.setMBeanServer(getMBeanServer());
  598       }
  599   
  600       /* We repeat the definitions of connection{Opened,Closed,Failed}
  601          here so that they are accessible to other classes in this package
  602          even though they have protected access.  */
  603   
  604       protected void connectionOpened(String connectionId, String message,
  605                                       Object userData) {
  606           super.connectionOpened(connectionId, message, userData);
  607       }
  608   
  609       protected void connectionClosed(String connectionId, String message,
  610                                       Object userData) {
  611           super.connectionClosed(connectionId, message, userData);
  612       }
  613   
  614       protected void connectionFailed(String connectionId, String message,
  615                                       Object userData) {
  616           super.connectionFailed(connectionId, message, userData);
  617       }
  618   
  619       /**
  620        * Bind a stub to a registry.
  621        * @param jndiUrl URL of the stub in the registry, extracted
  622        *        from the <code>JMXServiceURL</code>.
  623        * @param attributes A Hashtable containing environment parameters,
  624        *        built from the Map specified at this object creation.
  625        * @param rmiServer The object to bind in the registry
  626        * @param rebind true if the object must be rebound.
  627        **/
  628       void bind(String jndiUrl, Hashtable attributes,
  629                 RMIServer rmiServer, boolean rebind)
  630           throws NamingException, MalformedURLException {
  631           // if jndiURL is not null, we nust bind the stub to a
  632           // directory.
  633           InitialContext ctx =
  634               new InitialContext(attributes);
  635   
  636           if (rebind)
  637               ctx.rebind(jndiUrl, rmiServer);
  638           else
  639               ctx.bind(jndiUrl, rmiServer);
  640           ctx.close();
  641       }
  642   
  643       /**
  644        * Creates a new RMIServerImpl.
  645        **/
  646       RMIServerImpl newServer() throws IOException {
  647           final boolean iiop = isIiopURL(address,true);
  648           final int port;
  649           if (address == null)
  650               port = 0;
  651           else
  652               port = address.getPort();
  653           if (iiop)
  654               return newIIOPServer(attributes);
  655           else
  656               return newJRMPServer(attributes, port);
  657       }
  658   
  659       /**
  660        * Encode a stub into the JMXServiceURL.
  661        * @param rmiServer The stub object to encode in the URL
  662        * @param attributes A Map containing environment parameters,
  663        *        built from the Map specified at this object creation.
  664        **/
  665       private void encodeStubInAddress(RMIServer rmiServer, Map attributes)
  666               throws IOException {
  667   
  668           final String protocol, host;
  669           final int port;
  670   
  671           if (address == null) {
  672               if (rmiServer instanceof javax.rmi.CORBA.Stub)
  673                   protocol = "iiop";
  674               else
  675                   protocol = "rmi";
  676               host = null; // will default to local host name
  677               port = 0;
  678           } else {
  679               protocol = address.getProtocol();
  680               host = (address.getHost().equals("")) ? null : address.getHost();
  681               port = address.getPort();
  682           }
  683   
  684           final String urlPath = encodeStub(rmiServer, attributes);
  685   
  686           address = new JMXServiceURL(protocol, host, port, urlPath);
  687       }
  688   
  689       static boolean isIiopURL(JMXServiceURL directoryURL, boolean strict)
  690           throws MalformedURLException {
  691           String protocol = directoryURL.getProtocol();
  692           if (protocol.equals("rmi"))
  693               return false;
  694           else if (protocol.equals("iiop"))
  695               return true;
  696           else if (strict) {
  697   
  698               throw new MalformedURLException("URL must have protocol " +
  699                                               "\"rmi\" or \"iiop\": \"" +
  700                                               protocol + "\"");
  701           }
  702           return false;
  703       }
  704   
  705       /**
  706        * Returns the IOR of the given rmiServer.
  707        **/
  708       static String encodeStub(RMIServer rmiServer, Map env) throws IOException {
  709           if (rmiServer instanceof javax.rmi.CORBA.Stub)
  710               return "/ior/" + encodeIIOPStub(rmiServer, env);
  711           else
  712               return "/stub/" + encodeJRMPStub(rmiServer, env);
  713       }
  714   
  715       static String encodeJRMPStub(RMIServer rmiServer, Map env)
  716               throws IOException {
  717           ByteArrayOutputStream bout = new ByteArrayOutputStream();
  718           ObjectOutputStream oout = new ObjectOutputStream(bout);
  719           oout.writeObject(rmiServer);
  720           oout.close();
  721           byte[] bytes = bout.toByteArray();
  722           return byteArrayToBase64(bytes);
  723       }
  724   
  725       static String encodeIIOPStub(RMIServer rmiServer, Map env)
  726               throws IOException {
  727           try {
  728               javax.rmi.CORBA.Stub stub =
  729                   (javax.rmi.CORBA.Stub) rmiServer;
  730               return stub._orb().object_to_string(stub);
  731           } catch (org.omg.CORBA.BAD_OPERATION x) {
  732               throw newIOException(x.getMessage(), x);
  733           }
  734       }
  735   
  736       /**
  737        * Object that we will bind to the registry.
  738        * This object is a stub connected to our RMIServerImpl.
  739        **/
  740       private static RMIServer objectToBind(RMIServerImpl rmiServer, Map env)
  741           throws IOException {
  742           return RMIConnector.
  743               connectStub((RMIServer)rmiServer.toStub(),env);
  744       }
  745   
  746       private static RMIServerImpl newJRMPServer(Map<String, ?> env, int port)
  747               throws IOException {
  748           RMIClientSocketFactory csf = (RMIClientSocketFactory)
  749               env.get(RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE);
  750           RMIServerSocketFactory ssf = (RMIServerSocketFactory)
  751               env.get(RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE);
  752           return new RMIJRMPServerImpl(port, csf, ssf, env);
  753       }
  754   
  755       private static RMIServerImpl newIIOPServer(Map<String, ?> env)
  756               throws IOException {
  757           return new RMIIIOPServerImpl(env);
  758       }
  759   
  760       private static String byteArrayToBase64(byte[] a) {
  761           int aLen = a.length;
  762           int numFullGroups = aLen/3;
  763           int numBytesInPartialGroup = aLen - 3*numFullGroups;
  764           int resultLen = 4*((aLen + 2)/3);
  765           final StringBuilder result = new StringBuilder(resultLen);
  766   
  767           // Translate all full groups from byte array elements to Base64
  768           int inCursor = 0;
  769           for (int i=0; i<numFullGroups; i++) {
  770               int byte0 = a[inCursor++] & 0xff;
  771               int byte1 = a[inCursor++] & 0xff;
  772               int byte2 = a[inCursor++] & 0xff;
  773               result.append(intToAlpha[byte0 >> 2]);
  774               result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
  775               result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]);
  776               result.append(intToAlpha[byte2 & 0x3f]);
  777           }
  778   
  779           // Translate partial group if present
  780           if (numBytesInPartialGroup != 0) {
  781               int byte0 = a[inCursor++] & 0xff;
  782               result.append(intToAlpha[byte0 >> 2]);
  783               if (numBytesInPartialGroup == 1) {
  784                   result.append(intToAlpha[(byte0 << 4) & 0x3f]);
  785                   result.append("==");
  786               } else {
  787                   // assert numBytesInPartialGroup == 2;
  788                   int byte1 = a[inCursor++] & 0xff;
  789                   result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
  790                   result.append(intToAlpha[(byte1 << 2)&0x3f]);
  791                   result.append('=');
  792               }
  793           }
  794           // assert inCursor == a.length;
  795           // assert result.length() == resultLen;
  796           return result.toString();
  797       }
  798   
  799       /**
  800        * This array is a lookup table that translates 6-bit positive integer
  801        * index values into their "Base64 Alphabet" equivalents as specified
  802        * in Table 1 of RFC 2045.
  803        */
  804       private static final char intToAlpha[] = {
  805           'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  806           'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  807           'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  808           'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  809           '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
  810       };
  811   
  812       /**
  813        * Construct a new IOException with a nested exception.
  814        * The nested exception is set only if JDK >= 1.4
  815        */
  816       private static IOException newIOException(String message,
  817                                                 Throwable cause) {
  818           final IOException x = new IOException(message);
  819           return EnvHelp.initCause(x,cause);
  820       }
  821   
  822   
  823       // Private variables
  824       // -----------------
  825   
  826       private static ClassLogger logger =
  827           new ClassLogger("javax.management.remote.rmi", "RMIConnectorServer");
  828   
  829       private JMXServiceURL address;
  830       private RMIServerImpl rmiServerImpl;
  831       private final Map<String, ?> attributes;
  832       private ClassLoader defaultClassLoader = null;
  833   
  834       private String boundJndiUrl;
  835   
  836       // state
  837       private static final int CREATED = 0;
  838       private static final int STARTED = 1;
  839       private static final int STOPPED = 2;
  840   
  841       private int state = CREATED;
  842       private final static Set<RMIConnectorServer> openedServers =
  843               new HashSet<RMIConnectorServer>();
  844   }

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