diff --git a/pom.xml b/pom.xml
index 4fbe583..23a7968 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.gossiperl
gossiperl-core
- 1.0.0
+ 2.0.0
jar
@@ -14,6 +14,9 @@
0.9.2
3.0
2.7
+ 4.12
+ 1.7.7
+ 1.2.16
@@ -48,6 +51,31 @@
libthrift
${thrift.version}
+
+ junit
+ junit
+ ${junit.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${slf4j-api.version}
+
+
+ org.slf4j
+ jcl-over-slf4j
+ ${slf4j-api.version}
+
+
+ org.slf4j
+ slf4j-log4j12
+ ${slf4j-api.version}
+
+
+ log4j
+ log4j
+ ${log4j.version}
+
\ No newline at end of file
diff --git a/src/main/java/com/gossiperl/client/Util.java b/src/main/java/com/gossiperl/client/Util.java
new file mode 100644
index 0000000..0bbb481
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/Util.java
@@ -0,0 +1,7 @@
+package com.gossiperl.client;
+
+public class Util {
+ public static long getTimestamp() {
+ return (long)System.currentTimeMillis()/1000;
+ }
+}
diff --git a/src/main/java/com/gossiperl/client/encryption/Aes256.java b/src/main/java/com/gossiperl/client/encryption/Aes256.java
new file mode 100644
index 0000000..b6b32f6
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/encryption/Aes256.java
@@ -0,0 +1,58 @@
+package com.gossiperl.client.encryption;
+
+import org.apache.log4j.Logger;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.*;
+import java.security.*;
+
+public class Aes256 {
+
+ private SecretKeySpec key;
+ private static Logger LOG = Logger.getLogger(Aes256.class);
+
+ public Aes256(String key) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ byte[] digestBytes = md.digest(key.getBytes("utf-8"));
+ this.key = new SecretKeySpec(digestBytes, "AES");
+ }
+
+ public byte[] encrypt(byte[] data) throws NoSuchAlgorithmException,
+ NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
+ IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
+ byte[] ivBytes = generateIv();
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.ENCRYPT_MODE, this.key, new IvParameterSpec(ivBytes));
+ try {
+ byte[] encrypted = cipher.doFinal(data);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ outputStream.write(ivBytes);
+ outputStream.write(encrypted);
+ return outputStream.toByteArray();
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ public byte[] decrypt(byte[] data) throws NoSuchAlgorithmException,
+ NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException,
+ IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
+ byte[] ivBytes = new byte[16];
+ byte[] message = new byte[ data.length - 16 ];
+ System.arraycopy(data, 0, ivBytes, 0, ivBytes.length);
+ System.arraycopy(data, ivBytes.length, message, 0, message.length);
+ Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
+ cipher.init(Cipher.DECRYPT_MODE, this.key, new IvParameterSpec(ivBytes));
+ return cipher.doFinal(message);
+ }
+
+ private byte[] generateIv() {
+ SecureRandom random = new SecureRandom();
+ byte[] ivBytes = new byte[16];
+ random.nextBytes(ivBytes);
+ return ivBytes;
+ }
+
+}
diff --git a/src/main/java/com/gossiperl/client/exceptions/GossiperlClientException.java b/src/main/java/com/gossiperl/client/exceptions/GossiperlClientException.java
new file mode 100644
index 0000000..877ce9c
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/exceptions/GossiperlClientException.java
@@ -0,0 +1,8 @@
+package com.gossiperl.client.exceptions;
+
+public class GossiperlClientException extends Exception {
+ public GossiperlClientException() {}
+ public GossiperlClientException(String message) { super(message); }
+ public GossiperlClientException(Throwable cause) { super(cause); }
+ public GossiperlClientException(String message, Throwable cause) { super(message, cause); }
+}
diff --git a/src/main/java/com/gossiperl/client/exceptions/GossiperlUnsupportedSerializableTypeException.java b/src/main/java/com/gossiperl/client/exceptions/GossiperlUnsupportedSerializableTypeException.java
new file mode 100644
index 0000000..907f3a1
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/exceptions/GossiperlUnsupportedSerializableTypeException.java
@@ -0,0 +1,5 @@
+package com.gossiperl.client.exceptions;
+
+public class GossiperlUnsupportedSerializableTypeException extends Exception {
+ public GossiperlUnsupportedSerializableTypeException(String message) { super(message); }
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/CustomDigestField.java b/src/main/java/com/gossiperl/client/serialization/CustomDigestField.java
new file mode 100644
index 0000000..51547f1
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/CustomDigestField.java
@@ -0,0 +1,106 @@
+package com.gossiperl.client.serialization;
+
+import com.gossiperl.client.exceptions.GossiperlClientException;
+import org.apache.thrift.protocol.TType;
+
+public class CustomDigestField {
+
+ private String fieldName;
+ private Object value;
+ private byte type;
+ private short fieldOrder;
+
+ public CustomDigestField(String fieldName, String value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = value;
+ this.type = TType.STRING;
+ }
+
+ public CustomDigestField(String fieldName, boolean value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = new Boolean(value);
+ this.type = TType.BOOL;
+ }
+
+ public CustomDigestField(String fieldName, byte value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = new Byte(value);
+ this.type = TType.BYTE;
+ }
+
+ public CustomDigestField(String fieldName, double value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = new Double(value);
+ this.type = TType.DOUBLE;
+ }
+
+ public CustomDigestField(String fieldName, short value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = new Short(value);
+ this.type = TType.I16;
+ }
+
+ public CustomDigestField(String fieldName, int value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = new Integer(value);
+ this.type = TType.I32;
+ }
+
+ public CustomDigestField(String fieldName, long value, int fieldOrder)
+ throws GossiperlClientException {
+ if ( fieldOrder < 0 || fieldOrder > Short.MAX_VALUE ) {
+ throw new GossiperlClientException("Field ID must be at least 0 and no greater than " + Short.MAX_VALUE + ".");
+ }
+ this.fieldName = fieldName;
+ this.fieldOrder = (short)fieldOrder;
+ this.value = new Long(value);
+ this.type = TType.I64;
+ }
+
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public byte getType() {
+ return type;
+ }
+
+ public short getFieldOrder() {
+ return fieldOrder;
+ }
+
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/DeserializeKillPill.java b/src/main/java/com/gossiperl/client/serialization/DeserializeKillPill.java
new file mode 100644
index 0000000..22f86d8
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/DeserializeKillPill.java
@@ -0,0 +1,7 @@
+package com.gossiperl.client.serialization;
+
+/**
+ * Created by rad on 18/12/14.
+ */
+public class DeserializeKillPill extends DeserializeResult {
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/DeserializeResult.java b/src/main/java/com/gossiperl/client/serialization/DeserializeResult.java
new file mode 100644
index 0000000..bc2fd85
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/DeserializeResult.java
@@ -0,0 +1,4 @@
+package com.gossiperl.client.serialization;
+
+public abstract class DeserializeResult {
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/DeserializeResultCustomOK.java b/src/main/java/com/gossiperl/client/serialization/DeserializeResultCustomOK.java
new file mode 100644
index 0000000..b78d932
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/DeserializeResultCustomOK.java
@@ -0,0 +1,18 @@
+package com.gossiperl.client.serialization;
+
+import java.util.Map;
+
+public class DeserializeResultCustomOK extends DeserializeResult {
+ private String digestType;
+ private Map resultData;
+ public DeserializeResultCustomOK(String digestType, Map resultData) {
+ this.digestType = digestType;
+ this.resultData = resultData;
+ }
+ public String getDigestType() {
+ return this.digestType;
+ }
+ public Map getResultData() {
+ return this.resultData;
+ }
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/DeserializeResultError.java b/src/main/java/com/gossiperl/client/serialization/DeserializeResultError.java
new file mode 100644
index 0000000..ea211bb
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/DeserializeResultError.java
@@ -0,0 +1,13 @@
+package com.gossiperl.client.serialization;
+
+import com.gossiperl.client.exceptions.GossiperlClientException;
+
+public class DeserializeResultError extends DeserializeResult {
+ private GossiperlClientException cause;
+ public DeserializeResultError(GossiperlClientException ex) {
+ this.cause = ex;
+ }
+ public GossiperlClientException getCause() {
+ return this.cause;
+ }
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/DeserializeResultForward.java b/src/main/java/com/gossiperl/client/serialization/DeserializeResultForward.java
new file mode 100644
index 0000000..4af1b07
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/DeserializeResultForward.java
@@ -0,0 +1,29 @@
+package com.gossiperl.client.serialization;
+
+import com.gossiperl.client.thrift.DigestEnvelope;
+import org.apache.thrift.TBase;
+
+public class DeserializeResultForward extends DeserializeResult {
+
+ private String digestType;
+ private byte[] binaryEnvelope;
+ private String envelopeId;
+
+ public DeserializeResultForward(String digestType, byte[] binaryEnvelope, String envelopeId) {
+ this.digestType = digestType;
+ this.binaryEnvelope = binaryEnvelope;
+ this.envelopeId = envelopeId;
+ }
+
+ public String getDigestType() {
+ return digestType;
+ }
+
+ public byte[] getBinaryEnvelope() {
+ return binaryEnvelope;
+ }
+
+ public String getEnvelopeId() {
+ return envelopeId;
+ }
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/DeserializeResultOK.java b/src/main/java/com/gossiperl/client/serialization/DeserializeResultOK.java
new file mode 100644
index 0000000..4111e22
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/DeserializeResultOK.java
@@ -0,0 +1,18 @@
+package com.gossiperl.client.serialization;
+
+import org.apache.thrift.TBase;
+
+public class DeserializeResultOK extends DeserializeResult {
+ private String digestType;
+ private TBase digest;
+ public DeserializeResultOK(String digestType, TBase digest) {
+ this.digestType = digestType;
+ this.digest = digest;
+ }
+ public String getDigestType() {
+ return this.digestType;
+ }
+ public TBase getDigest() {
+ return this.digest;
+ }
+}
diff --git a/src/main/java/com/gossiperl/client/serialization/Serializer.java b/src/main/java/com/gossiperl/client/serialization/Serializer.java
new file mode 100644
index 0000000..526ad45
--- /dev/null
+++ b/src/main/java/com/gossiperl/client/serialization/Serializer.java
@@ -0,0 +1,237 @@
+package com.gossiperl.client.serialization;
+
+import com.gossiperl.client.exceptions.GossiperlClientException;
+import com.gossiperl.client.thrift.DigestEnvelope;
+import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
+import org.apache.thrift.TBase;
+import org.apache.thrift.TException;
+import org.apache.thrift.TSerializer;
+import org.apache.thrift.protocol.*;
+import org.apache.thrift.transport.TIOStreamTransport;
+import org.apache.thrift.transport.TMemoryInputTransport;
+import org.apache.thrift.transport.TTransport;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+
+public class Serializer {
+
+ private HashMap types = new HashMap();
+
+ public static final String DIGEST_ERROR = "digestError";
+ public static final String DIGEST_FORWARDED_ACK = "digestForwardedAck";
+ public static final String DIGEST_ENVELOPE = "digestEnvelope";
+ public static final String DIGEST = "digest";
+ public static final String DIGEST_ACK = "digestAck";
+ public static final String DIGEST_SUBSCRIPTIONS = "digestSubscriptions";
+ public static final String DIGEST_EXIT = "digestExit";
+ public static final String DIGEST_SUBSCRIBE = "digestSubscribe";
+ public static final String DIGEST_SUBSCRIBE_ACK = "digestSubscribeAck";
+ public static final String DIGEST_UNSUBSCRIBE = "digestUnsubscribe";
+ public static final String DIGEST_UNSUBSCRIBE_ACK = "digestUnsubscribeAck";
+ public static final String DIGEST_EVENT = "digestEvent";
+
+ public Serializer() {
+ this.types.put(DIGEST_ERROR, com.gossiperl.client.thrift.DigestError.class);
+ this.types.put(DIGEST_FORWARDED_ACK, com.gossiperl.client.thrift.DigestForwardedAck.class);
+ this.types.put(DIGEST_ENVELOPE, com.gossiperl.client.thrift.DigestEnvelope.class);
+ this.types.put(DIGEST, com.gossiperl.client.thrift.Digest.class);
+ this.types.put(DIGEST_ACK, com.gossiperl.client.thrift.DigestAck.class);
+ this.types.put(DIGEST_SUBSCRIPTIONS, com.gossiperl.client.thrift.DigestSubscriptions.class);
+ this.types.put(DIGEST_EXIT, com.gossiperl.client.thrift.DigestExit.class);
+ this.types.put(DIGEST_SUBSCRIBE, com.gossiperl.client.thrift.DigestSubscribe.class);
+ this.types.put(DIGEST_SUBSCRIBE_ACK, com.gossiperl.client.thrift.DigestSubscribeAck.class);
+ this.types.put(DIGEST_UNSUBSCRIBE, com.gossiperl.client.thrift.DigestUnsubscribe.class);
+ this.types.put(DIGEST_UNSUBSCRIBE_ACK, com.gossiperl.client.thrift.DigestUnsubscribeAck.class);
+ this.types.put(DIGEST_EVENT, com.gossiperl.client.thrift.DigestEvent.class);
+
+
+ }
+
+ public byte[] serializeArbitrary(String digestType, List digestData) throws TException, GossiperlClientException {
+ return this.serializeArbitrary( digestType, digestData, 1024 );
+ }
+
+ public byte[] serializeArbitrary(String digestType, List digestData, int thriftWindowSize)
+ throws TException, GossiperlClientException {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream( thriftWindowSize );
+ TTransport transport = new TIOStreamTransport( stream );
+ TProtocol protocol = new TBinaryProtocol(transport);
+ protocol.writeStructBegin(new TStruct( digestType ));
+ for (CustomDigestField field : digestData) {
+ protocol.writeFieldBegin( new TField( field.getFieldName(), field.getType(), field.getFieldOrder() ));
+ if ( field.getType() == TType.BOOL ) {
+ try {
+ protocol.writeBool( ((Boolean)field.getValue()).booleanValue() );
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as BOOL.", ex);
+ }
+ } else if ( field.getType() == TType.BYTE ) {
+ try {
+ protocol.writeByte(((Byte) field.getValue()).byteValue());
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as BYTE.", ex);
+ }
+ } else if ( field.getType() == TType.DOUBLE ) {
+ try {
+ protocol.writeDouble(((Double) field.getValue()).doubleValue());
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as DOUBLE.", ex);
+ }
+ } else if ( field.getType() == TType.I16 ) {
+ try {
+ protocol.writeI16(((Long) field.getValue()).shortValue());
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as I16.", ex);
+ }
+ } else if ( field.getType() == TType.I32 ) {
+ try {
+ protocol.writeI32(((Long) field.getValue()).intValue());
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as I32.", ex);
+ }
+ } else if ( field.getType() == TType.I64 ) {
+ try {
+ protocol.writeI64(((Long)field.getValue()).longValue());
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as I64.", ex);
+ }
+ } else if ( field.getType() == TType.STRING ) {
+ try {
+ protocol.writeString((String) field.getValue());
+ } catch (Exception ex) {
+ throw new GossiperlClientException("Failed to write value " + field.getValue().toString() + " as STRING.", ex);
+ }
+ }
+ protocol.writeFieldEnd();
+ }
+ protocol.writeFieldStop();
+ protocol.writeStructEnd();
+ DigestEnvelope envelope = new DigestEnvelope();
+ envelope.setPayload_type( digestType );
+ envelope.setBin_payload(Base64.encode( stream.toByteArray() ));
+ envelope.setId(UUID.randomUUID().toString());
+ return digestToBinary( envelope );
+ }
+
+ public byte[] serialize(org.apache.thrift.TBase digest) throws GossiperlClientException,
+ UnsupportedEncodingException {
+ String digestType = this.getDigestName( digest );
+ if ( digestType.equals(DIGEST_ENVELOPE) ) {
+ return digestToBinary( digest );
+ }
+ DigestEnvelope envelope = new DigestEnvelope();
+ envelope.setPayload_type( digestType );
+ envelope.setBin_payload(Base64.encode(digestToBinary(digest)));
+ envelope.setId(UUID.randomUUID().toString());
+ return digestToBinary( envelope );
+ }
+
+ public DeserializeResult deserializeArbitrary(String digestType, byte[] binDigest, List digestInfo)
+ throws GossiperlClientException, UnsupportedEncodingException, TException {
+ DigestEnvelope envelope = (DigestEnvelope)digestFromBinary( DIGEST_ENVELOPE, binDigest );
+
+ byte[] digest = Base64.decode(envelope.getBin_payload());
+ TTransport transport = new TIOStreamTransport(new ByteArrayInputStream( digest ));
+ TBinaryProtocol protocol = new TBinaryProtocol( transport );
+ protocol.readStructBegin();
+ HashMap< String, Object > result = new HashMap();
+ for (int i=0; i digestInfo ) {
+ for ( int i=0; i digestInfo;
+
+ public void setUp() {
+ this.serializer = new Serializer();
+ this.digestType = "someDigestType";
+ this.digestInfo = new ArrayList();
+ try {
+ this.digestInfo.add(new CustomDigestField("some_property", "this is some string value to test", 1));
+ this.digestInfo.add(new CustomDigestField("some_other_property", 1234L, 2));
+ } catch (Exception ex) {
+ System.out.println("[Serialization test]: Error in set up.");
+ ex.printStackTrace();
+ }
+ }
+
+ public void tearDown() {
+ }
+
+ public void testSerializeDeserialize() throws Exception {
+ byte[] envelope = this.serializer.serializeArbitrary( this.digestType, this.digestInfo );
+ DeserializeResult result = this.serializer.deserializeArbitrary( this.digestType, envelope, this.digestInfo );
+ assertTrue(result.getClass().getName().equals(DeserializeResultCustomOK.class.getName()));
+ DeserializeResultCustomOK finalResult = (DeserializeResultCustomOK)result;
+ assertEquals(this.digestType, finalResult.getDigestType());
+ assertTrue( finalResult.getResultData().containsKey("some_property") );
+ assertTrue( finalResult.getResultData().containsKey("some_other_property") );
+ }
+
+}