I have a class in which I am passing certain parameters through the constructor and then using those parameters to make one final byte array with a proper format (header + data) and it works fine:
public final class Frame { private final byte addressedCenter; private final byte version; private final Map<byte[], byte[]> keyDataHolder; private final long location; private final long locationFrom; private final long locationOrigin; private final byte partition; private final byte copy; public Frame(byte addressedCenter, byte version, Map<byte[], byte[]> keyDataHolder, long location, long locationFrom, long locationOrigin, byte partition, byte copy) { this.addressedCenter = addressedCenter; this.version = version; this.keyDataHolder = keyDataHolder; this.location = location; this.locationFrom = locationFrom; this.locationOrigin = locationOrigin; this.partition = partition; this.copy = copy; } public byte[] serialize() { ByteBuffer byteBuffer = ByteBuffer.allocate(getBufferUsed(keyDataHolder)).order(ByteOrder.BIG_ENDIAN); // header layout byteBuffer.put(addressedCenter).put(version).putInt(keyDataHolder.size()) .putInt(getBufferUsed(keyDataHolder)).putLong(location).putLong(locationFrom) .putLong(locationOrigin).put(partition).put(copy); // now the data layout for (Map.Entry<byte[], byte[]> entry : keyDataHolder.entrySet()) { byte keyType = 0; byte[] key = entry.getKey(); byte[] value = entry.getValue(); byte keyLength = (byte) key.length; short valueLength = (short) value.length; ByteBuffer dataBuffer = ByteBuffer.wrap(value); long timestamp = valueLength > 10 ? dataBuffer.getLong(2) : System.currentTimeMillis(); byteBuffer.put(keyType).put(keyLength).put(key).putLong(timestamp).putShort(valueLength) .put(value); } return byteBuffer.array(); } // this method is returning only `locationFrom` public static long getLocationFrom(final byte[] byteArray) { ByteBuffer bb = ByteBuffer.wrap(byteArray).order(ByteOrder.BIG_ENDIAN); bb.rewind(); byte addressedCenter = bb.get(); byte version = bb.get(); int numOfRecords = bb.getInt(); int bufferUsed = bb.getInt(); long location = bb.getLong(); long locationFrom = bb.getLong(); long locationOrigin = bb.getLong(); byte partition = bb.get(); byte copy = bb.get(); byte keyType = bb.get(); byte keyLength = bb.get(); byte[] extractKeyValue = new byte[keyLength]; bb.get(extractKeyValue); String key = new String(extractKeyValue, StandardCharsets.UTF_8); long timestamp = bb.getLong(); short dataSize = bb.getShort(); if (dataSize >= 16) { location = bb.getLong(); locationFrom = bb.getLong(); } else { // log that we have different size and we cannot extract // location and locationFrom it. } return locationFrom; } // this method is returning only `location` public static long getLocation(final byte[] byteArray) { ByteBuffer bb = ByteBuffer.wrap(byteArray).order(ByteOrder.BIG_ENDIAN); bb.rewind(); byte addressedCenter = bb.get(); byte version = bb.get(); int numOfRecords = bb.getInt(); int bufferUsed = bb.getInt(); long location = bb.getLong(); long locationFrom = bb.getLong(); long locationOrigin = bb.getLong(); byte partition = bb.get(); byte copy = bb.get(); byte keyType = bb.get(); byte keyLength = bb.get(); byte[] extractKeyValue = new byte[keyLength]; bb.get(extractKeyValue); String key = new String(extractKeyValue, StandardCharsets.UTF_8); long timestamp = bb.getLong(); short dataSize = bb.getShort(); if (dataSize >= 16) { location = bb.getLong(); locationFrom = bb.getLong(); } else { // log that we have different size and we cannot extract // location and locationFrom it. } return location; } // 36 + dataSize + 1 + 1 + keyLength + 8 + 2; private int getBufferUsed(final Map<byte[], byte[]> keyDataHolder) { int size = 36; for (Map.Entry<byte[], byte[]> entry : keyDataHolder.entrySet()) { size += 1 + 1 + 8 + 2; size += entry.getKey().length; size += entry.getValue().length; } return size; } }
Question
Now once I call serialize()
method of above class, it will give me actual packed byteArray
:
Frame frame = new Frame(......); byte[] packedByteArray = frame.serialize(); // send packedByteArray to some other system ....... byte[] byteArray = getFromOtherSystem(); long locationFrom = Frame.getLocationFrom(byteArray); long location = Frame.getLocation(byteArray);
Now once I have this packedByteArray
, I send it to some other system and then that system will return me back another byteArray
which is packed in the same format as shown above in the serialize
method. Now I need to use this byteArray
and extract all the individual fields from it so I wrote the getLocationFrom
method that takes the byteArray
as the parameter and it will return me locationFrom
field value only for now. When the other system sends me back the byteArray
, its data size should be around 16 only and actual data will have two long values in it and data will always be in the same format when it comes from other system.
Right now my getLocationFrom
method just returns locationFrom
field only but if I want to get location
or locationOrigin
or version
or other fields then should I add respective static methods in my DataFrame
class? If I do that, then I will be repeating all the deserialization logic in all the static methods for each fields.
How should I design this Frame
class so that it can take all the parameters to make one packedByteArray
(through the constructor I am doing right now) and also it should be able to take byteArray
parameter as well and give me all other fields by deserializing this? Just like I am doing for locationFrom
field.
I am mainly trying to see how I can design my class so that if I asks for individual fields value given a byteArray
, it should be able to give me those values back. Also, it should be able to serialize everything into one byteArray
given all the fields.