/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.exceptions;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import org.voltdb.VoltProcedure;
/**
* Base class for runtime exceptions that can be serialized to ByteBuffers without involving Java's
* serialization mechanism
*
*/
public class SerializableException extends VoltProcedure.VoltAbortException {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Storage for the detailed message describing the error that generated this exception
*/
private final String m_message;
/**
* Enum correlating the integer ordinals that are serialized as the type of an exception
* with the class that deserializes that type.
*
*/
protected enum SerializableExceptions {
None() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return null;
}
},
EEException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new EEException(b);
}
},
SQLException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new SQLException(b);
}
},
ConstraintFailureException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new ConstraintFailureException(b);
}
},
EvictedTupleAccessException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new EvictedTupleAccessException(b);
}
},
UnknownBlockAccessException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new UnknownBlockAccessException(b);
}
},
MispredictionException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new MispredictionException(b);
}
},
GenericSerializableException() {
@Override
protected SerializableException deserializeException(ByteBuffer b) {
return new SerializableException(b);
}
};
abstract protected SerializableException deserializeException(ByteBuffer b);
}
public SerializableException() {
m_message = null;
}
public SerializableException(Throwable t) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
t.printStackTrace(pw);
pw.flush();
m_message = sw.toString();
}
public SerializableException(ByteBuffer b) {
final int messageLength = b.getInt();
final byte messageBytes[] = new byte[messageLength];
b.get(messageBytes);
try {
m_message = new String(messageBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
/**
* Get the detailed message describing the error that generated this exception
*/
@Override
public String getMessage() { return m_message; }
/**
* Number of bytes necessary to store the serialized representation of this exception
* @return Number of bytes
*/
public int getSerializedSize() {
// sizes: as near as I can tell,
// 3 is sizeof(short) for buffer length and sizeof(byte) for exception type
// 4 is sizeof(int) for message string length
if (m_message == null) {
return 5 + 4 + p_getSerializedSize();
}
return 5 + 4 + m_message.getBytes().length + p_getSerializedSize();//one byte ordinal and 4 byte length
}
/**
* Method for subclasses to implement that returns the number of bytes necessary to store
* subclass data
* @return Number of bytes necessary to store subclass data
*/
protected int p_getSerializedSize() {
return 0;
}
/**
* Serialize this exception to the supplied byte buffer
* @param b ByteBuffer to serialize this exception to
* @throws IOException
*/
public void serializeToBuffer(ByteBuffer b) throws IOException {
assert(getSerializedSize() <= b.remaining());
b.putInt(getSerializedSize() - 4);
b.put((byte)getExceptionType().ordinal());
if (m_message != null) {
final byte messageBytes[] = m_message.getBytes();
b.putInt(messageBytes.length);
b.put(messageBytes);
} else {
b.putInt(0);
}
p_serializeToBuffer(b);
}
/**
* Serialize this exception to a new ByteBuffer
* WARNING: This is slow and should normally be used!
* @return
*/
public ByteBuffer serializeToBuffer() {
int size = this.getSerializedSize();
ByteBuffer buffer = ByteBuffer.allocate(size);
try {
this.serializeToBuffer(buffer);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return (buffer);
}
/**
* Method for subclasses to implement that serializes the subclass's contents to
* the ByteBuffer
* @param b ByteBuffer to serialize the subclass contents to
* @throws IOException
*/
protected void p_serializeToBuffer(ByteBuffer b) throws IOException {}
/**
* Method for subclasses to specify what constant from the SerializableExceptions enum
* is defined for this type of exception
* @return Type of excption
*/
protected SerializableExceptions getExceptionType() {
return SerializableExceptions.GenericSerializableException;
}
/**
* Deserialize an exception (if any) from the ByteBuffer
* @param b ByteBuffer containing the exception to be deserialized
* @return A deserialized exception if one was serialized or null
*/
public static SerializableException deserializeFromBuffer(ByteBuffer b) {
final int length = b.getInt();
if (length == 0) {
return null;
}
final int ordinal = b.get();
assert (ordinal != SerializableExceptions.None.ordinal());
return SerializableExceptions.values()[ordinal].deserializeException(b);
}
@Override
public void printStackTrace(PrintStream s) {
s.print(m_message);
}
@Override
public void printStackTrace(PrintWriter p) {
p.print(m_message);
}
}