Calling multiples webservices at same time using Java

I would like to call 3 webservices in a same method, and each result will be setted in a object in like this code bellow:

public Dossie procuraPorCPF(String cpf) {

    Dossie dossie = new Dossie();

    // first webservice call
    dossie.setCnh(detectaClientCnh.verificaCNH(cpf));

    // second webservice call
    dossie.setFotoCnh(detectaClientCnhFoto.verificaFotoCNHPorCpf(cpf));

    // third webservice call
    dossie.setPm(consultaPMWService.getPMPorCPF(cpf).getProcuraPMPorCPFResult());

    return dossie;
}

In this case above, I'm calling 3 different webservices, they takes about 5 seconds each one, then is not good continue with this code. I would like to know what is the best way to call the 3 webservices at same time and how could I do it using this code as example. I've searched some articles or even answers here, but I didn't figure out how to do it. Thanks for the patience.

Upvotes: 2

Views: 1450

Answers (3)

displayName
displayName

Reputation: 14389

I started writing this answer in the night and then left it for the next day. By the morning two answers were given and one of those was accepted by OP. Leaving this answer here for anyone who wishes to see the complete design and idea of the implementation, instead of actual code.


The Design:

Since web service calls take time to respond, they are called asynchronously, meaning that the main thread is not synchronized with these calls.

So, they are designed such that you make individual calls on separate threads.

For your case, the important Java elements are:

  1. Callable Interface;
  2. Future Interface;
  3. ExecutorService.

The Implementation:

  1. Implement a class which extends implements the Callable interface and contains the calling code for the web service in the call() method.
    • Do that for all three web service calls;
  2. Start an ExecutorService which will do this calling and result gathering for you;
  3. When you have to make the call, create an instance of the three calls and store them in an ArrayList;
  4. Using the ExecutorService you created in step 2, invoke the instances you created in step 3.

Bonus Step: It seems to me that the three web service calls need to be made in the same order repeatedly (I don't understand the language - it isn't English). For such a code, a separate orchestrator class can be created at the top of classes created in step 1 of the implementation.

This orchestrator class can extend Thread class and make the three web service calls. Now, this one class can be run asynchronously, instead of making the three web service calls. Keeps the code modular and abstracts away the complexity.

Upvotes: 1

Pankaj
Pankaj

Reputation: 2678

You can use ExecutorService to submit Callable and call Future.get() to retrieve result as shown below (change Future<String> to the appropriate return value). You should also think about error handling and creating threadpool outside of method (if possible at app startup).

public Dossie procuraPorCPF(String cpf) {

    ExecutorService executor = Executors.newFixedThreadPool(3);

    Future<String> cnh = executor.submit(() -> detectaClientCnh.verificaCNH(cpf));
    Future<String> fotoCnh = executor.submit(() -> detectaClientCnhFoto.verificaFotoCNHPorCpf(cpf));
    Future<String> pm =
        executor.submit(() -> consultaPMWService.getPMPorCPF(cpf).getProcuraPMPorCPFResult());

    Dossie dossie = new Dossie();

    try {
      dossie.setCnh(cnh.get());
      dossie.setFotoCnh(fotoCnh.get());
      dossie.setPm(pm.get());
    } catch (InterruptedException | ExecutionException cause) {
      cause.printStackTrace();
    }
    executor.shutdown();
    return dossie;
  }

Upvotes: 1

Elan Hamburger
Elan Hamburger

Reputation: 2177

Concurrency in Java is handled through the Thread class. The Thread constructor accepts a Runnable parameter with the code for the Thread to run. When the start() method is called, the JVM creates the new thread and executes the code in the run() method of the Runnable.

Since Runnable only has the single abstract method run(), you can use a lambda expression for more readable code. I used traditional syntax for the first call and lambda syntax for the other 2 to demonstrate both methods.

public Dossie procuraPorCPF(String cpf) {
    Dossie dossie = new Dossie();
    Thread[] threads = new Thread[3];

    threads[0] = new Thread(new Runnable() {
        @Override
        public void run() {
            dossie.setCnh(detectaClientCnh.verificaCNH(cpf));
        }
    };
    threads[0].start();

    threads[1] = new Thread(() ->
        dossie.setFotoCnh(detectaClientCnhFoto.verificaFotoCNHPorCpf(cpf));
    threads[1].start();

    threads[2] = new Thread(() ->
        dossie.setPm(consultaPMWService.getPMPorCPF(cpf).getProcuraPMPorCPFResult());
    threads[2].start();

    try {
        threads[0].join();
        threads[1].join();
        threads[2].join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    return dossie;
}

The join() method blocks program execution until the calling Thread has finished running. By placing the join()s at the end of the method, you can guarantee that all 3 requests have finished before you return. If it doesn't matter whether those requests have finished before you return, you can simply remove the join()s and the requests will continue in the background while the rest of your application runs.

For larger applications, Java also includes thread pools which will manage the creation of Threads for you. Since your application only uses the same 3 Runnables in a local context, I think the solution above is better suited (and more educational for the purposes of this answer).

Upvotes: 2

Related Questions