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

org.jboss.naming.remote.protocol.v1.Protocol 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).

There is a newer version: 32.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.naming.remote.protocol.v1;

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

import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;

import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.Unmarshaller;
import org.jboss.naming.remote.client.RemoteContext;
import org.jboss.naming.remote.client.RemoteNamingStore;
import org.jboss.naming.remote.protocol.ProtocolCommand;
import org.jboss.naming.remote.server.RemoteNamingService;
import org.jboss.remoting3.Channel;
import org.xnio.IoFuture;

import static org.jboss.naming.remote.client.ClientUtil.namingException;
import static org.jboss.naming.remote.protocol.v1.Constants.BINDING;
import static org.jboss.naming.remote.protocol.v1.Constants.CONTEXT;
import static org.jboss.naming.remote.protocol.v1.Constants.NAME;
import static org.jboss.naming.remote.protocol.v1.Constants.OBJECT;
import static org.jboss.naming.remote.protocol.v1.Constants.SUCCESS;
import static org.jboss.naming.remote.protocol.v1.ReadUtil.prepareForUnMarshalling;
import static org.jboss.naming.remote.protocol.v1.WriteUtil.prepareForMarshalling;
import static org.jboss.naming.remote.protocol.v1.WriteUtil.write;
import static org.jboss.naming.remote.protocol.v1.WriteUtil.writeExceptionResponse;
import static org.jboss.naming.remote.protocol.v1.WriteUtil.writeResponse;

/**
 * @author John Bailey
 */
class Protocol {
    static ProtocolCommand LOOKUP = new BaseProtocolCommand>((byte) 0x01) {
        public Object execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Lookup requires a single name argument");
            }
            final Name name = Name.class.cast(args[0]);
            final ClassLoadingNamedIoFuture future = new ClassLoadingNamedIoFuture(name, Thread.currentThread().getContextClassLoader());
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return future.get();
                    default:
                        throw new NamingException("Unable to invoke lookup, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to lookup", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {

            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.close();
            }

            try {
                final Object result = remoteNamingService.getLocalContext().lookup(name);
                write(channel, new WriteUtil.Writer() {
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);
                        output.writeByte(SUCCESS);
                        if (result instanceof Context) {
                            output.writeByte(CONTEXT);
                        } else {
                            output.writeByte(OBJECT);
                            final Marshaller marshaller = prepareForMarshalling(output);
                            marshaller.writeObject(result);
                            marshaller.finish();
                        }
                    }
                });
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(final DataInput input, final int correlationId, final RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, ClassLoadingNamedIoFuture future) throws IOException {
                    byte parameterType = input.readByte();
                    switch (parameterType) {
                        case OBJECT: {
                            try {
                                final Unmarshaller unmarshaller = prepareForUnMarshalling(input, future.getClassLoader());
                                future.setResult(unmarshaller.readObject());
                                unmarshaller.finish();
                            } catch (ClassNotFoundException e) {
                                throw new IOException(e);
                            } catch (ClassCastException e) {
                                throw new IOException(e);
                            }
                            break;
                        }
                        case CONTEXT: {
                            future.setResult(new RemoteContext(NamedIoFuture.class.cast(future).name, namingStore, new Hashtable()));
                            break;
                        }
                        default: {
                            throw new IOException("Unexpected response parameter received.");
                        }

                    }
                }
            });
        }
    };

    static ProtocolCommand BIND = new BaseProtocolCommand>((byte) 0x02) {
        public Void execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 2 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Rebind requires a name and object argument");
            }
            final Name name = Name.class.cast(args[0]);
            final Object object = args[1];

            final ProtocolIoFuture future = new ProtocolIoFuture();
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.writeByte(OBJECT);
                        marshaller.writeObject(object);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return null;
                    default:
                        throw new NamingException("Unable to invoke bind, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to bind", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {
            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            Object object;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);

                paramType = unmarshaller.readByte();
                if (paramType != OBJECT) {
                    remoteNamingService.getLogger().unexpectedParameterType(OBJECT, paramType);
                }
                object = unmarshaller.readObject();
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.finish();
            }

            try {
                remoteNamingService.getLocalContext().bind(name, object);
                writeResponse(channel, getCommandId(), correlationId);
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(DataInput input, int correlationId, RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, ProtocolIoFuture future) throws IOException {
                    future.setResult(null);
                }
            });
        }
    };

    static ProtocolCommand REBIND = new BaseProtocolCommand>((byte) 0x03) {
        public Void execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 2 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Bind requires a name and object argument");
            }
            final Name name = Name.class.cast(args[0]);
            final Object object = args[1];

            final ProtocolIoFuture future = new ProtocolIoFuture();
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.writeByte(OBJECT);
                        marshaller.writeObject(object);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return null;
                    default:
                        throw new NamingException("Unable to invoke rebind, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to rebind", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {
            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            Object object;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);

                paramType = unmarshaller.readByte();
                if (paramType != OBJECT) {
                    remoteNamingService.getLogger().unexpectedParameterType(OBJECT, paramType);
                }
                object = unmarshaller.readObject();
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.finish();
            }

            try {
                remoteNamingService.getLocalContext().rebind(name, object);
                writeResponse(channel, getCommandId(), correlationId);
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(DataInput input, int correlationId, RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, ProtocolIoFuture future) throws IOException {
                    future.setResult(null);
                }
            });
        }
    };

    static ProtocolCommand> LIST = new BaseProtocolCommand, ProtocolIoFuture>>((byte) 0x04) {
        public List execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("List requires a name argument.");
            }
            final Name name = Name.class.cast(args[0]);

            final ProtocolIoFuture> future = new ProtocolIoFuture>();
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return future.get();
                    default:
                        throw new NamingException("Unable to invoke list, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to list", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {
            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.finish();
            }

            try {
                final NamingEnumeration results = remoteNamingService.getLocalContext().list(name);
                final List resultList = new ArrayList();
                while (results.hasMore()) {
                    resultList.add(results.next());
                }
                write(channel, new WriteUtil.Writer() {
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);
                        output.writeByte(SUCCESS);

                        output.writeByte(Constants.LIST);
                        output.writeInt(resultList.size());

                        final Marshaller marshaller = prepareForMarshalling(output);
                        for (NameClassPair nameClassPair : resultList) {
                            marshaller.writeObject(nameClassPair);
                        }
                        marshaller.finish();
                    }
                });
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(DataInput input, int correlationId, RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>>() {
                public void read(final DataInput input, ProtocolIoFuture> future) throws IOException {
                    byte parameterType = input.readByte();
                    if (parameterType != Constants.LIST) {
                        throw new IOException("Unexpected response parameter received.");
                    }
                    final int listSize = input.readInt();
                    final List results = new ArrayList(listSize);
                    final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
                    for (int i = 0; i < listSize; i++) {
                        try {
                            results.add(unmarshaller.readObject(NameClassPair.class));
                        } catch (ClassNotFoundException e) {
                            throw new IOException(e);
                        } catch (ClassCastException e) {
                            throw new IOException(e);
                        }
                    }
                    unmarshaller.finish();
                    future.setResult(results);
                }
            });
        }
    };

    static ProtocolCommand> LIST_BINDINGS = new BaseProtocolCommand, ClassLoadingNamedIoFuture>>((byte) 0x05) {
        public List execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("List requires a name argument.");
            }
            final Name name = Name.class.cast(args[0]);

            final ClassLoadingNamedIoFuture> future = new ClassLoadingNamedIoFuture>(name, Thread.currentThread().getContextClassLoader());
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return future.get();
                    default:
                        throw new NamingException("Unable to invoke listBindings, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to list bindings", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {
            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.finish();
            }

            try {
                final NamingEnumeration results = remoteNamingService.getLocalContext().listBindings(name);
                final List resultList = new ArrayList();
                while (results.hasMore()) {
                    resultList.add(results.next());
                }
                write(channel, new WriteUtil.Writer() {
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);
                        output.writeByte(SUCCESS);

                        output.writeByte(Constants.LIST);
                        output.writeInt(resultList.size());
                        final Marshaller marshaller = prepareForMarshalling(output);
                        for (Binding binding : resultList) {
                            if (binding.getObject() instanceof Context) {
                                marshaller.writeByte(Constants.CONTEXT);
                                marshaller.writeUTF(binding.getName());
                            } else {
                                marshaller.writeByte(Constants.BINDING);
                                marshaller.writeObject(binding);
                            }
                        }
                        marshaller.finish();
                    }
                });
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(final DataInput input, final int correlationId, final RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>>() {
                public void read(final DataInput input, ClassLoadingNamedIoFuture> future) throws IOException {
                    byte parameterType = input.readByte();
                    if (parameterType != Constants.LIST) {
                        throw new IOException("Unexpected response parameter received.");
                    }
                    final int listSize = input.readInt();
                    final List results = new ArrayList(listSize);
                    final Unmarshaller unmarshaller = prepareForUnMarshalling(input, future.getClassLoader());
                    for (int i = 0; i < listSize; i++) {
                        parameterType = unmarshaller.readByte();
                        switch (parameterType) {
                            case BINDING: {
                                try {
                                    final Binding binding = unmarshaller.readObject(Binding.class);
                                    results.add(binding);
                                } catch (ClassNotFoundException e) {
                                    throw new IOException(e);
                                } catch (ClassCastException e) {
                                    throw new IOException(e);
                                }
                                break;
                            }
                            case CONTEXT: {
                                final String bindingName = unmarshaller.readUTF();
                                final Name contextName;
                                try {
                                    contextName = Name.class.cast(NamedIoFuture.class.cast(future).name.clone()).add(bindingName);
                                } catch (InvalidNameException e) {
                                    throw new IOException(e);
                                }
                                final Context context = new RemoteContext(contextName, namingStore, new Hashtable());
                                results.add(new Binding(bindingName, Context.class.getName(), context));
                                break;
                            }
                            default: {
                                throw new IOException("Unexpected response parameter received.");
                            }
                        }
                    }
                    unmarshaller.finish();
                    future.setResult(results);
                }
            });
        }
    };

    static ProtocolCommand UNBIND = new BaseProtocolCommand>((byte) 0x06) {
        public Void execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Rebind requires a name");
            }
            final Name name = Name.class.cast(args[0]);

            final ProtocolIoFuture future = new ProtocolIoFuture();
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return null;
                    default:
                        throw new NamingException("Unable to invoke unbind, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to unbind", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {
            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.finish();
            }

            try {
                remoteNamingService.getLocalContext().unbind(name);
                writeResponse(channel, getCommandId(), correlationId);
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(DataInput input, int correlationId, RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, ProtocolIoFuture future) throws IOException {
                    future.setResult(null);
                }
            });
        }
    };

    static ProtocolCommand RENAME = new BaseProtocolCommand>((byte) 0x07) {
        public Void execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 2 || !(args[0] instanceof Name) || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Rename requires two name arguments");
            }
            final Name name = Name.class.cast(args[0]);
            final Name newName = Name.class.cast(args[1]);

            final ProtocolIoFuture future = new ProtocolIoFuture();
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(newName);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return null;
                    default:
                        throw new NamingException("Unable to invoke rename, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to rename", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {
            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            final Name name;
            final Name newName;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);

                paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(OBJECT, paramType);
                }
                newName = unmarshaller.readObject(Name.class);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.finish();
            }

            try {
                remoteNamingService.getLocalContext().rename(name, newName);
                writeResponse(channel, getCommandId(), correlationId);
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(DataInput input, int correlationId, RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, ProtocolIoFuture future) throws IOException {
                    future.setResult(null);
                }
            });
        }
    };

    static ProtocolCommand CREATE_SUBCONTEXT = new BaseProtocolCommand>((byte) 0x08) {

        public Context execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Create subcontext requires a single name argument");
            }
            final Name name = Name.class.cast(args[0]);
            final NamedIoFuture future = new NamedIoFuture(name);
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return future.get();
                    default:
                        throw new NamingException("Unable to invoke createSubcontext, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to create subcontext", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(final Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {

            Name name;
            try {
                final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
                unmarshaller.finish();
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            }

            try {
                remoteNamingService.getLocalContext().createSubcontext(name);
                write(channel, new WriteUtil.Writer() {
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);
                        output.writeByte(SUCCESS);
                        output.writeByte(CONTEXT);
                    }
                });
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(final DataInput input, final int correlationId, final RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, final NamedIoFuture future) throws IOException {
                    final byte parameterType = input.readByte();
                    if (parameterType != CONTEXT) {
                        throw new IOException("Unexpected paramType");
                    }
                    future.setResult(new RemoteContext(NamedIoFuture.class.cast(future).name, namingStore, new Hashtable()));
                }
            });
        }
    };

    static ProtocolCommand DESTROY_SUBCONTEXT = new BaseProtocolCommand>((byte) 0x09) {

        public Void execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Destroy subcontext requires a single name argument");
            }
            final Name name = Name.class.cast(args[0]);
            final NamedIoFuture future = new NamedIoFuture(name);
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return future.get();
                    default:
                        throw new NamingException("Unable to invoke destroySubcontext, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to destroy subcontext", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {

            Name name;
            try {
                final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
                unmarshaller.finish();
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            }

            try {
                remoteNamingService.getLocalContext().destroySubcontext(name);
                writeResponse(channel, getCommandId(), correlationId);
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(final DataInput input, final int correlationId, final RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, NamedIoFuture future) throws IOException {
                	// should this return the context?
                    future.setResult(null);
                }
            });
        }
    };

    static ProtocolCommand LOOKUP_LINK = new BaseProtocolCommand>((byte) 0x10) {
        public Object execute(final Channel channel, final Object... args) throws IOException, NamingException {
            if (args.length != 1 || !(args[0] instanceof Name)) {
                throw new IllegalArgumentException("Lookup link requires a single name argument");
            }
            final Name name = Name.class.cast(args[0]);
            final ClassLoadingNamedIoFuture future = new ClassLoadingNamedIoFuture(name, Thread.currentThread().getContextClassLoader());
            final int correlationId = reserveNextCorrelationId(future);
            try {
                write(channel, new WriteUtil.Writer() {
                    public void write(final DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);

                        final Marshaller marshaller = prepareForMarshalling(output);
                        marshaller.writeByte(NAME);
                        marshaller.writeObject(name);
                        marshaller.finish();
                    }
                });

                final IoFuture.Status result = future.await(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                switch (result) {
                    case FAILED:
                        if (future.getHeldException() != null) {
                            throw future.getHeldException();
                        }
                        throw future.getException();
                    case DONE:
                        return future.get();
                    default:
                        throw new NamingException("Unable to invoke lookupLink, status=" + result.toString());
                }

            } catch (NamingException e) {
                throw e;
            } catch (Exception e) {
                throw namingException("Failed to lookup link", e);
            } finally {
                releaseCorrelationId(correlationId);
            }
        }

        public void handleServerMessage(Channel channel, final DataInput input, final int correlationId, final RemoteNamingService remoteNamingService) throws IOException {

            final Unmarshaller unmarshaller = prepareForUnMarshalling(input, this.getClass().getClassLoader());
            Name name;
            try {
                byte paramType = unmarshaller.readByte();
                if (paramType != NAME) {
                    remoteNamingService.getLogger().unexpectedParameterType(NAME, paramType);
                }
                name = unmarshaller.readObject(Name.class);
            } catch (ClassNotFoundException cnfe) {
                throw new IOException(cnfe);
            } finally {
                unmarshaller.close();
            }

            try {
                final Object result = remoteNamingService.getLocalContext().lookupLink(name);
                write(channel, new WriteUtil.Writer() {
                    public void write(DataOutput output) throws IOException {
                        output.writeByte(getCommandId());
                        output.writeInt(correlationId);
                        output.writeByte(SUCCESS);
                        if (result instanceof Context) {
                            output.writeByte(CONTEXT);
                        } else {
                            output.writeByte(BINDING);
                            final Marshaller marshaller = prepareForMarshalling(output);
                            marshaller.writeObject(result);
                            marshaller.finish();
                        }
                    }
                });
            } catch (NamingException e) {
                writeExceptionResponse(channel, e, getCommandId(), correlationId);
            }
        }

        public void handleClientMessage(final DataInput input, final int correlationId, final RemoteNamingStore namingStore) throws IOException {
            readResult(correlationId, input, new ValueReader>() {
                public void read(final DataInput input, ClassLoadingNamedIoFuture future) throws IOException {
                    byte parameterType = input.readByte();
                    switch (parameterType) {
                        case BINDING: {
                            try {
                                final Unmarshaller unmarshaller = prepareForUnMarshalling(input, future.getClassLoader());
                                future.setResult(unmarshaller.readObject());
                                unmarshaller.finish();
                            } catch (ClassNotFoundException e) {
                                throw new IOException(e);
                            } catch (ClassCastException e) {
                                throw new IOException(e);
                            }
                            break;
                        }
                        case CONTEXT: {
                            future.setResult(new RemoteContext(NamedIoFuture.class.cast(future).name, namingStore, new Hashtable()));
                            break;
                        }
                        default: {
                            throw new IOException("Unexpected response parameter received.");
                        }

                    }
                }
            });
        }
    };

    private static class NamedIoFuture extends ProtocolIoFuture {
        private final Name name;

        private NamedIoFuture(Name name) {
            this.name = name;
        }
    }

    private static class ClassLoadingNamedIoFuture extends NamedIoFuture {
        /** The classloader to use when loading any returned objects*/
        private ClassLoader classLoader;

        private ClassLoadingNamedIoFuture(Name name, ClassLoader classLoader) {
            super(name);
        	this.classLoader = classLoader;
        }

        public ClassLoader getClassLoader() {
        	return classLoader;
        }
    }

    private static final Map commands = new HashMap();

    static void register(final ProtocolCommand command) {
        commands.put(command.getCommandId(), command);
    }

    static {
        register(LOOKUP);
        register(BIND);
        register(REBIND);
        register(LIST);
        register(LIST_BINDINGS);
        register(UNBIND);
        register(RENAME);
        register(CREATE_SUBCONTEXT);
        register(DESTROY_SUBCONTEXT);
        register(LOOKUP_LINK);
    }

    public static ProtocolCommand forId(final byte id) {
        return commands.get(id);
    }
}