Jon Jagger
jon@jaggersoft.com

Even More Java Exceptions

A Recap

In Overload 49 Alan Griffiths continued his exploration of the exception safety landscape in the wilds of Java and looked at a problem introduced by Tom Cargill; namely how to make a copy of a whole object holding multiple parts in an exception safe manner. In his article Alan ensured that if construction of the whole object failed, the resources of all fully constructed parts were released. He achieved this by careful use of try/finally blocks and statement ordering - essentially, like this:

public class Part implements Cloneable
{
    ...
    public void dispose();
}

public class Whole
{   
    ...
    public void copy(Whole other)
    {
        Part t1 = (Part)other.p1.clone();
        if (t1 != null) {
            try {
                Part t2 = (Part)other.p2.clone();
                if (t2 != null) {
                    t1.setParent(this);
                    t2.setParent(this);

                    // pivot point

                    Part swap1 = t1;
                    t1 = p1;
                    p1 = swap1;

                    Part swap2 = t2;
                    t2 = p2;
                    p2 = swap2;
                }
            }
            finally {
                t2.dispose();
            }
        }
        finally {
            t1.dispose();
        }
    }

    private Part p1, p2; // never null
}

A detailed look

The copy method succeeds atomically (and returns) if nothing before the pivot point throws an exception:

The copy method fails atomically (with an exception) if any of the methods before the pivot point throw an exception:

A potential problem or two

One potential problem with copy as it stands is that if a clone() call returns null there will be no exception, copy will return, and the target of the copy will remain unchanged. This means you don't know the state of the object if the call to copy returns (rather than throwing an exception). There are at least two solutions to this problem:

I prefer the latter option, partly because it fits with the exception-for-failure model, but mostly because we've already established that other.p1 and other.p2 are never null and null simply isn't a clone of something that's not null.

There's another copy subtlety worth mentioning. It concerns the calls to setParent. These calls nicely illustrate that a whole-part relationship is, in general, a two-way relationship. If you follow the code through carefully, you'll see that once the swaps have taken place, the cloned parts (now referred to by the p1 and p2 fields) see the target of the copy (this) as their parent (which is fine), but that the old parts (now in t1 and t2) also see the target of the copy (this) as their parent. In other words, if a Whole holds N parts then N*2 parts will simultaneously think they're in the same Whole. In this case this isn't a problem because half of the parts are about to be disposed of. This is an issue that will reappear.

An interface: Disposable resource

The strategy I used in my attempt to progress through the jungle is an interface. The Disposable interface embodies the Disposal Method pattern - a method you can explicitly call to dispose of a resource at a known point in the code (this interface is noticeable by its absence from the Java SDK).

public interface Disposable
{
    void dispose();
}

A specification: Stack of Disposable resources

The next step is to create something that holds a stack of Disposable objects. The purpose of this class will be to hold a number of resources. I call it a stack because it effectively plays the part of the control flow stack in C++. That is, a stack in the sense of the opposite of a heap; the stack that, in C++, holds local objects and calls their destructors when they go out of scope. DisposableStack is an example of the Composite pattern (although I don't actually require the composite in this article).

public final class DisposableStack implements Disposable
{
    public DisposableArray(int initialCapacity);
    public void push(Disposable resource);
    public void clear();
    public void dispose();
    ...
}

An assumption

Now consider what would happen if Part implemented Cloneable and Disposable (I will remove this assumption later):

public class Part implements Cloneable, Disposable
{
    ...
}

A beginning

With these pieces of the puzzle in place you can collapse the multiple nested try/finally blocks in Whole.copy (one per part) down to just a single try/finally block. Notice that each resource is held twice, once in a local variable (eg t1, t2) and once inside the DisposableStack (resources). The trickiest part is the last three statements before the finally block. These statements ensure that a successful copy (one that passes the pivot point) disposes of the old Part resources just swapped from p1, p2 into t1, t2:

public class Whole
{
    ...
    public void copy(Whole other)
    {
        DisposableStack resources = new DisposableStack(2);
        try 
        {
            Part t1 = (Part)other.p1.clone();
            push(resources, t1);

            Part t2 = (Part)other.p2.clone();
            push(resources, t2);

            t1.setParent(this);
            t2.setParent(this);

            Part swap1 = t1;
            t1 = p1;
            p1 = swap1;

            Part swap2 = t2;
            t2 = p2;
            p2 = swap2;

            resources.clear();
            resources.push(t1);
            resources.push(t2);
        }
        finally
        {
            resources.dispose();
        }
    }

    private static void push(DisposableStack resources, Part resource)
    {
        if (resource == null)
        {
            throw ...
        }
        resources.push(resource);
    }

    private Part p1, p2; // never null
}

If a call to clone() returns null, the attempt to add it to the resources collection must throw an exception. We'll handle this in a Whole helper method rather than in DisposableStack so that DisposableStack can stay a general purpose class. If all the parts are cloned and added to the resources collection, the part specific calls take place (setParent) and then the swaps are performed. The swaps must not throw an exception. After the swaps, the part clones (not null) are in p1 and p2, and the old parts (also not null) are in t1 and t2. The resources stack is cleared, and the old parts are pushed in ready to be disposed of in the finally block. The code tells us the critical exception guarantees of the methods:

An implementation: Stack of Disposable resources

How can we guarantee these constraints? You might think about using an ArrayList. Can ArrayList methods throw exceptions? I'm not sure and I'm not going to bother looking because there is a better solution. Don't use a library container; use an array! This will not only provide us with complete assurance of exceptional behaviour; it will probably also makes the solution a little faster. Notice that this version disposes of the resources in a last-in last-out fashion (which seems appropriate), and also avoids the need for any casting:

public final class DisposableStack implements Disposable
{
    public DisposableStack(int fixedCapacity)
    {
        resources = new Disposable[fixedCapacity];
        at = 0;
    }

    public void push(Disposable resource)
    {
        // precondition(at < resources.length)

        resources[at] = resource;
        ++at;
    }

    public void clear()
    {
        resources = null;
        at = 0;
    }

    public void dispose()
    {
        while (at-- > 0)
        {
            resources[at].dispose();
        }
    }

    private final Disposable[] resources; 
    private int at;
}

A refactoring: Null Object Pattern

One glitch in this implementation is that if you push null, bad things will happen. You could solve this by checking for null in a push precondition. You can also use the Null Object pattern, like this:

public final class BenignNullDisposable implements Disposable
{
    public void dispose()
    {
        // nothing
    }
}
public final class DisposableStack implements Disposable
{
    ...
    public void push(Disposable resource)
    {
        resources[at] = 
            resource != null ? resource : nullDisposable;
        ++at;
    }
    ...
    private final Disposable nullDisposable = new BenignNullDisposable();
    ...
}

A refactoring: swap method

Something else that bothers me about this solution is the lack of a swap abstraction. If you study Alan's original code you'll see that the swap cannot be factored out because it swaps local variables that are disposed in their following finally blocks. However, at the cost of introducing another object, it is possible (and while we're here let's also make the swap the last statements in the try block):

public class Whole
{
    ...
    public void copy(Whole other)
    {
        DisposableStack resources = new DisposableStack(2);
        try 
        {
            Whole copy = new Whole();
            copy.p1 = (Part)other.p1.clone()
            push(resources, copy.p1); 
            copy.p2 = (Part)other.p2.clone();
            push(resources, copy.p2); 

            copy.p1.setParent(this);
            copy.p2.setParent(this);

            resources.clear();
            resources.push(p1);
            resources.push(p2);

            copy.swap(this);
        }
        finally
        {
            resources.dispose();
        }
    }

    public void swap(Whole other)
    {
        Part swap1 = p1;
        p1 = other.p1;
        other.p1 = swap1;

        Part swap2 = p2;
        p2 = other.p2;
        other.p2 = swap2;
    }
    ...
    private Part p1, p2; // never null
}

Note that this "solution" requires a default Whole constructor that creates an empty Whole object, that is, one that contains no resources. The idea is that you create an empty whole object, and then gradually fill it. If you've already implemented the default constructor and it doesn't do this you can simply create a private constructor with a dummy argument.

public class Whole
{   ...
    public Whole copy(Whole other)
    {
        DisposableStack resources = new DisposableStack(2);
        try 
        {
            Whole copy = new Whole(new Empty());
            // as before
        }
        ...
    }

    private static final class Empty 
    {
    }
 
    private Whole(Empty unused)
    {
    }
    ...
}

A bug: shallow copy

This would be a reasonable solution if it worked but unfortunately it doesn't. Before the swap, both objects are well formed. The target of the copy (this) has its parts and these parts know this is their parent. Similarly, the copied object has its parts (clones of this's parts) and these parts know copied is their parent. All well and good! The problem is that the swap only swaps the references; it's a shallow swap and it needs to be a deep swap. After the swap, the parts will be referring to the wrong parents. To swap a whole you have to be able to swap its parts. It's not enough to just swap the references.

public class Part implements Cloneable, Disposable
{   
    ...
    public void swap(Part other)
    {
        // swap all fields. must not throw
        ...
    }
}
public class Whole
{
    ...
    public void swap(Whole other)
    {
        // shallow
        Part swap1 = p1;
        p1 = other.p1;
        other.p1 = swap1;

        Part swap2 = p2;
        p2 = other.p2;
        other.p2 = swap2;

        // deep
        p1.swap(other.p1);
        p2.swap(other.p2);
    }
    ...
}

A refactoring: copy constructor

Let's stop for a moment to think about what we've just done. We've created an empty object so that we can gradually fill it with cloned parts. And remember that we're doing this so we can implement the copy method. With a little thought the idea of using a copy constructor suggests itself. That way, we won't have to worry about the initial state of the object we're creating. (If you've already implemented the copy constructor and it doesn't do this you can simply create a private constructor with a dummy second argument as before). Should the copy constructor be public? Well, the copy method is public so it seems reasonable.

public class Whole
{
    ...
    public Whole(Whole other)
    {
        DisposableStack resources = new DisposableStack(2);
        try 
        {
            push(resources, p1 = (Part)other.p1.clone());
            push(resources, p2 = (Part)other.p2.clone());

            p1.setParent(this);
            p2.setParent(this);

            resources.clear();
        }
        finally
        {
            resources.dispose();
        }
    }
    ...
    private Part p1, p2; // never null
}

A final piece: copy method

With this copy constructor in place, the obvious way to write the copy method is as follows:

public class Whole
{
    ...
    public void copy(Whole other)
    {
        Whole copy = new Whole(other);
        copy.swap(this);
    }
}

The problem is that copy does not dispose of the old parts after the swap. This is not C++ remember! Luckily we have exactly the right tool to fix this problem: DisposableStack.

public class Whole
{
    ...
    public void copy(Whole other)
    {
        DisposableStack resources = new DisposableStack(2);
        try
        {
            Whole copy = new Whole(other);
            push(resources, p1);
            push(resources, p2);
            copy.swap(this);
        }
        finally
        {
            resources.dispose();
        }
    }
    ...
}

A refactoring: resources as a field

This is now a reasonable solution. However, there is one last refactoring we can perform before returning to the assumption that Part implements Disposable. It revolves around the observation that there is an awful lot of pushing going on! A possibly more natural approach would be for the pushes to happen only at construction and for the clear to happen only at "disposal". The way to achieve this is to make the DisposableStack a field instead of a local variable. There are a number of points to note in this solution:

public class Whole
{
    ...
    public Whole(Whole other)
    {
        boolean exception = true;
        try 
        {
            push(p1 = (Part)other.p1.clone());
            push(p2 = (Part)other.p2.clone());

            p1.setParent(this);
            p2.setParent(this);

            exception = false; 
        }
        finally
        {
            if (exception)
            {
                try
                {
                    resources.dispose();
                }
                catch (Exception dropped)
                {
                    // let part clone exception dominate
                }
            }
        }
    }
    
    public void copy(Whole other)
    {
        Whole copy = new Whole(other);
        copy.swap(this);
        copy.resources.dispose(); 
    }

    public void swap(Whole other)
    {
        // shallow
        DisposableStack swap0 = resources;
        resources = other.resources;
        other.resources = swap0;

        Part swap1 = p1;
        p1 = other.p1;
        other.p1 = swap1;

        Part swap2 = p2;
        p2 = other.p2;
        other.p2 = swap2;

        // deep
        p1.swap(other.p1);
        p2.swap(other.p2);
    }

    private void push(Part resource)
    {
        if (resource == null)
        {
            throw ...
        }
        resources.push(resource);
    }

    private DisposableStack resources = new DisposableStack(2);

    private Part p1, p2; // never null
}

An assumption revisited: Disposable Part

Now let's return to the original assumption. What if Part does not implement the Disposable interface? Simple, just use an Adapter.

public final class PartDisposer implements Disposable
{
    public PartDisposer(Part adapted)
    {    
        // preCondition(adapted != null)

        adaptee = adapted;
    }
  
    public void dispose()
    {
        adaptee.dispose();
    }
 
    private final Part adaptee;
}

How do you use the adapter? There is a final trap we must take care not to fall into. The obvious (but flawed) way to use this adapter is as follows:

public class Whole
{
    ...
    private void push(Part resource)
    {
        if (resource == null)
        {
            throw ...
        }
        resources.push(new PartDisposer(resource));
    }
    ...
}

The problem is that if the creation of a new PartDisposer throws an exception the copied Part will not be pushed onto the resources stack. There are a number of ways to fix this. One is via a careful use of a try/finally block in the Whole.push helper method:

public class Whole
{
    ...
    private void push(Part resource)
    {
        if (resource == null)
        {
            throw ... 
        }
        try
        {
            resources.push(new PartDisposer(resource));
            resource = null;
        }
        finally
        {
            if (resource != null)
            {
                resource.dispose();
            }
        }
    }
    ... 
}

Conclusion

Has it been worth it? Is this version useful? As always there are opposing forces. This version is something of an overly large sledgehammer cracking a small nut when the number of parts is small. But as the number of parts increases, so does the depth of the try/finally nesting, and so the more attractive this version becomes. However, it does so at the cost of creating extra objects. It's also noticeable that this version involves a lot less work if the Part classes implement the Disposable interface, thus avoiding the need for Adapters.

Perhaps more than anything it's best to think of this solution to the whole-part copy problem as just one way to use the DisposableStack class. The exception safety guarantees make sense in Java and should be applied when writing or reviewing code. The problem is that the Java language offers almost no in-built support for this activity. DisposableStack is a class that might help.

I travelled down several false, but interesting trails, during this trek (and noticed a few that I didn't take). In design, as in life, you learn more from making the journey than you do from reaching the destination.

{ JSL }
Jagger Software Ltd
Company # 4070126
VAT # 762 5213 42