Composite Design Pattern

0

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.

You can download the source code here: compositeDesignPatternExample.zip
Share.

Leave A Reply