Java - Handle and Declare example..

.. with all those nice "caused by"s and "... x more"s!

This is the source code I have:

A.java
package a;
public class A {
    public void a() throws AException {
        throw new AException();
    }
}

AException.java
package a;
public class AException extends Exception {
    AException() {
        super("Message from AException!");
    }
}

B.java
package b;
 
import a.A;
import a.AException;
 
public class B {
 
    public void b() throws BException {
        final A a = new A();
        try {
            a.a();
        } catch (AException e) {
            final BException bException = new BException("I am the B exception!");
            bException.initCause(e);
            throw bException;
        }
    }
}

BException.java
package b;
 
public class BException extends Exception {
    BException(String message) {
        super(message);
    }
}

C.java
package c;
 
import b.B;
import b.BException;
 
public class C {
 
    public void c() throws CException {
        B b = new B();
        try {
            b.b();
        } catch (BException e) {
            final CException cException = new CException();
            cException.initCause(e);
            throw cException;
        }
    }
}

CException.java
package c;
 
public class CException extends Exception {}

And finally TestClass.java
import c.C;
import c.CException;
 
public class TestClass {
 
    public static void main(String[] args) throws CException {
        final C c = new C();
        c.c();
    }
}

Yada Yada 

This is somewhat like a layered architecture. You can see A as the lowest layer, and C as the highest layer which makes B the middle (or service) layer.. What is important here is, imagine B starts using D instead of A.. We will not require to change C at all, since it is only depends BException. And we are using the initCause method from Throwable class so we are not losing where the exception is originated from. Lets run our example:

If we modify C.java as follows:
package c;
 
import b.B;
import b.BException;
 
public class C {
 
    public void c() throws CException {
        B b = new B();
        try {
            b.b();
        } catch (BException e) {
            final CException cException = new CException();
            throw cException;
        }
    }
}

all we get will be:
which is not very useful, is it?

What about all those ... 1 more and ... 2 more stuff?
Here is the answer to that.