All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.samaxes.stripes.ejb3.EJBInterceptor Maven / Gradle / Ivy

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; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy