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