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