001/*
002 * (c) 2003-2005, 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 14-May-2004
010 */
011package com.thoughtworks.proxy.toys.failover;
012
013import static com.thoughtworks.proxy.toys.delegate.DelegationMode.DIRECT;
014
015import java.lang.reflect.InvocationTargetException;
016import java.lang.reflect.Method;
017
018import com.thoughtworks.proxy.ProxyFactory;
019import com.thoughtworks.proxy.kit.SimpleReference;
020import com.thoughtworks.proxy.toys.hotswap.HotSwappingInvoker;
021
022/**
023 * {@link com.thoughtworks.proxy.Invoker Invoker} that implements a failover strategy by using different delegates in
024 * case of an exception. The implemented strategy is a simple round-robin algorithm to change the delegate in case of a
025 * relevant exception.
026 *
027 * @author Dan North
028 * @author Aslak Hellesøy
029 * @author Jörg Schaible
030 * @since 0.1
031 */
032public class FailoverInvoker<T> extends HotSwappingInvoker<T> {
033    private static final long serialVersionUID = -8289095570093619184L;
034    private T[] delegates;
035    private Class<? extends Throwable> exceptionClass;
036    private int current;
037
038    /**
039     * Construct a FailoverInvoker.
040     *
041     * @param types          the types of the proxy
042     * @param proxyFactory   the {@link ProxyFactory} to use
043     * @param delegates      the delegates to use
044     * @param exceptionClass the type of the exception
045     * @since 0.1
046     */
047    public FailoverInvoker(final Class<?>[] types, final ProxyFactory proxyFactory, final T[] delegates, final Class<? extends Throwable> exceptionClass) {
048        super(types, proxyFactory, new SimpleReference<Object>(delegates[0]), DIRECT);
049        this.delegates = delegates;
050        this.exceptionClass = exceptionClass;
051    }
052
053    @Override
054    protected Object invokeOnDelegate(final Method method, final Object[] args) throws InvocationTargetException {
055        Object result = null;
056        final int original = current;
057        while (result == null) {
058            try {
059                result = super.invokeOnDelegate(method, args);
060                break;
061            } catch (InvocationTargetException e) {
062                if (exceptionClass.isInstance(e.getTargetException())) {
063                    synchronized (this) {
064                        current++;
065                        current = current % delegates.length;
066                        if (original == current) {
067                            throw e;
068                        }
069                        hotswap(delegates[current]);
070                    }
071                } else {
072                    throw e;
073                }
074            }
075        }
076        return result;
077    }
078}