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