Skip to content

Instantly share code, notes, and snippets.

@pholser
Created October 26, 2015 19:12
Show Gist options
  • Select an option

  • Save pholser/38de784fc58dcdabb1ee to your computer and use it in GitHub Desktop.

Select an option

Save pholser/38de784fc58dcdabb1ee to your computer and use it in GitHub Desktop.

Revisions

  1. pholser created this gist Oct 26, 2015.
    140 changes: 140 additions & 0 deletions FailoverProxyFactoryBean.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,140 @@
    import com.containerstore.common.base.exception.SystemException;
    import com.google.common.collect.Iterables;
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.apache.log4j.Logger;
    import org.springframework.aop.framework.ProxyFactory;
    import org.springframework.beans.factory.FactoryBean;
    import org.springframework.remoting.support.RemoteAccessor;

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;

    import static com.containerstore.common.thirdparty.log4j.Log4JHelper.*;

    /**
    * Adapted from
    * <a href="http://agilearchitect.blogspot.com/2006/02/failover-mechansim-using-spring.html">this post [sic]</a>.
    */
    public class FailoverProxyFactoryBean<T>
    extends RemoteAccessor
    implements MethodInterceptor, FactoryBean<T> {

    private static final Logger LOG = classLogger();
    private static final String FATAL_FAILOVER_MESSAGE =
    "No service providers could satisfy method [%s] with arguments [%s]."
    + " Last attempted service provider [%s] raised exception.";

    private T serviceProxy;
    private List<T> serviceProviders = new ArrayList<>();

    public Object invoke(MethodInvocation invocation) throws Throwable {
    List<InvocationTargetException> exceptions = new ArrayList<>();

    for (T each : serviceProviders) {
    try {
    return invokeService(invocation, each);
    } catch (InvocationTargetException e) {
    exceptions.add(e);
    }
    }

    return handleServiceFatality(invocation, exceptions);
    }

    public void setServiceProviders(List<T> serviceProviders) {
    this.serviceProviders.addAll(serviceProviders);
    }

    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() throws Exception {
    if (getServiceInterface() == null) {
    throw new IllegalArgumentException("Service interface missing");
    }
    if (serviceProviders.isEmpty()) {
    throw new IllegalArgumentException("No service providers configured");
    }
    for (T each : serviceProviders) {
    if (!getServiceInterface().isInstance(each)) {
    throw new IllegalArgumentException(
    each.getClass()
    + " does not implement the serviceInterface: "
    + getServiceInterface());
    }
    }

    serviceProxy = (T) ProxyFactory.getProxy(getServiceInterface(), this);
    }

    public T getObject() {
    return this.serviceProxy;
    }

    public Class<?> getObjectType() {
    return getServiceInterface();
    }

    public boolean isSingleton() {
    return true;
    }

    private Object invokeService(MethodInvocation invocation, T provider)
    throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {

    Method method = invocation.getMethod();

    debug(LOG,
    "Invoking method [%s] with arguments [%s] on service provider [%s]",
    method.getName(),
    Arrays.asList(invocation.getArguments()),
    provider);

    try {
    return provider.getClass().getMethod(method.getName(), method.getParameterTypes())
    .invoke(provider, invocation.getArguments());
    } catch (IllegalAccessException
    | IllegalArgumentException
    | NoSuchMethodException
    | SecurityException e) {

    LOG.error(String.format(
    "Unexpected exception invoking method [%s] with arguments [%s] on service provider [%s]",
    method.getName(),
    Arrays.asList(invocation.getArguments()),
    provider), e);
    throw e;
    } catch (InvocationTargetException e) {
    LOG.error(String.format(
    "Exception invoking method [%s] with arguments [%s] on service provider [%s]",
    method.getName(),
    Arrays.asList(invocation.getArguments()),
    provider), e);
    throw e;
    }
    }

    private Object handleServiceFatality(
    MethodInvocation invocation,
    List<InvocationTargetException> exceptions) {

    InvocationTargetException last = Iterables.getLast(exceptions);
    LOG.fatal(
    String.format(
    FATAL_FAILOVER_MESSAGE,
    invocation.getMethod().getName(),
    Arrays.asList(invocation.getArguments()),
    Iterables.getLast(serviceProviders)),
    last);

    throw new SystemException(
    String.format(
    FATAL_FAILOVER_MESSAGE,
    invocation.getMethod().getName(),
    Arrays.asList(invocation.getArguments()),
    Iterables.getLast(serviceProviders)),
    last);
    }
    }