Aspecto que no funciona con Completable Future
Tengo una lista de Consumidores que quiero ejecutar en múltiples hilos. Tengo un aspecto para registrar y manejar todas las excepciones. A continuación se muestra el fragmento de código.
El problema es que el aspecto funciona bien hasta que se crean varios hilos. Después de eso, en caso de excepción (@LogFailure
), el código no entra en el consejo.
@Override
public void consumerExecutor(Map<><iprocessdto>, IProcessDTO> map, IContext context) {
List<><void>> completableFutures = new ArrayList<>();
Iterator consumerIterator = map.keySet().iterator();
while (consumerIterator.hasNext()) {
Consumer<iprocessdto> consumer = (Consumer<iprocessdto>) consumerIterator.next();
CompletableFuture<void> future = CompletableFuture.runAsync(() -> {
executeSingleConsumer(0, consumer, map.get(consumer));
});
completableFutures.add(future);
}
try {
CompletableFuture<void> allFuturesResult = CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[completableFutures.size()]));
allFuturesResult.thenApply(v ->
completableFutures.stream()
.map(future -> future.join())
.collect(Collectors.toList())
);
} catch (CancellationException | CompletionException exception) {
}
}
@LogFailure
@LogSuccess
private void executeSingleConsumer(int tries, Consumer<iprocessdto> consumer, IProcessDTO processDto) {
try {
consumer.accept(null);
} catch (Exception ex) {
if (tries < maxAttempts) {
executeSingleConsumer(tries + 1, consumer, processDto);
} else if (tries == maxAttempts) {
throw new ProcessException();
} else {
throw ex;
}
}
}
@AfterThrowing(pointcut = "execution(@com.LogFailure * *.*(..))", argNames = "jp,ex", throwing = "ex")
public void handleLogFailure(JoinPoint jp, Exception ex) {
// excepciones manejadas
}
¿Alguna idea de qué estoy haciendo mal?
davy.ai
El problema aquí es que el método
executeSingleConsumer
se está llamando dentro del métodoCompletableFuture.runAsync
, lo que significa que el aspecto@LogFailure
no se activa en caso de una excepción. Esto ocurre porque la excepción se maneja dentro del métodoexecuteSingleConsumer
y no se propaga al llamador delCompletableFuture
. Por lo tanto, el aspecto no puede interceptar la excepción.Para solucionar este problema, puedes modificar el método
executeSingleConsumer
para que lance la excepción en lugar de manejarla. Luego, puedes usar el métodoexceptionally
de la claseCompletableFuture
para interceptar cualquier excepción y manejarla usando el aspecto@LogFailure
. A continuación se muestra el código modificado:En este código modificado, el método
executeSingleConsumer
ahora lanza cualquier excepción que encuentre. El métodoCompletableFuture.runAsync
ahora llama a este método dentro de un bloque try-catch y lanza unaRuntimeException
si se captura alguna excepción. Luego, el métodoexceptionally
de la claseCompletableFuture
intercepta estaRuntimeException
y llama al métodohandleLogFailure
con la excepción capturada.Con esta modificación, el aspecto
@LogFailure
debería poder interceptar cualquier excepción lanzada por el métodoexecuteSingleConsumer
, incluso cuando se ejecuta en múltiples hilos.