Adding basic support for the subscription feature of the blockchain API and listening for transfer operations in transactions
This commit is contained in:
parent
fc9915ab78
commit
aec4e55953
16 changed files with 596 additions and 109 deletions
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
* <a href="https://en.wikipedia.org/wiki/Unix_time">Unix time</a>
|
||||
*/
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ public class Main {
|
|||
// test.testAssetSerialization();
|
||||
// test.testGetMarketHistory();
|
||||
// test.testGetAccountBalances();
|
||||
test.testGetAssetHoldersCount();
|
||||
// test.testGetAssetHoldersCount();
|
||||
test.testSubscription(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,55 @@
|
|||
package de.bitsharesmunich.graphenej;
|
||||
|
||||
/**
|
||||
* Enum type used to keep track of all the operation types and their corresponding ids.
|
||||
*
|
||||
* <a href="https://bitshares.org/doxygen/operations_8hpp_source.html">Source</a>
|
||||
*
|
||||
* 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
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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<HoldersCount> holdersCountList = (List<HoldersCount>) response.result;
|
||||
for(HoldersCount holdersCount : holdersCountList){
|
||||
List<AssetHolderCount> holdersCountList = (List<AssetHolderCount>) 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<Serializable> updatedObjects = (List<Serializable>) 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BaseOperation> 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<BaseOperation> 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<Transaction> {
|
||||
/**
|
||||
* 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<Transaction> {
|
||||
|
||||
@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<Transaction> {
|
||||
|
||||
@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<BaseOperation> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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{
|
||||
|
|
|
@ -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<WitnessResponse<List<HoldersCount>>>(){}.getType();
|
||||
Type AssetTokenHolders = new TypeToken<WitnessResponse<List<AssetHolderCount>>>(){}.getType();
|
||||
GsonBuilder builder = new GsonBuilder();
|
||||
builder.registerTypeAdapter(HoldersCount.class, new HoldersCount.HoldersCountDeserializer());
|
||||
WitnessResponse<List<HoldersCount>> witnessResponse = builder.create().fromJson(response, AssetTokenHolders);
|
||||
builder.registerTypeAdapter(AssetHolderCount.class, new AssetHolderCount.HoldersCountDeserializer());
|
||||
WitnessResponse<List<AssetHolderCount>> witnessResponse = builder.create().fromJson(response, AssetTokenHolders);
|
||||
mListener.onSuccess(witnessResponse);
|
||||
websocket.disconnect();
|
||||
}else{
|
||||
|
|
|
@ -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<String, List<String>> headers) throws Exception {
|
||||
ArrayList<Serializable> 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<Serializable> 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<WitnessResponse<Integer>>() {}.getType();
|
||||
WitnessResponse<Integer> witnessResponse = gson.fromJson(message, ApiIdResponse);
|
||||
databaseApiId = witnessResponse.result;
|
||||
|
||||
ArrayList<Serializable> 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -88,6 +88,8 @@ public class ApiCall implements JsonSerializable {
|
|||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
|
|
@ -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<HoldersCount> {
|
||||
public static class HoldersCountDeserializer implements JsonDeserializer<AssetHolderCount> {
|
||||
|
||||
@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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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<Serializable> 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<SubscriptionResponse> {
|
||||
private HashMap<ObjectType, Integer> listenerTypeCount;
|
||||
private LinkedList<SubscriptionListener> 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<SubscriptionListener> 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,7 +108,18 @@ 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());
|
||||
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());
|
||||
|
@ -59,11 +134,22 @@ public class SubscriptionResponse {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue