org.globus.replica.rls.impl.rpc.SimpleRLSConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.replica.rls.impl.rpc.SimpleRLSConnection.java

Source

/*
 * Copyright 1999-2006 University of Chicago
 *
 * 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.globus.replica.rls.impl.rpc;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.gsi.GSIConstants;
import org.globus.gsi.gssapi.GSSConstants;
import org.globus.gsi.gssapi.GlobusGSSManagerImpl;
import org.globus.gsi.gssapi.auth.Authorization;
import org.globus.gsi.gssapi.auth.HostAuthorization;
import org.globus.gsi.gssapi.net.GssSocket;
import org.globus.gsi.gssapi.net.GssSocketFactory;
import org.globus.replica.rls.Administrative;
import org.globus.replica.rls.AttributeResult;
import org.globus.replica.rls.AttributeSearch;
import org.globus.replica.rls.BatchCatalogQuery;
import org.globus.replica.rls.BatchIndexQuery;
import org.globus.replica.rls.CatalogExistenceQuery;
import org.globus.replica.rls.CatalogQuery;
import org.globus.replica.rls.ConfigurationOption;
import org.globus.replica.rls.IndexExistenceQuery;
import org.globus.replica.rls.IndexMapping;
import org.globus.replica.rls.IndexMappingResult;
import org.globus.replica.rls.IndexQuery;
import org.globus.replica.rls.LocalReplicaCatalog;
import org.globus.replica.rls.Mapping;
import org.globus.replica.rls.MappingResult;
import org.globus.replica.rls.QueryResults;
import org.globus.replica.rls.RLIUpdate;
import org.globus.replica.rls.RLSAttribute;
import org.globus.replica.rls.RLSAttributeObject;
import org.globus.replica.rls.RLSClient;
import org.globus.replica.rls.RLSConnection;
import org.globus.replica.rls.RLSException;
import org.globus.replica.rls.RLSIOException;
import org.globus.replica.rls.RLSLRCInfo;
import org.globus.replica.rls.RLSOffsetLimit;
import org.globus.replica.rls.RLSRLIInfo;
import org.globus.replica.rls.RLSStatusCode;
import org.globus.replica.rls.RLSStats;
import org.globus.replica.rls.Rename;
import org.globus.replica.rls.RenameResult;
import org.globus.replica.rls.ReplicaLocationIndex;
import org.globus.replica.rls.Results;
import org.globus.replica.rls.SimpleCatalogQuery;
import org.globus.replica.rls.SimpleIndexQuery;
import org.globus.util.GlobusURL;
import org.gridforum.jgss.ExtendedGSSContext;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;

/**
 * These are implementation details. Too mysterious and spookey for YOU to be
 * looking at, don't you think?
 */
public class SimpleRLSConnection implements RLSConnection {

    private static final int DEFAULT_PORT = 39281;
    private static final String PROTOCOL_SECURE = "rls";
    private static final String PROTOCOL_UNSECURE = "rlsn";

    private static final String CLOSE = "close";

    // CONSTANTS //

    private static final String LOCALHOST = "localhost";

    private static final int RPC_NORESPONSE = 1;
    private static final int RPC_NORESULT = 2;
    private static final int RPC_SINGLERESULT = 4;
    private static final int RPC_MULTIPLERESULTS = 8;

    // STATIC //

    private static Log logger = LogFactory.getLog(SimpleRLSConnection.class.getName());

    // FIELDS //

    LocalReplicaCatalog catalog = null;
    ReplicaLocationIndex index = null;
    Administrative admin = null;

    Socket sock = null;
    RPCOutputStream out = null;
    RPCInputStream in = null;

    // CONNECTION INTERFACE //

    /**
     * Implements the RLSConnection interface for clients of the RLS server
     * using the RPC-style protocol.
     */
    public SimpleRLSConnection(GlobusURL url, GSSCredential cred) throws RLSException {

        // Validate params
        if (url == null)
            throw new IllegalArgumentException("url == null");

        // Establish connection according to chosen protocol
        if (PROTOCOL_UNSECURE.equals(url.getProtocol())) {
            _establishUnsecuredConnection(url);
        } else if (PROTOCOL_SECURE.equals(url.getProtocol())) {
            _establishSecuredConnection(url, cred);
        } else {
            throw new IllegalArgumentException("Protocol not supported (" + "protocol: " + url.getProtocol() + ")");
        }
    }

    /**
     * Establishes a connection to RLS server without security.
     */
    private void _establishUnsecuredConnection(GlobusURL url) throws RLSException {

        // Establish RLS connection
        try {
            // Parse port
            int port = url.getPort();
            if (port < 0)
                port = DEFAULT_PORT;

            // Parse host
            String host = url.getHost();
            if (LOCALHOST.equalsIgnoreCase(host))
                host = InetAddress.getLocalHost().getCanonicalHostName();

            // Connect to server
            sock = new Socket(host, port);

            // Get socket input/output streams
            OutputStream rawOut = sock.getOutputStream();
            InputStream rawIn = sock.getInputStream();
            rawOut.flush(); // Not required to flush

            // Wrap I/O streams with RPC streams
            out = new RPCOutputStream(rawOut);
            in = new RPCInputStream(rawIn);

            // Verify RLS server response
            int rc = in.readInt();
            if (rc != RLSStatusCode.RLS_SUCCESS) {
                logger.debug("Failed to establish RLS connection, RLS server" + " returned rc=" + rc);
                _release();
                throw new RLSException(rc, RLSStatusCode.toMessage(rc));
            }

            // Instantiate RLS interfaces
            catalog = new LRCImpl();
            index = new RLIImpl();
            admin = new AdminImpl();
        } catch (UnknownHostException e) {
            _release();
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Unable to " + "resolve localhost (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (IOException e) {
            _release();
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "I/O failure" + " when establishing connection (cause: " + e.getLocalizedMessage() + ")", e);
        }
    }

    /**
     * Establishes a connection to RLS server with security.
     */
    private void _establishSecuredConnection(GlobusURL url, GSSCredential cred) throws RLSException {

        // Establish RLS connection
        try {
            // Parse port
            int port = url.getPort();
            if (port < 0)
                port = DEFAULT_PORT;

            // Parse host
            String host = url.getHost();
            if (LOCALHOST.equalsIgnoreCase(host))
                host = InetAddress.getLocalHost().getCanonicalHostName();

            // Instantiate authorization
            Authorization auth = HostAuthorization.getInstance();

            // Instantiate gss manager
            GSSManager manager = GlobusGSSManagerImpl.getInstance();

            // Establish context
            ExtendedGSSContext context = (ExtendedGSSContext) manager.createContext(null, GSSConstants.MECH_OID,
                    cred, GSSContext.DEFAULT_LIFETIME);
            context.requestCredDeleg(false);
            context.requestConf(true);
            context.requestAnonymity(false);
            context.setOption(GSSConstants.GSS_MODE, GSIConstants.MODE_GSI);

            // Connect to server
            sock = GssSocketFactory.getDefault().createSocket(host, port, context);
            GssSocket gssSock = (GssSocket) sock;
            gssSock.setWrapMode(GssSocket.SSL_MODE);
            gssSock.setAuthorization(auth);

            // Get secure input/output streams
            OutputStream secureOut = sock.getOutputStream();
            InputStream secureIn = sock.getInputStream();
            secureOut.flush(); // Not required to flush

            // Get unsecure "raw" input/output streams
            Socket rawSock = gssSock.getWrappedSocket();
            OutputStream rawOut = rawSock.getOutputStream();
            InputStream rawIn = rawSock.getInputStream();

            // Due to lack of security on the RLS stream the "raw" I/O streams
            // are used to created the RPC stream. When the RLS server is fixed
            // we will instead use the secure streams below.
            out = new RPCOutputStream(rawOut);
            in = new RPCInputStream(rawIn);

            // Ensure context is established
            if (!context.isEstablished()) {
                throw new RLSException(RLSStatusCode.RLS_GLOBUSERR, "Security context could not be established.");
            }
            logger.debug("Initiator : " + context.getSrcName());
            logger.debug("Acceptor  : " + context.getTargName());
            logger.debug("Lifetime  : " + context.getLifetime());
            logger.debug("Privacy   : " + context.getConfState());
            logger.debug("Anonymity : " + context.getAnonymityState());

            // Verify RLS server response
            int rc = in.readInt();
            if (rc != RLSStatusCode.RLS_SUCCESS) {
                logger.debug("Failed to establish RLS connection, RLS server" + " returned rc=" + rc);
                _release();
                throw new RLSException(rc, RLSStatusCode.toMessage(rc));
            }

            // Instantiate RLS interfaces
            catalog = new LRCImpl();
            index = new RLIImpl();
            admin = new AdminImpl();

            // Release context
            context.dispose();
        } catch (GSSException e) {
            _release();
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Failed to " + "establish security context (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (UnknownHostException e) {
            _release();
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Unable to " + "resolve localhost (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (IOException e) {
            _release();
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "I/O failure" + " when establishing connection (cause: " + e.getLocalizedMessage() + ")", e);
        }
    }

    /**
     * Frees system resources.
     * @throws IOException
     */
    private void _release() {
        if (in != null) {
            try {
                in.close();
            } catch (Exception e) {
            }
            in = null;
        }
        if (out != null) {
            try {
                out.close();
            } catch (Exception e) {
            }
            out = null;
        }
        if (sock != null) {
            try {
                sock.close();
            } catch (Exception e) {
            }
            sock = null;
        }
        catalog = null;
        index = null;
        admin = null;
    }

    /**
     * Set SoTimeout for underlying Socket connection, specified in 
     * <b>milliseconds</b>.
     * @param timeout SoTimeout in <b>milliseconds</b>.
     * @throws RLSException Wrapped SocketException. See {@link java.net.SocketException}.
     */
    public void setTimeout(int timeout) throws RLSException {
        try {
            sock.setSoTimeout(timeout);
        } catch (SocketException e) {
            throw new RLSIOException(e);
        }
    }

    /**
     * Get SoTimeout for underlying Socket connection, value returned in 
     * <b>milliseconds</b>.
     * @return SoTimeout in <b>milliseconds</b>.
     * @throws RLSException Wrapped SocketException. See {@link java.net.SocketException}.
     */
    public int getTimeout() throws RLSException {
        try {
            return sock.getSoTimeout();
        } catch (SocketException e) {
            throw new RLSIOException(e);
        }
    }

    public LocalReplicaCatalog catalog() {
        return this.catalog;
    }

    public ReplicaLocationIndex index() {
        return this.index;
    }

    public Administrative admin() {
        return this.admin;
    }

    public void close() throws org.globus.replica.rls.RLSException {
        _call(CLOSE, null, null, null, null, null, RPC_NORESPONSE);
        _release();
    }

    // ADMIN INTERFACE //
    class AdminImpl implements Administrative {

        // CONSTANTS //

        private static final String SET_CONFIGURATION = "set_configuration";
        private static final String GET_CONFIGURATION = "get_configuration";
        private static final String STATS = "stats";
        private static final String RLI_SENDER_LIST = "rli_sender_list";
        private static final String RLI_RLI_LIST = "rli_rli_list";
        private static final String LRC_RLI_LIST = "lrc_rli_list";
        private static final String LRC_RLI_INFO = "lrc_rli_info";
        private static final String LRC_RLI_GET_PART = "lrc_rli_get_part";
        private static final String LRC_RLI_DELETE = "lrc_rli_delete";
        private static final String LRC_RLI_ADD = "lrc_rli_add";
        private static final String ADMIN = "admin";
        private static final int ADMIN_PING = RLSClient.ADMIN_PING;
        private static final int ADMIN_QUIT = RLSClient.ADMIN_QUIT;
        private static final int ADMIN_UPDATE = RLSClient.ADMIN_SSU;

        public void addUpdate(RLIUpdate update) throws RLSException {
            // Validate rliurl
            String rliurl = update.getUrl();
            if (rliurl == null || rliurl.length() == 0) {
                throw new IllegalArgumentException("RLI url is null or empty (rliurl: " + rliurl + ")");
            }
            // Validate flags
            int flags = update.getFlags();
            if (flags != 0 && flags != RLSClient.RLIFLAG_BLOOMFILTER) {
                throw new IllegalArgumentException("Invalid argument (flags: " + flags + ")");
            }

            // Contruct input arguments
            List input = new ArrayList(3);
            input.add(rliurl);
            input.add(Integer.toString(flags));
            input.add(update.getPattern());

            // Call server
            _call(LRC_RLI_ADD, null, input, null, stringSerializer, null, RPC_NORESULT);
        }

        public void deleteUpdate(RLIUpdate update) throws RLSException {
            // Validate rliurl
            String rliurl = update.getUrl();
            if (rliurl == null || rliurl.length() == 0) {
                throw new IllegalArgumentException("RLI url is null or empty (rliurl: " + rliurl + ")");
            }

            // Contruct input arguments
            List input = new ArrayList(1);
            input.add(update);

            // Call server
            _call(LRC_RLI_DELETE, null, input, null, rliUpdateSerializer, null, RPC_NORESULT);
        }

        public RLSRLIInfo findUpdate(String rliurl) throws RLSException {
            // Construct input args
            List input = new ArrayList(1);
            input.add(rliurl);

            // Call server
            Results results = _call(LRC_RLI_INFO, null, input, null, stringSerializer, rliInfoDeserializer,
                    RPC_SINGLERESULT);
            return (RLSRLIInfo) results.getBatch().get(0);
        }

        public List getConfiguration(String option) throws RLSException {
            // Construct input args
            if (option == null)
                option = "";
            List input = new ArrayList(1);
            input.add(option);

            // Call server
            Results results = _call(GET_CONFIGURATION, null, input, null, stringSerializer,
                    configOptionDeserializer, RPC_MULTIPLERESULTS);
            return results.getBatch();
        }

        public List listRliToRliUpdates() throws RLSException {
            // Call server
            Results results = _call(RLI_RLI_LIST, null, null, null, null, rliInfoDeserializer, RPC_MULTIPLERESULTS);
            return results.getBatch();
        }

        public List listUpdatePartitions(RLIUpdate update) throws RLSException {
            // Contruct input arguments
            List input = new ArrayList(1);
            input.add(update);

            // Call server
            Results results = _call(LRC_RLI_GET_PART, null, input, null, rliUpdateSerializer, rliUpdateDeserializer,
                    RPC_MULTIPLERESULTS);
            return results.getBatch();
        }

        public List listUpdates() throws RLSException {
            // Call server
            Results results = _call(LRC_RLI_LIST, null, null, null, null, rliInfoDeserializer, RPC_MULTIPLERESULTS);
            return results.getBatch();
        }

        public void ping() throws RLSException {
            // Construct input
            List input = new ArrayList(1);
            input.add(Integer.toString(ADMIN_PING));

            // Call server
            _call(ADMIN, null, input, null, stringSerializer, null, RPC_NORESULT);
        }

        public void quit() throws RLSException {
            // Construct input
            List input = new ArrayList(1);
            input.add(Integer.toString(ADMIN_QUIT));

            // Call server
            _call(ADMIN, null, input, null, stringSerializer, null, RPC_NORESULT);
        }

        public void setConfiguration(String name, String value) throws RLSException {
            // Construct input args
            List input = new ArrayList(2);
            input.add(name);
            input.add(value);

            // Call server
            _call(SET_CONFIGURATION, null, input, null, stringSerializer, null, RPC_NORESULT);
        }

        public RLSStats stats() throws RLSException {
            // Call server
            Results results = _call(STATS, null, null, null, null, statsDeserializer, RPC_SINGLERESULT);
            return (RLSStats) results.getBatch().get(0);
        }

        public void update() throws RLSException {
            // Construct input
            List input = new ArrayList(1);
            input.add(Integer.toString(ADMIN_UPDATE));

            // Call server
            _call(ADMIN, null, input, null, stringSerializer, null, RPC_NORESULT);
        }

        public List updatedBy() throws RLSException {
            // Call server
            Results results = _call(RLI_SENDER_LIST, null, null, null, null, senderInfoDeserializer,
                    RPC_MULTIPLERESULTS);
            return results.getBatch();
        }
    } // END AdminImpl //

    // LRC INTERFACE //
    class LRCImpl implements LocalReplicaCatalog {

        // CONSTANTS //

        private static final String LRC_ATTR_GET = "lrc_attr_get";
        private static final String LRC_RENAMEPFN_BULK = "lrc_renamepfn_bulk";
        private static final String LRC_RENAMELFN_BULK = "lrc_renamelfn_bulk";
        private static final String LRC_ADD_BULK = "lrc_add_bulk";
        private static final String LRC_CREATE_BULK = "lrc_create_bulk";
        private static final String LRC_DELETE_BULK = "lrc_delete_bulk";
        private static final String LRC_EXISTS_BULK = "lrc_exists_bulk";
        private static final String LRC_ATTR_SEARCH = "lrc_attr_search";
        private static final String LRC_GET_PFN_BULK = "lrc_get_pfn_bulk";
        private static final String LRC_GET_LFN_BULK = "lrc_get_lfn_bulk";
        private static final String LRC_ATTR_VALUE_GET_BULK = "lrc_attr_value_get_bulk";
        private static final String LRC_ATTR_REMOVE_BULK = "lrc_attr_remove_bulk";
        private static final String LRC_ATTR_ADD_BULK = "lrc_attr_add_bulk";
        private static final String LRC_ATTR_MODIFY = "lrc_attr_modify";
        private static final String LRC_ATTR_DELETE = "lrc_attr_delete";
        private static final String LRC_ATTR_CREATE = "lrc_attr_create";
        private static final String LRC_MAPPING_EXISTS = "lrc_mapping_exists";
        private static final String LRC_GET_PFN_WC = "lrc_get_pfn_wc";
        private static final String LRC_GET_LFN_WC = "lrc_get_lfn_wc";
        private static final String LRC_GET_PFN = "lrc_get_pfn";
        private static final String LRC_GET_LFN = "lrc_get_lfn";

        public List createMappings(List mappings) throws RLSException {
            Results r = _callBulk(LRC_CREATE_BULK, null, mappings, mappingSerializer, mappingUpdateDeserializer);
            return r.getBatch();
        }

        public List deleteMappings(List mappings) throws RLSException {
            Results r = _callBulk(LRC_DELETE_BULK, null, mappings, mappingSerializer, mappingUpdateDeserializer);
            return r.getBatch();
        }

        public List addMappings(List mappings) throws RLSException {
            Results r = _callBulk(LRC_ADD_BULK, null, mappings, mappingSerializer, mappingUpdateDeserializer);
            return r.getBatch();
        }

        public List defineAttributes(List attributes) throws RLSException {
            // Iterate over the input parameters and make individual operation
            // calls, then collect results in order to 'bulkify' the call
            List results = new LinkedList();
            List input = new ArrayList(1);
            Iterator itr = attributes.iterator();
            while (itr.hasNext()) {
                RLSAttribute attr = (RLSAttribute) itr.next();
                input.clear();
                input.add(attr);
                int rc;
                try {
                    Results subr = _call(LRC_ATTR_CREATE, null, input, null, attrdefSerializer, null, RPC_NORESULT);
                    rc = subr.getRC();
                } catch (RLSException ex) {
                    rc = ex.GetRC();
                }
                // Only report failures
                if (rc != RLSStatusCode.RLS_SUCCESS)
                    results.add(new AttributeResult(rc, null, attr.name));
            }
            return results;
        }

        public List undefineAttributes(List attributes, boolean clearvalues) throws RLSException {
            // Iterate over the input parameters and make individual operation
            // calls, then collect results in order to 'bulkify' the call
            List results = new LinkedList();
            List suffix = new ArrayList(1);
            suffix.add((clearvalues) ? new Integer(1) : new Integer(0));
            List input = new ArrayList(1);
            Iterator itr = attributes.iterator();
            while (itr.hasNext()) {
                RLSAttribute attr = (RLSAttribute) itr.next();
                input.clear();
                input.add(attr);
                int rc;
                try {
                    Results subr = _call(LRC_ATTR_DELETE, null, input, suffix, minAttrSerializer, null,
                            RPC_NORESULT);
                    rc = subr.getRC();
                } catch (RLSException ex) {
                    rc = ex.GetRC();
                }
                // Only report failures
                if (rc != RLSStatusCode.RLS_SUCCESS)
                    results.add(new AttributeResult(rc, null, attr.name));
            }
            return results;
        }

        public List addAttributes(List attributes) throws RLSException {
            Results r = _callBulk(LRC_ATTR_ADD_BULK, null, attributes, attrobjSerializer,
                    attrObjResultDeserializer);
            return r.getBatch();
        }

        public List modifyAttributes(List attributes) throws RLSException {
            // Iterate over the input parameters and make individual operation
            // calls, then collect results in order to 'bulkify' the call
            List results = new LinkedList();
            List input = new ArrayList(1);
            Iterator itr = attributes.iterator();
            while (itr.hasNext()) {
                RLSAttributeObject ao = (RLSAttributeObject) itr.next();
                input.clear();
                input.add(ao);
                int rc;
                try {
                    Results subr = _call(LRC_ATTR_MODIFY, null, input, null, modifyAttrobjSerializer, null,
                            RPC_NORESULT);
                    rc = subr.getRC();
                } catch (RLSException ex) {
                    rc = ex.GetRC();
                }
                // Only report failures
                if (rc != RLSStatusCode.RLS_SUCCESS)
                    results.add(new AttributeResult(rc, null, ao.attr.name));
            }
            return results;
        }

        public List removeAttributes(List attributes) throws RLSException {
            Results r = _callBulk(LRC_ATTR_REMOVE_BULK, null, attributes, removeAttrobjSerializer,
                    attrObjResultDeserializer);
            return r.getBatch();
        }

        public List renameLogicalNames(List renames) throws RLSException {
            Results r = _callBulk(LRC_RENAMELFN_BULK, null, renames, renameSerializer, renameResultDeserializer);
            return r.getBatch();
        }

        public List renameTargetNames(List renames) throws RLSException {
            Results r = _callBulk(LRC_RENAMEPFN_BULK, null, renames, renameSerializer, renameResultDeserializer);
            return r.getBatch();
        }

        // LRC QUERY METHODS //

        public Results query(CatalogQuery query) throws RLSException {

            if (query instanceof CatalogExistenceQuery) {
                return _existenceQuery((CatalogExistenceQuery) query);
            } else if (query instanceof SimpleCatalogQuery) {
                return _simpleQuery((SimpleCatalogQuery) query);
            } else if (query instanceof AttributeSearch) {
                return _attributeSearch((AttributeSearch) query);
            } else if (query instanceof BatchCatalogQuery) {
                return _batchQuery((BatchCatalogQuery) query);
            } else {
                throw new IllegalArgumentException(
                        "Unsupported query object" + " (object: " + query.toString() + ")");
            }
        }

        /** Helper method for existence queries. */
        private Results _existenceQuery(CatalogExistenceQuery query) throws RLSException {

            // Determine existence query type: object or mapping
            if (CatalogExistenceQuery.objectExists.equals(query.getType())) {

                List prefix = new ArrayList(1);
                prefix.add(query.getObjectType());
                return _query(LRC_EXISTS_BULK, prefix, query.getBatch(), stringSerializer,
                        mappingExistsDeserializer);
            } else if (CatalogExistenceQuery.mappingExists.equals(query.getType())) {

                // RLS does not support bulk mapping exists, so we must make
                // multiple calls instead
                QueryResults results = new QueryResults(RLSStatusCode.RLS_SUCCESS, new LinkedList());
                List input = new ArrayList(1);
                Iterator itr = query.getBatch().iterator();
                while (itr.hasNext()) {
                    Mapping map = (Mapping) itr.next();
                    input.clear();
                    input.add(map);
                    int rc;
                    try {
                        Results subr = _call(LRC_MAPPING_EXISTS, null, input, null, mappingSerializer, null,
                                RPC_NORESULT);
                        rc = subr.getRC();
                    } catch (RLSException ex) {
                        rc = ex.GetRC();
                    }
                    results.getBatch().add(new MappingResult(rc, map));
                }
                return results;
            } else {
                throw new IllegalArgumentException(
                        "Unsupport existence query " + "type (type: " + query.getObjectType() + ")");
            }
        }

        /** Helper method for simple queries. */
        private Results _simpleQuery(SimpleCatalogQuery query) throws RLSException {

            // Determine method
            String method;
            Integer type = query.getType();
            if (SimpleCatalogQuery.queryMappingsByLogicalNamePattern.equals(type)) {
                method = LRC_GET_PFN_WC;
            } else if (SimpleCatalogQuery.queryMappingsByTargetNamePattern.equals(type)) {
                method = LRC_GET_LFN_WC;
            } else if (SimpleCatalogQuery.queryMappingsByLogicalName.equals(type)) {
                method = LRC_GET_PFN;
            } else if (SimpleCatalogQuery.queryMappingsByTargetName.equals(type)) {
                method = LRC_GET_LFN;
            } else if (SimpleCatalogQuery.queryAttributeDefinitions.equals(type)) {
                method = LRC_ATTR_GET;
            } else {
                throw new IllegalArgumentException("Unsupported query type" + " (type: " + type + ")");
            }

            // Determine serializer/deserializer
            RPCObjectSerializer ser;
            RPCObjectDeserializer deser;
            if (query.getParam() instanceof RLSAttribute) {
                ser = minAttrSerializer;
                deser = new AttrDeserializer(((RLSAttribute) query.getParam()).GetObjType());
            } else {
                ser = stringSerializer;
                deser = simpleMappingResultDeserializer;
            }

            // Create input params
            List input = new ArrayList(1);
            input.add(query.getParam());

            // Call server
            return _query(method, null, input, query.getOffsetLimit(), ser, deser);
        }

        /** Helper method for attribute search operation */
        private Results _attributeSearch(AttributeSearch query) throws RLSException {

            // Construct input args
            List input = new ArrayList(1);
            input.add(query);

            // Initialize deserializer
            AttrSearchResultDeserializer deser = new AttrSearchResultDeserializer(query.getName(),
                    query.getObjtype());

            return _query(LRC_ATTR_SEARCH, null, input, query.getOffsetLimit(), attributeSearchSerializer, deser);
        }

        /** Helper method for batch queries. */
        private Results _batchQuery(BatchCatalogQuery query) throws RLSException {

            // Check query type
            Integer queryType = query.getType();
            if (BatchCatalogQuery.attributeQuery.equals(queryType)) {

                // Create prefix from attr name and obj type
                List prefix = new ArrayList(2);
                prefix.add(query.getAttributeName());
                prefix.add(query.getAttributeObjectType().toString());

                // Keys
                List keys = query.getBatch();

                // Initialize deserializer
                AttrValGetResultDeserializer deser = new AttrValGetResultDeserializer(
                        query.getAttributeObjectType().intValue());

                // Attribute (value) query
                return _callBulk(LRC_ATTR_VALUE_GET_BULK, prefix, keys, stringSerializer, deser);

            } else if (BatchCatalogQuery.mappingQueryByLogicalNames.equals(queryType)) {

                // Logical name query
                return _query(LRC_GET_PFN_BULK, null, query.getBatch(), stringSerializer,
                        mappingResultDeserializer);

            } else if (BatchCatalogQuery.mappingQueryByTargetNames.equals(queryType)) {

                // Target name query
                return _query(LRC_GET_LFN_BULK, null, query.getBatch(), stringSerializer,
                        mappingResultDeserializer);
            } else {
                throw new IllegalArgumentException("Unsupported query type" + " (type: " + queryType + ")");
            }
        }
    } // END LRCImpl

    // RLI INTERFACE //
    class RLIImpl implements ReplicaLocationIndex {

        private static final String RLI_MAPPING_EXISTS = "rli_mapping_exists";
        private static final String RLI_EXISTS_BULK = "rli_exists_bulk";
        private static final String RLI_GET_LRC_BULK = "rli_get_lrc_bulk";
        private static final String RLI_GET_LRC_WC = "rli_get_lrc_wc";
        private static final String RLI_GET_LRC = "rli_get_lrc";

        public Results query(IndexQuery query) throws RLSException {
            if (query instanceof SimpleIndexQuery)
                return _simpleIndexQuery((SimpleIndexQuery) query);
            else if (query instanceof BatchIndexQuery)
                return _batchIndexQuery((BatchIndexQuery) query);
            else if (query instanceof IndexExistenceQuery)
                return _existenceQuery((IndexExistenceQuery) query);
            else
                throw new IllegalArgumentException("Unsupported query object");
        }

        private Results _simpleIndexQuery(SimpleIndexQuery query) throws RLSException {
            // Determine method
            String method;
            if (SimpleIndexQuery.queryMappingsByLogicalName.equals(query.getType())) {
                method = RLI_GET_LRC;
            } else if (SimpleIndexQuery.queryMappingsByLogicalNamePattern.equals(query.getType())) {
                method = RLI_GET_LRC_WC;
            } else {
                throw new IllegalArgumentException("Invalid query type");
            }

            // Construct params
            List input = new ArrayList(1);
            input.add(query.getParam());

            // Call server
            return _query(method, null, input, query.getOffsetLimit(), stringSerializer,
                    simpleIndexMappingResultDeserializer);
        }

        private Results _batchIndexQuery(BatchIndexQuery query) throws RLSException {
            // Determine method
            String method;
            if (BatchIndexQuery.queryMappingsByLogicalNames.equals(query.getType())) {
                method = RLI_GET_LRC_BULK;
            } else {
                throw new IllegalArgumentException("Invalid query type");
            }

            // Call server
            return _query(method, null, query.getBatch(), stringSerializer, indexMappingResultDeserializer);
        }

        private Results _existenceQuery(IndexExistenceQuery query) throws RLSException {

            if (IndexExistenceQuery.objectExists.equals(query.getType())) {

                // Make prefix
                List prefix = new ArrayList(1);
                prefix.add(query.getObjectType().toString());

                return _query(RLI_EXISTS_BULK, prefix, query.getBatch(), stringSerializer,
                        indexMappingExistsDeserializer);

            } else if (IndexExistenceQuery.mappingExists.equals(query.getType())) {

                throw new RLSException(RLSStatusCode.RLS_UNSUPPORTED,
                        "This method is unsupported. If this method is "
                                + "critical to your usage of RLS, please notify the "
                                + "RLS developers at rls-dev@globus.org.");
                /*
                 * NOTE: Using the "rli_mapping_exists" RPC method on a call to
                 * the RLS server causes an I/O mismatch that results in the
                 * client not returning from the query. The method itself works
                 * with the RLS C client. In order to fix this method, we need
                 * to evaluate the RPC protocol more closely. For now, we have
                 * avoided this task. If you are a user that needs this method,
                 * please inform the RLS developers.  
                 *
                // RLS does not support bulk mapping exists, so we must make
                // multiple calls instead
                QueryResults results = new QueryResults(
                      RLSResultCode.RLS_SUCCESS,
                      new LinkedList());
                List prefix = null;
                List input = new ArrayList(1);
                    
                Iterator itr = query.getBatch().iterator(); 
                while(itr.hasNext()) {
                   IndexMapping map = (IndexMapping) itr.next();
                   input.clear();
                   input.add(map);
                   int rc;
                   try {
                      Results subr = _call(RLI_MAPPING_EXISTS, prefix, input, null,
                       indexMappingSerializer, null, RPC_NORESULT);
                      rc = subr.getRC();
                   }
                   catch (RLSException ex) {
                      rc = ex.GetRC();
                   }
                   results.getBatch().add(new IndexMappingResult(rc, map));
                }
                return results;
                 *
                 */
            } else {
                throw new IllegalArgumentException("Unsupported query object");
            }
        }
    } // RLIImpl

    // RPC PROXY METHODS //

    /**
     * Query interface that supports bulk operations.
     * @param method
     * @param prefix
     * @param input
     * @param serializer
     * @param deserializer
     * @return
     * @throws RLSException
     */
    private Results _query(String method, List prefix, List input, RPCObjectSerializer serializer,
            RPCObjectDeserializer deserializer) throws RLSException {
        if (input == null)
            throw new NullPointerException("Input list is null");
        if (input.isEmpty())
            throw new IllegalArgumentException("Input list is empty");
        if (input.get(0) == null) {
            throw new IllegalArgumentException("Input list contains" + " null object");
        }
        if (!serializer.isValidInstance(input.get(0))) {
            throw new IllegalArgumentException("Input list contains" + " object of type "
                    + input.get(0).getClass().getName() + " which is not supported by this operation");
        }

        int rc;
        List results;
        try {
            // Check input stream
            _checkInputStream(in);

            out.writeString(method);

            if (prefix != null) {
                for (Iterator i = prefix.iterator(); i.hasNext();) {
                    out.writeString(i.next().toString());
                }
            }

            for (Iterator i = input.iterator(); i.hasNext();) {
                serializer.serialize(out, i.next());
            }
            out.writeTerminator();

            // Read global result code
            rc = in.readInt();
            if (rc != RLSStatusCode.RLS_SUCCESS) {
                throw new RLSException(rc, in.readString());
            }

            // Read results
            results = new LinkedList();
            for (Object obj = deserializer.deserialize(in); obj != null; obj = deserializer.deserialize(in)) {
                results.add(obj);
            }

        } catch (IOException e) {
            throw new RLSIOException(RLSStatusCode.RLS_GLOBUSERR,
                    "I/O failure (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (RPCDeserializationException e) {
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Deserialization failure (cause: " + e.getLocalizedMessage() + ")", e);
        }

        return new QueryResults(rc, results);
    }

    /**
     * This query interface supports non-bulk methods.
     * @param method
     * @param prefix
     * @param input
     * @param offsetLimit
     * @param serializer
     * @param deserializer
     * @return
     * @throws RLSException
     */
    private Results _query(String method, List prefix, List input, RLSOffsetLimit offsetLimit,
            RPCObjectSerializer serializer, RPCObjectDeserializer deserializer) throws RLSException {
        if (input == null)
            throw new NullPointerException("Input list is null");
        if (input.isEmpty())
            throw new IllegalArgumentException("Input list is empty");
        if (input.get(0) == null) {
            throw new IllegalArgumentException("Input list contains" + " null object");
        }
        if (!serializer.isValidInstance(input.get(0))) {
            throw new IllegalArgumentException("Input list contains" + " object of type "
                    + input.get(0).getClass().getName() + " which is not supported by this operation");
        }

        List results;
        if (offsetLimit == null || offsetLimit.reslimit <= 0) {
            offsetLimit = new RLSOffsetLimit(0, 0);
            // Single query when offsetLimit not specified or reslimit == 0
            results = _subquery(method, prefix, input, offsetLimit, serializer, deserializer);
        } else {
            // Get first set of subquery results
            results = new LinkedList();
            List subqresults = _subquery(method, prefix, input, offsetLimit, serializer, deserializer);

            // Execute subqueries until no more results available
            while (!subqresults.isEmpty()) {
                results.addAll(subqresults);
                subqresults = _subquery(method, prefix, input, offsetLimit, serializer, deserializer);
            }
        }

        return new QueryResults(RLSStatusCode.RLS_SUCCESS, results);
    }

    /**
     * Processes the subqueries of queries with offset and result limit
     * specified.
     * 
     * @param method The method name.
     * @param prefix The prefix. MAY BE null.
     * @param input The input list.
     * @param offsetLimit MUST NOT be null.
     * @param serializer The input serializer.
     * @param deserializer The output deserializer.
     * @return The result list. NEVER null. MAY BE empty.
     * @throws RLSException The results code returned from the RLS server
     *          indicated a failure.
     */
    private List _subquery(String method, List prefix, List input, RLSOffsetLimit offsetLimit,
            RPCObjectSerializer serializer, RPCObjectDeserializer deserializer) throws RLSException {

        int rc;
        List results;
        try {
            // Check input stream
            _checkInputStream(in);

            out.writeString(method);

            if (prefix != null) {
                for (Iterator i = prefix.iterator(); i.hasNext();) {
                    out.writeString(i.next().toString());
                }
            }

            for (Iterator i = input.iterator(); i.hasNext();) {
                serializer.serialize(out, i.next());
            }

            // offsetLimit expected in subqueries
            out.writeInt(offsetLimit.offset);
            out.writeInt(offsetLimit.reslimit);

            // Read global result code
            rc = in.readInt();
            if (rc != RLSStatusCode.RLS_SUCCESS) {
                throw new RLSException(rc, in.readString());
            }

            // Read results
            results = new LinkedList();
            for (Object obj = deserializer.deserialize(in); obj != null; obj = deserializer.deserialize(in)) {

                if (obj instanceof ResultsContinuationMarker)
                    break;

                results.add(obj);
                offsetLimit.offset++;
            }

        } catch (IOException e) {
            throw new RLSIOException(RLSStatusCode.RLS_GLOBUSERR,
                    "I/O failure (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (RPCDeserializationException e) {
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Deserialization failure (cause: " + e.getLocalizedMessage() + ")", e);
        }

        return results;
    }

    /**
     * Remote call for non-bulk operations.
     * @param method
     * @param prefix
     * @param input
     * @param suffix
     * @param serializer
     * @param deserializer
     * @return
     * @throws RLSException
     */
    private Results _call(String method, List prefix, List input, List suffix, RPCObjectSerializer serializer,
            RPCObjectDeserializer deserializer, int options) throws RLSException {

        // Input not accepted when serializer is null
        if (serializer == null && input != null) {
            throw new IllegalArgumentException("Input must be null when " + "serializer is null");
        } else if (serializer != null) {
            if (input == null)
                throw new NullPointerException("Input list is null");
            if (input.isEmpty())
                throw new IllegalArgumentException("Input list is empty");
            if (input.get(0) == null) {
                throw new IllegalArgumentException("Input list contains" + " null object");
            }
            if (!serializer.isValidInstance(input.get(0))) {
                throw new IllegalArgumentException("Input list contains" + " object of type "
                        + input.get(0).getClass().getName() + " which is not supported by this operation");
            }
        }

        int rc;
        List results = null;
        try {
            // Check input stream
            _checkInputStream(in);

            out.writeString(method);

            if (prefix != null) {
                for (Iterator i = prefix.iterator(); i.hasNext();) {
                    out.writeString(i.next().toString());
                }
            }

            if (input != null) {
                for (Iterator i = input.iterator(); i.hasNext();) {
                    serializer.serialize(out, i.next());
                }
            }

            if (suffix != null) {
                for (Iterator i = suffix.iterator(); i.hasNext();) {
                    out.writeString(i.next().toString());
                }
            }

            // Check response, if necessary
            rc = RLSStatusCode.RLS_SUCCESS;
            if ((options & RPC_NORESPONSE) == 0) {
                // Read global result code
                rc = in.readInt();
                if (rc != RLSStatusCode.RLS_SUCCESS) {
                    throw new RLSException(rc, in.readString());
                }

                // Read results, if necessary
                results = new LinkedList();
                if ((options & RPC_SINGLERESULT) > 0) {
                    results.add(deserializer.deserialize(in));
                } else if ((options & RPC_MULTIPLERESULTS) > 0) {
                    for (Object obj = deserializer.deserialize(in); obj != null; obj = deserializer
                            .deserialize(in)) {
                        results.add(obj);
                    }
                }
            }

        } catch (IOException e) {
            throw new RLSIOException(RLSStatusCode.RLS_GLOBUSERR,
                    "I/O failure (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (RPCDeserializationException e) {
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Deserialization failure (cause: " + e.getLocalizedMessage() + ")", e);
        }

        return new QueryResults(rc, results);
    }

    /**
     * Remote call for bulk operations.
     * @param method
     * @param prefix
     * @param input
     * @param serializer
     * @param deserializer
     * @return
     * @throws RLSException
     */
    private Results _callBulk(String method, List prefix, List input, RPCObjectSerializer serializer,
            RPCObjectDeserializer deserializer) throws RLSException {
        if (input == null)
            throw new NullPointerException("Input list is null");
        if (input.isEmpty())
            throw new IllegalArgumentException("Input list is empty");
        if (input.get(0) == null) {
            throw new IllegalArgumentException("Input list contains" + " null object");
        }
        if (!serializer.isValidInstance(input.get(0))) {
            throw new IllegalArgumentException("Input list contains" + " object of type "
                    + input.get(0).getClass().getName() + " which is not supported by this operation");
        }

        int rc;
        List results;
        try {
            // Check input stream
            _checkInputStream(in);

            out.writeString(method);

            if (prefix != null) {
                for (Iterator i = prefix.iterator(); i.hasNext();) {
                    out.writeString(i.next().toString());
                }
            }

            for (Iterator i = input.iterator(); i.hasNext();) {
                serializer.serialize(out, i.next());
            }
            out.writeTerminator();

            // Read global result code
            rc = in.readInt();
            if (rc != RLSStatusCode.RLS_SUCCESS) {
                throw new RLSException(rc, in.readString());
            }

            // Read results
            results = new LinkedList();
            for (Object obj = deserializer.deserialize(in); obj != null; obj = deserializer.deserialize(in)) {
                results.add(obj);
            }

        } catch (IOException e) {
            throw new RLSIOException(RLSStatusCode.RLS_GLOBUSERR,
                    "I/O failure (cause: " + e.getLocalizedMessage() + ")", e);
        } catch (RPCDeserializationException e) {
            throw new RLSException(RLSStatusCode.RLS_GLOBUSERR,
                    "Deserialization failure (cause: " + e.getLocalizedMessage() + ")", e);
        }

        return new QueryResults(rc, results);
    }

    /** Check input steam to see if bytes are (unexpectedly) waiting to be read.
     */
    private void _checkInputStream(InputStream in) {
        try {
            while (in.available() > 0) {
                logger.warn(
                        "Unexpected bytes are waiting on input steam. " + "Skipping " + in.available() + " bytes.");
                in.skip(in.available());
            }
        } catch (Exception e) {
            logger.warn("Exception when checking for available bytes on the " + "input stream: "
                    + e.getLocalizedMessage());
        }
    }

    // SERIALIZERS //

    /** Strings */
    class StringSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            out.writeString((String) obj);
        }

        public boolean isValidInstance(Object obj) {
            return (obj == null || obj instanceof java.lang.String);
        }
    }

    StringSerializer stringSerializer = new StringSerializer();

    /** Mappings */
    class MappingSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            Mapping m = (Mapping) obj;
            out.writeString(m.getLogical());
            out.writeString(m.getTarget());
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof Mapping);
        }
    }

    MappingSerializer mappingSerializer = new MappingSerializer();

    /** Index Mappings */
    class IndexMappingSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            IndexMapping m = (IndexMapping) obj;
            out.writeString(m.getLogical());
            out.writeString(m.getCatalog());
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof IndexMapping);
        }
    }

    IndexMappingSerializer indexMappingSerializer = new IndexMappingSerializer();

    /** Minimal attribute serializer */
    class MinimalAttributeSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            RLSAttribute a = (RLSAttribute) obj;
            out.writeString(a.name);
            out.writeInt(a.GetObjType());
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof RLSAttribute);
        }
    }

    MinimalAttributeSerializer minAttrSerializer = new MinimalAttributeSerializer();

    /** Define attribute */
    class AttributeDefineSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            RLSAttribute a = (RLSAttribute) obj;
            out.writeString(a.name);
            out.writeInt(a.GetObjType());
            out.writeInt(a.GetValType());
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof RLSAttribute);
        }
    }

    AttributeDefineSerializer attrdefSerializer = new AttributeDefineSerializer();

    /** Writes complete attribute object */
    class AttributeObjectSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            RLSAttributeObject ao = (RLSAttributeObject) obj;
            out.writeString(ao.key);
            out.writeInt(ao.attr.GetObjType());
            out.writeInt(ao.attr.GetValType());
            out.writeString(ao.attr.name);
            serializeAttributeValue(out, ao.attr);
        }

        protected void serializeAttributeValue(RPCOutputStream out, RLSAttribute attr) throws IOException {
            int valtype = attr.GetValType();
            if (valtype == RLSAttribute.STR)
                out.writeString(attr.GetStrVal());
            else if (valtype == RLSAttribute.INT)
                out.writeInt(attr.GetIntVal());
            else if (valtype == RLSAttribute.DOUBLE)
                out.writeDouble(attr.GetDoubleVal());
            else if (valtype == RLSAttribute.DATE) {
                out.writeDate(attr.GetDateVal());
            } else
                throw new IllegalArgumentException("Unknown attribute value type");
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof RLSAttributeObject);
        }
    }

    AttributeObjectSerializer attrobjSerializer = new AttributeObjectSerializer();

    /** Removing an attribute involves fewer fields than adding. */
    class RemoveAttributeObjectSerializer extends AttributeObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            RLSAttributeObject ao = (RLSAttributeObject) obj;
            out.writeString(ao.key);
            out.writeInt(ao.attr.GetObjType());
            out.writeString(ao.attr.name);
        }
    }

    RemoveAttributeObjectSerializer removeAttrobjSerializer = new RemoveAttributeObjectSerializer();

    /** Modifying an attribute involves different field ordering than adding/removing */
    class ModifyAttributeObjectSerializer extends AttributeObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            RLSAttributeObject ao = (RLSAttributeObject) obj;
            out.writeString(ao.key);
            out.writeString(ao.attr.name);
            out.writeInt(ao.attr.GetObjType());
            out.writeInt(ao.attr.GetValType());
            serializeAttributeValue(out, ao.attr);
        }
    }

    ModifyAttributeObjectSerializer modifyAttrobjSerializer = new ModifyAttributeObjectSerializer();

    /** AttributeSearch requires its own serializer. */
    class AttributeSearchSerializer extends AttributeObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            AttributeSearch as = (AttributeSearch) obj;
            out.writeString(as.getName());
            out.writeInt(as.getObjtype());
            out.writeInt(as.getOp());
            // Operand 1
            if (as.getOp1() != null)
                serializeAttributeValue(out, as.getOp1());
            else
                out.writeString("");
            // Operand 2
            if (as.getOp2() != null)
                serializeAttributeValue(out, as.getOp2());
            else
                out.writeString("");
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof AttributeSearch);
        }
    }

    AttributeSearchSerializer attributeSearchSerializer = new AttributeSearchSerializer();

    /** Renames */
    class RenameSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            Rename r = (Rename) obj;
            out.writeString(r.getFrom());
            out.writeString(r.getTo());
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof Rename);
        }
    }

    RenameSerializer renameSerializer = new RenameSerializer();

    /** RLIUpdate */
    class RLIUpdateSerializer implements RPCObjectSerializer {

        public void serialize(RPCOutputStream out, Object obj) throws IOException {
            RLIUpdate r = (RLIUpdate) obj;
            out.writeString(r.getUrl());
            out.writeString(r.getPattern());
        }

        public boolean isValidInstance(Object obj) {
            return (obj instanceof RLIUpdate);
        }
    }

    RLIUpdateSerializer rliUpdateSerializer = new RLIUpdateSerializer();

    // DESERIALIZERS //

    /** Reads string from non-bulk call (no bulk rc). */
    class StringResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            return in.readString();
        }
    }

    StringResultDeserializer stringDeserializer = new StringResultDeserializer();

    /** Reads bulk query mapping results: rc, logical/target */
    class MappingResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            if (rc == RLSStatusCode.RLS_SUCCESS)
                return new MappingResult(rc, in.readString(), in.readString());
            if (rc == RLSStatusCode.RLS_LFN_NEXIST)
                return new MappingResult(rc, in.readString(), null);
            if (rc == RLSStatusCode.RLS_PFN_NEXIST)
                return new MappingResult(rc, null, in.readString());
            throw new RPCDeserializationException(
                    "Unexpected result code " + "value returned from stream (rc: " + rc + ")");
        }
    }

    MappingResultDeserializer mappingResultDeserializer = new MappingResultDeserializer();

    /** Reads bulk update mapping results: rc, logical/target */
    class MappingUpdateDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            return new MappingResult(rc, in.readString(), in.readString());
        }
    }

    MappingUpdateDeserializer mappingUpdateDeserializer = new MappingUpdateDeserializer();

    /** Reads bulk exist mapping results: rc, logical/target */
    class MappingExistsDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            if (rc == RLSStatusCode.RLS_SUCCESS)
                return new MappingResult(rc, in.readString(), in.readString());
            if (rc == RLSStatusCode.RLS_LFN_NEXIST)
                return new MappingResult(rc, in.readString(), null);
            if (rc == RLSStatusCode.RLS_PFN_NEXIST)
                return new MappingResult(rc, null, in.readString());
            throw new RPCDeserializationException(
                    "Unexpected result code " + "value returned from stream (rc: " + rc + ")");
        }
    }

    MappingExistsDeserializer mappingExistsDeserializer = new MappingExistsDeserializer();

    /** Reads logical-target name pairs. */
    class SimpleMappingResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            String logical, target;

            // Read logical
            logical = in.readString();
            if (logical == null)
                return null;

            // Check for continuation marker
            byte[] b = logical.getBytes();
            if (b[0] == 1 && b.length == 1)
                return new ResultsContinuationMarker();

            // Read target
            target = in.readString();
            if (target == null)
                throw new RPCDeserializationException(
                        "Expected logical-target string pair but only found a " + "single string");
            return new MappingResult(RLSStatusCode.RLS_SUCCESS, logical, target);
        }
    }

    SimpleMappingResultDeserializer simpleMappingResultDeserializer = new SimpleMappingResultDeserializer();

    /** Reads bulk index mapping results: rc, logical/catalog */
    class IndexMappingResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            if (rc == RLSStatusCode.RLS_SUCCESS)
                return new IndexMappingResult(rc, in.readString(), in.readString());
            if (rc == RLSStatusCode.RLS_LFN_NEXIST)
                return new IndexMappingResult(rc, in.readString(), null);
            if (rc == RLSStatusCode.RLS_LRC_NEXIST)
                return new IndexMappingResult(rc, null, in.readString());
            throw new RPCDeserializationException(
                    "Unexpected result code " + "value returned from stream (rc: " + rc + ")");
        }
    }

    IndexMappingResultDeserializer indexMappingResultDeserializer = new IndexMappingResultDeserializer();

    /** Reads bulk index mapping results: rc, logical/catalog */
    class IndexMappingExistsDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            if (rc == RLSStatusCode.RLS_SUCCESS)
                return new IndexMappingResult(rc, in.readString(), null);
            if (rc == RLSStatusCode.RLS_LFN_NEXIST)
                return new IndexMappingResult(rc, in.readString(), null);
            if (rc == RLSStatusCode.RLS_LRC_NEXIST)
                return new IndexMappingResult(rc, null, in.readString());
            throw new RPCDeserializationException(
                    "Unexpected result code " + "value returned from stream (rc: " + rc + ")");
        }
    }

    IndexMappingExistsDeserializer indexMappingExistsDeserializer = new IndexMappingExistsDeserializer();

    /** Reads logical-catalog string pairs. */
    class SimpleIndexMappingResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            String logical, catalog;

            // Read logical
            logical = in.readString();
            if (logical == null)
                return null;

            // Check for continuation marker
            byte[] b = logical.getBytes();
            if (b[0] == 1 && b.length == 1)
                return new ResultsContinuationMarker();

            // Read catalog
            catalog = in.readString();
            if (catalog == null)
                throw new RPCDeserializationException(
                        "Expected logical-catalog string pair but only found a " + "single string");
            return new IndexMappingResult(RLSStatusCode.RLS_SUCCESS, logical, catalog);
        }
    }

    SimpleIndexMappingResultDeserializer simpleIndexMappingResultDeserializer = new SimpleIndexMappingResultDeserializer();

    /** Reads bulk rename results: rc, from/to */
    class RenameResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            return new RenameResult(rc, in.readString(), in.readString());
        }
    }

    RenameResultDeserializer renameResultDeserializer = new RenameResultDeserializer();

    /** Reads logical-target name pairs. */
    class RLIUpdateDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            String url, pattern;

            // Read url
            url = in.readString();
            if (url == null)
                return null;

            // Read pattern. "Null" is valid value for pattern.
            pattern = in.readString();

            return new RLIUpdate(url, pattern);
        }
    }

    RLIUpdateDeserializer rliUpdateDeserializer = new RLIUpdateDeserializer();

    /** Reads RLI Info. */
    class RLIInfoDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            // Read url
            String url = in.readString();
            if (url == null)
                return null;

            // Read updateinterval
            Integer updateinterval = in.readInteger();
            if (updateinterval == null)
                throw new RPCDeserializationException("updateinterval is null");

            // Read flags
            Integer flags = in.readInteger();
            if (flags == null)
                throw new RPCDeserializationException("flags is null");

            // Read lastupdate
            Integer lastupdate = in.readInteger();
            if (lastupdate == null)
                throw new RPCDeserializationException("lastupdate is null");

            return new RLSRLIInfo(url, updateinterval.intValue(), flags.intValue(), lastupdate.intValue());
        }
    }

    RLIInfoDeserializer rliInfoDeserializer = new RLIInfoDeserializer();

    /** Reads RLI Info. */
    class SenderInfoDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            // Read url
            String url = in.readString();
            if (url == null)
                return null;

            // Read lastupdate
            Integer lastupdate = in.readInteger();
            if (lastupdate == null)
                throw new RPCDeserializationException("lastupdate is null");

            return new RLSLRCInfo(url, lastupdate.intValue());
        }
    }

    SenderInfoDeserializer senderInfoDeserializer = new SenderInfoDeserializer();

    /** Reads RLS Stats. */
    class StatsDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            // Read url
            String ver = in.readString();
            if (ver == null)
                return null;

            // Read uptime
            Integer uptime = in.readInteger();
            if (uptime == null)
                throw new RPCDeserializationException("uptime is null");

            // Read flags
            Integer flags = in.readInteger();
            if (flags == null)
                throw new RPCDeserializationException("flags is null");

            // Read bloomfilterui
            Integer bloomfilterui = in.readInteger();
            if (bloomfilterui == null)
                throw new RPCDeserializationException("bloomfilterui is null");

            // Read lfnlistui
            Integer lfnlistui = in.readInteger();
            if (lfnlistui == null)
                throw new RPCDeserializationException("lfnlistui is null");

            // Read numlfn
            Integer numlfn = in.readInteger();
            if (numlfn == null)
                throw new RPCDeserializationException("numlfn is null");

            // Read numpfn
            Integer numpfn = in.readInteger();
            if (numpfn == null)
                throw new RPCDeserializationException("numpfn is null");

            // Read nummap
            Integer nummap = in.readInteger();
            if (nummap == null)
                throw new RPCDeserializationException("nummap is null");

            // Read rli_numlfn
            Integer rli_numlfn = in.readInteger();
            if (rli_numlfn == null)
                throw new RPCDeserializationException("rli_numlfn is null");

            // Read rli_numlrc
            Integer rli_numlrc = in.readInteger();
            if (rli_numlrc == null)
                throw new RPCDeserializationException("rli_numlrc is null");

            // Read rli_numsender
            Integer rli_numsender = in.readInteger();
            if (rli_numsender == null)
                throw new RPCDeserializationException("rli_numsender is null");

            // Read rli_nummap
            Integer rli_nummap = in.readInteger();
            if (rli_nummap == null)
                throw new RPCDeserializationException("rli_nummap is null");

            return new RLSStats(ver, uptime.intValue(), flags.intValue(), bloomfilterui.intValue(),
                    lfnlistui.intValue(), numlfn.intValue(), numpfn.intValue(), nummap.intValue(),
                    rli_numlfn.intValue(), rli_numlrc.intValue(), rli_numsender.intValue(), rli_nummap.intValue());
        }
    }

    StatsDeserializer statsDeserializer = new StatsDeserializer();

    /** Reads Configuration Options. */
    class ConfigOptionDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            // Read name
            String name = in.readString();
            if (name == null)
                return null;

            // Read value
            String value = in.readString();
            if (value == null)
                throw new RPCDeserializationException("value is null");

            return new ConfigurationOption(name, value);
        }
    }

    ConfigOptionDeserializer configOptionDeserializer = new ConfigOptionDeserializer();

    /** Reads object name (i.e., logical or target name) and the attribute name. */
    class AttrObjResultDeserializer implements RPCObjectDeserializer {

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            if (rc != RLSStatusCode.RLS_SUCCESS)
                return new AttributeResult(rc, in.readString(), in.readString());
            else
                return null;
        }
    }

    AttrObjResultDeserializer attrObjResultDeserializer = new AttrObjResultDeserializer();

    /** Deseializes attribute value. */
    abstract class AttrValDeserializer implements RPCObjectDeserializer {

        public RLSAttribute deserializeAttribute(RPCInputStream in, String name, int objtype, int valtype)
                throws IOException, RPCDeserializationException {
            RLSAttribute attr;
            switch (valtype) {
            case RLSAttribute.STR:
                attr = new RLSAttribute(name, objtype, in.readString());
                break;
            case RLSAttribute.INT:
                attr = new RLSAttribute(name, objtype, valtype, in.readInt());
                break;
            case RLSAttribute.DOUBLE:
                attr = new RLSAttribute(name, objtype, in.readDouble());
                break;
            case RLSAttribute.DATE:
                attr = new RLSAttribute(name, objtype, in.readDate());
                break;
            default:
                throw new RPCDeserializationException("Invalid attribute value type: " + valtype);
            }
            return attr;
        }
    }

    /** Reads object name (i.e., logical or target name) and the attribute name. */
    class AttrValGetResultDeserializer extends AttrValDeserializer {

        private int objtype;

        AttrValGetResultDeserializer(int objtype) {
            this.objtype = objtype;
        }

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            Integer RC = in.readInteger();
            if (RC == null)
                return null;
            int rc = RC.intValue();
            if (rc != RLSStatusCode.RLS_SUCCESS) {
                String key = in.readString();
                String name = in.readString();
                return new RLSAttributeObject(rc, new RLSAttribute(name, objtype, -1), key);
            } else {
                String key = in.readString();
                String name = in.readString();
                int valtype = in.readInt();
                RLSAttribute attr = deserializeAttribute(in, name, -1, valtype);
                return new RLSAttributeObject(rc, attr, key);
            }
        }
    }

    /** Reads attribute search results. */
    class AttrSearchResultDeserializer extends AttrValDeserializer {

        private String name;
        private int objtype;

        AttrSearchResultDeserializer(String name, int objtype) {
            this.name = name;
            this.objtype = objtype;
        }

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            String key = in.readString();
            if (key == null)
                return null;
            int valtype = in.readInt();
            RLSAttribute attr = deserializeAttribute(in, name, objtype, valtype);
            return new RLSAttributeObject(attr, key);
        }
    }

    /** Reads attribute definitions. */
    class AttrDeserializer implements RPCObjectDeserializer {

        private int objtype;

        AttrDeserializer(int objtype) {
            this.objtype = objtype;
        }

        public Object deserialize(RPCInputStream in) throws IOException, RPCDeserializationException {
            String name = in.readString();
            if (name == null)
                return null;
            int valtype = in.readInt();
            return new RLSAttribute(name, objtype, valtype);
        }
    }

    // OTHER //

    /** A class used to indicate that the results continuation marker has been
     * reached when reading results from a query using the offset/reslimit
     * options.
     */
    class ResultsContinuationMarker {
    }
}