All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.remotingjmx.protocol.v2.ClientConnection Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

The newest version!
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2023 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.remotingjmx.protocol.v2;

import static org.jboss.remotingjmx.protocol.v2.Constants.ADD_NOTIFICATION_LISTENER;
import static org.jboss.remotingjmx.protocol.v2.Constants.ATTRIBUTE;
import static org.jboss.remotingjmx.protocol.v2.Constants.ATTRIBUTE_LIST;
import static org.jboss.remotingjmx.protocol.v2.Constants.BOOLEAN;
import static org.jboss.remotingjmx.protocol.v2.Constants.CREATE_MBEAN;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_ATTRIBUTE;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_ATTRIBUTES;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_DEFAULT_DOMAIN;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_DOMAINS;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_MBEAN_COUNT;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_MBEAN_INFO;
import static org.jboss.remotingjmx.protocol.v2.Constants.GET_OBJECT_INSTANCE;
import static org.jboss.remotingjmx.protocol.v2.Constants.INSTANCE_OF;
import static org.jboss.remotingjmx.protocol.v2.Constants.INTEGER;
import static org.jboss.remotingjmx.protocol.v2.Constants.INTEGER_ARRAY;
import static org.jboss.remotingjmx.protocol.v2.Constants.INVOKE;
import static org.jboss.remotingjmx.protocol.v2.Constants.IS_REGISTERED;
import static org.jboss.remotingjmx.protocol.v2.Constants.MBEAN_INFO;
import static org.jboss.remotingjmx.protocol.v2.Constants.NOTIFICATION;
import static org.jboss.remotingjmx.protocol.v2.Constants.NOTIFICATION_FILTER;
import static org.jboss.remotingjmx.protocol.v2.Constants.OBJECT;
import static org.jboss.remotingjmx.protocol.v2.Constants.OBJECT_ARRAY;
import static org.jboss.remotingjmx.protocol.v2.Constants.OBJECT_INSTANCE;
import static org.jboss.remotingjmx.protocol.v2.Constants.OBJECT_NAME;
import static org.jboss.remotingjmx.protocol.v2.Constants.QUERY_EXP;
import static org.jboss.remotingjmx.protocol.v2.Constants.QUERY_MBEANS;
import static org.jboss.remotingjmx.protocol.v2.Constants.QUERY_NAMES;
import static org.jboss.remotingjmx.protocol.v2.Constants.REMOVE_NOTIFICATION_LISTENER;
import static org.jboss.remotingjmx.protocol.v2.Constants.RESPONSE_MASK;
import static org.jboss.remotingjmx.protocol.v2.Constants.SEND_NOTIFICATION;
import static org.jboss.remotingjmx.protocol.v2.Constants.SET_ATTRIBUTE;
import static org.jboss.remotingjmx.protocol.v2.Constants.SET_ATTRIBUTES;
import static org.jboss.remotingjmx.protocol.v2.Constants.SET_OBJECT_INSTANCE;
import static org.jboss.remotingjmx.protocol.v2.Constants.SET_OBJECT_NAME;
import static org.jboss.remotingjmx.protocol.v2.Constants.STRING;
import static org.jboss.remotingjmx.protocol.v2.Constants.STRING_ARRAY;
import static org.jboss.remotingjmx.protocol.v2.Constants.UNREGISTER_MBEAN;
import static org.jboss.remotingjmx.protocol.v2.Constants.VOID;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

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.JMRuntimeException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServerConnection;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.QueryExp;
import javax.management.ReflectionException;
import javax.security.auth.Subject;

import org.jboss.logging.Logger;
import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.Unmarshaller;
import org.jboss.remoting3.Channel;
import org.jboss.remoting3.Connection;
import org.jboss.remotingjmx.RemotingMBeanServerConnection;
import org.jboss.remotingjmx.VersionedConnection;
import org.xnio.IoFuture;

/**
 * The VersionOne client connection.
 *
 * @author Darran Lofthouse
 */
class ClientConnection extends ClientCommon implements VersionedConnection {

    private static final Logger log = Logger.getLogger(ClientConnection.class);

    private final Channel channel;
    // Registry of handlers for the incoming messages.
    private final Map handlerRegistry;

    private final String connectionId;
    private TheConnection mbeanServerConnection;
    private final ClientRequestManager clientRequestManager;
    private final ClientExecutorManager clientExecutorManager;
    private LocalNotificationManager localNotificationManager;

    ClientConnection(final Channel channel, final Map environment, final ClientRequestManager clientRequestManager,
            final ClientExecutorManager clientExecutorManager, final String connectionId) {
        super(channel, environment);
        this.channel = channel;
        this.clientRequestManager = clientRequestManager;
        this.clientExecutorManager = clientExecutorManager;
        this.connectionId = connectionId;
        handlerRegistry = createHandlerRegistry();
    }

    private Map createHandlerRegistry() {
        Map registry = new HashMap();
        registry.put((byte) (ADD_NOTIFICATION_LISTENER ^ RESPONSE_MASK), new MarshalledResponseHandler(VOID));
        registry.put((byte) (CREATE_MBEAN ^ RESPONSE_MASK), new MarshalledResponseHandler(OBJECT_INSTANCE));
        registry.put((byte) (GET_ATTRIBUTE ^ RESPONSE_MASK), new MarshalledResponseHandler(OBJECT));
        registry.put((byte) (GET_ATTRIBUTES ^ RESPONSE_MASK), new MarshalledResponseHandler(ATTRIBUTE_LIST));
        registry.put((byte) (GET_DEFAULT_DOMAIN ^ RESPONSE_MASK), new StringResponseHandler());
        registry.put((byte) (GET_DOMAINS ^ RESPONSE_MASK), new StringArrayResponseHandler());
        registry.put((byte) (GET_MBEAN_COUNT ^ RESPONSE_MASK), new IntegerResponseHandler());
        registry.put((byte) (GET_MBEAN_INFO ^ RESPONSE_MASK), new MarshalledResponseHandler(MBEAN_INFO));
        registry.put((byte) (GET_OBJECT_INSTANCE ^ RESPONSE_MASK), new MarshalledResponseHandler(
                OBJECT_INSTANCE));
        registry.put((byte) (INSTANCE_OF ^ RESPONSE_MASK), new BooleanResponseHandler());
        registry.put((byte) (IS_REGISTERED ^ RESPONSE_MASK), new BooleanResponseHandler());
        registry.put((byte) (INVOKE ^ RESPONSE_MASK), new MarshalledResponseHandler(OBJECT));
        registry.put((byte) (QUERY_MBEANS ^ RESPONSE_MASK), new MarshalledResponseHandler>(
                SET_OBJECT_INSTANCE));
        registry.put((byte) (QUERY_NAMES ^ RESPONSE_MASK), new MarshalledResponseHandler>(SET_OBJECT_NAME));
        registry.put((byte) (REMOVE_NOTIFICATION_LISTENER ^ RESPONSE_MASK), new MarshalledResponseHandler(VOID));
        registry.put((byte) (SET_ATTRIBUTE ^ RESPONSE_MASK), new MarshalledResponseHandler(VOID));
        registry.put((byte) (SET_ATTRIBUTES ^ RESPONSE_MASK), new MarshalledResponseHandler(ATTRIBUTE_LIST));
        registry.put((byte) (UNREGISTER_MBEAN ^ RESPONSE_MASK), new MarshalledResponseHandler(VOID));

        registry.put(SEND_NOTIFICATION, new NotificationHandler());

        return Collections.unmodifiableMap(registry);
    }

    void start() {
        mbeanServerConnection = new TheConnection();
        localNotificationManager = new LocalNotificationManager();
        channel.receiveMessage(new MessageReceiver());
    }

    @Override
    Map getHandlerRegistry() {
        return handlerRegistry;
    }

    @Override
    protected ClientRequestManager getClientRequestManager() {
        return clientRequestManager;
    }

    @Override
    protected ClientExecutorManager getClientExecutorManager() {
        return clientExecutorManager;
    }

    public String getConnectionId() {
        if (connectionId == null) {
            throw new IllegalStateException("Connection ID not set");
        }

        return connectionId;
    }

    public MBeanServerConnection getMBeanServerConnection(Subject subject) {
        if (subject != null) {
            throw new UnsupportedOperationException("Subject delegation not supported for getMBeanServerConnection");
        }

        // It is normal for only a single instance of MBeanServerConnection to be used.
        return mbeanServerConnection;
    }

    public void close() {
        clientExecutorManager.close();
    }

    /**
     * The local management of notifications.
     */
    private class LocalNotificationManager {

        private int nextNotificationId = 1;

        private Map listeners = new HashMap();

        private synchronized int getNextNotificationId() {
            int next = nextNotificationId++;
            // After the maximum integer start back at the beginning.
            if (next < 0) {
                nextNotificationId = 2;
                next = 1;
            }
            return next;
        }

        private synchronized int associate(ObjectName target, NotificationListener listener, NotificationFilter filter,
                Object handBack) {
            Integer next = getNextNotificationId();

            // Not likely but possible to use all IDs and start back at beginning while
            // old request still in progress.
            while (listeners.containsKey(next)) {
                next = getNextNotificationId();
            }

            Association association = new Association();
            association.target = target;
            association.listener = listener;
            association.filter = filter;
            association.handBack = handBack;

            listeners.put(next, association);
            return next;
        }

        private synchronized void cancel(int id) {
            listeners.remove(id);
        }

        private synchronized Association get(int id) {
            return listeners.get(id);
        }

        /*
         * The message received will already be being processed on a Thread obtained from the local Executor, for this reason
         * there is no point in creating a new Runnable and passing it to the executor to process the Notification.
         */

        private void notify(int id, Notification n, Object handback) {
            Association association = get(id);
            if (association != null) {

                association.listener.handleNotification(n, association.handBack);
            } else {
                // If an invalid ID is received don't throw an error, instead just send
                // a message to the server canceling the notification by id.
                try {
                    log.warnf("Notification recieved for non existant NotificationListener %d", id);
                    mbeanServerConnection.removeNotificationListener(new int[] { id });
                } catch (InstanceNotFoundException e) {
                } catch (ListenerNotFoundException e) {
                } catch (IOException e) {
                }
            }
        }

        private synchronized int[] matchToRemove(ObjectName name, NotificationListener listener) {
            List toRemove = new ArrayList();
            for (Integer current : listeners.keySet()) {
                Association association = listeners.get(current);
                if ((name == association.target || name.equals(association.target) && listener == association.listener)) {
                    toRemove.add(current);
                }
            }

            int[] response = new int[toRemove.size()];
            for (int i = 0; i < response.length; i++) {
                response[i] = toRemove.get(i);
                listeners.remove(response[i]);
            }
            return response;
        }

        private synchronized int[] matchToRemove(ObjectName name, NotificationListener listener, NotificationFilter filter,
                Object handback) {
            List toRemove = new ArrayList();
            for (Integer current : listeners.keySet()) {
                Association association = listeners.get(current);
                if ((name == association.target || name.equals(association.target)) && listener == association.listener
                        && filter == association.filter && handback == association.handBack) {
                    toRemove.add(current);
                }
            }

            int[] response = new int[toRemove.size()];
            for (int i = 0; i < response.length; i++) {
                response[i] = toRemove.get(i);
                listeners.remove(response[i]);
            }
            return response;
        }

        private class Association {
            private ObjectName target;
            private NotificationListener listener;
            private NotificationFilter filter;
            private Object handBack;
        }

    }

    private class TheConnection implements RemotingMBeanServerConnection {

        public Connection getConnection() {
            return channel.getConnection();
        }

        // TODO - Consider a proxy so the specific methods only need to marshall their specific
        // portion of the protocol.

        public ObjectInstance createMBean(final String className, final ObjectName name) throws ReflectionException,
                InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException,
                IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {
                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(CREATE_MBEAN);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(2); // Sending 2 parameters.

                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(className);

                        marshaller.writeByte(OBJECT_NAME);

                        marshaller.writeObject(name);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] createMBean - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        reflectionException(response.e);
                        instanceAlreadyExistsException(response.e);
                        mbeanRegistrationException(response.e);
                        mbeanException(response.e);
                        notCompliantMBeanException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to obtain createMBean, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public ObjectInstance createMBean(final String className, final ObjectName name, final ObjectName loaderName)
                throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException,
                NotCompliantMBeanException, InstanceNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(CREATE_MBEAN);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(3); // Sending 3 parameters.

                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(className);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(name);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(loaderName);

                        marshaller.close();

                    }
                });

                log.tracef("[%d] createMBean - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        reflectionException(response.e);
                        instanceAlreadyExistsException(response.e);
                        mbeanRegistrationException(response.e);
                        mbeanException(response.e);
                        notCompliantMBeanException(response.e);
                        instanceNotFoundException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to obtain isRegistered, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public ObjectInstance createMBean(final String className, final ObjectName name, final Object[] params,
                final String[] signature) throws ReflectionException, InstanceAlreadyExistsException,
                MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(CREATE_MBEAN);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(4); // Sending 4 parameters.

                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(className);

                        marshaller.writeByte(OBJECT_NAME);

                        marshaller.writeObject(name);

                        marshaller.writeByte(OBJECT_ARRAY);
                        marshaller.writeInt(params.length);
                        for (Object current : params) {
                            marshaller.writeObject(current);
                        }

                        marshaller.writeByte(STRING_ARRAY);
                        marshaller.writeInt(signature.length);
                        for (String current : signature) {
                            marshaller.writeUTF(current);
                        }

                        marshaller.close();
                    }
                });
                log.tracef("[%d] createMBean - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        reflectionException(response.e);
                        instanceAlreadyExistsException(response.e);
                        mbeanRegistrationException(response.e);
                        mbeanException(response.e);
                        notCompliantMBeanException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke createMBean, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public ObjectInstance createMBean(final String className, final ObjectName name, final ObjectName loaderName,
                final Object[] params, final String[] signature) throws ReflectionException, InstanceAlreadyExistsException,
                MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(CREATE_MBEAN);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(5); // Sending 5 parameters.

                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(className);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(name);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(loaderName);

                        marshaller.writeByte(OBJECT_ARRAY);
                        marshaller.writeInt(params.length);
                        for (Object current : params) {
                            marshaller.writeObject(current);
                        }

                        marshaller.writeByte(STRING_ARRAY);
                        marshaller.writeInt(signature.length);
                        for (String current : signature) {
                            marshaller.writeUTF(current);
                        }

                        marshaller.close();

                    }
                });

                log.tracef("[%d] createMBean - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        reflectionException(response.e);
                        instanceAlreadyExistsException(response.e);
                        mbeanRegistrationException(response.e);
                        mbeanException(response.e);
                        notCompliantMBeanException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke createMBean, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public void unregisterMBean(final ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException,
                IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(UNREGISTER_MBEAN);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] unregisterMBean - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return;
                        }

                        instanceNotFoundException(response.e);
                        mbeanRegistrationException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke unregisterMBean, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public ObjectInstance getObjectInstance(final ObjectName name) throws InstanceNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_OBJECT_INSTANCE);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] getObjectInstance - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        instanceNotFoundException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke getObjectInstance, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public Set queryMBeans(final ObjectName name, final QueryExp query) throws IOException {
            VersionedIoFuture>> future = new VersionedIoFuture>>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(QUERY_MBEANS);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(QUERY_EXP);
                        marshaller.writeObject(query);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] queryMBeans - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder> response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke queryMBeans, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public Set queryNames(final ObjectName name, final QueryExp query) throws IOException {
            VersionedIoFuture>> future = new VersionedIoFuture>>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(QUERY_NAMES);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(QUERY_EXP);
                        marshaller.writeObject(query);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] queryNames - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder> response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to obtain isRegistered, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public boolean isRegistered(final ObjectName name) throws IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(IS_REGISTERED);
                        output.writeInt(correlationId);
                        output.writeByte(OBJECT_NAME);

                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] isRegistered - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    case FAILED:
                        throw future.getException();
                    default:
                        throw new IOException("Unable to obtain isRegistered, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public Integer getMBeanCount() throws IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {
                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_MBEAN_COUNT);
                        output.writeInt(correlationId);
                    }
                });

                log.tracef("[%d] getMBeanCount - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    case FAILED:
                        throw future.getException();
                    default:
                        throw new IOException("Unable to obtain MBeanCount, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public Object getAttribute(final ObjectName name, final String attribute) throws MBeanException,
                AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_ATTRIBUTE);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(attribute);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] getAttribute - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        mbeanException(response.e);
                        attributeNotFoundException(response.e);
                        instanceNotFoundException(response.e);
                        reflectionException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to obtain isRegistered, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public AttributeList getAttributes(final ObjectName name, final String[] attributes) throws InstanceNotFoundException,
                ReflectionException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_ATTRIBUTES);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(STRING_ARRAY);
                        marshaller.writeInt(attributes.length);
                        for (String current : attributes) {
                            marshaller.writeUTF(current);
                        }

                        marshaller.close();
                    }
                });

                log.tracef("[%d] getAttributes - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        instanceNotFoundException(response.e);
                        reflectionException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke getAttributes, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public void setAttribute(final ObjectName name, final Attribute attribute) throws InstanceNotFoundException,
                AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(SET_ATTRIBUTE);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(ATTRIBUTE);
                        marshaller.writeObject(attribute);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] setAttribute - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();

                        if (response.e == null) {
                            return;
                        }

                        instanceNotFoundException(response.e);
                        attributeNotFoundException(response.e);
                        invalidAttributeValueException(response.e);
                        mbeanException(response.e);
                        reflectionException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke setAttribute, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public AttributeList setAttributes(final ObjectName name, final AttributeList attributes)
                throws InstanceNotFoundException, ReflectionException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(SET_ATTRIBUTES);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(ATTRIBUTE_LIST);
                        marshaller.writeObject(attributes);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] setAttributes - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        instanceNotFoundException(response.e);
                        reflectionException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke setAttributes, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public Object invoke(final ObjectName name, final String operationName, final Object[] params, final String[] signature)
                throws InstanceNotFoundException, MBeanException, ReflectionException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(INVOKE);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(operationName);

                        marshaller.writeByte(OBJECT_ARRAY);
                        if (params != null) {
                            marshaller.writeInt(params.length);
                            for (Object current : params) {
                                marshaller.writeObject(current);
                            }
                        } else {
                            marshaller.writeInt(0);
                        }

                        marshaller.writeByte(STRING_ARRAY);
                        if (signature != null) {
                            marshaller.writeInt(signature.length);
                            for (String current : signature) {
                                marshaller.writeUTF(current);
                            }
                        } else {
                            marshaller.writeInt(0);
                        }

                        marshaller.close();
                    }
                });

                log.tracef("[%d] invoke - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        instanceNotFoundException(response.e);
                        mbeanException(response.e);
                        reflectionException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke invoke(), status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public String getDefaultDomain() throws IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_DEFAULT_DOMAIN);
                        output.writeInt(correlationId);
                    }
                });

                log.tracef("[%d] getDefaultDomain - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    case FAILED:
                        throw future.getException();
                    default:
                        throw new IOException("Unable to obtain DefaultDomain, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public String[] getDomains() throws IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_DOMAINS);
                        output.writeInt(correlationId);
                    }
                });

                log.tracef("[%d] getDomains - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    case FAILED:
                        throw future.getException();
                    default:
                        throw new IOException("Unable to obtain Domains, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public void addNotificationListener(final ObjectName name, NotificationListener listener,
                final NotificationFilter filter, final Object handback) throws InstanceNotFoundException, IOException {
            final int notificationId = localNotificationManager.associate(name, listener, filter, handback);

            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(ADD_NOTIFICATION_LISTENER);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        // This indicates that the target is remote.
                        marshaller.writeByte(INTEGER);
                        marshaller.writeInt(notificationId);

                        marshaller.writeByte(NOTIFICATION_FILTER);
                        marshaller.writeObject(filter);

                        marshaller.writeByte(OBJECT);
                        marshaller.writeObject(null);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] addNotificationListener - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        localNotificationManager.cancel(notificationId);
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();

                        if (response.e == null) {
                            return;
                        }

                        localNotificationManager.cancel(notificationId);
                        instanceNotFoundException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        localNotificationManager.cancel(notificationId);
                        throw new IOException("Unable to invoke addNotificationListener, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public void addNotificationListener(final ObjectName name, final ObjectName listener, final NotificationFilter filter,
                final Object handback) throws InstanceNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(ADD_NOTIFICATION_LISTENER);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(listener);

                        marshaller.writeByte(NOTIFICATION_FILTER);
                        marshaller.writeObject(filter);

                        marshaller.writeByte(OBJECT);
                        marshaller.writeObject(handback);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] addNotificationListener - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();

                        if (response.e == null) {
                            return;
                        }

                        jmRuntimeException(response.e);
                        instanceNotFoundException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke addNotificationListener, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public void removeNotificationListener(final ObjectName name, final ObjectName listener)
                throws InstanceNotFoundException, ListenerNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(REMOVE_NOTIFICATION_LISTENER);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(2); // Sending 2 parameters.

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(listener);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] removeNotificationListener - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();

                        if (response.e == null) {
                            return;
                        }

                        jmRuntimeException(response.e);
                        instanceNotFoundException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke removeNotificationListener, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public void removeNotificationListener(final ObjectName name, final ObjectName listener,
                final NotificationFilter filter, final Object handback) throws InstanceNotFoundException,
                ListenerNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(REMOVE_NOTIFICATION_LISTENER);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(4); // Sending 4 parameters.

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(OBJECT_NAME);
                        marshaller.writeObject(listener);

                        marshaller.writeByte(NOTIFICATION_FILTER);
                        marshaller.writeObject(filter);

                        marshaller.writeByte(OBJECT);
                        marshaller.writeObject(handback);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] removeNotificationListener - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();

                        if (response.e == null) {
                            return;
                        }

                        jmRuntimeException(response.e);
                        instanceNotFoundException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke removeNotificationListener, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        private void removeNotificationListener(final int[] listenerIds) throws InstanceNotFoundException,
                ListenerNotFoundException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(REMOVE_NOTIFICATION_LISTENER);
                        output.writeInt(correlationId);

                        output.writeByte(INTEGER);
                        output.writeInt(1); // Sending 2 parameters.

                        output.writeByte(INTEGER_ARRAY);
                        output.writeInt(listenerIds.length);
                        for (int current : listenerIds) {
                            output.writeInt(current);
                        }
                    }
                });

                log.tracef("[%d] removeNotificationListener - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();

                        if (response.e == null) {
                            return;
                        }

                        instanceNotFoundException(response.e);
                        listenerNotFoundException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to invoke removeNotificationListener, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }

        }

        public void removeNotificationListener(ObjectName name, NotificationListener listener)
                throws InstanceNotFoundException, ListenerNotFoundException, IOException {
            removeNotificationListener(localNotificationManager.matchToRemove(name, listener));
        }

        public void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter,
                Object handback) throws InstanceNotFoundException, ListenerNotFoundException, IOException {
            removeNotificationListener(localNotificationManager.matchToRemove(name, listener, filter, handback));
        }

        public MBeanInfo getMBeanInfo(final ObjectName name) throws InstanceNotFoundException, IntrospectionException,
                ReflectionException, IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(GET_MBEAN_INFO);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.close();
                    }
                });

                log.tracef("[%d] getMBeanInfo - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        instanceNotFoundException(response.e);
                        introspectionException(response.e);
                        reflectionException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to obtain isRegistered, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        public boolean isInstanceOf(final ObjectName name, final String className) throws InstanceNotFoundException,
                IOException {
            VersionedIoFuture> future = new VersionedIoFuture>();
            final int correlationId = clientRequestManager.reserveNextCorrelationId(future);
            try {
                write(new MessageWriter() {

                    @Override
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(INSTANCE_OF);
                        output.writeInt(correlationId);

                        output.writeByte(OBJECT_NAME);
                        Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeObject(name);

                        marshaller.writeByte(STRING);
                        marshaller.writeUTF(className);
                    }
                });

                log.tracef("[%d] isInstanceOf - Request Sent", correlationId);

                IoFuture.Status result = future.await(timeoutSeconds, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        throw future.getException();
                    case DONE:
                        TypeExceptionHolder response = future.get();
                        if (response.e == null) {
                            return response.value;
                        }
                        instanceNotFoundException(response.e);
                        jmRuntimeException(response.e);
                        throw toIoException(response.e);
                    default:
                        throw new IOException("Unable to obtain isRegistered, status=" + result.toString());
                }
            } finally {
                clientRequestManager.releaseCorrelationId(correlationId);
            }
        }

        private void attributeNotFoundException(Exception e) throws AttributeNotFoundException {
            if (e != null && e instanceof AttributeNotFoundException) {
                throw (AttributeNotFoundException) e;
            }
        }

        private void instanceAlreadyExistsException(Exception e) throws InstanceAlreadyExistsException {
            if (e != null && e instanceof InstanceAlreadyExistsException) {
                throw (InstanceAlreadyExistsException) e;
            }
        }

        private void instanceNotFoundException(Exception e) throws InstanceNotFoundException {
            if (e != null && e instanceof InstanceNotFoundException) {
                throw (InstanceNotFoundException) e;
            }
        }

        private void introspectionException(Exception e) throws IntrospectionException {
            if (e != null && e instanceof IntrospectionException) {
                throw (IntrospectionException) e;
            }
        }

        private void invalidAttributeValueException(Exception e) throws InvalidAttributeValueException {
            if (e != null && e instanceof InvalidAttributeValueException) {
                throw (InvalidAttributeValueException) e;
            }
        }

        private void listenerNotFoundException(Exception e) throws ListenerNotFoundException {
            if (e != null && e instanceof ListenerNotFoundException) {
                throw (ListenerNotFoundException) e;
            }
        }

        private void mbeanRegistrationException(Exception e) throws MBeanRegistrationException {
            if (e != null && e instanceof MBeanRegistrationException) {
                throw (MBeanRegistrationException) e;
            }
        }

        private void mbeanException(Exception e) throws MBeanException {
            if (e != null && e instanceof MBeanException) {
                throw (MBeanException) e;
            }
        }

        private void notCompliantMBeanException(Exception e) throws NotCompliantMBeanException {
            if (e != null && e instanceof NotCompliantMBeanException) {
                throw (NotCompliantMBeanException) e;
            }
        }

        private void reflectionException(Exception e) throws ReflectionException {
            if (e != null && e instanceof ReflectionException) {
                throw (ReflectionException) e;
            }
        }

        private void jmRuntimeException(Exception e) {
            if (e instanceof JMRuntimeException) {
                throw (JMRuntimeException) e;
            }
        }
    }

    private class BooleanResponseHandler extends BaseResponseHandler {

        @Override
        protected byte getExpectedType() {
            return BOOLEAN;
        }

        @Override
        protected Boolean readValue(DataInput input) throws IOException {
            return input.readBoolean();
        }

    }

    private class IntegerResponseHandler extends BaseResponseHandler {

        @Override
        protected byte getExpectedType() {
            return INTEGER;
        }

        @Override
        protected Integer readValue(DataInput input) throws IOException {
            return input.readInt();
        }

    }

    private class StringArrayResponseHandler extends BaseResponseHandler {

        @Override
        protected byte getExpectedType() {
            return STRING_ARRAY;
        }

        @Override
        protected String[] readValue(DataInput input) throws IOException {
            int count = input.readInt();
            String[] response = new String[count];
            for (int i = 0; i < count; i++) {
                response[i] = input.readUTF();
            }

            return response;
        }

    }

    private class NotificationHandler implements Common.MessageHandler {

        /*
         * The message received will already be being processed on a Thread obtained from the local Executor, for this reason
         * there is no point in creating a new Runnable and passing it to the executor to process the Notification.
         */

        @Override
        public void handle(DataInput input, int correlationId) throws IOException {
            log.trace("Notification");

            byte paramType = input.readByte();
            if (paramType != INTEGER) {
                throw new IOException("Unexpected paramType");
            }
            int listenerId = input.readInt();

            paramType = input.readByte();
            if (paramType != NOTIFICATION) {
                throw new IOException("Unexpected paramType");
            }

            try {
                Unmarshaller unmarshaller = prepareForUnMarshalling(input);
                Notification notification = unmarshaller.readObject(Notification.class);

                paramType = unmarshaller.readByte();
                if (paramType != OBJECT) {
                    throw new IOException("Unexpected paramType");
                }
                Object handBack = unmarshaller.readObject();

                localNotificationManager.notify(listenerId, notification, handBack);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            }

        }

    }

}