src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java
changeset 47216 71c04702a3d5
parent 43503 bc7f8619ab70
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.management.rmi/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1834 @@
+/*
+ * Copyright (c) 2002, 2016, 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 java.io.IOException;
+import java.rmi.MarshalledObject;
+import java.rmi.UnmarshalException;
+import java.rmi.server.Unreferenced;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.Permissions;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.management.*;
+import javax.management.remote.JMXServerErrorException;
+import javax.management.remote.NotificationResult;
+import javax.security.auth.Subject;
+import sun.reflect.misc.ReflectUtil;
+
+import static javax.management.remote.rmi.RMIConnector.Util.cast;
+import com.sun.jmx.remote.internal.ServerCommunicatorAdmin;
+import com.sun.jmx.remote.internal.ServerNotifForwarder;
+import com.sun.jmx.remote.security.JMXSubjectDomainCombiner;
+import com.sun.jmx.remote.security.SubjectDelegator;
+import com.sun.jmx.remote.util.ClassLoaderWithRepository;
+import com.sun.jmx.remote.util.ClassLogger;
+import com.sun.jmx.remote.util.EnvHelp;
+import com.sun.jmx.remote.util.OrderClassLoaders;
+import javax.management.loading.ClassLoaderRepository;
+
+/**
+ * <p>Implementation of the {@link RMIConnection} interface.  User
+ * code will not usually reference this class.</p>
+ *
+ * @since 1.5
+ */
+/*
+ * Notice that we omit the type parameter from MarshalledObject everywhere,
+ * even though it would add useful information to the documentation.  The
+ * reason is that it was only added in Mustang (Java SE 6), whereas versions
+ * 1.4 and 2.0 of the JMX API must be implementable on Tiger per our
+ * commitments for JSR 255.
+ */
+public class RMIConnectionImpl implements RMIConnection, Unreferenced {
+
+    /**
+     * Constructs a new {@link RMIConnection}. This connection can be
+     * used with the JRMP transport. This object does
+     * not export itself: it is the responsibility of the caller to
+     * export it appropriately (see {@link
+     * RMIJRMPServerImpl#makeClient(String,Subject)}).
+     *
+     * @param rmiServer The RMIServerImpl object for which this
+     * connection is created.  The behavior is unspecified if this
+     * parameter is null.
+     * @param connectionId The ID for this connection.  The behavior
+     * is unspecified if this parameter is null.
+     * @param defaultClassLoader The default ClassLoader to be used
+     * when deserializing marshalled objects.  Can be null, to signify
+     * the bootstrap class loader.
+     * @param subject the authenticated subject to be used for
+     * authorization.  Can be null, to signify that no subject has
+     * been authenticated.
+     * @param env the environment containing attributes for the new
+     * <code>RMIServerImpl</code>.  Can be null, equivalent to an
+     * empty map.
+     */
+    public RMIConnectionImpl(RMIServerImpl rmiServer,
+                             String connectionId,
+                             ClassLoader defaultClassLoader,
+                             Subject subject,
+                             Map<String,?> env) {
+        if (rmiServer == null || connectionId == null)
+            throw new NullPointerException("Illegal null argument");
+        if (env == null)
+            env = Collections.emptyMap();
+        this.rmiServer = rmiServer;
+        this.connectionId = connectionId;
+        this.defaultClassLoader = defaultClassLoader;
+
+        this.subjectDelegator = new SubjectDelegator();
+        this.subject = subject;
+        if (subject == null) {
+            this.acc = null;
+            this.removeCallerContext = false;
+        } else {
+            this.removeCallerContext =
+                SubjectDelegator.checkRemoveCallerContext(subject);
+            if (this.removeCallerContext) {
+                this.acc =
+                    JMXSubjectDomainCombiner.getDomainCombinerContext(subject);
+            } else {
+                this.acc =
+                    JMXSubjectDomainCombiner.getContext(subject);
+            }
+        }
+        this.mbeanServer = rmiServer.getMBeanServer();
+
+        final ClassLoader dcl = defaultClassLoader;
+
+        ClassLoaderRepository repository = AccessController.doPrivileged(
+            new PrivilegedAction<ClassLoaderRepository>() {
+                public ClassLoaderRepository run() {
+                    return mbeanServer.getClassLoaderRepository();
+                }
+            },
+            withPermissions(new MBeanPermission("*", "getClassLoaderRepository"))
+        );
+        this.classLoaderWithRepository = AccessController.doPrivileged(
+            new PrivilegedAction<ClassLoaderWithRepository>() {
+                public ClassLoaderWithRepository run() {
+                    return new ClassLoaderWithRepository(
+                        repository,
+                        dcl);
+                }
+            },
+            withPermissions(new RuntimePermission("createClassLoader"))
+        );
+
+        this.defaultContextClassLoader =
+            AccessController.doPrivileged(
+                new PrivilegedAction<ClassLoader>() {
+            @Override
+                    public ClassLoader run() {
+                        return new CombinedClassLoader(Thread.currentThread().getContextClassLoader(),
+                                dcl);
+                    }
+                });
+
+        serverCommunicatorAdmin = new
+          RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env));
+
+        this.env = env;
+    }
+
+    private static AccessControlContext withPermissions(Permission ... perms){
+        Permissions col = new Permissions();
+
+        for (Permission thePerm : perms ) {
+            col.add(thePerm);
+        }
+
+        final ProtectionDomain pd = new ProtectionDomain(null, col);
+        return new AccessControlContext( new ProtectionDomain[] { pd });
+    }
+
+    private synchronized ServerNotifForwarder getServerNotifFwd() {
+        // Lazily created when first use. Mainly when
+        // addNotificationListener is first called.
+        if (serverNotifForwarder == null)
+            serverNotifForwarder =
+                new ServerNotifForwarder(mbeanServer,
+                                         env,
+                                         rmiServer.getNotifBuffer(),
+                                         connectionId);
+        return serverNotifForwarder;
+    }
+
+    public String getConnectionId() throws IOException {
+        // We should call reqIncomming() here... shouldn't we?
+        return connectionId;
+    }
+
+    public void close() throws IOException {
+        final boolean debug = logger.debugOn();
+        final String  idstr = (debug?"["+this.toString()+"]":null);
+
+        synchronized (this) {
+            if (terminated) {
+                if (debug) logger.debug("close",idstr + " already terminated.");
+                return;
+            }
+
+            if (debug) logger.debug("close",idstr + " closing.");
+
+            terminated = true;
+
+            if (serverCommunicatorAdmin != null) {
+                serverCommunicatorAdmin.terminate();
+            }
+
+            if (serverNotifForwarder != null) {
+                serverNotifForwarder.terminate();
+            }
+        }
+
+        rmiServer.clientClosed(this);
+
+        if (debug) logger.debug("close",idstr + " closed.");
+    }
+
+    public void unreferenced() {
+        logger.debug("unreferenced", "called");
+        try {
+            close();
+            logger.debug("unreferenced", "done");
+        } catch (IOException e) {
+            logger.fine("unreferenced", e);
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    // MBeanServerConnection Wrapper
+    //-------------------------------------------------------------------------
+
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      Subject delegationSubject)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        IOException {
+        try {
+            final Object params[] =
+                new Object[] { className, name };
+
+            if (logger.debugOn())
+                logger.debug("createMBean(String,ObjectName)",
+                             "connectionId=" + connectionId +", className=" +
+                             className+", name=" + name);
+
+            return (ObjectInstance)
+                doPrivilegedOperation(
+                  CREATE_MBEAN,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof InstanceAlreadyExistsException)
+                throw (InstanceAlreadyExistsException) e;
+            if (e instanceof MBeanRegistrationException)
+                throw (MBeanRegistrationException) e;
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof NotCompliantMBeanException)
+                throw (NotCompliantMBeanException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      ObjectName loaderName,
+                                      Subject delegationSubject)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        InstanceNotFoundException,
+        IOException {
+        try {
+            final Object params[] =
+                new Object[] { className, name, loaderName };
+
+            if (logger.debugOn())
+                logger.debug("createMBean(String,ObjectName,ObjectName)",
+                      "connectionId=" + connectionId
+                      +", className=" + className
+                      +", name=" + name
+                      +", loaderName=" + loaderName);
+
+            return (ObjectInstance)
+                doPrivilegedOperation(
+                  CREATE_MBEAN_LOADER,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof InstanceAlreadyExistsException)
+                throw (InstanceAlreadyExistsException) e;
+            if (e instanceof MBeanRegistrationException)
+                throw (MBeanRegistrationException) e;
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof NotCompliantMBeanException)
+                throw (NotCompliantMBeanException) e;
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public ObjectInstance createMBean(String className,
+                                      ObjectName name,
+                                      MarshalledObject params,
+                                      String signature[],
+                                      Subject delegationSubject)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        IOException {
+
+        final Object[] values;
+        final boolean debug = logger.debugOn();
+
+        if (debug) logger.debug(
+                  "createMBean(String,ObjectName,Object[],String[])",
+                  "connectionId=" + connectionId
+                  +", unwrapping parameters using classLoaderWithRepository.");
+
+        values =
+            nullIsEmpty(unwrap(params, classLoaderWithRepository, Object[].class,delegationSubject));
+
+        try {
+            final Object params2[] =
+                new Object[] { className, name, values,
+                               nullIsEmpty(signature) };
+
+            if (debug)
+               logger.debug("createMBean(String,ObjectName,Object[],String[])",
+                             "connectionId=" + connectionId
+                             +", className=" + className
+                             +", name=" + name
+                             +", signature=" + strings(signature));
+
+            return (ObjectInstance)
+                doPrivilegedOperation(
+                  CREATE_MBEAN_PARAMS,
+                  params2,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof InstanceAlreadyExistsException)
+                throw (InstanceAlreadyExistsException) e;
+            if (e instanceof MBeanRegistrationException)
+                throw (MBeanRegistrationException) e;
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof NotCompliantMBeanException)
+                throw (NotCompliantMBeanException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public ObjectInstance createMBean(String className,
+                                 ObjectName name,
+                                 ObjectName loaderName,
+                                 MarshalledObject params,
+                                 String signature[],
+                                 Subject delegationSubject)
+        throws
+        ReflectionException,
+        InstanceAlreadyExistsException,
+        MBeanRegistrationException,
+        MBeanException,
+        NotCompliantMBeanException,
+        InstanceNotFoundException,
+        IOException {
+
+        final Object[] values;
+        final boolean debug = logger.debugOn();
+
+        if (debug) logger.debug(
+                 "createMBean(String,ObjectName,ObjectName,Object[],String[])",
+                 "connectionId=" + connectionId
+                 +", unwrapping params with MBean extended ClassLoader.");
+
+        values = nullIsEmpty(unwrap(params,
+                                    getClassLoader(loaderName),
+                                    defaultClassLoader,
+                                    Object[].class,delegationSubject));
+
+        try {
+            final Object params2[] =
+               new Object[] { className, name, loaderName, values,
+                              nullIsEmpty(signature) };
+
+           if (debug) logger.debug(
+                 "createMBean(String,ObjectName,ObjectName,Object[],String[])",
+                 "connectionId=" + connectionId
+                 +", className=" + className
+                 +", name=" + name
+                 +", loaderName=" + loaderName
+                 +", signature=" + strings(signature));
+
+            return (ObjectInstance)
+                doPrivilegedOperation(
+                  CREATE_MBEAN_LOADER_PARAMS,
+                  params2,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof InstanceAlreadyExistsException)
+                throw (InstanceAlreadyExistsException) e;
+            if (e instanceof MBeanRegistrationException)
+                throw (MBeanRegistrationException) e;
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof NotCompliantMBeanException)
+                throw (NotCompliantMBeanException) e;
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public void unregisterMBean(ObjectName name, Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        MBeanRegistrationException,
+        IOException {
+        try {
+            final Object params[] = new Object[] { name };
+
+            if (logger.debugOn()) logger.debug("unregisterMBean",
+                 "connectionId=" + connectionId
+                 +", name="+name);
+
+            doPrivilegedOperation(
+              UNREGISTER_MBEAN,
+              params,
+              delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof MBeanRegistrationException)
+                throw (MBeanRegistrationException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public ObjectInstance getObjectInstance(ObjectName name,
+                                            Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        IOException {
+
+        checkNonNull("ObjectName", name);
+
+        try {
+            final Object params[] = new Object[] { name };
+
+            if (logger.debugOn()) logger.debug("getObjectInstance",
+                 "connectionId=" + connectionId
+                 +", name="+name);
+
+            return (ObjectInstance)
+                doPrivilegedOperation(
+                  GET_OBJECT_INSTANCE,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public Set<ObjectInstance>
+        queryMBeans(ObjectName name,
+                    MarshalledObject query,
+                    Subject delegationSubject)
+        throws IOException {
+        final QueryExp queryValue;
+        final boolean debug=logger.debugOn();
+
+        if (debug) logger.debug("queryMBeans",
+                 "connectionId=" + connectionId
+                 +" unwrapping query with defaultClassLoader.");
+
+        queryValue = unwrap(query, defaultContextClassLoader, QueryExp.class, delegationSubject);
+
+        try {
+            final Object params[] = new Object[] { name, queryValue };
+
+            if (debug) logger.debug("queryMBeans",
+                 "connectionId=" + connectionId
+                 +", name="+name +", query="+query);
+
+            return cast(
+                doPrivilegedOperation(
+                  QUERY_MBEANS,
+                  params,
+                  delegationSubject));
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public Set<ObjectName>
+        queryNames(ObjectName name,
+                   MarshalledObject query,
+                   Subject delegationSubject)
+        throws IOException {
+        final QueryExp queryValue;
+        final boolean debug=logger.debugOn();
+
+        if (debug) logger.debug("queryNames",
+                 "connectionId=" + connectionId
+                 +" unwrapping query with defaultClassLoader.");
+
+        queryValue = unwrap(query, defaultContextClassLoader, QueryExp.class, delegationSubject);
+
+        try {
+            final Object params[] = new Object[] { name, queryValue };
+
+            if (debug) logger.debug("queryNames",
+                 "connectionId=" + connectionId
+                 +", name="+name +", query="+query);
+
+            return cast(
+                doPrivilegedOperation(
+                  QUERY_NAMES,
+                  params,
+                  delegationSubject));
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public boolean isRegistered(ObjectName name,
+                                Subject delegationSubject) throws IOException {
+        try {
+            final Object params[] = new Object[] { name };
+            return ((Boolean)
+                doPrivilegedOperation(
+                  IS_REGISTERED,
+                  params,
+                  delegationSubject)).booleanValue();
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public Integer getMBeanCount(Subject delegationSubject)
+        throws IOException {
+        try {
+            final Object params[] = new Object[] { };
+
+            if (logger.debugOn()) logger.debug("getMBeanCount",
+                 "connectionId=" + connectionId);
+
+            return (Integer)
+                doPrivilegedOperation(
+                  GET_MBEAN_COUNT,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public Object getAttribute(ObjectName name,
+                               String attribute,
+                               Subject delegationSubject)
+        throws
+        MBeanException,
+        AttributeNotFoundException,
+        InstanceNotFoundException,
+        ReflectionException,
+        IOException {
+        try {
+            final Object params[] = new Object[] { name, attribute };
+            if (logger.debugOn()) logger.debug("getAttribute",
+                                   "connectionId=" + connectionId
+                                   +", name=" + name
+                                   +", attribute="+ attribute);
+
+            return
+                doPrivilegedOperation(
+                  GET_ATTRIBUTE,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof AttributeNotFoundException)
+                throw (AttributeNotFoundException) e;
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public AttributeList getAttributes(ObjectName name,
+                                       String[] attributes,
+                                       Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        ReflectionException,
+        IOException {
+        try {
+            final Object params[] = new Object[] { name, attributes };
+
+            if (logger.debugOn()) logger.debug("getAttributes",
+                                   "connectionId=" + connectionId
+                                   +", name=" + name
+                                   +", attributes="+ strings(attributes));
+
+            return (AttributeList)
+                doPrivilegedOperation(
+                  GET_ATTRIBUTES,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public void setAttribute(ObjectName name,
+                             MarshalledObject attribute,
+                             Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        AttributeNotFoundException,
+        InvalidAttributeValueException,
+        MBeanException,
+        ReflectionException,
+        IOException {
+        final Attribute attr;
+        final boolean debug=logger.debugOn();
+
+        if (debug) logger.debug("setAttribute",
+                 "connectionId=" + connectionId
+                 +" unwrapping attribute with MBean extended ClassLoader.");
+
+        attr = unwrap(attribute,
+                      getClassLoaderFor(name),
+                      defaultClassLoader,
+                      Attribute.class, delegationSubject);
+
+        try {
+            final Object params[] = new Object[] { name, attr };
+
+            if (debug) logger.debug("setAttribute",
+                             "connectionId=" + connectionId
+                             +", name="+name
+                             +", attribute name="+attr.getName());
+
+            doPrivilegedOperation(
+              SET_ATTRIBUTE,
+              params,
+              delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof AttributeNotFoundException)
+                throw (AttributeNotFoundException) e;
+            if (e instanceof InvalidAttributeValueException)
+                throw (InvalidAttributeValueException) e;
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public AttributeList setAttributes(ObjectName name,
+                         MarshalledObject attributes,
+                         Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        ReflectionException,
+        IOException {
+        final AttributeList attrlist;
+        final boolean debug=logger.debugOn();
+
+        if (debug) logger.debug("setAttributes",
+                 "connectionId=" + connectionId
+                 +" unwrapping attributes with MBean extended ClassLoader.");
+
+        attrlist =
+            unwrap(attributes,
+                   getClassLoaderFor(name),
+                   defaultClassLoader,
+                   AttributeList.class, delegationSubject);
+
+        try {
+            final Object params[] = new Object[] { name, attrlist };
+
+            if (debug) logger.debug("setAttributes",
+                             "connectionId=" + connectionId
+                             +", name="+name
+                             +", attribute names="+RMIConnector.getAttributesNames(attrlist));
+
+            return (AttributeList)
+                doPrivilegedOperation(
+                  SET_ATTRIBUTES,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public Object invoke(ObjectName name,
+                         String operationName,
+                         MarshalledObject params,
+                         String signature[],
+                         Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        MBeanException,
+        ReflectionException,
+        IOException {
+
+        checkNonNull("ObjectName", name);
+        checkNonNull("Operation name", operationName);
+
+        final Object[] values;
+        final boolean debug=logger.debugOn();
+
+        if (debug) logger.debug("invoke",
+                 "connectionId=" + connectionId
+                 +" unwrapping params with MBean extended ClassLoader.");
+
+        values = nullIsEmpty(unwrap(params,
+                                    getClassLoaderFor(name),
+                                    defaultClassLoader,
+                                    Object[].class, delegationSubject));
+
+        try {
+            final Object params2[] =
+                new Object[] { name, operationName, values,
+                               nullIsEmpty(signature) };
+
+            if (debug) logger.debug("invoke",
+                             "connectionId=" + connectionId
+                             +", name="+name
+                             +", operationName="+operationName
+                             +", signature="+strings(signature));
+
+            return
+                doPrivilegedOperation(
+                  INVOKE,
+                  params2,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof MBeanException)
+                throw (MBeanException) e;
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public String getDefaultDomain(Subject delegationSubject)
+        throws IOException {
+        try {
+            final Object params[] = new Object[] { };
+
+            if (logger.debugOn())  logger.debug("getDefaultDomain",
+                                    "connectionId=" + connectionId);
+
+            return (String)
+                doPrivilegedOperation(
+                  GET_DEFAULT_DOMAIN,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public String[] getDomains(Subject delegationSubject) throws IOException {
+        try {
+            final Object params[] = new Object[] { };
+
+            if (logger.debugOn())  logger.debug("getDomains",
+                                    "connectionId=" + connectionId);
+
+            return (String[])
+                doPrivilegedOperation(
+                  GET_DOMAINS,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public MBeanInfo getMBeanInfo(ObjectName name, Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        IntrospectionException,
+        ReflectionException,
+        IOException {
+
+        checkNonNull("ObjectName", name);
+
+        try {
+            final Object params[] = new Object[] { name };
+
+            if (logger.debugOn())  logger.debug("getMBeanInfo",
+                                    "connectionId=" + connectionId
+                                    +", name="+name);
+
+            return (MBeanInfo)
+                doPrivilegedOperation(
+                  GET_MBEAN_INFO,
+                  params,
+                  delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof IntrospectionException)
+                throw (IntrospectionException) e;
+            if (e instanceof ReflectionException)
+                throw (ReflectionException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public boolean isInstanceOf(ObjectName name,
+                                String className,
+                                Subject delegationSubject)
+        throws InstanceNotFoundException, IOException {
+
+        checkNonNull("ObjectName", name);
+
+        try {
+            final Object params[] = new Object[] { name, className };
+
+            if (logger.debugOn())  logger.debug("isInstanceOf",
+                                    "connectionId=" + connectionId
+                                    +", name="+name
+                                    +", className="+className);
+
+            return ((Boolean)
+                doPrivilegedOperation(
+                  IS_INSTANCE_OF,
+                  params,
+                  delegationSubject)).booleanValue();
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public Integer[] addNotificationListeners(ObjectName[] names,
+                      MarshalledObject[] filters,
+                      Subject[] delegationSubjects)
+            throws InstanceNotFoundException, IOException {
+
+        if (names == null || filters == null) {
+            throw new IllegalArgumentException("Got null arguments.");
+        }
+
+        Subject[] sbjs = (delegationSubjects != null) ? delegationSubjects :
+        new Subject[names.length];
+        if (names.length != filters.length || filters.length != sbjs.length) {
+            final String msg =
+                "The value lengths of 3 parameters are not same.";
+            throw new IllegalArgumentException(msg);
+        }
+
+        for (int i=0; i<names.length; i++) {
+            if (names[i] == null) {
+                throw new IllegalArgumentException("Null Object name.");
+            }
+        }
+
+        int i=0;
+        ClassLoader targetCl;
+        NotificationFilter[] filterValues =
+            new NotificationFilter[names.length];
+        Integer[] ids = new Integer[names.length];
+        final boolean debug=logger.debugOn();
+
+        try {
+            for (; i<names.length; i++) {
+                targetCl = getClassLoaderFor(names[i]);
+
+                if (debug) logger.debug("addNotificationListener"+
+                                        "(ObjectName,NotificationFilter)",
+                                        "connectionId=" + connectionId +
+                      " unwrapping filter with target extended ClassLoader.");
+
+                filterValues[i] =
+                    unwrap(filters[i], targetCl, defaultClassLoader,
+                           NotificationFilter.class, sbjs[i]);
+
+                if (debug) logger.debug("addNotificationListener"+
+                                        "(ObjectName,NotificationFilter)",
+                                        "connectionId=" + connectionId
+                                        +", name=" + names[i]
+                                        +", filter=" + filterValues[i]);
+
+                ids[i] = (Integer)
+                    doPrivilegedOperation(ADD_NOTIFICATION_LISTENERS,
+                                          new Object[] { names[i],
+                                                         filterValues[i] },
+                                          sbjs[i]);
+            }
+
+            return ids;
+        } catch (Exception e) {
+            // remove all registered listeners
+            for (int j=0; j<i; j++) {
+                try {
+                    getServerNotifFwd().removeNotificationListener(names[j],
+                                                                   ids[j]);
+                } catch (Exception eee) {
+                    // strange
+                }
+            }
+
+            if (e instanceof PrivilegedActionException) {
+                e = extractException(e);
+            }
+
+            if (e instanceof ClassCastException) {
+                throw (ClassCastException) e;
+            } else if (e instanceof IOException) {
+                throw (IOException)e;
+            } else if (e instanceof InstanceNotFoundException) {
+                throw (InstanceNotFoundException) e;
+            } else if (e instanceof RuntimeException) {
+                throw (RuntimeException) e;
+            } else {
+                throw newIOException("Got unexpected server exception: "+e,e);
+            }
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public void addNotificationListener(ObjectName name,
+                       ObjectName listener,
+                       MarshalledObject filter,
+                       MarshalledObject handback,
+                       Subject delegationSubject)
+        throws InstanceNotFoundException, IOException {
+
+        checkNonNull("Target MBean name", name);
+        checkNonNull("Listener MBean name", listener);
+
+        final NotificationFilter filterValue;
+        final Object handbackValue;
+        final boolean debug=logger.debugOn();
+
+        final ClassLoader targetCl = getClassLoaderFor(name);
+
+        if (debug) logger.debug("addNotificationListener"+
+                 "(ObjectName,ObjectName,NotificationFilter,Object)",
+                 "connectionId=" + connectionId
+                 +" unwrapping filter with target extended ClassLoader.");
+
+        filterValue =
+            unwrap(filter, targetCl, defaultClassLoader, NotificationFilter.class, delegationSubject);
+
+        if (debug) logger.debug("addNotificationListener"+
+                 "(ObjectName,ObjectName,NotificationFilter,Object)",
+                 "connectionId=" + connectionId
+                 +" unwrapping handback with target extended ClassLoader.");
+
+        handbackValue =
+            unwrap(handback, targetCl, defaultClassLoader, Object.class, delegationSubject);
+
+        try {
+            final Object params[] =
+                new Object[] { name, listener, filterValue, handbackValue };
+
+            if (debug) logger.debug("addNotificationListener"+
+                 "(ObjectName,ObjectName,NotificationFilter,Object)",
+                             "connectionId=" + connectionId
+                             +", name=" + name
+                             +", listenerName=" + listener
+                             +", filter=" + filterValue
+                             +", handback=" + handbackValue);
+
+            doPrivilegedOperation(
+              ADD_NOTIFICATION_LISTENER_OBJECTNAME,
+              params,
+              delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public void removeNotificationListeners(ObjectName name,
+                                            Integer[] listenerIDs,
+                                            Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        ListenerNotFoundException,
+        IOException {
+
+        if (name == null || listenerIDs == null)
+            throw new IllegalArgumentException("Illegal null parameter");
+
+        for (int i = 0; i < listenerIDs.length; i++) {
+            if (listenerIDs[i] == null)
+                throw new IllegalArgumentException("Null listener ID");
+        }
+
+        try {
+            final Object params[] = new Object[] { name, listenerIDs };
+
+            if (logger.debugOn()) logger.debug("removeNotificationListener"+
+                                   "(ObjectName,Integer[])",
+                                   "connectionId=" + connectionId
+                                   +", name=" + name
+                                   +", listenerIDs=" + objects(listenerIDs));
+
+            doPrivilegedOperation(
+              REMOVE_NOTIFICATION_LISTENER,
+              params,
+              delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof ListenerNotFoundException)
+                throw (ListenerNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public void removeNotificationListener(ObjectName name,
+                                           ObjectName listener,
+                                           Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        ListenerNotFoundException,
+        IOException {
+
+        checkNonNull("Target MBean name", name);
+        checkNonNull("Listener MBean name", listener);
+
+        try {
+            final Object params[] = new Object[] { name, listener };
+
+            if (logger.debugOn()) logger.debug("removeNotificationListener"+
+                                   "(ObjectName,ObjectName)",
+                                   "connectionId=" + connectionId
+                                   +", name=" + name
+                                   +", listenerName=" + listener);
+
+            doPrivilegedOperation(
+              REMOVE_NOTIFICATION_LISTENER_OBJECTNAME,
+              params,
+              delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof ListenerNotFoundException)
+                throw (ListenerNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    @SuppressWarnings("rawtypes")  // MarshalledObject
+    public void removeNotificationListener(ObjectName name,
+                        ObjectName listener,
+                        MarshalledObject filter,
+                        MarshalledObject handback,
+                        Subject delegationSubject)
+        throws
+        InstanceNotFoundException,
+        ListenerNotFoundException,
+        IOException {
+
+        checkNonNull("Target MBean name", name);
+        checkNonNull("Listener MBean name", listener);
+
+        final NotificationFilter filterValue;
+        final Object handbackValue;
+        final boolean debug=logger.debugOn();
+
+        final ClassLoader targetCl = getClassLoaderFor(name);
+
+        if (debug) logger.debug("removeNotificationListener"+
+                 "(ObjectName,ObjectName,NotificationFilter,Object)",
+                 "connectionId=" + connectionId
+                 +" unwrapping filter with target extended ClassLoader.");
+
+        filterValue =
+            unwrap(filter, targetCl, defaultClassLoader, NotificationFilter.class, delegationSubject);
+
+        if (debug) logger.debug("removeNotificationListener"+
+                 "(ObjectName,ObjectName,NotificationFilter,Object)",
+                 "connectionId=" + connectionId
+                 +" unwrapping handback with target extended ClassLoader.");
+
+        handbackValue =
+            unwrap(handback, targetCl, defaultClassLoader, Object.class, delegationSubject);
+
+        try {
+            final Object params[] =
+                new Object[] { name, listener, filterValue, handbackValue };
+
+            if (debug) logger.debug("removeNotificationListener"+
+                 "(ObjectName,ObjectName,NotificationFilter,Object)",
+                             "connectionId=" + connectionId
+                             +", name=" + name
+                             +", listenerName=" + listener
+                             +", filter=" + filterValue
+                             +", handback=" + handbackValue);
+
+            doPrivilegedOperation(
+              REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK,
+              params,
+              delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof InstanceNotFoundException)
+                throw (InstanceNotFoundException) e;
+            if (e instanceof ListenerNotFoundException)
+                throw (ListenerNotFoundException) e;
+            if (e instanceof IOException)
+                throw (IOException) e;
+            throw newIOException("Got unexpected server exception: " + e, e);
+        }
+    }
+
+    public NotificationResult fetchNotifications(long clientSequenceNumber,
+                                                 int maxNotifications,
+                                                 long timeout)
+        throws IOException {
+
+        if (logger.debugOn()) logger.debug("fetchNotifications",
+                               "connectionId=" + connectionId
+                               +", timeout=" + timeout);
+
+        if (maxNotifications < 0 || timeout < 0)
+            throw new IllegalArgumentException("Illegal negative argument");
+
+        final boolean serverTerminated =
+            serverCommunicatorAdmin.reqIncoming();
+        try {
+            if (serverTerminated) {
+                // we must not call fetchNotifs() if the server is
+                // terminated (timeout elapsed).
+                // returns null to force the client to stop fetching
+                if (logger.debugOn()) logger.debug("fetchNotifications",
+                               "The notification server has been closed, "
+                                       + "returns null to force the client to stop fetching");
+                return null;
+            }
+            final long csn = clientSequenceNumber;
+            final int mn = maxNotifications;
+            final long t = timeout;
+            PrivilegedAction<NotificationResult> action =
+                new PrivilegedAction<NotificationResult>() {
+                    public NotificationResult run() {
+                        return getServerNotifFwd().fetchNotifs(csn, t, mn);
+                    }
+            };
+            if (acc == null)
+                return action.run();
+            else
+                return AccessController.doPrivileged(action, acc);
+        } finally {
+            serverCommunicatorAdmin.rspOutgoing();
+        }
+    }
+
+    /**
+     * <p>Returns a string representation of this object.  In general,
+     * the <code>toString</code> 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() {
+        return super.toString() + ": connectionId=" + connectionId;
+    }
+
+    //------------------------------------------------------------------------
+    // private classes
+    //------------------------------------------------------------------------
+
+    private class PrivilegedOperation
+            implements PrivilegedExceptionAction<Object> {
+
+        public PrivilegedOperation(int operation, Object[] params) {
+            this.operation = operation;
+            this.params = params;
+        }
+
+        public Object run() throws Exception {
+            return doOperation(operation, params);
+        }
+
+        private int operation;
+        private Object[] params;
+    }
+
+    //------------------------------------------------------------------------
+    // private classes
+    //------------------------------------------------------------------------
+    private class RMIServerCommunicatorAdmin extends ServerCommunicatorAdmin {
+        public RMIServerCommunicatorAdmin(long timeout) {
+            super(timeout);
+        }
+
+        protected void doStop() {
+            try {
+                close();
+            } catch (IOException ie) {
+                logger.warning("RMIServerCommunicatorAdmin-doStop",
+                               "Failed to close: " + ie);
+                logger.debug("RMIServerCommunicatorAdmin-doStop",ie);
+            }
+        }
+
+    }
+
+
+    //------------------------------------------------------------------------
+    // private methods
+    //------------------------------------------------------------------------
+
+    private ClassLoader getClassLoader(final ObjectName name)
+        throws InstanceNotFoundException {
+        try {
+            return
+                AccessController.doPrivileged(
+                    new PrivilegedExceptionAction<ClassLoader>() {
+                        public ClassLoader run() throws InstanceNotFoundException {
+                            return mbeanServer.getClassLoader(name);
+                        }
+                    },
+                    withPermissions(new MBeanPermission("*", "getClassLoader"))
+            );
+        } catch (PrivilegedActionException pe) {
+            throw (InstanceNotFoundException) extractException(pe);
+        }
+    }
+
+    private ClassLoader getClassLoaderFor(final ObjectName name)
+        throws InstanceNotFoundException {
+        try {
+            return (ClassLoader)
+                AccessController.doPrivileged(
+                    new PrivilegedExceptionAction<Object>() {
+                        public Object run() throws InstanceNotFoundException {
+                            return mbeanServer.getClassLoaderFor(name);
+                        }
+                    },
+                    withPermissions(new MBeanPermission("*", "getClassLoaderFor"))
+            );
+        } catch (PrivilegedActionException pe) {
+            throw (InstanceNotFoundException) extractException(pe);
+        }
+    }
+
+    private Object doPrivilegedOperation(final int operation,
+                                         final Object[] params,
+                                         final Subject delegationSubject)
+        throws PrivilegedActionException, IOException {
+
+        serverCommunicatorAdmin.reqIncoming();
+        try {
+
+            final AccessControlContext reqACC;
+            if (delegationSubject == null)
+                reqACC = acc;
+            else {
+                if (subject == null) {
+                    final String msg =
+                        "Subject delegation cannot be enabled unless " +
+                        "an authenticated subject is put in place";
+                    throw new SecurityException(msg);
+                }
+                reqACC = subjectDelegator.delegatedContext(
+                    acc, delegationSubject, removeCallerContext);
+            }
+
+            PrivilegedOperation op =
+                new PrivilegedOperation(operation, params);
+            if (reqACC == null) {
+                try {
+                    return op.run();
+                } catch (Exception e) {
+                    if (e instanceof RuntimeException)
+                        throw (RuntimeException) e;
+                    throw new PrivilegedActionException(e);
+                }
+            } else {
+                return AccessController.doPrivileged(op, reqACC);
+            }
+        } catch (Error e) {
+            throw new JMXServerErrorException(e.toString(),e);
+        } finally {
+            serverCommunicatorAdmin.rspOutgoing();
+        }
+    }
+
+    private Object doOperation(int operation, Object[] params)
+        throws Exception {
+
+        switch (operation) {
+
+        case CREATE_MBEAN:
+            return mbeanServer.createMBean((String)params[0],
+                                           (ObjectName)params[1]);
+
+        case CREATE_MBEAN_LOADER:
+            return mbeanServer.createMBean((String)params[0],
+                                           (ObjectName)params[1],
+                                           (ObjectName)params[2]);
+
+        case CREATE_MBEAN_PARAMS:
+            return mbeanServer.createMBean((String)params[0],
+                                           (ObjectName)params[1],
+                                           (Object[])params[2],
+                                           (String[])params[3]);
+
+        case CREATE_MBEAN_LOADER_PARAMS:
+            return mbeanServer.createMBean((String)params[0],
+                                           (ObjectName)params[1],
+                                           (ObjectName)params[2],
+                                           (Object[])params[3],
+                                           (String[])params[4]);
+
+        case GET_ATTRIBUTE:
+            return mbeanServer.getAttribute((ObjectName)params[0],
+                                            (String)params[1]);
+
+        case GET_ATTRIBUTES:
+            return mbeanServer.getAttributes((ObjectName)params[0],
+                                             (String[])params[1]);
+
+        case GET_DEFAULT_DOMAIN:
+            return mbeanServer.getDefaultDomain();
+
+        case GET_DOMAINS:
+            return mbeanServer.getDomains();
+
+        case GET_MBEAN_COUNT:
+            return mbeanServer.getMBeanCount();
+
+        case GET_MBEAN_INFO:
+            return mbeanServer.getMBeanInfo((ObjectName)params[0]);
+
+        case GET_OBJECT_INSTANCE:
+            return mbeanServer.getObjectInstance((ObjectName)params[0]);
+
+        case INVOKE:
+            return mbeanServer.invoke((ObjectName)params[0],
+                                      (String)params[1],
+                                      (Object[])params[2],
+                                      (String[])params[3]);
+
+        case IS_INSTANCE_OF:
+            return mbeanServer.isInstanceOf((ObjectName)params[0],
+                                            (String)params[1])
+                ? Boolean.TRUE : Boolean.FALSE;
+
+        case IS_REGISTERED:
+            return mbeanServer.isRegistered((ObjectName)params[0])
+                ? Boolean.TRUE : Boolean.FALSE;
+
+        case QUERY_MBEANS:
+            return mbeanServer.queryMBeans((ObjectName)params[0],
+                                           (QueryExp)params[1]);
+
+        case QUERY_NAMES:
+            return mbeanServer.queryNames((ObjectName)params[0],
+                                          (QueryExp)params[1]);
+
+        case SET_ATTRIBUTE:
+            mbeanServer.setAttribute((ObjectName)params[0],
+                                     (Attribute)params[1]);
+            return null;
+
+        case SET_ATTRIBUTES:
+            return mbeanServer.setAttributes((ObjectName)params[0],
+                                             (AttributeList)params[1]);
+
+        case UNREGISTER_MBEAN:
+            mbeanServer.unregisterMBean((ObjectName)params[0]);
+            return null;
+
+        case ADD_NOTIFICATION_LISTENERS:
+            return getServerNotifFwd().addNotificationListener(
+                                                (ObjectName)params[0],
+                                                (NotificationFilter)params[1]);
+
+        case ADD_NOTIFICATION_LISTENER_OBJECTNAME:
+            mbeanServer.addNotificationListener((ObjectName)params[0],
+                                                (ObjectName)params[1],
+                                                (NotificationFilter)params[2],
+                                                params[3]);
+            return null;
+
+        case REMOVE_NOTIFICATION_LISTENER:
+            getServerNotifFwd().removeNotificationListener(
+                                                   (ObjectName)params[0],
+                                                   (Integer[])params[1]);
+            return null;
+
+        case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME:
+            mbeanServer.removeNotificationListener((ObjectName)params[0],
+                                                   (ObjectName)params[1]);
+            return null;
+
+        case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK:
+            mbeanServer.removeNotificationListener(
+                                          (ObjectName)params[0],
+                                          (ObjectName)params[1],
+                                          (NotificationFilter)params[2],
+                                          params[3]);
+            return null;
+
+        default:
+            throw new IllegalArgumentException("Invalid operation");
+        }
+    }
+
+    private static class SetCcl implements PrivilegedExceptionAction<ClassLoader> {
+        private final ClassLoader classLoader;
+
+        SetCcl(ClassLoader classLoader) {
+            this.classLoader = classLoader;
+        }
+
+        public ClassLoader run() {
+            Thread currentThread = Thread.currentThread();
+            ClassLoader old = currentThread.getContextClassLoader();
+            currentThread.setContextClassLoader(classLoader);
+            return old;
+        }
+    }
+
+    private <T> T unwrap(final MarshalledObject<?> mo,
+                                final ClassLoader cl,
+                                final Class<T> wrappedClass,
+                                Subject delegationSubject)
+            throws IOException {
+        if (mo == null) {
+            return null;
+        }
+        try {
+            final ClassLoader old = AccessController.doPrivileged(new SetCcl(cl));
+            try{
+                final AccessControlContext reqACC;
+                if (delegationSubject == null)
+                    reqACC = acc;
+                else {
+                    if (subject == null) {
+                        final String msg =
+                            "Subject delegation cannot be enabled unless " +
+                            "an authenticated subject is put in place";
+                        throw new SecurityException(msg);
+                    }
+                    reqACC = subjectDelegator.delegatedContext(
+                        acc, delegationSubject, removeCallerContext);
+                }
+                if(reqACC != null){
+                    return AccessController.doPrivileged(
+                            (PrivilegedExceptionAction<T>) () ->
+                                    wrappedClass.cast(mo.get()), reqACC);
+                }else{
+                    return wrappedClass.cast(mo.get());
+                }
+            }finally{
+                AccessController.doPrivileged(new SetCcl(old));
+            }
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException) {
+                throw (IOException) e;
+            }
+            if (e instanceof ClassNotFoundException) {
+                throw new UnmarshalException(e.toString(), e);
+            }
+            logger.warning("unwrap", "Failed to unmarshall object: " + e);
+            logger.debug("unwrap", e);
+        }catch (ClassNotFoundException ex) {
+            logger.warning("unwrap", "Failed to unmarshall object: " + ex);
+            logger.debug("unwrap", ex);
+            throw new UnmarshalException(ex.toString(), ex);
+        }
+        return null;
+    }
+
+    private <T> T unwrap(final MarshalledObject<?> mo,
+                                final ClassLoader cl1,
+                                final ClassLoader cl2,
+                                final Class<T> wrappedClass,
+                                Subject delegationSubject)
+        throws IOException {
+        if (mo == null) {
+            return null;
+        }
+        try {
+            ClassLoader orderCL = AccessController.doPrivileged(
+                new PrivilegedExceptionAction<ClassLoader>() {
+                    public ClassLoader run() throws Exception {
+                        return new CombinedClassLoader(Thread.currentThread().getContextClassLoader(),
+                                new OrderClassLoaders(cl1, cl2));
+                    }
+                }
+            );
+            return unwrap(mo, orderCL, wrappedClass,delegationSubject);
+        } catch (PrivilegedActionException pe) {
+            Exception e = extractException(pe);
+            if (e instanceof IOException) {
+                throw (IOException) e;
+            }
+            if (e instanceof ClassNotFoundException) {
+                throw new UnmarshalException(e.toString(), e);
+            }
+            logger.warning("unwrap", "Failed to unmarshall object: " + e);
+            logger.debug("unwrap", e);
+        }
+        return null;
+    }
+
+    /**
+     * Construct a new IOException with a nested exception.
+     * The nested exception is set only if JDK {@literal >= 1.4}
+     */
+    private static IOException newIOException(String message,
+                                              Throwable cause) {
+        final IOException x = new IOException(message);
+        return EnvHelp.initCause(x,cause);
+    }
+
+    /**
+     * Iterate until we extract the real exception
+     * from a stack of PrivilegedActionExceptions.
+     */
+    private static Exception extractException(Exception e) {
+        while (e instanceof PrivilegedActionException) {
+            e = ((PrivilegedActionException)e).getException();
+        }
+        return e;
+    }
+
+    private static final Object[] NO_OBJECTS = new Object[0];
+    private static final String[] NO_STRINGS = new String[0];
+
+    /*
+     * The JMX spec doesn't explicitly say that a null Object[] or
+     * String[] in e.g. MBeanServer.invoke is equivalent to an empty
+     * array, but the RI behaves that way.  In the interests of
+     * maximal interoperability, we make it so even when we're
+     * connected to some other JMX implementation that might not do
+     * that.  This should be clarified in the next version of JMX.
+     */
+    private static Object[] nullIsEmpty(Object[] array) {
+        return (array == null) ? NO_OBJECTS : array;
+    }
+
+    private static String[] nullIsEmpty(String[] array) {
+        return (array == null) ? NO_STRINGS : array;
+    }
+
+    /*
+     * Similarly, the JMX spec says for some but not all methods in
+     * MBeanServer that take an ObjectName target, that if it's null
+     * you get this exception.  We specify it for all of them, and
+     * make it so for the ones where it's not specified in JMX even if
+     * the JMX implementation doesn't do so.
+     */
+    private static void checkNonNull(String what, Object x) {
+        if (x == null) {
+            RuntimeException wrapped =
+                new IllegalArgumentException(what + " must not be null");
+            throw new RuntimeOperationsException(wrapped);
+        }
+    }
+
+    //------------------------------------------------------------------------
+    // private variables
+    //------------------------------------------------------------------------
+
+    private final Subject subject;
+
+    private final SubjectDelegator subjectDelegator;
+
+    private final boolean removeCallerContext;
+
+    private final AccessControlContext acc;
+
+    private final RMIServerImpl rmiServer;
+
+    private final MBeanServer mbeanServer;
+
+    private final ClassLoader defaultClassLoader;
+
+    private final ClassLoader defaultContextClassLoader;
+
+    private final ClassLoaderWithRepository classLoaderWithRepository;
+
+    private boolean terminated = false;
+
+    private final String connectionId;
+
+    private final ServerCommunicatorAdmin serverCommunicatorAdmin;
+
+    // Method IDs for doOperation
+    //---------------------------
+
+    private final static int
+        ADD_NOTIFICATION_LISTENERS                              = 1;
+    private final static int
+        ADD_NOTIFICATION_LISTENER_OBJECTNAME                    = 2;
+    private final static int
+        CREATE_MBEAN                                            = 3;
+    private final static int
+        CREATE_MBEAN_PARAMS                                     = 4;
+    private final static int
+        CREATE_MBEAN_LOADER                                     = 5;
+    private final static int
+        CREATE_MBEAN_LOADER_PARAMS                              = 6;
+    private final static int
+        GET_ATTRIBUTE                                           = 7;
+    private final static int
+        GET_ATTRIBUTES                                          = 8;
+    private final static int
+        GET_DEFAULT_DOMAIN                                      = 9;
+    private final static int
+        GET_DOMAINS                                             = 10;
+    private final static int
+        GET_MBEAN_COUNT                                         = 11;
+    private final static int
+        GET_MBEAN_INFO                                          = 12;
+    private final static int
+        GET_OBJECT_INSTANCE                                     = 13;
+    private final static int
+        INVOKE                                                  = 14;
+    private final static int
+        IS_INSTANCE_OF                                          = 15;
+    private final static int
+        IS_REGISTERED                                           = 16;
+    private final static int
+        QUERY_MBEANS                                            = 17;
+    private final static int
+        QUERY_NAMES                                             = 18;
+    private final static int
+        REMOVE_NOTIFICATION_LISTENER                            = 19;
+    private final static int
+        REMOVE_NOTIFICATION_LISTENER_OBJECTNAME                 = 20;
+    private final static int
+        REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 21;
+    private final static int
+        SET_ATTRIBUTE                                           = 22;
+    private final static int
+        SET_ATTRIBUTES                                          = 23;
+    private final static int
+        UNREGISTER_MBEAN                                        = 24;
+
+    // SERVER NOTIFICATION
+    //--------------------
+
+    private ServerNotifForwarder serverNotifForwarder;
+    private Map<String, ?> env;
+
+    // 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);
+    }
+
+    private static final ClassLogger logger =
+        new ClassLogger("javax.management.remote.rmi", "RMIConnectionImpl");
+
+    private static final class CombinedClassLoader extends ClassLoader {
+
+        private final static class ClassLoaderWrapper extends ClassLoader {
+            ClassLoaderWrapper(ClassLoader cl) {
+                super(cl);
+            }
+
+            @Override
+            protected Class<?> loadClass(String name, boolean resolve)
+                    throws ClassNotFoundException {
+                return super.loadClass(name, resolve);
+            }
+        };
+
+        final ClassLoaderWrapper defaultCL;
+
+        private CombinedClassLoader(ClassLoader parent, ClassLoader defaultCL) {
+            super(parent);
+            this.defaultCL = new ClassLoaderWrapper(defaultCL);
+        }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve)
+        throws ClassNotFoundException {
+            ReflectUtil.checkPackageAccess(name);
+            try {
+                super.loadClass(name, resolve);
+            } catch(Exception e) {
+                for(Throwable t = e; t != null; t = t.getCause()) {
+                    if(t instanceof SecurityException) {
+                        throw t==e?(SecurityException)t:new SecurityException(t.getMessage(), e);
+                    }
+                }
+            }
+            final Class<?> cl = defaultCL.loadClass(name, resolve);
+            return cl;
+        }
+
+    }
+}