--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnector.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,2318 @@
+/*
+ * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package javax.management.remote.rmi;
+
+import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
+import com.sun.jmx.remote.internal.ClientListenerInfo;
+import com.sun.jmx.remote.internal.ClientNotifForwarder;
+import com.sun.jmx.remote.internal.rmi.ProxyRef;
+import com.sun.jmx.remote.util.ClassLogger;
+import com.sun.jmx.remote.util.EnvHelp;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.lang.module.ModuleDescriptor;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Proxy;
+import java.net.MalformedURLException;
+import java.rmi.MarshalledObject;
+import java.rmi.NoSuchObjectException;
+import java.rmi.Remote;
+import java.rmi.ServerException;
+import java.rmi.UnmarshalException;
+import java.rmi.server.RMIClientSocketFactory;
+import java.rmi.server.RemoteObject;
+import java.rmi.server.RemoteObjectInvocationHandler;
+import java.rmi.server.RemoteRef;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.stream.Collectors;
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.AttributeNotFoundException;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.IntrospectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.ListenerNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerDelegate;
+import javax.management.MBeanServerNotification;
+import javax.management.NotCompliantMBeanException;
+import javax.management.Notification;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationFilterSupport;
+import javax.management.NotificationListener;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.QueryExp;
+import javax.management.ReflectionException;
+import javax.management.remote.JMXConnectionNotification;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.NotificationResult;
+import javax.management.remote.JMXAddressable;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.rmi.ssl.SslRMIClientSocketFactory;
+import javax.security.auth.Subject;
+import jdk.internal.module.Modules;
+import sun.reflect.misc.ReflectUtil;
+import sun.rmi.server.UnicastRef2;
+import sun.rmi.transport.LiveRef;
+import java.io.NotSerializableException;
+
+import static java.lang.module.ModuleDescriptor.Modifier.SYNTHETIC;
+
+/**
+ * <p>A connection to a remote RMI connector. Usually, such
+ * connections are made using {@link
+ * javax.management.remote.JMXConnectorFactory JMXConnectorFactory}.
+ * However, specialized applications can use this class directly, for
+ * example with an {@link RMIServer} stub obtained without going
+ * through JNDI.</p>
+ *
+ * @since 1.5
+ */
+public class RMIConnector implements JMXConnector, Serializable, JMXAddressable {
+
+ private static final ClassLogger logger =
+ new ClassLogger("javax.management.remote.rmi", "RMIConnector");
+
+ private static final long serialVersionUID = 817323035842634473L;
+
+ static final class Util {
+ private Util() {}
+
+ /* This method can be used by code that is deliberately violating the
+ * allowed checked casts. Rather than marking the whole method containing
+ * the code with @SuppressWarnings, you can use a call to this method for
+ * the exact place where you need to escape the constraints. Typically
+ * you will "import static" this method and then write either
+ * X x = cast(y);
+ * or, if that doesn't work (e.g. X is a type variable)
+ * Util.<X>cast(y);
+ */
+ @SuppressWarnings("unchecked")
+ public static <T> T cast(Object x) {
+ return (T) x;
+ }
+ }
+
+ private RMIConnector(RMIServer rmiServer, JMXServiceURL address,
+ Map<String, ?> environment) {
+ if (rmiServer == null && address == null) throw new
+ IllegalArgumentException("rmiServer and jmxServiceURL both null");
+ initTransients();
+
+ this.rmiServer = rmiServer;
+ this.jmxServiceURL = address;
+ if (environment == null) {
+ this.env = Collections.emptyMap();
+ } else {
+ EnvHelp.checkAttributes(environment);
+ this.env = Collections.unmodifiableMap(environment);
+ }
+ }
+
+ /**
+ * <p>Constructs an {@code RMIConnector} that will connect
+ * the RMI connector server with the given address.</p>
+ *
+ * <p>The address can refer directly to the connector server,
+ * using the following syntax:</p>
+ *
+ * <pre>
+ * service:jmx:rmi://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
+ * </pre>
+ *
+ * <p>(Here, the square brackets {@code []} are not part of the
+ * address but indicate that the host and port are optional.)</p>
+ *
+ * <p>The address can instead indicate where to find an RMI stub
+ * through JNDI, using the following syntax:</p>
+ *
+ * <pre>
+ * service:jmx:rmi://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
+ * </pre>
+ *
+ * <p>An implementation may also recognize additional address
+ * syntaxes, for example:</p>
+ *
+ * <pre>
+ * service:jmx:iiop://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
+ * </pre>
+ *
+ * @param url the address of the RMI connector server.
+ *
+ * @param environment additional attributes specifying how to make
+ * the connection. For JNDI-based addresses, these attributes can
+ * usefully include JNDI attributes recognized by {@link
+ * InitialContext#InitialContext(Hashtable) InitialContext}. This
+ * parameter can be null, which is equivalent to an empty Map.
+ *
+ * @exception IllegalArgumentException if {@code url}
+ * is null.
+ */
+ public RMIConnector(JMXServiceURL url, Map<String,?> environment) {
+ this(null, url, environment);
+ }
+
+ /**
+ * <p>Constructs an {@code RMIConnector} using the given RMI stub.
+ *
+ * @param rmiServer an RMI stub representing the RMI connector server.
+ * @param environment additional attributes specifying how to make
+ * the connection. This parameter can be null, which is
+ * equivalent to an empty Map.
+ *
+ * @exception IllegalArgumentException if {@code rmiServer}
+ * is null.
+ */
+ public RMIConnector(RMIServer rmiServer, Map<String,?> environment) {
+ this(rmiServer, null, environment);
+ }
+
+ /**
+ * <p>Returns a string representation of this object. In general,
+ * the {@code toString} method returns a string that
+ * "textually represents" this object. The result should be a
+ * concise but informative representation that is easy for a
+ * person to read.</p>
+ *
+ * @return a String representation of this object.
+ **/
+ @Override
+ public String toString() {
+ final StringBuilder b = new StringBuilder(this.getClass().getName());
+ b.append(":");
+ if (rmiServer != null) {
+ b.append(" rmiServer=").append(rmiServer.toString());
+ }
+ if (jmxServiceURL != null) {
+ if (rmiServer!=null) b.append(",");
+ b.append(" jmxServiceURL=").append(jmxServiceURL.toString());
+ }
+ return b.toString();
+ }
+
+ /**
+ * <p>The address of this connector.</p>
+ *
+ * @return the address of this connector, or null if it
+ * does not have one.
+ *
+ * @since 1.6
+ */
+ public JMXServiceURL getAddress() {
+ return jmxServiceURL;
+ }
+
+ //--------------------------------------------------------------------
+ // implements JMXConnector interface
+ //--------------------------------------------------------------------
+
+ /**
+ * @throws IOException if the connection could not be made because of a
+ * communication problem
+ */
+ public void connect() throws IOException {
+ connect(null);
+ }
+
+ /**
+ * @throws IOException if the connection could not be made because of a
+ * communication problem
+ */
+ public synchronized void connect(Map<String,?> environment)
+ throws IOException {
+ final boolean tracing = logger.traceOn();
+ String idstr = (tracing?"["+this.toString()+"]":null);
+
+ if (terminated) {
+ logger.trace("connect",idstr + " already closed.");
+ throw new IOException("Connector closed");
+ }
+ if (connected) {
+ logger.trace("connect",idstr + " already connected.");
+ return;
+ }
+
+ try {
+ if (tracing) logger.trace("connect",idstr + " connecting...");
+
+ final Map<String, Object> usemap =
+ new HashMap<String, Object>((this.env==null) ?
+ Collections.<String, Object>emptyMap() : this.env);
+
+
+ if (environment != null) {
+ EnvHelp.checkAttributes(environment);
+ usemap.putAll(environment);
+ }
+
+ // Get RMIServer stub from directory or URL encoding if needed.
+ if (tracing) logger.trace("connect",idstr + " finding stub...");
+ RMIServer stub = (rmiServer!=null)?rmiServer:
+ findRMIServer(jmxServiceURL, usemap);
+
+ // Check for secure RMIServer stub if the corresponding
+ // client-side environment property is set to "true".
+ //
+ String stringBoolean = (String) usemap.get("jmx.remote.x.check.stub");
+ boolean checkStub = EnvHelp.computeBooleanFromString(stringBoolean);
+
+ if (checkStub) checkStub(stub, rmiServerImplStubClass);
+
+ if (tracing) logger.trace("connect",idstr + " connecting stub...");
+ idstr = (tracing?"["+this.toString()+"]":null);
+
+ // Calling newClient on the RMIServer stub.
+ if (tracing)
+ logger.trace("connect",idstr + " getting connection...");
+ Object credentials = usemap.get(CREDENTIALS);
+
+ try {
+ connection = getConnection(stub, credentials, checkStub);
+ } catch (java.rmi.RemoteException re) {
+ throw re;
+ }
+
+ // Always use one of:
+ // ClassLoader provided in Map at connect time,
+ // or contextClassLoader at connect time.
+ if (tracing)
+ logger.trace("connect",idstr + " getting class loader...");
+ defaultClassLoader = EnvHelp.resolveClientClassLoader(usemap);
+
+ usemap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
+ defaultClassLoader);
+
+ rmiNotifClient = new RMINotifClient(defaultClassLoader, usemap);
+
+ env = usemap;
+ final long checkPeriod = EnvHelp.getConnectionCheckPeriod(usemap);
+ communicatorAdmin = new RMIClientCommunicatorAdmin(checkPeriod);
+
+ connected = true;
+
+ // The connectionId variable is used in doStart(), when
+ // reconnecting, to identify the "old" connection.
+ //
+ connectionId = getConnectionId();
+
+ Notification connectedNotif =
+ new JMXConnectionNotification(JMXConnectionNotification.OPENED,
+ this,
+ connectionId,
+ clientNotifSeqNo++,
+ "Successful connection",
+ null);
+ sendNotification(connectedNotif);
+
+ if (tracing) logger.trace("connect",idstr + " done...");
+ } catch (IOException e) {
+ if (tracing)
+ logger.trace("connect",idstr + " failed to connect: " + e);
+ throw e;
+ } catch (RuntimeException e) {
+ if (tracing)
+ logger.trace("connect",idstr + " failed to connect: " + e);
+ throw e;
+ } catch (NamingException e) {
+ final String msg = "Failed to retrieve RMIServer stub: " + e;
+ if (tracing) logger.trace("connect",idstr + " " + msg);
+ throw EnvHelp.initCause(new IOException(msg),e);
+ }
+ }
+
+ public synchronized String getConnectionId() throws IOException {
+ if (terminated || !connected) {
+ if (logger.traceOn())
+ logger.trace("getConnectionId","["+this.toString()+
+ "] not connected.");
+
+ throw new IOException("Not connected");
+ }
+
+ // we do a remote call to have an IOException if the connection is broken.
+ // see the bug 4939578
+ return connection.getConnectionId();
+ }
+
+ public synchronized MBeanServerConnection getMBeanServerConnection()
+ throws IOException {
+ return getMBeanServerConnection(null);
+ }
+
+ public synchronized MBeanServerConnection
+ getMBeanServerConnection(Subject delegationSubject)
+ throws IOException {
+
+ if (terminated) {
+ if (logger.traceOn())
+ logger.trace("getMBeanServerConnection","[" + this.toString() +
+ "] already closed.");
+ throw new IOException("Connection closed");
+ } else if (!connected) {
+ if (logger.traceOn())
+ logger.trace("getMBeanServerConnection","[" + this.toString() +
+ "] is not connected.");
+ throw new IOException("Not connected");
+ }
+
+ return getConnectionWithSubject(delegationSubject);
+ }
+
+ public void
+ addConnectionNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback) {
+ if (listener == null)
+ throw new NullPointerException("listener");
+ connectionBroadcaster.addNotificationListener(listener, filter,
+ handback);
+ }
+
+ public void
+ removeConnectionNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException {
+ if (listener == null)
+ throw new NullPointerException("listener");
+ connectionBroadcaster.removeNotificationListener(listener);
+ }
+
+ public void
+ removeConnectionNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws ListenerNotFoundException {
+ if (listener == null)
+ throw new NullPointerException("listener");
+ connectionBroadcaster.removeNotificationListener(listener, filter,
+ handback);
+ }
+
+ private void sendNotification(Notification n) {
+ connectionBroadcaster.sendNotification(n);
+ }
+
+ public synchronized void close() throws IOException {
+ close(false);
+ }
+
+ // allows to do close after setting the flag "terminated" to true.
+ // It is necessary to avoid a deadlock, see 6296324
+ private synchronized void close(boolean intern) throws IOException {
+ final boolean tracing = logger.traceOn();
+ final boolean debug = logger.debugOn();
+ final String idstr = (tracing?"["+this.toString()+"]":null);
+
+ if (!intern) {
+ // Return if already cleanly closed.
+ //
+ if (terminated) {
+ if (closeException == null) {
+ if (tracing) logger.trace("close",idstr + " already closed.");
+ return;
+ }
+ } else {
+ terminated = true;
+ }
+ }
+
+ if (closeException != null && tracing) {
+ // Already closed, but not cleanly. Attempt again.
+ //
+ if (tracing) {
+ logger.trace("close",idstr + " had failed: " + closeException);
+ logger.trace("close",idstr + " attempting to close again.");
+ }
+ }
+
+ String savedConnectionId = null;
+ if (connected) {
+ savedConnectionId = connectionId;
+ }
+
+ closeException = null;
+
+ if (tracing) logger.trace("close",idstr + " closing.");
+
+ if (communicatorAdmin != null) {
+ communicatorAdmin.terminate();
+ }
+
+ if (rmiNotifClient != null) {
+ try {
+ rmiNotifClient.terminate();
+ if (tracing) logger.trace("close",idstr +
+ " RMI Notification client terminated.");
+ } catch (RuntimeException x) {
+ closeException = x;
+ if (tracing) logger.trace("close",idstr +
+ " Failed to terminate RMI Notification client: " + x);
+ if (debug) logger.debug("close",x);
+ }
+ }
+
+ if (connection != null) {
+ try {
+ connection.close();
+ if (tracing) logger.trace("close",idstr + " closed.");
+ } catch (NoSuchObjectException nse) {
+ // OK, the server maybe closed itself.
+ } catch (IOException e) {
+ closeException = e;
+ if (tracing) logger.trace("close",idstr +
+ " Failed to close RMIServer: " + e);
+ if (debug) logger.debug("close",e);
+ }
+ }
+
+ // Clean up MBeanServerConnection table
+ //
+ rmbscMap.clear();
+
+ /* Send notification of closure. We don't do this if the user
+ * never called connect() on the connector, because there's no
+ * connection id in that case. */
+
+ if (savedConnectionId != null) {
+ Notification closedNotif =
+ new JMXConnectionNotification(JMXConnectionNotification.CLOSED,
+ this,
+ savedConnectionId,
+ clientNotifSeqNo++,
+ "Client has been closed",
+ null);
+ sendNotification(closedNotif);
+ }
+
+ // throw exception if needed
+ //
+ if (closeException != null) {
+ if (tracing) logger.trace("close",idstr + " failed to close: " +
+ closeException);
+ if (closeException instanceof IOException)
+ throw (IOException) closeException;
+ if (closeException instanceof RuntimeException)
+ throw (RuntimeException) closeException;
+ final IOException x =
+ new IOException("Failed to close: " + closeException);
+ throw EnvHelp.initCause(x,closeException);
+ }
+ }
+
+ // added for re-connection
+ private Integer addListenerWithSubject(ObjectName name,
+ MarshalledObject<NotificationFilter> filter,
+ Subject delegationSubject,
+ boolean reconnect)
+ throws InstanceNotFoundException, IOException {
+
+ final boolean debug = logger.debugOn();
+ if (debug)
+ logger.debug("addListenerWithSubject",
+ "(ObjectName,MarshalledObject,Subject)");
+
+ final ObjectName[] names = new ObjectName[] {name};
+ final MarshalledObject<NotificationFilter>[] filters =
+ Util.cast(new MarshalledObject<?>[] {filter});
+ final Subject[] delegationSubjects = new Subject[] {
+ delegationSubject
+ };
+
+ final Integer[] listenerIDs =
+ addListenersWithSubjects(names,filters,delegationSubjects,
+ reconnect);
+
+ if (debug) logger.debug("addListenerWithSubject","listenerID="
+ + listenerIDs[0]);
+ return listenerIDs[0];
+ }
+
+ // added for re-connection
+ private Integer[] addListenersWithSubjects(ObjectName[] names,
+ MarshalledObject<NotificationFilter>[] filters,
+ Subject[] delegationSubjects,
+ boolean reconnect)
+ throws InstanceNotFoundException, IOException {
+
+ final boolean debug = logger.debugOn();
+ if (debug)
+ logger.debug("addListenersWithSubjects",
+ "(ObjectName[],MarshalledObject[],Subject[])");
+
+ final ClassLoader old = pushDefaultClassLoader();
+ Integer[] listenerIDs = null;
+
+ try {
+ listenerIDs = connection.addNotificationListeners(names,
+ filters,
+ delegationSubjects);
+ } catch (NoSuchObjectException noe) {
+ // maybe reconnect
+ if (reconnect) {
+ communicatorAdmin.gotIOException(noe);
+
+ listenerIDs = connection.addNotificationListeners(names,
+ filters,
+ delegationSubjects);
+ } else {
+ throw noe;
+ }
+ } catch (IOException ioe) {
+ // send a failed notif if necessary
+ communicatorAdmin.gotIOException(ioe);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+
+ if (debug) logger.debug("addListenersWithSubjects","registered "
+ + ((listenerIDs==null)?0:listenerIDs.length)
+ + " listener(s)");
+ return listenerIDs;
+ }
+
+ //--------------------------------------------------------------------
+ // Implementation of MBeanServerConnection
+ //--------------------------------------------------------------------
+ private class RemoteMBeanServerConnection implements MBeanServerConnection {
+ private Subject delegationSubject;
+
+ public RemoteMBeanServerConnection() {
+ this(null);
+ }
+
+ public RemoteMBeanServerConnection(Subject delegationSubject) {
+ this.delegationSubject = delegationSubject;
+ }
+
+ public ObjectInstance createMBean(String className,
+ ObjectName name)
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ IOException {
+ if (logger.debugOn())
+ logger.debug("createMBean(String,ObjectName)",
+ "className=" + className + ", name=" +
+ name);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.createMBean(className,
+ name,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.createMBean(className,
+ name,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public ObjectInstance createMBean(String className,
+ ObjectName name,
+ ObjectName loaderName)
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ InstanceNotFoundException,
+ IOException {
+
+ if (logger.debugOn())
+ logger.debug("createMBean(String,ObjectName,ObjectName)",
+ "className=" + className + ", name="
+ + name + ", loaderName="
+ + loaderName + ")");
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.createMBean(className,
+ name,
+ loaderName,
+ delegationSubject);
+
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.createMBean(className,
+ name,
+ loaderName,
+ delegationSubject);
+
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public ObjectInstance createMBean(String className,
+ ObjectName name,
+ Object params[],
+ String signature[])
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ IOException {
+ if (logger.debugOn())
+ logger.debug("createMBean(String,ObjectName,Object[],String[])",
+ "className=" + className + ", name="
+ + name + ", signature=" + strings(signature));
+
+ final MarshalledObject<Object[]> sParams =
+ new MarshalledObject<Object[]>(params);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.createMBean(className,
+ name,
+ sParams,
+ signature,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.createMBean(className,
+ name,
+ sParams,
+ signature,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public ObjectInstance createMBean(String className,
+ ObjectName name,
+ ObjectName loaderName,
+ Object params[],
+ String signature[])
+ throws ReflectionException,
+ InstanceAlreadyExistsException,
+ MBeanRegistrationException,
+ MBeanException,
+ NotCompliantMBeanException,
+ InstanceNotFoundException,
+ IOException {
+ if (logger.debugOn()) logger.debug(
+ "createMBean(String,ObjectName,ObjectName,Object[],String[])",
+ "className=" + className + ", name=" + name + ", loaderName="
+ + loaderName + ", signature=" + strings(signature));
+
+ final MarshalledObject<Object[]> sParams =
+ new MarshalledObject<Object[]>(params);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.createMBean(className,
+ name,
+ loaderName,
+ sParams,
+ signature,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.createMBean(className,
+ name,
+ loaderName,
+ sParams,
+ signature,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public void unregisterMBean(ObjectName name)
+ throws InstanceNotFoundException,
+ MBeanRegistrationException,
+ IOException {
+ if (logger.debugOn())
+ logger.debug("unregisterMBean", "name=" + name);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ connection.unregisterMBean(name, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.unregisterMBean(name, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public ObjectInstance getObjectInstance(ObjectName name)
+ throws InstanceNotFoundException,
+ IOException {
+ if (logger.debugOn())
+ logger.debug("getObjectInstance", "name=" + name);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getObjectInstance(name, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getObjectInstance(name, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public Set<ObjectInstance> queryMBeans(ObjectName name,
+ QueryExp query)
+ throws IOException {
+ if (logger.debugOn()) logger.debug("queryMBeans",
+ "name=" + name + ", query=" + query);
+
+ final MarshalledObject<QueryExp> sQuery =
+ new MarshalledObject<QueryExp>(query);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.queryMBeans(name, sQuery, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.queryMBeans(name, sQuery, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public Set<ObjectName> queryNames(ObjectName name,
+ QueryExp query)
+ throws IOException {
+ if (logger.debugOn()) logger.debug("queryNames",
+ "name=" + name + ", query=" + query);
+
+ final MarshalledObject<QueryExp> sQuery =
+ new MarshalledObject<QueryExp>(query);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.queryNames(name, sQuery, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.queryNames(name, sQuery, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public boolean isRegistered(ObjectName name)
+ throws IOException {
+ if (logger.debugOn())
+ logger.debug("isRegistered", "name=" + name);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.isRegistered(name, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.isRegistered(name, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public Integer getMBeanCount()
+ throws IOException {
+ if (logger.debugOn()) logger.debug("getMBeanCount", "");
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getMBeanCount(delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getMBeanCount(delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public Object getAttribute(ObjectName name,
+ String attribute)
+ throws MBeanException,
+ AttributeNotFoundException,
+ InstanceNotFoundException,
+ ReflectionException,
+ IOException {
+ if (logger.debugOn()) logger.debug("getAttribute",
+ "name=" + name + ", attribute="
+ + attribute);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getAttribute(name,
+ attribute,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getAttribute(name,
+ attribute,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public AttributeList getAttributes(ObjectName name,
+ String[] attributes)
+ throws InstanceNotFoundException,
+ ReflectionException,
+ IOException {
+ if (logger.debugOn()) logger.debug("getAttributes",
+ "name=" + name + ", attributes="
+ + strings(attributes));
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getAttributes(name,
+ attributes,
+ delegationSubject);
+
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getAttributes(name,
+ attributes,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+
+ public void setAttribute(ObjectName name,
+ Attribute attribute)
+ throws InstanceNotFoundException,
+ AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException,
+ IOException {
+
+ if (logger.debugOn()) logger.debug("setAttribute",
+ "name=" + name + ", attribute name="
+ + attribute.getName());
+
+ final MarshalledObject<Attribute> sAttribute =
+ new MarshalledObject<Attribute>(attribute);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ connection.setAttribute(name, sAttribute, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.setAttribute(name, sAttribute, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public AttributeList setAttributes(ObjectName name,
+ AttributeList attributes)
+ throws InstanceNotFoundException,
+ ReflectionException,
+ IOException {
+
+ if (logger.debugOn()) {
+ logger.debug("setAttributes",
+ "name=" + name + ", attribute names="
+ + getAttributesNames(attributes));
+ }
+
+ final MarshalledObject<AttributeList> sAttributes =
+ new MarshalledObject<AttributeList>(attributes);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.setAttributes(name,
+ sAttributes,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.setAttributes(name,
+ sAttributes,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+
+ public Object invoke(ObjectName name,
+ String operationName,
+ Object params[],
+ String signature[])
+ throws InstanceNotFoundException,
+ MBeanException,
+ ReflectionException,
+ IOException {
+
+ if (logger.debugOn()) logger.debug("invoke",
+ "name=" + name
+ + ", operationName=" + operationName
+ + ", signature=" + strings(signature));
+
+ final MarshalledObject<Object[]> sParams =
+ new MarshalledObject<Object[]>(params);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.invoke(name,
+ operationName,
+ sParams,
+ signature,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.invoke(name,
+ operationName,
+ sParams,
+ signature,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+
+ public String getDefaultDomain()
+ throws IOException {
+ if (logger.debugOn()) logger.debug("getDefaultDomain", "");
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getDefaultDomain(delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getDefaultDomain(delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public String[] getDomains() throws IOException {
+ if (logger.debugOn()) logger.debug("getDomains", "");
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getDomains(delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getDomains(delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public MBeanInfo getMBeanInfo(ObjectName name)
+ throws InstanceNotFoundException,
+ IntrospectionException,
+ ReflectionException,
+ IOException {
+
+ if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.getMBeanInfo(name, delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.getMBeanInfo(name, delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+
+ public boolean isInstanceOf(ObjectName name,
+ String className)
+ throws InstanceNotFoundException,
+ IOException {
+ if (logger.debugOn())
+ logger.debug("isInstanceOf", "name=" + name +
+ ", className=" + className);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ return connection.isInstanceOf(name,
+ className,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ return connection.isInstanceOf(name,
+ className,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public void addNotificationListener(ObjectName name,
+ ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ IOException {
+
+ if (logger.debugOn())
+ logger.debug("addNotificationListener" +
+ "(ObjectName,ObjectName,NotificationFilter,Object)",
+ "name=" + name + ", listener=" + listener
+ + ", filter=" + filter + ", handback=" + handback);
+
+ final MarshalledObject<NotificationFilter> sFilter =
+ new MarshalledObject<NotificationFilter>(filter);
+ final MarshalledObject<Object> sHandback =
+ new MarshalledObject<Object>(handback);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ connection.addNotificationListener(name,
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.addNotificationListener(name,
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public void removeNotificationListener(ObjectName name,
+ ObjectName listener)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException,
+ IOException {
+
+ if (logger.debugOn()) logger.debug("removeNotificationListener" +
+ "(ObjectName,ObjectName)",
+ "name=" + name
+ + ", listener=" + listener);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ connection.removeNotificationListener(name,
+ listener,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.removeNotificationListener(name,
+ listener,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ public void removeNotificationListener(ObjectName name,
+ ObjectName listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException,
+ IOException {
+ if (logger.debugOn())
+ logger.debug("removeNotificationListener" +
+ "(ObjectName,ObjectName,NotificationFilter,Object)",
+ "name=" + name
+ + ", listener=" + listener
+ + ", filter=" + filter
+ + ", handback=" + handback);
+
+ final MarshalledObject<NotificationFilter> sFilter =
+ new MarshalledObject<NotificationFilter>(filter);
+ final MarshalledObject<Object> sHandback =
+ new MarshalledObject<Object>(handback);
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ connection.removeNotificationListener(name,
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.removeNotificationListener(name,
+ listener,
+ sFilter,
+ sHandback,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ }
+
+ // Specific Notification Handle ----------------------------------
+
+ public void addNotificationListener(ObjectName name,
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ IOException {
+
+ final boolean debug = logger.debugOn();
+
+ if (debug)
+ logger.debug("addNotificationListener" +
+ "(ObjectName,NotificationListener,"+
+ "NotificationFilter,Object)",
+ "name=" + name
+ + ", listener=" + listener
+ + ", filter=" + filter
+ + ", handback=" + handback);
+
+ final Integer listenerID =
+ addListenerWithSubject(name,
+ new MarshalledObject<NotificationFilter>(filter),
+ delegationSubject,true);
+ rmiNotifClient.addNotificationListener(listenerID, name, listener,
+ filter, handback,
+ delegationSubject);
+ }
+
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException,
+ IOException {
+
+ final boolean debug = logger.debugOn();
+
+ if (debug) logger.debug("removeNotificationListener"+
+ "(ObjectName,NotificationListener)",
+ "name=" + name
+ + ", listener=" + listener);
+
+ final Integer[] ret =
+ rmiNotifClient.getListenerIds(name, listener);
+
+ if (debug) logger.debug("removeNotificationListener",
+ "listenerIDs=" + objects(ret));
+
+ final ClassLoader old = pushDefaultClassLoader();
+
+ try {
+ connection.removeNotificationListeners(name,
+ ret,
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.removeNotificationListeners(name,
+ ret,
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ rmiNotifClient.removeNotificationListener(name, listener);
+ }
+
+ public void removeNotificationListener(ObjectName name,
+ NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ throws InstanceNotFoundException,
+ ListenerNotFoundException,
+ IOException {
+ final boolean debug = logger.debugOn();
+
+ if (debug)
+ logger.debug("removeNotificationListener"+
+ "(ObjectName,NotificationListener,"+
+ "NotificationFilter,Object)",
+ "name=" + name
+ + ", listener=" + listener
+ + ", filter=" + filter
+ + ", handback=" + handback);
+
+ final Integer ret =
+ rmiNotifClient.getListenerId(name, listener,
+ filter, handback);
+
+ if (debug) logger.debug("removeNotificationListener",
+ "listenerID=" + ret);
+
+ final ClassLoader old = pushDefaultClassLoader();
+ try {
+ connection.removeNotificationListeners(name,
+ new Integer[] {ret},
+ delegationSubject);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.removeNotificationListeners(name,
+ new Integer[] {ret},
+ delegationSubject);
+ } finally {
+ popDefaultClassLoader(old);
+ }
+ rmiNotifClient.removeNotificationListener(name, listener,
+ filter, handback);
+ }
+ }
+
+ //--------------------------------------------------------------------
+ private class RMINotifClient extends ClientNotifForwarder {
+ public RMINotifClient(ClassLoader cl, Map<String, ?> env) {
+ super(cl, env);
+ }
+
+ protected NotificationResult fetchNotifs(long clientSequenceNumber,
+ int maxNotifications,
+ long timeout)
+ throws IOException, ClassNotFoundException {
+
+ boolean retried = false;
+ while (true) { // used for a successful re-connection
+ // or a transient network problem
+ try {
+ return connection.fetchNotifications(clientSequenceNumber,
+ maxNotifications,
+ timeout); // return normally
+ } catch (IOException ioe) {
+ // Examine the chain of exceptions to determine whether this
+ // is a deserialization issue. If so - we propagate the
+ // appropriate exception to the caller, who will then
+ // proceed with fetching notifications one by one
+ rethrowDeserializationException(ioe);
+
+ try {
+ communicatorAdmin.gotIOException(ioe);
+ // reconnection OK, back to "while" to do again
+ } catch (IOException ee) {
+ boolean toClose = false;
+
+ synchronized (this) {
+ if (terminated) {
+ // the connection is closed.
+ throw ioe;
+ } else if (retried) {
+ toClose = true;
+ }
+ }
+
+ if (toClose) {
+ // JDK-8049303
+ // We received an IOException - but the communicatorAdmin
+ // did not close the connection - possibly because
+ // the original exception was raised by a transient network
+ // problem?
+ // We already know that this exception is not due to a deserialization
+ // issue as we already took care of that before involving the
+ // communicatorAdmin. Moreover - we already made one retry attempt
+ // at fetching the same batch of notifications - and the
+ // problem persisted.
+ // Since trying again doesn't seem to solve the issue, we will now
+ // close the connection. Doing otherwise might cause the
+ // NotifFetcher thread to die silently.
+ final Notification failedNotif =
+ new JMXConnectionNotification(
+ JMXConnectionNotification.FAILED,
+ this,
+ connectionId,
+ clientNotifSeqNo++,
+ "Failed to communicate with the server: " + ioe.toString(),
+ ioe);
+
+ sendNotification(failedNotif);
+
+ try {
+ close(true);
+ } catch (Exception e) {
+ // OK.
+ // We are closing
+ }
+ throw ioe; // the connection is closed here.
+ } else {
+ // JDK-8049303 possible transient network problem,
+ // let's try one more time
+ retried = true;
+ }
+ }
+ }
+ }
+ }
+
+ private void rethrowDeserializationException(IOException ioe)
+ throws ClassNotFoundException, IOException {
+ // specially treating for an UnmarshalException
+ if (ioe instanceof UnmarshalException) {
+ NotSerializableException nse = new NotSerializableException();
+ nse.initCause(ioe);
+ throw nse; // the fix of 6937053 made ClientNotifForwarder.fetchNotifs
+ // fetch one by one with UnmarshalException
+ }
+
+ // Not serialization problem, return.
+ }
+
+ protected Integer addListenerForMBeanRemovedNotif()
+ throws IOException, InstanceNotFoundException {
+ NotificationFilterSupport clientFilter =
+ new NotificationFilterSupport();
+ clientFilter.enableType(
+ MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
+ MarshalledObject<NotificationFilter> sFilter =
+ new MarshalledObject<NotificationFilter>(clientFilter);
+
+ Integer[] listenerIDs;
+ final ObjectName[] names =
+ new ObjectName[] {MBeanServerDelegate.DELEGATE_NAME};
+ final MarshalledObject<NotificationFilter>[] filters =
+ Util.cast(new MarshalledObject<?>[] {sFilter});
+ final Subject[] subjects = new Subject[] {null};
+ try {
+ listenerIDs =
+ connection.addNotificationListeners(names,
+ filters,
+ subjects);
+
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ listenerIDs =
+ connection.addNotificationListeners(names,
+ filters,
+ subjects);
+ }
+ return listenerIDs[0];
+ }
+
+ protected void removeListenerForMBeanRemovedNotif(Integer id)
+ throws IOException, InstanceNotFoundException,
+ ListenerNotFoundException {
+ try {
+ connection.removeNotificationListeners(
+ MBeanServerDelegate.DELEGATE_NAME,
+ new Integer[] {id},
+ null);
+ } catch (IOException ioe) {
+ communicatorAdmin.gotIOException(ioe);
+
+ connection.removeNotificationListeners(
+ MBeanServerDelegate.DELEGATE_NAME,
+ new Integer[] {id},
+ null);
+ }
+
+ }
+
+ protected void lostNotifs(String message, long number) {
+ final String notifType = JMXConnectionNotification.NOTIFS_LOST;
+
+ final JMXConnectionNotification n =
+ new JMXConnectionNotification(notifType,
+ RMIConnector.this,
+ connectionId,
+ clientNotifCounter++,
+ message,
+ Long.valueOf(number));
+ sendNotification(n);
+ }
+ }
+
+ private class RMIClientCommunicatorAdmin extends ClientCommunicatorAdmin {
+ public RMIClientCommunicatorAdmin(long period) {
+ super(period);
+ }
+
+ @Override
+ public void gotIOException(IOException ioe) throws IOException {
+ if (ioe instanceof NoSuchObjectException) {
+ // need to restart
+ super.gotIOException(ioe);
+
+ return;
+ }
+
+ // check if the connection is broken
+ try {
+ connection.getDefaultDomain(null);
+ } catch (IOException ioexc) {
+ boolean toClose = false;
+
+ synchronized(this) {
+ if (!terminated) {
+ terminated = true;
+
+ toClose = true;
+ }
+ }
+
+ if (toClose) {
+ // we should close the connection,
+ // but send a failed notif at first
+ final Notification failedNotif =
+ new JMXConnectionNotification(
+ JMXConnectionNotification.FAILED,
+ this,
+ connectionId,
+ clientNotifSeqNo++,
+ "Failed to communicate with the server: "+ioe.toString(),
+ ioe);
+
+ sendNotification(failedNotif);
+
+ try {
+ close(true);
+ } catch (Exception e) {
+ // OK.
+ // We are closing
+ }
+ }
+ }
+
+ // forward the exception
+ if (ioe instanceof ServerException) {
+ /* Need to unwrap the exception.
+ Some user-thrown exception at server side will be wrapped by
+ rmi into a ServerException.
+ For example, a RMIConnnectorServer will wrap a
+ ClassNotFoundException into a UnmarshalException, and rmi
+ will throw a ServerException at client side which wraps this
+ UnmarshalException.
+ No failed notif here.
+ */
+ Throwable tt = ((ServerException)ioe).detail;
+
+ if (tt instanceof IOException) {
+ throw (IOException)tt;
+ } else if (tt instanceof RuntimeException) {
+ throw (RuntimeException)tt;
+ }
+ }
+
+ throw ioe;
+ }
+
+ public void reconnectNotificationListeners(ClientListenerInfo[] old) throws IOException {
+ final int len = old.length;
+ int i;
+
+ ClientListenerInfo[] clis = new ClientListenerInfo[len];
+
+ final Subject[] subjects = new Subject[len];
+ final ObjectName[] names = new ObjectName[len];
+ final NotificationListener[] listeners = new NotificationListener[len];
+ final NotificationFilter[] filters = new NotificationFilter[len];
+ final MarshalledObject<NotificationFilter>[] mFilters =
+ Util.cast(new MarshalledObject<?>[len]);
+ final Object[] handbacks = new Object[len];
+
+ for (i=0;i<len;i++) {
+ subjects[i] = old[i].getDelegationSubject();
+ names[i] = old[i].getObjectName();
+ listeners[i] = old[i].getListener();
+ filters[i] = old[i].getNotificationFilter();
+ mFilters[i] = new MarshalledObject<NotificationFilter>(filters[i]);
+ handbacks[i] = old[i].getHandback();
+ }
+
+ try {
+ Integer[] ids = addListenersWithSubjects(names,mFilters,subjects,false);
+
+ for (i=0;i<len;i++) {
+ clis[i] = new ClientListenerInfo(ids[i],
+ names[i],
+ listeners[i],
+ filters[i],
+ handbacks[i],
+ subjects[i]);
+ }
+
+ rmiNotifClient.postReconnection(clis);
+
+ return;
+ } catch (InstanceNotFoundException infe) {
+ // OK, we will do one by one
+ }
+
+ int j = 0;
+ for (i=0;i<len;i++) {
+ try {
+ Integer id = addListenerWithSubject(names[i],
+ new MarshalledObject<NotificationFilter>(filters[i]),
+ subjects[i],
+ false);
+
+ clis[j++] = new ClientListenerInfo(id,
+ names[i],
+ listeners[i],
+ filters[i],
+ handbacks[i],
+ subjects[i]);
+ } catch (InstanceNotFoundException infe) {
+ logger.warning("reconnectNotificationListeners",
+ "Can't reconnect listener for " +
+ names[i]);
+ }
+ }
+
+ if (j != len) {
+ ClientListenerInfo[] tmp = clis;
+ clis = new ClientListenerInfo[j];
+ System.arraycopy(tmp, 0, clis, 0, j);
+ }
+
+ rmiNotifClient.postReconnection(clis);
+ }
+
+ protected void checkConnection() throws IOException {
+ if (logger.debugOn())
+ logger.debug("RMIClientCommunicatorAdmin-checkConnection",
+ "Calling the method getDefaultDomain.");
+
+ connection.getDefaultDomain(null);
+ }
+
+ protected void doStart() throws IOException {
+ // Get RMIServer stub from directory or URL encoding if needed.
+ RMIServer stub;
+ try {
+ stub = (rmiServer!=null)?rmiServer:
+ findRMIServer(jmxServiceURL, env);
+ } catch (NamingException ne) {
+ throw new IOException("Failed to get a RMI stub: "+ne);
+ }
+
+ // Calling newClient on the RMIServer stub.
+ Object credentials = env.get(CREDENTIALS);
+ connection = stub.newClient(credentials);
+
+ // notif issues
+ final ClientListenerInfo[] old = rmiNotifClient.preReconnection();
+
+ reconnectNotificationListeners(old);
+
+ connectionId = getConnectionId();
+
+ Notification reconnectedNotif =
+ new JMXConnectionNotification(JMXConnectionNotification.OPENED,
+ this,
+ connectionId,
+ clientNotifSeqNo++,
+ "Reconnected to server",
+ null);
+ sendNotification(reconnectedNotif);
+
+ }
+
+ protected void doStop() {
+ try {
+ close();
+ } catch (IOException ioe) {
+ logger.warning("RMIClientCommunicatorAdmin-doStop",
+ "Failed to call the method close():" + ioe);
+ logger.debug("RMIClientCommunicatorAdmin-doStop",ioe);
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // Private stuff - Serialization
+ //--------------------------------------------------------------------
+ /**
+ * Read RMIConnector fields from an {@link java.io.ObjectInputStream
+ * ObjectInputStream}.
+ * Calls {@code s.defaultReadObject()} and then initializes
+ * all transient variables that need initializing.
+ * @param s The ObjectInputStream to read from.
+ * @exception InvalidObjectException if none of <var>rmiServer</var> stub
+ * or <var>jmxServiceURL</var> are set.
+ * @see #RMIConnector(JMXServiceURL,Map)
+ * @see #RMIConnector(RMIServer,Map)
+ **/
+ private void readObject(java.io.ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+
+ if (rmiServer == null && jmxServiceURL == null) throw new
+ InvalidObjectException("rmiServer and jmxServiceURL both null");
+
+ initTransients();
+ }
+
+ /**
+ * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
+ * ObjectOutputStream}.
+ * <p>Connects the underlying RMIServer stub to an ORB, if needed,
+ * before serializing it. This is done using the environment
+ * map that was provided to the constructor, if any, and as documented
+ * in {@link javax.management.remote.rmi}.</p>
+ * <p>This method then calls {@code s.defaultWriteObject()}.
+ * Usually, <var>rmiServer</var> is null if this object
+ * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
+ * is null if this object is constructed with a RMIServer stub.
+ * <p>Note that the environment Map is not serialized, since the objects
+ * it contains are assumed to be contextual and relevant only
+ * with respect to the local environment (class loader, ORB, etc...).</p>
+ * <p>After an RMIConnector is deserialized, it is assumed that the
+ * user will call {@link #connect(Map)}, providing a new Map that
+ * can contain values which are contextually relevant to the new
+ * local environment.</p>
+ * <p>Since connection to the ORB is needed prior to serializing, and
+ * since the ORB to connect to is one of those contextual parameters,
+ * it is not recommended to re-serialize a just de-serialized object -
+ * as the de-serialized object has no map. Thus, when an RMIConnector
+ * object is needed for serialization or transmission to a remote
+ * application, it is recommended to obtain a new RMIConnector stub
+ * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
+ * @param s The ObjectOutputStream to write to.
+ * @exception InvalidObjectException if none of <var>rmiServer</var> stub
+ * or <var>jmxServiceURL</var> are set.
+ * @see #RMIConnector(JMXServiceURL,Map)
+ * @see #RMIConnector(RMIServer,Map)
+ **/
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws IOException {
+ if (rmiServer == null && jmxServiceURL == null) throw new
+ InvalidObjectException("rmiServer and jmxServiceURL both null.");
+ s.defaultWriteObject();
+ }
+
+ // Initialization of transient variables.
+ private void initTransients() {
+ rmbscMap = new WeakHashMap<Subject, WeakReference<MBeanServerConnection>>();
+ connected = false;
+ terminated = false;
+
+ connectionBroadcaster = new NotificationBroadcasterSupport();
+ }
+
+ //--------------------------------------------------------------------
+ // Private stuff - Check if stub can be trusted.
+ //--------------------------------------------------------------------
+
+ private static void checkStub(Remote stub,
+ Class<?> stubClass) {
+
+ // Check remote stub is from the expected class.
+ //
+ if (stub.getClass() != stubClass) {
+ if (!Proxy.isProxyClass(stub.getClass())) {
+ throw new SecurityException(
+ "Expecting a " + stubClass.getName() + " stub!");
+ } else {
+ InvocationHandler handler = Proxy.getInvocationHandler(stub);
+ if (handler.getClass() != RemoteObjectInvocationHandler.class)
+ throw new SecurityException(
+ "Expecting a dynamic proxy instance with a " +
+ RemoteObjectInvocationHandler.class.getName() +
+ " invocation handler!");
+ else
+ stub = (Remote) handler;
+ }
+ }
+
+ // Check RemoteRef in stub is from the expected class
+ // "sun.rmi.server.UnicastRef2".
+ //
+ RemoteRef ref = ((RemoteObject)stub).getRef();
+ if (ref.getClass() != UnicastRef2.class)
+ throw new SecurityException(
+ "Expecting a " + UnicastRef2.class.getName() +
+ " remote reference in stub!");
+
+ // Check RMIClientSocketFactory in stub is from the expected class
+ // "javax.rmi.ssl.SslRMIClientSocketFactory".
+ //
+ LiveRef liveRef = ((UnicastRef2)ref).getLiveRef();
+ RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
+ if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class)
+ throw new SecurityException(
+ "Expecting a " + SslRMIClientSocketFactory.class.getName() +
+ " RMI client socket factory in stub!");
+ }
+
+ //--------------------------------------------------------------------
+ // Private stuff - RMIServer creation
+ //--------------------------------------------------------------------
+
+ private RMIServer findRMIServer(JMXServiceURL directoryURL,
+ Map<String, Object> environment)
+ throws NamingException, IOException {
+
+ String path = directoryURL.getURLPath();
+ int end = path.indexOf(';');
+ if (end < 0) end = path.length();
+ if (path.startsWith("/jndi/"))
+ return findRMIServerJNDI(path.substring(6,end), environment);
+ else if (path.startsWith("/stub/"))
+ return findRMIServerJRMP(path.substring(6,end), environment);
+ else {
+ final String msg = "URL path must begin with /jndi/ or /stub/ " +
+ "or /ior/: " + path;
+ throw new MalformedURLException(msg);
+ }
+ }
+
+ /**
+ * Lookup the RMIServer stub in a directory.
+ * @param jndiURL A JNDI URL indicating the location of the Stub
+ * (see {@link javax.management.remote.rmi}), e.g.:
+ * <ul><li>{@code rmi://registry-host:port/rmi-stub-name}</li>
+ * <li>or {@code ldap://ldap-host:port/java-container-dn}</li>
+ * </ul>
+ * @param env the environment Map passed to the connector.
+ * @return The retrieved RMIServer stub.
+ * @exception NamingException if the stub couldn't be found.
+ **/
+ private RMIServer findRMIServerJNDI(String jndiURL, Map<String, ?> env)
+ throws NamingException {
+
+ InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env));
+
+ Object objref = ctx.lookup(jndiURL);
+ ctx.close();
+
+ return narrowJRMPServer(objref);
+ }
+
+ private static RMIServer narrowJRMPServer(Object objref) {
+
+ return (RMIServer) objref;
+ }
+
+ private RMIServer findRMIServerJRMP(String base64, Map<String, ?> env)
+ throws IOException {
+ final byte[] serialized;
+ try {
+ serialized = base64ToByteArray(base64);
+ } catch (IllegalArgumentException e) {
+ throw new MalformedURLException("Bad BASE64 encoding: " +
+ e.getMessage());
+ }
+ final ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
+
+ final ClassLoader loader = EnvHelp.resolveClientClassLoader(env);
+ final ObjectInputStream oin =
+ (loader == null) ?
+ new ObjectInputStream(bin) :
+ new ObjectInputStreamWithLoader(bin, loader);
+ final Object stub;
+ try {
+ stub = oin.readObject();
+ } catch (ClassNotFoundException e) {
+ throw new MalformedURLException("Class not found: " + e);
+ }
+ return (RMIServer)stub;
+ }
+
+ private static final class ObjectInputStreamWithLoader
+ extends ObjectInputStream {
+ ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
+ throws IOException, IllegalArgumentException {
+ super(in);
+ if (cl == null ) {
+ throw new IllegalArgumentException("class loader is null");
+ }
+ this.loader = cl;
+ }
+
+ @Override
+ protected Class<?> resolveClass(ObjectStreamClass classDesc)
+ throws IOException, ClassNotFoundException {
+ String name = classDesc.getName();
+ ReflectUtil.checkPackageAccess(name);
+ return Class.forName(name, false, Objects.requireNonNull(loader));
+ }
+
+ private final ClassLoader loader;
+ }
+
+ private MBeanServerConnection getConnectionWithSubject(Subject delegationSubject) {
+ MBeanServerConnection conn = null;
+
+ if (delegationSubject == null) {
+ if (nullSubjectConnRef == null
+ || (conn = nullSubjectConnRef.get()) == null) {
+ conn = new RemoteMBeanServerConnection(null);
+ nullSubjectConnRef = new WeakReference<MBeanServerConnection>(conn);
+ }
+ } else {
+ WeakReference<MBeanServerConnection> wr = rmbscMap.get(delegationSubject);
+ if (wr == null || (conn = wr.get()) == null) {
+ conn = new RemoteMBeanServerConnection(delegationSubject);
+ rmbscMap.put(delegationSubject, new WeakReference<MBeanServerConnection>(conn));
+ }
+ }
+ return conn;
+ }
+
+ /*
+ The following section of code avoids a class loading problem
+ with RMI. The problem is that an RMI stub, when deserializing
+ a remote method return value or exception, will first of all
+ consult the first non-bootstrap class loader it finds in the
+ call stack. This can lead to behavior that is not portable
+ between implementations of the JMX Remote API. Notably, an
+ implementation on J2SE 1.4 will find the RMI stub's loader on
+ the stack. But in J2SE 5, this stub is loaded by the
+ bootstrap loader, so RMI will find the loader of the user code
+ that called an MBeanServerConnection method.
+
+ To avoid this problem, we take advantage of what the RMI stub
+ is doing internally. Each remote call will end up calling
+ ref.invoke(...), where ref is the RemoteRef parameter given to
+ the RMI stub's constructor. It is within this call that the
+ deserialization will happen. So we fabricate our own RemoteRef
+ that delegates everything to the "real" one but that is loaded
+ by a class loader that knows no other classes. The class
+ loader NoCallStackClassLoader does this: the RemoteRef is an
+ instance of the class named by proxyRefClassName, which is
+ fabricated by the class loader using byte code that is defined
+ by the string below.
+
+ The call stack when the deserialization happens is thus this:
+ MBeanServerConnection.getAttribute (or whatever)
+ -> RMIConnectionImpl_Stub.getAttribute
+ -> ProxyRef.invoke(...getAttribute...)
+ -> UnicastRef.invoke(...getAttribute...)
+ -> internal RMI stuff
+
+ Here UnicastRef is the RemoteRef created when the stub was
+ deserialized (which is of some RMI internal class). It and the
+ "internal RMI stuff" are loaded by the bootstrap loader, so are
+ transparent to the stack search. The first non-bootstrap
+ loader found is our ProxyRefLoader, as required.
+
+ In a future version of this code as integrated into J2SE 5,
+ this workaround could be replaced by direct access to the
+ internals of RMI. For now, we use the same code base for J2SE
+ and for the standalone Reference Implementation.
+
+ The byte code below encodes the following class, compiled using
+ J2SE 1.4.2 with the -g:none option.
+
+ package jdk.jmx.remote.internal.rmi;
+
+ import java.lang.reflect.Method;
+ import java.rmi.Remote;
+ import java.rmi.server.RemoteRef;
+ import com.sun.jmx.remote.internal.rmi.ProxyRef;
+
+ public class PRef extends ProxyRef {
+ public PRef(RemoteRef ref) {
+ super(ref);
+ }
+
+ public Object invoke(Remote obj, Method method,
+ Object[] params, long opnum)
+ throws Exception {
+ return ref.invoke(obj, method, params, opnum);
+ }
+ }
+ */
+
+ private static final String rmiServerImplStubClassName =
+ RMIServer.class.getName() + "Impl_Stub";
+ private static final Class<?> rmiServerImplStubClass;
+ private static final String rmiConnectionImplStubClassName =
+ RMIConnection.class.getName() + "Impl_Stub";
+ private static final Class<?> rmiConnectionImplStubClass;
+ private static final String pRefClassName =
+ "jdk.jmx.remote.internal.rmi.PRef";
+ private static final Constructor<?> proxyRefConstructor;
+ static {
+ final String pRefByteCodeString =
+ "\312\376\272\276\0\0\0\65\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17"+
+ "\0\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/Remote"+
+ "Ref;)V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/"+
+ "reflect/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12E"+
+ "xceptions\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1"+
+ "\0 jdk/jmx/remote/internal/rmi/PRef\1\0(com/sun/jmx/remote/int"+
+ "ernal/rmi/ProxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Lj"+
+ "ava/rmi/server/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0"+
+ "\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"+
+ "\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"+
+ "\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"+
+ "\4\0\1\0\14\0\0";
+ final byte[] pRefByteCode =
+ NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
+ PrivilegedExceptionAction<Constructor<?>> action =
+ new PrivilegedExceptionAction<Constructor<?>>() {
+ public Constructor<?> run() throws Exception {
+ Class<RMIConnector> thisClass = RMIConnector.class;
+ ClassLoader thisLoader = thisClass.getClassLoader();
+ ProtectionDomain thisProtectionDomain =
+ thisClass.getProtectionDomain();
+
+ String proxyRefCName = ProxyRef.class.getName();
+ ClassLoader cl =
+ new NoCallStackClassLoader(pRefClassName,
+ pRefByteCode,
+ new String[] { proxyRefCName },
+ thisLoader,
+ thisProtectionDomain);
+
+ Module jmxModule = ProxyRef.class.getModule();
+ Module rmiModule = RemoteRef.class.getModule();
+
+ String pkg = packageOf(pRefClassName);
+ assert pkg != null && pkg.length() > 0 &&
+ !pkg.equals(packageOf(proxyRefCName));
+
+ ModuleDescriptor descriptor =
+ ModuleDescriptor.newModule("jdk.remoteref", Set.of(SYNTHETIC))
+ .packages(Set.of(pkg))
+ .build();
+ Module m = Modules.defineModule(cl, descriptor, null);
+
+ // jdk.remoteref needs to read to java.base and jmxModule
+ Modules.addReads(m, Object.class.getModule());
+ Modules.addReads(m, jmxModule);
+ Modules.addReads(m, rmiModule);
+
+ // jdk.remoteref needs access to ProxyRef class
+ Modules.addExports(jmxModule, packageOf(proxyRefCName), m);
+
+ // java.management needs to instantiate the fabricated RemoteRef class
+ Modules.addReads(jmxModule, m);
+ Modules.addExports(m, pkg, jmxModule);
+
+ Class<?> c = cl.loadClass(pRefClassName);
+ return c.getConstructor(RemoteRef.class);
+ }
+ };
+
+ Class<?> serverStubClass;
+ try {
+ serverStubClass = Class.forName(rmiServerImplStubClassName);
+ } catch (Exception e) {
+ logger.error("<clinit>",
+ "Failed to instantiate " +
+ rmiServerImplStubClassName + ": " + e);
+ logger.debug("<clinit>",e);
+ serverStubClass = null;
+ }
+ rmiServerImplStubClass = serverStubClass;
+
+ Class<?> stubClass;
+ Constructor<?> constr;
+ try {
+ stubClass = Class.forName(rmiConnectionImplStubClassName);
+ constr = (Constructor<?>) AccessController.doPrivileged(action);
+ } catch (Exception e) {
+ logger.error("<clinit>",
+ "Failed to initialize proxy reference constructor "+
+ "for " + rmiConnectionImplStubClassName + ": " + e);
+ logger.debug("<clinit>",e);
+ stubClass = null;
+ constr = null;
+ }
+ rmiConnectionImplStubClass = stubClass;
+ proxyRefConstructor = constr;
+ }
+
+ private static String packageOf(String cn) {
+ int i = cn.lastIndexOf('.');
+ return i > 0 ? cn.substring(0, i) : "";
+ }
+
+ private static RMIConnection shadowJrmpStub(RemoteObject stub)
+ throws InstantiationException, IllegalAccessException,
+ InvocationTargetException, ClassNotFoundException,
+ NoSuchMethodException {
+ RemoteRef ref = stub.getRef();
+ RemoteRef proxyRef = (RemoteRef)
+ proxyRefConstructor.newInstance(new Object[] {ref});
+ final Constructor<?> rmiConnectionImplStubConstructor =
+ rmiConnectionImplStubClass.getConstructor(RemoteRef.class);
+ Object[] args = {proxyRef};
+ RMIConnection proxyStub = (RMIConnection)
+ rmiConnectionImplStubConstructor.newInstance(args);
+ return proxyStub;
+ }
+
+ private static RMIConnection getConnection(RMIServer server,
+ Object credentials,
+ boolean checkStub)
+ throws IOException {
+ RMIConnection c = server.newClient(credentials);
+ if (checkStub) checkStub(c, rmiConnectionImplStubClass);
+ try {
+ if (c.getClass() == rmiConnectionImplStubClass)
+ return shadowJrmpStub((RemoteObject) c);
+ logger.trace("getConnection",
+ "Did not wrap " + c.getClass() + " to foil " +
+ "stack search for classes: class loading semantics " +
+ "may be incorrect");
+ } catch (Exception e) {
+ logger.error("getConnection",
+ "Could not wrap " + c.getClass() + " to foil " +
+ "stack search for classes: class loading semantics " +
+ "may be incorrect: " + e);
+ logger.debug("getConnection",e);
+ // so just return the original stub, which will work for all
+ // but the most exotic class loading situations
+ }
+ return c;
+ }
+
+ private static byte[] base64ToByteArray(String s) {
+ int sLen = s.length();
+ int numGroups = sLen/4;
+ if (4*numGroups != sLen)
+ throw new IllegalArgumentException(
+ "String length must be a multiple of four.");
+ int missingBytesInLastGroup = 0;
+ int numFullGroups = numGroups;
+ if (sLen != 0) {
+ if (s.charAt(sLen-1) == '=') {
+ missingBytesInLastGroup++;
+ numFullGroups--;
+ }
+ if (s.charAt(sLen-2) == '=')
+ missingBytesInLastGroup++;
+ }
+ byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
+
+ // Translate all full groups from base64 to byte array elements
+ int inCursor = 0, outCursor = 0;
+ for (int i=0; i<numFullGroups; i++) {
+ int ch0 = base64toInt(s.charAt(inCursor++));
+ int ch1 = base64toInt(s.charAt(inCursor++));
+ int ch2 = base64toInt(s.charAt(inCursor++));
+ int ch3 = base64toInt(s.charAt(inCursor++));
+ result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
+ result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
+ result[outCursor++] = (byte) ((ch2 << 6) | ch3);
+ }
+
+ // Translate partial group, if present
+ if (missingBytesInLastGroup != 0) {
+ int ch0 = base64toInt(s.charAt(inCursor++));
+ int ch1 = base64toInt(s.charAt(inCursor++));
+ result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
+
+ if (missingBytesInLastGroup == 1) {
+ int ch2 = base64toInt(s.charAt(inCursor++));
+ result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
+ }
+ }
+ // assert inCursor == s.length()-missingBytesInLastGroup;
+ // assert outCursor == result.length;
+ return result;
+ }
+
+ /**
+ * Translates the specified character, which is assumed to be in the
+ * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
+ *
+ * @throws IllegalArgumentException if
+ * c is not in the Base64 Alphabet.
+ */
+ private static int base64toInt(char c) {
+ int result;
+
+ if (c >= base64ToInt.length)
+ result = -1;
+ else
+ result = base64ToInt[c];
+
+ if (result < 0)
+ throw new IllegalArgumentException("Illegal character " + c);
+ return result;
+ }
+
+ /**
+ * This array is a lookup table that translates unicode characters
+ * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
+ * into their 6-bit positive integer equivalents. Characters that
+ * are not in the Base64 alphabet but fall within the bounds of the
+ * array are translated to -1.
+ */
+ private static final byte base64ToInt[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ };
+
+ //--------------------------------------------------------------------
+ // Private stuff - Find / Set default class loader
+ //--------------------------------------------------------------------
+ private ClassLoader pushDefaultClassLoader() {
+ final Thread t = Thread.currentThread();
+ final ClassLoader old = t.getContextClassLoader();
+ if (defaultClassLoader != null)
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ t.setContextClassLoader(defaultClassLoader);
+ return null;
+ }
+ });
+ return old;
+ }
+
+ private void popDefaultClassLoader(final ClassLoader old) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ Thread.currentThread().setContextClassLoader(old);
+ return null;
+ }
+ });
+ }
+
+ //--------------------------------------------------------------------
+ // Private variables
+ //--------------------------------------------------------------------
+ /**
+ * @serial The RMIServer stub of the RMI JMX Connector server to
+ * which this client connector is (or will be) connected. This
+ * field can be null when <var>jmxServiceURL</var> is not
+ * null. This includes the case where <var>jmxServiceURL</var>
+ * contains a serialized RMIServer stub. If both
+ * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
+ * serialization will fail.
+ *
+ * @see #RMIConnector(RMIServer,Map)
+ **/
+ private final RMIServer rmiServer;
+
+ /**
+ * @serial The JMXServiceURL of the RMI JMX Connector server to
+ * which this client connector will be connected. This field can
+ * be null when <var>rmiServer</var> is not null. If both
+ * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
+ * serialization will fail.
+ *
+ * @see #RMIConnector(JMXServiceURL,Map)
+ **/
+ private final JMXServiceURL jmxServiceURL;
+
+ // ---------------------------------------------------------
+ // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
+ // ---------------------------------------------------------
+ // Any transient variable which needs to be initialized should
+ // be initialized in the method initTransient()
+ private transient Map<String, Object> env;
+ private transient ClassLoader defaultClassLoader;
+ private transient RMIConnection connection;
+ private transient String connectionId;
+
+ private transient long clientNotifSeqNo = 0;
+
+ private transient WeakHashMap<Subject, WeakReference<MBeanServerConnection>> rmbscMap;
+ private transient WeakReference<MBeanServerConnection> nullSubjectConnRef = null;
+
+ private transient RMINotifClient rmiNotifClient;
+ // = new RMINotifClient(new Integer(0));
+
+ private transient long clientNotifCounter = 0;
+
+ private transient boolean connected;
+ // = false;
+ private transient boolean terminated;
+ // = false;
+
+ private transient Exception closeException;
+
+ private transient NotificationBroadcasterSupport connectionBroadcaster;
+
+ private transient ClientCommunicatorAdmin communicatorAdmin;
+
+ /**
+ * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
+ * connect unconnected stubs.
+ **/
+ private static volatile WeakReference<Object> orb = null;
+
+ // TRACES & DEBUG
+ //---------------
+ private static String objects(final Object[] objs) {
+ if (objs == null)
+ return "null";
+ else
+ return Arrays.asList(objs).toString();
+ }
+
+ private static String strings(final String[] strs) {
+ return objects(strs);
+ }
+
+ static String getAttributesNames(AttributeList attributes) {
+ return attributes != null ?
+ attributes.asList().stream()
+ .map(Attribute::getName)
+ .collect(Collectors.joining(", ", "[", "]"))
+ : "[]";
+ }
+}