I've a webservice client that invokes several webservices with same wsdl on different endpoint, so I decided to make asynchronous calls in a parallel way.
I generated wsimport async jax-ws client and I decided to use a Future invocation with Async callback (using a single AsyncHandler instance for all the futures operation),
The question is: is there a way in the handleResponse(Response response) method to know to which of the future in the futures list the response is referred? Anything that can be done in the request context and propagate to reponse context?
Should I indeed preserve a state in the AsyncHandler and instatiate a new Handler foreach future operation and store the response in a shared list (maybe a thread safe class member)?
Is there a way to Propagate Request Context to the Response as in here?
[https://docs.oracle.com/middleware/1213/wls/WSGET/jax-ws-async-roadmap.htm#BABFAFHD][1]
Thanks all. My snippet:
WsSoapClient ws = new ....;
List<Future> futures = new ArrayList<>();
for (String url : urlList) {
BindingProvider bp = (BindingProvider) ws;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
Future invokeAsync = ws.getAsync("foo", mh);
futures.add(invokeAsync);
}
futures.stream().parallel().forEach((t) -> {
try {
t.get(15L, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
}
});
class MessageHandler implements AsyncHandler<String> {
private final List<String> responses;
public MessageHandler() {
List<String> rr = new ArrayList<>();
responses = Collections.synchronizedList(rr);
}
#Override
public void handleResponse(Response<String> response) {
try {
String get = response.get();
responses.add(get);
} catch (Exception e) {
e.printStackTrace();
}
}
public List<String> getResponses() {
return responses;
}
}
[1]: https://docs.oracle.com/middleware/1213/wls/WSGET/jax-ws-async-roadmap.htm#BABFAFHD
I ended up implementing a custom callback handler that mantains a state. One different instance for each future.
Related
I have an executor service which creates, say, 10 threads. Each thread is supposed to handle processing of some piece of data. Finally, we have to wait for all the 10 threads to finish processing.
First approach
This is how I am currently implementing this:
public Processor implements Callable<Void> {
private Data myData;
public Processor(Data myData) {
this.myData = myData;
}
public Void call() throws BusinessLogicException {
// Business logic to handle myData
}
}
Handler class to create & handle threads:
public class Handler() {
ExecutorService executorService;
public Handler() {
executorService = Executors.newFixedThreadPool(10);
}
public void handle(List<Data> dataList) {
List<Processor> workers = new ArrayList<>();
List<String> dataId = new ArrayList<>();
for(Data data : dataList) {
workers.add(new Processor(data));
dataId.add(data.getId());
}
try {
List<Future<Void>> futures = executorService.invokeAll(workers);
int i = 0;
for(Future<Void> future : futures) {
try {
future.get();
} catch(CancellationException | InterruptedException | ExecutionException e) {
System.err.println("For data with id " + dataId.get(i) + " encountered " + e);
} finally {
i++;
}
}
} catch(InterruptedException | NullPointerException | RejectedExecutionException e) {
System.err.println(e);
} finally {
executorService.shutdown();
}
}
}
Second approach
Another way I can think of is to write callable as a lambda expression. Something of the sorts:
List<Future<Void>> futures = new ArrayList<>();
for(Data myData: dataList) {
workers.add(
executorService.submit( () -> {
try {
//Business logic to handle myData
} catch (Exception e) {
//Log the error using myData and e
//Need to handle all exceptions here, not just BusinessLogicException
}
})
)
}
for(Future<Void> future : futures) {
//The same try-catch logic as above
}
I need feedback on which is the better and cleaner code design to handle this problem.
Issues I see with first approach:
Multiple instances of Processor created. Processor is stateful because it need input Data to process - I can't seem to think of a good way to circumvent this issue.
I need to maintain a List dataId which I use to identify the ID of the data which a thread failed to process.
Issues I see with second approach:
Seems messy compared to first one.
Need to handle all the exceptions within the lambda expression.
Here's a sample of the code I've been working on
items contains 100 elements, thus obtaining data using synchronous calling takes up a lot of time. Can someone suggest a way to increase the speed of this operation so that it takes less time.
Currently this takes 15-20 seconds to execute. I'm new to rxjava so please provide a detailed solution to this problem if possible. dataResponses contains RouteDistance objects for each of the 100 items.
for(int i = 0 ; i<items.size();i++){
Map<String, String> map2 = new HashMap<>();
map2.put("units", "metric");
map2.put("origin", currentLocation.getLatitude()+","+currentLocation.getLongitude());
map2.put("destination", items.get(i).getPosition().get(0)+","+items.get(i).getPosition().get(1));
map2.put("transportMode", "car");
requests.add(RetrofitClient4_RouteDist.getClient().getRouteDist(map2));
}
Observable.zip(requests, new Function<Object[], List<RouteDist>>() {
#Override
public List<RouteDist> apply(Object[] objects) throws Exception {
Log.i("onSubscribe", "apply: " + objects.length);
List<RouteDist> dataaResponses = new ArrayList<>();
for (Object o : objects) {
dataaResponses.add((RouteDist) o);
}
return dataaResponses;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
new Consumer<List<RouteDist>>() {
#Override
public void accept(List<RouteDist> dataaResponses) throws Exception {
Log.i("onSubscribe", "YOUR DATA IS HERE: "+dataaResponses.toString());
recyclerViewAdapter_profile = new RecyclerViewAdapter_Profile(items,dataaResponses);
recyclerView.setAdapter(recyclerViewAdapter_profile);
}
},
new Consumer<Throwable>() {
#Override
public void accept(Throwable e) throws Exception {
Log.e("onSubscribe", "Throwable: " + e);
}
});
API
interface Client {
Observable<RouteDist> routeDist();
}
final class RouteDist {
}
final class ClientImpl implements Client {
#Override
public Observable<RouteDist> routeDist() {
return Observable.fromCallable(() -> {
// with this log, you see, that each subscription to an Observable is executed on the ThreadPool
// Log.e("---------------------", Thread.currentThread().getName());
return new RouteDist();
});
}
}
Apply threading via subscribeOn
final class ClientProxy implements Client {
private final Client api;
private final Scheduler scheduler;
ClientProxy(Client api, Scheduler scheduler) {
this.api = api;
this.scheduler = scheduler;
}
#Override
public Observable<RouteDist> routeDist() {
// apply #subscribeOn in order to move subscribeAcutal call on given Scheduler
return api.routeDist().subscribeOn(scheduler);
}
}
AndroidTest
#Test
public void name() {
// CachedThreadPool, in order to avoid creating 100-Threads or more. It is always a good idea to use own Schedulers (e.g. Testing)
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, 10,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>());
// wrap real client with Proxy, in order to move the subscribeActual call to the ThreadPool
Client client = new ClientProxy(new ClientImpl(), Schedulers.from(threadPool));
List<Observable<RouteDist>> observables = Arrays.asList(client.routeDist(), client.routeDist(), client.routeDist());
TestObserver<List<RouteDist>> test = Observable.zip(observables, objects -> {
return Arrays.stream(objects).map(t -> (RouteDist) t).collect(Collectors.toList());
})
.observeOn(AndroidSchedulers.mainThread())
.test();
test.awaitCount(1);
// verify that onNext in subscribe is called in Android-EventLoop
assertThat(test.lastThread()).isEqualTo(Looper.getMainLooper().getThread());
// verify that 3 calls were made and merged into one List
test.assertValueAt(0, routeDists -> {
assertThat(routeDists).hasSize(3);
return true;
});
}
Further reading:
http://tomstechnicalblog.blogspot.de/2016/02/rxjava-understanding-observeon-and.html
Note:
It is not recommanded to call an API 100-times concurrently at once. Furthermore when using Zip, this is what will acutally happen, when you have a ThreadPool, which is big enough. When one API-call times-out, an onError will probably emitted for this API-calls. The onError will be propagated further to the subscriber. You will not get any result, even if only on API-call fails. It is recommanded to have some onErrorResumeNext or some other error-handling operator, in order to ensure, that one API-call does not cancel the overall result.
Is it possible with Project Reactor to wait in a mono for an event / condition without needing to use a blocking thread per mono? With a CompletableFuture I can pull such a thing off but I can't see how to do it with Project Reactor.
My problem is that I need to correlate requests with responses. The response time varies wildly and some will even never get a reply and timeout. When on the client side a blocking thread per request isn't a problem but since this is a server application I don't want to end up with spawning a thread per request that blocks waiting for a response.
The API looks something like this:
Mono<Response> doRequest(Mono<Request> request);
Since I don't know how to do it with Reactor I will explain how to do it with a CompletableFuture to clarify what I'm looking for. The API would look like this:
CompletableFuture<Response> doRequest(Request request);
When invoked by a caller a request to a server is made which has a correlation ID in it generated by this method. The caller is returned a CompletableFuture and the method stores a reference to this CompletableFuture in map with the correlation ID as key.
There is also a thread (pool) which receives all the responses for the server. When it receives a response it takes the correlation ID from the response and uses it to look up the original request (ie. the CompletableFuture) in the map and calls complete(response); on it.
In this implementation you don't need a blocking thread per request. This is basically more of a Vert.X / Netty way of thinking? I would like to know how to implement such a thing (if possible) with Project Reactor.
EDIT 25-07-2019:
As per request in the comments to clarify what I'm getting at below is an example of how I would implement this with CompleteableFuture's.
I also noticed I made a mistake which might have been rather confusing: In the CompletableFuture example I passed a Mono as argument. That should have been just a "normal" argument. My apologies and I hope I didn't confuse people too much with it.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
class NonBlockingCorrelatingExample {
/**
* This example shows how to implement correlating requests with responses without needing a (sleeping)
* thread per request to wait for the response with the use of {#link CompletableFuture}'s.
*
* So the main feat of this example is that there is always a fixed (small) number of threads used even if one
* would fire a thousands requests.
*/
public static void main(String[] args) throws Exception {
RequestResponseService requestResponseService = new RequestResponseService();
Request request = new Request();
request.correlationId = 1;
request.question = "Do you speak Spanish?";
CompletableFuture<Response> responseFuture = requestResponseService.doRequest(request);
responseFuture.whenComplete((response, throwable) -> System.out.println(response.answer));
// The blocking call here is just so the application doesn't exit until the demo is completed.
responseFuture.get();
}
static class RequestResponseService {
/** The key in this map is the correlation ID. */
private final ConcurrentHashMap<Long, CompletableFuture<Response>> responses = new ConcurrentHashMap<>();
CompletableFuture<Response> doRequest(Request request) {
Response response = new Response();
response.correlationId = request.correlationId;
CompletableFuture<Response> reponseFuture = new CompletableFuture<>();
responses.put(response.correlationId, reponseFuture);
doNonBlockingFireAndForgetRequest(request);
return reponseFuture;
}
private void doNonBlockingFireAndForgetRequest(Request request) {
// In my case this is where the request would be published on an MQTT broker (message bus) in a request topic.
// Right now we will just make a call which will simulate a response message coming in after a while.
simulateResponses();
}
private void processResponse(Response response) {
// There would usually be a (small) thread pool which is subscribed to the message bus which receives messages
// in a response topic and calls this method to handle those messages.
CompletableFuture<Response> responseFuture = responses.get(response.correlationId);
responseFuture.complete(response);
}
void simulateResponses() {
// This is just to make the example work. Not part of the example.
new Thread(() -> {
try {
// Simulate a delay.
Thread.sleep(10_000);
Response response = new Response();
response.correlationId = 1;
response.answer = "Si!";
processResponse(response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
static class Request {
long correlationId;
String question;
}
static class Response {
long correlationId;
String answer;
}
}
Yes, it is possible. You can use reactor.core.publisher.Mono#create method to achieve it
For your example:
public static void main(String[] args) throws Exception {
RequestResponseService requestResponseService = new RequestResponseService();
Request request = new Request();
request.correlationId = 1;
request.question = "Do you speak Spanish?";
Mono<Request> requestMono = Mono.just(request)
.doOnNext(rq -> System.out.println(rq.question));
requestResponseService.doRequest(requestMono)
.doOnNext(response -> System.out.println(response.answer))
// The blocking call here is just so the application doesn't exit until the demo is completed.
.block();
}
static class RequestResponseService {
private final ConcurrentHashMap<Long, Consumer<Response>> responses =
new ConcurrentHashMap<>();
Mono<Response> doRequest(Mono<Request> request) {
return request.flatMap(rq -> doNonBlockingFireAndForgetRequest(rq)
.then(Mono.create(sink -> responses.put(rq.correlationId, sink::success))));
}
private Mono<Void> doNonBlockingFireAndForgetRequest(Request request) {
return Mono.fromRunnable(this::simulateResponses);
}
private void processResponse(Response response) {
responses.get(response.correlationId).accept(response);
}
void simulateResponses() {
// This is just to make the example work. Not part of the example.
new Thread(() -> {
try {
// Simulate a delay.
Thread.sleep(10_000);
Response response = new Response();
response.correlationId = 1;
response.answer = "Si!";
processResponse(response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
I need to send multiple requests to many different web services and receive the results. The problem is that, if I send the requests one by one it takes so long as I need to send and process all individually.
I am wondering how I can send all the requests at once and receive the results.
As the following code shows, I have three major methods and each has its own sub methods.
Each sub method sends request to its associated web service and receive the results;therefore, for example, to receive the results of web service 9 I have to wait till all web services from 1 to 8 get completed, it takes a long time to send all the requests one by one and receive their results.
As shown below none of the methods nor sub-methods are related to each other, so I can call them all and receive their results in any order, the only thing which is important is to receive the results of each sub-method and populate their associated lists.
private List<StudentsResults> studentsResults = new ArrayList();
private List<DoctorsResults> doctorsResults = new ArrayList();
private List<PatientsResults> patientsResults = new ArrayList();
main (){
retrieveAllLists();
}
retrieveAllLists(){
retrieveStudents();
retrieveDoctors();
retrievePatients();
}
retrieveStudents(){
this.studentsResults = retrieveStdWS1(); //send request to Web Service 1 to receive its list of students
this.studentsResults = retrieveStdWS2(); //send request to Web Service 2 to receive its list of students
this.studentsResults = retrieveStdWS3(); //send request to Web Service 3 to receive its list of students
}
retrieveDoctors(){
this.doctorsResults = retrieveDocWS4(); //send request to Web Service 4 to receive its list of doctors
this.doctorsResults = retrieveDocWS5(); //send request to Web Service 5 to receive its list of doctors
this.doctorsResults = retrieveDocWS6(); //send request to Web Service 6 to receive its list of doctors
}
retrievePatients(){
this.patientsResults = retrievePtWS7(); //send request to Web Service 7 to receive its list of patients
this.patientsResults = retrievePtWS8(); //send request to Web Service 8 to receive its list of patients
this.patientsResults = retrievePtWS9(); //send request to Web Service 9 to receive its list of patients
}
That is a simple fork-join approach, but for clarity, you can start any number of threads and retrieve the results later as they are available, such as this approach.
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Callable<String>> tasks = new ArrayList<>();
tasks.add(new Callable<String>() {
public String call() throws Exception {
Thread.sleep((new Random().nextInt(5000)) + 500);
return "Hello world";
}
});
List<Future<String>> results = pool.invokeAll(tasks);
for (Future<String> future : results) {
System.out.println(future.get());
}
pool.shutdown();
UPDATE, COMPLETE:
Here's a verbose, but workable solution. I wrote it ad hoc, and have not compiled it.
Given the three lists have diffent types, and the WS methods are individual, it is not
really modular, but try to use your best programming skills and see if you can modularize it a bit better.
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Callable<List<StudentsResults>>> stasks = new ArrayList<>();
List<Callable<List<DoctorsResults>>> dtasks = new ArrayList<>();
List<Callable<List<PatientsResults>>> ptasks = new ArrayList<>();
stasks.add(new Callable<List<StudentsResults>>() {
public List<StudentsResults> call() throws Exception {
return retrieveStdWS1();
}
});
stasks.add(new Callable<List<StudentsResults>>() {
public List<StudentsResults> call() throws Exception {
return retrieveStdWS2();
}
});
stasks.add(new Callable<List<StudentsResults>>() {
public List<StudentsResults> call() throws Exception {
return retrieveStdWS3();
}
});
dtasks.add(new Callable<List<DoctorsResults>>() {
public List<DoctorsResults> call() throws Exception {
return retrieveDocWS4();
}
});
dtasks.add(new Callable<List<DoctorsResults>>() {
public List<DoctorsResults> call() throws Exception {
return retrieveDocWS5();
}
});
dtasks.add(new Callable<List<DoctorsResults>>() {
public List<DoctorsResults> call() throws Exception {
return retrieveDocWS6();
}
});
ptasks.add(new Callable<List<PatientsResults>>() {
public List<PatientsResults> call() throws Exception {
return retrievePtWS7();
}
});
ptasks.add(new Callable<List<PatientsResults>>() {
public List<PatientsResults> call() throws Exception {
return retrievePtWS8();
}
});
ptasks.add(new Callable<List<PatientsResults>>() {
public List<PatientsResults> call() throws Exception {
return retrievePtWS9();
}
});
List<Future<List<StudentsResults>>> sresults = pool.invokeAll(stasks);
List<Future<List<DoctorsResults>>> dresults = pool.invokeAll(dtasks);
List<Future<List<PatientsResults>>> presults = pool.invokeAll(ptasks);
for (Future<List<StudentsResults>> future : sresults) {
this.studentsResults.addAll(future.get());
}
for (Future<List<DoctorsResults>> future : dresults) {
this.doctorsResults.addAll(future.get());
}
for (Future<List<PatientsResults>> future : presults) {
this.patientsResults.addAll(future.get());
}
pool.shutdown();
Each Callable returns a list of results, and is called in its own separate thread.
When you invoke the Future.get() method you get the result back onto the main thread.
The result is NOT available until the Callable have finished, hence there is no concurrency issues.
So just for fun I am providing two working examples. The first one shows the old school way of doing this before java 1.5. The second shows a much cleaner way using tools available within java 1.5:
import java.util.ArrayList;
public class ThreadingExample
{
private ArrayList <MyThread> myThreads;
public static class MyRunnable implements Runnable
{
private String data;
public String getData()
{
return data;
}
public void setData(String data)
{
this.data = data;
}
#Override
public void run()
{
}
}
public static class MyThread extends Thread
{
private MyRunnable myRunnable;
MyThread(MyRunnable runnable)
{
super(runnable);
setMyRunnable(runnable);
}
/**
* #return the myRunnable
*/
public MyRunnable getMyRunnable()
{
return myRunnable;
}
/**
* #param myRunnable the myRunnable to set
*/
public void setMyRunnable(MyRunnable myRunnable)
{
this.myRunnable = myRunnable;
}
}
public ThreadingExample()
{
myThreads = new ArrayList <MyThread> ();
}
public ArrayList <String> retrieveMyData ()
{
ArrayList <String> allmyData = new ArrayList <String> ();
if (isComplete() == false)
{
// Sadly we aren't done
return (null);
}
for (MyThread myThread : myThreads)
{
allmyData.add(myThread.getMyRunnable().getData());
}
return (allmyData);
}
private boolean isComplete()
{
boolean complete = true;
// wait for all of them to finish
for (MyThread x : myThreads)
{
if (x.isAlive())
{
complete = false;
break;
}
}
return (complete);
}
public void kickOffQueries()
{
myThreads.clear();
MyThread a = new MyThread(new MyRunnable()
{
#Override
public void run()
{
// This is where you make the call to external services
// giving the results to setData("");
setData("Data from list A");
}
});
myThreads.add(a);
MyThread b = new MyThread (new MyRunnable()
{
#Override
public void run()
{
// This is where you make the call to external services
// giving the results to setData("");
setData("Data from list B");
}
});
myThreads.add(b);
for (MyThread x : myThreads)
{
x.start();
}
boolean done = false;
while (done == false)
{
if (isComplete())
{
done = true;
}
else
{
// Sleep for 10 milliseconds
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
public static void main(String [] args)
{
ThreadingExample example = new ThreadingExample();
example.kickOffQueries();
ArrayList <String> data = example.retrieveMyData();
if (data != null)
{
for (String s : data)
{
System.out.println (s);
}
}
}
}
This is the much simpler working version:
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class ThreadingExample
{
public static void main(String [] args)
{
ExecutorService service = Executors.newCachedThreadPool();
Set <Callable<String>> callables = new HashSet <Callable<String>> ();
callables.add(new Callable<String>()
{
#Override
public String call() throws Exception
{
return "This is where I make the call to web service A, and put its results here";
}
});
callables.add(new Callable<String>()
{
#Override
public String call() throws Exception
{
return "This is where I make the call to web service B, and put its results here";
}
});
callables.add(new Callable<String>()
{
#Override
public String call() throws Exception
{
return "This is where I make the call to web service C, and put its results here";
}
});
try
{
List<Future<String>> futures = service.invokeAll(callables);
for (Future<String> future : futures)
{
System.out.println (future.get());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
}
You can ask your jax-ws implementation to generate asynchronous bindings for the web service.
This has two advantages that I can see:
As discussed in Asynchronous web services calls with JAX-WS: Use wsimport support for asynchrony or roll my own? , jax-ws will generate well-tested (and possibly fancier) code for you, you need not instantiate the ExecutorService yourself. So less work for you! (but also less control over the threading implementation details)
The generated bindings include a method where you specify a callback handler, which may suit your needs better than synchronously get() ting all response lists on the thread calling retrieveAllLists(). It allows for per-service-call error handling and will process the results in parallel, which is nice if processing is non-trivial.
An example for Metro can be found on the Metro site. Note the contents of the custom bindings file custom-client.xml :
<bindings ...>
<bindings node="wsdl:definitions">
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
</bindings>
When you specify this bindings file to wsimport, it'll generate a client which returns an object that implements javax.xml.ws.Response<T>. Response extends the Future interface that others also suggest you use when rolling your own implementation.
So, unsurprisingly, if you go without the callbacks, the code will look similar to the other answers:
public void retrieveAllLists() throws ExecutionException{
// first fire all requests
Response<List<StudentsResults>> students1 = ws1.getStudents();
Response<List<StudentsResults>> students2 = ws2.getStudents();
Response<List<StudentsResults>> students3 = ws3.getStudents();
Response<List<DoctorsResults>> doctors1 = ws4.getDoctors();
Response<List<DoctorsResults>> doctors2 = ws5.getDoctors();
Response<List<DoctorsResults>> doctors3 = ws6.getDoctors();
Response<List<PatientsResults>> patients1 = ws7.getPatients();
Response<List<PatientsResults>> patients2 = ws8.getPatients();
Response<List<PatientsResults>> patients3 = ws9.getPatients();
// then await and collect all the responses
studentsResults.addAll(students1.get());
studentsResults.addAll(students2.get());
studentsResults.addAll(students3.get());
doctorsResults.addAll(doctors1.get());
doctorsResults.addAll(doctors2.get());
doctorsResults.addAll(doctors3.get());
patientsResults.addAll(patients1.get());
patientsResults.addAll(patients2.get());
patientsResults.addAll(patients3.get());
}
If you create callback handers such as
private class StudentsCallbackHandler
implements AsyncHandler<Response<List<StudentsResults>>> {
public void handleResponse(List<StudentsResults> response) {
try {
studentsResults.addAll(response.get());
} catch (ExecutionException e) {
errors.add(new CustomError("Failed to retrieve Students.", e.getCause()));
} catch (InterruptedException e) {
log.error("Interrupted", e);
}
}
}
you can use them like this:
public void retrieveAllLists() {
List<Future<?>> responses = new ArrayList<Future<?>>();
// fire all requests, specifying callback handlers
responses.add(ws1.getStudents(new StudentsCallbackHandler()));
responses.add(ws2.getStudents(new StudentsCallbackHandler()));
responses.add(ws3.getStudents(new StudentsCallbackHandler()));
...
// await completion
for( Future<?> response: responses ) {
response.get();
}
// or do some other work, and poll response.isDone()
}
Note that the studentResults collection needs to be thread safe now, since results will get added concurrently!
Looking at the problem, you need to integrate your application with 10+ different webservices.While making all the calls asynchronous. This can be done easily with Apache Camel. It is a prominent framework for enterprise integration and also supports async processing. You can use its CXF component for calling webservices and its routing engine for invocation and processing results. Look at the following page regarding camel's async routing capability. They have also provided a complete example invoking webservices async using CXF, it available at its maven repo. Also see the following page for more details.
You might consider the following paradigm in which you create work (serially), but the actual work is done in parallel. One way to do this is to: 1) have your "main" create a queue of work items; 2) create a "doWork" object that queries the queue for work to do; 3) have "main" start some number of "doWork" threads (can be same number as number of different services, or a smaller number); have the "doWork" objects put add their results to an object list (whatever construct works Vector, list...).
Each "doWork" object would mark their queue item complete, put all results in the passed container and check for new work (if no more on the queue, it would sleep and try again).
Of course you will want to see how well you can construct your class model. If each of the webservices is quite different for parsing, then you may want to create an Interface that each of your "retrieveinfo" classes promises to implement.
It has got various option to develop this.
JMS : quality of service and management, e.g. redelivery attempt, dead message queue, load management, scalability, clustering, monitoring, etc.
Simply using the Observer pattern for this. For more details OODesign and How to solve produce and consumer follow this Kodelog**
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();