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