package org.ff4j.aop;
/*
* #%L ff4j-aop %% Copyright (C) 2013 Ff4J %% Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License. #L%
*/
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.stereotype.Component;
/**
* Spring bean used to scan classpath and create dynamic proxy on annotated beans (@Flip).
*
* @author Cedrick LUNVEN (@clunven)
*/
@Component("ff.autoproxy")
public class FeatureAutoProxy extends AbstractAutoProxyCreator {
/** Serial number. */
private static final long serialVersionUID = -364406999854610869L;
/** Cache to avoid two-passes on same interfaces. */
private final Map<String, Boolean> processedInterface = new HashMap<String, Boolean>();
/**
* Default constructor invoked by spring.
*/
public FeatureAutoProxy() {
// Define scanner for classes at startup
setInterceptorNames(getBeanNameOfFeatureAdvisor());
}
/**
* Read advisor bean name.
*
* @return
* id of {@link FeatureAdvisor} bean
*/
private String getBeanNameOfFeatureAdvisor() {
return FeatureAdvisor.class.getAnnotation(Component.class).value();
}
/** {@inheritDoc} */
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
// Do not used any AOP here as still working with classes and not objects
if (!beanClass.isInterface() && beanClass.getInterfaces() != null) {
for (Class<?> currentInterface : beanClass.getInterfaces()) {
Object[] r = scanInterface(currentInterface);
if (r != null) {
return r;
}
}
}
return DO_NOT_PROXY;
}
/**
* Add current annotated interface.
*
* @param currentInterface
* class to be scanned
* @return
*/
private Object[] scanInterface(Class<?> currentInterface) {
String currentInterfaceName = currentInterface.getCanonicalName();
// Do not scan internals
if (isJdkInterface(currentInterfaceName)) {
return null;
}
// Never scanned, scan first time
if (!processedInterface.containsKey(currentInterfaceName)) {
return scanInterfaceForAnnotation(currentInterface, currentInterfaceName);
}
// Already scanned and flipped do not add interceptors
Boolean isInterfaceFlipped = processedInterface.get(currentInterfaceName);
return isInterfaceFlipped ? PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS : null;
}
/**
* Avoid JDK classes.
*
* @param currentInterfaceName
* @return
*/
private boolean isJdkInterface(String currentInterfaceName) {
return currentInterfaceName.startsWith("java.");
}
private Object[] scanInterfaceForAnnotation(Class<?> currentInterface, String currentInterfaceName) {
// Interface never scan
if (currentInterface.isAnnotationPresent(Flip.class)) {
processedInterface.put(currentInterfaceName, true);
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
} else {
// not found on bean, check methods
for (Method method : currentInterface.getDeclaredMethods()) {
if (method.isAnnotationPresent(Flip.class)) {
processedInterface.put(currentInterfaceName, true);
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
}
}
}
// annotation has not been found
processedInterface.put(currentInterfaceName, false);
return null;
}
}