BAEL-6865, implemented review comments
This commit is contained in:
parent
a60be2d0b7
commit
2b88d3e646
@ -1,31 +1,34 @@
|
|||||||
package com.baeldung.aggregateEx;
|
package com.baeldung.aggregateEx;
|
||||||
|
|
||||||
|
import com.baeldung.aggregateEx.entity.ExceptionAggregator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collector;
|
import java.util.stream.Collector;
|
||||||
|
|
||||||
public class ExceptionCollector<T, R> {
|
public class CustomCollector<T, R> {
|
||||||
private final List<R> results = new ArrayList<>();
|
private final List<R> results = new ArrayList<>();
|
||||||
private final List<Exception> exceptions = new ArrayList<>();
|
private final List<Throwable> exceptions = new ArrayList<>();
|
||||||
|
|
||||||
private ExceptionCollector() {
|
private final ExceptionAggregator exceptionAggregator = new ExceptionAggregator("Exceptions occurred");
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, R> Collector<T, ?, ExceptionCollector<T, R>> of(Function<T, R> mapper) {
|
public static <T, R> Collector<T, ?, CustomCollector<T, R>> of(Function<T, R> mapper) {
|
||||||
return Collector.of(
|
return Collector.of(
|
||||||
ExceptionCollector::new,
|
CustomCollector::new,
|
||||||
(collector, item) -> {
|
(collector, item) -> {
|
||||||
try {
|
try {
|
||||||
R result = mapper.apply(item);
|
R result = mapper.apply(item);
|
||||||
collector.results.add(result);
|
collector.results.add(result);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
collector.exceptions.add(e);
|
collector.exceptions.add(e);
|
||||||
|
collector.exceptionAggregator.addException(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(left, right) -> {
|
(left, right) -> {
|
||||||
left.results.addAll(right.results);
|
left.results.addAll(right.results);
|
||||||
left.exceptions.addAll(right.exceptions);
|
left.exceptions.addAll(right.exceptions);
|
||||||
|
left.exceptionAggregator.addExceptions(right.exceptions);
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -35,7 +38,11 @@ public class ExceptionCollector<T, R> {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Exception> getExceptions() {
|
public List<Throwable> getExceptions() {
|
||||||
return exceptions;
|
return exceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExceptionAggregator getExceptionAggregator() {
|
||||||
|
return exceptionAggregator;
|
||||||
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import com.baeldung.aggregateEx.entity.Result;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class CustomMapper {
|
public class CustomMapper {
|
||||||
public static <T, R> Function<T, Result<R>> mapper(Function<T, R> func) {
|
public static <T, R> Function<T, Result<R, Throwable>> mapper(Function<T, R> func) {
|
||||||
return arg -> {
|
return arg -> {
|
||||||
try {
|
try {
|
||||||
return new Result(func.apply(arg));
|
return new Result(func.apply(arg));
|
||||||
|
@ -4,24 +4,24 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ExceptionAggregator extends RuntimeException {
|
public class ExceptionAggregator extends RuntimeException {
|
||||||
private List<Exception> exceptions;
|
private List<Throwable> exceptions;
|
||||||
|
|
||||||
public ExceptionAggregator(String message) {
|
public ExceptionAggregator(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
exceptions = new ArrayList<>();
|
exceptions = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Exception> getExceptions() {
|
public List<Throwable> getExceptions() {
|
||||||
return exceptions;
|
return exceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception addException(Exception e) {
|
public Throwable addException(Throwable e) {
|
||||||
this.addSuppressed(e);
|
this.addSuppressed(e);
|
||||||
exceptions.add(e);
|
exceptions.add(e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addExceptions(List<Exception> exceptions) {
|
public void addExceptions(List<Throwable> exceptions) {
|
||||||
exceptions.forEach(this::addException);
|
exceptions.forEach(this::addException);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,16 @@ package com.baeldung.aggregateEx.entity;
|
|||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class Result<R> {
|
public class Result<R, E extends Throwable> {
|
||||||
private Optional<R> result;
|
private Optional<R> result;
|
||||||
private Optional<Exception> exception;
|
private Optional<E> exception;
|
||||||
|
|
||||||
public Result(R result) {
|
public Result(R result) {
|
||||||
this.result = Optional.of(result);
|
this.result = Optional.of(result);
|
||||||
this.exception = Optional.empty();
|
this.exception = Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result(Exception exception) {
|
public Result(E exception) {
|
||||||
this.exception = Optional.of(exception);
|
this.exception = Optional.of(exception);
|
||||||
this.result = Optional.empty();
|
this.result = Optional.empty();
|
||||||
}
|
}
|
||||||
@ -20,8 +20,7 @@ public class Result<R> {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Exception> getException() {
|
public Optional<E> getException() {
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ import com.baeldung.aggregateEx.entity.Result;
|
|||||||
import io.vavr.control.Either;
|
import io.vavr.control.Either;
|
||||||
import io.vavr.control.Try;
|
import io.vavr.control.Try;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -12,172 +14,138 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
public class AggregateExceptionHandlerUnitTest {
|
public class AggregateExceptionHandlerUnitTest {
|
||||||
|
static Logger logger = LoggerFactory.getLogger(AggregateExceptionHandlerUnitTest.class);
|
||||||
private static RuntimeException process(String str) {
|
|
||||||
try {
|
|
||||||
Integer.parseInt(str);
|
|
||||||
return null;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object transform(String str) {
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(str);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenExtractedMethod_whenFoundNonInt_thenSuppressExIntoRuntimeEx() {
|
public void givenExtractedMethod_whenFoundEx_thenSuppressExIntoRuntimeEx() {
|
||||||
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
||||||
RuntimeException runEx = Arrays.stream(strings)
|
Throwable runEx = Arrays.stream(strings)
|
||||||
.map(AggregateExceptionHandlerUnitTest::process)
|
.map(str -> callProcessThrowsExAndNoOutput(str))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
||||||
o1.addSuppressed(o2);
|
o1.addSuppressed(o2);
|
||||||
return o1;
|
return o1;
|
||||||
});
|
});
|
||||||
|
processExceptions(runEx);
|
||||||
assertEquals("Errors Occurred", runEx.getMessage());
|
assertEquals("Errors Occurred", runEx.getMessage());
|
||||||
assertEquals(3, runEx.getSuppressed().length);
|
assertEquals(3, runEx.getSuppressed().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenTryCatchInPipeline_whenFoundNonInts_thenSuppressExIntoRuntimeEx() {
|
public void givenTryCatchInPipeline_whenFoundEx_thenSuppressExIntoRuntimeEx() {
|
||||||
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
||||||
RuntimeException runEx = Arrays.stream(strings)
|
RuntimeException runEx = Arrays.stream(strings).map(str -> {
|
||||||
.map(str -> {
|
|
||||||
try {
|
try {
|
||||||
Integer.parseInt(str);
|
processThrowsExAndNoOutput(str);
|
||||||
return null;
|
return null;
|
||||||
} catch (NumberFormatException e) {
|
} catch (RuntimeException e) {
|
||||||
return new RuntimeException(e);
|
return e;
|
||||||
}
|
}
|
||||||
})
|
}).filter(Objects::nonNull)
|
||||||
.filter(Objects::nonNull)
|
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> {
|
||||||
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
RuntimeException runtimeException = new RuntimeException("Errors Occurred");
|
||||||
o1.addSuppressed(o2);
|
list.forEach(runtimeException::addSuppressed);
|
||||||
return o1;
|
return runtimeException;
|
||||||
});
|
}));
|
||||||
|
processExceptions(runEx);
|
||||||
assertEquals("Errors Occurred", runEx.getMessage());
|
assertEquals("Errors Occurred", runEx.getMessage());
|
||||||
assertEquals(3, runEx.getSuppressed().length);
|
assertEquals(3, runEx.getSuppressed().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenExtractedMethodReturnOutputAndEx_whenFoundNonInts_thenSuppressExIntoRuntimeEx() {
|
public void givenProcessMethod_whenStreamResultHasExAndOutput_thenHandleExceptionListAndOutputList() {
|
||||||
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
|
||||||
Map resultMap = Arrays.stream(strings)
|
Map map = strings.stream()
|
||||||
.map(AggregateExceptionHandlerUnitTest::transform)
|
.map(s -> processReturnsExAndOutput(s))
|
||||||
.collect(Collectors.partitioningBy(o -> o instanceof RuntimeException, Collectors.toList()));
|
.collect(Collectors.partitioningBy(o -> o instanceof RuntimeException, Collectors.toList()));
|
||||||
|
assert(map.containsKey(Boolean.TRUE) && map.containsKey(Boolean.FALSE));
|
||||||
RuntimeException ex = null;
|
handleExceptionsAndOutputs((List<RuntimeException>) map.get(Boolean.TRUE), (List<Integer>)map.get(Boolean.FALSE));
|
||||||
|
|
||||||
assertTrue(resultMap.containsKey(Boolean.TRUE));
|
|
||||||
|
|
||||||
List<RuntimeException> exs = (List<RuntimeException>) resultMap.get(Boolean.TRUE);
|
|
||||||
ex = exs.stream()
|
|
||||||
.reduce(
|
|
||||||
new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
|
||||||
o1.addSuppressed(o2);
|
|
||||||
return o1;
|
|
||||||
});
|
|
||||||
|
|
||||||
assertEquals("Errors Occurred", ex.getMessage());
|
|
||||||
assertEquals(3, ex.getSuppressed().length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenWrapFunction_whenFoundNonInts_thenUseExAggregator() throws ExceptionAggregator {
|
public void givenCustomMapper_whenStreamResultHasExAndSuccess_thenHandleExceptionListAndOutputList() {
|
||||||
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
|
||||||
Map<Boolean, List<Result<Integer>>> resultmap = Arrays.stream(strings)
|
strings.stream()
|
||||||
.map(CustomMapper.mapper(Integer::parseInt))
|
.map(CustomMapper.mapper(Integer::parseInt))
|
||||||
.collect(Collectors.partitioningBy(r -> r.getException().isEmpty(), Collectors.toList()));
|
.collect(Collectors.collectingAndThen(Collectors.toList(), list -> handleErrorsAndOutPutForResult(list)));
|
||||||
|
|
||||||
assertTrue(resultmap.containsKey(Boolean.TRUE));
|
|
||||||
|
|
||||||
List<Result<Integer>> resultList = resultmap.get(Boolean.FALSE);
|
|
||||||
List<Exception> exceptionList = resultList.stream()
|
|
||||||
.map(opex -> opex.getException().get())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
assertThrows(ExceptionAggregator.class, () -> handleExceptions(exceptionList));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleExceptions(List<Exception> exceptions) throws ExceptionAggregator {
|
|
||||||
ExceptionAggregator exceptionAggregator = new ExceptionAggregator("Errors occurred");
|
|
||||||
exceptionAggregator.addExceptions(exceptions);
|
|
||||||
throw exceptionAggregator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void givenExCollector_whenFoundNonInts_thenAggregateExInCustomCollector() throws ExceptionAggregator {
|
public void givenCustomCollector_whenStreamResultHasExAndSuccess_thenHandleAggrExceptionAndResults() {
|
||||||
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
String[] strings = {"1", "2", "3", "a", "b", "c"};
|
||||||
ExceptionCollector exCollector = Arrays.stream(strings)
|
Arrays.stream(strings)
|
||||||
.collect(ExceptionCollector.of(Integer::parseInt));
|
.collect(Collectors.collectingAndThen(CustomCollector.of(Integer::parseInt), col -> {
|
||||||
List<Integer> output = exCollector.getResults();
|
handleExAndResults(col.getExceptionAggregator(), col.getResults());
|
||||||
List<RuntimeException> runEx = exCollector.getExceptions();
|
return col;
|
||||||
assertEquals(3, runEx.size());
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Either<RuntimeException, Integer> processAndReturnEither(String str) {
|
@Test
|
||||||
|
public void givenVavrEitherAndTry_whenStreamResultHasExAndSuccess_thenHandleExceptionListAndOutputList() {
|
||||||
|
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
|
||||||
|
strings.stream()
|
||||||
|
.map(str -> Try.of(() -> Integer.parseInt(str)).toEither())
|
||||||
|
.collect(Collectors.collectingAndThen(Collectors.partitioningBy(Either::isLeft, Collectors.toList()), map -> {
|
||||||
|
handleErrorsAndOutPutForEither(map);
|
||||||
|
return map;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processThrowsExAndNoOutput(String input) {
|
||||||
|
//return exception when input is "a", "b", "c"
|
||||||
|
if (input.matches("[a-c]")) {
|
||||||
|
throw new RuntimeException("Downstream method throws exception for " + input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static Throwable callProcessThrowsExAndNoOutput(String input) {
|
||||||
try {
|
try {
|
||||||
return Either.right(Integer.parseInt(str));
|
processThrowsExAndNoOutput(input);
|
||||||
} catch (NumberFormatException e) {
|
return null;
|
||||||
return Either.left(new RuntimeException(e));
|
} catch (RuntimeException e) {
|
||||||
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private static Object processReturnsExAndOutput(String input) {
|
||||||
public void givenVavrEither_whenFoundNonInts_thenSuppressExIntoRuntimeEx() {
|
logger.info("call a downstream method that returns an Integer");
|
||||||
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
|
try {
|
||||||
Map<Boolean, List<Either<RuntimeException, Integer>>> map = strings.stream()
|
return Integer.parseInt(input);
|
||||||
.map(str -> processAndReturnEither(str))
|
} catch (Exception e) {
|
||||||
.collect(Collectors.partitioningBy(t -> t.isLeft(), Collectors.toList()));
|
return new RuntimeException("Exception in processWithReturnOutput for " + input, e);
|
||||||
|
}
|
||||||
assertTrue(map.containsKey(Boolean.TRUE));
|
|
||||||
|
|
||||||
RuntimeException runEx = map.get(Boolean.TRUE)
|
|
||||||
.stream().map(either -> either.getLeft())
|
|
||||||
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
|
||||||
o1.addSuppressed(o2);
|
|
||||||
return o1;
|
|
||||||
});
|
|
||||||
assertEquals(3, runEx.getSuppressed().length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private static void processExceptions(Throwable throwable) {
|
||||||
public void givenVavrTry_whenFoundNonInts_thenSuppressExIntoRuntimeEx() {
|
logger.error("Process Exception" + throwable.getMessage());
|
||||||
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
|
|
||||||
Map<Boolean, List<Try<Integer>>> map = strings.stream()
|
|
||||||
.map(str -> Try.of(() -> Integer.parseInt(str)))
|
|
||||||
.collect(Collectors.partitioningBy(t -> t.isFailure(), Collectors.toList()));
|
|
||||||
Throwable runEx = map.get(Boolean.TRUE).stream()
|
|
||||||
.map(t -> t.getCause())
|
|
||||||
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
|
||||||
o1.addSuppressed(o2);
|
|
||||||
return o1;
|
|
||||||
});
|
|
||||||
assertEquals(3, runEx.getSuppressed().length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private static void handleExceptionsAndOutputs(List<RuntimeException> exs, List output) {
|
||||||
public void givenVavrEitherAndTry_whenFoundNonInts_thenSuppressExIntoRuntimeEx() {
|
logger.info("handle exceptions and output");
|
||||||
List<String> strings = List.of("1", "2", "3", "a", "b", "c");
|
}
|
||||||
Map<Boolean, List<Either<Throwable, Integer>>> map = strings.stream()
|
|
||||||
.map(str -> Try.of(() -> Integer.parseInt(str)).toEither())
|
private static void handleExAndResults(ExceptionAggregator exAgg, List<Integer> results ) {
|
||||||
.collect(Collectors.partitioningBy(Either::isLeft, Collectors.toList()));
|
logger.info("handle aggregated exceptions and results" + exAgg.getExceptions().size() + " " + results.size());
|
||||||
Throwable runEx = map.get(Boolean.TRUE).stream()
|
}
|
||||||
.map(either -> either.getLeft())
|
|
||||||
.reduce(new RuntimeException("Errors Occurred"), (o1, o2) -> {
|
private static void handleErrorsAndOutPutForEither(Map<Boolean, List<Either<Throwable, Integer>>> map) {
|
||||||
o1.addSuppressed(o2);
|
logger.info("handle errors and output");
|
||||||
return o1;
|
map.get(Boolean.TRUE).forEach(either -> logger.error("Process Exception " + either.getLeft()));
|
||||||
});
|
|
||||||
assertEquals(3, runEx.getSuppressed().length);
|
map.get(Boolean.FALSE).forEach(either -> logger.info("Process Result " + either.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String handleErrorsAndOutPutForResult(List<Result<Integer, Throwable>> successAndErrors) {
|
||||||
|
logger.info("handle errors and output");
|
||||||
|
successAndErrors.forEach(result -> {
|
||||||
|
if (result.getException().isPresent()) {
|
||||||
|
logger.error("Process Exception " + result.getException().get());
|
||||||
|
} else {
|
||||||
|
logger.info("Process Result" + result.getResult());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return "Errors and Output Handled";
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user