The composite pattern is used when you want to create a tree structure where a component can be a leaf or a branch and they are tied together by a common operation. The only difference being that a branch can have further components which in turn can be either a branch or a leaf. This ability is useful if the client wants to treat compositions of objects and the individual objects in the same way.
In our example here, we will use the composite design pattern to build an exception that is a composed of one or more other exceptions where the exception itself can be another CompositeException. As we print the stack trace the list of throwables is iterated through to build the composite stack trace.
CompositeException:
package com.javarticles.concurrency; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public class CompositeException extends RuntimeException { private static final long serialVersionUID = 5026420048545643238L; private final List<Throwable> exceptions; private final String message; private Throwable cause; public CompositeException(Throwable... exceptions) { this(Arrays.asList(exceptions)); } public CompositeException(Iterable<? extends Throwable> exceptions) { Set<Throwable> allExceptions = new LinkedHashSet<Throwable>(); for (Throwable ex : exceptions) { if (ex instanceof CompositeException) { allExceptions.addAll(((CompositeException) ex).getExceptions()); } else { allExceptions.add(ex); } } this.exceptions = Collections.unmodifiableList(new ArrayList<>(allExceptions)); this.message = this.exceptions.size() + " exceptions occurred."; } public List<Throwable> getExceptions() { return exceptions; } @Override public String getMessage() { return message; } @Override public synchronized Throwable getCause() { if (cause == null) { CompositeExceptionChain localCause = new CompositeExceptionChain(); Set<Throwable> uniqueThrowables = new HashSet<Throwable>(); Throwable chain = localCause; for (Throwable e : exceptions) { if (uniqueThrowables.contains(e)) { continue; } uniqueThrowables.add(e); List<Throwable> listOfCauses = getAllNestedCauses(e); for (Throwable child : listOfCauses) { if (uniqueThrowables.contains(child)) { continue; } uniqueThrowables.add(child); } try { chain.initCause(e); } catch (Throwable t) { } chain = getRootCause(chain); } cause = localCause; } return cause; } @Override public void printStackTrace() { System.err.println(getThreadStackTrace()); } public String getThreadStackTrace() { StringBuilder b = new StringBuilder(128); b.append(this).append('\n'); for (StackTraceElement myStackElement : getStackTrace()) { b.append("\tat ").append(myStackElement).append('\n'); } int i = 1; for (Throwable ex : exceptions) { b.append(" ComposedException ").append(i).append(" :\n"); appendStackTrace(b, ex, "\t"); i++; } return b.toString(); } private void appendStackTrace(StringBuilder b, Throwable ex, String prefix) { b.append(prefix).append(ex).append('\n'); for (StackTraceElement stackElement : ex.getStackTrace()) { b.append("\t\tat ").append(stackElement).append('\n'); } if (ex.getCause() != null) { b.append("\tCaused by: "); appendStackTrace(b, ex.getCause(), ""); } } static final class CompositeExceptionChain extends RuntimeException { private static final long serialVersionUID = 1270173020671865330L; } private List<Throwable> getAllNestedCauses(Throwable ex) { List<Throwable> list = new ArrayList<Throwable>(); Throwable root = ex.getCause(); if (root == null || root == ex) { return list; } else { while (true) { list.add(root); Throwable cause = root.getCause(); if (cause == null || cause == root) { return list; } else { root = cause; } } } } public int size() { return exceptions.size(); } Throwable getRootCause(Throwable e) { Throwable root = e.getCause(); if (root == null || cause == root) { return e; } while (true) { Throwable cause = root.getCause(); if (cause == null || cause == root) { return root; } root = cause; } } }
Now we will build a stack of exceptions. Exception Ex3
root cause is Ex2
and Ex2
root cause is Ex1
. We build our first composite exception based on the three exceptions.
List<Throwable> throwables = new ArrayList<Throwable>(); throwables.add(ex1); throwables.add(ex2); throwables.add(ex3); CompositeException compositeException = new CompositeException(throwables);
The second composite exception that we build is composed of an exception and composite exception itself.
Throwable ex4 = new Throwable("Ex4"); compositeException = new CompositeException(compositeException, ex4);
After we building each of the composite exception, we print the stack trace which will give us a glimpse how how we have linked the exceptions.
CompositeDesignPatternExample:
package com.javarticles.concurrency; import java.util.ArrayList; import java.util.List; public class CompositeDesignPatternExample { public static void main(String[] args) { Throwable ex1 = new Throwable("Ex1"); Throwable ex2 = new Throwable("Ex2", ex1); Throwable ex3 = new Throwable("Ex3", ex2); List<Throwable> throwables = new ArrayList<Throwable>(); throwables.add(ex1); throwables.add(ex2); throwables.add(ex3); CompositeException compositeException = new CompositeException(throwables); compositeException.printStackTrace(); System.err.println("_______________________________________________"); Throwable ex4 = new Throwable("Ex4"); compositeException = new CompositeException(compositeException, ex4); compositeException.printStackTrace(); } }
Output:
com.javarticles.concurrency.CompositeException: 3 exceptions occurred. at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:17) ComposedException 1 : java.lang.Throwable: Ex1 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:9) ComposedException 2 : java.lang.Throwable: Ex2 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:10) Caused by: java.lang.Throwable: Ex1 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:9) ComposedException 3 : java.lang.Throwable: Ex3 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:11) Caused by: java.lang.Throwable: Ex2 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:10) Caused by: java.lang.Throwable: Ex1 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:9) _______________________________________________ com.javarticles.concurrency.CompositeException: 4 exceptions occurred. at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:21) ComposedException 1 : java.lang.Throwable: Ex1 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:9) ComposedException 2 : java.lang.Throwable: Ex2 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:10) Caused by: java.lang.Throwable: Ex1 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:9) ComposedException 3 : java.lang.Throwable: Ex3 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:11) Caused by: java.lang.Throwable: Ex2 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:10) Caused by: java.lang.Throwable: Ex1 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:9) ComposedException 4 : java.lang.Throwable: Ex4 at com.javarticles.concurrency.CompositeDesignPatternExample.main(CompositeDesignPatternExample.java:20)
Download the source code
This was an example about composite design pattern.