com.samaxes.stripes.ejb3.EJBInterceptor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of stripejb3 Show documentation
Show all versions of stripejb3 Show documentation
Injects EJB beans into Stripes ActionBeans after it has been instantiated.
The newest version!
/*
* $Id: EJBInterceptor.java 65 2010-06-28 22:17:36Z samaxes $
*
* Copyright 2008 samaxes.com
*
* 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.
*/
package com.samaxes.stripes.ejb3;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.controller.ExecutionContext;
import net.sourceforge.stripes.controller.Interceptor;
import net.sourceforge.stripes.controller.Intercepts;
import net.sourceforge.stripes.controller.LifecycleStage;
import net.sourceforge.stripes.exception.StripesRuntimeException;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.util.ReflectUtil;
/**
*
* An {@code Interceptor} that uses a initial context to inject EJB beans into newly created action beans immediateley
* following ActionBeanResolution.
*
*
*
* To configure {@code EJBInterceptor}, add the following initialization parameters to your Stripes filter configuration
* in web.xml
:
*
*
*
* Stripes 1.5.x:
*
*
* <init-param>
* <param-name>Interceptor.Classes</param-name>
* <param-value>
* com.samaxes.stripes.ejb3.EJBInterceptor
* </param-value>
* </init-param>
*
*
*
*
*
* Stripes 1.4.x:
*
*
* <init-param>
* <param-name>Interceptor.Classes</param-name>
* <param-value>
* com.samaxes.stripes.ejb3.EJBInterceptor,
* net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
* </param-value>
* </init-param>
*
*
*
*
*
* If one or more interceptors are already configured in your web.xml
simply separate the fully qualified
* names of the interceptors with commas.
*
*
* @see EJBBean
* @author Samuel Santos
* @version $Revision: 26 $
*/
@Intercepts(LifecycleStage.ActionBeanResolution)
public class EJBInterceptor implements Interceptor {
private static final Log log = Log.getInstance(EJBInterceptor.class);
private static InitialContext ctx = null;
/** Lazily filled in map of Class to methods annotated with EJBBean. */
private static Map, Collection> methodMap = new ConcurrentHashMap, Collection>();
/** Lazily filled in map of Class to fields annotated with EJBBean. */
private static Map, Collection> fieldMap = new ConcurrentHashMap, Collection>();
/**
* Allows ActionBean resolution to proceed and then once the ActionBean has been located performs the EJB injection.
*
* @param ctx the current execution context
* @return the Resolution produced by calling context.proceed()
* @throws Exception if the EJB binding process produced unrecoverable errors
*/
public Resolution intercept(ExecutionContext ctx) throws Exception {
Resolution resolution = ctx.proceed();
log.debug("Running EJB dependency injection for instance of ", ctx.getActionBean().getClass().getSimpleName());
ActionBean bean = ctx.getActionBean();
// First inject any values using annotated methods
for (Method method : getMethods(bean.getClass())) {
try {
EJBBean ejbBean = method.getAnnotation(EJBBean.class);
boolean nameSupplied = !"".equals(ejbBean.value());
String name = nameSupplied ? ejbBean.value() : methodToPropertyName(method);
Object managedBean = findEJB(name);
method.invoke(bean, managedBean);
} catch (Exception e) {
throw new StripesRuntimeException("Exception while trying to lookup and inject "
+ "an EJB bean into a bean of type " + bean.getClass().getSimpleName() + " using method "
+ method.toString(), e);
}
}
// And then inject any properties that are annotated
for (Field field : getFields(bean.getClass())) {
try {
EJBBean ejbBean = field.getAnnotation(EJBBean.class);
boolean nameSupplied = !"".equals(ejbBean.value());
String name = nameSupplied ? ejbBean.value() : field.getName();
Object managedBean = findEJB(name);
field.set(bean, managedBean);
} catch (Exception e) {
throw new StripesRuntimeException("Exception while trying to lookup and inject "
+ "a EJB bean into a bean of type " + bean.getClass().getSimpleName()
+ " using field access on field " + field.toString(), e);
}
}
return resolution;
}
/**
* Fetches the methods on a class that are annotated with EJBBean. The first time it is called for a particular
* class it will introspect the class and cache the results. All non-overridden methods are examined, including
* protected and private methods. If a method is not public an attempt it made to make it accessible - if it fails
* it is removed from the collection and an error is logged.
*
* @param clazz the class on which to look for EJBBean annotated methods
* @return the collection of methods with the annotation
*/
protected static Collection getMethods(Class> clazz) {
Collection methods = methodMap.get(clazz);
if (methods == null) {
methods = ReflectUtil.getMethods(clazz);
Iterator iterator = methods.iterator();
while (iterator.hasNext()) {
Method method = iterator.next();
if (!method.isAnnotationPresent(EJBBean.class)) {
iterator.remove();
} else {
// If the method isn't public, try to make it accessible
if (!method.isAccessible()) {
try {
method.setAccessible(true);
} catch (SecurityException se) {
throw new StripesRuntimeException("Method " + clazz.getName() + "." + method.getName()
+ "is marked " + "with @EJBBean and is not public. An attempt to call "
+ "setAccessible(true) resulted in a SecurityException. Please "
+ "either make the method public or modify your JVM security "
+ "policy to allow Stripes to setAccessible(true).", se);
}
}
// Ensure the method has only the one parameter
if (method.getParameterTypes().length != 1) {
throw new StripesRuntimeException(
"A method marked with @EJBBean must have exactly one parameter: "
+ "the bean to be injected. Method [" + method.toGenericString() + "] has "
+ method.getParameterTypes().length + " parameters.");
}
}
}
methodMap.put(clazz, methods);
}
return methods;
}
/**
* Fetches the fields on a class that are annotated with EJBBean. The first time it is called for a particular class
* it will introspect the class and cache the results. All non-overridden fields are examined, including protected
* and private fields. If a field is not public an attempt it made to make it accessible - if it fails it is removed
* from the collection and an error is logged.
*
* @param clazz the class on which to look for EJBBean annotated fields
* @return the collection of methods with the annotation
*/
protected static Collection getFields(Class> clazz) {
Collection fields = fieldMap.get(clazz);
if (fields == null) {
fields = ReflectUtil.getFields(clazz);
Iterator iterator = fields.iterator();
while (iterator.hasNext()) {
Field field = iterator.next();
if (!field.isAnnotationPresent(EJBBean.class)) {
iterator.remove();
} else if (!field.isAccessible()) {
// If the field isn't public, try to make it accessible
try {
field.setAccessible(true);
} catch (SecurityException se) {
throw new StripesRuntimeException("Field " + clazz.getName() + "." + field.getName()
+ "is marked " + "with @EJBBean and is not public. An attempt to call "
+ "setAccessible(true) resulted in a SecurityException. Please "
+ "either make the field public, annotate a public setter instead "
+ "or modify your JVM security policy to allow Stripes to " + "setAccessible(true).",
se);
}
}
}
fieldMap.put(clazz, fields);
}
return fields;
}
/**
* Looks up an EJB managed bean from an Initial Context.
*
* @param name the name of the EJB bean to look for
* @exception StripesRuntimeException StripesRuntimeException is thrown if it is not possible to find a matching
* bean in the initial context.
*/
protected static Object findEJB(String name) {
// Try to lookup using the name provided
try {
if (ctx == null) {
ctx = new InitialContext();
}
Object ejb = ctx.lookup(name);
log.debug("Found EJB bean with name [", name, "]");
return ejb;
} catch (NamingException e) {
throw new StripesRuntimeException("Unable to find an EJBBean with name [" + name
+ "] in the initial context.");
}
}
/**
* A slightly unusual, and somewhat "loose" conversion of a method name to a property name. Assumes that the name is
* in fact a mutator for a property and will do the usual {@code setFoo} to {@code foo} conversion if the method
* follows the normal syntax, otherwise will just return the method name.
*
* @param m the method to determine the property name of
* @return a String property name
*/
protected static String methodToPropertyName(Method m) {
String name = m.getName();
if (name.startsWith("set") && name.length() > 3) {
String ret = name.substring(3, 4).toLowerCase();
if (name.length() > 4) {
ret += name.substring(4);
}
return ret;
} else {
return name;
}
}
}