Truica Sorin
Truica Sorin

Reputation: 539

Spring Boot executing parallel methods inside a controller

I'm trying to execute some parallel methods inside my controller but i'm having a few problems with the return type.

I have 4 methods and each of those methods return a list.I need to execute those methods in a parallel way and then get each returned list and place all of those lists inside a map and return that map from the controller. Here's the code:

Callable<List> callable1 = new Callable<List>()
           {
              @Override
              public List call() throws Exception
              {
                List<SearchResultAutovit> lista;
                lista = scrapperAutovit.searchAutovit(marcaId, modelId, pretDeLa, pretPanaLa, anFabrDeLa, anFabrPanaLa,
                        orasParam);
                 return lista;
              }
           };

           Callable<List> callable2 = new Callable<List>()
           {
              @Override
              public List call() throws Exception
              {
                  List<SearchResultOlx> listaOlx;
                String marcaOlx = marcaId.toLowerCase();
                String modelOlx = modelId.toLowerCase();
                String orasOlx = orasParam.toLowerCase();
                listaOlx = scrapperOlx.searchOlx(marcaOlx, modelOlx, pretDeLa, pretPanaLa, anFabrDeLa, anFabrPanaLa, orasOlx);
                 return listaOlx;
              }
           };

           Callable<List> callable3 = new Callable<List>()
           {
              @Override
              public List call() throws Exception
              {
                List<SearchResultPubli24> listaPubli24;
                String orasPubli24 = orasParam.toLowerCase();
                listaPubli24 = scrapperPubli24.searchPubli24(marcaId, modelId, orasPubli24, anFabrDeLa, anFabrPanaLa, pretDeLa, pretPanaLa);
                 return listaPubli24;
              }
           };

           Callable<List> callable4 = new Callable<List>()
           {
              @Override
              public List call() throws Exception
              {
                 List<SearchResultAutoUncle> listaAutoUncle;
                listaAutoUncle = scrapperAutoUncle.searchAutoUncle(marcaId, modelId, pretDeLa, pretPanaLa, anFabrDeLa, anFabrPanaLa, orasParam);
                 return listaAutoUncle;
              }
           };

           //add to a list
           List<Callable<List>> taskList = new ArrayList<Callable<List>>();
           taskList.add(callable1);
           taskList.add(callable2);
           taskList.add(callable3);
           taskList.add(callable4);

           ExecutorService executor = Executors.newFixedThreadPool(3);
           executor.invokeAll(taskList);

           Map<String,List<?>> listOfWebsites = new HashMap<>();
            listOfWebsites.put("listaAutovit", (List<?>) taskList.get(0));
            listOfWebsites.put("listaOlx", (List<?>) taskList.get(1));
            listOfWebsites.put("listaPubli24", (List<?>) taskList.get(2));
            listOfWebsites.put("listaAutoUncle", (List<?>) taskList.get(3));

        return listOfWebsites;

I'm pretty sure that i'm not doing something right because it throws java.lang.ClassCastException: com.test.controller.HomeController$1 incompatible with java.util.List I guess the problem is the map called listOfWebsites which should have the returned type of the callables, some lists:(

Upvotes: 2

Views: 5529

Answers (2)

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13515

First, change type of callables to avoid casting:

    Callable<List<?>> callable1 = new Callable<List>(){...}

Then, you have to wait for results of parallel computations. You need not to create CompletableFutures because you can obtain Futures directly from ExecutorService:

    ExecutorService executor = Executors.newFixedThreadPool(3);
    Future<List<?>> future1 = executor.submit(callable1);
    Future<List<?>> future2 = executor.submit(callable2);
    Future<List<?>> future3 = executor.submit(callable3);
    Future<List<?>> future4 = executor.submit(callable4);

    Map<String,List<?>> listOfWebsites = new HashMap<>();
    listOfWebsites.put("listaAutovit", future1.get());
    listOfWebsites.put("listaOlx", future2.get());
    listOfWebsites.put("listaPubli24", future3.get());
    listOfWebsites.put("listaAutoUncle", future4.get());

    return listOfWebsites;

That's it.

Upvotes: 4

GauravRai1512
GauravRai1512

Reputation: 844

I would suggest to use completableFuture Object with supplyasync method. Giving you approach like below:

CompletableFuture<String> completableFuture
  = CompletableFuture.supplyAsync(() -> scrapperAutovit.searchAutovit);

CompletableFuture<String> completableFuture1
  = CompletableFuture.supplyAsync(() -> scrapperOlx.searchOlxwith);

CompletableFuture<String> completableFuture2
  = CompletableFuture.supplyAsync(() -> scrapperPubli24.searchPubli24);

would be used to check whether you got some response from above call or not.

Like that you can open parallel thread for scrapperOlx.searchOlxwith and scrapperPubli24.searchPubli24 completableFuture object and wait for execution to finish there task and after that consolidate all data and you can process further.

For more details you can refer below link https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html https://dzone.com/articles/java-8-definitive-guide https://www.callicoder.com/java-8-completablefuture-tutorial/

This is the good example to understand completablefuture Multiple thenApply in a completableFuture

Upvotes: 1

Related Questions