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