package org.togglz.spring.proxy; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.HashSet; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBeanNotInitializedException; import org.springframework.beans.factory.InitializingBean; import org.togglz.core.Feature; import org.togglz.core.proxy.FeatureProxyInvocationHandler; import org.togglz.core.util.NamedFeature; import org.togglz.core.util.Validate; /** * <p> * Implementation of {@link FactoryBean} that creates a proxy that delegates invocation to one of two target beans depending on * that state of a given feature. * </p> * * <p> * You can use the factory like this: * </p> * * <pre> * <bean id="someService" class="org.togglz.spring.proxy.FeatureProxyFactoryBean"> * <property name="feature" value="FEATURE_ONE" /> * <property name="active" ref="newServiceImpl" /> * <property name="inactive" ref="oldServiceImpl" /> * </bean> * </pre> * * @author Christian Kaltepoth */ public class FeatureProxyFactoryBean implements FactoryBean<Object>, InitializingBean { private String feature; private Object active; private Object inactive; private Class<?> proxyType; private boolean initialized = false; @Override public void afterPropertiesSet() throws Exception { Validate.notBlank(feature, "The 'feature' property is required"); Validate.notNull(active, "The 'active' property is required"); Validate.notNull(inactive, "The 'inactive' property is required"); if (proxyType != null && !proxyType.isInterface()) { throw new IllegalArgumentException(proxyType.getClass().getName() + " is not an interface"); } initialized = true; } @Override public Object getObject() throws Exception { // make sure the factory is fully initialized if (!initialized) { throw new FactoryBeanNotInitializedException(); } // create the invocation handler that switches between implementations Feature namedFeature = new NamedFeature(feature); FeatureProxyInvocationHandler proxy = new FeatureProxyInvocationHandler(namedFeature, active, inactive); // obtain the interface for which to create the proxy Class<?> proxyType = getEffectiveProxyType(); // create the proxy return Proxy.newProxyInstance(getSuitableClassLoader(), new Class<?>[] { proxyType }, proxy); } private Class<?> getEffectiveProxyType() { // prefer the business interface manually set by the user if (proxyType != null) { return proxyType; } // check which interfaces the both delegates implements HashSet<Class<?>> activeInterfaces = new HashSet<Class<?>>(Arrays.asList(active.getClass().getInterfaces())); HashSet<Class<?>> inactiveInterfaces = new HashSet<Class<?>>(Arrays.asList(inactive.getClass().getInterfaces())); // build the intersection activeInterfaces.retainAll(inactiveInterfaces); // we need exactly one interface to share if (activeInterfaces.size() != 1) { throw new IllegalArgumentException("The active and the inactive class must share exactly one interface"); } return activeInterfaces.iterator().next(); } private ClassLoader getSuitableClassLoader() { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader == null) { classLoader = this.getClass().getClassLoader(); } return classLoader; } @Override public Class<?> getObjectType() { if (initialized) { return getEffectiveProxyType(); } else { return null; } } @Override public boolean isSingleton() { return true; } public String getFeature() { return feature; } public void setFeature(String feature) { this.feature = feature; } public Object getActive() { return active; } public void setActive(Object active) { this.active = active; } public Object getInactive() { return inactive; } public void setInactive(Object inactive) { this.inactive = inactive; } public Class<?> getProxyType() { return proxyType; } public void setProxyType(Class<?> proxyType) { this.proxyType = proxyType; } }