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

com.datastax.driver.core.Responses Maven / Gradle / Ivy

Go to download

A driver for Apache Cassandra 1.2+ that works exclusively with the Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol.

The newest version!
/*
 *      Copyright (C) 2012 DataStax Inc.
 *
 *   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 com.datastax.driver.core;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

import org.jboss.netty.buffer.ChannelBuffer;

import com.datastax.driver.core.exceptions.*;
import com.datastax.driver.core.utils.Bytes;

class Responses {

    private Responses() {}

    public static class Error extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            @Override
            public Error decode(ChannelBuffer body) {
                ExceptionCode code = ExceptionCode.fromValue(body.readInt());
                String msg = CBUtil.readString(body);
                Object infos = null;
                switch (code) {
                    case UNAVAILABLE:
                        ConsistencyLevel clu = CBUtil.readConsistencyLevel(body);
                        int required = body.readInt();
                        int alive = body.readInt();
                        infos = new UnavailableException(clu, required, alive);
                        break;
                    case WRITE_TIMEOUT:
                    case READ_TIMEOUT:
                        ConsistencyLevel clt = CBUtil.readConsistencyLevel(body);
                        int received = body.readInt();
                        int blockFor = body.readInt();
                        if (code == ExceptionCode.WRITE_TIMEOUT) {
                            WriteType writeType = Enum.valueOf(WriteType.class, CBUtil.readString(body));
                            infos = new WriteTimeoutException(clt, writeType, received, blockFor);
                        } else {
                            byte dataPresent = body.readByte();
                            infos = new ReadTimeoutException(clt, received, blockFor, dataPresent != 0);
                        }
                        break;
                    case UNPREPARED:
                        infos = MD5Digest.wrap(CBUtil.readBytes(body));
                        break;
                    case ALREADY_EXISTS:
                        String ksName = CBUtil.readString(body);
                        String cfName = CBUtil.readString(body);
                        infos = new AlreadyExistsException(ksName, cfName);
                        break;
                }
                return new Error(code, msg, infos);
            }
        };

        public final ExceptionCode code;
        public final String message;
        public final Object infos; // can be null

        private Error(ExceptionCode code, String message, Object infos) {
            super(Message.Response.Type.ERROR);
            this.code = code;
            this.message = message;
            this.infos = infos;
        }

        public DriverException asException(InetSocketAddress host) {
            switch (code) {
                case SERVER_ERROR:     return new DriverInternalError(String.format("An unexpected error occurred server side on %s: %s", host, message));
                case PROTOCOL_ERROR:   return new DriverInternalError("An unexpected protocol error occurred. This is a bug in this library, please report: " + message);
                case BAD_CREDENTIALS:  return new AuthenticationException(host, message);
                case UNAVAILABLE:      return ((UnavailableException)infos).copy(); // We copy to have a nice stack trace
                case OVERLOADED:       return new DriverInternalError(String.format("Queried host (%s) was overloaded; this shouldn't happen, another node should have been tried", host));
                case IS_BOOTSTRAPPING: return new DriverInternalError(String.format("Queried host (%s) was bootstrapping; this shouldn't happen, another node should have been tried", host));
                case TRUNCATE_ERROR:   return new TruncateException(message);
                case WRITE_TIMEOUT:    return ((WriteTimeoutException)infos).copy();
                case READ_TIMEOUT:     return ((ReadTimeoutException)infos).copy();
                case SYNTAX_ERROR:     return new SyntaxError(message);
                case UNAUTHORIZED:     return new UnauthorizedException(message);
                case INVALID:          return new InvalidQueryException(message);
                case CONFIG_ERROR:     return new InvalidConfigurationInQueryException(message);
                case ALREADY_EXISTS:   return ((AlreadyExistsException)infos).copy();
                case UNPREPARED:       return new DriverInternalError(String.format("A prepared query was submitted on %s but was not known of that node; this shouldn't happen, the query should have been re-prepared", host));
                default:               return new DriverInternalError(String.format("Unknown protocol error code %s returned by %s. The error message was: %s", code, host, message));
            }
        }

        @Override
        public String toString() {
            return "ERROR " + code + ": " + message;
        }
    }

    public static class Ready extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            public Ready decode(ChannelBuffer body) {
                // TODO: Would it be cool to return a singleton? Check we don't need to
                // set the streamId or something
                return new Ready();
            }
        };

        public Ready() {
            super(Message.Response.Type.READY);
        }

        @Override
        public String toString() {
            return "READY";
        }
    }

    public static class Authenticate extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            public Authenticate decode(ChannelBuffer body) {
                String authenticator = CBUtil.readString(body);
                return new Authenticate(authenticator);
            }
        };

        public final String authenticator;

        public Authenticate(String authenticator) {
            super(Message.Response.Type.AUTHENTICATE);
            this.authenticator = authenticator;
        }

        @Override
        public String toString() {
            return "AUTHENTICATE " + authenticator;
        }
    }

    public static class Supported extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            public Supported decode(ChannelBuffer body) {
                return new Supported(CBUtil.readStringToStringListMap(body));
            }
        };

        public final Map> supported;
        public final Set supportedCompressions = EnumSet.noneOf(ProtocolOptions.Compression.class);

        public Supported(Map> supported) {
            super(Message.Response.Type.SUPPORTED);
            this.supported = supported;

            parseCompressions();
        }

        private void parseCompressions() {
            List compList = supported.get(Requests.Startup.COMPRESSION_OPTION);
            if (compList == null)
                return;

            for (String compStr : compList) {
                ProtocolOptions.Compression compr = ProtocolOptions.Compression.fromString(compStr);
                if (compr != null)
                    supportedCompressions.add(compr);
            }
        }

        @Override
        public String toString() {
            return "SUPPORTED " + supported;
        }
    }

    public static abstract class Result extends Message.Response {

        public static final Message.Decoder decoderV1 = new Message.Decoder() {
            public Result decode(ChannelBuffer body) {
                Kind kind = Kind.fromId(body.readInt());
                return kind.subDecoderV1.decode(body);
            }
        };

        public static final Message.Decoder decoderV2 = new Message.Decoder() {
            public Result decode(ChannelBuffer body) {
                Kind kind = Kind.fromId(body.readInt());
                return kind.subDecoderV2.decode(body);
            }
        };

        public enum Kind {
            VOID         (1, Void.subcodec, Void.subcodec),
            ROWS         (2, Rows.subcodec, Rows.subcodec),
            SET_KEYSPACE (3, SetKeyspace.subcodec, SetKeyspace.subcodec),
            PREPARED     (4, Prepared.subcodecV1, Prepared.subcodecV2),
            SCHEMA_CHANGE(5, SchemaChange.subcodec, SchemaChange.subcodec);

            private final int id;
            private final Message.Decoder subDecoderV1;
            private final Message.Decoder subDecoderV2;

            private static final Kind[] ids;
            static {
                int maxId = -1;
                for (Kind k : Kind.values())
                    maxId = Math.max(maxId, k.id);
                ids = new Kind[maxId + 1];
                for (Kind k : Kind.values()) {
                    if (ids[k.id] != null)
                        throw new IllegalStateException("Duplicate kind id");
                    ids[k.id] = k;
                }
            }

            private Kind(int id, Message.Decoder subDecoderV1, Message.Decoder subDecoderV2) {
                this.id = id;
                this.subDecoderV1 = subDecoderV1;
                this.subDecoderV2 = subDecoderV2;
            }

            public static Kind fromId(int id) {
                Kind k = ids[id];
                if (k == null)
                    throw new DriverInternalError(String.format("Unknown kind id %d in RESULT message", id));
                return k;
            }
        }

        public final Kind kind;

        protected Result(Kind kind) {
            super(Message.Response.Type.RESULT);
            this.kind = kind;
        }

        public static class Void extends Result {
            // Even though we have no specific information here, don't make a
            // singleton since as each message it has in fact a streamid and connection.
            public Void() {
                super(Kind.VOID);
            }

            public static final Message.Decoder subcodec = new Message.Decoder() {
                public Result decode(ChannelBuffer body) {
                    return new Void();
                }
            };

            @Override
            public String toString() {
                return "EMPTY RESULT";
            }
        }

        public static class SetKeyspace extends Result {
            public final String keyspace;

            private SetKeyspace(String keyspace) {
                super(Kind.SET_KEYSPACE);
                this.keyspace = keyspace;
            }

            public static final Message.Decoder subcodec = new Message.Decoder() {
                public Result decode(ChannelBuffer body) {
                    return new SetKeyspace(CBUtil.readString(body));
                }
            };

            @Override
            public String toString() {
                return "RESULT set keyspace " + keyspace;
            }
        }

        public static class Rows extends Result {

            public static class Metadata {

                private static enum Flag
                {
                    // The order of that enum matters!!
                    GLOBAL_TABLES_SPEC,
                    HAS_MORE_PAGES,
                    NO_METADATA;

                    public static EnumSet deserialize(int flags) {
                        EnumSet set = EnumSet.noneOf(Flag.class);
                        Flag[] values = Flag.values();
                        for (int n = 0; n < values.length; n++) {
                            if ((flags & (1 << n)) != 0)
                                set.add(values[n]);
                        }
                        return set;
                    }

                    public static int serialize(EnumSet flags) {
                        int i = 0;
                        for (Flag flag : flags)
                            i |= 1 << flag.ordinal();
                        return i;
                    }
                }

                static final Metadata EMPTY = new Metadata(0, null, null);

                public final int columnCount;
                public final ColumnDefinitions columns; // Can be null if no metadata was asked by the query
                public final ByteBuffer pagingState;

                private Metadata(int columnCount, ColumnDefinitions columns, ByteBuffer pagingState) {
                    this.columnCount = columnCount;
                    this.columns = columns;
                    this.pagingState = pagingState;
                }

                public static Metadata decode(ChannelBuffer body) {

                    // flags & column count
                    EnumSet flags = Flag.deserialize(body.readInt());
                    int columnCount = body.readInt();

                    ByteBuffer state = null;
                    if (flags.contains(Flag.HAS_MORE_PAGES))
                        state = CBUtil.readValue(body);

                    if (flags.contains(Flag.NO_METADATA))
                        return new Metadata(columnCount, null, state);

                    boolean globalTablesSpec = flags.contains(Flag.GLOBAL_TABLES_SPEC);

                    String globalKsName = null;
                    String globalCfName = null;
                    if (globalTablesSpec) {
                        globalKsName = CBUtil.readString(body);
                        globalCfName = CBUtil.readString(body);
                    }

                    // metadata (names/types)
                    ColumnDefinitions.Definition[] defs = new ColumnDefinitions.Definition[columnCount];
                    for (int i = 0; i < columnCount; i++) {
                        String ksName = globalTablesSpec ? globalKsName : CBUtil.readString(body);
                        String cfName = globalTablesSpec ? globalCfName : CBUtil.readString(body);
                        String name = CBUtil.readString(body);
                        DataType type = DataType.decode(body);
                        defs[i] = new ColumnDefinitions.Definition(ksName, cfName, name, type);
                    }

                    return new Metadata(columnCount, new ColumnDefinitions(defs), state);
                }

                @Override
                public String toString() {
                    StringBuilder sb = new StringBuilder();

                    if (columns == null) {
                        sb.append('[').append(columnCount).append(" columns]");
                    } else {
                        for (ColumnDefinitions.Definition column : columns) {
                            sb.append('[').append(column.getName());
                            sb.append(" (").append(column.getType()).append(")]");
                        }
                    }
                    if (pagingState != null)
                        sb.append(" (to be continued)");
                    return sb.toString();
                }
            }

            public static final Message.Decoder subcodec = new Message.Decoder() {
                public Result decode(ChannelBuffer body) {

                    Metadata metadata = Metadata.decode(body);

                    int rowCount = body.readInt();
                    int columnCount = metadata.columnCount;

                    Queue> data = new ArrayDeque>(rowCount);
                    for (int i = 0; i < rowCount; i++) {
                        List row = new ArrayList(columnCount);
                        for (int j = 0; j < columnCount; j++)
                            row.add(CBUtil.readValue(body));
                        data.add(row);
                    }

                    return new Rows(metadata, data);
                }
            };

            public final Metadata metadata;
            public final Queue> data;

            private Rows(Metadata metadata, Queue> data) {
                super(Kind.ROWS);
                this.metadata = metadata;
                this.data = data;
            }

            @Override
            public String toString() {
                StringBuilder sb = new StringBuilder();
                sb.append("ROWS ").append(metadata).append('\n');
                for (List row : data) {
                    for (int i = 0; i < row.size(); i++) {
                        ByteBuffer v = row.get(i);
                        if (v == null) {
                            sb.append(" | null");
                        } else {
                            sb.append(" | ");
                            if (metadata.columns == null)
                                sb.append(Bytes.toHexString(v));
                            else
                                sb.append(metadata.columns.getType(i).deserialize(v));
                        }
                    }
                    sb.append('\n');
                }
                sb.append("---");
                return sb.toString();
            }
        }

        public static class Prepared extends Result {

            public static final Message.Decoder subcodecV1 = new Message.Decoder() {
                public Result decode(ChannelBuffer body) {
                    MD5Digest id = MD5Digest.wrap(CBUtil.readBytes(body));
                    Rows.Metadata metadata = Rows.Metadata.decode(body);
                    return new Prepared(id, metadata, Rows.Metadata.EMPTY);
                }
            };

            public static final Message.Decoder subcodecV2 = new Message.Decoder() {
                public Result decode(ChannelBuffer body) {
                    MD5Digest id = MD5Digest.wrap(CBUtil.readBytes(body));
                    Rows.Metadata metadata = Rows.Metadata.decode(body);
                    Rows.Metadata resultMetadata = Rows.Metadata.decode(body);
                    return new Prepared(id, metadata, resultMetadata);
                }
            };

            public final MD5Digest statementId;
            public final Rows.Metadata metadata;
            public final Rows.Metadata resultMetadata;

            private Prepared(MD5Digest statementId, Rows.Metadata metadata, Rows.Metadata resultMetadata) {
                super(Kind.PREPARED);
                this.statementId = statementId;
                this.metadata = metadata;
                this.resultMetadata = resultMetadata;
            }

            @Override
            public String toString() {
                return "RESULT PREPARED " + statementId + ' ' + metadata + " (resultMetadata=" + resultMetadata + ')';
            }
        }

        public static class SchemaChange extends Result {

            public enum Change { CREATED, UPDATED, DROPPED }

            public final Change change;
            public final String keyspace;
            public final String columnFamily;

            public static final Message.Decoder subcodec = new Message.Decoder() {
                public Result decode(ChannelBuffer body)
                {
                    Change change = CBUtil.readEnumValue(Change.class, body);
                    String keyspace = CBUtil.readString(body);
                    String columnFamily = CBUtil.readString(body);
                    return new SchemaChange(change, keyspace, columnFamily);
                }
            };

            private SchemaChange(Change change, String keyspace, String columnFamily) {
                super(Kind.SCHEMA_CHANGE);
                this.change = change;
                this.keyspace = keyspace;
                this.columnFamily = columnFamily;
            }

            @Override
            public String toString() {
                return "RESULT schema change " + change + " on " + keyspace + (columnFamily.isEmpty() ? "" : '.' + columnFamily);
            }
        }
    }

    public static class Event extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            public Event decode(ChannelBuffer body) {
                return new Event(ProtocolEvent.deserialize(body));
            }
        };

        public final ProtocolEvent event;

        public Event(ProtocolEvent event) {
            super(Message.Response.Type.EVENT);
            this.event = event;
        }

        @Override
        public String toString() {
            return "EVENT " + event;
        }
    }

    public static class AuthChallenge extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            public AuthChallenge decode(ChannelBuffer body) {
                ByteBuffer b = CBUtil.readValue(body);
                if (b == null)
                    return new AuthChallenge(null);

                byte[] token = new byte[b.remaining()];
                b.get(token);
                return new AuthChallenge(token);
            }
        };

        public final byte[] token;

        private AuthChallenge(byte[] token) {
            super(Message.Response.Type.AUTH_CHALLENGE);
            this.token = token;
        }
    }

    public static class AuthSuccess extends Message.Response {

        public static final Message.Decoder decoder = new Message.Decoder() {
            public AuthSuccess decode(ChannelBuffer body) {
                ByteBuffer b = CBUtil.readValue(body);
                if (b == null)
                    return new AuthSuccess(null);

                byte[] token = new byte[b.remaining()];
                b.get(token);
                return new AuthSuccess(token);
            }
        };

        public final byte[] token;

        private AuthSuccess(byte[] token) {
            super(Message.Response.Type.AUTH_SUCCESS);
            this.token = token;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy