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 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             throw new IOException("Failed to close: " + closeException, closeException);
 547         }
 548     }
 549 
 550     // added for re-connection
 551     private Integer addListenerWithSubject(ObjectName name,
 552                                            MarshalledObject<NotificationFilter> filter,
 553                                            Subject delegationSubject,
 554                                            boolean reconnect)
 555         throws InstanceNotFoundException, IOException {
 556 
 557         final boolean debug = logger.debugOn();
 558         if (debug)
 559             logger.debug("addListenerWithSubject",
 560                     "(ObjectName,MarshalledObject,Subject)");
 561 
 562         final ObjectName[] names = new ObjectName[] {name};
 563         final MarshalledObject<NotificationFilter>[] filters =
 564                 Util.cast(new MarshalledObject<?>[] {filter});
 565         final Subject[] delegationSubjects = new Subject[] {
 566             delegationSubject
 567         };
 568 
 569         final Integer[] listenerIDs =
 570                 addListenersWithSubjects(names,filters,delegationSubjects,
 571                 reconnect);
 572 
 573         if (debug) logger.debug("addListenerWithSubject","listenerID="
 574                 + listenerIDs[0]);
 575         return listenerIDs[0];
 576     }
 577 
 578     // added for re-connection
 579     private Integer[] addListenersWithSubjects(ObjectName[]       names,
 580                              MarshalledObject<NotificationFilter>[] filters,
 581                              Subject[]          delegationSubjects,
 582                              boolean            reconnect)
 583         throws InstanceNotFoundException, IOException {
 584 
 585         final boolean debug = logger.debugOn();
 586         if (debug)
 587             logger.debug("addListenersWithSubjects",
 588                     "(ObjectName[],MarshalledObject[],Subject[])");
 589 
 590         final ClassLoader old = pushDefaultClassLoader();
 591         Integer[] listenerIDs = null;
 592 
 593         try {
 594             listenerIDs = connection.addNotificationListeners(names,
 595                     filters,
 596                     delegationSubjects);
 597         } catch (NoSuchObjectException noe) {
 598             // maybe reconnect
 599             if (reconnect) {
 600                 communicatorAdmin.gotIOException(noe);
 601 
 602                 listenerIDs = connection.addNotificationListeners(names,
 603                         filters,
 604                         delegationSubjects);
 605             } else {
 606                 throw noe;
 607             }
 608         } catch (IOException ioe) {
 609             // send a failed notif if necessary
 610             communicatorAdmin.gotIOException(ioe);
 611         } finally {
 612             popDefaultClassLoader(old);
 613         }
 614 
 615         if (debug) logger.debug("addListenersWithSubjects","registered "
 616                 + ((listenerIDs==null)?0:listenerIDs.length)
 617                 + " listener(s)");
 618         return listenerIDs;
 619     }
 620 
 621     //--------------------------------------------------------------------
 622     // Implementation of MBeanServerConnection
 623     //--------------------------------------------------------------------
 624     private class RemoteMBeanServerConnection implements MBeanServerConnection {
 625         private Subject delegationSubject;
 626 
 627         public RemoteMBeanServerConnection() {
 628             this(null);
 629         }
 630 
 631         public RemoteMBeanServerConnection(Subject delegationSubject) {
 632             this.delegationSubject = delegationSubject;
 633         }
 634 
 635         public ObjectInstance createMBean(String className,
 636                 ObjectName name)
 637                 throws ReflectionException,
 638                 InstanceAlreadyExistsException,
 639                 MBeanRegistrationException,
 640                 MBeanException,
 641                 NotCompliantMBeanException,
 642                 IOException {
 643             if (logger.debugOn())
 644                 logger.debug("createMBean(String,ObjectName)",
 645                         "className=" + className + ", name=" +
 646                         name);
 647 
 648             final ClassLoader old = pushDefaultClassLoader();
 649             try {
 650                 return connection.createMBean(className,
 651                         name,
 652                         delegationSubject);
 653             } catch (IOException ioe) {
 654                 communicatorAdmin.gotIOException(ioe);
 655 
 656                 return connection.createMBean(className,
 657                         name,
 658                         delegationSubject);
 659             } finally {
 660                 popDefaultClassLoader(old);
 661             }
 662         }
 663 
 664         public ObjectInstance createMBean(String className,
 665                 ObjectName name,
 666                 ObjectName loaderName)
 667                 throws ReflectionException,
 668                 InstanceAlreadyExistsException,
 669                 MBeanRegistrationException,
 670                 MBeanException,
 671                 NotCompliantMBeanException,
 672                 InstanceNotFoundException,
 673                 IOException {
 674 
 675             if (logger.debugOn())
 676                 logger.debug("createMBean(String,ObjectName,ObjectName)",
 677                         "className=" + className + ", name="
 678                         + name + ", loaderName="
 679                         + loaderName + ")");
 680 
 681             final ClassLoader old = pushDefaultClassLoader();
 682             try {
 683                 return connection.createMBean(className,
 684                         name,
 685                         loaderName,
 686                         delegationSubject);
 687 
 688             } catch (IOException ioe) {
 689                 communicatorAdmin.gotIOException(ioe);
 690 
 691                 return connection.createMBean(className,
 692                         name,
 693                         loaderName,
 694                         delegationSubject);
 695 
 696             } finally {
 697                 popDefaultClassLoader(old);
 698             }
 699         }
 700 
 701         public ObjectInstance createMBean(String className,
 702                 ObjectName name,
 703                 Object params[],
 704                 String signature[])
 705                 throws ReflectionException,
 706                 InstanceAlreadyExistsException,
 707                 MBeanRegistrationException,
 708                 MBeanException,
 709                 NotCompliantMBeanException,
 710                 IOException {
 711             if (logger.debugOn())
 712                 logger.debug("createMBean(String,ObjectName,Object[],String[])",
 713                         "className=" + className + ", name="
 714                         + name + ", signature=" + strings(signature));
 715 
 716             final MarshalledObject<Object[]> sParams =
 717                     new MarshalledObject<Object[]>(params);
 718             final ClassLoader old = pushDefaultClassLoader();
 719             try {
 720                 return connection.createMBean(className,
 721                         name,
 722                         sParams,
 723                         signature,
 724                         delegationSubject);
 725             } catch (IOException ioe) {
 726                 communicatorAdmin.gotIOException(ioe);
 727 
 728                 return connection.createMBean(className,
 729                         name,
 730                         sParams,
 731                         signature,
 732                         delegationSubject);
 733             } finally {
 734                 popDefaultClassLoader(old);
 735             }
 736         }
 737 
 738         public ObjectInstance createMBean(String className,
 739                 ObjectName name,
 740                 ObjectName loaderName,
 741                 Object params[],
 742                 String signature[])
 743                 throws ReflectionException,
 744                 InstanceAlreadyExistsException,
 745                 MBeanRegistrationException,
 746                 MBeanException,
 747                 NotCompliantMBeanException,
 748                 InstanceNotFoundException,
 749                 IOException {
 750             if (logger.debugOn()) logger.debug(
 751                     "createMBean(String,ObjectName,ObjectName,Object[],String[])",
 752                     "className=" + className + ", name=" + name + ", loaderName="
 753                     + loaderName + ", signature=" + strings(signature));
 754 
 755             final MarshalledObject<Object[]> sParams =
 756                     new MarshalledObject<Object[]>(params);
 757             final ClassLoader old = pushDefaultClassLoader();
 758             try {
 759                 return connection.createMBean(className,
 760                         name,
 761                         loaderName,
 762                         sParams,
 763                         signature,
 764                         delegationSubject);
 765             } catch (IOException ioe) {
 766                 communicatorAdmin.gotIOException(ioe);
 767 
 768                 return connection.createMBean(className,
 769                         name,
 770                         loaderName,
 771                         sParams,
 772                         signature,
 773                         delegationSubject);
 774             } finally {
 775                 popDefaultClassLoader(old);
 776             }
 777         }
 778 
 779         public void unregisterMBean(ObjectName name)
 780         throws InstanceNotFoundException,
 781                 MBeanRegistrationException,
 782                 IOException {
 783             if (logger.debugOn())
 784                 logger.debug("unregisterMBean", "name=" + name);
 785 
 786             final ClassLoader old = pushDefaultClassLoader();
 787             try {
 788                 connection.unregisterMBean(name, delegationSubject);
 789             } catch (IOException ioe) {
 790                 communicatorAdmin.gotIOException(ioe);
 791 
 792                 connection.unregisterMBean(name, delegationSubject);
 793             } finally {
 794                 popDefaultClassLoader(old);
 795             }
 796         }
 797 
 798         public ObjectInstance getObjectInstance(ObjectName name)
 799         throws InstanceNotFoundException,
 800                 IOException {
 801             if (logger.debugOn())
 802                 logger.debug("getObjectInstance", "name=" + name);
 803 
 804             final ClassLoader old = pushDefaultClassLoader();
 805             try {
 806                 return connection.getObjectInstance(name, delegationSubject);
 807             } catch (IOException ioe) {
 808                 communicatorAdmin.gotIOException(ioe);
 809 
 810                 return connection.getObjectInstance(name, delegationSubject);
 811             } finally {
 812                 popDefaultClassLoader(old);
 813             }
 814         }
 815 
 816         public Set<ObjectInstance> queryMBeans(ObjectName name,
 817                 QueryExp query)
 818                 throws IOException {
 819             if (logger.debugOn()) logger.debug("queryMBeans",
 820                     "name=" + name + ", query=" + query);
 821 
 822             final MarshalledObject<QueryExp> sQuery =
 823                     new MarshalledObject<QueryExp>(query);
 824             final ClassLoader old = pushDefaultClassLoader();
 825             try {
 826                 return connection.queryMBeans(name, sQuery, delegationSubject);
 827             } catch (IOException ioe) {
 828                 communicatorAdmin.gotIOException(ioe);
 829 
 830                 return connection.queryMBeans(name, sQuery, delegationSubject);
 831             } finally {
 832                 popDefaultClassLoader(old);
 833             }
 834         }
 835 
 836         public Set<ObjectName> queryNames(ObjectName name,
 837                 QueryExp query)
 838                 throws IOException {
 839             if (logger.debugOn()) logger.debug("queryNames",
 840                     "name=" + name + ", query=" + query);
 841 
 842             final MarshalledObject<QueryExp> sQuery =
 843                     new MarshalledObject<QueryExp>(query);
 844             final ClassLoader old = pushDefaultClassLoader();
 845             try {
 846                 return connection.queryNames(name, sQuery, delegationSubject);
 847             } catch (IOException ioe) {
 848                 communicatorAdmin.gotIOException(ioe);
 849 
 850                 return connection.queryNames(name, sQuery, delegationSubject);
 851             } finally {
 852                 popDefaultClassLoader(old);
 853             }
 854         }
 855 
 856         public boolean isRegistered(ObjectName name)
 857         throws IOException {
 858             if (logger.debugOn())
 859                 logger.debug("isRegistered", "name=" + name);
 860 
 861             final ClassLoader old = pushDefaultClassLoader();
 862             try {
 863                 return connection.isRegistered(name, delegationSubject);
 864             } catch (IOException ioe) {
 865                 communicatorAdmin.gotIOException(ioe);
 866 
 867                 return connection.isRegistered(name, delegationSubject);
 868             } finally {
 869                 popDefaultClassLoader(old);
 870             }
 871         }
 872 
 873         public Integer getMBeanCount()
 874         throws IOException {
 875             if (logger.debugOn()) logger.debug("getMBeanCount", "");
 876 
 877             final ClassLoader old = pushDefaultClassLoader();
 878             try {
 879                 return connection.getMBeanCount(delegationSubject);
 880             } catch (IOException ioe) {
 881                 communicatorAdmin.gotIOException(ioe);
 882 
 883                 return connection.getMBeanCount(delegationSubject);
 884             } finally {
 885                 popDefaultClassLoader(old);
 886             }
 887         }
 888 
 889         public Object getAttribute(ObjectName name,
 890                 String attribute)
 891                 throws MBeanException,
 892                 AttributeNotFoundException,
 893                 InstanceNotFoundException,
 894                 ReflectionException,
 895                 IOException {
 896             if (logger.debugOn()) logger.debug("getAttribute",
 897                     "name=" + name + ", attribute="
 898                     + attribute);
 899 
 900             final ClassLoader old = pushDefaultClassLoader();
 901             try {
 902                 return connection.getAttribute(name,
 903                         attribute,
 904                         delegationSubject);
 905             } catch (IOException ioe) {
 906                 communicatorAdmin.gotIOException(ioe);
 907 
 908                 return connection.getAttribute(name,
 909                         attribute,
 910                         delegationSubject);
 911             } finally {
 912                 popDefaultClassLoader(old);
 913             }
 914         }
 915 
 916         public AttributeList getAttributes(ObjectName name,
 917                 String[] attributes)
 918                 throws InstanceNotFoundException,
 919                 ReflectionException,
 920                 IOException {
 921             if (logger.debugOn()) logger.debug("getAttributes",
 922                     "name=" + name + ", attributes="
 923                     + strings(attributes));
 924 
 925             final ClassLoader old = pushDefaultClassLoader();
 926             try {
 927                 return connection.getAttributes(name,
 928                         attributes,
 929                         delegationSubject);
 930 
 931             } catch (IOException ioe) {
 932                 communicatorAdmin.gotIOException(ioe);
 933 
 934                 return connection.getAttributes(name,
 935                         attributes,
 936                         delegationSubject);
 937             } finally {
 938                 popDefaultClassLoader(old);
 939             }
 940         }
 941 
 942 
 943         public void setAttribute(ObjectName name,
 944                 Attribute attribute)
 945                 throws InstanceNotFoundException,
 946                 AttributeNotFoundException,
 947                 InvalidAttributeValueException,
 948                 MBeanException,
 949                 ReflectionException,
 950                 IOException {
 951 
 952             if (logger.debugOn()) logger.debug("setAttribute",
 953                     "name=" + name + ", attribute name="
 954                     + attribute.getName());
 955 
 956             final MarshalledObject<Attribute> sAttribute =
 957                     new MarshalledObject<Attribute>(attribute);
 958             final ClassLoader old = pushDefaultClassLoader();
 959             try {
 960                 connection.setAttribute(name, sAttribute, delegationSubject);
 961             } catch (IOException ioe) {
 962                 communicatorAdmin.gotIOException(ioe);
 963 
 964                 connection.setAttribute(name, sAttribute, delegationSubject);
 965             } finally {
 966                 popDefaultClassLoader(old);
 967             }
 968         }
 969 
 970         public AttributeList setAttributes(ObjectName name,
 971                 AttributeList attributes)
 972                 throws InstanceNotFoundException,
 973                 ReflectionException,
 974                 IOException {
 975 
 976             if (logger.debugOn()) {
 977                 logger.debug("setAttributes",
 978                     "name=" + name + ", attribute names="
 979                     + getAttributesNames(attributes));
 980             }
 981 
 982             final MarshalledObject<AttributeList> sAttributes =
 983                     new MarshalledObject<AttributeList>(attributes);
 984             final ClassLoader old = pushDefaultClassLoader();
 985             try {
 986                 return connection.setAttributes(name,
 987                         sAttributes,
 988                         delegationSubject);
 989             } catch (IOException ioe) {
 990                 communicatorAdmin.gotIOException(ioe);
 991 
 992                 return connection.setAttributes(name,
 993                         sAttributes,
 994                         delegationSubject);
 995             } finally {
 996                 popDefaultClassLoader(old);
 997             }
 998         }
 999 
1000 
1001         public Object invoke(ObjectName name,
1002                 String operationName,
1003                 Object params[],
1004                 String signature[])
1005                 throws InstanceNotFoundException,
1006                 MBeanException,
1007                 ReflectionException,
1008                 IOException {
1009 
1010             if (logger.debugOn()) logger.debug("invoke",
1011                     "name=" + name
1012                     + ", operationName=" + operationName
1013                     + ", signature=" + strings(signature));
1014 
1015             final MarshalledObject<Object[]> sParams =
1016                     new MarshalledObject<Object[]>(params);
1017             final ClassLoader old = pushDefaultClassLoader();
1018             try {
1019                 return connection.invoke(name,
1020                         operationName,
1021                         sParams,
1022                         signature,
1023                         delegationSubject);
1024             } catch (IOException ioe) {
1025                 communicatorAdmin.gotIOException(ioe);
1026 
1027                 return connection.invoke(name,
1028                         operationName,
1029                         sParams,
1030                         signature,
1031                         delegationSubject);
1032             } finally {
1033                 popDefaultClassLoader(old);
1034             }
1035         }
1036 
1037 
1038         public String getDefaultDomain()
1039         throws IOException {
1040             if (logger.debugOn()) logger.debug("getDefaultDomain", "");
1041 
1042             final ClassLoader old = pushDefaultClassLoader();
1043             try {
1044                 return connection.getDefaultDomain(delegationSubject);
1045             } catch (IOException ioe) {
1046                 communicatorAdmin.gotIOException(ioe);
1047 
1048                 return connection.getDefaultDomain(delegationSubject);
1049             } finally {
1050                 popDefaultClassLoader(old);
1051             }
1052         }
1053 
1054         public String[] getDomains() throws IOException {
1055             if (logger.debugOn()) logger.debug("getDomains", "");
1056 
1057             final ClassLoader old = pushDefaultClassLoader();
1058             try {
1059                 return connection.getDomains(delegationSubject);
1060             } catch (IOException ioe) {
1061                 communicatorAdmin.gotIOException(ioe);
1062 
1063                 return connection.getDomains(delegationSubject);
1064             } finally {
1065                 popDefaultClassLoader(old);
1066             }
1067         }
1068 
1069         public MBeanInfo getMBeanInfo(ObjectName name)
1070         throws InstanceNotFoundException,
1071                 IntrospectionException,
1072                 ReflectionException,
1073                 IOException {
1074 
1075             if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name);
1076             final ClassLoader old = pushDefaultClassLoader();
1077             try {
1078                 return connection.getMBeanInfo(name, delegationSubject);
1079             } catch (IOException ioe) {
1080                 communicatorAdmin.gotIOException(ioe);
1081 
1082                 return connection.getMBeanInfo(name, delegationSubject);
1083             } finally {
1084                 popDefaultClassLoader(old);
1085             }
1086         }
1087 
1088 
1089         public boolean isInstanceOf(ObjectName name,
1090                 String className)
1091                 throws InstanceNotFoundException,
1092                 IOException {
1093             if (logger.debugOn())
1094                 logger.debug("isInstanceOf", "name=" + name +
1095                         ", className=" + className);
1096 
1097             final ClassLoader old = pushDefaultClassLoader();
1098             try {
1099                 return connection.isInstanceOf(name,
1100                         className,
1101                         delegationSubject);
1102             } catch (IOException ioe) {
1103                 communicatorAdmin.gotIOException(ioe);
1104 
1105                 return connection.isInstanceOf(name,
1106                         className,
1107                         delegationSubject);
1108             } finally {
1109                 popDefaultClassLoader(old);
1110             }
1111         }
1112 
1113         public void addNotificationListener(ObjectName name,
1114                 ObjectName listener,
1115                 NotificationFilter filter,
1116                 Object handback)
1117                 throws InstanceNotFoundException,
1118                 IOException {
1119 
1120             if (logger.debugOn())
1121                 logger.debug("addNotificationListener" +
1122                         "(ObjectName,ObjectName,NotificationFilter,Object)",
1123                         "name=" + name + ", listener=" + listener
1124                         + ", filter=" + filter + ", handback=" + handback);
1125 
1126             final MarshalledObject<NotificationFilter> sFilter =
1127                     new MarshalledObject<NotificationFilter>(filter);
1128             final MarshalledObject<Object> sHandback =
1129                     new MarshalledObject<Object>(handback);
1130             final ClassLoader old = pushDefaultClassLoader();
1131             try {
1132                 connection.addNotificationListener(name,
1133                         listener,
1134                         sFilter,
1135                         sHandback,
1136                         delegationSubject);
1137             } catch (IOException ioe) {
1138                 communicatorAdmin.gotIOException(ioe);
1139 
1140                 connection.addNotificationListener(name,
1141                         listener,
1142                         sFilter,
1143                         sHandback,
1144                         delegationSubject);
1145             } finally {
1146                 popDefaultClassLoader(old);
1147             }
1148         }
1149 
1150         public void removeNotificationListener(ObjectName name,
1151                 ObjectName listener)
1152                 throws InstanceNotFoundException,
1153                 ListenerNotFoundException,
1154                 IOException {
1155 
1156             if (logger.debugOn()) logger.debug("removeNotificationListener" +
1157                     "(ObjectName,ObjectName)",
1158                     "name=" + name
1159                     + ", listener=" + listener);
1160 
1161             final ClassLoader old = pushDefaultClassLoader();
1162             try {
1163                 connection.removeNotificationListener(name,
1164                         listener,
1165                         delegationSubject);
1166             } catch (IOException ioe) {
1167                 communicatorAdmin.gotIOException(ioe);
1168 
1169                 connection.removeNotificationListener(name,
1170                         listener,
1171                         delegationSubject);
1172             } finally {
1173                 popDefaultClassLoader(old);
1174             }
1175         }
1176 
1177         public void removeNotificationListener(ObjectName name,
1178                 ObjectName listener,
1179                 NotificationFilter filter,
1180                 Object handback)
1181                 throws InstanceNotFoundException,
1182                 ListenerNotFoundException,
1183                 IOException {
1184             if (logger.debugOn())
1185                 logger.debug("removeNotificationListener" +
1186                         "(ObjectName,ObjectName,NotificationFilter,Object)",
1187                         "name=" + name
1188                         + ", listener=" + listener
1189                         + ", filter=" + filter
1190                         + ", handback=" + handback);
1191 
1192             final MarshalledObject<NotificationFilter> sFilter =
1193                     new MarshalledObject<NotificationFilter>(filter);
1194             final MarshalledObject<Object> sHandback =
1195                     new MarshalledObject<Object>(handback);
1196             final ClassLoader old = pushDefaultClassLoader();
1197             try {
1198                 connection.removeNotificationListener(name,
1199                         listener,
1200                         sFilter,
1201                         sHandback,
1202                         delegationSubject);
1203             } catch (IOException ioe) {
1204                 communicatorAdmin.gotIOException(ioe);
1205 
1206                 connection.removeNotificationListener(name,
1207                         listener,
1208                         sFilter,
1209                         sHandback,
1210                         delegationSubject);
1211             } finally {
1212                 popDefaultClassLoader(old);
1213             }
1214         }
1215 
1216         // Specific Notification Handle ----------------------------------
1217 
1218         public void addNotificationListener(ObjectName name,
1219                 NotificationListener listener,
1220                 NotificationFilter filter,
1221                 Object handback)
1222                 throws InstanceNotFoundException,
1223                 IOException {
1224 
1225             final boolean debug = logger.debugOn();
1226 
1227             if (debug)
1228                 logger.debug("addNotificationListener" +
1229                         "(ObjectName,NotificationListener,"+
1230                         "NotificationFilter,Object)",
1231                         "name=" + name
1232                         + ", listener=" + listener
1233                         + ", filter=" + filter
1234                         + ", handback=" + handback);
1235 
1236             final Integer listenerID =
1237                     addListenerWithSubject(name,
1238                     new MarshalledObject<NotificationFilter>(filter),
1239                     delegationSubject,true);
1240             rmiNotifClient.addNotificationListener(listenerID, name, listener,
1241                     filter, handback,
1242                     delegationSubject);
1243         }
1244 
1245         public void removeNotificationListener(ObjectName name,
1246                 NotificationListener listener)
1247                 throws InstanceNotFoundException,
1248                 ListenerNotFoundException,
1249                 IOException {
1250 
1251             final boolean debug = logger.debugOn();
1252 
1253             if (debug) logger.debug("removeNotificationListener"+
1254                     "(ObjectName,NotificationListener)",
1255                     "name=" + name
1256                     + ", listener=" + listener);
1257 
1258             final Integer[] ret =
1259                     rmiNotifClient.getListenerIds(name, listener);
1260 
1261             if (debug) logger.debug("removeNotificationListener",
1262                     "listenerIDs=" + objects(ret));
1263 
1264             final ClassLoader old = pushDefaultClassLoader();
1265 
1266             try {
1267                 connection.removeNotificationListeners(name,
1268                         ret,
1269                         delegationSubject);
1270             } catch (IOException ioe) {
1271                 communicatorAdmin.gotIOException(ioe);
1272 
1273                 connection.removeNotificationListeners(name,
1274                         ret,
1275                         delegationSubject);
1276             } finally {
1277                 popDefaultClassLoader(old);
1278             }
1279             rmiNotifClient.removeNotificationListener(name, listener);
1280         }
1281 
1282         public void removeNotificationListener(ObjectName name,
1283                 NotificationListener listener,
1284                 NotificationFilter filter,
1285                 Object handback)
1286                 throws InstanceNotFoundException,
1287                 ListenerNotFoundException,
1288                 IOException {
1289             final boolean debug = logger.debugOn();
1290 
1291             if (debug)
1292                 logger.debug("removeNotificationListener"+
1293                         "(ObjectName,NotificationListener,"+
1294                         "NotificationFilter,Object)",
1295                         "name=" + name
1296                         + ", listener=" + listener
1297                         + ", filter=" + filter
1298                         + ", handback=" + handback);
1299 
1300             final Integer ret =
1301                     rmiNotifClient.getListenerId(name, listener,
1302                     filter, handback);
1303 
1304             if (debug) logger.debug("removeNotificationListener",
1305                     "listenerID=" + ret);
1306 
1307             final ClassLoader old = pushDefaultClassLoader();
1308             try {
1309                 connection.removeNotificationListeners(name,
1310                         new Integer[] {ret},
1311                         delegationSubject);
1312             } catch (IOException ioe) {
1313                 communicatorAdmin.gotIOException(ioe);
1314 
1315                 connection.removeNotificationListeners(name,
1316                         new Integer[] {ret},
1317                         delegationSubject);
1318             } finally {
1319                 popDefaultClassLoader(old);
1320             }
1321             rmiNotifClient.removeNotificationListener(name, listener,
1322                     filter, handback);
1323         }
1324     }
1325 
1326     //--------------------------------------------------------------------
1327     private class RMINotifClient extends ClientNotifForwarder {
1328         public RMINotifClient(ClassLoader cl, Map<String, ?> env) {
1329             super(cl, env);
1330         }
1331 
1332         protected NotificationResult fetchNotifs(long clientSequenceNumber,
1333                 int maxNotifications,
1334                 long timeout)
1335                 throws IOException, ClassNotFoundException {
1336 
1337             boolean retried = false;
1338             while (true) { // used for a successful re-connection
1339                            // or a transient network problem
1340                 try {
1341                     return connection.fetchNotifications(clientSequenceNumber,
1342                             maxNotifications,
1343                             timeout); // return normally
1344                 } catch (IOException ioe) {
1345                     // Examine the chain of exceptions to determine whether this
1346                     // is a deserialization issue. If so - we propagate the
1347                     // appropriate exception to the caller, who will then
1348                     // proceed with fetching notifications one by one
1349                     rethrowDeserializationException(ioe);
1350 
1351                     try {
1352                         communicatorAdmin.gotIOException(ioe);
1353                         // reconnection OK, back to "while" to do again
1354                     } catch (IOException ee) {
1355                         boolean toClose = false;
1356 
1357                         synchronized (this) {
1358                             if (terminated) {
1359                                 // the connection is closed.
1360                                 throw ioe;
1361                             } else if (retried) {
1362                                 toClose = true;
1363                             }
1364                         }
1365 
1366                         if (toClose) {
1367                             // JDK-8049303
1368                             // We received an IOException - but the communicatorAdmin
1369                             // did not close the connection - possibly because
1370                             // the original exception was raised by a transient network
1371                             // problem?
1372                             // We already know that this exception is not due to a deserialization
1373                             // issue as we already took care of that before involving the
1374                             // communicatorAdmin. Moreover - we already made one retry attempt
1375                             // at fetching the same batch of notifications - and the
1376                             // problem persisted.
1377                             // Since trying again doesn't seem to solve the issue, we will now
1378                             // close the connection. Doing otherwise might cause the
1379                             // NotifFetcher thread to die silently.
1380                             final Notification failedNotif =
1381                                     new JMXConnectionNotification(
1382                                     JMXConnectionNotification.FAILED,
1383                                     this,
1384                                     connectionId,
1385                                     clientNotifSeqNo++,
1386                                     "Failed to communicate with the server: " + ioe.toString(),
1387                                     ioe);
1388 
1389                             sendNotification(failedNotif);
1390 
1391                             try {
1392                                 close(true);
1393                             } catch (Exception e) {
1394                                 // OK.
1395                                 // We are closing
1396                             }
1397                             throw ioe; // the connection is closed here.
1398                         } else {
1399                             // JDK-8049303 possible transient network problem,
1400                             // let's try one more time
1401                             retried = true;
1402                         }
1403                     }
1404                 }
1405             }
1406         }
1407 
1408         private void rethrowDeserializationException(IOException ioe)
1409                 throws ClassNotFoundException, IOException {
1410             // specially treating for an UnmarshalException
1411             if (ioe instanceof UnmarshalException) {
1412                 NotSerializableException nse = new NotSerializableException();
1413                 nse.initCause(ioe);
1414                 throw nse; // the fix of 6937053 made ClientNotifForwarder.fetchNotifs
1415                            // fetch one by one with UnmarshalException
1416             }
1417 
1418             // Not serialization problem, return.
1419         }
1420 
1421         protected Integer addListenerForMBeanRemovedNotif()
1422         throws IOException, InstanceNotFoundException {
1423             NotificationFilterSupport clientFilter =
1424                     new NotificationFilterSupport();
1425             clientFilter.enableType(
1426                     MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
1427             MarshalledObject<NotificationFilter> sFilter =
1428                 new MarshalledObject<NotificationFilter>(clientFilter);
1429 
1430             Integer[] listenerIDs;
1431             final ObjectName[] names =
1432                 new ObjectName[] {MBeanServerDelegate.DELEGATE_NAME};
1433             final MarshalledObject<NotificationFilter>[] filters =
1434                 Util.cast(new MarshalledObject<?>[] {sFilter});
1435             final Subject[] subjects = new Subject[] {null};
1436             try {
1437                 listenerIDs =
1438                         connection.addNotificationListeners(names,
1439                         filters,
1440                         subjects);
1441 
1442             } catch (IOException ioe) {
1443                 communicatorAdmin.gotIOException(ioe);
1444 
1445                 listenerIDs =
1446                         connection.addNotificationListeners(names,
1447                         filters,
1448                         subjects);
1449             }
1450             return listenerIDs[0];
1451         }
1452 
1453         protected void removeListenerForMBeanRemovedNotif(Integer id)
1454         throws IOException, InstanceNotFoundException,
1455                 ListenerNotFoundException {
1456             try {
1457                 connection.removeNotificationListeners(
1458                         MBeanServerDelegate.DELEGATE_NAME,
1459                         new Integer[] {id},
1460                         null);
1461             } catch (IOException ioe) {
1462                 communicatorAdmin.gotIOException(ioe);
1463 
1464                 connection.removeNotificationListeners(
1465                         MBeanServerDelegate.DELEGATE_NAME,
1466                         new Integer[] {id},
1467                         null);
1468             }
1469 
1470         }
1471 
1472         protected void lostNotifs(String message, long number) {
1473             final String notifType = JMXConnectionNotification.NOTIFS_LOST;
1474 
1475             final JMXConnectionNotification n =
1476                 new JMXConnectionNotification(notifType,
1477                                               RMIConnector.this,
1478                                               connectionId,
1479                                               clientNotifCounter++,
1480                                               message,
1481                                               Long.valueOf(number));
1482             sendNotification(n);
1483         }
1484     }
1485 
1486     private class RMIClientCommunicatorAdmin extends ClientCommunicatorAdmin {
1487         public RMIClientCommunicatorAdmin(long period) {
1488             super(period);
1489         }
1490 
1491         @Override
1492         public void gotIOException(IOException ioe) throws IOException {
1493             if (ioe instanceof NoSuchObjectException) {
1494                 // need to restart
1495                 super.gotIOException(ioe);
1496 
1497                 return;
1498             }
1499 
1500             // check if the connection is broken
1501             try {
1502                 connection.getDefaultDomain(null);
1503             } catch (IOException ioexc) {
1504                 boolean toClose = false;
1505 
1506                 synchronized(this) {
1507                     if (!terminated) {
1508                         terminated = true;
1509 
1510                         toClose = true;
1511                     }
1512                 }
1513 
1514                 if (toClose) {
1515                     // we should close the connection,
1516                     // but send a failed notif at first
1517                     final Notification failedNotif =
1518                             new JMXConnectionNotification(
1519                             JMXConnectionNotification.FAILED,
1520                             this,
1521                             connectionId,
1522                             clientNotifSeqNo++,
1523                             "Failed to communicate with the server: "+ioe.toString(),
1524                             ioe);
1525 
1526                     sendNotification(failedNotif);
1527 
1528                     try {
1529                         close(true);
1530                     } catch (Exception e) {
1531                         // OK.
1532                         // We are closing
1533                     }
1534                 }
1535             }
1536 
1537             // forward the exception
1538             if (ioe instanceof ServerException) {
1539                 /* Need to unwrap the exception.
1540                    Some user-thrown exception at server side will be wrapped by
1541                    rmi into a ServerException.
1542                    For example, a RMIConnnectorServer will wrap a
1543                    ClassNotFoundException into a UnmarshalException, and rmi
1544                    will throw a ServerException at client side which wraps this
1545                    UnmarshalException.
1546                    No failed notif here.
1547                  */
1548                 Throwable tt = ((ServerException)ioe).detail;
1549 
1550                 if (tt instanceof IOException) {
1551                     throw (IOException)tt;
1552                 } else if (tt instanceof RuntimeException) {
1553                     throw (RuntimeException)tt;
1554                 }
1555             }
1556 
1557             throw ioe;
1558         }
1559 
1560         public void reconnectNotificationListeners(ClientListenerInfo[] old) throws IOException {
1561             final int len  = old.length;
1562             int i;
1563 
1564             ClientListenerInfo[] clis = new ClientListenerInfo[len];
1565 
1566             final Subject[] subjects = new Subject[len];
1567             final ObjectName[] names = new ObjectName[len];
1568             final NotificationListener[] listeners = new NotificationListener[len];
1569             final NotificationFilter[] filters = new NotificationFilter[len];
1570             final MarshalledObject<NotificationFilter>[] mFilters =
1571                     Util.cast(new MarshalledObject<?>[len]);
1572             final Object[] handbacks = new Object[len];
1573 
1574             for (i=0;i<len;i++) {
1575                 subjects[i]  = old[i].getDelegationSubject();
1576                 names[i]     = old[i].getObjectName();
1577                 listeners[i] = old[i].getListener();
1578                 filters[i]   = old[i].getNotificationFilter();
1579                 mFilters[i]  = new MarshalledObject<NotificationFilter>(filters[i]);
1580                 handbacks[i] = old[i].getHandback();
1581             }
1582 
1583             try {
1584                 Integer[] ids = addListenersWithSubjects(names,mFilters,subjects,false);
1585 
1586                 for (i=0;i<len;i++) {
1587                     clis[i] = new ClientListenerInfo(ids[i],
1588                             names[i],
1589                             listeners[i],
1590                             filters[i],
1591                             handbacks[i],
1592                             subjects[i]);
1593                 }
1594 
1595                 rmiNotifClient.postReconnection(clis);
1596 
1597                 return;
1598             } catch (InstanceNotFoundException infe) {
1599                 // OK, we will do one by one
1600             }
1601 
1602             int j = 0;
1603             for (i=0;i<len;i++) {
1604                 try {
1605                     Integer id = addListenerWithSubject(names[i],
1606                             new MarshalledObject<NotificationFilter>(filters[i]),
1607                             subjects[i],
1608                             false);
1609 
1610                     clis[j++] = new ClientListenerInfo(id,
1611                             names[i],
1612                             listeners[i],
1613                             filters[i],
1614                             handbacks[i],
1615                             subjects[i]);
1616                 } catch (InstanceNotFoundException infe) {
1617                     logger.warning("reconnectNotificationListeners",
1618                             "Can't reconnect listener for " +
1619                             names[i]);
1620                 }
1621             }
1622 
1623             if (j != len) {
1624                 ClientListenerInfo[] tmp = clis;
1625                 clis = new ClientListenerInfo[j];
1626                 System.arraycopy(tmp, 0, clis, 0, j);
1627             }
1628 
1629             rmiNotifClient.postReconnection(clis);
1630         }
1631 
1632         protected void checkConnection() throws IOException {
1633             if (logger.debugOn())
1634                 logger.debug("RMIClientCommunicatorAdmin-checkConnection",
1635                         "Calling the method getDefaultDomain.");
1636 
1637             connection.getDefaultDomain(null);
1638         }
1639 
1640         protected void doStart() throws IOException {
1641             // Get RMIServer stub from directory or URL encoding if needed.
1642             RMIServer stub;
1643             try {
1644                 stub = (rmiServer!=null)?rmiServer:
1645                     findRMIServer(jmxServiceURL, env);
1646             } catch (NamingException ne) {
1647                 throw new IOException("Failed to get a RMI stub: "+ne);
1648             }
1649 
1650             // Calling newClient on the RMIServer stub.
1651             Object credentials = env.get(CREDENTIALS);
1652             connection = stub.newClient(credentials);
1653 
1654             // notif issues
1655             final ClientListenerInfo[] old = rmiNotifClient.preReconnection();
1656 
1657             reconnectNotificationListeners(old);
1658 
1659             connectionId = getConnectionId();
1660 
1661             Notification reconnectedNotif =
1662                     new JMXConnectionNotification(JMXConnectionNotification.OPENED,
1663                     this,
1664                     connectionId,
1665                     clientNotifSeqNo++,
1666                     "Reconnected to server",
1667                     null);
1668             sendNotification(reconnectedNotif);
1669 
1670         }
1671 
1672         protected void doStop() {
1673             try {
1674                 close();
1675             } catch (IOException ioe) {
1676                 logger.warning("RMIClientCommunicatorAdmin-doStop",
1677                         "Failed to call the method close():" + ioe);
1678                 logger.debug("RMIClientCommunicatorAdmin-doStop",ioe);
1679             }
1680         }
1681     }
1682 
1683     //--------------------------------------------------------------------
1684     // Private stuff - Serialization
1685     //--------------------------------------------------------------------
1686     /**
1687      * Read RMIConnector fields from an {@link java.io.ObjectInputStream
1688      * ObjectInputStream}.
1689      * Calls {@code s.defaultReadObject()} and then initializes
1690      * all transient variables that need initializing.
1691      * @param s The ObjectInputStream to read from.
1692      * @exception InvalidObjectException if none of <var>rmiServer</var> stub
1693      *    or <var>jmxServiceURL</var> are set.
1694      * @see #RMIConnector(JMXServiceURL,Map)
1695      * @see #RMIConnector(RMIServer,Map)
1696      **/
1697     private void readObject(java.io.ObjectInputStream s)
1698     throws IOException, ClassNotFoundException  {
1699         s.defaultReadObject();
1700 
1701         if (rmiServer == null && jmxServiceURL == null) throw new
1702                 InvalidObjectException("rmiServer and jmxServiceURL both null");
1703 
1704         initTransients();
1705     }
1706 
1707     /**
1708      * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
1709      * ObjectOutputStream}.
1710      * <p>Connects the underlying RMIServer stub to an ORB, if needed,
1711      * before serializing it. This is done using the environment
1712      * map that was provided to the constructor, if any, and as documented
1713      * in {@link javax.management.remote.rmi}.</p>
1714      * <p>This method then calls {@code s.defaultWriteObject()}.
1715      * Usually, <var>rmiServer</var> is null if this object
1716      * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
1717      * is null if this object is constructed with a RMIServer stub.
1718      * <p>Note that the environment Map is not serialized, since the objects
1719      * it contains are assumed to be contextual and relevant only
1720      * with respect to the local environment (class loader, ORB, etc...).</p>
1721      * <p>After an RMIConnector is deserialized, it is assumed that the
1722      * user will call {@link #connect(Map)}, providing a new Map that
1723      * can contain values which are contextually relevant to the new
1724      * local environment.</p>
1725      * <p>Since connection to the ORB is needed prior to serializing, and
1726      * since the ORB to connect to is one of those contextual parameters,
1727      * it is not recommended to re-serialize a just de-serialized object -
1728      * as the de-serialized object has no map. Thus, when an RMIConnector
1729      * object is needed for serialization or transmission to a remote
1730      * application, it is recommended to obtain a new RMIConnector stub
1731      * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
1732      * @param s The ObjectOutputStream to write to.
1733      * @exception InvalidObjectException if none of <var>rmiServer</var> stub
1734      *    or <var>jmxServiceURL</var> are set.
1735      * @see #RMIConnector(JMXServiceURL,Map)
1736      * @see #RMIConnector(RMIServer,Map)
1737      **/
1738     private void writeObject(java.io.ObjectOutputStream s)
1739     throws IOException {
1740         if (rmiServer == null && jmxServiceURL == null) throw new
1741                 InvalidObjectException("rmiServer and jmxServiceURL both null.");
1742         s.defaultWriteObject();
1743     }
1744 
1745     // Initialization of transient variables.
1746     private void initTransients() {
1747         rmbscMap = new WeakHashMap<Subject, WeakReference<MBeanServerConnection>>();
1748         connected = false;
1749         terminated = false;
1750 
1751         connectionBroadcaster = new NotificationBroadcasterSupport();
1752     }
1753 
1754     //--------------------------------------------------------------------
1755     // Private stuff - Check if stub can be trusted.
1756     //--------------------------------------------------------------------
1757 
1758     private static void checkStub(Remote stub,
1759             Class<?> stubClass) {
1760 
1761         // Check remote stub is from the expected class.
1762         //
1763         if (stub.getClass() != stubClass) {
1764             if (!Proxy.isProxyClass(stub.getClass())) {
1765                 throw new SecurityException(
1766                         "Expecting a " + stubClass.getName() + " stub!");
1767             } else {
1768                 InvocationHandler handler = Proxy.getInvocationHandler(stub);
1769                 if (handler.getClass() != RemoteObjectInvocationHandler.class)
1770                     throw new SecurityException(
1771                             "Expecting a dynamic proxy instance with a " +
1772                             RemoteObjectInvocationHandler.class.getName() +
1773                             " invocation handler!");
1774                 else
1775                     stub = (Remote) handler;
1776             }
1777         }
1778 
1779         // Check RemoteRef in stub is from the expected class
1780         // "sun.rmi.server.UnicastRef2".
1781         //
1782         RemoteRef ref = ((RemoteObject)stub).getRef();
1783         if (ref.getClass() != UnicastRef2.class)
1784             throw new SecurityException(
1785                     "Expecting a " + UnicastRef2.class.getName() +
1786                     " remote reference in stub!");
1787 
1788         // Check RMIClientSocketFactory in stub is from the expected class
1789         // "javax.rmi.ssl.SslRMIClientSocketFactory".
1790         //
1791         LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();
1792         RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
1793         if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class)
1794             throw new SecurityException(
1795                     "Expecting a " + SslRMIClientSocketFactory.class.getName() +
1796                     " RMI client socket factory in stub!");
1797     }
1798 
1799     //--------------------------------------------------------------------
1800     // Private stuff - RMIServer creation
1801     //--------------------------------------------------------------------
1802 
1803     private RMIServer findRMIServer(JMXServiceURL directoryURL,
1804             Map<String, Object> environment)
1805             throws NamingException, IOException {
1806 
1807         String path = directoryURL.getURLPath();
1808         int end = path.indexOf(';');
1809         if (end < 0) end = path.length();
1810         if (path.startsWith("/jndi/"))
1811             return findRMIServerJNDI(path.substring(6,end), environment);
1812         else if (path.startsWith("/stub/"))
1813             return findRMIServerJRMP(path.substring(6,end), environment);
1814         else {
1815             final String msg = "URL path must begin with /jndi/ or /stub/ " +
1816                     "or /ior/: " + path;
1817             throw new MalformedURLException(msg);
1818         }
1819     }
1820 
1821     /**
1822      * Lookup the RMIServer stub in a directory.
1823      * @param jndiURL A JNDI URL indicating the location of the Stub
1824      *                (see {@link javax.management.remote.rmi}), e.g.:
1825      *   <ul><li>{@code rmi://registry-host:port/rmi-stub-name}</li>
1826      *       <li>or {@code ldap://ldap-host:port/java-container-dn}</li>
1827      *   </ul>
1828      * @param env the environment Map passed to the connector.
1829      * @return The retrieved RMIServer stub.
1830      * @exception NamingException if the stub couldn't be found.
1831      **/
1832     private RMIServer findRMIServerJNDI(String jndiURL, Map<String, ?> env)
1833             throws NamingException {
1834 
1835         InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env));
1836 
1837         Object objref = ctx.lookup(jndiURL);
1838         ctx.close();
1839 
1840         return narrowJRMPServer(objref);
1841     }
1842 
1843     private static RMIServer narrowJRMPServer(Object objref) {
1844 
1845         return (RMIServer) objref;
1846     }
1847 
1848     private RMIServer findRMIServerJRMP(String base64, Map<String, ?> env)
1849         throws IOException {
1850         final byte[] serialized;
1851         try {
1852             serialized = base64ToByteArray(base64);
1853         } catch (IllegalArgumentException e) {
1854             throw new MalformedURLException("Bad BASE64 encoding: " +
1855                     e.getMessage());
1856         }
1857         final ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
1858 
1859         final ClassLoader loader = EnvHelp.resolveClientClassLoader(env);
1860         final ObjectInputStream oin =
1861                 (loader == null) ?
1862                     new ObjectInputStream(bin) :
1863                     new ObjectInputStreamWithLoader(bin, loader);
1864         final Object stub;
1865         try {
1866             stub = oin.readObject();
1867         } catch (ClassNotFoundException e) {
1868             throw new MalformedURLException("Class not found: " + e);
1869         }
1870         return (RMIServer)stub;
1871     }
1872 
1873     private static final class ObjectInputStreamWithLoader
1874             extends ObjectInputStream {
1875         ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
1876         throws IOException, IllegalArgumentException {
1877             super(in);
1878             if (cl == null ) {
1879               throw new IllegalArgumentException("class loader is null");
1880             }
1881             this.loader = cl;
1882         }
1883 
1884         @Override
1885         protected Class<?> resolveClass(ObjectStreamClass classDesc)
1886                 throws IOException, ClassNotFoundException {
1887             String name = classDesc.getName();
1888             ReflectUtil.checkPackageAccess(name);
1889             return Class.forName(name, false, Objects.requireNonNull(loader));
1890         }
1891 
1892         private final ClassLoader loader;
1893     }
1894 
1895     private MBeanServerConnection getConnectionWithSubject(Subject delegationSubject) {
1896         MBeanServerConnection conn = null;
1897 
1898         if (delegationSubject == null) {
1899             if (nullSubjectConnRef == null
1900                     || (conn = nullSubjectConnRef.get()) == null) {
1901                 conn = new RemoteMBeanServerConnection(null);
1902                 nullSubjectConnRef = new WeakReference<MBeanServerConnection>(conn);
1903             }
1904         } else {
1905             WeakReference<MBeanServerConnection> wr = rmbscMap.get(delegationSubject);
1906             if (wr == null || (conn = wr.get()) == null) {
1907                 conn = new RemoteMBeanServerConnection(delegationSubject);
1908                 rmbscMap.put(delegationSubject, new WeakReference<MBeanServerConnection>(conn));
1909             }
1910         }
1911         return conn;
1912     }
1913 
1914     /*
1915        The following section of code avoids a class loading problem
1916        with RMI.  The problem is that an RMI stub, when deserializing
1917        a remote method return value or exception, will first of all
1918        consult the first non-bootstrap class loader it finds in the
1919        call stack.  This can lead to behavior that is not portable
1920        between implementations of the JMX Remote API.  Notably, an
1921        implementation on J2SE 1.4 will find the RMI stub's loader on
1922        the stack.  But in J2SE 5, this stub is loaded by the
1923        bootstrap loader, so RMI will find the loader of the user code
1924        that called an MBeanServerConnection method.
1925 
1926        To avoid this problem, we take advantage of what the RMI stub
1927        is doing internally.  Each remote call will end up calling
1928        ref.invoke(...), where ref is the RemoteRef parameter given to
1929        the RMI stub's constructor.  It is within this call that the
1930        deserialization will happen.  So we fabricate our own RemoteRef
1931        that delegates everything to the "real" one but that is loaded
1932        by a class loader that knows no other classes.  The class
1933        loader NoCallStackClassLoader does this: the RemoteRef is an
1934        instance of the class named by proxyRefClassName, which is
1935        fabricated by the class loader using byte code that is defined
1936        by the string below.
1937 
1938        The call stack when the deserialization happens is thus this:
1939        MBeanServerConnection.getAttribute (or whatever)
1940        -> RMIConnectionImpl_Stub.getAttribute
1941           -> ProxyRef.invoke(...getAttribute...)
1942              -> UnicastRef.invoke(...getAttribute...)
1943                 -> internal RMI stuff
1944 
1945        Here UnicastRef is the RemoteRef created when the stub was
1946        deserialized (which is of some RMI internal class).  It and the
1947        "internal RMI stuff" are loaded by the bootstrap loader, so are
1948        transparent to the stack search.  The first non-bootstrap
1949        loader found is our ProxyRefLoader, as required.
1950 
1951        In a future version of this code as integrated into J2SE 5,
1952        this workaround could be replaced by direct access to the
1953        internals of RMI.  For now, we use the same code base for J2SE
1954        and for the standalone Reference Implementation.
1955 
1956        The byte code below encodes the following class, compiled using
1957        J2SE 1.4.2 with the -g:none option.
1958 
1959         package jdk.jmx.remote.internal.rmi;
1960 
1961         import java.lang.reflect.Method;
1962         import java.rmi.Remote;
1963         import java.rmi.server.RemoteRef;
1964         import com.sun.jmx.remote.internal.rmi.ProxyRef;
1965 
1966         public class PRef extends ProxyRef {
1967             public PRef(RemoteRef ref) {
1968                 super(ref);
1969             }
1970 
1971             public Object invoke(Remote obj, Method method,
1972                                  Object[] params, long opnum)
1973                     throws Exception {
1974                 return ref.invoke(obj, method, params, opnum);
1975             }
1976         }
1977      */
1978 
1979     private static final String rmiServerImplStubClassName =
1980         RMIServer.class.getName() + "Impl_Stub";
1981     private static final Class<?> rmiServerImplStubClass;
1982     private static final String rmiConnectionImplStubClassName =
1983             RMIConnection.class.getName() + "Impl_Stub";
1984     private static final Class<?> rmiConnectionImplStubClass;
1985     private static final String pRefClassName =
1986         "jdk.jmx.remote.internal.rmi.PRef";
1987     private static final Constructor<?> proxyRefConstructor;
1988     static {
1989         final String pRefByteCodeString =
1990                 "\312\376\272\276\0\0\0\65\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17"+
1991                 "\0\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/Remote"+
1992                 "Ref;)V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/"+
1993                 "reflect/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12E"+
1994                 "xceptions\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1"+
1995                 "\0 jdk/jmx/remote/internal/rmi/PRef\1\0(com/sun/jmx/remote/int"+
1996                 "ernal/rmi/ProxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Lj"+
1997                 "ava/rmi/server/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0"+
1998                 "\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"+
1999                 "\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"+
2000                 "\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"+
2001                 "\4\0\1\0\14\0\0";
2002         final byte[] pRefByteCode =
2003                 NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
2004         PrivilegedExceptionAction<Constructor<?>> action =
2005                 new PrivilegedExceptionAction<Constructor<?>>() {
2006             public Constructor<?> run() throws Exception {
2007                 Class<RMIConnector> thisClass = RMIConnector.class;
2008                 ClassLoader thisLoader = thisClass.getClassLoader();
2009                 ProtectionDomain thisProtectionDomain =
2010                         thisClass.getProtectionDomain();
2011 
2012                 String proxyRefCName = ProxyRef.class.getName();
2013                 ClassLoader cl =
2014                         new NoCallStackClassLoader(pRefClassName,
2015                         pRefByteCode,
2016                         new String[] { proxyRefCName },
2017                         thisLoader,
2018                         thisProtectionDomain);
2019 
2020                 Module jmxModule = ProxyRef.class.getModule();
2021                 Module rmiModule = RemoteRef.class.getModule();
2022 
2023                 String pkg = packageOf(pRefClassName);
2024                 assert pkg != null && pkg.length() > 0 &&
2025                         !pkg.equals(packageOf(proxyRefCName));
2026 
2027                 ModuleDescriptor descriptor =
2028                     ModuleDescriptor.newModule("jdk.remoteref", Set.of(SYNTHETIC))
2029                         .packages(Set.of(pkg))
2030                         .build();
2031                 Module m = Modules.defineModule(cl, descriptor, null);
2032 
2033                 // jdk.remoteref needs to read to java.base and jmxModule
2034                 Modules.addReads(m, Object.class.getModule());
2035                 Modules.addReads(m, jmxModule);
2036                 Modules.addReads(m, rmiModule);
2037 
2038                 // jdk.remoteref needs access to ProxyRef class
2039                 Modules.addExports(jmxModule, packageOf(proxyRefCName), m);
2040 
2041                 // java.management needs to instantiate the fabricated RemoteRef class
2042                 Modules.addReads(jmxModule, m);
2043                 Modules.addExports(m, pkg, jmxModule);
2044 
2045                 Class<?> c = cl.loadClass(pRefClassName);
2046                 return c.getConstructor(RemoteRef.class);
2047             }
2048         };
2049 
2050         Class<?> serverStubClass;
2051         try {
2052             serverStubClass = Class.forName(rmiServerImplStubClassName);
2053         } catch (Exception e) {
2054             logger.error("<clinit>",
2055                     "Failed to instantiate " +
2056                     rmiServerImplStubClassName + ": " + e);
2057             logger.debug("<clinit>",e);
2058             serverStubClass = null;
2059         }
2060         rmiServerImplStubClass = serverStubClass;
2061 
2062         Class<?> stubClass;
2063         Constructor<?> constr;
2064         try {
2065             stubClass = Class.forName(rmiConnectionImplStubClassName);
2066             @SuppressWarnings("removal")
2067             Constructor<?> tmp = (Constructor<?>) AccessController.doPrivileged(action);
2068             constr = tmp;
2069         } catch (Exception e) {
2070             logger.error("<clinit>",
2071                     "Failed to initialize proxy reference constructor "+
2072                     "for " + rmiConnectionImplStubClassName + ": " + e);
2073             logger.debug("<clinit>",e);
2074             stubClass = null;
2075             constr = null;
2076         }
2077         rmiConnectionImplStubClass = stubClass;
2078         proxyRefConstructor = constr;
2079     }
2080 
2081     private static String packageOf(String cn) {
2082         int i = cn.lastIndexOf('.');
2083         return i > 0 ? cn.substring(0, i) : "";
2084     }
2085 
2086     private static RMIConnection shadowJrmpStub(RemoteObject stub)
2087     throws InstantiationException, IllegalAccessException,
2088             InvocationTargetException, ClassNotFoundException,
2089             NoSuchMethodException {
2090         RemoteRef ref = stub.getRef();
2091         RemoteRef proxyRef = (RemoteRef)
2092             proxyRefConstructor.newInstance(new Object[] {ref});
2093         final Constructor<?> rmiConnectionImplStubConstructor =
2094             rmiConnectionImplStubClass.getConstructor(RemoteRef.class);
2095         Object[] args = {proxyRef};
2096         RMIConnection proxyStub = (RMIConnection)
2097         rmiConnectionImplStubConstructor.newInstance(args);
2098         return proxyStub;
2099     }
2100 
2101     private static RMIConnection getConnection(RMIServer server,
2102             Object credentials,
2103             boolean checkStub)
2104             throws IOException {
2105         RMIConnection c = server.newClient(credentials);
2106         if (checkStub) checkStub(c, rmiConnectionImplStubClass);
2107         try {
2108             if (c.getClass() == rmiConnectionImplStubClass)
2109                 return shadowJrmpStub((RemoteObject) c);
2110             logger.trace("getConnection",
2111                     "Did not wrap " + c.getClass() + " to foil " +
2112                     "stack search for classes: class loading semantics " +
2113                     "may be incorrect");
2114         } catch (Exception e) {
2115             logger.error("getConnection",
2116                     "Could not wrap " + c.getClass() + " to foil " +
2117                     "stack search for classes: class loading semantics " +
2118                     "may be incorrect: " + e);
2119             logger.debug("getConnection",e);
2120             // so just return the original stub, which will work for all
2121             // but the most exotic class loading situations
2122         }
2123         return c;
2124     }
2125 
2126     private static byte[] base64ToByteArray(String s) {
2127         int sLen = s.length();
2128         int numGroups = sLen/4;
2129         if (4*numGroups != sLen)
2130             throw new IllegalArgumentException(
2131                     "String length must be a multiple of four.");
2132         int missingBytesInLastGroup = 0;
2133         int numFullGroups = numGroups;
2134         if (sLen != 0) {
2135             if (s.charAt(sLen-1) == '=') {
2136                 missingBytesInLastGroup++;
2137                 numFullGroups--;
2138             }
2139             if (s.charAt(sLen-2) == '=')
2140                 missingBytesInLastGroup++;
2141         }
2142         byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
2143 
2144         // Translate all full groups from base64 to byte array elements
2145         int inCursor = 0, outCursor = 0;
2146         for (int i=0; i<numFullGroups; i++) {
2147             int ch0 = base64toInt(s.charAt(inCursor++));
2148             int ch1 = base64toInt(s.charAt(inCursor++));
2149             int ch2 = base64toInt(s.charAt(inCursor++));
2150             int ch3 = base64toInt(s.charAt(inCursor++));
2151             result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
2152             result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
2153             result[outCursor++] = (byte) ((ch2 << 6) | ch3);
2154         }
2155 
2156         // Translate partial group, if present
2157         if (missingBytesInLastGroup != 0) {
2158             int ch0 = base64toInt(s.charAt(inCursor++));
2159             int ch1 = base64toInt(s.charAt(inCursor++));
2160             result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
2161 
2162             if (missingBytesInLastGroup == 1) {
2163                 int ch2 = base64toInt(s.charAt(inCursor++));
2164                 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
2165             }
2166         }
2167         // assert inCursor == s.length()-missingBytesInLastGroup;
2168         // assert outCursor == result.length;
2169         return result;
2170     }
2171 
2172     /**
2173      * Translates the specified character, which is assumed to be in the
2174      * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
2175      *
2176      * @throws IllegalArgumentException if
2177      *        c is not in the Base64 Alphabet.
2178      */
2179     private static int base64toInt(char c) {
2180         int result;
2181 
2182         if (c >= base64ToInt.length)
2183             result = -1;
2184         else
2185             result = base64ToInt[c];
2186 
2187         if (result < 0)
2188             throw new IllegalArgumentException("Illegal character " + c);
2189         return result;
2190     }
2191 
2192     /**
2193      * This array is a lookup table that translates unicode characters
2194      * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
2195      * into their 6-bit positive integer equivalents.  Characters that
2196      * are not in the Base64 alphabet but fall within the bounds of the
2197      * array are translated to -1.
2198      */
2199     private static final byte base64ToInt[] = {
2200         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2201         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2202         -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
2203         55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
2204         5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
2205         24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
2206         35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
2207     };
2208 
2209     //--------------------------------------------------------------------
2210     // Private stuff - Find / Set default class loader
2211     //--------------------------------------------------------------------
2212     @SuppressWarnings("removal")
2213     private ClassLoader pushDefaultClassLoader() {
2214         final Thread t = Thread.currentThread();
2215         final ClassLoader old =  t.getContextClassLoader();
2216         if (defaultClassLoader != null)
2217             AccessController.doPrivileged(new PrivilegedAction<Void>() {
2218                 public Void run() {
2219                     t.setContextClassLoader(defaultClassLoader);


2220                     return null;
2221                 }
2222             });
2223             return old;
2224     }
2225 
2226     @SuppressWarnings("removal")
2227     private void popDefaultClassLoader(final ClassLoader old) {
2228         AccessController.doPrivileged(new PrivilegedAction<Void>() {
2229             public Void run() {
2230                 Thread.currentThread().setContextClassLoader(old);



2231                 return null;
2232             }
2233         });
2234     }
2235 
2236     //--------------------------------------------------------------------
2237     // Private variables
2238     //--------------------------------------------------------------------
2239     /**
2240      * @serial The RMIServer stub of the RMI JMX Connector server to
2241      * which this client connector is (or will be) connected. This
2242      * field can be null when <var>jmxServiceURL</var> is not
2243      * null. This includes the case where <var>jmxServiceURL</var>
2244      * contains a serialized RMIServer stub. If both
2245      * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
2246      * serialization will fail.
2247      *
2248      * @see #RMIConnector(RMIServer,Map)
2249      **/
2250     @SuppressWarnings("serial") // Not statically typed as Serializable
2251     private final RMIServer rmiServer;
2252 
2253     /**
2254      * @serial The JMXServiceURL of the RMI JMX Connector server to
2255      * which this client connector will be connected. This field can
2256      * be null when <var>rmiServer</var> is not null. If both
2257      * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
2258      * serialization will fail.
2259      *
2260      * @see #RMIConnector(JMXServiceURL,Map)
2261      **/
2262     private final JMXServiceURL jmxServiceURL;
2263 
2264     // ---------------------------------------------------------
2265     // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
2266     // ---------------------------------------------------------
2267     // Any transient variable which needs to be initialized should
2268     // be initialized in the method initTransient()
2269     private transient Map<String, Object> env;
2270     private transient ClassLoader defaultClassLoader;
2271     private transient RMIConnection connection;
2272     private transient String connectionId;
2273 
2274     private transient long clientNotifSeqNo = 0;
2275 
2276     private transient WeakHashMap<Subject, WeakReference<MBeanServerConnection>> rmbscMap;
2277     private transient WeakReference<MBeanServerConnection> nullSubjectConnRef = null;
2278 
2279     private transient RMINotifClient rmiNotifClient;
2280     // = new RMINotifClient(new Integer(0));
2281 
2282     private transient long clientNotifCounter = 0;
2283 
2284     private transient boolean connected;
2285     // = false;
2286     private transient boolean terminated;
2287     // = false;
2288 
2289     private transient Exception closeException;
2290 
2291     private transient NotificationBroadcasterSupport connectionBroadcaster;
2292 
2293     private transient ClientCommunicatorAdmin communicatorAdmin;
2294 
2295     /**
2296      * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
2297      * connect unconnected stubs.
2298      **/
2299     private static volatile WeakReference<Object> orb = null;
2300 
2301     // TRACES & DEBUG
2302     //---------------
2303     private static String objects(final Object[] objs) {
2304         if (objs == null)
2305             return "null";
2306         else
2307             return Arrays.asList(objs).toString();
2308     }
2309 
2310     private static String strings(final String[] strs) {
2311         return objects(strs);
2312     }
2313 
2314     static String getAttributesNames(AttributeList attributes) {
2315         return attributes != null ?
2316                 attributes.asList().stream()
2317                         .map(Attribute::getName)
2318                         .collect(Collectors.joining(", ", "[", "]"))
2319                 : "[]";
2320     }
2321 }
--- EOF ---