/* This file is part of VoltDB. * Copyright (C) 2008-2010 VoltDB Inc. * * 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.export; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import org.voltdb.VoltType; import org.voltdb.messaging.FastDeserializer; import org.voltdb.messaging.FastSerializer; /** * Message exchanged during execution of poll/ack protocol */ public class ExportProtoMessage { public static final short kOpen = 1 << 0; public static final short kOpenResponse = 1 << 1; public static final short kPoll = 1 << 2; public static final short kPollResponse = 1 << 3; public static final short kAck = 1 << 4; public static final short kClose = 1 << 5; public static final short kError = 1 << 6; public static final short kSync = 1 << 7; public boolean isOpen() {return (m_type & kOpen) != 0;} public boolean isOpenResponse() {return (m_type & kOpenResponse) != 0;} public boolean isPoll() {return (m_type & kPoll) != 0;} public boolean isPollResponse() {return (m_type & kPollResponse) != 0;} public boolean isAck() {return (m_type & kAck) != 0;} public boolean isClose() {return (m_type & kClose) != 0;} public boolean isError() {return (m_type & kError) != 0;} public boolean isSync() {return (m_type & kSync) != 0;} /** * The Export data source metadata returned in a kOpenResponse message. */ static public class AdvertisedDataSource { final private byte m_isReplicated; final private int m_partitionId; final private long m_tableId; final private String m_tableName; private ArrayList<String> m_columnNames = new ArrayList<String>(); private ArrayList<VoltType> m_columnTypes = new ArrayList<VoltType>(); public AdvertisedDataSource(byte isReplicated, int p_id, long t_id, String t_name, ArrayList<String> names, ArrayList<VoltType> types) { m_isReplicated = isReplicated; m_partitionId = p_id; m_tableId = t_id; m_tableName = t_name; m_columnNames = names; m_columnTypes = types; } public boolean isReplicated() { return (m_isReplicated != 0); } public int partitionId() { return m_partitionId; } public long tableId() { return m_tableId; } public VoltType columnType(int index) { return m_columnTypes.get(index); } public ArrayList<VoltType> columnTypes() { return m_columnTypes; } public String columnName(int index) { return m_columnNames.get(index); } public ArrayList<String> columnNames() { return m_columnNames; } public String tableName() { return m_tableName; } } /** * Called to produce an Export protocol message from a FastDeserializer. * Note that this expects the length preceding value was already * read (probably how the buffer length was originally determined). * @param fds * @return new ExportProtoMessage from deserializer contents * @throws IOException */ public static ExportProtoMessage readExternal(FastDeserializer fds) throws IOException { ExportProtoMessage m = new ExportProtoMessage(0, 0); m.m_version = fds.readShort(); m.m_type = fds.readShort(); m.m_partitionId = fds.readInt(); m.m_tableId = fds.readLong(); m.m_offset = fds.readLong(); // if no data is remaining, m_data will have 0 capacity. m.m_data = fds.remainder(); return m; } public ExportProtoMessage(int partitionId, long tableId) { m_partitionId = partitionId; m_tableId = tableId; } public String messageTypesAsString() { String retval = "|"; if (isOpen()) retval += "OPEN|"; if (isOpenResponse()) retval += "OPEN_REPONSE|"; if (isPoll()) retval += "POLL|"; if (isPollResponse()) retval += "POLL_RESPONSE|"; if (isAck()) retval += "ACK|"; if (isClose()) retval += "CLOSE|"; if (isError()) retval += "ERROR|"; if (isSync()) retval += "SYNC|"; return retval; } public ByteBuffer toBuffer() throws IOException { FastSerializer fs = new FastSerializer(); writeToFastSerializer(fs); return fs.getBuffer(); } /** * Total bytes that would be used if serialized in its current state. * Does not include the 4 byte length prefix! * @return byte count. */ public int serializableBytes() { return FIXED_PAYLOAD_LENGTH + (m_data != null ? m_data.remaining() : 0); } public void writeToFastSerializer(FastSerializer fs) throws IOException { // write the length first. then the payload. fs.writeInt(serializableBytes()); fs.writeShort(m_version); fs.writeShort(m_type); fs.writeInt(m_partitionId); fs.writeLong(m_tableId); fs.writeLong(m_offset); if (m_data != null) { fs.write(m_data); // write advances m_data's position. m_data.flip(); } } public short version() { return m_version; } public ExportProtoMessage error() { m_type |= kError; return this; } public ExportProtoMessage open() { m_type |= kOpen; return this; } public ExportProtoMessage openResponse(ByteBuffer bb) { m_type |= kOpenResponse; m_data = bb; return this; } public ExportProtoMessage poll() { m_type |= kPoll; return this; } public ExportProtoMessage pollResponse(long offset, ByteBuffer bb) { m_type |= kPollResponse; m_data = bb; m_offset = offset; return this; } public ExportProtoMessage ack(long ackedOffset) { m_type |= kAck; m_offset = ackedOffset; return this; } public ExportProtoMessage close() { m_type |= kClose; return this; } public ByteBuffer getData() { return m_data; } public int getPartitionId() { return m_partitionId; } public long getTableId() { return m_tableId; } public long getAckOffset() { return m_offset; } /** * Provide a simple accessor to read the list of advertised data sources * returned as the payload to an open response. * @return List of data sources advertised with an open response. * @throws IOException */ public ArrayList<AdvertisedDataSource> getAdvertisedDataSources() throws IOException { if (!isOpenResponse()) { return null; } ArrayList<AdvertisedDataSource> result = new ArrayList<AdvertisedDataSource>(); FastDeserializer fds = new FastDeserializer(m_data); int count = m_data.getInt(); for (int i=0; i < count; i++) { ArrayList<VoltType> types = new ArrayList<VoltType>(); ArrayList<String> names = new ArrayList<String>(); byte is_replicated = fds.readByte(); int p_id = fds.readInt(); long t_id = fds.readLong(); String t_name = fds.readString(); int colcnt = fds.readInt(); for (int jj = 0; jj < colcnt; jj++) { names.add(fds.readString()); types.add(VoltType.get((byte)fds.readInt())); } result.add(new AdvertisedDataSource(is_replicated, p_id, t_id, t_name, names, types)); } return result; } @Override public String toString() { String s = "ExportProtoMessage: type(" + m_type + ") offset(" + m_offset + ") partitionId(" + m_partitionId + ") tableId(" + m_tableId +")" + " serializableBytes(" + serializableBytes() + ")"; if (m_data != null) { s = s + " payloadBytes(" + m_data.remaining() +")"; } else { s = s + " no payoad."; } return s; } // calculate bytes of fixed payload private static int FIXED_PAYLOAD_LENGTH = (Short.SIZE/8 * 2) + Integer.SIZE/8 + (Long.SIZE/8 * 2); // message version. Currently all messages are version 1. short m_version = 1; // bitmask of protocol actions in this message. short m_type = 0; // partition id for this ack or poll int m_partitionId = -1; // the table id for this ack or poll long m_tableId = -1; // if kAck, the offset being acked. // if kPollResponse, the offset of the last byte returned. long m_offset = -1; // poll payload data ByteBuffer m_data; }