简体   繁体   中英

Catch and throw custom Exception from Java ExecutorService

I have a function that goes through a list of HappyObjects and sets their fields asynchronously. In the Callable, a JsonProcessingException can occur. I have to wrap this and other exceptions from this function into a custom exception (ControllerException) and throw that instead.

Other Stack Overflow posts seem to suggest collect into a List of Futures and use get() to catch the exceptions. Thus, this is what I have so far:

default List<HappyObj> fillfunction(final List<HappyObj> happyObjs) throws ControllerException {
    ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
    List<Future<HappyObj>> futures = new ArrayList<>();

    for (HappyObj happyObj : happyObjs) {
      Future<HappyObj> future = executor.submit(
          () -> {
            final List<Mood> moods = getMoods();

            for (Mood mood : moods) {
              final String json = getJsonEmotion();

              ObjectMapper mapper = new ObjectMapper();
              mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

              List<Emotion> emotions =
                  mapper.readValue(json, new TypeReference<List<Emotion>>() {}); //JsonProcessingException can occur here

              MoodMetadata metadata = mood.getMoodMetadata();
              if (metadata != null && metadata.getEmotionMetadata() != null) {
                metadata.getEmotionMetadata().setEmotions(emotions);
              }
            }
            happyObj.setMoods(moods);
            return happyObj;
          });
      futures.add(future);
    }
    executor.shutdown();
    final long maxSlaSec = 1;
    try {
      executor.awaitTermination(maxSlaSec, TimeUnit.SECONDS);
      List<HappyObj> happyResult = new ArrayList<>();
      for (Future<HappyObj> future : futures) {
        happyResult.add(future.get());
      }
      return happyResult;
    } catch (InterruptedException | ExecutionException e) {
      executor.shutdownNow();
      throw new ControllerException(e);
    }
  }

Is there a more elegant way than iterating through List<Future> and calling get on each to catch ExecutorException? I thought about using execute() vs. submit() , but then I can't handle the JsonProcessingException. I saw another post suggesting creating a ThreadPoolExecutor subclass and override the afterExecute(), but I wasn't able to handle the JsonProcessingException.

One of the reasons I asked this question is because since this method consists mainly of setters, the function was originally manipulating the given objects and returning void.

According to the docs of ExecutionException (and also the docs of Future#get ), it already has wrapped that information. That is, you can use its getCause to inspect the Exception thrown by the Callable 's body.

Notice that Callable#call itself throws an Exception ... When you throw an Exception from the Callable , it will be wrapped into an ExecutionException which will be thrown from the Future#get method, for each Callable , meaning that you can change your loop to catch an ExecutionException for each Future and inspect its getCause !

So you don't actually need to wrap it to a custom ControllerException .

The Callable s you create, can still return null of type Void for example, without needing to do anything about them.

Unless the scenario changes, then you don't need to extend ThreadPoolExecutor in this case. You don't even have to cast to ThreadPoolExecutor , because the ExecutorService interface already has the submit s you need. Just throw whatever Exception you need from the Callable (such as JsonProcessingException that you mentioned) when something goes wrong in the Callable , and then inspect the ExecutionException from each Future#get method to tell if an JsonProcessingException was thrown (and you can also determine in which one it was thrown, if you need).

Is there a more elegant way than iterating through List and calling get on each to catch ExecutorException?

In my opinion, no, there is not, because you want to submit all Callable s first, then let them run in parallel, and at the end inspect their ExecutionException for any Exception thrown by the Callable s' body for each Callable (via Future#get of the returned Future by submit ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM