Key-value message class: full in Java

import org.zeromq.ZContext;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Socket;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.UUID;

public class kvmsg
{
// Keys are short strings
private static final int KVMSG_KEY_MAX = 255;

// Message is formatted on wire as 5 frames:
// frame 0: getKey (0MQ string)
// frame 1: getSequence (8 bytes, network order)
// frame 2: uuid (blob, 16 bytes)
// frame 3: properties (0MQ string)
// frame 4: body (blob)
private static final int FRAME_KEY = 0;
private static final int FRAME_SEQ = 1;
private static final int FRAME_UUID = 2;
private static final int FRAME_PROPS = 3;
private static final int FRAME_BODY = 4;
private static final int KVMSG_FRAMES = 5;

// Presence indicators for each frame
private boolean[] present = new boolean[KVMSG_FRAMES];
// Corresponding 0MQ message frames, if any
private byte[][] frame = new byte[KVMSG_FRAMES][];
// Key, copied into safe string
private String key;
// List of properties, as name=value strings
private Properties props;
private int props_size;

// These two helpers serialize a list of properties to and from a
// message frame:

private void encodeProps()
{
ByteBuffer msg = ByteBuffer.allocate(props_size);
for (Entry<Object, Object> o: props.entrySet()) {
String prop = o.getKey().toString() + "=" + o.getValue().toString() + "\n";
msg.put(prop.getBytes());
}
present[FRAME_PROPS] = true;
frame[FRAME_PROPS] = msg.array();
}

private void decodeProps()
{
byte[] msg = frame[FRAME_PROPS];
props_size = msg.length;
props.clear();

if (msg.length == 0)
return;

System.out.println("" + msg.length + " :" + new String(msg));
for (String prop: new String(msg).split("\n")) {
String[] split = prop.split("=");
props.setProperty(split[0], split[1]);
}
}

// Here are the constructor and destructor for the class:

// Constructor, takes a getSequence number for the new kvmsg instance:
public kvmsg(long sequence)
{
props = new Properties();
setSequence(sequence);
}

public void destroy()
{
}

// This method reads a getKey-value message from the socket and returns a
// new kvmsg instance:
public static kvmsg recv(Socket socket)
{
// This method is almost unchanged from kvsimple
assert (socket != null);
kvmsg self = new kvmsg(0);

// Read all frames off the wire, reject if bogus
int frameNbr;
for (frameNbr = 0; frameNbr < KVMSG_FRAMES; frameNbr++) {
//zmq_msg_init (&self->frame [frameNbr]);
self.present[frameNbr] = true;
if ((self.frame[frameNbr] = socket.recv(0)) == null) {
self.destroy();
break;
}
// Verify multipart framing
boolean rcvmore = (frameNbr < KVMSG_FRAMES - 1)? true: false;
if (socket.hasReceiveMore() != rcvmore) {
self.destroy();
break;
}
}
self.decodeProps();
return self;
}
// Send getKey-value message to socket; any empty frames are sent as such.
public void send(Socket socket)
{
assert (socket != null);

encodeProps();
// The rest of the method is unchanged from kvsimple
int frameNbr;
for (frameNbr = 0; frameNbr < KVMSG_FRAMES; frameNbr++) {
byte[] copy = new byte[0];
if (present[frameNbr])
copy = frame[frameNbr];
socket.send(copy, (frameNbr < KVMSG_FRAMES - 1)? ZMQ.SNDMORE: 0);
}
}

// This method duplicates a kvmsg instance, returns the new instance:
public kvmsg dup()
{
kvmsg kvmsg = new kvmsg(0);
int frameNbr;
for (frameNbr = 0; frameNbr < KVMSG_FRAMES; frameNbr++) {
if (present[frameNbr]) {
kvmsg.frame[frameNbr] = new byte[frame[frameNbr].length];
System.arraycopy(frame[frameNbr], 0,
kvmsg.frame[frameNbr], 0, frame[frameNbr].length);
kvmsg.present[frameNbr] = true;
}
}
kvmsg.props_size = props_size;
kvmsg.props.putAll(props);
return kvmsg;
}
// The getKey, getSequence, body, and size methods are the same as in kvsimple.

// Return getKey from last read message, if any, else NULL
public String getKey()
{
if (present[FRAME_KEY]) {
if (key == null) {
int size = frame[FRAME_KEY].length;
if (size > KVMSG_KEY_MAX)
size = KVMSG_KEY_MAX;
byte[] buf = new byte[size];
System.arraycopy(frame[FRAME_KEY], 0, buf, 0, size);
key = new String(buf);
}
return key;
}
else
return null;
}

// Set message getKey as provided
public void setKey(String key)
{
byte[] msg = new byte[key.length()];
System.arraycopy(key.getBytes(), 0, msg, 0, key.length());
frame[FRAME_KEY] = msg;
present[FRAME_KEY] = true;
}

// Set message getKey using printf format
public void fmtKey(String fmt, Object args)
{
setKey(String.format(fmt, args));
}

// Return getSequence nbr from last read message, if any
public long getSequence()
{
if (present[FRAME_SEQ]) {
assert (frame[FRAME_SEQ].length == 8);
ByteBuffer source = ByteBuffer.wrap(frame[FRAME_SEQ]);
return source.getLong();
}
else
return 0;
}

// Set message getSequence number
public void setSequence(long sequence)
{
ByteBuffer msg = ByteBuffer.allocate(8);

msg.putLong(sequence);

present[FRAME_SEQ] = true;
frame[FRAME_SEQ] = msg.array();
}

// Return body from last read message, if any, else NULL
public byte[] body()
{
if (present[FRAME_BODY])
return frame[FRAME_BODY];
else
return null;
}

// Set message body
public void setBody(byte[] body)
{
byte[] msg = new byte[body.length];
System.arraycopy(body, 0, msg, 0, body.length);
frame [FRAME_BODY] = msg;
present[FRAME_BODY] = true;
}

// Set message body using printf format
public void fmtBody(String fmt, Object args)
{
setBody(String.format(fmt, args).getBytes());
}

// Return body size from last read message, if any, else zero
public int size()
{
if (present[FRAME_BODY])
return frame[FRAME_BODY].length;
else
return 0;
}

// These methods get and set the UUID for the getKey-value message:
public byte[] UUID()
{
if (present[FRAME_UUID])
return frame[FRAME_UUID];
else
return null;
}

// Sets the UUID to a randomly generated value
public void setUUID()
{
byte[] msg = UUID.randomUUID().toString().getBytes();
present[FRAME_UUID] = true;
frame[FRAME_UUID] = msg;
}

// These methods get and set a specified message property:

// Get message property, return "" if no such property is defined.
public String getProp(String name)
{
return props.getProperty(name, "");
}

// Set message property. Property name cannot contain '='. Max length of
// value is 255 chars.
public void setProp(String name, String fmt, Object args)
{
String value = String.format(fmt, args);
Object old = props.setProperty(name, value);
if (old != null)
props_size -= old.toString().length();
else
props_size += name.length() + 2;
props_size += value.length();
}

// This method stores the getKey-value message into a hash map, unless
// the getKey and value are both null. It nullifies the kvmsg reference
// so that the object is owned by the hash map, not the caller:

public void store(Map<String, kvmsg> hash)
{
if (size() > 0) {
if (present[FRAME_KEY] && present[FRAME_BODY]) {
hash.put(getKey(), this);
}
} else
hash.remove(getKey());
}

// This method extends the kvsimple implementation with support for
// message properties:

public void dump()
{
int size = size();
byte[] body = body();
System.err.printf("[seq:%d]", getSequence());
System.err.printf("[getKey:%s]", getKey());
System.err.printf("[size:%d] ", size);
System.err.printf("[");
for (String key: props.stringPropertyNames()) {
System.err.printf("%s=%s;", key, props.getProperty(key));
}
System.err.printf("]");

for (int charNbr = 0; charNbr < size; charNbr++)
System.err.printf("%02X", body[charNbr]);
System.err.printf("\n");
}

// This method is the same as in kvsimple with added support
// for the uuid and property features of kvmsg:
public void test(boolean verbose)
{
System.out.printf(" * kvmsg: ");

// Prepare our context and sockets
ZContext ctx = new ZContext();
Socket output = ctx.createSocket(ZMQ.DEALER);
output.bind("ipc://kvmsg_selftest.ipc");
Socket input = ctx.createSocket(ZMQ.DEALER);
input.connect("ipc://kvmsg_selftest.ipc");

Map<String,kvmsg> kvmap = new HashMap<String, kvmsg>();

// Test send and receive of simple message
kvmsg kvmsg = new kvmsg(1);
kvmsg.setKey("getKey");
kvmsg.setUUID();
kvmsg.setBody("body".getBytes());
if (verbose)
kvmsg.dump();
kvmsg.send(output);
kvmsg.store(kvmap);

kvmsg = kvmsg.recv(input);
if (verbose)
kvmsg.dump();
assert (kvmsg.getKey().equals("getKey"));
kvmsg.store(kvmap);

// Test send and receive of message with properties
kvmsg = new kvmsg(2);
kvmsg.setProp("prop1", "value1");
kvmsg.setProp("prop2", "value1");
kvmsg.setProp("prop2", "value2");
kvmsg.setKey("getKey");
kvmsg.setUUID();
kvmsg.setBody("body".getBytes());
assert (kvmsg.getProp("prop2").equals("value2"));
if (verbose)
kvmsg.dump();
kvmsg.send(output);
kvmsg.destroy();

kvmsg = kvmsg.recv(input);
if (verbose)
kvmsg.dump();
assert (kvmsg.key.equals("getKey"));
assert (kvmsg.getProp("prop2").equals("value2"));
kvmsg.destroy();

// Shutdown and destroy all objects
ctx.destroy();

System.out.printf("OK\n");

}
}