diff --git a/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateOperation.java b/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateOperation.java
index 7c29c1f..b97f230 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateOperation.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/AccountUpdateOperation.java
@@ -8,7 +8,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
- * Class used to encapsulate operations related to the account_update_operation.
+ * Class used to encapsulate operations related to the ACCOUNT_UPDATE_OPERATION.
*/
public class AccountUpdateOperation extends BaseOperation {
public static final String KEY_ACCOUNT = "account";
@@ -34,7 +34,7 @@ public class AccountUpdateOperation extends BaseOperation {
* @param fee The fee to pay. Can be null.
*/
public AccountUpdateOperation(UserAccount account, Authority owner, Authority active, AccountOptions options, AssetAmount fee){
- super(OperationType.account_update_operation);
+ super(OperationType.ACCOUNT_UPDATE_OPERATION);
this.fee = fee;
this.account = account;
this.owner = new Optional<>(owner);
diff --git a/src/main/java/de/bitsharesmunich/graphenej/BlockData.java b/src/main/java/de/bitsharesmunich/graphenej/BlockData.java
index ad789b6..4a65917 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/BlockData.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/BlockData.java
@@ -12,7 +12,7 @@ public class BlockData implements ByteSerializable {
private int refBlockNum;
private long refBlockPrefix;
- private long relativeExpiration;
+ private long expiration;
/**
* Block data constructor
@@ -22,22 +22,20 @@ public class BlockData implements ByteSerializable {
* Recall that block IDs have 32 bits of block number followed by the
* actual block hash, so this field should be set using the second 32 bits
* in the block_id_type
- * @param relative_expiration: This field specifies the number of block intervals after the
- * reference block until this transaction becomes invalid. If this field is
- * set to zero, the "ref_block_prefix" is interpreted as an absolute timestamp
- * of the time the transaction becomes invalid.
+ * @param relative_expiration: Expiration time specified as a POSIX or
+ * Unix time
*/
public BlockData(int ref_block_num, long ref_block_prefix, long relative_expiration){
this.refBlockNum = ref_block_num;
this.refBlockPrefix = ref_block_prefix;
- this.relativeExpiration = relative_expiration;
+ this.expiration = relative_expiration;
}
/**
* Block data constructor that takes in raw blockchain information.
* @param head_block_number: The last block number.
* @param head_block_id: The last block apiId.
- * @param relative_expiration: The relative expiration
+ * @param relative_expiration: The expiration time.
*/
public BlockData(long head_block_number, String head_block_id, long relative_expiration){
String hashData = head_block_id.substring(8, 16);
@@ -47,7 +45,7 @@ public class BlockData implements ByteSerializable {
}
this.setRefBlockNum(head_block_number);
this.setRefBlockPrefix(head_block_id);
- this.relativeExpiration = relative_expiration;
+ this.expiration = relative_expiration;
}
/**
@@ -97,12 +95,12 @@ public class BlockData implements ByteSerializable {
return refBlockPrefix;
}
- public long getRelativeExpiration() {
- return relativeExpiration;
+ public long getExpiration() {
+ return expiration;
}
- public void setRelativeExpiration(long relativeExpiration) {
- this.relativeExpiration = relativeExpiration;
+ public void setExpiration(long expiration) {
+ this.expiration = expiration;
}
@@ -120,7 +118,7 @@ public class BlockData implements ByteSerializable {
}else if(i >= REF_BLOCK_NUM_BYTES && i < REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES){
result[i] = (byte) (this.refBlockPrefix >> 8 * (i - REF_BLOCK_NUM_BYTES));
}else{
- result[i] = (byte) (this.relativeExpiration >> 8 * (i - REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES));
+ result[i] = (byte) (this.expiration >> 8 * (i - REF_BLOCK_NUM_BYTES + REF_BLOCK_PREFIX_BYTES));
}
}
return result;
diff --git a/src/main/java/de/bitsharesmunich/graphenej/GrapheneObject.java b/src/main/java/de/bitsharesmunich/graphenej/GrapheneObject.java
index 52b7151..88f20a4 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/GrapheneObject.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/GrapheneObject.java
@@ -8,6 +8,8 @@ package de.bitsharesmunich.graphenej;
* Created by nelson on 11/8/16.
*/
public class GrapheneObject {
+ public static final String KEY_ID = "id";
+
public static final int PROTOCOL_SPACE = 1;
public static final int IMPLEMENTATION_SPACE = 2;
diff --git a/src/main/java/de/bitsharesmunich/graphenej/Main.java b/src/main/java/de/bitsharesmunich/graphenej/Main.java
index 8789f9d..a56442e 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/Main.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/Main.java
@@ -73,6 +73,7 @@ public class Main {
// test.testAssetSerialization();
// test.testGetMarketHistory();
// test.testGetAccountBalances();
- test.testGetAssetHoldersCount();
+// test.testGetAssetHoldersCount();
+ test.testSubscription(null);
}
}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/OperationType.java b/src/main/java/de/bitsharesmunich/graphenej/OperationType.java
index 2f54488..36d9f52 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/OperationType.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/OperationType.java
@@ -1,52 +1,55 @@
package de.bitsharesmunich.graphenej;
/**
+ * Enum type used to keep track of all the operation types and their corresponding ids.
+ *
+ * Source
+ *
* Created by nelson on 11/6/16.
*/
public enum OperationType {
- transfer_operation,
- limit_order_create_operation,
- limit_order_cancel_operation,
- call_order_update_operation,
- fill_order_operation, // VIRTUAL
- account_create_operation,
- account_update_operation,
- account_whitelist_operation,
- account_upgrade_operation,
- account_transfer_operation,
- asset_create_operation,
- asset_update_operation,
- asset_update_bitasset_operation,
- asset_update_feed_producers_operation,
- asset_issue_operation,
- asset_reserve_operation,
- asset_fund_fee_pool_operation,
- asset_settle_operation,
- asset_global_settle_operation,
- asset_publish_feed_operation,
- witness_create_operation,
- witness_update_operation,
- proposal_create_operation,
- proposal_update_operation,
- proposal_delete_operation,
- withdraw_permission_create_operation,
- withdraw_permission_update_operation,
- withdraw_permission_claim_operation,
- withdraw_permission_delete_operation,
- committee_member_create_operation,
- committee_member_update_operation,
- committee_member_update_global_parameters_operation,
- vesting_balance_create_operation,
- vesting_balance_withdraw_operation,
- worker_create_operation,
- custom_operation,
- assert_operation,
- balance_claim_operation,
- override_transfer_operation,
- transfer_to_blind_operation,
- blind_transfer_operation,
- transfer_from_blind_operation,
- asset_settle_cancel_operation, // VIRTUAL
- asset_claim_fees_operation,
- fba_distribute_operation // VIRTUAL
+ TRANSFER_OPERATION,
+ LIMIT_ORDER_CREATE_OPERATION,
+ LIMIT_ORDER_CANCEL_OPERATION,
+ CALL_ORDER_UPDATE_OPERATION,
+ FILL_ORDER_OPERATION, // VIRTUAL
+ ACCOUNT_CREATE_OPERATION,
+ ACCOUNT_UPDATE_OPERATION,
+ ACCOUNT_WHITELIST_OPERATION,
+ ACCOUNT_UPGRADE_OPERATION,
+ ACCOUNT_TRANSFER_OPERATION,
+ ASSET_CREATE_OPERATION,
+ ASSET_UPDATE_OPERATION,
+ ASSET_UPDATE_BITASSET_OPERATION,
+ ASSET_UPDATE_FEED_PRODUCERS_OPERATION,
+ ASSET_ISSUE_OPERATION,
+ ASSET_RESERVE_OPERATION,
+ ASSET_FUND_FEE_POOL_OPERATION,
+ ASSET_SETTLE_OPERATION,
+ ASSET_GLOBAL_SETTLE_OPERATION,
+ ASSET_PUBLISH_FEED_OPERATION,
+ WITNESS_CREATE_OPERATION,
+ WITNESS_UPDATE_OPERATION,
+ PROPOSAL_CREATE_OPERATION,
+ PROPOSAL_UPDATE_OPERATION,
+ PROPOSAL_DELETE_OPERATION,
+ WITHDRAW_PERMISSION_CREATE_OPERATION,
+ WITHDRAW_PERMISSION_UPDATE_OPERATION,
+ WITHDRAW_PERMISSION_CLAIM_OPERATION,
+ WITHDRAW_PERMISSION_DELETE_OPERATION,
+ COMMITTEE_MEMBER_CREATE_OPERATION,
+ COMMITTEE_MEMBER_UPDATE_OPERATION,
+ COMMITTEE_MEMBER_UPDATE_GLOBAL_PARAMETERS_OPERATION,
+ VESTING_BALANCE_CREATE_OPERATION,
+ VESTING_BALANCE_WITHDRAW_OPERATION,
+ WORKER_CREATE_OPERATION,
+ CUSTOM_OPERATION,
+ ASSERT_OPERATION,
+ BALANCE_CLAIM_OPERATION,
+ OVERRIDE_TRANSFER_OPERATION,
+ TRANSFER_TO_BLIND_OPERATION,
+ BLIND_TRANSFER_OPERATION,
+ TRANSFER_FROM_BLIND_OPERATION,
+ ASSET_SETTLE_CANCEL_OPERATION, // VIRTUAL
+ ASSET_CLAIM_FEES_OPERATION
}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/RPC.java b/src/main/java/de/bitsharesmunich/graphenej/RPC.java
index 7e15568..bf97c50 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/RPC.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/RPC.java
@@ -10,6 +10,7 @@ public class RPC {
public static final String CALL_HISTORY = "history";
public static final String CALL_DATABASE = "database";
public static final String CALL_ASSET = "asset";
+ public static final String CALL_SET_SUBSCRIBE_CALLBACK = "set_subscribe_callback";
public static final String CALL_GET_ACCOUNT_BY_NAME = "get_account_by_name";
public static final String CALL_GET_ACCOUNTS = "get_accounts";
public static final String CALL_GET_DYNAMIC_GLOBAL_PROPERTIES = "get_dynamic_global_properties";
diff --git a/src/main/java/de/bitsharesmunich/graphenej/Test.java b/src/main/java/de/bitsharesmunich/graphenej/Test.java
index f500011..237cc25 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/Test.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/Test.java
@@ -1,5 +1,6 @@
package de.bitsharesmunich.graphenej;
+import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener;
import de.bitsharesmunich.graphenej.models.*;
import de.bitsharesmunich.graphenej.objects.Memo;
import com.google.common.primitives.UnsignedLong;
@@ -14,12 +15,10 @@ import de.bitsharesmunich.graphenej.test.NaiveSSLContext;
import com.neovisionaries.ws.client.*;
import de.bitsharesmunich.graphenej.api.*;
import org.bitcoinj.core.*;
-import org.spongycastle.asn1.x509.Holder;
import org.spongycastle.crypto.digests.RIPEMD160Digest;
import javax.net.ssl.SSLContext;
import java.io.*;
-import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -592,7 +591,7 @@ public class Test {
// Set the custom SSL context.
factory.setSSLContext(context);
- WebSocket mWebSocket = factory.createSocket(OPENLEDGER_WITNESS_URL);
+ WebSocket mWebSocket = factory.createSocket(AMAZON_WITNESS);
mWebSocket.addListener(relativeAccountHistory);
mWebSocket.connect();
} catch (IOException e) {
@@ -1190,8 +1189,8 @@ public class Test {
@Override
public void onSuccess(WitnessResponse response) {
System.out.println("onSuccess");
- List holdersCountList = (List) response.result;
- for(HoldersCount holdersCount : holdersCountList){
+ List holdersCountList = (List) response.result;
+ for(AssetHolderCount holdersCount : holdersCountList){
System.out.println(String.format("Asset %s has %d holders", holdersCount.asset.getObjectId(), holdersCount.count));
}
}
@@ -1221,4 +1220,60 @@ public class Test {
System.out.println("IOException. Msg: " + e.getMessage());
}
}
+
+ public void testSubscription(WitnessResponseListener listener){
+ SSLContext context = null;
+
+ try {
+ context = NaiveSSLContext.getInstance("TLS");
+ WebSocketFactory factory = new WebSocketFactory();
+
+ // Set the custom SSL context.
+ factory.setSSLContext(context);
+
+ WebSocket mWebSocket = factory.createSocket(BLOCK_PAY_DE);
+
+ SubscriptionMessagesHub subscriptionHub = new SubscriptionMessagesHub("", "");
+ mWebSocket.addListener(subscriptionHub);
+ mWebSocket.connect();
+ subscriptionHub.addSubscriptionListener(new SubscriptionListener() {
+ @Override
+ public ObjectType getInterestObjectType() {
+ return ObjectType.TRANSACTION_OBJECT;
+ }
+
+ @Override
+ public void onSubscriptionUpdate(SubscriptionResponse response) {
+ try{
+ List updatedObjects = (List) response.params.get(1);
+ if(updatedObjects.size() > 0){
+ for(Serializable update : updatedObjects){
+ if(update instanceof BroadcastedTransaction){
+ Transaction t = ((BroadcastedTransaction) update).getTransaction();
+ if(t.getOperations().size() > 0){
+ for(BaseOperation op : t.getOperations()){
+ if(op instanceof TransferOperation){
+ System.out.println(String.format("Got transaction from: %s, to: %s", ((TransferOperation) op).getFrom().getObjectId(), ((TransferOperation) op).getTo().getObjectId()));
+ }
+ }
+ }
+ }
+ }
+ }
+ }catch(Exception e){
+ System.out.println("Exception. Msg: "+e.getMessage());
+ for(StackTraceElement el : e.getStackTrace()){
+ System.out.println(el.getFileName()+"#"+el.getMethodName()+":"+el.getLineNumber());
+ }
+ }
+ }
+ });
+ } catch (NoSuchAlgorithmException e) {
+ System.out.println("NoSuchAlgorithmException. Msg: " + e.getMessage());
+ } catch (WebSocketException e) {
+ System.out.println("WebSocketException. Msg: " + e.getMessage());
+ } catch (IOException e) {
+ System.out.println("IOException. Msg: " + e.getMessage());
+ }
+ }
}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/Transaction.java b/src/main/java/de/bitsharesmunich/graphenej/Transaction.java
index 244247c..e7d64cf 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/Transaction.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/Transaction.java
@@ -1,12 +1,7 @@
package de.bitsharesmunich.graphenej;
import com.google.common.primitives.Bytes;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
+import com.google.gson.*;
import de.bitsharesmunich.graphenej.interfaces.ByteSerializable;
import de.bitsharesmunich.graphenej.interfaces.JsonSerializable;
@@ -16,6 +11,7 @@ import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Utils;
import java.lang.reflect.Type;
+import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@@ -26,15 +22,18 @@ import java.util.TimeZone;
* Class used to represent a generic Graphene transaction.
*/
public class Transaction implements ByteSerializable, JsonSerializable {
- private final String TAG = this.getClass().getName();
+ /* Default expiration time */
public static final int DEFAULT_EXPIRATION_TIME = 30;
+
+ /* Constant field names used for serialization/deserialization purposes */
public static final String KEY_EXPIRATION = "expiration";
public static final String KEY_SIGNATURES = "signatures";
public static final String KEY_OPERATIONS = "operations";
public static final String KEY_EXTENSIONS = "extensions";
public static final String KEY_REF_BLOCK_NUM = "ref_block_num";
public static final String KEY_REF_BLOCK_PREFIX = "ref_block_prefix";
+ public static final String TIME_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private ECKey privateKey;
private BlockData blockData;
@@ -64,6 +63,17 @@ public class Transaction implements ByteSerializable, JsonSerializable {
this(DumpedPrivateKey.fromBase58(null, wif).getKey(), block_data, operation_list);
}
+ /**
+ * Constructor used to build a Transaction object without a private key. This kind of object
+ * is used to represent a transaction data that we don't intend to serialize and sign.
+ * @param blockData: Block data instance, containing information about the location of this transaction in the blockchain.
+ * @param operationList: The list of operations included in this transaction.
+ */
+ public Transaction(BlockData blockData, List operationList){
+ this.blockData = blockData;
+ this.operations = operationList;
+ }
+
/**
* Updates the block data
* @param blockData: New block data
@@ -87,6 +97,14 @@ public class Transaction implements ByteSerializable, JsonSerializable {
public List getOperations(){ return this.operations; }
+ /**
+ * This method is used to query whether the instance has a private key.
+ * @return
+ */
+ public boolean hasPrivateKey(){
+ return this.privateKey != null;
+ }
+
/**
* Obtains a signature of this transaction. Please note that due to the current reliance on
* bitcoinj to generate the signatures, and due to the fact that it uses deterministic
@@ -125,7 +143,7 @@ public class Transaction implements ByteSerializable, JsonSerializable {
if(((sigData[0] & 0x80) != 0) || (sigData[0] == 0) ||
((sigData[1] & 0x80) != 0) || ((sigData[32] & 0x80) != 0) ||
(sigData[32] == 0) || ((sigData[33] & 0x80) != 0)){
- this.blockData.setRelativeExpiration(this.blockData.getRelativeExpiration() + 1);
+ this.blockData.setExpiration(this.blockData.getExpiration() + 1);
}else{
isGrapheneCanonical = true;
}
@@ -186,8 +204,8 @@ public class Transaction implements ByteSerializable, JsonSerializable {
byte[] signature = getGrapheneSignature();
// Formatting expiration time
- Date expirationTime = new Date(blockData.getRelativeExpiration() * 1000);
- SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ Date expirationTime = new Date(blockData.getExpiration() * 1000);
+ SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
// Adding expiration
@@ -216,11 +234,144 @@ public class Transaction implements ByteSerializable, JsonSerializable {
}
- class TransactionSerializer implements JsonSerializer {
+ /**
+ * Class used to encapsulate the procedure to be followed when converting a transaction from a
+ * java object to its JSON string format representation.
+ */
+ public static class TransactionSerializer implements JsonSerializer {
@Override
public JsonElement serialize(Transaction transaction, Type type, JsonSerializationContext jsonSerializationContext) {
return transaction.toJsonObject();
}
}
+
+ /**
+ * Static inner class used to encapsulate the procedure to be followed when converting a transaction from its
+ * JSON string format representation into a java object instance.
+ */
+ public static class TransactionDeserializer implements JsonDeserializer {
+
+ @Override
+ public Transaction deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject jsonObject = json.getAsJsonObject();
+
+ // Parsing block data information
+ int refBlockNum = jsonObject.get(KEY_REF_BLOCK_NUM).getAsInt();
+ long refBlockPrefix = jsonObject.get(KEY_REF_BLOCK_PREFIX).getAsLong();
+ String expiration = jsonObject.get(KEY_EXPIRATION).getAsString();
+ SimpleDateFormat dateFormat = new SimpleDateFormat(TIME_DATE_FORMAT);
+ dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ Date expirationDate = dateFormat.parse(expiration, new ParsePosition(0));
+ BlockData blockData = new BlockData(refBlockNum, refBlockPrefix, expirationDate.getTime());
+
+ // Parsing operation list
+ BaseOperation operation = null;
+ ArrayList operationList = new ArrayList<>();
+ try {
+ for (JsonElement jsonOperation : jsonObject.get(KEY_OPERATIONS).getAsJsonArray()) {
+ int operationId = jsonOperation.getAsJsonArray().get(0).getAsInt();
+ if (operationId == OperationType.TRANSFER_OPERATION.ordinal()) {
+ System.out.println("Transfer operation detected!");
+ operation = context.deserialize(jsonOperation, TransferOperation.class);
+ } else if (operationId == OperationType.LIMIT_ORDER_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.LIMIT_ORDER_CANCEL_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.CALL_ORDER_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.FILL_ORDER_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ACCOUNT_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ACCOUNT_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ACCOUNT_WHITELIST_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ACCOUNT_UPGRADE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ACCOUNT_TRANSFER_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_UPDATE_BITASSET_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_UPDATE_FEED_PRODUCERS_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_ISSUE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_RESERVE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_FUND_FEE_POOL_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_SETTLE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_GLOBAL_SETTLE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_PUBLISH_FEED_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WITNESS_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WITNESS_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.PROPOSAL_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.PROPOSAL_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.PROPOSAL_DELETE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WITHDRAW_PERMISSION_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WITHDRAW_PERMISSION_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WITHDRAW_PERMISSION_CLAIM_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WITHDRAW_PERMISSION_DELETE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.COMMITTEE_MEMBER_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.COMMITTEE_MEMBER_UPDATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.COMMITTEE_MEMBER_UPDATE_GLOBAL_PARAMETERS_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.VESTING_BALANCE_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.VESTING_BALANCE_WITHDRAW_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.WORKER_CREATE_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.CUSTOM_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSERT_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.BALANCE_CLAIM_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.OVERRIDE_TRANSFER_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.TRANSFER_TO_BLIND_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.BLIND_TRANSFER_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.TRANSFER_FROM_BLIND_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_SETTLE_CANCEL_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ } else if (operationId == OperationType.ASSET_CLAIM_FEES_OPERATION.ordinal()) {
+ //TODO: Add operation deserialization support
+ }
+ if (operation != null) operationList.add(operation);
+ operation = null;
+ }
+ return new Transaction(blockData, operationList);
+ }catch(Exception e){
+ System.out.println("Exception. Msg: "+e.getMessage());
+ for(StackTraceElement el : e.getStackTrace()){
+ System.out.println(el.getFileName()+"#"+el.getMethodName()+":"+el.getLineNumber());
+ }
+ }
+ return new Transaction(blockData, operationList);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/de/bitsharesmunich/graphenej/TransferOperation.java b/src/main/java/de/bitsharesmunich/graphenej/TransferOperation.java
index 967afc1..bc0bae1 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/TransferOperation.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/TransferOperation.java
@@ -26,7 +26,7 @@ public class TransferOperation extends BaseOperation {
private String[] extensions;
public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount, AssetAmount fee){
- super(OperationType.transfer_operation);
+ super(OperationType.TRANSFER_OPERATION);
this.from = from;
this.to = to;
this.amount = transferAmount;
@@ -35,7 +35,7 @@ public class TransferOperation extends BaseOperation {
}
public TransferOperation(UserAccount from, UserAccount to, AssetAmount transferAmount){
- super(OperationType.transfer_operation);
+ super(OperationType.TRANSFER_OPERATION);
this.from = from;
this.to = to;
this.amount = transferAmount;
@@ -144,7 +144,7 @@ public class TransferOperation extends BaseOperation {
// This block is used just to check if we are in the first step of the deserialization
// when we are dealing with an array.
JsonArray serializedTransfer = json.getAsJsonArray();
- if(serializedTransfer.get(0).getAsInt() != OperationType.transfer_operation.ordinal()){
+ if(serializedTransfer.get(0).getAsInt() != OperationType.TRANSFER_OPERATION.ordinal()){
// If the operation type does not correspond to a transfer operation, we return null
return null;
}else{
diff --git a/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java b/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java
index bfb129d..9d92f8b 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/api/GetAllAssetHolders.java
@@ -63,10 +63,10 @@ public class GetAllAssetHolders extends BaseGrapheneHandler {
ApiCall apiCall = new ApiCall(assetApiId, RPC.CALL_GET_ALL_ASSET_HOLDERS, emptyParams, RPC.VERSION, currentId);
websocket.sendText(apiCall.toJsonString());
} else if (baseResponse.id == GET_ALL_ASSET_HOLDERS_COUNT) {
- Type AssetTokenHolders = new TypeToken>>(){}.getType();
+ Type AssetTokenHolders = new TypeToken>>(){}.getType();
GsonBuilder builder = new GsonBuilder();
- builder.registerTypeAdapter(HoldersCount.class, new HoldersCount.HoldersCountDeserializer());
- WitnessResponse> witnessResponse = builder.create().fromJson(response, AssetTokenHolders);
+ builder.registerTypeAdapter(AssetHolderCount.class, new AssetHolderCount.HoldersCountDeserializer());
+ WitnessResponse> witnessResponse = builder.create().fromJson(response, AssetTokenHolders);
mListener.onSuccess(witnessResponse);
websocket.disconnect();
}else{
diff --git a/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java b/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java
new file mode 100644
index 0000000..7180f28
--- /dev/null
+++ b/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java
@@ -0,0 +1,116 @@
+package de.bitsharesmunich.graphenej.api;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+import com.neovisionaries.ws.client.WebSocket;
+import com.neovisionaries.ws.client.WebSocketAdapter;
+import com.neovisionaries.ws.client.WebSocketException;
+import com.neovisionaries.ws.client.WebSocketFrame;
+import de.bitsharesmunich.graphenej.AssetAmount;
+import de.bitsharesmunich.graphenej.RPC;
+import de.bitsharesmunich.graphenej.Transaction;
+import de.bitsharesmunich.graphenej.TransferOperation;
+import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener;
+import de.bitsharesmunich.graphenej.models.ApiCall;
+import de.bitsharesmunich.graphenej.models.BaseResponse;
+import de.bitsharesmunich.graphenej.models.SubscriptionResponse;
+import de.bitsharesmunich.graphenej.models.WitnessResponse;
+
+import java.io.Serializable;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A websocket adapter prepared to be used as a basic dispatch hub for subscription messages.
+ *
+ * Created by nelson on 1/26/17.
+ */
+public class SubscriptionMessagesHub extends WebSocketAdapter {
+ // Sequence of message ids
+ private final static int LOGIN_ID = 1;
+ private final static int GET_DATABASE_ID = 2;
+ private final static int SUBCRIPTION_REQUEST = 3;
+
+ // ID of subscription notifications
+ private final static int SUBCRIPTION_NOTIFICATION = 4;
+
+ private SubscriptionResponse.SubscriptionResponseDeserializer mSubscriptionDeserializer;
+ private Gson gson;
+ private String user;
+ private String password;
+ private int currentId = LOGIN_ID;
+ private int databaseApiId = -1;
+
+ public SubscriptionMessagesHub(String user, String password){
+ this.user = user;
+ this.password = password;
+ this.mSubscriptionDeserializer = new SubscriptionResponse.SubscriptionResponseDeserializer();
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(SubscriptionResponse.class, mSubscriptionDeserializer);
+ builder.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer());
+ builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer());
+ builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
+ this.gson = builder.create();
+ }
+
+ public void addSubscriptionListener(SubscriptionListener listener){
+ this.mSubscriptionDeserializer.addSubscriptionListener(listener);
+ }
+
+ public void removeSubscriptionListener(SubscriptionListener listener){
+ this.removeSubscriptionListener(listener);
+ }
+
+ @Override
+ public void onConnected(WebSocket websocket, Map> headers) throws Exception {
+ ArrayList loginParams = new ArrayList<>();
+ loginParams.add(user);
+ loginParams.add(password);
+ ApiCall loginCall = new ApiCall(1, RPC.CALL_LOGIN, loginParams, RPC.VERSION, currentId);
+ websocket.sendText(loginCall.toJsonString());
+ }
+
+ @Override
+ public void onTextFrame(WebSocket websocket, WebSocketFrame frame) throws Exception {
+ String message = frame.getPayloadText();
+ System.out.println("<< "+message);
+ if(currentId == LOGIN_ID){
+ ArrayList emptyParams = new ArrayList<>();
+ ApiCall getDatabaseId = new ApiCall(1, RPC.CALL_DATABASE, emptyParams, RPC.VERSION, currentId);
+ websocket.sendText(getDatabaseId.toJsonString());
+ }else if(currentId == GET_DATABASE_ID){
+ Type ApiIdResponse = new TypeToken>() {}.getType();
+ WitnessResponse witnessResponse = gson.fromJson(message, ApiIdResponse);
+ databaseApiId = witnessResponse.result;
+
+ ArrayList subscriptionParams = new ArrayList<>();
+ subscriptionParams.add(String.format("%d", SUBCRIPTION_NOTIFICATION));
+ subscriptionParams.add(false);
+ ApiCall getDatabaseId = new ApiCall(databaseApiId, RPC.CALL_SET_SUBSCRIBE_CALLBACK, subscriptionParams, RPC.VERSION, currentId);
+ websocket.sendText(getDatabaseId.toJsonString());
+ }else if(currentId == SUBCRIPTION_REQUEST){
+ // Listeners are called from within the SubscriptionResponseDeserializer, so there's nothing to handle here.
+ }else{
+ SubscriptionResponse subscriptionResponse = gson.fromJson(message, SubscriptionResponse.class);
+ }
+ currentId++;
+ }
+
+ @Override
+ public void onFrameSent(WebSocket websocket, WebSocketFrame frame) throws Exception {
+ System.out.println(">> "+frame.getPayloadText());
+ }
+
+ @Override
+ public void onError(WebSocket websocket, WebSocketException cause) throws Exception {
+ super.onError(websocket, cause);
+ }
+
+ @Override
+ public void handleCallbackError(WebSocket websocket, Throwable cause) throws Exception {
+ super.handleCallbackError(websocket, cause);
+ }
+}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/interfaces/SubscriptionListener.java b/src/main/java/de/bitsharesmunich/graphenej/interfaces/SubscriptionListener.java
new file mode 100644
index 0000000..9081991
--- /dev/null
+++ b/src/main/java/de/bitsharesmunich/graphenej/interfaces/SubscriptionListener.java
@@ -0,0 +1,30 @@
+package de.bitsharesmunich.graphenej.interfaces;
+
+import de.bitsharesmunich.graphenej.ObjectType;
+import de.bitsharesmunich.graphenej.models.SubscriptionResponse;
+
+/**
+ * Generic interface that must be implemented by any class that wants to be informed about a specific
+ * event notification.
+ *
+ * Created by nelson on 1/26/17.
+ */
+public interface SubscriptionListener {
+
+ /**
+ * Every subscription listener must implement a method that returns the type of object it is
+ * interested in.
+ * @return: Instance of the ObjectType enum class.
+ */
+ ObjectType getInterestObjectType();
+
+
+ /**
+ * Method called whenever there is an update that might be of interest for this listener.
+ * Note however that the objects returned inside the SubscriptionResponse are not guaranteed to be
+ * only of the object type requested by this class in the getInterestObjectType.
+ *
+ * @param response: SubscriptionResponse instance, which may or may not contain an object of interest.
+ */
+ void onSubscriptionUpdate(SubscriptionResponse response);
+}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/models/ApiCall.java b/src/main/java/de/bitsharesmunich/graphenej/models/ApiCall.java
index e07416e..2fbaa55 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/models/ApiCall.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/models/ApiCall.java
@@ -75,19 +75,21 @@ public class ApiCall implements JsonSerializable {
}else if(this.params.get(i) instanceof String || this.params.get(i) == null){
// Other times they are plain strings
methodParams.add((String) this.params.get(i));
- }else if(this.params.get(i) instanceof ArrayList){
+ }else if(this.params.get(i) instanceof ArrayList) {
// Other times it might be an array
JsonArray array = new JsonArray();
ArrayList listArgument = (ArrayList) this.params.get(i);
- for(int l = 0; l < listArgument.size(); l++){
+ for (int l = 0; l < listArgument.size(); l++) {
Serializable element = listArgument.get(l);
- if(element instanceof JsonSerializable)
+ if (element instanceof JsonSerializable)
array.add(((JsonSerializable) element).toJsonObject());
- else if(element instanceof String){
+ else if (element instanceof String) {
array.add((String) element);
}
}
methodParams.add(array);
+ }else if(this.params.get(i) instanceof Boolean){
+ methodParams.add((boolean) this.params.get(i));
}else{
System.out.println("Skipping parameter of type: "+this.params.get(i).getClass());
}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/models/HoldersCount.java b/src/main/java/de/bitsharesmunich/graphenej/models/AssetHolderCount.java
similarity index 72%
rename from src/main/java/de/bitsharesmunich/graphenej/models/HoldersCount.java
rename to src/main/java/de/bitsharesmunich/graphenej/models/AssetHolderCount.java
index 4804a8c..9de3ddc 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/models/HoldersCount.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/models/AssetHolderCount.java
@@ -8,19 +8,19 @@ import java.lang.reflect.Type;
/**
* Created by nelson on 1/25/17.
*/
-public class HoldersCount {
+public class AssetHolderCount {
public static final String KEY_ASSET_ID = "asset_id";
public static final String KEY_COUNT = "count";
public Asset asset;
public long count;
- public static class HoldersCountDeserializer implements JsonDeserializer {
+ public static class HoldersCountDeserializer implements JsonDeserializer {
@Override
- public HoldersCount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ public AssetHolderCount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
- HoldersCount holdersCount = new HoldersCount();
+ AssetHolderCount holdersCount = new AssetHolderCount();
holdersCount.asset = new Asset(jsonObject.get(KEY_ASSET_ID).getAsString());
holdersCount.count = jsonObject.get(KEY_COUNT).getAsLong();
return holdersCount;
diff --git a/src/main/java/de/bitsharesmunich/graphenej/models/BroadcastedTransaction.java b/src/main/java/de/bitsharesmunich/graphenej/models/BroadcastedTransaction.java
new file mode 100644
index 0000000..ee53101
--- /dev/null
+++ b/src/main/java/de/bitsharesmunich/graphenej/models/BroadcastedTransaction.java
@@ -0,0 +1,42 @@
+package de.bitsharesmunich.graphenej.models;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import de.bitsharesmunich.graphenej.GrapheneObject;
+import de.bitsharesmunich.graphenej.Transaction;
+
+import java.io.Serializable;
+import java.lang.reflect.Type;
+
+/**
+ * Created by nelson on 1/28/17.
+ */
+public class BroadcastedTransaction extends GrapheneObject implements Serializable {
+ public static final String KEY_TRX = "trx";
+ public static final String KEY_TRX_ID = "trx_id";
+
+ private Transaction trx;
+ private String trx_id;
+
+ public BroadcastedTransaction(String id){
+ super(id);
+ }
+
+ public void setTransaction(Transaction t){
+ this.trx = t;
+ }
+
+ public Transaction getTransaction() {
+ return trx;
+ }
+
+ public void setTransactionId(String id){
+ this.trx_id = id;
+ }
+
+ public String getTransactionId() {
+ return trx_id;
+ }
+}
diff --git a/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java b/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java
index 4118963..d8502be 100644
--- a/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java
+++ b/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java
@@ -9,13 +9,35 @@ import com.google.gson.JsonParseException;
import java.io.Serializable;
import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
-import de.bitsharesmunich.graphenej.GrapheneObject;
-import de.bitsharesmunich.graphenej.ObjectType;
+import de.bitsharesmunich.graphenej.*;
+import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener;
/**
+ * Class that represents a generic subscription response.
+ * The template for every subscription response is the following:
+ *
+ * {
+ * "method": "notice"
+ * "params": [
+ * SUBSCRIPTION_ID,
+ * [[
+ * { "id": "2.1.0", ... },
+ * { "id": ... },
+ * { "id": ... },
+ * { "id": ... }
+ * ]]
+ * ],
+ * }
+ *
+ * As of 1/2017, the witness API returns all sort of events, not just the ones we're interested in once we
+ * make a call to the 'set_subscribe_callback', regardless of whether the 'clear_filter' parameter is set to
+ * true or false.
+ *
+ * To minimize CPU usage, we introduce a scheme of selective parsing, implemented by the static inner class
+ * SubscriptionResponseDeserializer.
+ *
* Created by nelson on 1/12/17.
*/
public class SubscriptionResponse {
@@ -27,7 +49,49 @@ public class SubscriptionResponse {
public String method;
public List params;
+ /**
+ * Deserializer class that is used to parse and deserialize subscription responses in a partial way,
+ * depending on the amount of SubscriptionListeners we might have registered.
+ *
+ * The rationale behind these architecture is to avoid wasting computational resources parsing unneeded
+ * objects that might come once the are subscribed to the witness notifications.
+ */
public static class SubscriptionResponseDeserializer implements JsonDeserializer {
+ private HashMap listenerTypeCount;
+ private LinkedList mListeners;
+
+ /**
+ * Constructor that will just create a list of SubscriptionListeners and
+ * a map of ObjectType to integer in order to keep track of how many listeners
+ * to each type of object we have.
+ */
+ public SubscriptionResponseDeserializer(){
+ mListeners = new LinkedList<>();
+ listenerTypeCount = new HashMap<>();
+ }
+
+ public void addSubscriptionListener(SubscriptionListener subscriptionListener){
+ int currentCount = 0;
+ if(listenerTypeCount.containsKey(subscriptionListener.getInterestObjectType())){
+ currentCount = listenerTypeCount.get(subscriptionListener.getInterestObjectType());
+ }
+ this.listenerTypeCount.put(subscriptionListener.getInterestObjectType(), currentCount + 1);
+ this.mListeners.add(subscriptionListener);
+ }
+
+ public List getSubscriptionListeners(){
+ return this.mListeners;
+ }
+
+ public void removeSubscriptionListener(SubscriptionListener subscriptionListener){
+ int currentCount = listenerTypeCount.get(subscriptionListener.getInterestObjectType());
+ if(currentCount != 0){
+ this.listenerTypeCount.put(subscriptionListener.getInterestObjectType(), currentCount);
+ }else{
+ System.out.println("Trying to remove subscription listener, but none is registered!");
+ }
+ this.mListeners.remove(subscriptionListener);
+ }
@Override
public SubscriptionResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
@@ -44,26 +108,48 @@ public class SubscriptionResponse {
JsonArray subArray = paramsArray.get(1).getAsJsonArray().get(0).getAsJsonArray();
for(JsonElement object : subArray){
if(object.isJsonObject()){
+
GrapheneObject grapheneObject = new GrapheneObject(object.getAsJsonObject().get(KEY_ID).getAsString());
- JsonObject jsonObject = object.getAsJsonObject();
- if(grapheneObject.getObjectType() == ObjectType.ACCOUNT_BALANCE_OBJECT){
- AccountBalanceUpdate balanceObject = new AccountBalanceUpdate(grapheneObject.getObjectId());
- balanceObject.owner = jsonObject.get(AccountBalanceUpdate.KEY_OWNER).getAsString();
- balanceObject.asset_type = jsonObject.get(AccountBalanceUpdate.KEY_ASSET_TYPE).getAsString();
- balanceObject.balance = jsonObject.get(AccountBalanceUpdate.KEY_BALANCE).getAsLong();
- secondArgument.add(balanceObject);
- }else if(grapheneObject.getObjectType() == ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT){
- DynamicGlobalProperties dynamicGlobal = new DynamicGlobalProperties(grapheneObject.getObjectId());
- dynamicGlobal.head_block_number = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_NUMBER).getAsLong();
- dynamicGlobal.head_block_id = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_ID).getAsString();
- dynamicGlobal.time = jsonObject.get(DynamicGlobalProperties.KEY_TIME).getAsString();
- //TODO: Deserialize all other attributes
- secondArgument.add(dynamicGlobal);
+ int listenerTypeCount = 0;
+ if(this.listenerTypeCount.containsKey(grapheneObject.getObjectType())){
+ listenerTypeCount = this.listenerTypeCount.get(grapheneObject.getObjectType());
+ }
+ /*
+ * Here's where we apply the selective deserialization logic, meaning we only completely deserialize
+ * an object contained in a notification if there is at least one registered listener interested in
+ * objects of that type.
+ */
+ if(listenerTypeCount > 0){
+ JsonObject jsonObject = object.getAsJsonObject();
+ if(grapheneObject.getObjectType() == ObjectType.ACCOUNT_BALANCE_OBJECT){
+ AccountBalanceUpdate balanceObject = new AccountBalanceUpdate(grapheneObject.getObjectId());
+ balanceObject.owner = jsonObject.get(AccountBalanceUpdate.KEY_OWNER).getAsString();
+ balanceObject.asset_type = jsonObject.get(AccountBalanceUpdate.KEY_ASSET_TYPE).getAsString();
+ balanceObject.balance = jsonObject.get(AccountBalanceUpdate.KEY_BALANCE).getAsLong();
+ secondArgument.add(balanceObject);
+ }else if(grapheneObject.getObjectType() == ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT){
+ DynamicGlobalProperties dynamicGlobal = new DynamicGlobalProperties(grapheneObject.getObjectId());
+ dynamicGlobal.head_block_number = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_NUMBER).getAsLong();
+ dynamicGlobal.head_block_id = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_ID).getAsString();
+ dynamicGlobal.time = jsonObject.get(DynamicGlobalProperties.KEY_TIME).getAsString();
+ //TODO: Deserialize all other attributes
+ secondArgument.add(dynamicGlobal);
+ }else if(grapheneObject.getObjectType() == ObjectType.TRANSACTION_OBJECT){
+ BroadcastedTransaction broadcastedTransaction = new BroadcastedTransaction(grapheneObject.getObjectId());
+ broadcastedTransaction.setTransaction(context.deserialize(jsonObject.get(BroadcastedTransaction.KEY_TRX), Transaction.class));
+ broadcastedTransaction.setTransactionId(jsonObject.get(BroadcastedTransaction.KEY_TRX_ID).getAsString());
+ secondArgument.add(broadcastedTransaction);
+ }else{
+ //TODO: Add support for other types of objects
+ }
}
}else{
secondArgument.add(object.getAsString());
}
}
+ for(SubscriptionListener listener : mListeners){
+ listener.onSubscriptionUpdate(response);
+ }
return response;
}
}