diff --git a/core-java-modules/core-java-exceptions/pom.xml b/core-java-modules/core-java-exceptions/pom.xml
index 60c5e2650a..0778b6b5a3 100644
--- a/core-java-modules/core-java-exceptions/pom.xml
+++ b/core-java-modules/core-java-exceptions/pom.xml
@@ -29,6 +29,11 @@
             ${lombok.version}
             provided
         
+        
+            org.apache.commons
+            commons-lang3
+            ${commons.lang3.version}
+        
         
         
             org.assertj
@@ -40,6 +45,7 @@
 
     
         1.5.0-b01
+        3.10
         
         3.10.0
     
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/Arithmetic.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/Arithmetic.java
new file mode 100644
index 0000000000..db29198b39
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/Arithmetic.java
@@ -0,0 +1,20 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Arithmetic {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(Arithmetic.class);
+
+    public static void main(String[] args) {
+
+        try {
+            int result = 30 / 0; // Trying to divide by zero
+        } catch (ArithmeticException e) {
+            LOGGER.error("ArithmeticException caught!");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ArrayIndexOutOfBounds.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ArrayIndexOutOfBounds.java
new file mode 100644
index 0000000000..54c95f224c
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ArrayIndexOutOfBounds.java
@@ -0,0 +1,24 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ArrayIndexOutOfBounds {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(ArrayIndexOutOfBounds.class);
+
+    public static void main(String[] args) {
+
+        int[] nums = new int[] { 1, 2, 3 };
+
+        try {
+            int numFromNegativeIndex = nums[-1]; // Trying to access at negative index
+            int numFromGreaterIndex = nums[4]; // Trying to access at greater index
+            int numFromLengthIndex = nums[3]; // Trying to access at index equal to size of the array
+        } catch (ArrayIndexOutOfBoundsException e) {
+            LOGGER.error("ArrayIndexOutOfBoundsException caught");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ClassCast.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ClassCast.java
new file mode 100644
index 0000000000..8f8a6cf9e6
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ClassCast.java
@@ -0,0 +1,36 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class Animal {
+
+}
+
+class Dog extends Animal {
+
+}
+
+class Lion extends Animal {
+
+}
+
+public class ClassCast {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(ClassCast.class);
+
+    public static void main(String[] args) {
+
+        try {
+            Animal animalOne = new Dog(); // At runtime the instance is dog
+            Dog bruno = (Dog) animalOne; // Downcasting
+
+            Animal animalTwo = new Lion(); // At runtime the instance is animal
+            Dog tommy = (Dog) animalTwo; // Downcasting
+        } catch (ClassCastException e) {
+            LOGGER.error("ClassCastException caught!");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/FileNotFound.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/FileNotFound.java
new file mode 100644
index 0000000000..a9f2e5ee84
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/FileNotFound.java
@@ -0,0 +1,25 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FileNotFound {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(FileNotFound.class);
+    
+    public static void main(String[] args) {
+        
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new FileReader(new File("/invalid/file/location")));
+        } catch (FileNotFoundException e) {
+            LOGGER.error("FileNotFoundException caught!");
+        }
+    }
+    
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/GlobalExceptionHandler.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/GlobalExceptionHandler.java
new file mode 100644
index 0000000000..f2e89f44e3
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/GlobalExceptionHandler.java
@@ -0,0 +1,28 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GlobalExceptionHandler {
+
+    public static void main(String[] args) {
+
+        Handler globalExceptionHandler = new Handler();
+        Thread.setDefaultUncaughtExceptionHandler(globalExceptionHandler);
+        new GlobalExceptionHandler().performArithmeticOperation(10, 0);
+    }
+
+    public int performArithmeticOperation(int num1, int num2) {
+        return num1/num2;
+    }
+
+}
+
+class Handler implements Thread.UncaughtExceptionHandler {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(Handler.class);
+
+    public void uncaughtException(Thread t, Throwable e) {
+        LOGGER.info("Unhandled exception caught!");
+    }
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/IllegalArgument.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/IllegalArgument.java
new file mode 100644
index 0000000000..d54757dfac
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/IllegalArgument.java
@@ -0,0 +1,18 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IllegalArgument {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(IllegalArgument.class);
+
+    public static void main(String[] args) {
+        try {
+            Thread.sleep(-1000);
+        } catch (InterruptedException e) {
+            LOGGER.error("IllegalArgumentException caught!");
+        }
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/IllegalState.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/IllegalState.java
new file mode 100644
index 0000000000..0a812d2b82
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/IllegalState.java
@@ -0,0 +1,32 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IllegalState {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(IllegalState.class);
+
+    public static void main(String[] args) {
+
+        List intList = new ArrayList<>();
+
+        for (int i = 0; i < 10; i++) {
+            intList.add(i);
+        }
+
+        Iterator intListIterator = intList.iterator(); // Initialized with index at -1
+
+        try {
+            intListIterator.remove(); // IllegalStateException
+        } catch (IllegalStateException e) {
+            LOGGER.error("IllegalStateException caught!");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/InterruptedExceptionExample.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/InterruptedExceptionExample.java
new file mode 100644
index 0000000000..d0c8bb2cd0
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/InterruptedExceptionExample.java
@@ -0,0 +1,28 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class ChildThread extends Thread {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(ChildThread.class);
+
+    public void run() {
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            LOGGER.error("InterruptedException caught!");
+        }
+    }
+    
+}
+
+public class InterruptedExceptionExample {
+
+    public static void main(String[] args) throws InterruptedException {
+        ChildThread childThread = new ChildThread();
+        childThread.start();
+        childThread.interrupt();
+    }
+    
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/MalformedURL.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/MalformedURL.java
new file mode 100644
index 0000000000..9a02f005fd
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/MalformedURL.java
@@ -0,0 +1,25 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MalformedURL {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(MalformedURL.class);
+
+    public static void main(String[] args) {
+
+        URL baeldungURL = null;
+
+        try {
+            baeldungURL = new URL("malformedurl");
+        } catch (MalformedURLException e) {
+            LOGGER.error("MalformedURLException caught!");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/NullPointer.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/NullPointer.java
new file mode 100644
index 0000000000..445cbecdc8
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/NullPointer.java
@@ -0,0 +1,36 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NullPointer {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(NullPointer.class);
+
+    public static void main(String[] args) {
+
+        Person personObj = null;
+
+        try {
+            String name = personObj.personName; // Accessing the field of a null object
+            personObj.personName = "Jon Doe"; // Modifying the field of a null object
+        } catch (NullPointerException e) {
+            LOGGER.error("NullPointerException caught!");
+        }
+
+    }
+}
+
+class Person {
+
+    public String personName;
+
+    public String getPersonName() {
+        return personName;
+    }
+
+    public void setPersonName(String personName) {
+        this.personName = personName;
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/NumberFormat.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/NumberFormat.java
new file mode 100644
index 0000000000..576fe51f78
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/NumberFormat.java
@@ -0,0 +1,23 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NumberFormat {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(NumberFormat.class);
+
+    public static void main(String[] args) {
+
+        String str1 = "100ABCD";
+
+        try {
+            int x = Integer.parseInt(str1); // Converting string with inappropriate format
+            int y = Integer.valueOf(str1);
+        } catch (NumberFormatException e) {
+            LOGGER.error("NumberFormatException caught!");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ParseExceptionExample.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ParseExceptionExample.java
new file mode 100644
index 0000000000..e3b3e04b10
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/ParseExceptionExample.java
@@ -0,0 +1,25 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ParseExceptionExample {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(ParseExceptionExample.class);
+
+    public static void main(String[] args) {
+
+        DateFormat format = new SimpleDateFormat("MM, dd, yyyy");
+
+        try {
+            format.parse("01, , 2010");
+        } catch (ParseException e) {
+            LOGGER.error("ParseException caught!");
+        }
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/StringIndexOutOfBounds.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/StringIndexOutOfBounds.java
new file mode 100644
index 0000000000..0ee132e568
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/globalexceptionhandler/StringIndexOutOfBounds.java
@@ -0,0 +1,23 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StringIndexOutOfBounds {
+
+    private static Logger LOGGER = LoggerFactory.getLogger(StringIndexOutOfBounds.class);
+    
+    public static void main(String[] args) {
+
+        String str = "Hello World";
+
+        try {
+            char charAtNegativeIndex = str.charAt(-1); // Trying to access at negative index
+            char charAtLengthIndex = str.charAt(11); // Trying to access at index equal to size of the string
+        } catch (StringIndexOutOfBoundsException e) {
+           LOGGER.error("StringIndexOutOfBoundsException caught");
+        }
+
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/rootcausefinder/RootCauseFinder.java b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/rootcausefinder/RootCauseFinder.java
new file mode 100644
index 0000000000..06610f3874
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/main/java/com/baeldung/exceptions/rootcausefinder/RootCauseFinder.java
@@ -0,0 +1,95 @@
+package com.baeldung.exceptions.rootcausefinder;
+
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.format.DateTimeParseException;
+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;
+    }
+
+    /**
+     *  Calculates the age of a person from a given date.
+     */
+    static class AgeCalculator {
+
+        private AgeCalculator() {
+        }
+
+        public static int calculateAge(String birthDate) throws CalculationException {
+            if (birthDate == null || birthDate.isEmpty()) {
+                throw new IllegalArgumentException();
+            }
+
+            try {
+                return Period
+                  .between(parseDate(birthDate), LocalDate.now())
+                  .getYears();
+            } catch (DateParseException ex) {
+                throw new CalculationException(ex);
+            }
+        }
+
+        private static LocalDate parseDate(String birthDateAsString) throws DateParseException {
+
+            LocalDate birthDate;
+            try {
+                birthDate = LocalDate.parse(birthDateAsString);
+            } catch (DateTimeParseException ex) {
+                throw new InvalidFormatException(birthDateAsString, ex);
+            }
+
+            if (birthDate.isAfter(LocalDate.now())) {
+                throw new DateOutOfRangeException(birthDateAsString);
+            }
+
+            return birthDate;
+        }
+
+    }
+
+    static class CalculationException extends Exception {
+
+        CalculationException(DateParseException ex) {
+            super(ex);
+        }
+    }
+
+    static class DateParseException extends Exception {
+
+        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-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/globalexceptionhandler/GlobalExceptionHandlerUnitTest.java b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/globalexceptionhandler/GlobalExceptionHandlerUnitTest.java
new file mode 100644
index 0000000000..74ceb3b442
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/globalexceptionhandler/GlobalExceptionHandlerUnitTest.java
@@ -0,0 +1,64 @@
+package com.baeldung.exceptions.globalexceptionhandler;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.slf4j.LoggerFactory;
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.core.Appender;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GlobalExceptionHandlerUnitTest {
+
+    @Mock
+    private Appender mockAppender;
+
+    @Captor
+    private ArgumentCaptor captorLoggingEvent;
+
+    @Before
+    public void setup() {
+        final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+        logger.addAppender(mockAppender);
+
+        Handler globalExceptionHandler = new Handler();
+        Thread.setDefaultUncaughtExceptionHandler(globalExceptionHandler);
+    }
+
+    @After
+    public void teardown() {
+        final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+        logger.detachAppender(mockAppender);
+    }
+
+    @Test
+    public void whenArithmeticException_thenUseUncaughtExceptionHandler() throws InterruptedException {
+
+        Thread globalExceptionHandlerThread = new Thread() {
+            public void run() {
+                GlobalExceptionHandler globalExceptionHandlerObj = new GlobalExceptionHandler();
+                globalExceptionHandlerObj.performArithmeticOperation(99, 0);
+            }
+        };
+
+        globalExceptionHandlerThread.start();
+        globalExceptionHandlerThread.join();
+
+        verify(mockAppender).doAppend(captorLoggingEvent.capture());
+        LoggingEvent loggingEvent = captorLoggingEvent.getValue();
+
+        assertThat(loggingEvent.getLevel()).isEqualTo(Level.INFO);
+        assertThat(loggingEvent.getFormattedMessage()).isEqualTo("Unhandled exception caught!");
+    }
+
+}
diff --git a/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/rootcausefinder/RootCauseFinderUnitTest.java b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/rootcausefinder/RootCauseFinderUnitTest.java
new file mode 100644
index 0000000000..f42388857a
--- /dev/null
+++ b/core-java-modules/core-java-exceptions/src/test/java/com/baeldung/exceptions/rootcausefinder/RootCauseFinderUnitTest.java
@@ -0,0 +1,99 @@
+package com.baeldung.exceptions.rootcausefinder;
+
+import com.baeldung.exceptions.rootcausefinder.RootCauseFinder.CalculationException;
+import com.baeldung.exceptions.rootcausefinder.RootCauseFinder.DateOutOfRangeException;
+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.RootCauseFinder.AgeCalculator;
+import static com.baeldung.exceptions.rootcausefinder.RootCauseFinder.findCauseUsingPlainJava;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the {@link RootCauseFinder}.
+ */
+public class RootCauseFinderUnitTest {
+
+    @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 {
+            AgeCalculator.calculateAge("010102");
+        } catch (CalculationException ex) {
+            assertTrue(findCauseUsingPlainJava(ex) instanceof DateTimeParseException);
+        }
+    }
+
+    @Test
+    public void givenOutOfRangeDate_whenFindingRootCauseUsingJava_thenRootCauseFound() {
+        try {
+            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 givenWrongFormatDate_whenFindingRootCauseUsingApacheCommons_thenRootCauseFound() {
+        try {
+            AgeCalculator.calculateAge("010102");
+        } catch (CalculationException ex) {
+            assertTrue(ExceptionUtils.getRootCause(ex) instanceof DateTimeParseException);
+        }
+    }
+
+    @Test
+    public void givenOutOfRangeDate_whenFindingRootCauseUsingApacheCommons_thenRootCauseFound() {
+        try {
+            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);
+        }
+    }
+
+}
diff --git a/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/exceptions/RootCauseFinder.java b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/exceptions/RootCauseFinder.java
new file mode 100644
index 0000000000..e05dc7a6cd
--- /dev/null
+++ b/core-java-modules/core-java-lang-2/src/test/java/com/baeldung/exceptions/RootCauseFinder.java
@@ -0,0 +1,95 @@
+package com.baeldung.exceptions;
+
+import java.time.LocalDate;
+import java.time.Period;
+import java.time.format.DateTimeParseException;
+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;
+    }
+
+    /**
+     *  Calculates the age of a person from a given date.
+     */
+    static class AgeCalculator {
+
+        private AgeCalculator() {
+        }
+
+        public static int calculateAge(String birthDate) throws CalculationException {
+            if (birthDate == null || birthDate.isEmpty()) {
+                throw new IllegalArgumentException();
+            }
+
+            try {
+                return Period
+                  .between(parseDate(birthDate), LocalDate.now())
+                  .getYears();
+            } catch (DateParseException ex) {
+                throw new CalculationException(ex);
+            }
+        }
+
+        private static LocalDate parseDate(String birthDateAsString) throws DateParseException {
+
+            LocalDate birthDate;
+            try {
+                birthDate = LocalDate.parse(birthDateAsString);
+            } catch (DateTimeParseException ex) {
+                throw new InvalidFormatException(birthDateAsString, ex);
+            }
+
+            if (birthDate.isAfter(LocalDate.now())) {
+                throw new DateOutOfRangeException(birthDateAsString);
+            }
+
+            return birthDate;
+        }
+
+    }
+
+    static class CalculationException extends Exception {
+
+        CalculationException(DateParseException ex) {
+            super(ex);
+        }
+    }
+
+    static class DateParseException extends Exception {
+
+        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);
+        }
+
+    }
+
+}