Merge branch 'develop' of github.com:Agorise/graphenej into develop

This commit is contained in:
Nelson R. Perez 2018-11-07 17:45:30 -05:00
commit c76548cbee
2 changed files with 115 additions and 58 deletions

View file

@ -80,9 +80,10 @@ public class NetworkService extends Service {
private final String TAG = this.getClass().getName(); private final String TAG = this.getClass().getName();
public static final int NORMAL_CLOSURE_STATUS = 1000; public static final int NORMAL_CLOSURE_STATUS = 1000;
private static final int NO_HISTORY_CLOSURE_STATUS = 1001;
// Time to wait before retrying a connection attempt // Time to wait before retrying a connection attempt
private final int DEFAULT_RETRY_DELAY = 500; private static final int DEFAULT_RETRY_DELAY = 500;
// Default connection delay when using the node latency verification strategy. This initial // Default connection delay when using the node latency verification strategy. This initial
// delay is required in order ot make sure we have a fair selection of node latencies from // delay is required in order ot make sure we have a fair selection of node latencies from
@ -285,7 +286,17 @@ public class NetworkService extends Service {
@Nullable @Nullable
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
Bundle extras = intent.getExtras(); return mBinder;
}
/**
* Initialize information and try to connect to a node accordingly. This methods were moved
* from onBind to avoid crashes due to components other than {@link NetworkServiceManager}
* binding to the service without submitting the proper information.
*
* @param extras Bundle that contains all required information for a proper initialization
*/
public void bootstrapService(Bundle extras) {
// Retrieving credentials and requested API data from the shared preferences // Retrieving credentials and requested API data from the shared preferences
mUsername = extras.getString(NetworkService.KEY_USERNAME, ""); mUsername = extras.getString(NetworkService.KEY_USERNAME, "");
mPassword = extras.getString(NetworkService.KEY_PASSWORD, ""); mPassword = extras.getString(NetworkService.KEY_PASSWORD, "");
@ -328,7 +339,6 @@ public class NetworkService extends Service {
mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY); mHandler.postDelayed(mConnectAttempt, DEFAULT_INITIAL_DELAY);
} }
} }
return mBinder;
} }
/** /**
@ -556,6 +566,9 @@ public class NetworkService extends Service {
} else if (requestClass == GetFullAccounts.class) { } else if (requestClass == GetFullAccounts.class) {
Type GetFullAccountsResponse = new TypeToken<JsonRpcResponse<List<FullAccountDetails>>>(){}.getType(); Type GetFullAccountsResponse = new TypeToken<JsonRpcResponse<List<FullAccountDetails>>>(){}.getType();
parsedResponse = gson.fromJson(text, GetFullAccountsResponse); parsedResponse = gson.fromJson(text, GetFullAccountsResponse);
if(parsedResponse != null)
verifyNodeHasHistoryApi(parsedResponse);
} else if(requestClass == GetKeyReferences.class){ } else if(requestClass == GetKeyReferences.class){
Type GetKeyReferencesResponse = new TypeToken<JsonRpcResponse<List<List<UserAccount>>>>(){}.getType(); Type GetKeyReferencesResponse = new TypeToken<JsonRpcResponse<List<List<UserAccount>>>>(){}.getType();
parsedResponse = gson.fromJson(text, GetKeyReferencesResponse); parsedResponse = gson.fromJson(text, GetKeyReferencesResponse);
@ -578,6 +591,30 @@ public class NetworkService extends Service {
RxBus.getBusInstance().send(parsedResponse); RxBus.getBusInstance().send(parsedResponse);
} }
/**
* This method inspects the node response to find out if the totalOps is equal to zero,
* in that case the current connected node may not have the history plugin so we would need
* to close the connection and choose a different node.
*
* @param parsedResponse A JSONRpcResponse from a GetFullAccounts API call
*/
private void verifyNodeHasHistoryApi(JsonRpcResponse parsedResponse) {
if(parsedResponse.result instanceof List &&
((List) parsedResponse.result).size() > 0 &&
((List) parsedResponse.result).get(0) instanceof FullAccountDetails) {
FullAccountDetails fullAccountDetails = (FullAccountDetails) ((List) parsedResponse.result).get(0);
long totalOps = fullAccountDetails.getStatistics().total_ops;
if (totalOps == 0) {
Log.d(TAG, "The node returned 0 total_ops for current account and may not have installed the history plugin. " +
"Trying to connect to a different node.");
mWebSocket.close(NO_HISTORY_CLOSURE_STATUS, null);
}
}
}
/** /**
* Private method that will just broadcast a de-serialized notification to all interested parties * Private method that will just broadcast a de-serialized notification to all interested parties
* @param notification De-serialized notification * @param notification De-serialized notification
@ -636,6 +673,37 @@ public class NetworkService extends Service {
public void onClosed(WebSocket webSocket, int code, String reason) { public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason); super.onClosed(webSocket, code, reason);
Log.d(TAG,"onClosed"); Log.d(TAG,"onClosed");
if (code == NO_HISTORY_CLOSURE_STATUS)
handleWebSocketDisconnection(true);
else
handleWebSocketDisconnection(false);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
Log.e(TAG,"onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
// Logging error stack trace
for(StackTraceElement element : t.getStackTrace()){
Log.v(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
}
// If there is a response, we print it
if(response != null){
Log.e(TAG,"Response: "+response.message());
}
handleWebSocketDisconnection(true);
}
/**
* Method that encapsulates the behavior of handling a disconnection to the current node, and
* potentially tries to reconnect to another one.
*
* @param tryReconnection Variable that states if a reconnection to other node should be tried.
*/
private void handleWebSocketDisconnection(boolean tryReconnection) {
RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE)); RxBus.getBusInstance().send(new ConnectionStatusUpdate(ConnectionStatusUpdate.DISCONNECTED, ApiAccess.API_NONE));
isLoggedIn = false; isLoggedIn = false;
@ -647,29 +715,11 @@ public class NetworkService extends Service {
if(nodeLatencyVerifier != null) if(nodeLatencyVerifier != null)
nodeLatencyVerifier.updateActiveNodeInformation(mSelectedNode); nodeLatencyVerifier.updateActiveNodeInformation(mSelectedNode);
if(tryReconnection) {
// We have currently no selected node
mSelectedNode = null;
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
Log.e(TAG,"onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
// Logging error stack trace
for(StackTraceElement element : t.getStackTrace()){
Log.v(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
}
// Registering current status // Registering current status
isLoggedIn = false;
mCurrentId = 0; mCurrentId = 0;
mApiIds.clear(); mApiIds.clear();
// If there is a response, we print it
if(response != null){
Log.e(TAG,"Response: "+response.message());
}
// Adding a very high latency value to this node in order to prevent // Adding a very high latency value to this node in order to prevent
// us from getting it again // us from getting it again
mSelectedNode.addLatencyValue(Long.MAX_VALUE); mSelectedNode.addLatencyValue(Long.MAX_VALUE);
@ -690,12 +740,7 @@ public class NetworkService extends Service {
} }
}, DEFAULT_RETRY_DELAY); }, DEFAULT_RETRY_DELAY);
} }
// Marking the selected node as not connected }
mSelectedNode.setConnected(false);
// Updating the selected node's 'connected' status on the NodeLatencyVerifier instance
if(nodeLatencyVerifier != null)
nodeLatencyVerifier.updateActiveNodeInformation(mSelectedNode);
// We have currently no selected node // We have currently no selected node
mSelectedNode = null; mSelectedNode = null;

View file

@ -25,14 +25,14 @@ import cy.agorise.graphenej.stats.ExponentialMovingAverage;
*/ */
public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks { public class NetworkServiceManager implements Application.ActivityLifecycleCallbacks {
private final static String TAG = "NetworkServiceManager"; private final String TAG = this.getClass().getName();
/** /**
* Constant used to specify how long will the app wait for another activity to go through its starting life * Constant used to specify how long will the app wait for another activity to go through its starting life
* cycle events before running the teardownConnectionTask task. * cycle events before running the teardownConnectionTask task.
* *
* This is used as a means to detect whether or not the user has left the app. * This is used as a means to detect whether or not the user has left the app.
*/ */
private final int DISCONNECT_DELAY = 1500; private static final int DISCONNECT_DELAY = 1500;
/** /**
* Handler instance used to schedule tasks back to the main thread * Handler instance used to schedule tasks back to the main thread
@ -72,8 +72,8 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
} }
}; };
public NetworkServiceManager(Context context){ private NetworkServiceManager(Context context){
mContextReference = new WeakReference<Context>(context); mContextReference = new WeakReference<>(context);
} }
@Override @Override
@ -89,7 +89,15 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
// Creating a new Intent that will be used to start the NetworkService // Creating a new Intent that will be used to start the NetworkService
Context context = mContextReference.get(); Context context = mContextReference.get();
Intent intent = new Intent(context, NetworkService.class); Intent intent = new Intent(context, NetworkService.class);
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
/**
* This method passes all the required information to the NetworkService to properly
* initialize itself
*/
private void passRequiredInfoToConfigureService() {
// Adding user-provided node URLs // Adding user-provided node URLs
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
Iterator<String> it = mCustomNodeUrls.iterator(); Iterator<String> it = mCustomNodeUrls.iterator();
@ -99,15 +107,17 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
} }
String customNodes = stringBuilder.toString(); String customNodes = stringBuilder.toString();
Bundle b = new Bundle();
// Adding all // Adding all
intent.putExtra(NetworkService.KEY_USERNAME, mUserName) b.putString(NetworkService.KEY_USERNAME, mUserName);
.putExtra(NetworkService.KEY_PASSWORD, mPassword) b.putString(NetworkService.KEY_PASSWORD, mPassword);
.putExtra(NetworkService.KEY_REQUESTED_APIS, mRequestedApis) b.putInt(NetworkService.KEY_REQUESTED_APIS, mRequestedApis);
.putExtra(NetworkService.KEY_NODE_URLS, customNodes) b.putString(NetworkService.KEY_NODE_URLS, customNodes);
.putExtra(NetworkService.KEY_AUTO_CONNECT, mAutoConnect) b.putBoolean(NetworkService.KEY_AUTO_CONNECT, mAutoConnect);
.putExtra(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, mVerifyLatency); b.putBoolean(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, mVerifyLatency);
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} mService.bootstrapService(b);
} }
@Override @Override
@ -133,6 +143,8 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
// We've bound to LocalService, cast the IBinder and get LocalService instance // We've bound to LocalService, cast the IBinder and get LocalService instance
NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service; NetworkService.LocalBinder binder = (NetworkService.LocalBinder) service;
mService = binder.getService(); mService = binder.getService();
passRequiredInfoToConfigureService();
} }
@Override @Override