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