Revision: 315
Author:   tfenne
Date:     2006-05-13 07:14:09 -0700 (Sat, 13 May 2006)
ViewCVS:  http://svn.sourceforge.net/stripes/?rev=315&view=rev

Log Message:
-----------
Major refactoring of the SpringHelper injection functionality, and the addition 
of tests for it.

Modified Paths:
--------------
    trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringBean.java
    
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringHelper.java
    
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringInterceptor.java
    trunk/stripes/src/net/sourceforge/stripes/util/ReflectUtil.java
    trunk/stripes/stripes.iml
    trunk/tests/build.xml
    trunk/tests/tests.iml

Added Paths:
-----------
    trunk/stripes/lib/test/spring.jar
    trunk/tests/lib/testng-4.6-jdk15.jar
    trunk/tests/src/net/sourceforge/stripes/integration/
    trunk/tests/src/net/sourceforge/stripes/integration/spring/
    
trunk/tests/src/net/sourceforge/stripes/integration/spring/SpringHelperTests.java

Removed Paths:
-------------
    trunk/stripes/lib/build/spring.jar
    trunk/tests/lib/testng-4.4.7-jdk15.jar
Deleted: trunk/stripes/lib/build/spring.jar
===================================================================
(Binary files differ)

Copied: trunk/stripes/lib/test/spring.jar (from rev 294, 
trunk/stripes/lib/build/spring.jar)
===================================================================
(Binary files differ)

Modified: 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringBean.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringBean.java    
    2006-05-13 02:11:11 UTC (rev 314)
+++ 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringBean.java    
    2006-05-13 14:14:09 UTC (rev 315)
@@ -21,15 +21,24 @@
 import java.lang.annotation.Documented;
 
 /**
- * <p>Annotation used for injecting Spring managed beans into an ActionBean. 
The value
- * represents the name of the bean in the Spring application context to 
inject. Please
- * see the SpringDispatcherServlet for strategies used in the event the bean 
was not
- * found in the application context.</p>
+ * <p>Annotation used for injecting Spring managed beans into objects within 
Stripes
+ * (usually ActionBeans).  The value of the annotation  represents the name of 
the bean
+ * in the Spring application context to inject. If the value is omitted then 
Stripes
+ * will attempt to auto-wire first by property/field name and then by type.</p>
  *
+ * <p>Both methods and fields can be annotated.  If a field is annotated 
Stripes will use
+ * field access to attempt to inject the bean into the field.  If a method is 
annotated Stripes
+ * will attempt to invoke the method and supply it the value to inject.  In 
both cases
+ * non-public fields/methods are supported (i.e. values can be injected into 
private fields
+ * and through private methods).</p>
+ *
+ * <p>For a more details description of the injection process and how 
auto-wiring occurs
+ * when explicit bean names are omitted see the [EMAIL PROTECTED] 
SpringHelper} class.</p>
+ *
  * @author Dan Hayes
  */
 @Retention(RetentionPolicy.RUNTIME)
[EMAIL PROTECTED](ElementType.METHOD)
[EMAIL PROTECTED]({ElementType.METHOD, ElementType.FIELD})
 @Documented
 public @interface SpringBean {
     String value() default "";

Modified: 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringHelper.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringHelper.java  
    2006-05-13 02:11:11 UTC (rev 314)
+++ 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringHelper.java  
    2006-05-13 14:14:09 UTC (rev 315)
@@ -15,102 +15,292 @@
 package net.sourceforge.stripes.integration.spring;
 
 import net.sourceforge.stripes.action.ActionBeanContext;
-import net.sourceforge.stripes.exception.StripesServletException;
+import net.sourceforge.stripes.exception.StripesRuntimeException;
 import net.sourceforge.stripes.util.Log;
-import org.springframework.beans.BeanUtils;
+import net.sourceforge.stripes.util.ReflectUtil;
 import org.springframework.context.ApplicationContext;
+import org.springframework.core.NestedRuntimeException;
 import org.springframework.web.context.support.WebApplicationContextUtils;
 
-import javax.servlet.http.HttpServletRequest;
-import java.beans.PropertyDescriptor;
+import javax.servlet.ServletContext;
+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;
 
 /**
  * <p>Static helper class that is used to lookup Spring beans and inject them 
into objects
- * (usually ActionBeans). Setter methods must be annotated using the [EMAIL 
PROTECTED] @SpringBean annotation}.
- * The value of the annotation should be the bean name in the Spring 
application context.  If value
- * is left blank, an attempt is made to auto-wire the bean; first by method 
name then by type. If
- * the value is left blank and more than one bean of the same type is found, 
an exception will be
- * raised.</p>
+ * (often ActionBeans). Is capable of injecting beans through setter methods 
(property access)
+ * and also through direct field access if the security policy allows it. 
Methods and fields
+ * must be annotated using the [EMAIL PROTECTED] @SpringBean} annotation.</p>
  *
+ * <p>Methods and fields may be public, protected, package-access or private. 
If they are not
+ * public an attempt is made to call [EMAIL PROTECTED] 
Method#setAccessible(boolean)} in order to make
+ * them accessible from this class.  If the attempt fails, an exception will 
be thrown.</p>
+ *
+ * <p>Method names can take any form.  For example [EMAIL PROTECTED] 
setSomeBean(Bean b)} or
+ * [EMAIL PROTECTED] someBean(bean b)}. In both cases, if a specific 
SpringBean name is not supplied,
+ * the default name of [EMAIL PROTECTED] someBean} will be used.</p>
+ *
+ * <p>The value of the [EMAIL PROTECTED] @SpringBean} annotation should be the 
bean name in the Spring
+ * application context if it is different from the field/property name.  If 
the value
+ * is left blank, an attempt is made to auto-wire the bean; first by 
field/property name and
+ * then by type. If the value is left blank and more than one bean of the same 
type is found,
+ * an exception will be raised.</p>
+ *
+ * <p>The first time that any of the injection methods in this class is called 
with a specific type
+ * of object, the object's class is examined for annotated fields and methods. 
The discovered
+ * fields and methods are then cached for future usage.</p>
+ *
  * @see SpringBean
  * @author Dan Hayes, Tim Fennell
  */
 public class SpringHelper {
     private static Log log = Log.getInstance(SpringHelper.class);
 
+    /** Lazily filled in map of Class to methods annotated with SpringBean. */
+    private static Map<Class<?>, Collection<Method>> methodMap =
+            new ConcurrentHashMap<Class<?>, Collection<Method>>();
+
+    /** Lazily filled in map of Class to fields annotated with SpringBean. */
+    private static Map<Class<?>, Collection<Field>> fieldMap =
+            new ConcurrentHashMap<Class<?>, Collection<Field>>();
+
     /**
-     * Injects Spring managed beans into ActionBeans via the SpringBean 
annotation.
-     * It first looks for the value attribute of the annotation for the name 
of the
-     * Spring managed bean to inject.  If this is empty, it will attempt to 
"auto-wire"
-     * the bean from the method name (i.e. "setSomeBean()" will resolve to 
"someBean").
-     * Finally, it will try to look for a bean of the type expected by the 
method. In
-     * the event that more than one Spring managed bean meets this criteria, 
an exception
-     * will be logged and thrown.
+     * Injects Spring managed beans into using a Web Application Context that 
is
+     * derived from the ServletContext, which is in turn looked up using the
+     * ActionBeanContext.
      *
      * @param bean    the object into which to inject spring managed bean
      * @param context the ActionBeanContext represented by the current request
-     * @throws Exception
      */
-    public static void injectBeans(Object bean, ActionBeanContext context) 
throws Exception {
-        HttpServletRequest request = context.getRequest();
-        ApplicationContext springContext = 
WebApplicationContextUtils.getWebApplicationContext(
-                request.getSession().getServletContext());
+    public static void injectBeans(Object bean, ActionBeanContext context) {
+        injectBeans(bean, 
context.getRequest().getSession().getServletContext());
+    }
 
-        for (Method m : bean.getClass().getMethods()) {
+    /**
+     * Injects Spring managed beans using a Web Application Context derived 
from
+     * the ServletContext.
+     *
+     * @param bean the object to have beans injected into
+     * @param ctx the ServletContext to use to find the Spring 
ApplicationContext
+     */
+    public static void injectBeans(Object bean, ServletContext ctx) {
+        ApplicationContext ac = 
WebApplicationContextUtils.getWebApplicationContext(ctx);
+        injectBeans(bean, ac);
+    }
 
-            if (m.isAnnotationPresent(SpringBean.class)) {
+    /**
+     * Looks for all methods and fields annotated with [EMAIL PROTECTED] 
@SpringBean} and attempts
+     * to lookup and inject a managed bean into the field/property. If any 
annotated
+     * element cannot be injected an exception is thrown.
+     *
+     * @param bean the bean into which to inject spring beans
+     * @param ctx the Spring application context
+     */
+    public static void injectBeans(Object bean, ApplicationContext ctx) {
+        // First inject any values using annotated methods
+        for (Method m : getMethods(bean.getClass())) {
+            try {
+                SpringBean springBean = m.getAnnotation(SpringBean.class);
+                boolean nameSupplied = !"".equals(springBean.value());
+                String name = nameSupplied ? springBean.value() : 
methodToPropertyName(m);
+                Class<?> beanType = m.getParameterTypes()[0];
+                Object managedBean = findSpringBean(ctx, name, beanType, 
!nameSupplied);
+                m.invoke(bean, managedBean);
+            }
+            catch (Exception e) {
+                throw new StripesRuntimeException("Exception while trying to 
lookup and inject " +
+                    "a Spring bean into a bean of type " + 
bean.getClass().getSimpleName() +
+                    " using method " + m.toString(), e);
+            }
+        }
 
-                String beanName = m.getAnnotation(SpringBean.class).value();
-                if (beanName != null && !beanName.equals("")) {
-                    // use the value to lookup the bean
-                    m.invoke(bean, springContext.getBean(beanName));
-                    log.debug("Injected ActionBean [",  
bean.getClass().getName(), "] property [",
-                              m.getName(), "] with Spring bean [", beanName, 
"].");
+        // And then inject any properties that are annotated
+        for (Field f : getFields(bean.getClass())) {
+            try {
+                SpringBean springBean = f.getAnnotation(SpringBean.class);
+                boolean nameSupplied = !"".equals(springBean.value());
+                String name = nameSupplied ? springBean.value() : f.getName();
+                Object managedBean = findSpringBean(ctx, name, f.getType(), 
!nameSupplied);
+                f.set(bean, managedBean);
+            }
+            catch (Exception e) {
+                throw new StripesRuntimeException("Exception while trying to 
lookup and inject " +
+                    "a Spring bean into a bean of type " + 
bean.getClass().getSimpleName() +
+                    " using field access on field " + f.toString(), e);
+            }
+        }
+    }
+
+    /**
+     * Fetches the methods on a class that are annotated with SpringBean. 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 SpringBean annotated methods
+     * @return the collection of methods with the annotation
+     */
+    protected static Collection<Method> getMethods(Class<?> clazz) {
+        Collection<Method> methods = methodMap.get(clazz);
+        if (methods == null) {
+            methods = ReflectUtil.getMethods(clazz);
+            Iterator<Method> iterator = methods.iterator();
+
+            while (iterator.hasNext()) {
+                Method method = iterator.next();
+                if (!method.isAnnotationPresent(SpringBean.class)) {
+                    iterator.remove();
                 }
                 else {
-                    // else, try to auto-wire by property name
-                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(m);
-                    if ( springContext.containsBean(pd.getName()) ) {
-                        m.invoke( bean, springContext.getBean(pd.getName()) );
-                        log.debug("Injected ActionBean [", 
bean.getClass().getName(),
-                                  "] property [",  pd.getName(), "] with 
Spring bean [",
-                                  pd.getName(), "]");
-                    }
-                    else {
-                        // or try to find by type
-                        String[] beanNames = 
springContext.getBeanNamesForType(pd.getPropertyType());
-                        if (beanNames == null || beanNames.length == 0) {
-                            // didn't find any beans of that type
-                            StripesServletException sse = new 
StripesServletException(
-                                "Unable to inject ActionBean [" + 
bean.getClass().getName() +
-                                "] property [" + pd.getName() + "]. No 
matching Spring beans " +
-                                "could be found with the name [" + 
pd.getName() + "] or type [" +
-                                pd.getPropertyType().getName() + "].");
-                            log.error(sse);
-                            throw sse;
+                    // If the method isn't public, try to make it accessible
+                    if (!method.isAccessible()) {
+                        try {
+                            method.setAccessible(true);
                         }
-                        else if (beanNames.length > 1) {
-                            // more than one bean found of this type
-                            StripesServletException sse = new 
StripesServletException(
-                                    "Unable to inject ActionBean [" + 
bean.getClass().getName() +
-                                    "] property [" + pd.getName() + "]. No 
matching Spring beans " +
-                                    "could be found with the name [" + 
pd.getName() + "]. " +
-                                    beanNames.length + " found with  type [" +
-                                    pd.getPropertyType().getName() + "].");
-                            log.error(sse);
-                            throw sse;
+                        catch (SecurityException se) {
+                            throw new StripesRuntimeException(
+                                "Method " + clazz.getName() + "." + 
method.getName() + "is marked " +
+                                "with @SpringBean 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);
                         }
-                        else {
-                            m.invoke(bean, 
springContext.getBean(beanNames[0]));
-                            log.warn("Injecting ActionBean [", 
bean.getClass().getName(),
-                                    "] property [", pd.getName(),"] with 
Spring bean name [",
-                                    beanNames[0], "] based upon type match. 
Matching on type is ",
-                                    "a little risky so watch out!");
-                        }
                     }
+
+                    // Ensure the method has only the one parameter
+                    if (method.getParameterTypes().length != 1) {
+                        throw new StripesRuntimeException(
+                            "A method marked with @SpringBean 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 SpringBean. 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 SpringBean annotated fields
+     * @return the collection of methods with the annotation
+     */
+    protected static Collection<Field> getFields(Class<?> clazz) {
+        Collection<Field> fields = fieldMap.get(clazz);
+        if (fields == null) {
+            fields = ReflectUtil.getFields(clazz);
+            Iterator<Field> iterator = fields.iterator();
+
+            while (iterator.hasNext()) {
+                Field field = iterator.next();
+                if (!field.isAnnotationPresent(SpringBean.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 @SpringBean 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 a Spring managed bean from an Application Context. First looks 
for a bean
+     * with name specified. If no such bean exists, looks for a bean by type. 
If there is
+     * only one bean of the appropriate type, it is returned. If zero or more 
than one bean
+     * of the correct type exists, an exception is thrown.
+     *
+     * @param ctx the Spring Application Context
+     * @param name the name of the spring bean to look for
+     * @param type the type of bean to look for
+     * @param allowFindByType true to indicate that finding a bean by type is 
acceptable
+     *        if find by name fails.
+     * @exception RuntimeException various subclasses of RuntimeException are 
thrown if it
+     *            is not possible to find a unique matching bean in the spring 
context given
+     *            the constraints supplied.
+     */
+    protected static Object findSpringBean(ApplicationContext ctx,
+                                           String name,
+                                           Class<?> type,
+                                           boolean allowFindByType) {
+        // First try to lookup using the name provided
+        try {
+            Object bean =  ctx.getBean(name, type);
+            log.debug("Found spring bean with name [", name, "] and type [",
+                      bean.getClass().getName(), "]");
+            return bean;
+        }
+        catch (NestedRuntimeException nre) {
+            if (!allowFindByType) throw nre;
+        }
+
+        // If we got here then we didn't find a bean yet, try by type
+        String[] beanNames = ctx.getBeanNamesForType(type);
+        if (beanNames.length == 0) {
+            throw new StripesRuntimeException(
+                "Unable to find SpringBean with name [" + name + "] or type [" 
+
+                type.getName() + "] in the Spring application context.");
+        }
+        else if (beanNames.length > 1) {
+            throw new StripesRuntimeException(
+                "Unable to find SpringBean with name [" + name + "] or unique 
bean with type [" +
+                type.getName() + "] in the Spring application context. Found " 
+ beanNames.length +
+                "beans of matching type.");
+        }
+        else {
+            log.warn("Found unique SpringBean with type [" + type.getName() + 
"]. Matching on ",
+                     "type is a little risky so watch out!");
+            return ctx.getBean(beanNames[0], type);
+        }
+    }
+
+    /**
+     * 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 [EMAIL PROTECTED] setFoo} to [EMAIL PROTECTED] 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;
+        }
+    }
 }

Modified: 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringInterceptor.java
===================================================================
--- 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringInterceptor.java
 2006-05-13 02:11:11 UTC (rev 314)
+++ 
trunk/stripes/src/net/sourceforge/stripes/integration/spring/SpringInterceptor.java
 2006-05-13 14:14:09 UTC (rev 315)
@@ -24,10 +24,9 @@
 
 /**
  * <p>An [EMAIL PROTECTED] Interceptor} that uses a Spring context to inject 
Spring beans into newly created
- * ActionBeans immediatley following ActionBeanResolution.  For more 
information on how the injection
+ * ActionBeans immediateley following ActionBeanResolution.  For more 
information on how the injection
  * is performed see [EMAIL PROTECTED] SpringHelper#injectBeans(Object,
- *  net.sourceforge.stripes.action.ActionBeanContext)}</p>
- * them back for processing.</p>
+ *  net.sourceforge.stripes.action.ActionBeanContext)}.</p>
  *
  * <p>To configure the SpringInterceptor for use you will need to add the 
following to your
  * web.xml (assuming no other interceptors are yet configured):</p>
@@ -35,7 +34,10 @@
  * <pre>
  * &lt;init-param&gt;
  *     &lt;param-name&gt;Interceptor.Classes&lt;/param-name&gt;
- *     
&lt;param-value&gt;net.sourceforge.stripes.integration.spring.SpringInterceptor&lt;/param-value&gt;
+ *     &lt;param-value&gt;
+ *         net.sourceforge.stripes.integration.spring.SpringInterceptor,
+ *         net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
+ *     &lt;/param-value&gt;
  * &lt;/init-param&gt;
  * </pre>
  *

Modified: trunk/stripes/src/net/sourceforge/stripes/util/ReflectUtil.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/ReflectUtil.java     
2006-05-13 02:11:11 UTC (rev 314)
+++ trunk/stripes/src/net/sourceforge/stripes/util/ReflectUtil.java     
2006-05-13 14:14:09 UTC (rev 315)
@@ -30,6 +30,7 @@
 import java.util.Arrays;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
+import java.lang.reflect.Field;
 
 /**
  * Common utilty methods that are useful when working with reflection.
@@ -224,4 +225,27 @@
 
         return found;
     }
+
+    /**
+     * Fetches all fields of all access types from the supplied class and super
+     * classes. Fieldss that have been overridden in the inheritance hierachy 
are
+     * only returned once, using the instance lowest down the hierarchy.
+     *
+     * @param clazz the class to inspect
+     * @return a collection of fields
+     */
+    public static Collection<Field> getFields(Class<?> clazz) {
+        Map<String,Field> fields = new HashMap<String, Field>();
+        while (clazz != null) {
+            for (Field field : clazz.getDeclaredFields()) {
+                if ( !fields.containsKey(field.getName()) ) {
+                    fields.put(field.getName(), field);
+                }
+            }
+
+            clazz = clazz.getSuperclass();
+        }
+
+        return fields.values();
+    }
 }

Modified: trunk/stripes/stripes.iml
===================================================================
--- trunk/stripes/stripes.iml   2006-05-13 02:11:11 UTC (rev 314)
+++ trunk/stripes/stripes.iml   2006-05-13 14:14:09 UTC (rev 315)
@@ -5,6 +5,7 @@
     <output url="file://$MODULE_DIR$/classes" />
     <exclude-output />
     <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
     </content>
     <orderEntry type="inheritedJdk" />
@@ -15,7 +16,9 @@
           <root url="jar://$MODULE_DIR$/lib/deploy/ognl-2.6.7.jar!/" />
         </CLASSES>
         <JAVADOC />
-        <SOURCES />
+        <SOURCES>
+          <root url="file:///Downloads/ognl-2.6.7-dist Folder/java" />
+        </SOURCES>
       </library>
     </orderEntry>
     <orderEntry type="module-library" exported="">
@@ -67,24 +70,33 @@
         <SOURCES />
       </library>
     </orderEntry>
-    <orderEntry type="module-library">
+    <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/lib/build/spring.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/build/mail.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
       </library>
     </orderEntry>
-    <orderEntry type="module-library">
+    <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/lib/build/mail.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/build/freemarker.jar!/" />
         </CLASSES>
         <JAVADOC />
         <SOURCES />
       </library>
     </orderEntry>
+    <orderEntry type="module-library" exported="">
+      <library>
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/lib/test/spring.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES />
+      </library>
+    </orderEntry>
     <orderEntryProperties />
     <javadoc-paths>
       <root url="file://$MODULE_DIR$/docs" />

Modified: trunk/tests/build.xml
===================================================================
--- trunk/tests/build.xml       2006-05-13 02:11:11 UTC (rev 314)
+++ trunk/tests/build.xml       2006-05-13 14:14:09 UTC (rev 315)
@@ -101,7 +101,7 @@
 
         <testng classpathref="class.path" outputdir="${test.out.dir}">
             <classfileset dir="${classes.dir}">
-                <include name="**/*Test.class"/>
+                <include name="**/*.class"/>
             </classfileset>
         </testng>
     </target>

Deleted: trunk/tests/lib/testng-4.4.7-jdk15.jar
===================================================================
(Binary files differ)

Added: trunk/tests/lib/testng-4.6-jdk15.jar
===================================================================
(Binary files differ)


Property changes on: trunk/tests/lib/testng-4.6-jdk15.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: 
trunk/tests/src/net/sourceforge/stripes/integration/spring/SpringHelperTests.java
===================================================================
--- 
trunk/tests/src/net/sourceforge/stripes/integration/spring/SpringHelperTests.java
                           (rev 0)
+++ 
trunk/tests/src/net/sourceforge/stripes/integration/spring/SpringHelperTests.java
   2006-05-13 14:14:09 UTC (rev 315)
@@ -0,0 +1,262 @@
+package net.sourceforge.stripes.integration.spring;
+
+import net.sourceforge.stripes.test.TestBean;
+import net.sourceforge.stripes.test.TestActionBean;
+import net.sourceforge.stripes.exception.StripesRuntimeException;
+import org.springframework.context.support.StaticApplicationContext;
+import org.testng.annotations.Configuration;
+import org.testng.annotations.Test;
+import org.testng.annotations.ExpectedExceptions;
+import org.testng.Assert;
+
+/**
+ * Unit tests for the SpringHelper class that injects spring managed beans
+ * into objects.
+ *
+ * @author Tim Fennell
+ */
+public class SpringHelperTests {
+    StaticApplicationContext ctx;
+
+    @Configuration(beforeTestClass=true)
+    protected void setupSpringContext() {
+        ctx = new StaticApplicationContext();
+        ctx.registerSingleton("test/TestBean", TestBean.class);
+        ctx.registerSingleton("testActionBean", TestActionBean.class);
+        ctx.registerPrototype("test/testActionBean", TestActionBean.class);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ExplicitPublicSetterTarget {
+        private TestBean bean;
+        @SpringBean("test/TestBean")
+        public void setBean(TestBean bean) { this.bean = bean; }
+        public TestBean getBean() { return bean; }
+    }
+
+    @Test(groups="fast")
+    public void testExplicitSetterInjection() {
+        ExplicitPublicSetterTarget target = new ExplicitPublicSetterTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ExplicitPrivateSetterTarget {
+        private TestBean bean;
+        @SpringBean("test/TestBean")
+        private void setBean(TestBean bean) { this.bean = bean; }
+        TestBean getBean() { return bean; }
+    }
+
+    @Test(groups="fast")
+    public void testPrivateSetterInjection() {
+        ExplicitPrivateSetterTarget target = new ExplicitPrivateSetterTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ExplicitPrivateFieldTarget {
+        @SpringBean("test/TestBean") private TestBean bean;
+        TestBean getBean() { return bean; }
+    }
+
+    @Test(groups="fast")
+    public void testPrivateFieldInjection() {
+        ExplicitPrivateFieldTarget target = new ExplicitPrivateFieldTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ExplicitNonStandardSetterTarget {
+        private TestBean bean;
+        @SpringBean("test/TestBean")
+        protected void injectHere(TestBean bean) { this.bean = bean; }
+        TestBean getBean() { return bean; }
+    }
+
+    @Test(groups="fast")
+    public void testExplicitNonStandardSetterInjection() {
+        ExplicitNonStandardSetterTarget target = new 
ExplicitNonStandardSetterTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ImplicitNonStandardSetterTarget {
+        private TestActionBean bean;
+        @SpringBean protected void testActionBean(TestActionBean bean) { 
this.bean = bean; }
+        TestActionBean getBean() { return bean; }
+    }
+
+    @Test(groups="fast")
+    public void testImplicitNonStandardSetterInjection() {
+        ImplicitNonStandardSetterTarget target = new 
ImplicitNonStandardSetterTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ImplicitStandardSetterTarget {
+        private TestActionBean bean;
+        @SpringBean protected void setTestActionBean(TestActionBean bean) { 
this.bean = bean; }
+        TestActionBean getBean() { return bean; }
+    }
+
+    @Test(groups="fast")
+    public void testImplicitStandardSetterInjection() {
+        ImplicitStandardSetterTarget target = new 
ImplicitStandardSetterTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ImplicitFieldTarget {
+        @SpringBean private TestActionBean testActionBean;
+        TestActionBean getBean() { return testActionBean; }
+    }
+
+    @Test(groups="fast")
+    public void testImplicitFieldInjection() {
+        ImplicitFieldTarget target = new ImplicitFieldTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ByTypeTarget {
+        @SpringBean private TestBean someBeanOrOther;
+        TestBean getBean() { return someBeanOrOther; }
+    }
+
+    @Test(groups="fast")
+    public void testByTypeInjection() {
+        ByTypeTarget target = new ByTypeTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.getBean());
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class MultipleInjectionTarget {
+        @SpringBean TestBean someBeanOrOther; // by type
+        @SpringBean TestActionBean testActionBean; // by field name
+        TestActionBean number3; // explicit private method
+        TestActionBean number4; // explicit public method
+
+        @SpringBean("test/testActionBean")
+        private void setNumber3(TestActionBean value) { this.number3 = value; }
+
+        @SpringBean("testActionBean")
+        public void whee(TestActionBean value) { this.number4 = value; }
+    }
+
+    @Test(groups="fast")
+    public void testMultipleInjection() {
+        MultipleInjectionTarget target = new MultipleInjectionTarget();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.someBeanOrOther);
+        Assert.assertNotNull(target.testActionBean);
+        Assert.assertNotNull(target.number3);
+        Assert.assertNotNull(target.number4);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class AmbiguousByTypeTarget {
+        @SpringBean TestActionBean someBeanOrOther;
+    }
+
+    @Test(groups="fast") @ExpectedExceptions(StripesRuntimeException.class)
+    public void testAmbiguousByTypeInjection() {
+        AmbiguousByTypeTarget target = new AmbiguousByTypeTarget();
+        SpringHelper.injectBeans(target, ctx);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ExplicitMisNamedTarget {
+        @SpringBean("nonExistentBean") TestActionBean someBeanOrOther;
+    }
+
+    @Test(groups="fast") @ExpectedExceptions(StripesRuntimeException.class)
+    public void testExplicitMisNamedTargetInjection() {
+    ExplicitMisNamedTarget target = new ExplicitMisNamedTarget();
+        SpringHelper.injectBeans(target, ctx);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class ImplicitMisNamedTarget {
+        @SpringBean TestActionBean tstActionBea;
+    }
+
+    @Test(groups="fast") @ExpectedExceptions(StripesRuntimeException.class)
+    public void testImplicitMisNamedTargetInjection() {
+        ImplicitMisNamedTarget target = new ImplicitMisNamedTarget();
+        SpringHelper.injectBeans(target, ctx);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class NoBeanOfTypeTarget {
+        @SpringBean SpringHelperTests noBeansOfType;
+    }
+
+    @Test(groups="fast") @ExpectedExceptions(StripesRuntimeException.class)
+    public void testNoBeansOfTargetTypeInjection() {
+        NoBeanOfTypeTarget target = new NoBeanOfTypeTarget();
+        SpringHelper.injectBeans(target, ctx);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class InvalidSetterSignatureTarget {
+        TestActionBean testActionBean;
+        @SpringBean
+        public void setTestActionBean(TestActionBean bean, TestActionBean 
other) {
+            this.testActionBean = bean;
+        }
+    }
+
+    @Test(groups="fast") @ExpectedExceptions(StripesRuntimeException.class)
+    public void testInvalidSetterSignatureInjection() {
+        InvalidSetterSignatureTarget target = new 
InvalidSetterSignatureTarget();
+        SpringHelper.injectBeans(target, ctx);
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    private static class MultipleInjectionTarget2 {
+        @SpringBean TestBean someBeanOrOther; // by type
+        @SpringBean TestActionBean testActionBean; // by field name
+        TestActionBean number3; // explicit private method
+        TestActionBean number4; // explicit public method
+
+        @SpringBean("test/testActionBean")
+        private void setNumber3(TestActionBean value) { this.number3 = value; }
+
+        @SpringBean("testActionBean")
+        public void whee(TestActionBean value) { this.number4 = value; }
+    }
+
+    @Test(groups="slow", threadPoolSize=10, invocationCount=1000)
+    public void testConcurrentInjection() {
+        MultipleInjectionTarget2 target = new MultipleInjectionTarget2();
+        SpringHelper.injectBeans(target, ctx);
+        Assert.assertNotNull(target.someBeanOrOther);
+        Assert.assertNotNull(target.testActionBean);
+        Assert.assertNotNull(target.number3);
+        Assert.assertNotNull(target.number4);
+    }
+}
\ No newline at end of file

Modified: trunk/tests/tests.iml
===================================================================
--- trunk/tests/tests.iml       2006-05-13 02:11:11 UTC (rev 314)
+++ trunk/tests/tests.iml       2006-05-13 14:14:09 UTC (rev 315)
@@ -10,14 +10,12 @@
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
     <orderEntry type="module" module-name="stripes" exported="" />
-    <orderEntry type="module-library">
+    <orderEntry type="module-library" exported="">
       <library>
         <CLASSES>
-          <root url="jar://$MODULE_DIR$/lib/testng-4.4.7-jdk15.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/testng-4.6-jdk15.jar!/" />
         </CLASSES>
-        <JAVADOC>
-          <root url="http://testng.org/javadocs/index.html/"; />
-        </JAVADOC>
+        <JAVADOC />
         <SOURCES />
       </library>
     </orderEntry>


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development

Reply via email to