/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cassandra.transport.messages; import java.nio.ByteBuffer; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.db.WriteType; import org.apache.cassandra.exceptions.*; import org.apache.cassandra.transport.CBUtil; import org.apache.cassandra.transport.Message; import org.apache.cassandra.transport.ProtocolException; import org.apache.cassandra.transport.ServerError; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.MD5Digest; /** * Message to indicate an error to the client. */ public class ErrorMessage extends Message.Response { private static final Logger logger = LoggerFactory.getLogger(ErrorMessage.class); public static final Message.Codec<ErrorMessage> codec = new Message.Codec<ErrorMessage>() { public ErrorMessage decode(ChannelBuffer body, int version) { ExceptionCode code = ExceptionCode.fromValue(body.readInt()); String msg = CBUtil.readString(body); TransportException te = null; switch (code) { case SERVER_ERROR: te = new ServerError(msg); break; case PROTOCOL_ERROR: te = new ProtocolException(msg); break; case BAD_CREDENTIALS: te = new AuthenticationException(msg); break; case UNAVAILABLE: { ConsistencyLevel cl = CBUtil.readConsistencyLevel(body); int required = body.readInt(); int alive = body.readInt(); te = new UnavailableException(cl, required, alive); } break; case OVERLOADED: te = new OverloadedException(msg); break; case IS_BOOTSTRAPPING: te = new IsBootstrappingException(); break; case TRUNCATE_ERROR: te = new TruncateException(msg); break; case WRITE_TIMEOUT: case READ_TIMEOUT: ConsistencyLevel cl = 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)); te = new WriteTimeoutException(writeType, cl, received, blockFor); } else { byte dataPresent = body.readByte(); te = new ReadTimeoutException(cl, received, blockFor, dataPresent != 0); } break; case UNPREPARED: { MD5Digest id = MD5Digest.wrap(CBUtil.readBytes(body)); te = new PreparedQueryNotFoundException(id); } break; case SYNTAX_ERROR: te = new SyntaxException(msg); break; case UNAUTHORIZED: te = new UnauthorizedException(msg); break; case INVALID: te = new InvalidRequestException(msg); break; case CONFIG_ERROR: te = new ConfigurationException(msg); break; case ALREADY_EXISTS: String ksName = CBUtil.readString(body); String cfName = CBUtil.readString(body); if (cfName.isEmpty()) te = new AlreadyExistsException(ksName); else te = new AlreadyExistsException(ksName, cfName); break; } return new ErrorMessage(te); } public void encode(ErrorMessage msg, ChannelBuffer dest, int version) { dest.writeInt(msg.error.code().value); CBUtil.writeString(msg.error.getMessage(), dest); switch (msg.error.code()) { case UNAVAILABLE: UnavailableException ue = (UnavailableException)msg.error; CBUtil.writeConsistencyLevel(ue.consistency, dest); dest.writeInt(ue.required); dest.writeInt(ue.alive); break; case WRITE_TIMEOUT: case READ_TIMEOUT: RequestTimeoutException rte = (RequestTimeoutException)msg.error; boolean isWrite = msg.error.code() == ExceptionCode.WRITE_TIMEOUT; CBUtil.writeConsistencyLevel(rte.consistency, dest); dest.writeInt(rte.received); dest.writeInt(rte.blockFor); if (isWrite) CBUtil.writeString(((WriteTimeoutException)rte).writeType.toString(), dest); else dest.writeByte((byte)(((ReadTimeoutException)rte).dataPresent ? 1 : 0)); break; case UNPREPARED: PreparedQueryNotFoundException pqnfe = (PreparedQueryNotFoundException)msg.error; CBUtil.writeBytes(pqnfe.id.bytes, dest); break; case ALREADY_EXISTS: AlreadyExistsException aee = (AlreadyExistsException)msg.error; CBUtil.writeString(aee.ksName, dest); CBUtil.writeString(aee.cfName, dest); break; } } public int encodedSize(ErrorMessage msg, int version) { int size = 4 + CBUtil.sizeOfString(msg.error.getMessage()); switch (msg.error.code()) { case UNAVAILABLE: UnavailableException ue = (UnavailableException)msg.error; size += CBUtil.sizeOfConsistencyLevel(ue.consistency) + 8; break; case WRITE_TIMEOUT: case READ_TIMEOUT: RequestTimeoutException rte = (RequestTimeoutException)msg.error; boolean isWrite = msg.error.code() == ExceptionCode.WRITE_TIMEOUT; size += CBUtil.sizeOfConsistencyLevel(rte.consistency) + 8; size += isWrite ? CBUtil.sizeOfString(((WriteTimeoutException)rte).writeType.toString()) : 1; break; case UNPREPARED: PreparedQueryNotFoundException pqnfe = (PreparedQueryNotFoundException)msg.error; size += CBUtil.sizeOfBytes(pqnfe.id.bytes); break; case ALREADY_EXISTS: AlreadyExistsException aee = (AlreadyExistsException)msg.error; size += CBUtil.sizeOfString(aee.ksName); size += CBUtil.sizeOfString(aee.cfName); break; } return size; } }; // We need to figure error codes out (#3979) public final TransportException error; private ErrorMessage(TransportException error) { super(Message.Type.ERROR); this.error = error; } private ErrorMessage(TransportException error, int streamId) { this(error); setStreamId(streamId); } public static ErrorMessage fromException(Throwable e) { int streamId = 0; if (e instanceof WrappedException) { streamId = ((WrappedException)e).streamId; e = e.getCause(); } if (e instanceof TransportException) return new ErrorMessage((TransportException)e, streamId); // Unexpected exception logger.error("Unexpected exception during request", e); return new ErrorMessage(new ServerError(e), streamId); } @Override public String toString() { return "ERROR " + error.code() + ": " + error.getMessage(); } public static RuntimeException wrap(Throwable t, int streamId) { return new WrappedException(t, streamId); } private static class WrappedException extends RuntimeException { private final int streamId; public WrappedException(Throwable cause, int streamId) { super(cause); this.streamId = streamId; } } }