src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java
changeset 47216 71c04702a3d5
parent 45060 adbeae0f677e
child 58766 54ffb15c4839
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2002, 2017, Oracle and/or its affiliates. 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package javax.management.remote.rmi;
       
    27 
       
    28 import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
       
    29 import com.sun.jmx.remote.internal.ClientListenerInfo;
       
    30 import com.sun.jmx.remote.internal.ClientNotifForwarder;
       
    31 import com.sun.jmx.remote.internal.rmi.ProxyRef;
       
    32 import com.sun.jmx.remote.util.ClassLogger;
       
    33 import com.sun.jmx.remote.util.EnvHelp;
       
    34 import java.io.ByteArrayInputStream;
       
    35 import java.io.IOException;
       
    36 import java.io.InputStream;
       
    37 import java.io.InvalidObjectException;
       
    38 import java.io.ObjectInputStream;
       
    39 import java.io.ObjectStreamClass;
       
    40 import java.io.Serializable;
       
    41 import java.lang.module.ModuleDescriptor;
       
    42 import java.lang.ref.WeakReference;
       
    43 import java.lang.reflect.Constructor;
       
    44 import java.lang.reflect.InvocationHandler;
       
    45 import java.lang.reflect.InvocationTargetException;
       
    46 import java.lang.reflect.Proxy;
       
    47 import java.net.MalformedURLException;
       
    48 import java.rmi.MarshalledObject;
       
    49 import java.rmi.NoSuchObjectException;
       
    50 import java.rmi.Remote;
       
    51 import java.rmi.ServerException;
       
    52 import java.rmi.UnmarshalException;
       
    53 import java.rmi.server.RMIClientSocketFactory;
       
    54 import java.rmi.server.RemoteObject;
       
    55 import java.rmi.server.RemoteObjectInvocationHandler;
       
    56 import java.rmi.server.RemoteRef;
       
    57 import java.security.AccessController;
       
    58 import java.security.PrivilegedAction;
       
    59 import java.security.PrivilegedExceptionAction;
       
    60 import java.security.ProtectionDomain;
       
    61 import java.util.Arrays;
       
    62 import java.util.Collections;
       
    63 import java.util.HashMap;
       
    64 import java.util.Hashtable;
       
    65 import java.util.Map;
       
    66 import java.util.Objects;
       
    67 import java.util.Set;
       
    68 import java.util.WeakHashMap;
       
    69 import java.util.stream.Collectors;
       
    70 import javax.management.Attribute;
       
    71 import javax.management.AttributeList;
       
    72 import javax.management.AttributeNotFoundException;
       
    73 import javax.management.InstanceAlreadyExistsException;
       
    74 import javax.management.InstanceNotFoundException;
       
    75 import javax.management.IntrospectionException;
       
    76 import javax.management.InvalidAttributeValueException;
       
    77 import javax.management.ListenerNotFoundException;
       
    78 import javax.management.MBeanException;
       
    79 import javax.management.MBeanInfo;
       
    80 import javax.management.MBeanRegistrationException;
       
    81 import javax.management.MBeanServerConnection;
       
    82 import javax.management.MBeanServerDelegate;
       
    83 import javax.management.MBeanServerNotification;
       
    84 import javax.management.NotCompliantMBeanException;
       
    85 import javax.management.Notification;
       
    86 import javax.management.NotificationBroadcasterSupport;
       
    87 import javax.management.NotificationFilter;
       
    88 import javax.management.NotificationFilterSupport;
       
    89 import javax.management.NotificationListener;
       
    90 import javax.management.ObjectInstance;
       
    91 import javax.management.ObjectName;
       
    92 import javax.management.QueryExp;
       
    93 import javax.management.ReflectionException;
       
    94 import javax.management.remote.JMXConnectionNotification;
       
    95 import javax.management.remote.JMXConnector;
       
    96 import javax.management.remote.JMXConnectorFactory;
       
    97 import javax.management.remote.JMXServiceURL;
       
    98 import javax.management.remote.NotificationResult;
       
    99 import javax.management.remote.JMXAddressable;
       
   100 import javax.naming.InitialContext;
       
   101 import javax.naming.NamingException;
       
   102 import javax.rmi.ssl.SslRMIClientSocketFactory;
       
   103 import javax.security.auth.Subject;
       
   104 import jdk.internal.module.Modules;
       
   105 import sun.reflect.misc.ReflectUtil;
       
   106 import sun.rmi.server.UnicastRef2;
       
   107 import sun.rmi.transport.LiveRef;
       
   108 import java.io.NotSerializableException;
       
   109 
       
   110 import static java.lang.module.ModuleDescriptor.Modifier.SYNTHETIC;
       
   111 
       
   112 /**
       
   113  * <p>A connection to a remote RMI connector.  Usually, such
       
   114  * connections are made using {@link
       
   115  * javax.management.remote.JMXConnectorFactory JMXConnectorFactory}.
       
   116  * However, specialized applications can use this class directly, for
       
   117  * example with an {@link RMIServer} stub obtained without going
       
   118  * through JNDI.</p>
       
   119  *
       
   120  * @since 1.5
       
   121  */
       
   122 public class RMIConnector implements JMXConnector, Serializable, JMXAddressable {
       
   123 
       
   124     private static final ClassLogger logger =
       
   125             new ClassLogger("javax.management.remote.rmi", "RMIConnector");
       
   126 
       
   127     private static final long serialVersionUID = 817323035842634473L;
       
   128 
       
   129     static final class Util {
       
   130         private Util() {}
       
   131 
       
   132         /* This method can be used by code that is deliberately violating the
       
   133          * allowed checked casts.  Rather than marking the whole method containing
       
   134          * the code with @SuppressWarnings, you can use a call to this method for
       
   135          * the exact place where you need to escape the constraints.  Typically
       
   136          * you will "import static" this method and then write either
       
   137          *    X x = cast(y);
       
   138          * or, if that doesn't work (e.g. X is a type variable)
       
   139          *    Util.<X>cast(y);
       
   140          */
       
   141         @SuppressWarnings("unchecked")
       
   142         public static <T> T cast(Object x) {
       
   143             return (T) x;
       
   144         }
       
   145     }
       
   146 
       
   147     private RMIConnector(RMIServer rmiServer, JMXServiceURL address,
       
   148             Map<String, ?> environment) {
       
   149         if (rmiServer == null && address == null) throw new
       
   150                 IllegalArgumentException("rmiServer and jmxServiceURL both null");
       
   151         initTransients();
       
   152 
       
   153         this.rmiServer = rmiServer;
       
   154         this.jmxServiceURL = address;
       
   155         if (environment == null) {
       
   156             this.env = Collections.emptyMap();
       
   157         } else {
       
   158             EnvHelp.checkAttributes(environment);
       
   159             this.env = Collections.unmodifiableMap(environment);
       
   160         }
       
   161     }
       
   162 
       
   163     /**
       
   164      * <p>Constructs an {@code RMIConnector} that will connect
       
   165      * the RMI connector server with the given address.</p>
       
   166      *
       
   167      * <p>The address can refer directly to the connector server,
       
   168      * using the following syntax:</p>
       
   169      *
       
   170      * <pre>
       
   171      * service:jmx:rmi://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
       
   172      * </pre>
       
   173      *
       
   174      * <p>(Here, the square brackets {@code []} are not part of the
       
   175      * address but indicate that the host and port are optional.)</p>
       
   176      *
       
   177      * <p>The address can instead indicate where to find an RMI stub
       
   178      * through JNDI, using the following syntax:</p>
       
   179      *
       
   180      * <pre>
       
   181      * service:jmx:rmi://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
       
   182      * </pre>
       
   183      *
       
   184      * <p>An implementation may also recognize additional address
       
   185      * syntaxes, for example:</p>
       
   186      *
       
   187      * <pre>
       
   188      * service:jmx:iiop://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
       
   189      * </pre>
       
   190      *
       
   191      * @param url the address of the RMI connector server.
       
   192      *
       
   193      * @param environment additional attributes specifying how to make
       
   194      * the connection.  For JNDI-based addresses, these attributes can
       
   195      * usefully include JNDI attributes recognized by {@link
       
   196      * InitialContext#InitialContext(Hashtable) InitialContext}.  This
       
   197      * parameter can be null, which is equivalent to an empty Map.
       
   198      *
       
   199      * @exception IllegalArgumentException if {@code url}
       
   200      * is null.
       
   201      */
       
   202     public RMIConnector(JMXServiceURL url, Map<String,?> environment) {
       
   203         this(null, url, environment);
       
   204     }
       
   205 
       
   206     /**
       
   207      * <p>Constructs an {@code RMIConnector} using the given RMI stub.
       
   208      *
       
   209      * @param rmiServer an RMI stub representing the RMI connector server.
       
   210      * @param environment additional attributes specifying how to make
       
   211      * the connection.  This parameter can be null, which is
       
   212      * equivalent to an empty Map.
       
   213      *
       
   214      * @exception IllegalArgumentException if {@code rmiServer}
       
   215      * is null.
       
   216      */
       
   217     public RMIConnector(RMIServer rmiServer, Map<String,?> environment) {
       
   218         this(rmiServer, null, environment);
       
   219     }
       
   220 
       
   221     /**
       
   222      * <p>Returns a string representation of this object.  In general,
       
   223      * the {@code toString} method returns a string that
       
   224      * "textually represents" this object. The result should be a
       
   225      * concise but informative representation that is easy for a
       
   226      * person to read.</p>
       
   227      *
       
   228      * @return a String representation of this object.
       
   229      **/
       
   230     @Override
       
   231     public String toString() {
       
   232         final StringBuilder b = new StringBuilder(this.getClass().getName());
       
   233         b.append(":");
       
   234         if (rmiServer != null) {
       
   235             b.append(" rmiServer=").append(rmiServer.toString());
       
   236         }
       
   237         if (jmxServiceURL != null) {
       
   238             if (rmiServer!=null) b.append(",");
       
   239             b.append(" jmxServiceURL=").append(jmxServiceURL.toString());
       
   240         }
       
   241         return b.toString();
       
   242     }
       
   243 
       
   244     /**
       
   245      * <p>The address of this connector.</p>
       
   246      *
       
   247      * @return the address of this connector, or null if it
       
   248      * does not have one.
       
   249      *
       
   250      * @since 1.6
       
   251      */
       
   252     public JMXServiceURL getAddress() {
       
   253         return jmxServiceURL;
       
   254     }
       
   255 
       
   256     //--------------------------------------------------------------------
       
   257     // implements JMXConnector interface
       
   258     //--------------------------------------------------------------------
       
   259 
       
   260     /**
       
   261      * @throws IOException if the connection could not be made because of a
       
   262      *   communication problem
       
   263      */
       
   264     public void connect() throws IOException {
       
   265         connect(null);
       
   266     }
       
   267 
       
   268     /**
       
   269      * @throws IOException if the connection could not be made because of a
       
   270      *   communication problem
       
   271      */
       
   272     public synchronized void connect(Map<String,?> environment)
       
   273     throws IOException {
       
   274         final boolean tracing = logger.traceOn();
       
   275         String        idstr   = (tracing?"["+this.toString()+"]":null);
       
   276 
       
   277         if (terminated) {
       
   278             logger.trace("connect",idstr + " already closed.");
       
   279             throw new IOException("Connector closed");
       
   280         }
       
   281         if (connected) {
       
   282             logger.trace("connect",idstr + " already connected.");
       
   283             return;
       
   284         }
       
   285 
       
   286         try {
       
   287             if (tracing) logger.trace("connect",idstr + " connecting...");
       
   288 
       
   289             final Map<String, Object> usemap =
       
   290                     new HashMap<String, Object>((this.env==null) ?
       
   291                         Collections.<String, Object>emptyMap() : this.env);
       
   292 
       
   293 
       
   294             if (environment != null) {
       
   295                 EnvHelp.checkAttributes(environment);
       
   296                 usemap.putAll(environment);
       
   297             }
       
   298 
       
   299             // Get RMIServer stub from directory or URL encoding if needed.
       
   300             if (tracing) logger.trace("connect",idstr + " finding stub...");
       
   301             RMIServer stub = (rmiServer!=null)?rmiServer:
       
   302                 findRMIServer(jmxServiceURL, usemap);
       
   303 
       
   304             // Check for secure RMIServer stub if the corresponding
       
   305             // client-side environment property is set to "true".
       
   306             //
       
   307             String stringBoolean =  (String) usemap.get("jmx.remote.x.check.stub");
       
   308             boolean checkStub = EnvHelp.computeBooleanFromString(stringBoolean);
       
   309 
       
   310             if (checkStub) checkStub(stub, rmiServerImplStubClass);
       
   311 
       
   312             if (tracing) logger.trace("connect",idstr + " connecting stub...");
       
   313             idstr = (tracing?"["+this.toString()+"]":null);
       
   314 
       
   315             // Calling newClient on the RMIServer stub.
       
   316             if (tracing)
       
   317                 logger.trace("connect",idstr + " getting connection...");
       
   318             Object credentials = usemap.get(CREDENTIALS);
       
   319 
       
   320             try {
       
   321                 connection = getConnection(stub, credentials, checkStub);
       
   322             } catch (java.rmi.RemoteException re) {
       
   323                 throw re;
       
   324             }
       
   325 
       
   326             // Always use one of:
       
   327             //   ClassLoader provided in Map at connect time,
       
   328             //   or contextClassLoader at connect time.
       
   329             if (tracing)
       
   330                 logger.trace("connect",idstr + " getting class loader...");
       
   331             defaultClassLoader = EnvHelp.resolveClientClassLoader(usemap);
       
   332 
       
   333             usemap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
       
   334                     defaultClassLoader);
       
   335 
       
   336             rmiNotifClient = new RMINotifClient(defaultClassLoader, usemap);
       
   337 
       
   338             env = usemap;
       
   339             final long checkPeriod = EnvHelp.getConnectionCheckPeriod(usemap);
       
   340             communicatorAdmin = new RMIClientCommunicatorAdmin(checkPeriod);
       
   341 
       
   342             connected = true;
       
   343 
       
   344             // The connectionId variable is used in doStart(), when
       
   345             // reconnecting, to identify the "old" connection.
       
   346             //
       
   347             connectionId = getConnectionId();
       
   348 
       
   349             Notification connectedNotif =
       
   350                     new JMXConnectionNotification(JMXConnectionNotification.OPENED,
       
   351                     this,
       
   352                     connectionId,
       
   353                     clientNotifSeqNo++,
       
   354                     "Successful connection",
       
   355                     null);
       
   356             sendNotification(connectedNotif);
       
   357 
       
   358             if (tracing) logger.trace("connect",idstr + " done...");
       
   359         } catch (IOException e) {
       
   360             if (tracing)
       
   361                 logger.trace("connect",idstr + " failed to connect: " + e);
       
   362             throw e;
       
   363         } catch (RuntimeException e) {
       
   364             if (tracing)
       
   365                 logger.trace("connect",idstr + " failed to connect: " + e);
       
   366             throw e;
       
   367         } catch (NamingException e) {
       
   368             final String msg = "Failed to retrieve RMIServer stub: " + e;
       
   369             if (tracing) logger.trace("connect",idstr + " " + msg);
       
   370             throw EnvHelp.initCause(new IOException(msg),e);
       
   371         }
       
   372     }
       
   373 
       
   374     public synchronized String getConnectionId() throws IOException {
       
   375         if (terminated || !connected) {
       
   376             if (logger.traceOn())
       
   377                 logger.trace("getConnectionId","["+this.toString()+
       
   378                         "] not connected.");
       
   379 
       
   380             throw new IOException("Not connected");
       
   381         }
       
   382 
       
   383         // we do a remote call to have an IOException if the connection is broken.
       
   384         // see the bug 4939578
       
   385         return connection.getConnectionId();
       
   386     }
       
   387 
       
   388     public synchronized MBeanServerConnection getMBeanServerConnection()
       
   389     throws IOException {
       
   390         return getMBeanServerConnection(null);
       
   391     }
       
   392 
       
   393     public synchronized MBeanServerConnection
       
   394             getMBeanServerConnection(Subject delegationSubject)
       
   395             throws IOException {
       
   396 
       
   397         if (terminated) {
       
   398             if (logger.traceOn())
       
   399                 logger.trace("getMBeanServerConnection","[" + this.toString() +
       
   400                         "] already closed.");
       
   401             throw new IOException("Connection closed");
       
   402         } else if (!connected) {
       
   403             if (logger.traceOn())
       
   404                 logger.trace("getMBeanServerConnection","[" + this.toString() +
       
   405                         "] is not connected.");
       
   406             throw new IOException("Not connected");
       
   407         }
       
   408 
       
   409         return getConnectionWithSubject(delegationSubject);
       
   410     }
       
   411 
       
   412     public void
       
   413             addConnectionNotificationListener(NotificationListener listener,
       
   414             NotificationFilter filter,
       
   415             Object handback) {
       
   416         if (listener == null)
       
   417             throw new NullPointerException("listener");
       
   418         connectionBroadcaster.addNotificationListener(listener, filter,
       
   419                 handback);
       
   420     }
       
   421 
       
   422     public void
       
   423             removeConnectionNotificationListener(NotificationListener listener)
       
   424             throws ListenerNotFoundException {
       
   425         if (listener == null)
       
   426             throw new NullPointerException("listener");
       
   427         connectionBroadcaster.removeNotificationListener(listener);
       
   428     }
       
   429 
       
   430     public void
       
   431             removeConnectionNotificationListener(NotificationListener listener,
       
   432             NotificationFilter filter,
       
   433             Object handback)
       
   434             throws ListenerNotFoundException {
       
   435         if (listener == null)
       
   436             throw new NullPointerException("listener");
       
   437         connectionBroadcaster.removeNotificationListener(listener, filter,
       
   438                 handback);
       
   439     }
       
   440 
       
   441     private void sendNotification(Notification n) {
       
   442         connectionBroadcaster.sendNotification(n);
       
   443     }
       
   444 
       
   445     public synchronized void close() throws IOException {
       
   446         close(false);
       
   447     }
       
   448 
       
   449     // allows to do close after setting the flag "terminated" to true.
       
   450     // It is necessary to avoid a deadlock, see 6296324
       
   451     private synchronized void close(boolean intern) throws IOException {
       
   452         final boolean tracing = logger.traceOn();
       
   453         final boolean debug   = logger.debugOn();
       
   454         final String  idstr   = (tracing?"["+this.toString()+"]":null);
       
   455 
       
   456         if (!intern) {
       
   457             // Return if already cleanly closed.
       
   458             //
       
   459             if (terminated) {
       
   460                 if (closeException == null) {
       
   461                     if (tracing) logger.trace("close",idstr + " already closed.");
       
   462                     return;
       
   463                 }
       
   464             } else {
       
   465                 terminated = true;
       
   466             }
       
   467         }
       
   468 
       
   469         if (closeException != null && tracing) {
       
   470             // Already closed, but not cleanly. Attempt again.
       
   471             //
       
   472             if (tracing) {
       
   473                 logger.trace("close",idstr + " had failed: " + closeException);
       
   474                 logger.trace("close",idstr + " attempting to close again.");
       
   475             }
       
   476         }
       
   477 
       
   478         String savedConnectionId = null;
       
   479         if (connected) {
       
   480             savedConnectionId = connectionId;
       
   481         }
       
   482 
       
   483         closeException = null;
       
   484 
       
   485         if (tracing) logger.trace("close",idstr + " closing.");
       
   486 
       
   487         if (communicatorAdmin != null) {
       
   488             communicatorAdmin.terminate();
       
   489         }
       
   490 
       
   491         if (rmiNotifClient != null) {
       
   492             try {
       
   493                 rmiNotifClient.terminate();
       
   494                 if (tracing) logger.trace("close",idstr +
       
   495                         " RMI Notification client terminated.");
       
   496             } catch (RuntimeException x) {
       
   497                 closeException = x;
       
   498                 if (tracing) logger.trace("close",idstr +
       
   499                         " Failed to terminate RMI Notification client: " + x);
       
   500                 if (debug) logger.debug("close",x);
       
   501             }
       
   502         }
       
   503 
       
   504         if (connection != null) {
       
   505             try {
       
   506                 connection.close();
       
   507                 if (tracing) logger.trace("close",idstr + " closed.");
       
   508             } catch (NoSuchObjectException nse) {
       
   509                 // OK, the server maybe closed itself.
       
   510             } catch (IOException e) {
       
   511                 closeException = e;
       
   512                 if (tracing) logger.trace("close",idstr +
       
   513                         " Failed to close RMIServer: " + e);
       
   514                 if (debug) logger.debug("close",e);
       
   515             }
       
   516         }
       
   517 
       
   518         // Clean up MBeanServerConnection table
       
   519         //
       
   520         rmbscMap.clear();
       
   521 
       
   522         /* Send notification of closure.  We don't do this if the user
       
   523          * never called connect() on the connector, because there's no
       
   524          * connection id in that case.  */
       
   525 
       
   526         if (savedConnectionId != null) {
       
   527             Notification closedNotif =
       
   528                     new JMXConnectionNotification(JMXConnectionNotification.CLOSED,
       
   529                     this,
       
   530                     savedConnectionId,
       
   531                     clientNotifSeqNo++,
       
   532                     "Client has been closed",
       
   533                     null);
       
   534             sendNotification(closedNotif);
       
   535         }
       
   536 
       
   537         // throw exception if needed
       
   538         //
       
   539         if (closeException != null) {
       
   540             if (tracing) logger.trace("close",idstr + " failed to close: " +
       
   541                     closeException);
       
   542             if (closeException instanceof IOException)
       
   543                 throw (IOException) closeException;
       
   544             if (closeException instanceof RuntimeException)
       
   545                 throw (RuntimeException) closeException;
       
   546             final IOException x =
       
   547                     new IOException("Failed to close: " + closeException);
       
   548             throw EnvHelp.initCause(x,closeException);
       
   549         }
       
   550     }
       
   551 
       
   552     // added for re-connection
       
   553     private Integer addListenerWithSubject(ObjectName name,
       
   554                                            MarshalledObject<NotificationFilter> filter,
       
   555                                            Subject delegationSubject,
       
   556                                            boolean reconnect)
       
   557         throws InstanceNotFoundException, IOException {
       
   558 
       
   559         final boolean debug = logger.debugOn();
       
   560         if (debug)
       
   561             logger.debug("addListenerWithSubject",
       
   562                     "(ObjectName,MarshalledObject,Subject)");
       
   563 
       
   564         final ObjectName[] names = new ObjectName[] {name};
       
   565         final MarshalledObject<NotificationFilter>[] filters =
       
   566                 Util.cast(new MarshalledObject<?>[] {filter});
       
   567         final Subject[] delegationSubjects = new Subject[] {
       
   568             delegationSubject
       
   569         };
       
   570 
       
   571         final Integer[] listenerIDs =
       
   572                 addListenersWithSubjects(names,filters,delegationSubjects,
       
   573                 reconnect);
       
   574 
       
   575         if (debug) logger.debug("addListenerWithSubject","listenerID="
       
   576                 + listenerIDs[0]);
       
   577         return listenerIDs[0];
       
   578     }
       
   579 
       
   580     // added for re-connection
       
   581     private Integer[] addListenersWithSubjects(ObjectName[]       names,
       
   582                              MarshalledObject<NotificationFilter>[] filters,
       
   583                              Subject[]          delegationSubjects,
       
   584                              boolean            reconnect)
       
   585         throws InstanceNotFoundException, IOException {
       
   586 
       
   587         final boolean debug = logger.debugOn();
       
   588         if (debug)
       
   589             logger.debug("addListenersWithSubjects",
       
   590                     "(ObjectName[],MarshalledObject[],Subject[])");
       
   591 
       
   592         final ClassLoader old = pushDefaultClassLoader();
       
   593         Integer[] listenerIDs = null;
       
   594 
       
   595         try {
       
   596             listenerIDs = connection.addNotificationListeners(names,
       
   597                     filters,
       
   598                     delegationSubjects);
       
   599         } catch (NoSuchObjectException noe) {
       
   600             // maybe reconnect
       
   601             if (reconnect) {
       
   602                 communicatorAdmin.gotIOException(noe);
       
   603 
       
   604                 listenerIDs = connection.addNotificationListeners(names,
       
   605                         filters,
       
   606                         delegationSubjects);
       
   607             } else {
       
   608                 throw noe;
       
   609             }
       
   610         } catch (IOException ioe) {
       
   611             // send a failed notif if necessary
       
   612             communicatorAdmin.gotIOException(ioe);
       
   613         } finally {
       
   614             popDefaultClassLoader(old);
       
   615         }
       
   616 
       
   617         if (debug) logger.debug("addListenersWithSubjects","registered "
       
   618                 + ((listenerIDs==null)?0:listenerIDs.length)
       
   619                 + " listener(s)");
       
   620         return listenerIDs;
       
   621     }
       
   622 
       
   623     //--------------------------------------------------------------------
       
   624     // Implementation of MBeanServerConnection
       
   625     //--------------------------------------------------------------------
       
   626     private class RemoteMBeanServerConnection implements MBeanServerConnection {
       
   627         private Subject delegationSubject;
       
   628 
       
   629         public RemoteMBeanServerConnection() {
       
   630             this(null);
       
   631         }
       
   632 
       
   633         public RemoteMBeanServerConnection(Subject delegationSubject) {
       
   634             this.delegationSubject = delegationSubject;
       
   635         }
       
   636 
       
   637         public ObjectInstance createMBean(String className,
       
   638                 ObjectName name)
       
   639                 throws ReflectionException,
       
   640                 InstanceAlreadyExistsException,
       
   641                 MBeanRegistrationException,
       
   642                 MBeanException,
       
   643                 NotCompliantMBeanException,
       
   644                 IOException {
       
   645             if (logger.debugOn())
       
   646                 logger.debug("createMBean(String,ObjectName)",
       
   647                         "className=" + className + ", name=" +
       
   648                         name);
       
   649 
       
   650             final ClassLoader old = pushDefaultClassLoader();
       
   651             try {
       
   652                 return connection.createMBean(className,
       
   653                         name,
       
   654                         delegationSubject);
       
   655             } catch (IOException ioe) {
       
   656                 communicatorAdmin.gotIOException(ioe);
       
   657 
       
   658                 return connection.createMBean(className,
       
   659                         name,
       
   660                         delegationSubject);
       
   661             } finally {
       
   662                 popDefaultClassLoader(old);
       
   663             }
       
   664         }
       
   665 
       
   666         public ObjectInstance createMBean(String className,
       
   667                 ObjectName name,
       
   668                 ObjectName loaderName)
       
   669                 throws ReflectionException,
       
   670                 InstanceAlreadyExistsException,
       
   671                 MBeanRegistrationException,
       
   672                 MBeanException,
       
   673                 NotCompliantMBeanException,
       
   674                 InstanceNotFoundException,
       
   675                 IOException {
       
   676 
       
   677             if (logger.debugOn())
       
   678                 logger.debug("createMBean(String,ObjectName,ObjectName)",
       
   679                         "className=" + className + ", name="
       
   680                         + name + ", loaderName="
       
   681                         + loaderName + ")");
       
   682 
       
   683             final ClassLoader old = pushDefaultClassLoader();
       
   684             try {
       
   685                 return connection.createMBean(className,
       
   686                         name,
       
   687                         loaderName,
       
   688                         delegationSubject);
       
   689 
       
   690             } catch (IOException ioe) {
       
   691                 communicatorAdmin.gotIOException(ioe);
       
   692 
       
   693                 return connection.createMBean(className,
       
   694                         name,
       
   695                         loaderName,
       
   696                         delegationSubject);
       
   697 
       
   698             } finally {
       
   699                 popDefaultClassLoader(old);
       
   700             }
       
   701         }
       
   702 
       
   703         public ObjectInstance createMBean(String className,
       
   704                 ObjectName name,
       
   705                 Object params[],
       
   706                 String signature[])
       
   707                 throws ReflectionException,
       
   708                 InstanceAlreadyExistsException,
       
   709                 MBeanRegistrationException,
       
   710                 MBeanException,
       
   711                 NotCompliantMBeanException,
       
   712                 IOException {
       
   713             if (logger.debugOn())
       
   714                 logger.debug("createMBean(String,ObjectName,Object[],String[])",
       
   715                         "className=" + className + ", name="
       
   716                         + name + ", signature=" + strings(signature));
       
   717 
       
   718             final MarshalledObject<Object[]> sParams =
       
   719                     new MarshalledObject<Object[]>(params);
       
   720             final ClassLoader old = pushDefaultClassLoader();
       
   721             try {
       
   722                 return connection.createMBean(className,
       
   723                         name,
       
   724                         sParams,
       
   725                         signature,
       
   726                         delegationSubject);
       
   727             } catch (IOException ioe) {
       
   728                 communicatorAdmin.gotIOException(ioe);
       
   729 
       
   730                 return connection.createMBean(className,
       
   731                         name,
       
   732                         sParams,
       
   733                         signature,
       
   734                         delegationSubject);
       
   735             } finally {
       
   736                 popDefaultClassLoader(old);
       
   737             }
       
   738         }
       
   739 
       
   740         public ObjectInstance createMBean(String className,
       
   741                 ObjectName name,
       
   742                 ObjectName loaderName,
       
   743                 Object params[],
       
   744                 String signature[])
       
   745                 throws ReflectionException,
       
   746                 InstanceAlreadyExistsException,
       
   747                 MBeanRegistrationException,
       
   748                 MBeanException,
       
   749                 NotCompliantMBeanException,
       
   750                 InstanceNotFoundException,
       
   751                 IOException {
       
   752             if (logger.debugOn()) logger.debug(
       
   753                     "createMBean(String,ObjectName,ObjectName,Object[],String[])",
       
   754                     "className=" + className + ", name=" + name + ", loaderName="
       
   755                     + loaderName + ", signature=" + strings(signature));
       
   756 
       
   757             final MarshalledObject<Object[]> sParams =
       
   758                     new MarshalledObject<Object[]>(params);
       
   759             final ClassLoader old = pushDefaultClassLoader();
       
   760             try {
       
   761                 return connection.createMBean(className,
       
   762                         name,
       
   763                         loaderName,
       
   764                         sParams,
       
   765                         signature,
       
   766                         delegationSubject);
       
   767             } catch (IOException ioe) {
       
   768                 communicatorAdmin.gotIOException(ioe);
       
   769 
       
   770                 return connection.createMBean(className,
       
   771                         name,
       
   772                         loaderName,
       
   773                         sParams,
       
   774                         signature,
       
   775                         delegationSubject);
       
   776             } finally {
       
   777                 popDefaultClassLoader(old);
       
   778             }
       
   779         }
       
   780 
       
   781         public void unregisterMBean(ObjectName name)
       
   782         throws InstanceNotFoundException,
       
   783                 MBeanRegistrationException,
       
   784                 IOException {
       
   785             if (logger.debugOn())
       
   786                 logger.debug("unregisterMBean", "name=" + name);
       
   787 
       
   788             final ClassLoader old = pushDefaultClassLoader();
       
   789             try {
       
   790                 connection.unregisterMBean(name, delegationSubject);
       
   791             } catch (IOException ioe) {
       
   792                 communicatorAdmin.gotIOException(ioe);
       
   793 
       
   794                 connection.unregisterMBean(name, delegationSubject);
       
   795             } finally {
       
   796                 popDefaultClassLoader(old);
       
   797             }
       
   798         }
       
   799 
       
   800         public ObjectInstance getObjectInstance(ObjectName name)
       
   801         throws InstanceNotFoundException,
       
   802                 IOException {
       
   803             if (logger.debugOn())
       
   804                 logger.debug("getObjectInstance", "name=" + name);
       
   805 
       
   806             final ClassLoader old = pushDefaultClassLoader();
       
   807             try {
       
   808                 return connection.getObjectInstance(name, delegationSubject);
       
   809             } catch (IOException ioe) {
       
   810                 communicatorAdmin.gotIOException(ioe);
       
   811 
       
   812                 return connection.getObjectInstance(name, delegationSubject);
       
   813             } finally {
       
   814                 popDefaultClassLoader(old);
       
   815             }
       
   816         }
       
   817 
       
   818         public Set<ObjectInstance> queryMBeans(ObjectName name,
       
   819                 QueryExp query)
       
   820                 throws IOException {
       
   821             if (logger.debugOn()) logger.debug("queryMBeans",
       
   822                     "name=" + name + ", query=" + query);
       
   823 
       
   824             final MarshalledObject<QueryExp> sQuery =
       
   825                     new MarshalledObject<QueryExp>(query);
       
   826             final ClassLoader old = pushDefaultClassLoader();
       
   827             try {
       
   828                 return connection.queryMBeans(name, sQuery, delegationSubject);
       
   829             } catch (IOException ioe) {
       
   830                 communicatorAdmin.gotIOException(ioe);
       
   831 
       
   832                 return connection.queryMBeans(name, sQuery, delegationSubject);
       
   833             } finally {
       
   834                 popDefaultClassLoader(old);
       
   835             }
       
   836         }
       
   837 
       
   838         public Set<ObjectName> queryNames(ObjectName name,
       
   839                 QueryExp query)
       
   840                 throws IOException {
       
   841             if (logger.debugOn()) logger.debug("queryNames",
       
   842                     "name=" + name + ", query=" + query);
       
   843 
       
   844             final MarshalledObject<QueryExp> sQuery =
       
   845                     new MarshalledObject<QueryExp>(query);
       
   846             final ClassLoader old = pushDefaultClassLoader();
       
   847             try {
       
   848                 return connection.queryNames(name, sQuery, delegationSubject);
       
   849             } catch (IOException ioe) {
       
   850                 communicatorAdmin.gotIOException(ioe);
       
   851 
       
   852                 return connection.queryNames(name, sQuery, delegationSubject);
       
   853             } finally {
       
   854                 popDefaultClassLoader(old);
       
   855             }
       
   856         }
       
   857 
       
   858         public boolean isRegistered(ObjectName name)
       
   859         throws IOException {
       
   860             if (logger.debugOn())
       
   861                 logger.debug("isRegistered", "name=" + name);
       
   862 
       
   863             final ClassLoader old = pushDefaultClassLoader();
       
   864             try {
       
   865                 return connection.isRegistered(name, delegationSubject);
       
   866             } catch (IOException ioe) {
       
   867                 communicatorAdmin.gotIOException(ioe);
       
   868 
       
   869                 return connection.isRegistered(name, delegationSubject);
       
   870             } finally {
       
   871                 popDefaultClassLoader(old);
       
   872             }
       
   873         }
       
   874 
       
   875         public Integer getMBeanCount()
       
   876         throws IOException {
       
   877             if (logger.debugOn()) logger.debug("getMBeanCount", "");
       
   878 
       
   879             final ClassLoader old = pushDefaultClassLoader();
       
   880             try {
       
   881                 return connection.getMBeanCount(delegationSubject);
       
   882             } catch (IOException ioe) {
       
   883                 communicatorAdmin.gotIOException(ioe);
       
   884 
       
   885                 return connection.getMBeanCount(delegationSubject);
       
   886             } finally {
       
   887                 popDefaultClassLoader(old);
       
   888             }
       
   889         }
       
   890 
       
   891         public Object getAttribute(ObjectName name,
       
   892                 String attribute)
       
   893                 throws MBeanException,
       
   894                 AttributeNotFoundException,
       
   895                 InstanceNotFoundException,
       
   896                 ReflectionException,
       
   897                 IOException {
       
   898             if (logger.debugOn()) logger.debug("getAttribute",
       
   899                     "name=" + name + ", attribute="
       
   900                     + attribute);
       
   901 
       
   902             final ClassLoader old = pushDefaultClassLoader();
       
   903             try {
       
   904                 return connection.getAttribute(name,
       
   905                         attribute,
       
   906                         delegationSubject);
       
   907             } catch (IOException ioe) {
       
   908                 communicatorAdmin.gotIOException(ioe);
       
   909 
       
   910                 return connection.getAttribute(name,
       
   911                         attribute,
       
   912                         delegationSubject);
       
   913             } finally {
       
   914                 popDefaultClassLoader(old);
       
   915             }
       
   916         }
       
   917 
       
   918         public AttributeList getAttributes(ObjectName name,
       
   919                 String[] attributes)
       
   920                 throws InstanceNotFoundException,
       
   921                 ReflectionException,
       
   922                 IOException {
       
   923             if (logger.debugOn()) logger.debug("getAttributes",
       
   924                     "name=" + name + ", attributes="
       
   925                     + strings(attributes));
       
   926 
       
   927             final ClassLoader old = pushDefaultClassLoader();
       
   928             try {
       
   929                 return connection.getAttributes(name,
       
   930                         attributes,
       
   931                         delegationSubject);
       
   932 
       
   933             } catch (IOException ioe) {
       
   934                 communicatorAdmin.gotIOException(ioe);
       
   935 
       
   936                 return connection.getAttributes(name,
       
   937                         attributes,
       
   938                         delegationSubject);
       
   939             } finally {
       
   940                 popDefaultClassLoader(old);
       
   941             }
       
   942         }
       
   943 
       
   944 
       
   945         public void setAttribute(ObjectName name,
       
   946                 Attribute attribute)
       
   947                 throws InstanceNotFoundException,
       
   948                 AttributeNotFoundException,
       
   949                 InvalidAttributeValueException,
       
   950                 MBeanException,
       
   951                 ReflectionException,
       
   952                 IOException {
       
   953 
       
   954             if (logger.debugOn()) logger.debug("setAttribute",
       
   955                     "name=" + name + ", attribute name="
       
   956                     + attribute.getName());
       
   957 
       
   958             final MarshalledObject<Attribute> sAttribute =
       
   959                     new MarshalledObject<Attribute>(attribute);
       
   960             final ClassLoader old = pushDefaultClassLoader();
       
   961             try {
       
   962                 connection.setAttribute(name, sAttribute, delegationSubject);
       
   963             } catch (IOException ioe) {
       
   964                 communicatorAdmin.gotIOException(ioe);
       
   965 
       
   966                 connection.setAttribute(name, sAttribute, delegationSubject);
       
   967             } finally {
       
   968                 popDefaultClassLoader(old);
       
   969             }
       
   970         }
       
   971 
       
   972         public AttributeList setAttributes(ObjectName name,
       
   973                 AttributeList attributes)
       
   974                 throws InstanceNotFoundException,
       
   975                 ReflectionException,
       
   976                 IOException {
       
   977 
       
   978             if (logger.debugOn()) {
       
   979                 logger.debug("setAttributes",
       
   980                     "name=" + name + ", attribute names="
       
   981                     + getAttributesNames(attributes));
       
   982             }
       
   983 
       
   984             final MarshalledObject<AttributeList> sAttributes =
       
   985                     new MarshalledObject<AttributeList>(attributes);
       
   986             final ClassLoader old = pushDefaultClassLoader();
       
   987             try {
       
   988                 return connection.setAttributes(name,
       
   989                         sAttributes,
       
   990                         delegationSubject);
       
   991             } catch (IOException ioe) {
       
   992                 communicatorAdmin.gotIOException(ioe);
       
   993 
       
   994                 return connection.setAttributes(name,
       
   995                         sAttributes,
       
   996                         delegationSubject);
       
   997             } finally {
       
   998                 popDefaultClassLoader(old);
       
   999             }
       
  1000         }
       
  1001 
       
  1002 
       
  1003         public Object invoke(ObjectName name,
       
  1004                 String operationName,
       
  1005                 Object params[],
       
  1006                 String signature[])
       
  1007                 throws InstanceNotFoundException,
       
  1008                 MBeanException,
       
  1009                 ReflectionException,
       
  1010                 IOException {
       
  1011 
       
  1012             if (logger.debugOn()) logger.debug("invoke",
       
  1013                     "name=" + name
       
  1014                     + ", operationName=" + operationName
       
  1015                     + ", signature=" + strings(signature));
       
  1016 
       
  1017             final MarshalledObject<Object[]> sParams =
       
  1018                     new MarshalledObject<Object[]>(params);
       
  1019             final ClassLoader old = pushDefaultClassLoader();
       
  1020             try {
       
  1021                 return connection.invoke(name,
       
  1022                         operationName,
       
  1023                         sParams,
       
  1024                         signature,
       
  1025                         delegationSubject);
       
  1026             } catch (IOException ioe) {
       
  1027                 communicatorAdmin.gotIOException(ioe);
       
  1028 
       
  1029                 return connection.invoke(name,
       
  1030                         operationName,
       
  1031                         sParams,
       
  1032                         signature,
       
  1033                         delegationSubject);
       
  1034             } finally {
       
  1035                 popDefaultClassLoader(old);
       
  1036             }
       
  1037         }
       
  1038 
       
  1039 
       
  1040         public String getDefaultDomain()
       
  1041         throws IOException {
       
  1042             if (logger.debugOn()) logger.debug("getDefaultDomain", "");
       
  1043 
       
  1044             final ClassLoader old = pushDefaultClassLoader();
       
  1045             try {
       
  1046                 return connection.getDefaultDomain(delegationSubject);
       
  1047             } catch (IOException ioe) {
       
  1048                 communicatorAdmin.gotIOException(ioe);
       
  1049 
       
  1050                 return connection.getDefaultDomain(delegationSubject);
       
  1051             } finally {
       
  1052                 popDefaultClassLoader(old);
       
  1053             }
       
  1054         }
       
  1055 
       
  1056         public String[] getDomains() throws IOException {
       
  1057             if (logger.debugOn()) logger.debug("getDomains", "");
       
  1058 
       
  1059             final ClassLoader old = pushDefaultClassLoader();
       
  1060             try {
       
  1061                 return connection.getDomains(delegationSubject);
       
  1062             } catch (IOException ioe) {
       
  1063                 communicatorAdmin.gotIOException(ioe);
       
  1064 
       
  1065                 return connection.getDomains(delegationSubject);
       
  1066             } finally {
       
  1067                 popDefaultClassLoader(old);
       
  1068             }
       
  1069         }
       
  1070 
       
  1071         public MBeanInfo getMBeanInfo(ObjectName name)
       
  1072         throws InstanceNotFoundException,
       
  1073                 IntrospectionException,
       
  1074                 ReflectionException,
       
  1075                 IOException {
       
  1076 
       
  1077             if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name);
       
  1078             final ClassLoader old = pushDefaultClassLoader();
       
  1079             try {
       
  1080                 return connection.getMBeanInfo(name, delegationSubject);
       
  1081             } catch (IOException ioe) {
       
  1082                 communicatorAdmin.gotIOException(ioe);
       
  1083 
       
  1084                 return connection.getMBeanInfo(name, delegationSubject);
       
  1085             } finally {
       
  1086                 popDefaultClassLoader(old);
       
  1087             }
       
  1088         }
       
  1089 
       
  1090 
       
  1091         public boolean isInstanceOf(ObjectName name,
       
  1092                 String className)
       
  1093                 throws InstanceNotFoundException,
       
  1094                 IOException {
       
  1095             if (logger.debugOn())
       
  1096                 logger.debug("isInstanceOf", "name=" + name +
       
  1097                         ", className=" + className);
       
  1098 
       
  1099             final ClassLoader old = pushDefaultClassLoader();
       
  1100             try {
       
  1101                 return connection.isInstanceOf(name,
       
  1102                         className,
       
  1103                         delegationSubject);
       
  1104             } catch (IOException ioe) {
       
  1105                 communicatorAdmin.gotIOException(ioe);
       
  1106 
       
  1107                 return connection.isInstanceOf(name,
       
  1108                         className,
       
  1109                         delegationSubject);
       
  1110             } finally {
       
  1111                 popDefaultClassLoader(old);
       
  1112             }
       
  1113         }
       
  1114 
       
  1115         public void addNotificationListener(ObjectName name,
       
  1116                 ObjectName listener,
       
  1117                 NotificationFilter filter,
       
  1118                 Object handback)
       
  1119                 throws InstanceNotFoundException,
       
  1120                 IOException {
       
  1121 
       
  1122             if (logger.debugOn())
       
  1123                 logger.debug("addNotificationListener" +
       
  1124                         "(ObjectName,ObjectName,NotificationFilter,Object)",
       
  1125                         "name=" + name + ", listener=" + listener
       
  1126                         + ", filter=" + filter + ", handback=" + handback);
       
  1127 
       
  1128             final MarshalledObject<NotificationFilter> sFilter =
       
  1129                     new MarshalledObject<NotificationFilter>(filter);
       
  1130             final MarshalledObject<Object> sHandback =
       
  1131                     new MarshalledObject<Object>(handback);
       
  1132             final ClassLoader old = pushDefaultClassLoader();
       
  1133             try {
       
  1134                 connection.addNotificationListener(name,
       
  1135                         listener,
       
  1136                         sFilter,
       
  1137                         sHandback,
       
  1138                         delegationSubject);
       
  1139             } catch (IOException ioe) {
       
  1140                 communicatorAdmin.gotIOException(ioe);
       
  1141 
       
  1142                 connection.addNotificationListener(name,
       
  1143                         listener,
       
  1144                         sFilter,
       
  1145                         sHandback,
       
  1146                         delegationSubject);
       
  1147             } finally {
       
  1148                 popDefaultClassLoader(old);
       
  1149             }
       
  1150         }
       
  1151 
       
  1152         public void removeNotificationListener(ObjectName name,
       
  1153                 ObjectName listener)
       
  1154                 throws InstanceNotFoundException,
       
  1155                 ListenerNotFoundException,
       
  1156                 IOException {
       
  1157 
       
  1158             if (logger.debugOn()) logger.debug("removeNotificationListener" +
       
  1159                     "(ObjectName,ObjectName)",
       
  1160                     "name=" + name
       
  1161                     + ", listener=" + listener);
       
  1162 
       
  1163             final ClassLoader old = pushDefaultClassLoader();
       
  1164             try {
       
  1165                 connection.removeNotificationListener(name,
       
  1166                         listener,
       
  1167                         delegationSubject);
       
  1168             } catch (IOException ioe) {
       
  1169                 communicatorAdmin.gotIOException(ioe);
       
  1170 
       
  1171                 connection.removeNotificationListener(name,
       
  1172                         listener,
       
  1173                         delegationSubject);
       
  1174             } finally {
       
  1175                 popDefaultClassLoader(old);
       
  1176             }
       
  1177         }
       
  1178 
       
  1179         public void removeNotificationListener(ObjectName name,
       
  1180                 ObjectName listener,
       
  1181                 NotificationFilter filter,
       
  1182                 Object handback)
       
  1183                 throws InstanceNotFoundException,
       
  1184                 ListenerNotFoundException,
       
  1185                 IOException {
       
  1186             if (logger.debugOn())
       
  1187                 logger.debug("removeNotificationListener" +
       
  1188                         "(ObjectName,ObjectName,NotificationFilter,Object)",
       
  1189                         "name=" + name
       
  1190                         + ", listener=" + listener
       
  1191                         + ", filter=" + filter
       
  1192                         + ", handback=" + handback);
       
  1193 
       
  1194             final MarshalledObject<NotificationFilter> sFilter =
       
  1195                     new MarshalledObject<NotificationFilter>(filter);
       
  1196             final MarshalledObject<Object> sHandback =
       
  1197                     new MarshalledObject<Object>(handback);
       
  1198             final ClassLoader old = pushDefaultClassLoader();
       
  1199             try {
       
  1200                 connection.removeNotificationListener(name,
       
  1201                         listener,
       
  1202                         sFilter,
       
  1203                         sHandback,
       
  1204                         delegationSubject);
       
  1205             } catch (IOException ioe) {
       
  1206                 communicatorAdmin.gotIOException(ioe);
       
  1207 
       
  1208                 connection.removeNotificationListener(name,
       
  1209                         listener,
       
  1210                         sFilter,
       
  1211                         sHandback,
       
  1212                         delegationSubject);
       
  1213             } finally {
       
  1214                 popDefaultClassLoader(old);
       
  1215             }
       
  1216         }
       
  1217 
       
  1218         // Specific Notification Handle ----------------------------------
       
  1219 
       
  1220         public void addNotificationListener(ObjectName name,
       
  1221                 NotificationListener listener,
       
  1222                 NotificationFilter filter,
       
  1223                 Object handback)
       
  1224                 throws InstanceNotFoundException,
       
  1225                 IOException {
       
  1226 
       
  1227             final boolean debug = logger.debugOn();
       
  1228 
       
  1229             if (debug)
       
  1230                 logger.debug("addNotificationListener" +
       
  1231                         "(ObjectName,NotificationListener,"+
       
  1232                         "NotificationFilter,Object)",
       
  1233                         "name=" + name
       
  1234                         + ", listener=" + listener
       
  1235                         + ", filter=" + filter
       
  1236                         + ", handback=" + handback);
       
  1237 
       
  1238             final Integer listenerID =
       
  1239                     addListenerWithSubject(name,
       
  1240                     new MarshalledObject<NotificationFilter>(filter),
       
  1241                     delegationSubject,true);
       
  1242             rmiNotifClient.addNotificationListener(listenerID, name, listener,
       
  1243                     filter, handback,
       
  1244                     delegationSubject);
       
  1245         }
       
  1246 
       
  1247         public void removeNotificationListener(ObjectName name,
       
  1248                 NotificationListener listener)
       
  1249                 throws InstanceNotFoundException,
       
  1250                 ListenerNotFoundException,
       
  1251                 IOException {
       
  1252 
       
  1253             final boolean debug = logger.debugOn();
       
  1254 
       
  1255             if (debug) logger.debug("removeNotificationListener"+
       
  1256                     "(ObjectName,NotificationListener)",
       
  1257                     "name=" + name
       
  1258                     + ", listener=" + listener);
       
  1259 
       
  1260             final Integer[] ret =
       
  1261                     rmiNotifClient.getListenerIds(name, listener);
       
  1262 
       
  1263             if (debug) logger.debug("removeNotificationListener",
       
  1264                     "listenerIDs=" + objects(ret));
       
  1265 
       
  1266             final ClassLoader old = pushDefaultClassLoader();
       
  1267 
       
  1268             try {
       
  1269                 connection.removeNotificationListeners(name,
       
  1270                         ret,
       
  1271                         delegationSubject);
       
  1272             } catch (IOException ioe) {
       
  1273                 communicatorAdmin.gotIOException(ioe);
       
  1274 
       
  1275                 connection.removeNotificationListeners(name,
       
  1276                         ret,
       
  1277                         delegationSubject);
       
  1278             } finally {
       
  1279                 popDefaultClassLoader(old);
       
  1280             }
       
  1281             rmiNotifClient.removeNotificationListener(name, listener);
       
  1282         }
       
  1283 
       
  1284         public void removeNotificationListener(ObjectName name,
       
  1285                 NotificationListener listener,
       
  1286                 NotificationFilter filter,
       
  1287                 Object handback)
       
  1288                 throws InstanceNotFoundException,
       
  1289                 ListenerNotFoundException,
       
  1290                 IOException {
       
  1291             final boolean debug = logger.debugOn();
       
  1292 
       
  1293             if (debug)
       
  1294                 logger.debug("removeNotificationListener"+
       
  1295                         "(ObjectName,NotificationListener,"+
       
  1296                         "NotificationFilter,Object)",
       
  1297                         "name=" + name
       
  1298                         + ", listener=" + listener
       
  1299                         + ", filter=" + filter
       
  1300                         + ", handback=" + handback);
       
  1301 
       
  1302             final Integer ret =
       
  1303                     rmiNotifClient.getListenerId(name, listener,
       
  1304                     filter, handback);
       
  1305 
       
  1306             if (debug) logger.debug("removeNotificationListener",
       
  1307                     "listenerID=" + ret);
       
  1308 
       
  1309             final ClassLoader old = pushDefaultClassLoader();
       
  1310             try {
       
  1311                 connection.removeNotificationListeners(name,
       
  1312                         new Integer[] {ret},
       
  1313                         delegationSubject);
       
  1314             } catch (IOException ioe) {
       
  1315                 communicatorAdmin.gotIOException(ioe);
       
  1316 
       
  1317                 connection.removeNotificationListeners(name,
       
  1318                         new Integer[] {ret},
       
  1319                         delegationSubject);
       
  1320             } finally {
       
  1321                 popDefaultClassLoader(old);
       
  1322             }
       
  1323             rmiNotifClient.removeNotificationListener(name, listener,
       
  1324                     filter, handback);
       
  1325         }
       
  1326     }
       
  1327 
       
  1328     //--------------------------------------------------------------------
       
  1329     private class RMINotifClient extends ClientNotifForwarder {
       
  1330         public RMINotifClient(ClassLoader cl, Map<String, ?> env) {
       
  1331             super(cl, env);
       
  1332         }
       
  1333 
       
  1334         protected NotificationResult fetchNotifs(long clientSequenceNumber,
       
  1335                 int maxNotifications,
       
  1336                 long timeout)
       
  1337                 throws IOException, ClassNotFoundException {
       
  1338 
       
  1339             boolean retried = false;
       
  1340             while (true) { // used for a successful re-connection
       
  1341                            // or a transient network problem
       
  1342                 try {
       
  1343                     return connection.fetchNotifications(clientSequenceNumber,
       
  1344                             maxNotifications,
       
  1345                             timeout); // return normally
       
  1346                 } catch (IOException ioe) {
       
  1347                     // Examine the chain of exceptions to determine whether this
       
  1348                     // is a deserialization issue. If so - we propagate the
       
  1349                     // appropriate exception to the caller, who will then
       
  1350                     // proceed with fetching notifications one by one
       
  1351                     rethrowDeserializationException(ioe);
       
  1352 
       
  1353                     try {
       
  1354                         communicatorAdmin.gotIOException(ioe);
       
  1355                         // reconnection OK, back to "while" to do again
       
  1356                     } catch (IOException ee) {
       
  1357                         boolean toClose = false;
       
  1358 
       
  1359                         synchronized (this) {
       
  1360                             if (terminated) {
       
  1361                                 // the connection is closed.
       
  1362                                 throw ioe;
       
  1363                             } else if (retried) {
       
  1364                                 toClose = true;
       
  1365                             }
       
  1366                         }
       
  1367 
       
  1368                         if (toClose) {
       
  1369                             // JDK-8049303
       
  1370                             // We received an IOException - but the communicatorAdmin
       
  1371                             // did not close the connection - possibly because
       
  1372                             // the original exception was raised by a transient network
       
  1373                             // problem?
       
  1374                             // We already know that this exception is not due to a deserialization
       
  1375                             // issue as we already took care of that before involving the
       
  1376                             // communicatorAdmin. Moreover - we already made one retry attempt
       
  1377                             // at fetching the same batch of notifications - and the
       
  1378                             // problem persisted.
       
  1379                             // Since trying again doesn't seem to solve the issue, we will now
       
  1380                             // close the connection. Doing otherwise might cause the
       
  1381                             // NotifFetcher thread to die silently.
       
  1382                             final Notification failedNotif =
       
  1383                                     new JMXConnectionNotification(
       
  1384                                     JMXConnectionNotification.FAILED,
       
  1385                                     this,
       
  1386                                     connectionId,
       
  1387                                     clientNotifSeqNo++,
       
  1388                                     "Failed to communicate with the server: " + ioe.toString(),
       
  1389                                     ioe);
       
  1390 
       
  1391                             sendNotification(failedNotif);
       
  1392 
       
  1393                             try {
       
  1394                                 close(true);
       
  1395                             } catch (Exception e) {
       
  1396                                 // OK.
       
  1397                                 // We are closing
       
  1398                             }
       
  1399                             throw ioe; // the connection is closed here.
       
  1400                         } else {
       
  1401                             // JDK-8049303 possible transient network problem,
       
  1402                             // let's try one more time
       
  1403                             retried = true;
       
  1404                         }
       
  1405                     }
       
  1406                 }
       
  1407             }
       
  1408         }
       
  1409 
       
  1410         private void rethrowDeserializationException(IOException ioe)
       
  1411                 throws ClassNotFoundException, IOException {
       
  1412             // specially treating for an UnmarshalException
       
  1413             if (ioe instanceof UnmarshalException) {
       
  1414                 NotSerializableException nse = new NotSerializableException();
       
  1415                 nse.initCause(ioe);
       
  1416                 throw nse; // the fix of 6937053 made ClientNotifForwarder.fetchNotifs
       
  1417                            // fetch one by one with UnmarshalException
       
  1418             }
       
  1419 
       
  1420             // Not serialization problem, return.
       
  1421         }
       
  1422 
       
  1423         protected Integer addListenerForMBeanRemovedNotif()
       
  1424         throws IOException, InstanceNotFoundException {
       
  1425             NotificationFilterSupport clientFilter =
       
  1426                     new NotificationFilterSupport();
       
  1427             clientFilter.enableType(
       
  1428                     MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
       
  1429             MarshalledObject<NotificationFilter> sFilter =
       
  1430                 new MarshalledObject<NotificationFilter>(clientFilter);
       
  1431 
       
  1432             Integer[] listenerIDs;
       
  1433             final ObjectName[] names =
       
  1434                 new ObjectName[] {MBeanServerDelegate.DELEGATE_NAME};
       
  1435             final MarshalledObject<NotificationFilter>[] filters =
       
  1436                 Util.cast(new MarshalledObject<?>[] {sFilter});
       
  1437             final Subject[] subjects = new Subject[] {null};
       
  1438             try {
       
  1439                 listenerIDs =
       
  1440                         connection.addNotificationListeners(names,
       
  1441                         filters,
       
  1442                         subjects);
       
  1443 
       
  1444             } catch (IOException ioe) {
       
  1445                 communicatorAdmin.gotIOException(ioe);
       
  1446 
       
  1447                 listenerIDs =
       
  1448                         connection.addNotificationListeners(names,
       
  1449                         filters,
       
  1450                         subjects);
       
  1451             }
       
  1452             return listenerIDs[0];
       
  1453         }
       
  1454 
       
  1455         protected void removeListenerForMBeanRemovedNotif(Integer id)
       
  1456         throws IOException, InstanceNotFoundException,
       
  1457                 ListenerNotFoundException {
       
  1458             try {
       
  1459                 connection.removeNotificationListeners(
       
  1460                         MBeanServerDelegate.DELEGATE_NAME,
       
  1461                         new Integer[] {id},
       
  1462                         null);
       
  1463             } catch (IOException ioe) {
       
  1464                 communicatorAdmin.gotIOException(ioe);
       
  1465 
       
  1466                 connection.removeNotificationListeners(
       
  1467                         MBeanServerDelegate.DELEGATE_NAME,
       
  1468                         new Integer[] {id},
       
  1469                         null);
       
  1470             }
       
  1471 
       
  1472         }
       
  1473 
       
  1474         protected void lostNotifs(String message, long number) {
       
  1475             final String notifType = JMXConnectionNotification.NOTIFS_LOST;
       
  1476 
       
  1477             final JMXConnectionNotification n =
       
  1478                 new JMXConnectionNotification(notifType,
       
  1479                                               RMIConnector.this,
       
  1480                                               connectionId,
       
  1481                                               clientNotifCounter++,
       
  1482                                               message,
       
  1483                                               Long.valueOf(number));
       
  1484             sendNotification(n);
       
  1485         }
       
  1486     }
       
  1487 
       
  1488     private class RMIClientCommunicatorAdmin extends ClientCommunicatorAdmin {
       
  1489         public RMIClientCommunicatorAdmin(long period) {
       
  1490             super(period);
       
  1491         }
       
  1492 
       
  1493         @Override
       
  1494         public void gotIOException(IOException ioe) throws IOException {
       
  1495             if (ioe instanceof NoSuchObjectException) {
       
  1496                 // need to restart
       
  1497                 super.gotIOException(ioe);
       
  1498 
       
  1499                 return;
       
  1500             }
       
  1501 
       
  1502             // check if the connection is broken
       
  1503             try {
       
  1504                 connection.getDefaultDomain(null);
       
  1505             } catch (IOException ioexc) {
       
  1506                 boolean toClose = false;
       
  1507 
       
  1508                 synchronized(this) {
       
  1509                     if (!terminated) {
       
  1510                         terminated = true;
       
  1511 
       
  1512                         toClose = true;
       
  1513                     }
       
  1514                 }
       
  1515 
       
  1516                 if (toClose) {
       
  1517                     // we should close the connection,
       
  1518                     // but send a failed notif at first
       
  1519                     final Notification failedNotif =
       
  1520                             new JMXConnectionNotification(
       
  1521                             JMXConnectionNotification.FAILED,
       
  1522                             this,
       
  1523                             connectionId,
       
  1524                             clientNotifSeqNo++,
       
  1525                             "Failed to communicate with the server: "+ioe.toString(),
       
  1526                             ioe);
       
  1527 
       
  1528                     sendNotification(failedNotif);
       
  1529 
       
  1530                     try {
       
  1531                         close(true);
       
  1532                     } catch (Exception e) {
       
  1533                         // OK.
       
  1534                         // We are closing
       
  1535                     }
       
  1536                 }
       
  1537             }
       
  1538 
       
  1539             // forward the exception
       
  1540             if (ioe instanceof ServerException) {
       
  1541                 /* Need to unwrap the exception.
       
  1542                    Some user-thrown exception at server side will be wrapped by
       
  1543                    rmi into a ServerException.
       
  1544                    For example, a RMIConnnectorServer will wrap a
       
  1545                    ClassNotFoundException into a UnmarshalException, and rmi
       
  1546                    will throw a ServerException at client side which wraps this
       
  1547                    UnmarshalException.
       
  1548                    No failed notif here.
       
  1549                  */
       
  1550                 Throwable tt = ((ServerException)ioe).detail;
       
  1551 
       
  1552                 if (tt instanceof IOException) {
       
  1553                     throw (IOException)tt;
       
  1554                 } else if (tt instanceof RuntimeException) {
       
  1555                     throw (RuntimeException)tt;
       
  1556                 }
       
  1557             }
       
  1558 
       
  1559             throw ioe;
       
  1560         }
       
  1561 
       
  1562         public void reconnectNotificationListeners(ClientListenerInfo[] old) throws IOException {
       
  1563             final int len  = old.length;
       
  1564             int i;
       
  1565 
       
  1566             ClientListenerInfo[] clis = new ClientListenerInfo[len];
       
  1567 
       
  1568             final Subject[] subjects = new Subject[len];
       
  1569             final ObjectName[] names = new ObjectName[len];
       
  1570             final NotificationListener[] listeners = new NotificationListener[len];
       
  1571             final NotificationFilter[] filters = new NotificationFilter[len];
       
  1572             final MarshalledObject<NotificationFilter>[] mFilters =
       
  1573                     Util.cast(new MarshalledObject<?>[len]);
       
  1574             final Object[] handbacks = new Object[len];
       
  1575 
       
  1576             for (i=0;i<len;i++) {
       
  1577                 subjects[i]  = old[i].getDelegationSubject();
       
  1578                 names[i]     = old[i].getObjectName();
       
  1579                 listeners[i] = old[i].getListener();
       
  1580                 filters[i]   = old[i].getNotificationFilter();
       
  1581                 mFilters[i]  = new MarshalledObject<NotificationFilter>(filters[i]);
       
  1582                 handbacks[i] = old[i].getHandback();
       
  1583             }
       
  1584 
       
  1585             try {
       
  1586                 Integer[] ids = addListenersWithSubjects(names,mFilters,subjects,false);
       
  1587 
       
  1588                 for (i=0;i<len;i++) {
       
  1589                     clis[i] = new ClientListenerInfo(ids[i],
       
  1590                             names[i],
       
  1591                             listeners[i],
       
  1592                             filters[i],
       
  1593                             handbacks[i],
       
  1594                             subjects[i]);
       
  1595                 }
       
  1596 
       
  1597                 rmiNotifClient.postReconnection(clis);
       
  1598 
       
  1599                 return;
       
  1600             } catch (InstanceNotFoundException infe) {
       
  1601                 // OK, we will do one by one
       
  1602             }
       
  1603 
       
  1604             int j = 0;
       
  1605             for (i=0;i<len;i++) {
       
  1606                 try {
       
  1607                     Integer id = addListenerWithSubject(names[i],
       
  1608                             new MarshalledObject<NotificationFilter>(filters[i]),
       
  1609                             subjects[i],
       
  1610                             false);
       
  1611 
       
  1612                     clis[j++] = new ClientListenerInfo(id,
       
  1613                             names[i],
       
  1614                             listeners[i],
       
  1615                             filters[i],
       
  1616                             handbacks[i],
       
  1617                             subjects[i]);
       
  1618                 } catch (InstanceNotFoundException infe) {
       
  1619                     logger.warning("reconnectNotificationListeners",
       
  1620                             "Can't reconnect listener for " +
       
  1621                             names[i]);
       
  1622                 }
       
  1623             }
       
  1624 
       
  1625             if (j != len) {
       
  1626                 ClientListenerInfo[] tmp = clis;
       
  1627                 clis = new ClientListenerInfo[j];
       
  1628                 System.arraycopy(tmp, 0, clis, 0, j);
       
  1629             }
       
  1630 
       
  1631             rmiNotifClient.postReconnection(clis);
       
  1632         }
       
  1633 
       
  1634         protected void checkConnection() throws IOException {
       
  1635             if (logger.debugOn())
       
  1636                 logger.debug("RMIClientCommunicatorAdmin-checkConnection",
       
  1637                         "Calling the method getDefaultDomain.");
       
  1638 
       
  1639             connection.getDefaultDomain(null);
       
  1640         }
       
  1641 
       
  1642         protected void doStart() throws IOException {
       
  1643             // Get RMIServer stub from directory or URL encoding if needed.
       
  1644             RMIServer stub;
       
  1645             try {
       
  1646                 stub = (rmiServer!=null)?rmiServer:
       
  1647                     findRMIServer(jmxServiceURL, env);
       
  1648             } catch (NamingException ne) {
       
  1649                 throw new IOException("Failed to get a RMI stub: "+ne);
       
  1650             }
       
  1651 
       
  1652             // Calling newClient on the RMIServer stub.
       
  1653             Object credentials = env.get(CREDENTIALS);
       
  1654             connection = stub.newClient(credentials);
       
  1655 
       
  1656             // notif issues
       
  1657             final ClientListenerInfo[] old = rmiNotifClient.preReconnection();
       
  1658 
       
  1659             reconnectNotificationListeners(old);
       
  1660 
       
  1661             connectionId = getConnectionId();
       
  1662 
       
  1663             Notification reconnectedNotif =
       
  1664                     new JMXConnectionNotification(JMXConnectionNotification.OPENED,
       
  1665                     this,
       
  1666                     connectionId,
       
  1667                     clientNotifSeqNo++,
       
  1668                     "Reconnected to server",
       
  1669                     null);
       
  1670             sendNotification(reconnectedNotif);
       
  1671 
       
  1672         }
       
  1673 
       
  1674         protected void doStop() {
       
  1675             try {
       
  1676                 close();
       
  1677             } catch (IOException ioe) {
       
  1678                 logger.warning("RMIClientCommunicatorAdmin-doStop",
       
  1679                         "Failed to call the method close():" + ioe);
       
  1680                 logger.debug("RMIClientCommunicatorAdmin-doStop",ioe);
       
  1681             }
       
  1682         }
       
  1683     }
       
  1684 
       
  1685     //--------------------------------------------------------------------
       
  1686     // Private stuff - Serialization
       
  1687     //--------------------------------------------------------------------
       
  1688     /**
       
  1689      * Read RMIConnector fields from an {@link java.io.ObjectInputStream
       
  1690      * ObjectInputStream}.
       
  1691      * Calls {@code s.defaultReadObject()} and then initializes
       
  1692      * all transient variables that need initializing.
       
  1693      * @param s The ObjectInputStream to read from.
       
  1694      * @exception InvalidObjectException if none of <var>rmiServer</var> stub
       
  1695      *    or <var>jmxServiceURL</var> are set.
       
  1696      * @see #RMIConnector(JMXServiceURL,Map)
       
  1697      * @see #RMIConnector(RMIServer,Map)
       
  1698      **/
       
  1699     private void readObject(java.io.ObjectInputStream s)
       
  1700     throws IOException, ClassNotFoundException  {
       
  1701         s.defaultReadObject();
       
  1702 
       
  1703         if (rmiServer == null && jmxServiceURL == null) throw new
       
  1704                 InvalidObjectException("rmiServer and jmxServiceURL both null");
       
  1705 
       
  1706         initTransients();
       
  1707     }
       
  1708 
       
  1709     /**
       
  1710      * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
       
  1711      * ObjectOutputStream}.
       
  1712      * <p>Connects the underlying RMIServer stub to an ORB, if needed,
       
  1713      * before serializing it. This is done using the environment
       
  1714      * map that was provided to the constructor, if any, and as documented
       
  1715      * in {@link javax.management.remote.rmi}.</p>
       
  1716      * <p>This method then calls {@code s.defaultWriteObject()}.
       
  1717      * Usually, <var>rmiServer</var> is null if this object
       
  1718      * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
       
  1719      * is null if this object is constructed with a RMIServer stub.
       
  1720      * <p>Note that the environment Map is not serialized, since the objects
       
  1721      * it contains are assumed to be contextual and relevant only
       
  1722      * with respect to the local environment (class loader, ORB, etc...).</p>
       
  1723      * <p>After an RMIConnector is deserialized, it is assumed that the
       
  1724      * user will call {@link #connect(Map)}, providing a new Map that
       
  1725      * can contain values which are contextually relevant to the new
       
  1726      * local environment.</p>
       
  1727      * <p>Since connection to the ORB is needed prior to serializing, and
       
  1728      * since the ORB to connect to is one of those contextual parameters,
       
  1729      * it is not recommended to re-serialize a just de-serialized object -
       
  1730      * as the de-serialized object has no map. Thus, when an RMIConnector
       
  1731      * object is needed for serialization or transmission to a remote
       
  1732      * application, it is recommended to obtain a new RMIConnector stub
       
  1733      * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
       
  1734      * @param s The ObjectOutputStream to write to.
       
  1735      * @exception InvalidObjectException if none of <var>rmiServer</var> stub
       
  1736      *    or <var>jmxServiceURL</var> are set.
       
  1737      * @see #RMIConnector(JMXServiceURL,Map)
       
  1738      * @see #RMIConnector(RMIServer,Map)
       
  1739      **/
       
  1740     private void writeObject(java.io.ObjectOutputStream s)
       
  1741     throws IOException {
       
  1742         if (rmiServer == null && jmxServiceURL == null) throw new
       
  1743                 InvalidObjectException("rmiServer and jmxServiceURL both null.");
       
  1744         s.defaultWriteObject();
       
  1745     }
       
  1746 
       
  1747     // Initialization of transient variables.
       
  1748     private void initTransients() {
       
  1749         rmbscMap = new WeakHashMap<Subject, WeakReference<MBeanServerConnection>>();
       
  1750         connected = false;
       
  1751         terminated = false;
       
  1752 
       
  1753         connectionBroadcaster = new NotificationBroadcasterSupport();
       
  1754     }
       
  1755 
       
  1756     //--------------------------------------------------------------------
       
  1757     // Private stuff - Check if stub can be trusted.
       
  1758     //--------------------------------------------------------------------
       
  1759 
       
  1760     private static void checkStub(Remote stub,
       
  1761             Class<?> stubClass) {
       
  1762 
       
  1763         // Check remote stub is from the expected class.
       
  1764         //
       
  1765         if (stub.getClass() != stubClass) {
       
  1766             if (!Proxy.isProxyClass(stub.getClass())) {
       
  1767                 throw new SecurityException(
       
  1768                         "Expecting a " + stubClass.getName() + " stub!");
       
  1769             } else {
       
  1770                 InvocationHandler handler = Proxy.getInvocationHandler(stub);
       
  1771                 if (handler.getClass() != RemoteObjectInvocationHandler.class)
       
  1772                     throw new SecurityException(
       
  1773                             "Expecting a dynamic proxy instance with a " +
       
  1774                             RemoteObjectInvocationHandler.class.getName() +
       
  1775                             " invocation handler!");
       
  1776                 else
       
  1777                     stub = (Remote) handler;
       
  1778             }
       
  1779         }
       
  1780 
       
  1781         // Check RemoteRef in stub is from the expected class
       
  1782         // "sun.rmi.server.UnicastRef2".
       
  1783         //
       
  1784         RemoteRef ref = ((RemoteObject)stub).getRef();
       
  1785         if (ref.getClass() != UnicastRef2.class)
       
  1786             throw new SecurityException(
       
  1787                     "Expecting a " + UnicastRef2.class.getName() +
       
  1788                     " remote reference in stub!");
       
  1789 
       
  1790         // Check RMIClientSocketFactory in stub is from the expected class
       
  1791         // "javax.rmi.ssl.SslRMIClientSocketFactory".
       
  1792         //
       
  1793         LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();
       
  1794         RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
       
  1795         if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class)
       
  1796             throw new SecurityException(
       
  1797                     "Expecting a " + SslRMIClientSocketFactory.class.getName() +
       
  1798                     " RMI client socket factory in stub!");
       
  1799     }
       
  1800 
       
  1801     //--------------------------------------------------------------------
       
  1802     // Private stuff - RMIServer creation
       
  1803     //--------------------------------------------------------------------
       
  1804 
       
  1805     private RMIServer findRMIServer(JMXServiceURL directoryURL,
       
  1806             Map<String, Object> environment)
       
  1807             throws NamingException, IOException {
       
  1808 
       
  1809         String path = directoryURL.getURLPath();
       
  1810         int end = path.indexOf(';');
       
  1811         if (end < 0) end = path.length();
       
  1812         if (path.startsWith("/jndi/"))
       
  1813             return findRMIServerJNDI(path.substring(6,end), environment);
       
  1814         else if (path.startsWith("/stub/"))
       
  1815             return findRMIServerJRMP(path.substring(6,end), environment);
       
  1816         else {
       
  1817             final String msg = "URL path must begin with /jndi/ or /stub/ " +
       
  1818                     "or /ior/: " + path;
       
  1819             throw new MalformedURLException(msg);
       
  1820         }
       
  1821     }
       
  1822 
       
  1823     /**
       
  1824      * Lookup the RMIServer stub in a directory.
       
  1825      * @param jndiURL A JNDI URL indicating the location of the Stub
       
  1826      *                (see {@link javax.management.remote.rmi}), e.g.:
       
  1827      *   <ul><li>{@code rmi://registry-host:port/rmi-stub-name}</li>
       
  1828      *       <li>or {@code ldap://ldap-host:port/java-container-dn}</li>
       
  1829      *   </ul>
       
  1830      * @param env the environment Map passed to the connector.
       
  1831      * @return The retrieved RMIServer stub.
       
  1832      * @exception NamingException if the stub couldn't be found.
       
  1833      **/
       
  1834     private RMIServer findRMIServerJNDI(String jndiURL, Map<String, ?> env)
       
  1835             throws NamingException {
       
  1836 
       
  1837         InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env));
       
  1838 
       
  1839         Object objref = ctx.lookup(jndiURL);
       
  1840         ctx.close();
       
  1841 
       
  1842         return narrowJRMPServer(objref);
       
  1843     }
       
  1844 
       
  1845     private static RMIServer narrowJRMPServer(Object objref) {
       
  1846 
       
  1847         return (RMIServer) objref;
       
  1848     }
       
  1849 
       
  1850     private RMIServer findRMIServerJRMP(String base64, Map<String, ?> env)
       
  1851         throws IOException {
       
  1852         final byte[] serialized;
       
  1853         try {
       
  1854             serialized = base64ToByteArray(base64);
       
  1855         } catch (IllegalArgumentException e) {
       
  1856             throw new MalformedURLException("Bad BASE64 encoding: " +
       
  1857                     e.getMessage());
       
  1858         }
       
  1859         final ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
       
  1860 
       
  1861         final ClassLoader loader = EnvHelp.resolveClientClassLoader(env);
       
  1862         final ObjectInputStream oin =
       
  1863                 (loader == null) ?
       
  1864                     new ObjectInputStream(bin) :
       
  1865                     new ObjectInputStreamWithLoader(bin, loader);
       
  1866         final Object stub;
       
  1867         try {
       
  1868             stub = oin.readObject();
       
  1869         } catch (ClassNotFoundException e) {
       
  1870             throw new MalformedURLException("Class not found: " + e);
       
  1871         }
       
  1872         return (RMIServer)stub;
       
  1873     }
       
  1874 
       
  1875     private static final class ObjectInputStreamWithLoader
       
  1876             extends ObjectInputStream {
       
  1877         ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
       
  1878         throws IOException, IllegalArgumentException {
       
  1879             super(in);
       
  1880             if (cl == null ) {
       
  1881               throw new IllegalArgumentException("class loader is null");
       
  1882             }
       
  1883             this.loader = cl;
       
  1884         }
       
  1885 
       
  1886         @Override
       
  1887         protected Class<?> resolveClass(ObjectStreamClass classDesc)
       
  1888                 throws IOException, ClassNotFoundException {
       
  1889             String name = classDesc.getName();
       
  1890             ReflectUtil.checkPackageAccess(name);
       
  1891             return Class.forName(name, false, Objects.requireNonNull(loader));
       
  1892         }
       
  1893 
       
  1894         private final ClassLoader loader;
       
  1895     }
       
  1896 
       
  1897     private MBeanServerConnection getConnectionWithSubject(Subject delegationSubject) {
       
  1898         MBeanServerConnection conn = null;
       
  1899 
       
  1900         if (delegationSubject == null) {
       
  1901             if (nullSubjectConnRef == null
       
  1902                     || (conn = nullSubjectConnRef.get()) == null) {
       
  1903                 conn = new RemoteMBeanServerConnection(null);
       
  1904                 nullSubjectConnRef = new WeakReference<MBeanServerConnection>(conn);
       
  1905             }
       
  1906         } else {
       
  1907             WeakReference<MBeanServerConnection> wr = rmbscMap.get(delegationSubject);
       
  1908             if (wr == null || (conn = wr.get()) == null) {
       
  1909                 conn = new RemoteMBeanServerConnection(delegationSubject);
       
  1910                 rmbscMap.put(delegationSubject, new WeakReference<MBeanServerConnection>(conn));
       
  1911             }
       
  1912         }
       
  1913         return conn;
       
  1914     }
       
  1915 
       
  1916     /*
       
  1917        The following section of code avoids a class loading problem
       
  1918        with RMI.  The problem is that an RMI stub, when deserializing
       
  1919        a remote method return value or exception, will first of all
       
  1920        consult the first non-bootstrap class loader it finds in the
       
  1921        call stack.  This can lead to behavior that is not portable
       
  1922        between implementations of the JMX Remote API.  Notably, an
       
  1923        implementation on J2SE 1.4 will find the RMI stub's loader on
       
  1924        the stack.  But in J2SE 5, this stub is loaded by the
       
  1925        bootstrap loader, so RMI will find the loader of the user code
       
  1926        that called an MBeanServerConnection method.
       
  1927 
       
  1928        To avoid this problem, we take advantage of what the RMI stub
       
  1929        is doing internally.  Each remote call will end up calling
       
  1930        ref.invoke(...), where ref is the RemoteRef parameter given to
       
  1931        the RMI stub's constructor.  It is within this call that the
       
  1932        deserialization will happen.  So we fabricate our own RemoteRef
       
  1933        that delegates everything to the "real" one but that is loaded
       
  1934        by a class loader that knows no other classes.  The class
       
  1935        loader NoCallStackClassLoader does this: the RemoteRef is an
       
  1936        instance of the class named by proxyRefClassName, which is
       
  1937        fabricated by the class loader using byte code that is defined
       
  1938        by the string below.
       
  1939 
       
  1940        The call stack when the deserialization happens is thus this:
       
  1941        MBeanServerConnection.getAttribute (or whatever)
       
  1942        -> RMIConnectionImpl_Stub.getAttribute
       
  1943           -> ProxyRef.invoke(...getAttribute...)
       
  1944              -> UnicastRef.invoke(...getAttribute...)
       
  1945                 -> internal RMI stuff
       
  1946 
       
  1947        Here UnicastRef is the RemoteRef created when the stub was
       
  1948        deserialized (which is of some RMI internal class).  It and the
       
  1949        "internal RMI stuff" are loaded by the bootstrap loader, so are
       
  1950        transparent to the stack search.  The first non-bootstrap
       
  1951        loader found is our ProxyRefLoader, as required.
       
  1952 
       
  1953        In a future version of this code as integrated into J2SE 5,
       
  1954        this workaround could be replaced by direct access to the
       
  1955        internals of RMI.  For now, we use the same code base for J2SE
       
  1956        and for the standalone Reference Implementation.
       
  1957 
       
  1958        The byte code below encodes the following class, compiled using
       
  1959        J2SE 1.4.2 with the -g:none option.
       
  1960 
       
  1961         package jdk.jmx.remote.internal.rmi;
       
  1962 
       
  1963         import java.lang.reflect.Method;
       
  1964         import java.rmi.Remote;
       
  1965         import java.rmi.server.RemoteRef;
       
  1966         import com.sun.jmx.remote.internal.rmi.ProxyRef;
       
  1967 
       
  1968         public class PRef extends ProxyRef {
       
  1969             public PRef(RemoteRef ref) {
       
  1970                 super(ref);
       
  1971             }
       
  1972 
       
  1973             public Object invoke(Remote obj, Method method,
       
  1974                                  Object[] params, long opnum)
       
  1975                     throws Exception {
       
  1976                 return ref.invoke(obj, method, params, opnum);
       
  1977             }
       
  1978         }
       
  1979      */
       
  1980 
       
  1981     private static final String rmiServerImplStubClassName =
       
  1982         RMIServer.class.getName() + "Impl_Stub";
       
  1983     private static final Class<?> rmiServerImplStubClass;
       
  1984     private static final String rmiConnectionImplStubClassName =
       
  1985             RMIConnection.class.getName() + "Impl_Stub";
       
  1986     private static final Class<?> rmiConnectionImplStubClass;
       
  1987     private static final String pRefClassName =
       
  1988         "jdk.jmx.remote.internal.rmi.PRef";
       
  1989     private static final Constructor<?> proxyRefConstructor;
       
  1990     static {
       
  1991         final String pRefByteCodeString =
       
  1992                 "\312\376\272\276\0\0\0\65\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17"+
       
  1993                 "\0\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/Remote"+
       
  1994                 "Ref;)V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/"+
       
  1995                 "reflect/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12E"+
       
  1996                 "xceptions\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1"+
       
  1997                 "\0 jdk/jmx/remote/internal/rmi/PRef\1\0(com/sun/jmx/remote/int"+
       
  1998                 "ernal/rmi/ProxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Lj"+
       
  1999                 "ava/rmi/server/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0"+
       
  2000                 "\4\0\5\0\0\0\0\0\2\0\1\0\6\0\7\0\1\0\10\0\0\0\22\0\2\0\2\0\0\0"+
       
  2001                 "\6*+\267\0\1\261\0\0\0\0\0\1\0\11\0\12\0\2\0\10\0\0\0\33\0\6\0"+
       
  2002                 "\6\0\0\0\17*\264\0\2+,-\26\4\271\0\3\6\0\260\0\0\0\0\0\13\0\0\0"+
       
  2003                 "\4\0\1\0\14\0\0";
       
  2004         final byte[] pRefByteCode =
       
  2005                 NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
       
  2006         PrivilegedExceptionAction<Constructor<?>> action =
       
  2007                 new PrivilegedExceptionAction<Constructor<?>>() {
       
  2008             public Constructor<?> run() throws Exception {
       
  2009                 Class<RMIConnector> thisClass = RMIConnector.class;
       
  2010                 ClassLoader thisLoader = thisClass.getClassLoader();
       
  2011                 ProtectionDomain thisProtectionDomain =
       
  2012                         thisClass.getProtectionDomain();
       
  2013 
       
  2014                 String proxyRefCName = ProxyRef.class.getName();
       
  2015                 ClassLoader cl =
       
  2016                         new NoCallStackClassLoader(pRefClassName,
       
  2017                         pRefByteCode,
       
  2018                         new String[] { proxyRefCName },
       
  2019                         thisLoader,
       
  2020                         thisProtectionDomain);
       
  2021 
       
  2022                 Module jmxModule = ProxyRef.class.getModule();
       
  2023                 Module rmiModule = RemoteRef.class.getModule();
       
  2024 
       
  2025                 String pkg = packageOf(pRefClassName);
       
  2026                 assert pkg != null && pkg.length() > 0 &&
       
  2027                         !pkg.equals(packageOf(proxyRefCName));
       
  2028 
       
  2029                 ModuleDescriptor descriptor =
       
  2030                     ModuleDescriptor.newModule("jdk.remoteref", Set.of(SYNTHETIC))
       
  2031                         .packages(Set.of(pkg))
       
  2032                         .build();
       
  2033                 Module m = Modules.defineModule(cl, descriptor, null);
       
  2034 
       
  2035                 // jdk.remoteref needs to read to java.base and jmxModule
       
  2036                 Modules.addReads(m, Object.class.getModule());
       
  2037                 Modules.addReads(m, jmxModule);
       
  2038                 Modules.addReads(m, rmiModule);
       
  2039 
       
  2040                 // jdk.remoteref needs access to ProxyRef class
       
  2041                 Modules.addExports(jmxModule, packageOf(proxyRefCName), m);
       
  2042 
       
  2043                 // java.management needs to instantiate the fabricated RemoteRef class
       
  2044                 Modules.addReads(jmxModule, m);
       
  2045                 Modules.addExports(m, pkg, jmxModule);
       
  2046 
       
  2047                 Class<?> c = cl.loadClass(pRefClassName);
       
  2048                 return c.getConstructor(RemoteRef.class);
       
  2049             }
       
  2050         };
       
  2051 
       
  2052         Class<?> serverStubClass;
       
  2053         try {
       
  2054             serverStubClass = Class.forName(rmiServerImplStubClassName);
       
  2055         } catch (Exception e) {
       
  2056             logger.error("<clinit>",
       
  2057                     "Failed to instantiate " +
       
  2058                     rmiServerImplStubClassName + ": " + e);
       
  2059             logger.debug("<clinit>",e);
       
  2060             serverStubClass = null;
       
  2061         }
       
  2062         rmiServerImplStubClass = serverStubClass;
       
  2063 
       
  2064         Class<?> stubClass;
       
  2065         Constructor<?> constr;
       
  2066         try {
       
  2067             stubClass = Class.forName(rmiConnectionImplStubClassName);
       
  2068             constr = (Constructor<?>) AccessController.doPrivileged(action);
       
  2069         } catch (Exception e) {
       
  2070             logger.error("<clinit>",
       
  2071                     "Failed to initialize proxy reference constructor "+
       
  2072                     "for " + rmiConnectionImplStubClassName + ": " + e);
       
  2073             logger.debug("<clinit>",e);
       
  2074             stubClass = null;
       
  2075             constr = null;
       
  2076         }
       
  2077         rmiConnectionImplStubClass = stubClass;
       
  2078         proxyRefConstructor = constr;
       
  2079     }
       
  2080 
       
  2081     private static String packageOf(String cn) {
       
  2082         int i = cn.lastIndexOf('.');
       
  2083         return i > 0 ? cn.substring(0, i) : "";
       
  2084     }
       
  2085 
       
  2086     private static RMIConnection shadowJrmpStub(RemoteObject stub)
       
  2087     throws InstantiationException, IllegalAccessException,
       
  2088             InvocationTargetException, ClassNotFoundException,
       
  2089             NoSuchMethodException {
       
  2090         RemoteRef ref = stub.getRef();
       
  2091         RemoteRef proxyRef = (RemoteRef)
       
  2092             proxyRefConstructor.newInstance(new Object[] {ref});
       
  2093         final Constructor<?> rmiConnectionImplStubConstructor =
       
  2094             rmiConnectionImplStubClass.getConstructor(RemoteRef.class);
       
  2095         Object[] args = {proxyRef};
       
  2096         RMIConnection proxyStub = (RMIConnection)
       
  2097         rmiConnectionImplStubConstructor.newInstance(args);
       
  2098         return proxyStub;
       
  2099     }
       
  2100 
       
  2101     private static RMIConnection getConnection(RMIServer server,
       
  2102             Object credentials,
       
  2103             boolean checkStub)
       
  2104             throws IOException {
       
  2105         RMIConnection c = server.newClient(credentials);
       
  2106         if (checkStub) checkStub(c, rmiConnectionImplStubClass);
       
  2107         try {
       
  2108             if (c.getClass() == rmiConnectionImplStubClass)
       
  2109                 return shadowJrmpStub((RemoteObject) c);
       
  2110             logger.trace("getConnection",
       
  2111                     "Did not wrap " + c.getClass() + " to foil " +
       
  2112                     "stack search for classes: class loading semantics " +
       
  2113                     "may be incorrect");
       
  2114         } catch (Exception e) {
       
  2115             logger.error("getConnection",
       
  2116                     "Could not wrap " + c.getClass() + " to foil " +
       
  2117                     "stack search for classes: class loading semantics " +
       
  2118                     "may be incorrect: " + e);
       
  2119             logger.debug("getConnection",e);
       
  2120             // so just return the original stub, which will work for all
       
  2121             // but the most exotic class loading situations
       
  2122         }
       
  2123         return c;
       
  2124     }
       
  2125 
       
  2126     private static byte[] base64ToByteArray(String s) {
       
  2127         int sLen = s.length();
       
  2128         int numGroups = sLen/4;
       
  2129         if (4*numGroups != sLen)
       
  2130             throw new IllegalArgumentException(
       
  2131                     "String length must be a multiple of four.");
       
  2132         int missingBytesInLastGroup = 0;
       
  2133         int numFullGroups = numGroups;
       
  2134         if (sLen != 0) {
       
  2135             if (s.charAt(sLen-1) == '=') {
       
  2136                 missingBytesInLastGroup++;
       
  2137                 numFullGroups--;
       
  2138             }
       
  2139             if (s.charAt(sLen-2) == '=')
       
  2140                 missingBytesInLastGroup++;
       
  2141         }
       
  2142         byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
       
  2143 
       
  2144         // Translate all full groups from base64 to byte array elements
       
  2145         int inCursor = 0, outCursor = 0;
       
  2146         for (int i=0; i<numFullGroups; i++) {
       
  2147             int ch0 = base64toInt(s.charAt(inCursor++));
       
  2148             int ch1 = base64toInt(s.charAt(inCursor++));
       
  2149             int ch2 = base64toInt(s.charAt(inCursor++));
       
  2150             int ch3 = base64toInt(s.charAt(inCursor++));
       
  2151             result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
       
  2152             result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
       
  2153             result[outCursor++] = (byte) ((ch2 << 6) | ch3);
       
  2154         }
       
  2155 
       
  2156         // Translate partial group, if present
       
  2157         if (missingBytesInLastGroup != 0) {
       
  2158             int ch0 = base64toInt(s.charAt(inCursor++));
       
  2159             int ch1 = base64toInt(s.charAt(inCursor++));
       
  2160             result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
       
  2161 
       
  2162             if (missingBytesInLastGroup == 1) {
       
  2163                 int ch2 = base64toInt(s.charAt(inCursor++));
       
  2164                 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
       
  2165             }
       
  2166         }
       
  2167         // assert inCursor == s.length()-missingBytesInLastGroup;
       
  2168         // assert outCursor == result.length;
       
  2169         return result;
       
  2170     }
       
  2171 
       
  2172     /**
       
  2173      * Translates the specified character, which is assumed to be in the
       
  2174      * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
       
  2175      *
       
  2176      * @throws IllegalArgumentException if
       
  2177      *        c is not in the Base64 Alphabet.
       
  2178      */
       
  2179     private static int base64toInt(char c) {
       
  2180         int result;
       
  2181 
       
  2182         if (c >= base64ToInt.length)
       
  2183             result = -1;
       
  2184         else
       
  2185             result = base64ToInt[c];
       
  2186 
       
  2187         if (result < 0)
       
  2188             throw new IllegalArgumentException("Illegal character " + c);
       
  2189         return result;
       
  2190     }
       
  2191 
       
  2192     /**
       
  2193      * This array is a lookup table that translates unicode characters
       
  2194      * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
       
  2195      * into their 6-bit positive integer equivalents.  Characters that
       
  2196      * are not in the Base64 alphabet but fall within the bounds of the
       
  2197      * array are translated to -1.
       
  2198      */
       
  2199     private static final byte base64ToInt[] = {
       
  2200         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       
  2201         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       
  2202         -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
       
  2203         55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
       
  2204         5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
       
  2205         24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
       
  2206         35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
       
  2207     };
       
  2208 
       
  2209     //--------------------------------------------------------------------
       
  2210     // Private stuff - Find / Set default class loader
       
  2211     //--------------------------------------------------------------------
       
  2212     private ClassLoader pushDefaultClassLoader() {
       
  2213         final Thread t = Thread.currentThread();
       
  2214         final ClassLoader old =  t.getContextClassLoader();
       
  2215         if (defaultClassLoader != null)
       
  2216             AccessController.doPrivileged(new PrivilegedAction<Void>() {
       
  2217                 public Void run() {
       
  2218                     t.setContextClassLoader(defaultClassLoader);
       
  2219                     return null;
       
  2220                 }
       
  2221             });
       
  2222             return old;
       
  2223     }
       
  2224 
       
  2225     private void popDefaultClassLoader(final ClassLoader old) {
       
  2226         AccessController.doPrivileged(new PrivilegedAction<Void>() {
       
  2227             public Void run() {
       
  2228                 Thread.currentThread().setContextClassLoader(old);
       
  2229                 return null;
       
  2230             }
       
  2231         });
       
  2232     }
       
  2233 
       
  2234     //--------------------------------------------------------------------
       
  2235     // Private variables
       
  2236     //--------------------------------------------------------------------
       
  2237     /**
       
  2238      * @serial The RMIServer stub of the RMI JMX Connector server to
       
  2239      * which this client connector is (or will be) connected. This
       
  2240      * field can be null when <var>jmxServiceURL</var> is not
       
  2241      * null. This includes the case where <var>jmxServiceURL</var>
       
  2242      * contains a serialized RMIServer stub. If both
       
  2243      * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
       
  2244      * serialization will fail.
       
  2245      *
       
  2246      * @see #RMIConnector(RMIServer,Map)
       
  2247      **/
       
  2248     private final RMIServer rmiServer;
       
  2249 
       
  2250     /**
       
  2251      * @serial The JMXServiceURL of the RMI JMX Connector server to
       
  2252      * which this client connector will be connected. This field can
       
  2253      * be null when <var>rmiServer</var> is not null. If both
       
  2254      * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
       
  2255      * serialization will fail.
       
  2256      *
       
  2257      * @see #RMIConnector(JMXServiceURL,Map)
       
  2258      **/
       
  2259     private final JMXServiceURL jmxServiceURL;
       
  2260 
       
  2261     // ---------------------------------------------------------
       
  2262     // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
       
  2263     // ---------------------------------------------------------
       
  2264     // Any transient variable which needs to be initialized should
       
  2265     // be initialized in the method initTransient()
       
  2266     private transient Map<String, Object> env;
       
  2267     private transient ClassLoader defaultClassLoader;
       
  2268     private transient RMIConnection connection;
       
  2269     private transient String connectionId;
       
  2270 
       
  2271     private transient long clientNotifSeqNo = 0;
       
  2272 
       
  2273     private transient WeakHashMap<Subject, WeakReference<MBeanServerConnection>> rmbscMap;
       
  2274     private transient WeakReference<MBeanServerConnection> nullSubjectConnRef = null;
       
  2275 
       
  2276     private transient RMINotifClient rmiNotifClient;
       
  2277     // = new RMINotifClient(new Integer(0));
       
  2278 
       
  2279     private transient long clientNotifCounter = 0;
       
  2280 
       
  2281     private transient boolean connected;
       
  2282     // = false;
       
  2283     private transient boolean terminated;
       
  2284     // = false;
       
  2285 
       
  2286     private transient Exception closeException;
       
  2287 
       
  2288     private transient NotificationBroadcasterSupport connectionBroadcaster;
       
  2289 
       
  2290     private transient ClientCommunicatorAdmin communicatorAdmin;
       
  2291 
       
  2292     /**
       
  2293      * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
       
  2294      * connect unconnected stubs.
       
  2295      **/
       
  2296     private static volatile WeakReference<Object> orb = null;
       
  2297 
       
  2298     // TRACES & DEBUG
       
  2299     //---------------
       
  2300     private static String objects(final Object[] objs) {
       
  2301         if (objs == null)
       
  2302             return "null";
       
  2303         else
       
  2304             return Arrays.asList(objs).toString();
       
  2305     }
       
  2306 
       
  2307     private static String strings(final String[] strs) {
       
  2308         return objects(strs);
       
  2309     }
       
  2310 
       
  2311     static String getAttributesNames(AttributeList attributes) {
       
  2312         return attributes != null ?
       
  2313                 attributes.asList().stream()
       
  2314                         .map(Attribute::getName)
       
  2315                         .collect(Collectors.joining(", ", "[", "]"))
       
  2316                 : "[]";
       
  2317     }
       
  2318 }