001/*
002 * (c) 2003-2004, 2009, 2010 ThoughtWorks Ltd
003 * All rights reserved.
004 *
005 * The software in this package is published under the terms of the BSD
006 * style license a copy of which has been included with this distribution in
007 * the LICENSE.txt file.
008 * 
009 * Created on 29-May-2004
010 */
011package com.thoughtworks.proxy.toys.future;
012
013import java.lang.reflect.InvocationTargetException;
014import java.lang.reflect.Method;
015import java.util.concurrent.Callable;
016import java.util.concurrent.ExecutorService;
017
018import com.thoughtworks.proxy.Invoker;
019import com.thoughtworks.proxy.ProxyFactory;
020import com.thoughtworks.proxy.toys.hotswap.HotSwapping;
021import com.thoughtworks.proxy.toys.hotswap.Swappable;
022import com.thoughtworks.proxy.toys.nullobject.Null;
023
024
025/**
026 * {@link com.thoughtworks.proxy.Invoker Invoker} that implements transparent asynchronous
027 * method calls. The invoked method will return immediately with a result that can be
028 * {@linkplain HotSwapping hot swapped}. This result proxy contains first a {@linkplain Null
029 * null object} and will automatically replaced later on when the asynchronous method call
030 * returns the correct result.
031 * 
032 * @author Aslak Hellesøy
033 * @since 1.0
034 */
035public class FutureInvoker implements Invoker {
036    private static final long serialVersionUID = 1L;
037    private final Object target;
038    private final ProxyFactory proxyFactory;
039    private final ExecutorService executor;
040
041    /**
042     * Construct the invoker.
043     * 
044     * @param target the instance that will have its methods called asynchronously
045     * @param proxyFactory the proxy factory used to create the proxy for the target instance
046     *            and all return types of the called methods
047     * @param executor the executor used to call the method asynchronously
048     * @since 1.0
049     */
050    public FutureInvoker(Object target, ProxyFactory proxyFactory, ExecutorService executor) {
051        this.target = target;
052        this.proxyFactory = proxyFactory;
053        this.executor = executor;
054    }
055
056    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
057        Class<?> returnType = method.getReturnType();
058        Object result = null;
059        if (!returnType.equals(void.class)) {
060            Object nullResult = Null.proxy(returnType).build(proxyFactory);
061            final Swappable swappableResult = Swappable.class.cast(HotSwapping.proxy(returnType).with(nullResult).build(proxyFactory));
062            result = swappableResult;
063            final Callable<Swappable> callable = new Callable<Swappable>() {
064                public Swappable call() throws IllegalAccessException, InvocationTargetException {
065                    Object invocationResult = method.invoke(target, args);
066                    swappableResult.hotswap(invocationResult);
067                    return swappableResult;
068                }
069            };
070            executor.submit(callable);
071        }
072
073        return result;
074    }
075}