I am trying to get the Authentication Token from an account in Android before I make my request to the server. I Am trying to control the flow with a CountdownLatch so that it waits until:
a) A timeout (10s)
b) We get the token
private CountDownLatch tokenLatch = new CountDownLatch(1);
final long tokenTimeoutSeconds = 10;
AccountManager manager = AccountManager.get(mContext);
Account userAccount = getCurrentAccount();
// Get the auth token
if (userAccount != null) {
AccountManagerFuture<Bundle> future = manager.getAuthToken(userAccount, AccountUtility.AUTHTOKEN_TYPE_FULL_ACCESS, true, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
currentAuthToken = bundle.get(AccountManager.KEY_AUTHTOKEN).toString();
tokenLatch.countDown();
} catch (Exception e) {
Log.e(LOG_TAG, "Problem getting auth token!", e);
}
}
}, null);
try {
tokenLatch.await(tokenTimeoutSeconds, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e(LOG_TAG, "Interupted while getting auth token!", e);
}
Context is passed:
mContext = ... getApplicationContext();
Right now it exits before either of those two cases. It does, however, always reach the AccountManagerCallback after all other processes are finished. Strange. I am most definitely doing something wrong.
Thanks for the helperooni!
This explanation presumes the posted code is running on the main thread. Because the Handler parameter in the call to getAuthToken() is null, the callback will also run on the main thread. This is a deadlock situation. After calling getAuthToken() the main thread blocks on the latch await(). The callback cannot run because the main thread is blocked. The latch never counts down to zero because the callback can't run.
The code posted at this blog offers an example of how an auth token can be obtained on the main thread without blocking.
Related
I'm trying to use biometric authentication. However my setup is complex, basically I'm trying to keep the function sync because it is invoked from C++:
User interaction -> C++ function -> Java JNI function -> Biometric Authenticate <- needs to go back
Skipping the C++ code, it calls via JNI the following function:
public String getSeed() throws ExecutionException, InterruptedException {
Context reactContext = this.getReactApplicationContext();
Activity act = this.getCurrentActivity();
act.runOnUiThread(new Runnable() {
#Override
public void run() {
Executor executor = ContextCompat.getMainExecutor(reactContext);
BiometricPrompt.AuthenticationCallback authenticationCallback = new WalletCoreAuthenticationCallback();
BiometricPrompt.PromptInfo info = BiometricUtils.createBiometricPromptInfo("ROPO", "ROPO", "ROPO");
BiometricPrompt prompt = new BiometricPrompt((FragmentActivity) act, executor, authenticationCallback);
prompt.authenticate(info);
}
});
// Here I need a Handler.merge or something similar to pause the execution while the user authenticates and then I retrieve the answer.
try {
return keyStore.getPlainText(getReactApplicationContext(), SEED_KEY);
} catch (FileNotFoundException fnfe) {
Log.w(Constants.TAG, "Could not get seed (file not found)");
return null;
} catch (Exception e) {
Log.w(Constants.TAG, "Could not get seed");
return null;
}
}
The idea is: if the user fails to authenticate I do not fetch the sensitive information (keyStore.getPlainText).
The problem however lies in the fact that the BiometricPrompt needs to be called from the main (UI) thread. I'm an Android noob, so far that was the best I could come up with, it in effect prompts the user for authentication, but I do not know how to pause/join the main java function call, until the user has authenticated.
Is this even possible?
Found one way to make it work that is simple enough, using a Mutex.
Each call to the parent Java creates a mutex (I also added one field to the WalletCoreAuthenticationCallback object to hold the response). Then I release the mutex on inside the calls, and just check for the stored response.
final Semaphore mutex = new Semaphore(0);
// This object now internally saves the response of the authentication callback
WalletCoreAuthenticationCallback authenticationCallback = new WalletCoreAuthenticationCallback(mutex);
act.runOnUiThread(new Runnable() {
#Override
public void run() {
Executor executor = ContextCompat.getMainExecutor(reactContext);
BiometricPrompt.PromptInfo info = BiometricUtils.createBiometricPromptInfo("ROPO", "ROPO", "ROPO");
BiometricPrompt prompt = new BiometricPrompt((FragmentActivity) act, executor, authenticationCallback);
prompt.authenticate(info);
}
});
try {
mutex.acquire();
} catch (InterruptedException e) {
Log.e(Constants.TAG, "INterruped mutex exception");
}
if(!authenticationCallback.isAuthenticated) {
return null;
}
This however has one side-effect, basically locking the calling thread, I'm calling this from a React Native application, which basically means it freezes the app. However since Auth is such a vital step, it's ok to freeze the app since the user cannot continue without authentication anyways.
If anybody has a more elegant solution, happy to check it out.
I've got a Spring Boot application which processes messages from an AWS SQS queue. The application makes a request for 10 messages, initiates a CountDownLatch to 10, then passes each message to an #Async method which returns a CompletableFuture. The CompletableFuture has a thenAccept() which deletes the message, and a whenComplete() which logs an exception if there is one and decrements the latch countdown. When the countdown completes, the next batch of messages is retrieved and the process starts over.
When there is no exception, this all runs perfectly. However, if an exception is thrown within the CompletableFuture method, the method executes twice before returning to the whenComplete().
Main method:
public void readAllMessages(String sqsUrl, MessageConsumer messageConsumer) {
ReceiveMessageRequest receiveMessageRequest = new ReceiveMessageRequest(sqsUrl).withMaxNumberOfMessages(10);
List<Message> messages;
try {
do {
messages = amazonSQS.receiveMessage(receiveMessageRequest).getMessages();
if (CollectionUtils.isNotEmpty(messages)) {
final CountDownLatch latch = new CountDownLatch(messages.size());
messages.forEach(message -> {
try {
messageConsumer.processMessage(message)
.thenAccept(m -> {
deleteMessage(sqsUrl, message);
})
.whenComplete((value, exception) -> {
LOGGER.info("Processing complete for message {}", message.getMessageId());
latch.countDown();
if (exception != null) {
exceptionLogger.logException(String.format("Couldn't process message. queue:%s. id:%s", sqsUrl, message.getMessageId()), exception);
}
});
} catch (Throwable e) {
LOGGER.error("Refreshing tax rate for message {} failed with exception {} ", message.getBody(), e);
}
});
latch.await();
} else {
LOGGER.debug("Queue is empty: '{}'", sqsUrl);
}
} while (CollectionUtils.isNotEmpty(messages));
} catch (InterruptedException e) {
LOGGER.info("Thread interrupted, stopping.");
}
}
MessageConsumer.processMessage
#XRayLogged(segmentName = "outdated_host_tax_rate_update")
#Async
#Transactional
#Override
public CompletableFuture<?> processMessage(Message message) {
OutdatedTaxRateMessage taxRateMessage;
try {
taxRateMessage = objectMapper.readValue(message.getBody(), OutdatedTaxRateMessage.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
Long id = taxRateMessage.getHostId();
LOGGER.info("Updating tax rate. hostId:'{}', messageId:'{}'", id, message.getMessageId());
hostTaxRateRefreshingService.refreshHostTaxRate(id);
LOGGER.info("Updated tax rate. hostId:'{}', messageId:'{}'", id, message.getMessageId());
return CompletableFuture.completedFuture(null);
}
When an exception is thrown, the "Updating tax rate. hostId:''" message is logged twice, followed by a single set of messages from the whenComplete() block ("Processing complete...", "Couldn't process message...")
Can anyone please help me understand why this is happening?
The cause of the issue was determined to be a custom annotation on the method. The annotation was meant to transmit information to AWS Xray, but was inadvertently executing the annotated method twice when an exception was thrown. That mechanism is still being worked out, but at least we've identified the culprit. The question has been updated, as the annotation had been left off.
I don't have much experience about multi threading so kindly help me out. I have a background thread in which i connect my google api client to find my current location. When i call myGoogleApiClient.connect() it tries to connect and i receive a call back when it's connected but after calling connect method my flows go back. I want my program to wait there and continue executing my next tasks. Here is the code
public class CurrentLocation implements GoogleApiClient.OnConnectionFailedListener,GoogleApiClient.ConnectionCallbacks{
private GoogleApiClient mGoogleApiClient;
private String placesTextFile;
Context context;
String TAG="NearbyPlaces";
CurrentLocation(Context context) {
this.context = context;
mGoogleApiClient = new GoogleApiClient
.Builder(context)
.addApi(Places.GEO_DATA_API)
.addApi(Places.PLACE_DETECTION_API)
.addOnConnectionFailedListener(this)
.addConnectionCallbacks(this)
.build();
}
private void connect() {
Log.d(TAG,"run called");
if(mGoogleApiClient.isConnected())
findLocations();
else
mGoogleApiClient.connect(); //Here my flow goes back but i want my program to wait here till it gets onConnected callback
}
private void findLocations(){
// some code here that need to be executed when my client connects
}
#Override
public void onConnected(#Nullable Bundle bundle) {
Log.d(TAG,"Google Client is Connected");
findLocations();
}
}
I'm calling my connect method from a timer task like this
private void StartTracker() {
Log.d(TAG,"TimerTask is in waiting state now");
timerScheduler.schedule(new TimerTask() {
#Override
public void run() {
while (isServiceRunning){
try {
currentLocation.connect();
//video recorder should only be started when i will find out my current location successfully
videoRecorder.startVideoRecorder();
Thread.sleep(getRandomRecordingDuration());
videoRecorder.stopVideoRecorder();
Thread.sleep(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, delayTime);
}
According to your code , you just want to do something everytime after you call the "connect" method and successfully connect.So maybe you'd better to do the things in the call back "onConnected".
so finally found the answer of my question. I used this technique to resolve my issue.
//for waiting to complete another job
synchronized (synchObj) {
try { synchObj.wait();}
catch (InterruptedException ie) {
}
}
//when job is done and want the execution from where it was stoped
synchronized (synchObj) {
synchObj.notify();
}
followed this guy to solve my problem http://forums.devshed.com/java-help-9/block-thread-callback-method-called-thread-904920.html
I am using the Ebay API to place a bid on an item. If there is some kind of network error so that the API call does not return, I want to retry the call immediately afterwards. It seems so simple but I've been going round in circles all day. I'm not really experienced with threading. Is this how it's supposed to work or am I totally wrong?
Here is the Callable class:
public class PlaceOfferThread implements Callable<Boolean> {
private PlaceOfferCall call;
public Boolean isComplete;
public PlaceOfferThread (PlaceOfferCall p) {
call = p;
}
#Override
public Boolean call() throws Exception {
try {
call.placeOffer();
return true;
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
return false;
}
}
And here is the caller
int timeout = 10;
int maxRetries = 5;
int retries = 0;
ExecutorService executor = Executors.newSingleThreadExecutor();
PlaceOfferThread thread = new PlaceOfferThread(call);
boolean flag = false;
while (!flag && retries++ < maxRetries) {
Future<Boolean> future = null;
try {
future = executor.submit(thread);
flag = future.get(timeout, TimeUnit.SECONDS);
future.cancel(true);
}
catch(TimeoutException ex) {
// no response from Ebay, potential network issues
// resubmit the call to Ebay with the same invocation id
future.cancel(true);
}
catch (Exception threadException) {
// any other exception indicates that we got a response from Ebay
// it just wasn't the response we wanted
throw new Exception(threadException.getMessage());
}
}
executor.shutdown(); // TODO
If there is some kind of network error so that the API call does not return, I want to retry the call immediately afterwards.
I'm not 100% sure how your application is working right now but here are some thoughts:
When you call future.cancel(true) you most likely will not be stopping the current transaction. Unless you are using NIO calls, IO methods are not interruptible. Interrupting a thread just sets a flag on the thread and caused those few methods that throw InterruptedException (like sleep, wait, join) to do so. You'll have to watch the Thread.currentThread().isInterrupted() method to be able to see the interrupt otherwise.
I think the right thing to do is to set the connection and IO timeouts of the underlying http-client object and have it throw or exit with an error if there are problems. Trying to kill it from another thread is going to be much more difficult.
In looking at your code I'm not sure why you are using threads at all. Maybe you are doing other processing but it might be better to make the call directly. Then you can tune the HttpClient's IO timeouts and handle them appropriately.
Imagine I'm in a Service that already has a background thread. Can I do a request using volley in that same thread, so that callbacks happen synchronously?
There are 2 reasons for this:
First, I do not need another thread and it would be a waste to create it.
Second, if I'm in a ServiceIntent, the execution of the thread will finish before the callback, and therefor I will have no response from Volley. I know I can create my own Service that has some thread with a runloop I can control, but it would be desirable having this functionality in volley.
It looks like it is possible with Volley's RequestFuture class. For example, to create a synchronous JSON HTTP GET request, you can do the following:
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(URL, new JSONObject(), future, future);
requestQueue.add(request);
try {
JSONObject response = future.get(); // this will block
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
}
Note #Matthews answer is correct BUT if you are on another thread and you do a volley call when you have no internet, your error callback will be called on the main thread, but the thread you are on will be blocked FOREVER. (Therefore if that thread is an IntentService, you will never be able to send another message to it and your service will be basically dead).
Use the version of get() that has a timeout future.get(30, TimeUnit.SECONDS) and catch the error to exit your thread.
To match #Mathews answer:
try {
return future.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
} catch (TimeoutException e) {
// exception handling
}
Below I wrapped it in a method & use a different request:
/**
* Runs a blocking Volley request
*
* #param method get/put/post etc
* #param url endpoint
* #param errorListener handles errors
* #return the input stream result or exception: NOTE returns null once the onErrorResponse listener has been called
*/
public InputStream runInputStreamRequest(int method, String url, Response.ErrorListener errorListener) {
RequestFuture<InputStream> future = RequestFuture.newFuture();
InputStreamRequest request = new InputStreamRequest(method, url, future, errorListener);
getQueue().add(request);
try {
return future.get(REQUEST_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e("Retrieve cards api call interrupted.", e);
errorListener.onErrorResponse(new VolleyError(e));
} catch (ExecutionException e) {
Log.e("Retrieve cards api call failed.", e);
errorListener.onErrorResponse(new VolleyError(e));
} catch (TimeoutException e) {
Log.e("Retrieve cards api call timed out.", e);
errorListener.onErrorResponse(new VolleyError(e));
}
return null;
}
It is probably recommended to use the Futures, but if for whatever reason you don't want to, instead of cooking your own synchronized blocking thing you should use a java.util.concurrent.CountDownLatch. So that would work like this..
//I'm running this in an instrumentation test, in real life you'd ofc obtain the context differently...
final Context context = InstrumentationRegistry.getTargetContext();
final RequestQueue queue = Volley.newRequestQueue(context);
final CountDownLatch countDownLatch = new CountDownLatch(1);
final Object[] responseHolder = new Object[1];
final StringRequest stringRequest = new StringRequest(Request.Method.GET, "http://google.com", new Response.Listener<String>() {
#Override
public void onResponse(String response) {
responseHolder[0] = response;
countDownLatch.countDown();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
responseHolder[0] = error;
countDownLatch.countDown();
}
});
queue.add(stringRequest);
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (responseHolder[0] instanceof VolleyError) {
final VolleyError volleyError = (VolleyError) responseHolder[0];
//TODO: Handle error...
} else {
final String response = (String) responseHolder[0];
//TODO: Handle response...
}
Since people seemed to actually try to do this and ran into some trouble I decided I'd actually provide a "real life" working sample of this in use. Here it is https://github.com/timolehto/SynchronousVolleySample
Now even though the solution works, it has some limitations. Most importantly, you can't call it on the main UI thread. Volley does execute the requests on the background, but by default Volley uses the main Looper of the application to dispatch the responses. This causes a deadlock as the main UI thread is waiting for the response, but the Looper is waiting for onCreate to finish before processing the delivery. If you really really want to do this you could, instead of the static helper methods, instantiate your own RequestQueue passing it your own ExecutorDelivery tied to a Handler using a Looper which is tied to different thread from the main UI thread.
You achieve this with kotlin Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7"
private suspend fun request(context: Context, link : String) : String{
return suspendCancellableCoroutine { continuation ->
val queue = Volley.newRequestQueue(context)
val stringRequest = StringRequest(Request.Method.GET, link,
{ response ->
continuation.resumeWith(Result.success(response))
},
{
continuation.cancel(Exception("Volley Error"))
})
queue.add(stringRequest)
}
}
And call with
CoroutineScope(Dispatchers.IO).launch {
val response = request(CONTEXT, "https://www.google.com")
withContext(Dispatchers.Main) {
Toast.makeText(CONTEXT, response,Toast.LENGTH_SHORT).show()
}
}
As a complementary observation to both #Blundells and #Mathews answers, I'm not sure any call is delivered to anything but the main thread by Volley.
The Source
Having a look at the RequestQueue implementation it seems the RequestQueue is using a NetworkDispatcher to execute the request and a ResponseDelivery to deliver the result (the ResponseDelivery is injected into the NetworkDispatcher). The ResponseDelivery is in turn created with a Handler spawn from the main thread (somewhere around line 112 in the RequestQueue implementation).
Somewhere about line 135 in the NetworkDispatcher implementation it seems like also successful results are delivered through the same ResponseDelivery as any errors. Again; a ResponseDelivery based on a Handler spawn from the main thread.
Rationale
For the use-case where a request is to be made from an IntentService it's fair to assume that the thread of the service should block until we have a response from Volley (to guarantee a living runtime scope to handle the result in).
Suggested solutions
One approach would be to override the default way a RequestQueue is created, where an alternative constructor is used instead, injecting a ResponseDelivery which spawns from the current thread rather than the main thread. I haven't investigated the implications of this, however.
I want to add something to Matthew's accepted answer. While RequestFuture might seem to make a synchronous call from the thread you created it, it does not. Instead, the call is executed on a background thread.
From what I understand after going through the library, requests in the RequestQueue are dispatched in its start() method:
public void start() {
....
mCacheDispatcher = new CacheDispatcher(...);
mCacheDispatcher.start();
....
NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
networkDispatcher.start();
....
}
Now both CacheDispatcher and NetworkDispatcher classes extend thread. So effectively a new worker thread is spawned for dequeuing the request queue and the response is returned to the success and error listeners implemented internally by RequestFuture.
Although your second purpose is attained but you first purpose is not since a new thread is always spawned, no matter from which thread you execute RequestFuture.
In short, true synchronous request is not possible with default Volley library. Correct me if I am wrong.
I use a lock to achieve that effect now im wondering if its correct my way
anyone want to comment ?
// as a field of the class where i wan't to do the synchronous `volley` call
Object mLock = new Object();
// need to have the error and success listeners notifyin
final boolean[] finished = {false};
Response.Listener<ArrayList<Integer>> responseListener = new Response.Listener<ArrayList<Integer>>() {
#Override
public void onResponse(ArrayList<Integer> response) {
synchronized (mLock) {
System.out.println();
finished[0] = true;
mLock.notify();
}
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
synchronized (mLock) {
System.out.println();
finished[0] = true;
System.out.println();
mLock.notify();
}
}
};
// after adding the Request to the volley queue
synchronized (mLock) {
try {
while(!finished[0]) {
mLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
You can do sync request with volley but you must call the method in different thread otherwise your running app will block, it should be like this :
public String syncCall(){
String URL = "http://192.168.1.35:8092/rest";
String response = new String();
RequestQueue requestQueue = Volley.newRequestQueue(this.getContext());
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, URL, new JSONObject(), future, future);
requestQueue.add(request);
try {
response = future.get().toString();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
after that you can call the method in thread :
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
String response = syncCall();
}
});
thread.start();