views:

124

answers:

2

I have a method which, conceptually, looks something like:

Object f(Object o1) {
    Object o2 = longProcess1(o1);
    Object o3 = longProcess2(o2);
    return longProcess3(o3);
}

Where the processes themselves might also be compound:

Object longProcess1(Object o1) {
    Object o2 = longSubProcess1(o1);
    return longSubProcess2(o2);
}

And so forth, with the different processes potentially sitting in different modules. Most of the processes are long because they are computationally expensive, not IO-bound.

So far so good, but now I want f as a whole to be interruptable. The recommended Java way to do that is to periodically check for the interrupted flag with Thread.interrupted(). It's pretty straightforward, but it can quickly become cumbersome if I need to change my methods to something like:

Object f(Object o1) {
    Object o2 = longProcess1(o1);
    if (Thread.interrupted()) throw new InterruptedException();
    Object o3 = longProcess2(o2);
    if (Thread.interrupted()) throw new InterruptedException();
    return longProcess3(o3);
}

Object longProcess1(Object o1) {
    Object o2 = longSubProcess1(o1);
    if (Thread.interrupted()) throw new InterruptedException();
    return longSubProcess2(o2);
}

...

Now, I do understand the rational for working like that - it allows me to better control when the InterruptedException (for example) will be thrown, avoiding leaving objects in inconsistent states - but I am curious to know if there's a more elegant way of doing that*.

* In Java, not AspectJ, which I guess is very appropriate here but I'm stuck with Java.

+4  A: 

You could use an interface and a Dynamic proxy:

public class Wrapper {
    public static <T> T wrap(Class<T> intf, final T impl) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        Object proxy = Proxy.newProxyInstance(cl, new Class<?>[] {intf},
                new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                if (Thread.interrupted()) {
                    throw new InterruptedException();
                }
                return method.invoke(impl, args);
            }
        });
        return intf.cast(proxy);
    }
}

interface Processes {
    Object longProcess1(Object o);
    ...
}

public class ProcessesImpl implement Processes {
    Processes self = Wrapper.wrap(Processes.class, this);

    public Object f(Object o1) {
        Object o2 = self.longProcess1(o1);
        Object o3 = self.longProcess2(o2);
        return self.longProcess3(o3);
    }

    public Object longProcess1(Object o1) {
        Object o2 = self.longSubProcess1(o1);
        return self.longSubProcess2(o2);
    }

    ....
}
Maurice Perry
Interesting, but still a bit cumbersome. I would need to add a field for all relevant classes, and qualify many method calls. The resulting methods *are* more elegant, though, I'll give you that!
Oak
A: 

Did I get this right that you sequentially run methods that are at the same nesting level? If so, why not just implement your computation methods as java.lang.Runnable instances, organize them into lists and start them in the loop? Then you would have only one place with Thread.interrupted() check.

You might consider using java.util.concurrent.ExecutorService to facilitate the control over computation tasks.

Updated with an example:

import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        List<CompoundProcess> subProcesses1 = new ArrayList<CompoundProcess>();
        subProcesses1.add(new CompoundProcess() {
            public void run() {
                System.out.println("Process 1.1");
            }
        });
        subProcesses1.add(new CompoundProcess() {
            public void run() {
                System.out.println("Process 1.2");
            }
        });

        List<CompoundProcess> subProcesses2 = new ArrayList<CompoundProcess>();
        subProcesses2.add(new CompoundProcess() {
            public void run() {
                System.out.println("Process 2.1");
            }
        });
        subProcesses2.add(new CompoundProcess() {
            public void run() {
                System.out.println("Process 2.2");
            }
        });

        List<CompoundProcess> processes1 = new ArrayList<CompoundProcess>() {};
        processes1.add(new CompoundProcess(subProcesses1));
        processes1.add(new CompoundProcess(subProcesses2));

        CompoundProcess process = new CompoundProcess(processes1);
        process.run();
    }


    static class CompoundProcess implements Runnable {

        private List<CompoundProcess> processes = new ArrayList<CompoundProcess>();

        public CompoundProcess() {
        }

        public CompoundProcess(List<CompoundProcess> processes) {
            this.processes = processes;
        }

        public void run() {
            for (Runnable process : processes) {
                if (Thread.interrupted()) {
                    throw new RuntimeException("The processing was interrupted");
                } else {
                    process.run();
                }
            }
        }
    }

}
fnt
No, the processes are in many different places - I tried to demonstrate it by showing that `longProcess1` is in itself compound, maybe I wasn't clear enough - so I've edited the question to clarify.
Oak
Once processes at the same level are run sequentially it does not matter much that they are started in different places. I updated my answer with an example. You can nest processes to the level you want.
fnt