From cde706f8c984964b061c3875eaacf405b1c5bd07 Mon Sep 17 00:00:00 2001 From: Marcos Lopez Gonzalez Date: Mon, 15 Apr 2019 20:47:23 +0200 Subject: [PATCH 1/5] root cause finder --- .../baeldung/exceptions/RootCauseFinder.java | 61 +++++++++++++++++++ .../exceptions/RootCauseFinderTest.java | 51 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java create mode 100644 core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java diff --git a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java new file mode 100644 index 0000000000..a7963c35a8 --- /dev/null +++ b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java @@ -0,0 +1,61 @@ +package com.baeldung.exceptions; + +import java.util.Objects; + +/** + * Utility class to find root cause exceptions. + */ +public class RootCauseFinder { + + public static Throwable findCauseUsingPlainJava(Throwable throwable) { + Objects.requireNonNull(throwable); + Throwable rootCause = throwable; + while (rootCause.getCause() != null) { + rootCause = rootCause.getCause(); + } + return rootCause; + } + + static class IntParser { + + private IntParser() { + } + + public static int parse(String input) throws InvalidNumber { + if (input == null || input.isEmpty()) { + throw new IllegalArgumentException(); + } + + try { + return new IntParser().stringToInt(input.trim()); + } catch (NaNException ex) { + throw new InvalidNumber(input, ex); + } + } + + private int stringToInt(String numberAsString) throws NaNException { + try { + return Integer.valueOf(numberAsString); + } catch (NumberFormatException ex) { + throw new NaNException(numberAsString, ex); + } + } + + } + + static class InvalidNumber extends Exception { + + InvalidNumber(String input, Throwable thr) { + super("Invalid input for a number: " + input, thr); + } + } + + static class NaNException extends Exception { + + NaNException(String number, Throwable thr) { + super(number + "is not a number", thr); + } + + } + +} diff --git a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java new file mode 100644 index 0000000000..1e58e3f602 --- /dev/null +++ b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java @@ -0,0 +1,51 @@ +package com.baeldung.exceptions; + +import com.google.common.base.Throwables; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.junit.jupiter.api.Test; + +import static com.baeldung.exceptions.RootCauseFinder.*; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests the {@link RootCauseFinder}. + */ +public class RootCauseFinderTest { + + @Test + public void givenNestedException_whenFindingRootCauseUsingJava_thenRootCauseFound() { + try { + IntParser.parse("text"); + } catch (InvalidNumber ex) { + assertTrue(findCauseUsingPlainJava(ex) instanceof NumberFormatException); + } + } + + @Test + public void givenNonNestedException_whenFindingRootCauseUsingJava_thenRootCauseFound() { + try { + IntParser.parse(null); + } catch (Exception ex) { + assertTrue(findCauseUsingPlainJava(ex) instanceof IllegalArgumentException); + } + } + + @Test + public void givenNestedException_whenFindingRootCauseUsingApacheCommons_thenRootCauseFound() { + try { + IntParser.parse("text"); + } catch (InvalidNumber ex) { + assertTrue(ExceptionUtils.getRootCause(ex) instanceof NumberFormatException); + } + } + + @Test + public void givenNestedException_whenFindingRootCauseUsingGuava_thenRootCauseFound() { + try { + IntParser.parse("text"); + } catch (InvalidNumber ex) { + assertTrue(Throwables.getRootCause(ex) instanceof NumberFormatException); + } + } + +} From a752486b794b38df466b3b63329d710adf9805c2 Mon Sep 17 00:00:00 2001 From: Marcos Lopez Gonzalez Date: Mon, 29 Apr 2019 19:58:51 +0200 Subject: [PATCH 2/5] changed example for finder of root cause exception --- .../baeldung/exceptions/RootCauseFinder.java | 68 ++++++++++++++----- .../exceptions/RootCauseFinderTest.java | 57 ++++++++++++---- 2 files changed, 94 insertions(+), 31 deletions(-) diff --git a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java index a7963c35a8..11f29a2cf0 100644 --- a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java +++ b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java @@ -1,5 +1,8 @@ package com.baeldung.exceptions; +import java.time.LocalDate; +import java.time.Period; +import java.time.format.DateTimeParseException; import java.util.Objects; /** @@ -16,44 +19,75 @@ public class RootCauseFinder { return rootCause; } - static class IntParser { + /** + * Calculates the age of a person from a given date. + */ + static class AgeCalculator { - private IntParser() { + private AgeCalculator() { } - public static int parse(String input) throws InvalidNumber { - if (input == null || input.isEmpty()) { + public static int calculateAge(String birthDate) throws CalculationException { + if (birthDate == null || birthDate.isEmpty()) { throw new IllegalArgumentException(); } try { - return new IntParser().stringToInt(input.trim()); - } catch (NaNException ex) { - throw new InvalidNumber(input, ex); + return calculateDifference(birthDate).getYears(); + } catch (DateParseException ex) { + throw new CalculationException(ex); } } - private int stringToInt(String numberAsString) throws NaNException { + private static Period calculateDifference(String birthDateAsString) throws DateParseException { + + LocalDate birthDate = null; try { - return Integer.valueOf(numberAsString); - } catch (NumberFormatException ex) { - throw new NaNException(numberAsString, ex); + birthDate = LocalDate.parse(birthDateAsString); + } catch (DateTimeParseException ex) { + throw new InvalidFormatException(birthDateAsString, ex); } + + LocalDate today = LocalDate.now(); + + if (birthDate.isAfter(today)) { + throw new DateOutOfRangeException(birthDateAsString); + } + + return Period.between(birthDate, today); } } - static class InvalidNumber extends Exception { + static class CalculationException extends Exception { - InvalidNumber(String input, Throwable thr) { - super("Invalid input for a number: " + input, thr); + CalculationException(DateParseException ex) { + super(ex); } } - static class NaNException extends Exception { + static class DateParseException extends Exception { - NaNException(String number, Throwable thr) { - super(number + "is not a number", thr); + DateParseException(String input) { + super(input); + } + + DateParseException(String input, Throwable thr) { + super(input, thr); + } + } + + static class InvalidFormatException extends DateParseException { + + InvalidFormatException(String input, Throwable thr) { + super("Invalid date format: " + input, thr); + } + } + + static class DateOutOfRangeException extends DateParseException { + + DateOutOfRangeException(String date) { + super("Date out of range: " + date); } } diff --git a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java index 1e58e3f602..c333a7ac26 100644 --- a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java +++ b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java @@ -4,6 +4,8 @@ import com.google.common.base.Throwables; import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.jupiter.api.Test; +import java.time.format.DateTimeParseException; + import static com.baeldung.exceptions.RootCauseFinder.*; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -13,38 +15,65 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class RootCauseFinderTest { @Test - public void givenNestedException_whenFindingRootCauseUsingJava_thenRootCauseFound() { + public void givenWrongFormatDate_whenFindingRootCauseUsingJava_thenRootCauseFound() { try { - IntParser.parse("text"); - } catch (InvalidNumber ex) { - assertTrue(findCauseUsingPlainJava(ex) instanceof NumberFormatException); + AgeCalculator.calculateAge("010102"); + } catch (CalculationException ex) { + assertTrue(findCauseUsingPlainJava(ex) instanceof DateTimeParseException); } } @Test - public void givenNonNestedException_whenFindingRootCauseUsingJava_thenRootCauseFound() { + public void givenOutOfRangeDate_whenFindingRootCauseUsingJava_thenRootCauseFound() { try { - IntParser.parse(null); + AgeCalculator.calculateAge("2020-04-04"); + } catch (CalculationException ex) { + assertTrue(findCauseUsingPlainJava(ex) instanceof DateOutOfRangeException); + } + } + + @Test + public void givenNullDate_whenFindingRootCauseUsingJava_thenRootCauseFound() { + try { + AgeCalculator.calculateAge(null); } catch (Exception ex) { assertTrue(findCauseUsingPlainJava(ex) instanceof IllegalArgumentException); } } @Test - public void givenNestedException_whenFindingRootCauseUsingApacheCommons_thenRootCauseFound() { + public void givenWrongFormatDate_whenFindingRootCauseUsingApacheCommons_thenRootCauseFound() { try { - IntParser.parse("text"); - } catch (InvalidNumber ex) { - assertTrue(ExceptionUtils.getRootCause(ex) instanceof NumberFormatException); + AgeCalculator.calculateAge("010102"); + } catch (CalculationException ex) { + assertTrue(ExceptionUtils.getRootCause(ex) instanceof DateTimeParseException); } } @Test - public void givenNestedException_whenFindingRootCauseUsingGuava_thenRootCauseFound() { + public void givenOutOfRangeDate_whenFindingRootCauseUsingApacheCommons_thenRootCauseFound() { try { - IntParser.parse("text"); - } catch (InvalidNumber ex) { - assertTrue(Throwables.getRootCause(ex) instanceof NumberFormatException); + AgeCalculator.calculateAge("2020-04-04"); + } catch (CalculationException ex) { + assertTrue(ExceptionUtils.getRootCause(ex) instanceof DateOutOfRangeException); + } + } + + @Test + public void givenWrongFormatDate_whenFindingRootCauseUsingGuava_thenRootCauseFound() { + try { + AgeCalculator.calculateAge("010102"); + } catch (CalculationException ex) { + assertTrue(Throwables.getRootCause(ex) instanceof DateTimeParseException); + } + } + + @Test + public void givenOutOfRangeDate_whenFindingRootCauseUsingGuava_thenRootCauseFound() { + try { + AgeCalculator.calculateAge("2020-04-04"); + } catch (CalculationException ex) { + assertTrue(Throwables.getRootCause(ex) instanceof DateOutOfRangeException); } } From 6da90b24b01f89a7c2ee0eb74795e2864171a9ae Mon Sep 17 00:00:00 2001 From: Marcos Lopez Gonzalez Date: Wed, 1 May 2019 13:10:42 +0200 Subject: [PATCH 3/5] AgeCalculator changes --- .../com/baeldung/exceptions/RootCauseFinder.java | 14 +++++++------- .../baeldung/exceptions/RootCauseFinderTest.java | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java index 11f29a2cf0..e05dc7a6cd 100644 --- a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java +++ b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java @@ -33,28 +33,28 @@ public class RootCauseFinder { } try { - return calculateDifference(birthDate).getYears(); + return Period + .between(parseDate(birthDate), LocalDate.now()) + .getYears(); } catch (DateParseException ex) { throw new CalculationException(ex); } } - private static Period calculateDifference(String birthDateAsString) throws DateParseException { + private static LocalDate parseDate(String birthDateAsString) throws DateParseException { - LocalDate birthDate = null; + LocalDate birthDate; try { birthDate = LocalDate.parse(birthDateAsString); } catch (DateTimeParseException ex) { throw new InvalidFormatException(birthDateAsString, ex); } - LocalDate today = LocalDate.now(); - - if (birthDate.isAfter(today)) { + if (birthDate.isAfter(LocalDate.now())) { throw new DateOutOfRangeException(birthDateAsString); } - return Period.between(birthDate, today); + return birthDate; } } diff --git a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java index c333a7ac26..cfac81b812 100644 --- a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java +++ b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java @@ -2,9 +2,12 @@ package com.baeldung.exceptions; import com.google.common.base.Throwables; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.time.LocalDate; import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoUnit; import static com.baeldung.exceptions.RootCauseFinder.*; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -14,6 +17,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue; */ public class RootCauseFinderTest { + @Test + public void givenBirthDate_whenCalculatingAge_thenAgeReturned() { + try { + int age = AgeCalculator.calculateAge("1990-01-01"); + Assertions.assertEquals(1990, LocalDate + .now() + .minus(age, ChronoUnit.YEARS) + .getYear()); + } catch (CalculationException e) { + Assertions.fail(e.getMessage()); + } + } + @Test public void givenWrongFormatDate_whenFindingRootCauseUsingJava_thenRootCauseFound() { try { From c2d94513d8df1b583958f9218a6cafb2c2c9f8f0 Mon Sep 17 00:00:00 2001 From: Marcos Lopez Gonzalez Date: Fri, 3 May 2019 20:13:38 +0200 Subject: [PATCH 4/5] RootCauseFinder improvements --- core-java/pom.xml | 2 +- .../baeldung/exceptions/RootCauseFinder.java | 5 ++++- .../exceptions/RootCauseFinderTest.java | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core-java/pom.xml b/core-java/pom.xml index 463b65a4ce..661e787149 100644 --- a/core-java/pom.xml +++ b/core-java/pom.xml @@ -454,7 +454,7 @@ 2.8.2 - 3.5 + 3.9 2.5 3.6.1 1.0.3 diff --git a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java index e05dc7a6cd..064ae27ac1 100644 --- a/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java +++ b/core-java/src/main/java/com/baeldung/exceptions/RootCauseFinder.java @@ -10,10 +10,13 @@ import java.util.Objects; */ public class RootCauseFinder { + private RootCauseFinder() { + } + public static Throwable findCauseUsingPlainJava(Throwable throwable) { Objects.requireNonNull(throwable); Throwable rootCause = throwable; - while (rootCause.getCause() != null) { + while (rootCause.getCause() != null && rootCause.getCause() != rootCause) { rootCause = rootCause.getCause(); } return rootCause; diff --git a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java index cfac81b812..5f61a39ad1 100644 --- a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java +++ b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java @@ -75,6 +75,15 @@ public class RootCauseFinderTest { } } + @Test + public void givenNullDate_whenFindingRootCauseUsingApacheCommons_thenRootCauseNotFound() { + try { + AgeCalculator.calculateAge(null); + } catch (Exception ex) { + assertTrue(ExceptionUtils.getRootCause(ex) instanceof IllegalArgumentException); + } + } + @Test public void givenWrongFormatDate_whenFindingRootCauseUsingGuava_thenRootCauseFound() { try { @@ -93,4 +102,13 @@ public class RootCauseFinderTest { } } + @Test + public void givenNullDate_whenFindingRootCauseUsingGuava_thenRootCauseFound() { + try { + AgeCalculator.calculateAge(null); + } catch (Exception ex) { + assertTrue(Throwables.getRootCause(ex) instanceof IllegalArgumentException); + } + } + } From c5f02232822e7081221a770afd297059e97bc8ef Mon Sep 17 00:00:00 2001 From: Marcos Lopez Gonzalez Date: Sat, 4 May 2019 19:59:12 +0200 Subject: [PATCH 5/5] test renamed to end with UnitTest suffix --- .../{RootCauseFinderTest.java => RootCauseFinderUnitTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename core-java/src/test/java/com/baeldung/exceptions/{RootCauseFinderTest.java => RootCauseFinderUnitTest.java} (98%) diff --git a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java similarity index 98% rename from core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java rename to core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java index 5f61a39ad1..5d0f3b9c3e 100644 --- a/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderTest.java +++ b/core-java/src/test/java/com/baeldung/exceptions/RootCauseFinderUnitTest.java @@ -15,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests the {@link RootCauseFinder}. */ -public class RootCauseFinderTest { +public class RootCauseFinderUnitTest { @Test public void givenBirthDate_whenCalculatingAge_thenAgeReturned() {