package ch.usi.da.paxos.message;
/*
* Copyright (c) 2013 Università della Svizzera italiana (USI)
*
* This file is part of URingPaxos.
*
* URingPaxos 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.
*
* URingPaxos 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 URingPaxos. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.zip.CRC32;
import ch.usi.da.paxos.api.PaxosRole;
/**
* Name: Message<br>
* Description: <br>
*
* Creation date: Mar 31, 2012<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class Message implements Serializable {
private static final long serialVersionUID = -4938636847085992695L;
private final long instance;
private final int sender;
private final PaxosRole receiver;
private final MessageType type;
private final int ballot;
private final int value_ballot; // version of the value
private final Value value;
private int vote_count = 0;
/**
* Public constructor
*
* @param instance the instance number
* @param sender the sender id
* @param receiver the receiver id
* @param type the message type
* @param ballot the ballot number
* @param value the value (can be null)
*/
public Message(long instance,int sender,PaxosRole receiver,MessageType type,int ballot,Value value){
this(instance,sender,receiver,type,ballot,-1,value);
}
/**
* Public constructor
*
* @param instance the instance number
* @param sender the sender id
* @param receiver the receiver id
* @param type the message type
* @param ballot the ballot number
* @param value_ballot the ballot corresponding to the value
* @param value the value (can be null)
*/
public Message(long instance,int sender,PaxosRole receiver,MessageType type,int ballot,int value_ballot,Value value){
this.instance = instance;
this.sender = sender;
this.receiver = receiver;
this.type = type;
this.ballot = ballot;
this.value_ballot = value_ballot;
this.value = value;
}
/**
* @return the instance
*/
public long getInstance() {
return instance;
}
/**
* @return the sender
*/
public int getSender() {
return sender;
}
/**
* @return the receiver
*/
public PaxosRole getReceiver() {
return receiver;
}
/**
* @return the type
*/
public MessageType getType() {
return type;
}
/**
* @return the ballot
*/
public int getBallot() {
return ballot;
}
/**
* @return the value_ballot
*/
public int getValueBallot() {
return value_ballot;
}
/**
* @return the value
*/
public Value getValue() {
return value;
}
/**
* @return the vote counter
*/
public synchronized int getVoteCount(){
return vote_count;
}
/**
* @param c set the vote counter
*/
public synchronized void setVoteCount(int c){
vote_count = c;
}
/**
* Increment the vote counter
*/
public synchronized void incrementVoteCount(){
vote_count = vote_count + 1;
}
public boolean equals(Object obj) {
if(obj instanceof Message){
Message m = (Message)obj;
if(this.instance == m.instance &&
this.ballot == m.ballot &&
this.receiver == m.receiver &&
this.sender == m.sender &&
this.type == m.type &&
(this.value != null ? this.value.equals(m.value) : true) &&
this.value_ballot == m.value_ballot &&
this.vote_count == m.vote_count){
return true;
}
}
return false;
}
public String toString(){
if(vote_count > 0){
return (this.getType() + " from:" + this.getSender() + " to:" + this.getReceiver() + " instance:" + this.instance + " ballot:" + this.getBallot() + " v_ballot:" + this.getValueBallot() + " value:" + this.getValue() + " votes:" + vote_count);
}else{
return (this.getType() + " from:" + this.getSender() + " to:" + this.getReceiver() + " instance:" + this.instance + " ballot:" + this.getBallot() + " v_ballot:" + this.getValueBallot() + " value:" + this.getValue());
}
}
/**
* An convenient way for wire representation
*
* @param m
* @return message byte array
*/
public static byte[] toWire(Message m){
ByteBuffer buffer = ByteBuffer.allocate(length(m));
toBuffer(buffer,m);
return buffer.array();
/*ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = null;
try {
out = new ObjectOutputStream(bos);
out.writeObject(m);
return bos.toByteArray();
} catch (IOException e) {
return new byte[0];
} finally {
try {
bos.close();
} catch (IOException e) {
}
if(out != null){
try {
out.close();
} catch (IOException e) {
}
}
}*/
}
/**
* This method is not really efficient; but convenient for std. IO
*
* @param b
* @return Message object
*/
public static Message fromWire(byte[] b) {
ByteBuffer buffer = ByteBuffer.wrap(b);
try {
return fromBuffer(buffer);
} catch (Exception e) {
return null;
}
/*ByteArrayInputStream bis = new ByteArrayInputStream(b);
ObjectInput in = null;
try {
in = new ObjectInputStream(bis);
return (Message) in.readObject();
} catch (IOException e) {
return null;
} catch (ClassNotFoundException e) {
return null;
} finally {
try {
bis.close();
} catch (IOException e) {
}
if(in != null){
try {
in.close();
} catch (IOException e) {
}
}
}*/
}
/**
* Get the Message length (without length prefix)
*
* @param m
* @return length of the message on the wire (without length prefix)
*/
public static int length(Message m){
int length = 32;
if(m.getValue() != null){
length = length + m.getValue().getByteID().length + 4 + m.getValue().getValue().length + 1;
}
return length;
}
/**
* Get the CRC32 of the Message (without the Value)
*
* @param m
* @return crc32 of the message (without the Value)
*/
public static long getCRC32(Message m){
if(m == null) return 0;
CRC32 crc = new CRC32();
crc.update((int)m.getInstance());
crc.update(m.getSender());
crc.update(m.getReceiver().getId());
crc.update(m.getType().getId());
crc.update(m.getBallot());
crc.update(m.getValueBallot());
crc.update(m.getVoteCount());
return crc.getValue();
}
/**
* This is the recommended way to serialize and send a Message trough NIO
*
* @param b
* @param m
*/
public static void toBuffer(ByteBuffer b,Message m){
// long instance
// int sender
// short role
// short type
// int ballot
// int value_ballot
// int vote count
// int ID length (or -1)
// byte[]ID
// int value length
// byte[]value
b.putLong(m.getInstance());
b.putInt(m.getSender());
b.putShort((short)m.getReceiver().getId());
b.putShort((short)m.getType().getId());
b.putInt(m.getBallot());
b.putInt(m.getValueBallot());
b.putInt(m.getVoteCount());
if(m.getValue() != null){
b.putInt(m.getValue().getByteID().length);
b.put(m.getValue().getByteID());
b.putInt(m.getValue().getValue().length);
b.put(m.getValue().getValue());
if(m.getValue().isBatch()){
b.put((byte) 0x01);
}else{
b.put((byte) 0x00);
}
}else{
b.putInt(-1);
}
}
/**
* This is the recommended way de-serialize and receive a Message trough NIO
*
* @param buffer
* @return Message object
*/
public static Message fromBuffer(ByteBuffer buffer) throws Exception {
long instance = buffer.getLong();
int sender = buffer.getInt();
PaxosRole role = PaxosRole.fromId(buffer.getShort());
MessageType type = MessageType.fromId(buffer.getShort());
int ballot = buffer.getInt();
int value_ballot = buffer.getInt();
int vote_count = buffer.getInt();
int id_length = buffer.getInt();
Value value = null;
if(id_length >= 0){
byte[] ib = new byte[id_length];
buffer.get(ib);
String id = new String(ib);
int v_length = buffer.getInt();
byte[] vb = new byte[v_length];
buffer.get(vb);
byte[] batch = new byte[1];
buffer.get(batch);
if(batch[0] > 0){
value = new Value(id,vb,true);
}else{
value = new Value(id,vb,false);
}
}
Message msg = new Message(instance,sender,role,type,ballot,value_ballot,value);
msg.setVoteCount(vote_count);
return msg;
}
}