package cn.agrael.struts.plugin.ejb3;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.ACTION_PROXY_INTERFACES;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.ENC_PATH;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.INTERCEPTOR_INTERFACES;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.IS_PARSE_EJB_ANNOTATION;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.IS_PARSE_RESOURCE_ANNOTATION;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.LOCAL;
import static cn.agrael.struts.plugin.ejb3.StrutsEJBPluginConstant.REMOTE;
import static com.opensymphony.xwork2.inject.util.ReferenceType.SOFT;
import static com.opensymphony.xwork2.inject.util.ReferenceType.STRONG;
import static com.opensymphony.xwork2.inject.util.ReferenceType.WEAK;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.interceptor.AroundInvoke;
import javax.interceptor.ExcludeClassInterceptors;
import javax.interceptor.ExcludeDefaultInterceptors;
import javax.interceptor.Interceptors;
import javax.interceptor.InvocationContext;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import cn.agrael.struts.plugin.ejb3.annotation.EJBComponent;
import cn.agrael.struts.plugin.ejb3.annotation.ResourceComponent;
import cn.agrael.util.ObjectUtils;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.inject.util.ReferenceMap;
import com.opensymphony.xwork2.interceptor.Interceptor;
/**
* struts 与 EJB 整合的相关工具。
*
* @author <a href="mailto:agraellee@gmail.com">Agrael·Lee</a>
*
*/
public class StrutsEJBUtils {
/** 以完全类名为 key 的 BeanEJBAnnotation 缓存 */
private static final ReferenceMap<String, BeanEJBAnnotation> BEAN_EJB_ANNOTATION_CACHE = new ReferenceMap<String, BeanEJBAnnotation>(
STRONG, SOFT);
/** 以完全类名为 key 的 Action公开方法 缓存 */
private static final ReferenceMap<Class<?>, Set<String>> ACTION_PUBLIC_METHODS_CACHE = new ReferenceMap<Class<?>, Set<String>>(WEAK,
STRONG);
private StrutsEJBUtils() {
}
/**
* 得到一个类缓存中的 BeanEJBAnnotation。
*
* @param beanName 完全类名。
* @return beanName 在 缓存中的 BeanEJBAnnotation ,如果不存在则返回 null。
*/
public static BeanEJBAnnotation getBeanEJBAnnotation(String beanName) {
return BEAN_EJB_ANNOTATION_CACHE.get(beanName);
}
/**
* 得到 bean 实例的类在缓存中的 BeanEJBAnnotation。
*
* @param bean 要得到缓存中的 BeanEJBAnnotation 的bean实例。
* @return bean 实例的类 缓存中的 BeanEJBAnnotation ,如果不存在则返回 null。
* @see #getBeanEJBAnnotation(String)
*/
public static BeanEJBAnnotation getBeanEJBAnnotation(Object bean) {
if (bean == null) {
return null;
}
return getBeanEJBAnnotation(bean.getClass().getName());
}
/**
* 查看缓存中是否存在 beanName 对应的BeanEJBAnnotation。
*
* @param beanName 完全类名。
* @return 存在返回 true ,不存在返回 false。
*/
public static boolean hasBeanEJBAnnotation(String beanName) {
return BEAN_EJB_ANNOTATION_CACHE.containsKey(beanName);
}
/**
* 查看缓存中是否存在 bean 实例的类 对应的BeanEJBAnnotation。
*
* @param bean 要得查看缓存中是否存在 BeanEJBAnnotation 的bean实例。
* @return 存在返回 true ,不存在返回 false。
*/
public static boolean hasBeanEJBAnnotation(Object bean) {
if (bean == null) {
return false;
}
return hasBeanEJBAnnotation(bean.getClass().getName());
}
/**
* 解析 bean 的 EJB 注解并返回 bean 的 EJB 注解信息。
*
* @param bean 需要解析注解的对象。
* @return bean 的 EJB 注解信息。
* @throws Exception 发生任何异常时。
*/
public static BeanEJBAnnotation parseBeanEJBAnnotation(Object bean) throws Exception {
if (bean == null) {
return null;
}
Class<?> clazz = bean.getClass();
String className = clazz.getName();
BeanEJBAnnotation beanEJBAnnotation = BEAN_EJB_ANNOTATION_CACHE.get(className);
if (beanEJBAnnotation == null) {
synchronized (BEAN_EJB_ANNOTATION_CACHE) {
if ((beanEJBAnnotation = BEAN_EJB_ANNOTATION_CACHE.get(className)) == null) {
beanEJBAnnotation = new BeanEJBAnnotation();
// 类注解
boolean isKeep = false;
for (Annotation annotation : clazz.getAnnotations()) {
if (!isKeep && annotation instanceof Resource) {
beanEJBAnnotation.setClassInject(new ClassInjectAnnotationWrapper(annotation));
isKeep = true;
}
if (!isKeep && annotation instanceof EJB) {
beanEJBAnnotation.setClassInject(new ClassInjectAnnotationWrapper(annotation));
isKeep = true;
}
if (!isKeep && annotation.getClass().isAnnotationPresent(ResourceComponent.class)) {
beanEJBAnnotation.setClassInject(new ClassInjectAnnotationWrapper(annotation));
isKeep = true;
}
if (!isKeep && annotation.getClass().isAnnotationPresent(EJBComponent.class)) {
beanEJBAnnotation.setClassInject(new ClassInjectAnnotationWrapper(annotation));
isKeep = true;
}
if (annotation instanceof Interceptors) {
InterceptorsAnnotationWrapper interceptorsAnnotationWrapper = new InterceptorsAnnotationWrapper();
Interceptors interceptorsAnnotation = (Interceptors) annotation;
Class<?>[] interceptorClasses = interceptorsAnnotation.value();
InterceptorsAnnotationEntry[] interceptors = new InterceptorsAnnotationEntry[interceptorClasses.length];
for (int i = 0; i < interceptors.length; i++) {
interceptors[i] = new InterceptorsAnnotationEntry(ObjectUtils.createObject(interceptorClasses[i]),
findInterceptorMethod(interceptorClasses[i]));
}
interceptorsAnnotationWrapper.setInterceptors(interceptors);
if (clazz.isAnnotationPresent(ExcludeDefaultInterceptors.class)) {
// 排除默认拦截
interceptorsAnnotationWrapper.setExcludeDefaultInterceptors(true);
}
beanEJBAnnotation.setClassInterceptors(interceptorsAnnotationWrapper);
}
}
// 字段注解
Field[] fields = ObjectUtils.getAllField(clazz);
for (Field field : fields) {
for (Annotation annotation : field.getAnnotations()) {
if (annotation instanceof Resource) {
beanEJBAnnotation.addFieldInject(field.getName(), new FieldInjectAnnotationWrapper(field, annotation));
break;
}
if (annotation instanceof EJB) {
beanEJBAnnotation.addFieldInject(field.getName(), new FieldInjectAnnotationWrapper(field, annotation));
break;
}
if (annotation.getClass().isAnnotationPresent(ResourceComponent.class)) {
beanEJBAnnotation.addFieldInject(field.getName(), new FieldInjectAnnotationWrapper(field, annotation));
break;
}
if (annotation.getClass().isAnnotationPresent(EJBComponent.class)) {
beanEJBAnnotation.addFieldInject(field.getName(), new FieldInjectAnnotationWrapper(field, annotation));
break;
}
}
}
// 方法注解
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String methodName = method.getName();
isKeep = false;
for (Annotation annotation : method.getAnnotations()) {
if (!isKeep && annotation instanceof Resource) {
beanEJBAnnotation.addMethodInject(methodName, new MethodInjectAnnotationWrapper(method, annotation));
isKeep = true;
}
if (!isKeep && annotation instanceof EJB) {
beanEJBAnnotation.addMethodInject(methodName, new MethodInjectAnnotationWrapper(method, annotation));
isKeep = true;
}
if (!isKeep && annotation.getClass().isAnnotationPresent(ResourceComponent.class)) {
beanEJBAnnotation.addMethodInject(methodName, new MethodInjectAnnotationWrapper(method, annotation));
isKeep = true;
}
if (!isKeep && annotation.getClass().isAnnotationPresent(EJBComponent.class)) {
beanEJBAnnotation.addMethodInject(methodName, new MethodInjectAnnotationWrapper(method, annotation));
isKeep = true;
}
if (annotation instanceof Interceptors) {
InterceptorsAnnotationWrapper interceptorsAnnotationWrapper = new InterceptorsAnnotationWrapper();
Interceptors interceptorsAnnotation = (Interceptors) annotation;
Class<?>[] interceptorClasses = interceptorsAnnotation.value();
InterceptorsAnnotationEntry[] interceptors = new InterceptorsAnnotationEntry[interceptorClasses.length];
for (int i = 0; i < interceptors.length; i++) {
interceptors[i] = new InterceptorsAnnotationEntry(ObjectUtils.createObject(interceptorClasses[i]),
findInterceptorMethod(interceptorClasses[i]));
}
interceptorsAnnotationWrapper.setInterceptors(interceptors);
if (method.isAnnotationPresent(ExcludeDefaultInterceptors.class)) {
// 排除默认拦截
interceptorsAnnotationWrapper.setExcludeDefaultInterceptors(true);
}
if (method.isAnnotationPresent(ExcludeClassInterceptors.class)) {
// 排除类拦截
interceptorsAnnotationWrapper.setExcludeClassInterceptors(true);
}
beanEJBAnnotation.addMethodInterceptor(methodName, interceptorsAnnotationWrapper);
}
if (annotation instanceof PostConstruct) {
// 初始化回调
beanEJBAnnotation.setPostConstructAnnotationMethod(method);
}
if (annotation instanceof PreDestroy) {
// 清除回调
beanEJBAnnotation.setPreDestroyAnnotationMethod(method);
}
}
}
// 加入缓存
BEAN_EJB_ANNOTATION_CACHE.put(className, beanEJBAnnotation);
}
}
}
return beanEJBAnnotation;
}
/**
* 执行EJB注入注解( {@link EJB} 和 {@link Resource})。该方法不包含 {@link Interceptors} 和
* {@link PreDestroy} 及 {@link PostConstruct}
* 这样的生命周期方法的处理。要处理这些注解请使用工具的其他方法。
*
* @param bean 要执行 EJB 注入的 bean 实例。
* @return bean 对应的 BeanEJBAnnotation 信息。
* @throws Exception 发生任何异常。
*/
public static BeanEJBAnnotation executeEJBInjectAnnotation(Object bean) throws Exception {
if (bean == null) {
return null;
}
BeanEJBAnnotation beanEJBAnnotation = parseBeanEJBAnnotation(bean);
if (beanEJBAnnotation.isEmpty()) {
return beanEJBAnnotation;
}
// 字段注入
Map<String, FieldInjectAnnotationWrapper> fieldInjectAnnotationWrapper = beanEJBAnnotation.getFieldInjects();
for (FieldInjectAnnotationWrapper injectAnnotationWrapper : fieldInjectAnnotationWrapper.values()) {
fieldInject(bean, injectAnnotationWrapper);
}
// 方法注入
Map<String, MethodInjectAnnotationWrapper> methodInjectAnnotationWrapper = beanEJBAnnotation.getMethodInjects();
for (MethodInjectAnnotationWrapper injectAnnotationWrapper : methodInjectAnnotationWrapper.values()) {
methodInject(bean, injectAnnotationWrapper);
}
return beanEJBAnnotation;
}
/**
* 创建 {@link Interceptor} 代理。
*
* @param interceptor 需要代理的 Interceptor。
* @param beanEJBAnnotation interceptor 的 BeanEJBAnnotation。
* @return 如果 interceptor 为null 或者 beanEJBAnnotation 为 null 或者
* beanEJBAnnotation 为空,则直接返回 actionProxy ,否则返回创建的代理实例。
*/
public static Interceptor createInterceptorsAnnotationProxy(Interceptor interceptor, BeanEJBAnnotation beanEJBAnnotation) {
if (interceptor == null || beanEJBAnnotation == null || beanEJBAnnotation.isEmpty()) {
return interceptor;
}
return (Interceptor) Proxy.newProxyInstance(
ObjectUtils.getDefaultClassLoader(),
INTERCEPTOR_INTERFACES,
new InterceptorProxyHandler(interceptor, beanEJBAnnotation.getClassInterceptors(), beanEJBAnnotation
.getMethodInterceptors()));
}
/**
* 创建 {@link ActionProxy} 代理。
*
* @param actionProxy 需要代理的 ActionProxy。
* @param beanEJBAnnotation actionProxy 中的 action 的 BeanEJBAnnotation。
* @return 如果 actionProxy 为null 或者 beanEJBAnnotation 为 null 或者
* beanEJBAnnotation 为空,则直接返回 actionProxy ,否则返回创建的代理实例。
*/
public static ActionProxy createActionAnnotationProxy(ActionProxy actionProxy, BeanEJBAnnotation beanEJBAnnotation) {
if (actionProxy == null || beanEJBAnnotation == null || beanEJBAnnotation.isEmpty()) {
return actionProxy;
}
return (ActionProxy) Proxy.newProxyInstance(ObjectUtils.getDefaultClassLoader(), ACTION_PROXY_INTERFACES, new ActionProxyHandler(
actionProxy, beanEJBAnnotation.getClassInterceptors(), beanEJBAnnotation.getMethodInterceptors()));
}
/**
* 创建 拦截器回调方法的上下文信息。
*
* @param target 目标实例。
* @param method 调用 Interceptor 的 Bean 类的方法。
* @param args 包含传入代理实例上方法调用的参数值的对象数组。
* @param methodInterceptorsAnnotationWrapper 当前目标实例与方法的方法级别拦截器注解信息包装器。
* @param classInterceptorsAnnotationWrapper 当前目标实例与方法的类级别拦截器注解信息包装器。
* @return 对应信息的 拦截器回调方法的上下文信息。
* @throws NullPointerException 拦截器包装器中的参数为 null 时。
*/
public static InvocationContext createInvocationContext(Object target, Method method, Object[] args,
InterceptorsAnnotationWrapper methodInterceptorsAnnotationWrapper,
InterceptorsAnnotationWrapper classInterceptorsAnnotationWrapper) {
List<InterceptorsAnnotationEntry> interceptors = new ArrayList<InterceptorsAnnotationEntry>(
classInterceptorsAnnotationWrapper.getInterceptors().length);
if (methodInterceptorsAnnotationWrapper == null) {
if (classInterceptorsAnnotationWrapper != null) {
// 如果没有方法级别拦截器则直接添加类级别拦截器
Collections.addAll(interceptors, classInterceptorsAnnotationWrapper.getInterceptors());
}
} else {
if (classInterceptorsAnnotationWrapper != null && !methodInterceptorsAnnotationWrapper.isExcludeClassInterceptors()) {
// 没有排除类级别拦截器则添加上类级别拦截器
Collections.addAll(interceptors, classInterceptorsAnnotationWrapper.getInterceptors());
}
// 添加方法拦截器
Collections.addAll(interceptors, methodInterceptorsAnnotationWrapper.getInterceptors());
}
return new InvocationContextImpl(target, method, interceptors.iterator());
}
/**
* 字段注解注入。
*
* @param bean 需要注入的 bean 实例。
* @param fieldInjectAnnotationWrapper 字段级别注入注解包装器。
* @throws Exception 发生任何异常。
*/
public static void fieldInject(Object bean, FieldInjectAnnotationWrapper fieldInjectAnnotationWrapper) throws Exception {
if (bean == null) {
return;
}
Object resourceObject = null;
String jndiPath = fieldInjectAnnotationWrapper.getJndiPath();
Field field = fieldInjectAnnotationWrapper.getField();
if (jndiPath == null) {
Annotation annotation = fieldInjectAnnotationWrapper.getInjectAnnotation();
if ("true".equals(IS_PARSE_RESOURCE_ANNOTATION) && annotation instanceof Resource) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.fieldResourceAnnotationLookup(bean.getClass(),
fieldInjectAnnotationWrapper);
} else if ("true".equals(IS_PARSE_EJB_ANNOTATION) && annotation instanceof EJB) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.fieldEJBAnnotationLookup(bean.getClass(),
fieldInjectAnnotationWrapper);
} else if (annotation.getClass().isAnnotationPresent(ResourceComponent.class)) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.fieldResourceComponentAnnotationLookup(bean.getClass(),
fieldInjectAnnotationWrapper);
} else if (annotation.getClass().isAnnotationPresent(EJBComponent.class)) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.fieldEJBComponentAnnotationLookup(bean.getClass(),
fieldInjectAnnotationWrapper);
}
} else {
resourceObject = lookup(jndiPath);
}
// 注入
fieldInject(bean, field, resourceObject);
}
/**
* 查找 clazz 上是 Local 或者 Remote 注解,并返回配置中 Local/Remote 对应的字符串。
*
* @param clazz 要查找的 Class。
* @return 如果找到 Local/Remote注解 ,则返回配置中 Local/Remote 对应的字符串,如果没有,则返回 null。
*/
public static String findLocalOrRemote(Class<?> clazz) {
// 找寻类注解上的本地/远程注解
if (clazz.isAnnotationPresent(Local.class)) {
// 本地接口
return LOCAL;
} else if (clazz.isAnnotationPresent(Remote.class)) {
// 远程接口
return REMOTE;
} else {
// 都不是
return null;
}
}
private static void fieldInject(Object object, final Field field, Object resourceObject) throws Exception {
// 注入
ObjectUtils.fieldSetAccessible(field);
field.set(object, resourceObject);
}
/**
* 方法注解注入。
*
* @param bean 需要注入的 bean 实例。
* @param methodInjectAnnotationWrapper 方法级别注入注解包装器。
* @throws Exception 发生任何异常。
*/
public static void methodInject(Object bean, MethodInjectAnnotationWrapper methodInjectAnnotationWrapper) throws Exception {
if (bean == null) {
return;
}
Object resourceObject = null;
String jndiPath = methodInjectAnnotationWrapper.getJndiPath();
Method method = methodInjectAnnotationWrapper.getMethod();
if (jndiPath == null) {
Annotation annotation = methodInjectAnnotationWrapper.getInjectAnnotation();
if ("true".equals(IS_PARSE_RESOURCE_ANNOTATION) && annotation instanceof Resource) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.methodResourceAnnotationLookup(bean.getClass(),
methodInjectAnnotationWrapper);
} else if ("true".equals(IS_PARSE_EJB_ANNOTATION) && annotation instanceof EJB) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.methodEJBAnnotationLookup(bean.getClass(),
methodInjectAnnotationWrapper);
} else if (annotation.getClass().isAnnotationPresent(ResourceComponent.class)) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.methodResourceComponentAnnotationLookup(bean.getClass(),
methodInjectAnnotationWrapper);
} else if (annotation.getClass().isAnnotationPresent(EJBComponent.class)) {
resourceObject = StrutsEJBPluginConstant.EJB_CONTAINER.methodEJBComponentAnnotationLookup(bean.getClass(),
methodInjectAnnotationWrapper);
}
} else {
resourceObject = lookup(jndiPath);
}
// 注入
methodInject(bean, method, resourceObject);
}
private static void methodInject(Object object, final Method method, Object resourceObject) throws Exception {
// 注入
ObjectUtils.methodSetAccessible(method);
method.invoke(object, resourceObject);
}
/**
* 查找资源。
*
* @param name 资源的jndi名。
* @return name 对应的资源。
* @throws NamingException 如果遇到命名异常。
*/
public static Object lookup(String name) throws NamingException {
Context context = new InitialContext();
try {
return context.lookup(name);
} finally {
if (context != null) {
context.close();
}
}
}
/**
* 从ENC上下文开始查找。
*
* @param name 相对于 ENC 的资源 jndi 名。
* @return 相对于 ENC 的name 对应的资源。
* @throws NamingException 如果遇到命名异常。
*/
public static Object lookupENC(String name) throws NamingException {
return lookup(ENC_PATH + name);
}
/**
* 从一个 Class 中找标注有 {@link AroundInvoke} 的方法。
*
* @param interceptorClass 要寻找的 Class 实例。
* @return 返回 {@link AroundInvoke} 标注的方法,如过有多个则返回查找到的第一个,如果 interceptorClass
* 为 null 则返回 null。
* @throws NoSuchMethodException 如果没有找到拦截方法。
*/
public static Method findInterceptorMethod(Class<?> interceptorClass) throws NoSuchMethodException {
if (interceptorClass == null) {
return null;
}
Method[] methods = interceptorClass.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(AroundInvoke.class)) {
return method;
}
}
throw new NoSuchMethodException("拦截器 [" + interceptorClass.getName() + "] 没有找到拦截方法。");
}
/**
* 模拟Bean生命周期。
*
* @param bean 需要模拟生命周期的 bean 实例。
* @param beanEJBAnnotation bean 对应的 BeanEJBAnnotation。
* @throws Exception 发生任何异常时。
*/
public static void simulation(Object bean, BeanEJBAnnotation beanEJBAnnotation) throws Exception {
// 初始化回调
Method postConstructMethod = beanEJBAnnotation.getPostConstructAnnotationMethod();
Map<String, InterceptorsAnnotationWrapper> methodInterceptors = beanEJBAnnotation.getMethodInterceptors();
if (postConstructMethod != null) {
if (methodInterceptors.containsKey(postConstructMethod.getName())) {
createInvocationContext(bean, postConstructMethod, null,
beanEJBAnnotation.getMethodInterceptors().get(postConstructMethod.getName()),
beanEJBAnnotation.getClassInterceptors()).proceed();
} else {
postConstructMethod.invoke(bean);
}
}
// TODO 目前不支持 PreDestroy
}
/**
* 得到 action 所有的公共方法名的 {@link Set} 。
*
* @param clazz action 的 {@link Class} 对象。
* @return 指定 clazz 的 action 的所有的公共方法名的 {@link Set} 。
*/
public static Set<String> getActionPublicMethodNames(Class<?> clazz) {
Set<String> methodSet = ACTION_PUBLIC_METHODS_CACHE.get(clazz);
if (methodSet == null) {
synchronized (ACTION_PUBLIC_METHODS_CACHE) {
if ((methodSet = ACTION_PUBLIC_METHODS_CACHE.get(clazz)) == null) {
Method[] methods = clazz.getMethods();
methodSet = new HashSet<String>();
for (Method method : methods) {
methodSet.add(method.getName());
}
ACTION_PUBLIC_METHODS_CACHE.put(clazz, methodSet);
}
}
}
return methodSet;
}
}