Moved to own MsgPack implementation

This commit is contained in:
2017-02-03 07:29:51 +01:00
parent 3f8980e236
commit 10a45cc79c
8 changed files with 129 additions and 171 deletions

View File

@ -25,7 +25,7 @@ artifacts {
dependencies {
compile 'org.slf4j:slf4j-api:1.7.12'
compile 'org.msgpack:msgpack-core:0.8.11'
compile 'ch.dissem.msgpack:msgpack:development-SNAPSHOT'
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'org.mockito:mockito-core:1.10.19'

View File

@ -1,9 +1,9 @@
package ch.dissem.bitmessage.entity.valueobject;
import ch.dissem.bitmessage.exception.ApplicationException;
import org.msgpack.core.MessagePack;
import org.msgpack.core.MessagePacker;
import org.msgpack.core.MessageUnpacker;
import ch.dissem.msgpack.types.MPMap;
import ch.dissem.msgpack.types.MPString;
import ch.dissem.msgpack.types.MPType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -39,13 +39,10 @@ public class ExtendedEncoding implements Serializable {
}
public byte[] zip() {
try (ByteArrayOutputStream out = new ByteArrayOutputStream();
DeflaterOutputStream zipper = new DeflaterOutputStream(out)) {
MessagePacker packer = MessagePack.newDefaultPacker(zipper);
content.pack(packer);
packer.close();
zipper.close();
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
try (DeflaterOutputStream zipper = new DeflaterOutputStream(out)) {
content.pack().pack(zipper);
}
return out.toByteArray();
} catch (IOException e) {
throw new ApplicationException(e);
@ -68,12 +65,12 @@ public class ExtendedEncoding implements Serializable {
public interface Unpacker<T extends ExtendedType> {
String getType();
T unpack(MessageUnpacker unpacker, int size);
T unpack(MPMap<MPString, MPType<?>> map);
}
public interface ExtendedType extends Serializable {
String getType();
void pack(MessagePacker packer) throws IOException;
MPMap<MPString, MPType<?>> pack() throws IOException;
}
}

View File

@ -3,8 +3,7 @@ package ch.dissem.bitmessage.entity.valueobject.extended;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import org.msgpack.core.MessagePacker;
import org.msgpack.core.MessageUnpacker;
import ch.dissem.msgpack.types.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -15,6 +14,10 @@ import java.net.URLConnection;
import java.nio.file.Files;
import java.util.*;
import static ch.dissem.bitmessage.entity.valueobject.extended.Attachment.Disposition.attachment;
import static ch.dissem.bitmessage.utils.Strings.str;
import static ch.dissem.msgpack.types.Utils.mp;
/**
* Extended encoding type 'message'. Properties 'parents' and 'files' not yet supported by PyBitmessage, so they might not work
* properly with future PyBitmessage implementations.
@ -74,45 +77,33 @@ public class Message implements ExtendedEncoding.ExtendedType {
return Objects.hash(subject, body, parents, files);
}
public void pack(MessagePacker packer) throws IOException {
int size = 3;
@Override
public MPMap<MPString, MPType<?>> pack() throws IOException {
MPMap<MPString, MPType<?>> result = new MPMap<>();
result.put(mp(""), mp(TYPE));
result.put(mp("subject"), mp(subject));
result.put(mp("body"), mp(body));
if (!files.isEmpty()) {
size++;
}
if (!parents.isEmpty()) {
size++;
}
packer.packMapHeader(size);
packer.packString("");
packer.packString(TYPE);
packer.packString("subject");
packer.packString(subject);
packer.packString("body");
packer.packString(body);
if (!files.isEmpty()) {
packer.packString("files");
packer.packArrayHeader(files.size());
MPArray<MPMap<MPString, MPType<?>>> items = new MPArray<>();
result.put(mp("files"), items);
for (Attachment file : files) {
packer.packMapHeader(4);
packer.packString("name");
packer.packString(file.getName());
packer.packString("data");
packer.packBinaryHeader(file.getData().length);
packer.writePayload(file.getData());
packer.packString("type");
packer.packString(file.getType());
packer.packString("disposition");
packer.packString(file.getDisposition().name());
MPMap<MPString, MPType<?>> item = new MPMap<>();
item.put(mp("name"), mp(file.getName()));
item.put(mp("data"), mp(file.getData()));
item.put(mp("type"), mp(file.getType()));
item.put(mp("disposition"), mp(file.getDisposition().name()));
items.add(item);
}
}
if (!parents.isEmpty()) {
packer.packString("parents");
packer.packArrayHeader(parents.size());
MPArray<MPBinary> items = new MPArray<>();
result.put(mp("parents"), items);
for (InventoryVector parent : parents) {
packer.packBinaryHeader(parent.getHash().length);
packer.writePayload(parent.getHash());
items.add(mp(parent.getHash()));
}
}
return result;
}
public static class Builder {
@ -185,86 +176,44 @@ public class Message implements ExtendedEncoding.ExtendedType {
}
@Override
public Message unpack(MessageUnpacker unpacker, int size) {
public Message unpack(MPMap<MPString, MPType<?>> map) {
Message.Builder builder = new Message.Builder();
try {
for (int i = 0; i < size; i++) {
String key = unpacker.unpackString();
switch (key) {
case "subject":
builder.subject(unpacker.unpackString());
break;
case "body":
builder.body(unpacker.unpackString());
break;
case "parents":
builder.parents = unpackParents(unpacker);
break;
case "files":
builder.files = unpackFiles(unpacker);
break;
default:
LOG.error("Unexpected data with key: " + key);
break;
}
builder.subject(str(map.get(mp("subject"))));
builder.body(str(map.get(mp("body"))));
@SuppressWarnings("unchecked")
MPArray<MPBinary> parents = (MPArray<MPBinary>) map.get(mp("parents"));
if (parents != null) {
for (MPBinary parent : parents) {
builder.addParent(new InventoryVector(parent.getValue()));
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
@SuppressWarnings("unchecked")
MPArray<MPMap<MPString, MPType<?>>> files = (MPArray<MPMap<MPString, MPType<?>>>) map.get(mp("files"));
if (files != null) {
for (MPMap<MPString, MPType<?>> item : files) {
Attachment.Builder b = new Attachment.Builder();
b.name(str(item.get(mp("name"))));
b.data(bin(item.get(mp("data"))));
b.type(str(item.get(mp("type"))));
String disposition = str(item.get(mp("disposition")));
if ("inline".equals(disposition)) {
b.inline();
} else if ("attachment".equals(disposition)) {
b.attachment();
}
builder.addFile(b.build());
}
}
return new Message(builder);
}
private static List<InventoryVector> unpackParents(MessageUnpacker unpacker) throws IOException {
int size = unpacker.unpackArrayHeader();
List<InventoryVector> parents = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
int binarySize = unpacker.unpackBinaryHeader();
parents.add(new InventoryVector(unpacker.readPayload(binarySize)));
private byte[] bin(MPType data) {
if (data instanceof MPBinary) {
return ((MPBinary) data).getValue();
} else {
return null;
}
return parents;
}
private static List<Attachment> unpackFiles(MessageUnpacker unpacker) throws IOException {
int size = unpacker.unpackArrayHeader();
List<Attachment> files = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
Attachment.Builder attachment = new Attachment.Builder();
int mapSize = unpacker.unpackMapHeader();
for (int j = 0; j < mapSize; j++) {
String key = unpacker.unpackString();
switch (key) {
case "name":
attachment.name(unpacker.unpackString());
break;
case "data":
int binarySize = unpacker.unpackBinaryHeader();
attachment.data(unpacker.readPayload(binarySize));
break;
case "type":
attachment.type(unpacker.unpackString());
break;
case "disposition":
String disposition = unpacker.unpackString();
switch (disposition) {
case "inline":
attachment.inline();
break;
case "attachment":
attachment.attachment();
break;
default:
LOG.debug("Unknown disposition: " + disposition);
break;
}
break;
default:
LOG.debug("Unknown file info '" + key + "' with data: " + unpacker.unpackValue());
break;
}
}
files.add(attachment.build());
}
return files;
}
}
}

View File

@ -3,20 +3,19 @@ package ch.dissem.bitmessage.entity.valueobject.extended;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import org.msgpack.core.MessagePacker;
import org.msgpack.core.MessageUnpacker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.dissem.msgpack.types.*;
import java.io.IOException;
import java.util.Objects;
import static ch.dissem.bitmessage.utils.Strings.str;
import static ch.dissem.msgpack.types.Utils.mp;
/**
* Extended encoding type 'vote'. Specification still outstanding, so this will need some work.
*/
public class Vote implements ExtendedEncoding.ExtendedType {
private static final long serialVersionUID = -8427038604209964837L;
private static final Logger LOG = LoggerFactory.getLogger(Vote.class);
public static final String TYPE = "vote";
@ -55,15 +54,13 @@ public class Vote implements ExtendedEncoding.ExtendedType {
return Objects.hash(msgId, vote);
}
public void pack(MessagePacker packer) throws IOException {
packer.packMapHeader(3);
packer.packString("");
packer.packString(TYPE);
packer.packString("msgId");
packer.packBinaryHeader(msgId.getHash().length);
packer.writePayload(msgId.getHash());
packer.packString("vote");
packer.packString(vote);
@Override
public MPMap<MPString, MPType<?>> pack() throws IOException {
MPMap<MPString, MPType<?>> result = new MPMap<>();
result.put(mp(""), mp(TYPE));
result.put(mp("msgId"), mp(msgId.getHash()));
result.put(mp("vote"), mp(vote));
return result;
}
public static class Builder {
@ -104,27 +101,13 @@ public class Vote implements ExtendedEncoding.ExtendedType {
}
@Override
public Vote unpack(MessageUnpacker unpacker, int size) {
public Vote unpack(MPMap<MPString, MPType<?>> map) {
Vote.Builder builder = new Vote.Builder();
try {
for (int i = 0; i < size; i++) {
String key = unpacker.unpackString();
switch (key) {
case "msgId":
int binarySize = unpacker.unpackBinaryHeader();
builder.msgId(new InventoryVector(unpacker.readPayload(binarySize)));
break;
case "vote":
builder.vote(unpacker.unpackString());
break;
default:
LOG.error("Unexpected data with key: " + key);
break;
}
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
MPType<?> msgId = map.get(mp("msgId"));
if (msgId instanceof MPBinary) {
builder.msgId(new InventoryVector(((MPBinary) msgId).getValue()));
}
builder.vote(str(map.get(mp("vote"))));
return new Vote(builder);
}
}

View File

@ -4,8 +4,10 @@ import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
import ch.dissem.bitmessage.entity.valueobject.extended.Message;
import ch.dissem.bitmessage.entity.valueobject.extended.Vote;
import ch.dissem.bitmessage.exception.ApplicationException;
import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageUnpacker;
import ch.dissem.msgpack.Reader;
import ch.dissem.msgpack.types.MPMap;
import ch.dissem.msgpack.types.MPString;
import ch.dissem.msgpack.types.MPType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -15,6 +17,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.zip.InflaterInputStream;
import static ch.dissem.bitmessage.utils.Strings.str;
/**
* Factory that creates {@link ExtendedEncoding} objects from byte arrays. You can register your own types by adding a
* {@link ExtendedEncoding.Unpacker} using {@link #registerFactory(ExtendedEncoding.Unpacker)}.
@ -22,7 +26,7 @@ import java.util.zip.InflaterInputStream;
public class ExtendedEncodingFactory {
private static final Logger LOG = LoggerFactory.getLogger(ExtendedEncodingFactory.class);
private static final ExtendedEncodingFactory INSTANCE = new ExtendedEncodingFactory();
private static final String KEY_MESSAGE_TYPE = "";
private static final MPString KEY_MESSAGE_TYPE = new MPString("");
private Map<String, ExtendedEncoding.Unpacker<?>> factories = new HashMap<>();
private ExtendedEncodingFactory() {
@ -37,17 +41,17 @@ public class ExtendedEncodingFactory {
public ExtendedEncoding unzip(byte[] zippedData) {
try (InflaterInputStream unzipper = new InflaterInputStream(new ByteArrayInputStream(zippedData))) {
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(unzipper);
int mapSize = unpacker.unpackMapHeader();
String key = unpacker.unpackString();
if (!KEY_MESSAGE_TYPE.equals(key)) {
LOG.error("Unexpected content: " + key);
Reader reader = Reader.getInstance();
@SuppressWarnings("unchecked")
MPMap<MPString, MPType<?>> map = (MPMap<MPString, MPType<?>>) reader.read(unzipper);
MPType<?> messageType = map.get(KEY_MESSAGE_TYPE);
if (messageType == null) {
LOG.error("Missing message type");
return null;
}
String type = unpacker.unpackString();
ExtendedEncoding.Unpacker<?> factory = factories.get(type);
return new ExtendedEncoding(factory.unpack(unpacker, mapSize - 1));
} catch (IOException e) {
ExtendedEncoding.Unpacker<?> factory = factories.get(str(messageType));
return new ExtendedEncoding(factory.unpack(map));
} catch (ClassCastException | IOException e) {
throw new ApplicationException(e);
}
}

View File

@ -37,4 +37,8 @@ public class Strings {
}
return hex;
}
public static String str(Object o) {
return o == null ? null : o.toString();
}
}