diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java index 9fa6a51..c4aedab 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHub.java @@ -4,7 +4,6 @@ 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.WebSocketException; import com.neovisionaries.ws.client.WebSocketFrame; import java.io.Serializable; @@ -20,6 +19,7 @@ import de.bitsharesmunich.graphenej.interfaces.SubscriptionHub; import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener; import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; import de.bitsharesmunich.graphenej.models.ApiCall; +import de.bitsharesmunich.graphenej.models.DynamicGlobalProperties; import de.bitsharesmunich.graphenej.models.SubscriptionResponse; import de.bitsharesmunich.graphenej.models.WitnessResponse; import de.bitsharesmunich.graphenej.operations.TransferOperation; @@ -55,6 +55,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs builder.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()); builder.registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()); builder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); + builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer()); this.gson = builder.create(); } @@ -101,7 +102,7 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs 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. + // There's nothing to handle here. }else{ SubscriptionResponse subscriptionResponse = gson.fromJson(message, SubscriptionResponse.class); } @@ -112,14 +113,4 @@ public class SubscriptionMessagesHub extends BaseGrapheneHandler implements Subs 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/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java index 5b560f6..30549d9 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/api/TransactionBroadcastSequence.java @@ -9,12 +9,9 @@ import com.neovisionaries.ws.client.WebSocketFrame; import java.io.Serializable; import java.lang.reflect.Type; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Map; -import java.util.TimeZone; import de.bitsharesmunich.graphenej.Asset; import de.bitsharesmunich.graphenej.AssetAmount; @@ -74,7 +71,9 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { if(frame.isTextFrame()) System.out.println("<<< "+frame.getPayloadText()); String response = frame.getPayloadText(); - Gson gson = new Gson(); + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(DynamicGlobalProperties.class, new DynamicGlobalProperties.DynamicGlobalPropertiesDeserializer()); + Gson gson = builder.create(); BaseResponse baseResponse = gson.fromJson(response, BaseResponse.class); if(baseResponse.error != null){ mListener.onError(baseResponse.error); @@ -104,12 +103,8 @@ public class TransactionBroadcastSequence extends BaseGrapheneHandler { WitnessResponse witnessResponse = gson.fromJson(response, DynamicGlobalPropertiesResponse); DynamicGlobalProperties dynamicProperties = witnessResponse.result; - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - Date date = dateFormat.parse(dynamicProperties.time); - // Adjusting dynamic block data to every transaction - long expirationTime = (date.getTime() / 1000) + Transaction.DEFAULT_EXPIRATION_TIME; + long expirationTime = (dynamicProperties.time.getTime() / 1000) + Transaction.DEFAULT_EXPIRATION_TIME; String headBlockId = dynamicProperties.head_block_id; long headBlockNumber = dynamicProperties.head_block_number; transaction.setBlockData(new BlockData(headBlockNumber, headBlockId, expirationTime)); diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/DynamicGlobalProperties.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/DynamicGlobalProperties.java index 59819b3..d1cc36f 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/DynamicGlobalProperties.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/DynamicGlobalProperties.java @@ -1,8 +1,19 @@ package de.bitsharesmunich.graphenej.models; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + import java.io.Serializable; +import java.lang.reflect.Type; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; import de.bitsharesmunich.graphenej.GrapheneObject; +import de.bitsharesmunich.graphenej.Util; /** * Class used to deserialize the 'result' field returned by the full node after making a call @@ -25,19 +36,62 @@ public class DynamicGlobalProperties extends GrapheneObject implements Serializa public long head_block_number; public String head_block_id; - public String time; + public Date time; public String current_witness; - public String next_maintenance_time; + public Date next_maintenance_time; public String last_budget_time; public long witness_budget; public long accounts_registered_this_interval; public long recently_missed_count; public long current_aslot; public String recent_slots_filled; - public long dynamic_flags; + public int dynamic_flags; public long last_irreversible_block_num; public DynamicGlobalProperties(String id) { super(id); } + + /** + * Class that will parse the JSON element containing the dynamic global properties object and + * return an instance of the {@link DynamicGlobalProperties} class. + */ + public static class DynamicGlobalPropertiesDeserializer implements JsonDeserializer { + + @Override + public DynamicGlobalProperties deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + + // Creating an instance of the DynamicGlobalProperties + DynamicGlobalProperties dynamicGlobal = new DynamicGlobalProperties(jsonElement.getAsJsonObject().get(KEY_ID).getAsString()); + + // Start to fill in the parsed details + dynamicGlobal.head_block_number = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_NUMBER).getAsLong(); + dynamicGlobal.head_block_id = jsonObject.get(DynamicGlobalProperties.KEY_HEAD_BLOCK_ID).getAsString(); + + SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT); + try { + dynamicGlobal.time = dateFormat.parse(jsonObject.get(DynamicGlobalProperties.KEY_TIME).getAsString()); + } catch (ParseException e) { + System.out.println("ParseException. Msg: "+e.getMessage()); + } + + try { + dynamicGlobal.next_maintenance_time = dateFormat.parse(jsonObject.get(DynamicGlobalProperties.KEY_NEXT_MAINTENANCE_TIME).getAsString()); + } catch (ParseException e) { + System.out.println("ParseException. Msg: "+e.getMessage()); + } + + dynamicGlobal.current_witness = jsonObject.get(DynamicGlobalProperties.KEY_CURRENT_WITNESS).getAsString(); + dynamicGlobal.last_budget_time = jsonObject.get(DynamicGlobalProperties.KEY_LAST_BUDGET_TIME).getAsString(); + dynamicGlobal.witness_budget = jsonObject.get(DynamicGlobalProperties.KEY_WITNESS_BUDGET).getAsLong(); + dynamicGlobal.accounts_registered_this_interval = jsonObject.get(DynamicGlobalProperties.KEY_ACCOUNTS_REGISTERED_THIS_INTERVAL).getAsLong(); + dynamicGlobal.recently_missed_count = jsonObject.get(DynamicGlobalProperties.KEY_RECENTLY_MISSED_COUNT).getAsLong(); + dynamicGlobal.current_aslot = jsonObject.get(DynamicGlobalProperties.KEY_CURRENT_ASLOT).getAsLong(); + dynamicGlobal.recent_slots_filled = jsonObject.get(DynamicGlobalProperties.KEY_RECENT_SLOTS_FILLED).getAsString(); + dynamicGlobal.dynamic_flags = jsonObject.get(DynamicGlobalProperties.KEY_DYNAMIC_FLAGS).getAsInt(); + dynamicGlobal.last_irreversible_block_num = jsonObject.get(DynamicGlobalProperties.KEY_LAST_IRREVERSIBLE_BLOCK_NUM).getAsLong(); + return dynamicGlobal; + } + } } \ No newline at end of file diff --git a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java index dbf896a..1e8741c 100644 --- a/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java +++ b/graphenej/src/main/java/de/bitsharesmunich/graphenej/models/SubscriptionResponse.java @@ -9,9 +9,14 @@ import com.google.gson.JsonParseException; import java.io.Serializable; import java.lang.reflect.Type; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; -import de.bitsharesmunich.graphenej.*; +import de.bitsharesmunich.graphenej.GrapheneObject; +import de.bitsharesmunich.graphenej.ObjectType; +import de.bitsharesmunich.graphenej.Transaction; import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener; /** @@ -57,7 +62,14 @@ public class SubscriptionResponse { * objects that might come once the are subscribed to the witness notifications. */ public static class SubscriptionResponseDeserializer implements JsonDeserializer { + /** + * Map of ObjectType to Integer used to keep track of the current amount of listener per type + */ private HashMap listenerTypeCount; + + /** + * List of listeners + */ private LinkedList mListeners; /** @@ -128,12 +140,8 @@ public class SubscriptionResponse { 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); + DynamicGlobalProperties dynamicGlobalProperties = context.deserialize(object, DynamicGlobalProperties.class); + secondArgument.add(dynamicGlobalProperties); }else if(grapheneObject.getObjectType() == ObjectType.TRANSACTION_OBJECT){ BroadcastedTransaction broadcastedTransaction = new BroadcastedTransaction(grapheneObject.getObjectId()); broadcastedTransaction.setTransaction((Transaction) context.deserialize(jsonObject.get(BroadcastedTransaction.KEY_TRX), Transaction.class)); diff --git a/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java new file mode 100644 index 0000000..9df50e0 --- /dev/null +++ b/graphenej/src/test/java/de/bitsharesmunich/graphenej/api/SubscriptionMessagesHubTest.java @@ -0,0 +1,85 @@ +package de.bitsharesmunich.graphenej.api; + +import com.neovisionaries.ws.client.WebSocketException; + +import org.junit.Test; +import java.util.List; +import de.bitsharesmunich.graphenej.ObjectType; +import de.bitsharesmunich.graphenej.interfaces.SubscriptionListener; +import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener; +import de.bitsharesmunich.graphenej.models.BaseResponse; +import de.bitsharesmunich.graphenej.models.DynamicGlobalProperties; +import de.bitsharesmunich.graphenej.models.SubscriptionResponse; +import de.bitsharesmunich.graphenej.models.WitnessResponse; + +/** + * Created by nelson on 4/25/17. + */ +public class SubscriptionMessagesHubTest extends BaseApiTest { + + private SubscriptionMessagesHub mMessagesHub; + private WitnessResponseListener mErrorListener = new WitnessResponseListener() { + @Override + public void onSuccess(WitnessResponse response) { + System.out.println("onSuccess"); + } + + @Override + public void onError(BaseResponse.Error error) { + System.out.println("onError"); + } + }; + + @Test + public void testGlobalPropertiesDeserializer(){ + try{ + mMessagesHub = new SubscriptionMessagesHub("", "", mErrorListener); + mMessagesHub.addSubscriptionListener(new SubscriptionListener() { + private int MAX_MESSAGES = 5; + private int messageCounter = 0; + + @Override + public ObjectType getInterestObjectType() { + return ObjectType.DYNAMIC_GLOBAL_PROPERTY_OBJECT; + } + + @Override + public void onSubscriptionUpdate(SubscriptionResponse response) { + if(response.params.size() == 2){ + try{ + List payload = (List) response.params.get(1); + if(payload.size() > 0 && payload.get(0) instanceof DynamicGlobalProperties){ + DynamicGlobalProperties globalProperties = (DynamicGlobalProperties) payload.get(0); + System.out.println("time.....................: "+globalProperties.time); + System.out.println("next_maintenance_time....: "+globalProperties.next_maintenance_time); + System.out.println("recent_slots_filled......: "+globalProperties.recent_slots_filled); + } + }catch(Exception e){ + System.out.println("Exception"); + System.out.println("Type: "+e.getClass()); + System.out.println("Msg: "+e.getMessage()); + } + } + // Waiting for MAX_MESSAGES messages before releasing the wait lock + messageCounter++; + if(messageCounter > MAX_MESSAGES){ + synchronized (SubscriptionMessagesHubTest.this){ + SubscriptionMessagesHubTest.this.notifyAll(); + } + } + } + }); + mWebSocket.addListener(mMessagesHub); + mWebSocket.connect(); + + // Holding this thread while we get update notifications + synchronized (this){ + wait(); + } + } catch (WebSocketException e) { + System.out.println("WebSocketException. Msg: " + e.getMessage()); + } catch (InterruptedException e) { + System.out.println("InterruptedException. Msg: "+e.getMessage()); + } + } +} \ No newline at end of file