From 4ba6f1d25c930f611942d658fe9cc76b84ee4298 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Thu, 14 Nov 2019 13:14:02 -0500 Subject: [PATCH 1/7] Clearing API permissions before connecting --- .../java/cy/agorise/graphenej/api/android/NetworkService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java index 5e68703..e1365f6 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java @@ -303,6 +303,7 @@ public class NetworkService { FullNode fullNode = nodeProvider.getBestNode(); if(fullNode != null){ System.out.println(String.format(Locale.ROOT, "Connected with %d latency results", latencyUpdateCounter)); + mApiIds.clear(); connect(); }else{ Disposable d = Observable.timer(DEFAULT_INITIAL_DELAY, TimeUnit.MILLISECONDS).subscribe(this); From a988321c27d1eb954b969f001aaab776f5071105 Mon Sep 17 00:00:00 2001 From: "Nelson R. Perez" Date: Mon, 18 Nov 2019 13:51:15 -0500 Subject: [PATCH 2/7] Introducing basic callback support to the NetworkService class --- .../cy/agorise/graphenej/api/ApiCallback.java | 26 ++++++++++++ .../graphenej/api/android/NetworkService.java | 42 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java b/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java new file mode 100644 index 0000000..687e646 --- /dev/null +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java @@ -0,0 +1,26 @@ +package cy.agorise.graphenej.api; + +import cy.agorise.graphenej.models.JsonRpcResponse; +import okhttp3.Response; + +/** + * Interface defining the basic contract an API request can expect. + */ +public interface ApiCallback { + + /** + * Method called whenever we have a successful response from the node. + * + * @param response Parsed response + * @param text Raw text response + */ + void onResponse(JsonRpcResponse response, String text); + + /** + * Method called whenever there was an error and the response could not be delivered. + * + * @param t Error Trowable, potentially with some details about the problem. + * @param response Node response, if any + */ + void onFailure(Throwable t, Response response); +} diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java index e1365f6..6d6ac27 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java @@ -1,5 +1,7 @@ package cy.agorise.graphenej.api.android; +import android.util.SparseArray; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; @@ -22,6 +24,7 @@ import cy.agorise.graphenej.RPC; import cy.agorise.graphenej.Transaction; import cy.agorise.graphenej.UserAccount; import cy.agorise.graphenej.api.ApiAccess; +import cy.agorise.graphenej.api.ApiCallback; import cy.agorise.graphenej.api.ConnectionStatusUpdate; import cy.agorise.graphenej.api.calls.ApiCallable; import cy.agorise.graphenej.api.calls.GetAccountBalances; @@ -117,6 +120,8 @@ public class NetworkService { private CompositeDisposable mCompositeDisposable; + private SparseArray mCallbackMap = new SparseArray(); + private Gson gson = new GsonBuilder() .registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()) .registerTypeAdapter(TransferOperation.class, new TransferOperation.TransferDeserializer()) @@ -228,6 +233,18 @@ public class NetworkService { return -1; } + public synchronized long sendMessage(ApiCallable apiCallable, int requiredApi, ApiCallback callback){ + long id = this.sendMessage(apiCallable, requiredApi); + if(callback != null){ + if(id != -1){ + mCallbackMap.put((int) id, callback); + }else{ + callback.onFailure(new Exception("Message could not be sent"), null); + } + } + return id; + } + /** * Method used to inform any external party a clue about the current connectivity status * @return True if the service is currently connected and logged in, false otherwise. @@ -247,6 +264,7 @@ public class NetworkService { nodeLatencyVerifier.stop(); mCompositeDisposable.dispose(); + mCallbackMap.clear(); } /** @@ -335,6 +353,20 @@ public class NetworkService { public void onComplete() { } }; + /** + * Method used to execute every callback failure method and remove them from the SparseArray. + * + * @param throwable + * @param response + */ + private void resetCallbacks(Throwable throwable, Response response){ + for(int i = 0; i < mCallbackMap.size(); i++){ + ApiCallback callback = mCallbackMap.get(i); + callback.onFailure(throwable, response); + mCallbackMap.remove(i); + } + } + private WebSocketListener mWebSocketListener = new WebSocketListener() { @Override @@ -449,6 +481,14 @@ public class NetworkService { */ private void handleJsonRpcResponse(JsonRpcResponse response, String text){ JsonRpcResponse parsedResponse = null; + + // Executing callback, if present + if(mCallbackMap.indexOfKey((int) response.id) > 0){ + ApiCallback callback = (ApiCallback) mCallbackMap.get((int)response.id); + callback.onResponse(response, text); + mCallbackMap.remove((int)response.id); + } + Class requestClass = mRequestClassMap.get(response.id); if(requestClass != null){ // Removing the class entry in the map @@ -590,6 +630,7 @@ public class NetworkService { @Override public void onClosed(WebSocket webSocket, int code, String reason) { super.onClosed(webSocket, code, reason); + resetCallbacks(new Exception("Websocket closed. Reason: " + reason), null); if(code == GOING_AWAY_STATUS) handleWebSocketDisconnection(true, false); else @@ -599,6 +640,7 @@ public class NetworkService { @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { super.onFailure(webSocket, t, response); + resetCallbacks(t, response); System.out.println("onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage()); // Logging error stack trace for(StackTraceElement element : t.getStackTrace()){ From b9f4ac7bea0ce9630588e0ec14d2ee54ccc32935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20R=2E=20P=C3=A9rez?= Date: Thu, 21 Nov 2019 13:13:22 -0500 Subject: [PATCH 3/7] Updated gradle plugin version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 77fec39..d9ab50e 100644 --- a/build.gradle +++ b/build.gradle @@ -23,7 +23,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.2' + classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.novoda:bintray-release:0.9.1' } } From 4a1bb69ee0f17a20f99ec73d89fa62be0f7f6eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20R=2E=20P=C3=A9rez?= Date: Thu, 21 Nov 2019 16:28:51 -0500 Subject: [PATCH 4/7] Fixed an issue with the callback implementation - We're now passing the parsed response, whenever possible - The SparseArray was replaced by a HashMap --- .../graphenej/api/android/NetworkService.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java index 6d6ac27..19172c9 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java @@ -120,7 +120,7 @@ public class NetworkService { private CompositeDisposable mCompositeDisposable; - private SparseArray mCallbackMap = new SparseArray(); + private HashMap mCallbackMap = new HashMap(); private Gson gson = new GsonBuilder() .registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer()) @@ -237,7 +237,7 @@ public class NetworkService { long id = this.sendMessage(apiCallable, requiredApi); if(callback != null){ if(id != -1){ - mCallbackMap.put((int) id, callback); + mCallbackMap.put(id, callback); }else{ callback.onFailure(new Exception("Message could not be sent"), null); } @@ -360,10 +360,11 @@ public class NetworkService { * @param response */ private void resetCallbacks(Throwable throwable, Response response){ - for(int i = 0; i < mCallbackMap.size(); i++){ - ApiCallback callback = mCallbackMap.get(i); - callback.onFailure(throwable, response); - mCallbackMap.remove(i); + for(ApiCallback callback : mCallbackMap.values()) { + if(callback != null) { + callback.onFailure(throwable, response); + mCallbackMap.remove(callback); + } } } @@ -482,13 +483,6 @@ public class NetworkService { private void handleJsonRpcResponse(JsonRpcResponse response, String text){ JsonRpcResponse parsedResponse = null; - // Executing callback, if present - if(mCallbackMap.indexOfKey((int) response.id) > 0){ - ApiCallback callback = (ApiCallback) mCallbackMap.get((int)response.id); - callback.onResponse(response, text); - mCallbackMap.remove((int)response.id); - } - Class requestClass = mRequestClassMap.get(response.id); if(requestClass != null){ // Removing the class entry in the map @@ -567,6 +561,14 @@ public class NetworkService { if(parsedResponse == null){ parsedResponse = response; } + + // Executing callback, if present with the parsed response + if(mCallbackMap.containsKey(response.id)){ + ApiCallback callback = mCallbackMap.get(response.id); + callback.onResponse(parsedResponse, text); + mCallbackMap.remove(response.id); + } + // Broadcasting the parsed response to all interested listeners RxBus.getBusInstance().send(parsedResponse); } From a8c13cac9629eacd9aad7e93fe0170c6c32a40fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20R=2E=20P=C3=A9rez?= Date: Wed, 27 Nov 2019 18:45:44 -0500 Subject: [PATCH 5/7] Modified the ApiCallback interface --- .../src/main/java/cy/agorise/graphenej/api/ApiCallback.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java b/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java index 687e646..dc04acf 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java @@ -14,7 +14,7 @@ public interface ApiCallback { * @param response Parsed response * @param text Raw text response */ - void onResponse(JsonRpcResponse response, String text); + void onResponse(JsonRpcResponse response, String text); /** * Method called whenever there was an error and the response could not be delivered. From bba825ca6235cc10aa9d950877ed53f2bdae545b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20R=2E=20P=C3=A9rez?= Date: Tue, 3 Dec 2019 12:39:42 -0500 Subject: [PATCH 6/7] Calling onFailure method in case the parsed response comes with a non-null error object --- .../src/main/java/cy/agorise/graphenej/api/ApiCallback.java | 6 ++++-- .../cy/agorise/graphenej/api/android/NetworkService.java | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java b/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java index dc04acf..188711a 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/ApiCallback.java @@ -1,5 +1,7 @@ package cy.agorise.graphenej.api; +import javax.annotation.Nullable; + import cy.agorise.graphenej.models.JsonRpcResponse; import okhttp3.Response; @@ -19,8 +21,8 @@ public interface ApiCallback { /** * Method called whenever there was an error and the response could not be delivered. * - * @param t Error Trowable, potentially with some details about the problem. + * @param t Error Throwable, potentially with some details about the problem. * @param response Node response, if any */ - void onFailure(Throwable t, Response response); + void onFailure(Throwable t, @Nullable Response response); } diff --git a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java index 19172c9..6fcc810 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/api/android/NetworkService.java @@ -565,7 +565,10 @@ public class NetworkService { // Executing callback, if present with the parsed response if(mCallbackMap.containsKey(response.id)){ ApiCallback callback = mCallbackMap.get(response.id); - callback.onResponse(parsedResponse, text); + if(response.error == null) + callback.onResponse(parsedResponse, text); + else + callback.onFailure(new Exception("Exception while trying to parse node response. Message: " + response.error.message), null); mCallbackMap.remove(response.id); } From 1d029c31114c45fe633b9a58f84f23b187cd3dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20R=2E=20P=C3=A9rez?= Date: Thu, 12 Mar 2020 17:59:01 -0500 Subject: [PATCH 7/7] Making the AssetAmount class implement the Comparable interface --- .../main/java/cy/agorise/graphenej/AssetAmount.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/graphenej/src/main/java/cy/agorise/graphenej/AssetAmount.java b/graphenej/src/main/java/cy/agorise/graphenej/AssetAmount.java index 37d087a..71b24c9 100644 --- a/graphenej/src/main/java/cy/agorise/graphenej/AssetAmount.java +++ b/graphenej/src/main/java/cy/agorise/graphenej/AssetAmount.java @@ -27,7 +27,7 @@ import cy.agorise.graphenej.interfaces.JsonSerializable; /** * Class used to represent a specific amount of a certain asset */ -public class AssetAmount implements ByteSerializable, JsonSerializable { +public class AssetAmount implements ByteSerializable, JsonSerializable, Comparable { /** * Constants used in the JSON serialization procedure. */ @@ -196,6 +196,14 @@ public class AssetAmount implements ByteSerializable, JsonSerializable { return String.format("(asset=%s, amount=%s)", asset.getObjectId(), amount.toString(10)); } + @Override + public int compareTo(AssetAmount other) { + if(!other.asset.equals(this.asset)) throw new AssertionError("Cannot compare amounts of different assets"); + if(this.amount == null) throw new NullPointerException("Asset amount field is null"); + if(other.amount == null) throw new NullPointerException("Asser amount field is null"); + return amount.compareTo(other.amount); + } + /** * Custom serializer used to translate this object into the JSON-formatted entry we need for a transaction. */