too many chanegs to list here

git-svn-id: https://svn.apache.org/repos/asf/struts/sandbox/trunk@833573 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java
index 7cf604a..6374928 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java
@@ -14,6 +14,12 @@
 
 import org.apache.commons.lang.xwork.StringUtils;
 
+/**
+ * <p>Builtin function availabe from UEL. Available functions are</p>
+ * <ul>
+ * <li>getText(String):Looks up a text from resource bundles using the parameter as a key</li>
+ * </ul>
+ */
 public class BuiltinFunctionMapper extends FunctionMapper {
     private static final Map<String, Method> BUILTIN_FUNCTIONS = new HashMap<String, Method>() {
         {
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java
index 89cf1a2..fa8effe 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java
@@ -1,9 +1,11 @@
 package org.apache.struts2.uelplugin;
 
+import com.opensymphony.xwork2.inject.Container;
 import de.odysseus.el.util.SimpleContext;
 import org.apache.struts2.uelplugin.elresolvers.CompoundRootELResolver;
-import org.apache.struts2.uelplugin.elresolvers.XWorkBeanELResolver;
 import org.apache.struts2.uelplugin.elresolvers.ValueStackContextResolver;
+import org.apache.struts2.uelplugin.elresolvers.XWorkBeanELResolver;
+import org.apache.struts2.uelplugin.elresolvers.XWorkListELResolver;
 
 import javax.el.*;
 
@@ -14,16 +16,17 @@
  */
 public class CompoundRootELContext extends SimpleContext {
     private final static BuiltinFunctionMapper BUILTIN_FUNCTION_MAPPER = new BuiltinFunctionMapper();
-    public CompoundRootELContext() {
+    public CompoundRootELContext(final Container container) {
         super(new CompositeELResolver() {
             {
-                add(new CompoundRootELResolver());
                 add(new ValueStackContextResolver());
+                add(new XWorkListELResolver(container));
+                add(new CompoundRootELResolver(container));
                 add(new ArrayELResolver(false));
-                add(new ListELResolver(false));
                 add(new MapELResolver(false));
                 add(new ResourceBundleELResolver());
-                add(new XWorkBeanELResolver());
+                add(new XWorkBeanELResolver(container));
+                add(new BeanELResolver());
             }});
     }
 
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java
index 5594997..4a2d229 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java
@@ -11,6 +11,10 @@
 import de.odysseus.el.tree.impl.ast.AstIdentifier;
 import org.apache.commons.lang.xwork.StringUtils;
 
+/**
+ * Plugs into JUEL parser to supper expressions like "#obj", to provide some level
+ * of backward compatibility with OGNL
+ */
 public class JUELExtensionBuilder extends Builder {
 
     /**
@@ -119,9 +123,3 @@
         return new ExtendedParser(this, expression);
     }
 }
-
-class ValueStackAstIdentifier extends AstIdentifier {
-    public ValueStackAstIdentifier(String name, int index) {
-        super("#"+name, index);
-    }
-}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelReflectionProvider.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelReflectionProvider.java
deleted file mode 100644
index 68eb94a..0000000
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelReflectionProvider.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.apache.struts2.uelplugin;
-
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.inject.Inject;
-import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
-import com.opensymphony.xwork2.util.reflection.ReflectionException;
-import org.apache.commons.beanutils.BeanUtils;
-import org.apache.commons.beanutils.PropertyUtils;
-import org.apache.commons.lang.xwork.StringUtils;
-
-import javax.el.ExpressionFactory;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Map;
-
-/**
- * A OgnlReflectionProvider based on Unified EL.
- */
-public class UelReflectionProvider extends OgnlReflectionProvider {
-    private ExpressionFactory factory;
-    private XWorkConverter xworkConverter;
-
-    @Inject
-    public void setXWorkConverter(XWorkConverter conv) {
-        this.xworkConverter = conv;
-    }
-
-
-    @Override
-    public Object getValue(String expr, Map context, Object root) throws ReflectionException {
-        try {
-            return PropertyUtils.getProperty(root, expr);
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        }
-
-        return null;
-    }
-
-    @Override
-    public void setValue(String expr, Map context, Object root, Object value) throws ReflectionException {
-        try {
-            BeanUtils.setProperty(root, expr, value);
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-        }
-    }
-
-    protected String wrap(String expr) {
-        if (!StringUtils.startsWith(expr, "${") && !StringUtils.startsWith(expr, "#{")) {
-            StringBuilder sb = new StringBuilder("${");
-            sb.append(expr);
-            sb.append("}");
-            return sb.toString();
-        } else return expr;
-    }
-}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelServletContextListener.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelServletContextListener.java
index bdd7b4a..5c786bf 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelServletContextListener.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelServletContextListener.java
@@ -12,7 +12,8 @@
 import org.apache.struts2.uelplugin.ExpressionFactoryHolder;
 
 /**
- * Responsible for registering the ELResolvers.
+ * Responsible for creating the ExpressionFactory that will be used by the
+ * UelValueStack
  */
 public class UelServletContextListener implements ServletContextListener {
     public void contextInitialized(ServletContextEvent contextEvent) {
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStack.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStack.java
index 10d872c..dac50b2 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStack.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStack.java
@@ -3,40 +3,49 @@
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.util.CompoundRoot;
 import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ClearableValueStack;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
 
 import javax.el.*;
 import java.util.Map;
 import java.util.TreeMap;
+import java.util.HashMap;
+
+import org.apache.struts2.uelplugin.elresolvers.AccessorsContextKey;
 
 /**
  * A ValueStack that uses Unified EL as the underlying Expression Language.
  */
-public class UelValueStack implements ValueStack {
+public class UelValueStack implements ValueStack, ClearableValueStack {
     private static final Logger LOG = LoggerFactory.getLogger(UelValueStack.class);
 
     private CompoundRoot root = new CompoundRoot();
     private transient Map context;
     private Class defaultType;
     private Map overrides;
-    private XWorkConverter xworkConverter;
 
     private ELContext elContext;
+    private Container container;
+    private XWorkConverter xworkConverter;
 
-    public UelValueStack(XWorkConverter xworkConverter) {
-        this(xworkConverter, new CompoundRoot());
+    public UelValueStack(Container container) {
+        this(container, new CompoundRoot());
     }
 
-    public UelValueStack(XWorkConverter xworkConverter, ValueStack vs) {
-        this(xworkConverter, new CompoundRoot(vs.getRoot()));
+    public UelValueStack(Container container, ValueStack vs) {
+        this(container, new CompoundRoot(vs.getRoot()));
     }
 
-    public UelValueStack(XWorkConverter xworkConverter, CompoundRoot root) {
-        this.xworkConverter = xworkConverter;
+    public UelValueStack(Container container, CompoundRoot root) {
+        this.container = container;
+        this.xworkConverter = container.getInstance(XWorkConverter.class);
         setRoot(new CompoundRoot(root));
     }
 
+
     public String findString(String expr, boolean throwException) {
         return (String) findValue(expr, String.class);
     }
@@ -70,6 +79,8 @@
             if (expr != null && !expr.startsWith("${") && !expr.startsWith("#{")) {
                 expr = "#{" + expr + "}";
             }
+
+            elContext.putContext(AccessorsContextKey.class, context);
             elContext.putContext(XWorkConverter.class, xworkConverter);
             elContext.putContext(CompoundRoot.class, root);
 
@@ -159,6 +170,7 @@
             if (expr != null && !expr.startsWith("${") && !expr.startsWith("#{")) {
                 expr = "#{" + expr + "}";
             }
+            elContext.putContext(AccessorsContextKey.class, context);
             elContext.putContext(XWorkConverter.class, xworkConverter);
             elContext.putContext(CompoundRoot.class, root);
 
@@ -181,6 +193,10 @@
         this.context = new TreeMap();
         context.put(VALUE_STACK, this);
         this.root = root;
-        elContext = new CompoundRootELContext();
+        elContext = new CompoundRootELContext(container);
+    }
+
+    public void clearContextValues() {
+        getContext().clear();        
     }
 }
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStackFactory.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStackFactory.java
index 12e0c88..dca2502 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStackFactory.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelValueStackFactory.java
@@ -31,13 +31,14 @@
 
 
     public ValueStack createValueStack() {
-        ValueStack results = new UelValueStack(xworkConverter);
+        ValueStack results = new UelValueStack(container);
         results.getContext().put(ActionContext.CONTAINER, container);
         return results;
     }
 
     public ValueStack createValueStack(ValueStack stack) {
-        ValueStack results = new UelValueStack(xworkConverter, stack);
+        ValueStack results = new UelValueStack(container, stack);
+        container.inject(results);
         results.getContext().put(ActionContext.CONTAINER, container);
         return results;
     }
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AbstractResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AbstractResolver.java
new file mode 100644
index 0000000..7646349
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AbstractResolver.java
@@ -0,0 +1,46 @@
+package org.apache.struts2.uelplugin.elresolvers;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.conversion.NullHandler;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.ObjectFactory;
+
+import javax.el.ELResolver;
+import javax.el.ELContext;
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+
+public abstract class AbstractResolver extends ELResolver {
+    protected ReflectionProvider reflectionProvider;
+    protected XWorkConverter xworkConverter;
+    protected NullHandler nullHandler;
+    protected ObjectTypeDeterminer objectTypeDeterminer;
+    protected ObjectFactory objectFactory;
+
+    public AbstractResolver(Container container) {
+        this.reflectionProvider = container.getInstance(ReflectionProvider.class);
+        this.xworkConverter = container.getInstance(XWorkConverter.class);
+        this.nullHandler = container.getInstance(NullHandler.class, "java.lang.Object");
+        this.objectTypeDeterminer = container.getInstance(ObjectTypeDeterminer.class);
+        this.objectFactory = container.getInstance(ObjectFactory.class);
+    }
+
+    public Class<?> getCommonPropertyType(ELContext elContext, Object o) {
+        return null;
+    }
+
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object o) {
+        return null;
+    }
+
+    public Class<?> getType(ELContext elContext, Object o, Object o1) {
+        return null;
+    }
+
+    public boolean isReadOnly(ELContext elContext, Object o, Object o1) {
+        return false;
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AccessorsContextKey.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AccessorsContextKey.java
new file mode 100644
index 0000000..f629cbc
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AccessorsContextKey.java
@@ -0,0 +1,5 @@
+package org.apache.struts2.uelplugin.elresolvers;
+
+
+public interface AccessorsContextKey {
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java
index 7847acd..032eb6a 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java
@@ -1,7 +1,10 @@
 package org.apache.struts2.uelplugin.elresolvers;
 
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.conversion.NullHandler;
 import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.inject.Container;
 import org.apache.commons.beanutils.BeanUtils;
 import org.apache.commons.beanutils.PropertyUtils;
 import org.apache.commons.lang.xwork.StringUtils;
@@ -15,86 +18,16 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.Map;
 
 /**
  * An ELResolver that is capable of resolving properties against the
  * CompoundRoot if available in the ELContext.
  */
-public class CompoundRootELResolver extends ELResolver {
+public class CompoundRootELResolver extends AbstractResolver {
 
-    @Override
-    public Class<?> getCommonPropertyType(ELContext context, Object base) {
-        if (base == null) {
-            return null;
-        }
-
-        return String.class;
-    }
-
-    @Override
-    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
-        // only resolve at the root of the context
-        if (base != null) {
-            return null;
-        }
-
-        CompoundRoot root = (CompoundRoot) context.getContext(CompoundRoot.class);
-        if (root == null) {
-            return null;
-        }
-
-        ArrayList<FeatureDescriptor> list = new ArrayList<FeatureDescriptor>();
-        if (root.size() > 0) {
-            FeatureDescriptor descriptor = new FeatureDescriptor();
-            descriptor.setValue("type", root.get(0).getClass());
-            descriptor.setValue("resolvableAtDesignTime", Boolean.FALSE);
-            list.add(descriptor);
-        }
-
-        for (Object bean : root) {
-            BeanInfo info = null;
-            try {
-                info = Introspector.getBeanInfo(base.getClass());
-            } catch (Exception ex) {
-            }
-            if (info != null) {
-                for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
-                    pd.setValue("type", pd.getPropertyType());
-                    pd.setValue("resolvableAtDesignTime", Boolean.FALSE);
-                    list.add(pd);
-                }
-            }
-        }
-        return list.iterator();
-    }
-
-    @Override
-    public Class<?> getType(ELContext context, Object base, Object property) {
-        // only resolve at the root of the context
-        if (base != null) {
-            return null;
-        }
-
-        CompoundRoot root = (CompoundRoot) context.getContext(CompoundRoot.class);
-        if (root == null) {
-            return null;
-        }
-        String propertyName = (String) property;
-        Object bean = findObjectForProperty(root, propertyName);
-        if (bean == null) {
-            return null;
-        }
-        try {
-            Class type = determineType(bean, propertyName);
-            context.setPropertyResolved(true);
-            return type;
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        } catch (InvocationTargetException e) {
-            throw new RuntimeException(e);
-        } catch (NoSuchMethodException e) {
-            throw new RuntimeException(e);
-        }
+    public CompoundRootELResolver(Container container) {
+        super(container);
     }
 
     @Override
@@ -122,29 +55,30 @@
             return root.get(0);
         }
 
-        try {
-            Object bean = findObjectForProperty(root, propertyName);
-            if (bean != null) {
-                Object retVal = PropertyUtils.getProperty(bean, propertyName);
-                context.setPropertyResolved(true);
-                return retVal;
+        Map<String, Object> reflectionContext = (Map) context.getContext(AccessorsContextKey.class);
+
+        Object bean = findObjectForProperty(root, propertyName);
+        if (bean != null) {
+            Object retVal = reflectionProvider.getValue(propertyName, reflectionContext, bean);
+
+            reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, bean.getClass());
+            reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, propertyName);
+
+            //if object is null, and create objects is enabled, lets do it
+            if (retVal == null && ReflectionContextState.isCreatingNullObjects(reflectionContext)) {
+                retVal = nullHandler.nullPropertyValue(reflectionContext, bean, property);
+                reflectionProvider.setValue(propertyName, reflectionContext, bean, retVal);
             }
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        } catch (InvocationTargetException e) {
-            throw new RuntimeException(e);
-        } catch (NoSuchMethodException e) {
-            throw new RuntimeException(e);
+
+            context.setPropertyResolved(true);
+            return retVal;
         }
+        
         return null;
     }
 
     @Override
     public boolean isReadOnly(ELContext context, Object base, Object property) {
-        if (context == null) {
-            throw new NullPointerException();
-        }
-
         return false;
     }
 
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java
index 4ebdfd7..90e5024 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java
@@ -1,52 +1,76 @@
 package org.apache.struts2.uelplugin.elresolvers;

 

 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;

+import com.opensymphony.xwork2.parameters.accessor.ParametersPropertyAccessor;

+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;

+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;

+import com.opensymphony.xwork2.inject.Container;

 import org.apache.commons.beanutils.PropertyUtils;

+import org.apache.struts2.StrutsException;

 

 import javax.el.BeanELResolver;

 import javax.el.ELContext;

 import java.lang.reflect.InvocationTargetException;

+import java.util.Map;

 

-public class XWorkBeanELResolver extends BeanELResolver {

-

-    public XWorkBeanELResolver() {

-        super(false);

+public class XWorkBeanELResolver extends AbstractResolver {

+    public XWorkBeanELResolver(Container container) {

+        super(container);

     }

 

-    /**

-     * Re-implement this to always return Object. We don't want unified EL to do

-     * type conversion, we do that in the setter using xwork type conversion

-     * framework.

-     */

-    @Override

-    public Class<?> getType(ELContext context, Object base, Object property) {

-        if (context == null) {

-            throw new NullPointerException();

-        }

+    public Object getValue(ELContext elContext, Object target, Object property) {

+        if (target != null && property != null) {

+            Map<String, Object> reflectionContext = (Map<String, Object>) elContext.getContext(AccessorsContextKey.class);

+            String propertyName = property.toString();

 

-        if (base == null || property == null) {

-            return null;

-        }

+            //only handle this if there is such a property

+            if (PropertyUtils.isReadable(target, propertyName)) {

+                try {

+                    Object obj = reflectionProvider.getValue(propertyName, reflectionContext, target);

 

-        context.setPropertyResolved(true);

-        return Object.class;

-    }

+                    reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());

+                    reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, property.toString());

 

-    @Override

-    public void setValue(ELContext context, Object base, Object property, Object value) {

-        XWorkConverter converter = (XWorkConverter) context.getContext(XWorkConverter.class);

-        try {

-            if (converter != null && base != null) {

-                Class propType = PropertyUtils.getPropertyType(base, property.toString());

-                value = converter.convertValue(null, value, propType);

+                    //if object is null, and create objects is enabled, lets do it

+                    if (obj == null && ReflectionContextState.isCreatingNullObjects(reflectionContext)) {

+                        obj = nullHandler.nullPropertyValue(reflectionContext, target, property);

+                        PropertyUtils.setProperty(target, propertyName, obj);

+                    }

+

+                    elContext.setPropertyResolved(true);

+                    return obj;

+                } catch (Exception e) {

+                    throw new StrutsException(e);

+                }

             }

-            super.setValue(context, base, property, value);

-        } catch (IllegalAccessException e) {

-            throw new RuntimeException(e);

-        } catch (InvocationTargetException e) {

-            throw new RuntimeException(e);

-        } catch (NoSuchMethodException e) {

-            throw new RuntimeException(e);

+        }

+

+        return null;

+    }

+

+    public void setValue(ELContext elContext, Object target, Object property, Object value) {

+        if (target != null && property != null) {

+            try {

+                Map<String, Object> reflectionContext = (Map<String, Object>) elContext.getContext(AccessorsContextKey.class);

+                String propertyName = property.toString();

+                Class targetType = target.getClass();

+

+                //only handle this if there is such a property

+                if (PropertyUtils.isReadable(target, propertyName)) {

+

+                    Class expectedType = reflectionProvider.getPropertyDescriptor(targetType, propertyName).getWriteMethod().getParameterTypes()[0];

+                    Class valueType = value.getClass();

+

+                    //convert value, if needed

+                    if (!expectedType.isAssignableFrom(valueType)) {

+                        value = xworkConverter.convertValue(reflectionContext, value, expectedType);

+                    }

+                    reflectionProvider.setValue(propertyName, reflectionContext, target, value);

+                    elContext.setPropertyResolved(true);

+                }

+            } catch (Exception e) {

+                throw new StrutsException(e);

+            }

         }

     }

 }

diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkListELResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkListELResolver.java
new file mode 100644
index 0000000..67e6b68
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkListELResolver.java
@@ -0,0 +1,140 @@
+package org.apache.struts2.uelplugin.elresolvers;
+
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.XWorkException;
+
+import javax.el.ELContext;
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+public class XWorkListELResolver extends AbstractResolver {
+    public XWorkListELResolver(Container container) {
+        super(container);
+    }
+
+    public Object getValue(ELContext elContext, Object target, Object property) {
+        Map<String, Object> context = (Map) elContext.getContext(AccessorsContextKey.class);
+
+        if (target != null && property != null && target instanceof List) {
+            List list = (List) target;
+
+            Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+            String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+
+            Integer numericValue = null;
+            try {
+                numericValue = Integer.valueOf(property.toString());
+            } catch (NumberFormatException e) {
+                //ignore
+            }
+
+            if (numericValue != null
+                    && ReflectionContextState.isCreatingNullObjects(context)
+                    && objectTypeDeterminer.shouldCreateIfNew(lastClass, lastProperty, target, null, true)) {
+                int index = numericValue.intValue();
+                int listSize = list.size();
+
+                /*if (lastClass == null || lastProperty == null) {
+                    return super.getProperty(context, target, name);
+                }*/
+                Class beanClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, property);
+                if (listSize <= index) {
+                    Object result = null;
+
+                    for (int i = listSize; i < index; i++) {
+                        list.add(null);
+                    }
+                    try {
+                        list.add(index, result = objectFactory.buildBean(beanClass, context));
+                    } catch (Exception exc) {
+                        throw new XWorkException(exc);
+                    }
+
+                    elContext.setPropertyResolved(true);
+                    return result;
+                } else if (list.get(index) == null) {
+                    Object result = null;
+                    try {
+                        list.set(index, result = objectFactory.buildBean(beanClass, context));
+                    } catch (Exception exc) {
+                        throw new XWorkException(exc);
+                    }
+
+                    elContext.setPropertyResolved(true);
+                    return result;
+                } else {
+                    elContext.setPropertyResolved(true);
+                    return list.get(index);
+                }
+            } else {
+                //try normal list
+                if (numericValue < list.size()) {
+                    elContext.setPropertyResolved(true);
+                    return list.get(numericValue);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public void setValue(ELContext elContext, Object target, Object property, Object value) {
+        Map<String, Object> context = (Map) elContext.getContext(AccessorsContextKey.class);
+        if (target != null && property != null && target instanceof List) {
+            Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+            String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+            Class convertToClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, property);
+
+            if (property instanceof String && value.getClass().isArray()) {
+                // looks like the input game in the form of "someList.foo" and
+                // we are expected to define the index values ourselves.
+                // So let's do it:
+
+                Collection c = (Collection) value;
+                Object[] values = (Object[]) value;
+                for (Object v : values) {
+                    try {
+                        Object o = objectFactory.buildBean(convertToClass, context);
+                        reflectionProvider.setProperty(property.toString(), value, target, context);
+                        c.add(o);
+                    } catch (Exception e) {
+                        throw new XWorkException("Error converting given String values for Collection.", e);
+                    }
+                }
+            }
+
+            Object realValue = getRealValue(context, value, convertToClass);
+
+            Long numericValue = null;
+            try {
+                numericValue = Long.valueOf(property.toString());
+            } catch (NumberFormatException e) {
+                //ignore
+            }
+
+            if (target instanceof List && numericValue != null) {
+                //make sure there are enough spaces in the List to set
+                List list = (List) target;
+                int listSize = list.size();
+                int count = numericValue.intValue();
+                if (count >= listSize) {
+                    for (int i = listSize; i <= count; i++) {
+                        list.add(null);
+                    }
+                }
+
+                ((List) target).set(numericValue.intValue(), realValue);
+            }
+        }
+    }
+
+    private Object getRealValue(Map context, Object value, Class convertToClass) {
+        if (value == null || convertToClass == null) {
+            return value;
+        }
+        return xworkConverter.convertValue(context, value, convertToClass);
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCache.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCache.java
new file mode 100644
index 0000000..a4929ad
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCache.java
@@ -0,0 +1,14 @@
+package org.apache.struts2.uelplugin.reflection;
+
+public interface ClassCache {
+
+    void setClassInspector(ClassCacheInspector inspector);
+
+    void clear();
+
+    int getSize();
+
+    Object get(Class key);
+
+    Object put(Class key, Object value);
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheImpl.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheImpl.java
new file mode 100644
index 0000000..4029cba
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheImpl.java
@@ -0,0 +1,100 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.util.Arrays;
+
+/**
+ * Taken from OGNL
+ */
+public class ClassCacheImpl implements ClassCache {
+
+    /* this MUST be a power of 2 */
+    private static final int TABLE_SIZE = 512;
+    /* ...and now you see why. The table size is used as a mask for generating hashes */
+    private static final int TABLE_SIZE_MASK = TABLE_SIZE - 1;
+
+    private Entry[] _table;
+    private ClassCacheInspector _classInspector;
+    private int _size = 0;
+
+    public ClassCacheImpl() {
+        _table = new Entry[TABLE_SIZE];
+    }
+
+    public void setClassInspector(ClassCacheInspector inspector) {
+        _classInspector = inspector;
+    }
+
+    public void clear() {
+        for (int i = 0; i < _table.length; i++) {
+            _table[i] = null;
+        }
+
+        _size = 0;
+    }
+
+    public int getSize() {
+        return _size;
+    }
+
+    public final Object get(Class key) {
+        Object result = null;
+        int i = key.hashCode() & TABLE_SIZE_MASK;
+
+        for (Entry entry = _table[i]; entry != null; entry = entry.next) {
+            if (entry.key == key) {
+                result = entry.value;
+                break;
+            }
+        }
+
+        return result;
+    }
+
+    public final Object put(Class key, Object value) {
+        if (_classInspector != null && !_classInspector.shouldCache(key))
+            return value;
+
+        Object result = null;
+        int i = key.hashCode() & TABLE_SIZE_MASK;
+        Entry entry = _table[i];
+
+        if (entry == null) {
+            _table[i] = new Entry(key, value);
+            _size++;
+        } else {
+            if (entry.key == key) {
+                result = entry.value;
+                entry.value = value;
+            } else {
+                while (true) {
+                    if (entry.key == key) {
+                        /* replace value */
+                        result = entry.value;
+                        entry.value = value;
+                        break;
+                    } else {
+                        if (entry.next == null) {
+                            /* add value */
+                            entry.next = new Entry(key, value);
+                            break;
+                        }
+                    }
+                    entry = entry.next;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public String toString() {
+        return "ClassCacheImpl[" +
+                "_table=" + (_table == null ? null : Arrays.asList(_table)) +
+                '\n' +
+                ", _classInspector=" + _classInspector +
+                '\n' +
+                ", _size=" + _size +
+                '\n' +
+                ']';
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheInspector.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheInspector.java
new file mode 100644
index 0000000..11dbc29
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheInspector.java
@@ -0,0 +1,17 @@
+package org.apache.struts2.uelplugin.reflection;
+
+/**
+ * Taken from OGNL
+ */
+public interface ClassCacheInspector {
+
+    /**
+     * Invoked just before storing a class type within a cache instance.
+     *
+     * @param type
+     *          The class that is to be stored.
+     *
+     * @return True if the class can be cached, false otherwise.
+     */
+    boolean shouldCache(Class type);
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/DefaultMemberAccess.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/DefaultMemberAccess.java
new file mode 100644
index 0000000..45b1e7b
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/DefaultMemberAccess.java
@@ -0,0 +1,114 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Modifier;
+
+
+/**
+ * Taken from OGNL
+ */
+public class DefaultMemberAccess implements MemberAccess
+{
+    public boolean      allowPrivateAccess = false;
+    public boolean      allowProtectedAccess = false;
+    public boolean      allowPackageProtectedAccess = false;
+
+	/*===================================================================
+		Constructors
+	  ===================================================================*/
+	public DefaultMemberAccess(boolean allowAllAccess)
+	{
+	    this(allowAllAccess, allowAllAccess, allowAllAccess);
+	}
+
+	public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess)
+	{
+	    super();
+	    this.allowPrivateAccess = allowPrivateAccess;
+	    this.allowProtectedAccess = allowProtectedAccess;
+	    this.allowPackageProtectedAccess = allowPackageProtectedAccess;
+	}
+
+	/*===================================================================
+		Public methods
+	  ===================================================================*/
+	public boolean getAllowPrivateAccess()
+	{
+	    return allowPrivateAccess;
+	}
+
+	public void setAllowPrivateAccess(boolean value)
+	{
+	    allowPrivateAccess = value;
+	}
+
+	public boolean getAllowProtectedAccess()
+	{
+	    return allowProtectedAccess;
+	}
+
+	public void setAllowProtectedAccess(boolean value)
+	{
+	    allowProtectedAccess = value;
+	}
+
+	public boolean getAllowPackageProtectedAccess()
+	{
+	    return allowPackageProtectedAccess;
+	}
+
+	public void setAllowPackageProtectedAccess(boolean value)
+	{
+	    allowPackageProtectedAccess = value;
+	}
+
+	/*===================================================================
+		MemberAccess interface
+	  ===================================================================*/
+    public Object setup(Map context, Object target, Member member, String propertyName)
+    {
+        Object      result = null;
+
+        if (isAccessible(context, target, member, propertyName)) {
+            AccessibleObject accessible = (AccessibleObject)member;
+
+            if (!accessible.isAccessible()) {
+                result = Boolean.TRUE;
+                accessible.setAccessible(true);
+            }
+        }
+        return result;
+    }
+
+    public void restore(Map context, Object target, Member member, String propertyName, Object state)
+    {
+        if (state != null) {
+            ((AccessibleObject)member).setAccessible(((Boolean)state).booleanValue());
+        }
+    }
+
+    /**
+        Returns true if the given member is accessible or can be made accessible
+        by this object.
+     */
+	public boolean isAccessible(Map context, Object target, Member member, String propertyName)
+	{
+	    int         modifiers = member.getModifiers();
+	    boolean     result = Modifier.isPublic(modifiers);
+
+	    if (!result) {
+	        if (Modifier.isPrivate(modifiers)) {
+	            result = getAllowPrivateAccess();
+	        } else {
+	            if (Modifier.isProtected(modifiers)) {
+	                result = getAllowProtectedAccess();
+	            } else {
+	                result = getAllowPackageProtectedAccess();
+	            }
+	        }
+	    }
+	    return result;
+	}
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/Entry.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/Entry.java
new file mode 100644
index 0000000..0aeca9f
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/Entry.java
@@ -0,0 +1,29 @@
+package org.apache.struts2.uelplugin.reflection;
+
+/**
+ * Taken from OGNL
+ */
+class Entry {
+
+    Entry next;
+    Class key;
+    Object value;
+
+    public Entry(Class key, Object value)
+    {
+        this.key = key;
+        this.value = value;
+    }
+
+    public String toString()
+    {
+        return "Entry[" +
+               "next=" + next +
+               '\n' +
+               ", key=" + key +
+               '\n' +
+               ", value=" + value +
+               '\n' +
+               ']';
+    }
+}
\ No newline at end of file
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/GenericReflectionProvider.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/GenericReflectionProvider.java
new file mode 100644
index 0000000..f46ecc9
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/GenericReflectionProvider.java
@@ -0,0 +1,797 @@
+package org.apache.struts2.uelplugin.reflection;
+
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.reflection.ReflectionException;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.*;
+import java.util.*;
+
+
+/**
+ * Taken from OGNL
+ */
+public class GenericReflectionProvider implements ReflectionProvider {
+    private static final Logger LOG = LoggerFactory.getLogger(GenericReflectionProvider.class);
+
+    static final ClassCache _fieldCache = new ClassCacheImpl();
+    static final List _superclasses = new ArrayList();
+    static final ClassCache[] _declaredMethods = new ClassCache[]{new ClassCacheImpl(), new ClassCacheImpl()};
+    static final Map _primitiveTypes = new HashMap(101);
+    static final ClassCache _primitiveDefaults = new ClassCacheImpl();
+    static final Map _methodParameterTypesCache = new HashMap(101);
+    static final Map _genericMethodParameterTypesCache = new HashMap(101);
+    static final Map _ctorParameterTypesCache = new HashMap(101);
+    static final ClassCache _propertyDescriptorCache = new ClassCacheImpl();
+    static final ClassCache _staticMethodCache = new ClassCacheImpl();
+    static final ClassCache _instanceMethodCache = new ClassCacheImpl();
+    static SecurityManager _securityManager = System.getSecurityManager();
+    public static final MemberAccess DEFAULT_MEMBER_ACCESS = new DefaultMemberAccess(false);
+
+    private MemberAccess _memberAccess = DEFAULT_MEMBER_ACCESS;
+
+    public static final Object NotFound = new Object();
+    public static final List NotFoundList = new ArrayList();
+    public static final Map NotFoundMap = new HashMap();
+    public static final Object[] NoArguments = new Object[]{};
+    public static final Class[] NoArgumentTypes = new Class[]{};
+
+    private static final String SET_PREFIX = "set";
+    private static final String GET_PREFIX = "get";
+    private static final String IS_PREFIX = "is";
+
+    private static boolean _jdk15 = false;
+    private static boolean _jdkChecked = false;
+    private static boolean devMode;
+    private XWorkConverter xworkConverter;
+
+    public static int INDEXED_PROPERTY_NONE = 0;
+    public static int INDEXED_PROPERTY_INT = 1;
+    public static int INDEXED_PROPERTY_OBJECT = 2;
+    private static final Object[] EMPTY_PROPERTY_DESCRIPTORS_ARRAY = new PropertyDescriptor[0];
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject("devMode")
+    public static void setDevMode(String mode) {
+        devMode = "true".equals(mode);
+    }
+
+
+    public Field getField(Class inClass, String name) {
+        Field result = null;
+
+        synchronized (_fieldCache) {
+            Object o = getFields(inClass).get(name);
+
+            if (o == null) {
+                _superclasses.clear();
+                for (Class sc = inClass; (sc != null); sc = sc.getSuperclass()) {
+                    if ((o = getFields(sc).get(name)) == NotFound)
+                        break;
+
+                    _superclasses.add(sc);
+
+                    if ((result = (Field) o) != null)
+                        break;
+                }
+                /*
+                 * Bubble the found value (either cache miss or actual field) to all supeclasses
+                 * that we saw for quicker access next time.
+                 */
+                for (int i = 0, icount = _superclasses.size(); i < icount; i++) {
+                    getFields((Class) _superclasses.get(i)).put(name, (result == null) ? NotFound : result);
+                }
+            } else {
+                if (o instanceof Field) {
+                    result = (Field) o;
+                } else {
+                    if (result == NotFound)
+                        result = null;
+                }
+            }
+        }
+        return result;
+    }
+
+    public static Map getFields(Class targetClass) {
+        Map result;
+
+        synchronized (_fieldCache) {
+            if ((result = (Map) _fieldCache.get(targetClass)) == null) {
+                Field fa[];
+
+                result = new HashMap(23);
+                fa = targetClass.getDeclaredFields();
+                for (int i = 0; i < fa.length; i++) {
+                    result.put(fa[i].getName(), fa[i]);
+                }
+                _fieldCache.put(targetClass, result);
+            }
+        }
+        return result;
+    }
+
+    public Method getGetMethod(Class targetClass, String propertyName) {
+        Method result = null;
+
+
+        List methods = getDeclaredMethods(targetClass, propertyName, false /* find 'get' methods */);
+
+        if (methods != null) {
+            for (int i = 0, icount = methods.size(); i < icount; i++) {
+                Method m = (Method) methods.get(i);
+                Class[] mParameterTypes = findParameterTypes(targetClass, m); //getParameterTypes(m);
+
+                if (mParameterTypes.length == 0) {
+                    result = m;
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Finds the appropriate parameter types for the given {@link Method} and
+     * {@link Class} instance of the type the method is associated with.  Correctly
+     * finds generic types if running in >= 1.5 jre as well.
+     *
+     * @param type The class type the method is being executed against.
+     * @param m    The method to find types for.
+     * @return Array of parameter types for the given method.
+     */
+    public static Class[] findParameterTypes(Class type, Method m) {
+        if (type == null) {
+            return getParameterTypes(m);
+        }
+
+        if (!isJdk15()
+                || type.getGenericSuperclass() == null
+                || !ParameterizedType.class.isInstance(type.getGenericSuperclass())
+                || m.getDeclaringClass().getTypeParameters() == null) {
+            return getParameterTypes(m);
+        }
+
+        synchronized (_genericMethodParameterTypesCache) {
+            Class[] types;
+
+            if ((types = (Class[]) _genericMethodParameterTypesCache.get(m)) != null) {
+                return types;
+            }
+
+            ParameterizedType param = (ParameterizedType) type.getGenericSuperclass();
+            Type[] genTypes = m.getGenericParameterTypes();
+            TypeVariable[] declaredTypes = m.getDeclaringClass().getTypeParameters();
+
+            types = new Class[genTypes.length];
+
+            typeSearch:
+            for (int i = 0; i < genTypes.length; i++) {
+                TypeVariable paramType = null;
+
+                if (TypeVariable.class.isInstance(genTypes[i])) {
+                    paramType = (TypeVariable) genTypes[i];
+                } else if (GenericArrayType.class.isInstance(genTypes[i])) {
+                    paramType = (TypeVariable) ((GenericArrayType) genTypes[i]).getGenericComponentType();
+                } else if (Class.class.isInstance(genTypes[i])) {
+                    types[i] = (Class) genTypes[i];
+                    continue;
+                }
+
+                Class resolved = resolveType(param, paramType, declaredTypes);
+
+                if (resolved != null) {
+                    if (GenericArrayType.class.isInstance(genTypes[i])) {
+                        resolved = Array.newInstance(resolved, 0).getClass();
+                    }
+
+                    types[i] = resolved;
+                    continue;
+                }
+
+                types[i] = m.getParameterTypes()[i];
+            }
+
+            _genericMethodParameterTypesCache.put(m, types);
+
+            return types;
+        }
+    }
+
+    static Class resolveType(ParameterizedType param, TypeVariable var, TypeVariable[] declaredTypes) {
+        if (param.getActualTypeArguments().length < 1)
+            return null;
+
+        for (int i = 0; i < declaredTypes.length; i++) {
+            if (!TypeVariable.class.isInstance(param.getActualTypeArguments()[i])
+                    && declaredTypes[i].getName().equals(var.getName())) {
+                return (Class) param.getActualTypeArguments()[i];
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the parameter types of the given method.
+     */
+    public static Class[] getParameterTypes(Method m) {
+        synchronized (_methodParameterTypesCache) {
+            Class[] result;
+
+            if ((result = (Class[]) _methodParameterTypesCache.get(m)) == null) {
+                _methodParameterTypesCache.put(m, result = m.getParameterTypes());
+            }
+            return result;
+        }
+    }
+
+    public static List getDeclaredMethods(Class targetClass, String propertyName, boolean findSets) {
+        List result = null;
+        ClassCache cache = _declaredMethods[findSets ? 0 : 1];
+
+        synchronized (cache) {
+            Map propertyCache = (Map) cache.get(targetClass);
+
+            if ((propertyCache == null) || ((result = (List) propertyCache.get(propertyName)) == null)) {
+
+                String baseName = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+
+                for (Class c = targetClass; c != null; c = c.getSuperclass()) {
+                    Method[] methods = c.getDeclaredMethods();
+
+                    for (int i = 0; i < methods.length; i++) {
+
+                        if (!isMethodCallable(methods[i]))
+                            continue;
+
+                        String ms = methods[i].getName();
+
+                        if (ms.endsWith(baseName)) {
+                            boolean isSet = false, isIs = false;
+
+                            if ((isSet = ms.startsWith(SET_PREFIX)) || ms.startsWith(GET_PREFIX)
+                                    || (isIs = ms.startsWith(IS_PREFIX))) {
+                                int prefixLength = (isIs ? 2 : 3);
+
+                                if (isSet == findSets) {
+                                    if (baseName.length() == (ms.length() - prefixLength)) {
+                                        if (result == null) {
+                                            result = new ArrayList();
+                                        }
+                                        result.add(methods[i]);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                if (propertyCache == null) {
+                    cache.put(targetClass, propertyCache = new HashMap(101));
+                }
+
+                propertyCache.put(propertyName, (result == null) ? NotFoundList : result);
+            }
+            return (result == NotFoundList) ? null : result;
+        }
+    }
+
+    static boolean isMethodCallable(Method m) {
+        if ((isJdk15() && m.isSynthetic()) || Modifier.isVolatile(m.getModifiers()))
+            return false;
+
+        return true;
+    }
+
+    public static boolean isJdk15() {
+        if (_jdkChecked)
+            return _jdk15;
+
+        try {
+            Class.forName("java.lang.annotation.Annotation");
+            _jdk15 = true;
+        } catch (Exception e) { /* ignore */ }
+
+        _jdkChecked = true;
+
+        return _jdk15;
+    }
+
+    public Method getSetMethod(Class targetClass, String propertyName) {
+        Method result = null;
+
+        List methods = getDeclaredMethods(targetClass, propertyName, true /* find 'set' methods */);
+
+        if (methods != null) {
+            for (int i = 0, icount = methods.size(); i < icount; i++) {
+                Method m = (Method) methods.get(i);
+                Class[] mParameterTypes = findParameterTypes(targetClass, m); //getParameterTypes(m);
+
+                if (mParameterTypes.length == 1) {
+                    result = m;
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    public void setProperties(Map<String, String> props, Object o, Map<String, Object> context) {
+        setProperties(props, o, context, false);
+    }
+
+    public void setProperties(Map<String, String> props, Object o, Map<String, Object> context,
+                              boolean throwPropertyExceptions) throws ReflectionException {
+        for (Map.Entry<String, ?> entry : props.entrySet()) {
+            String expression = entry.getKey();
+            setProperty(expression, entry.getValue(), o, context, throwPropertyExceptions);
+        }
+    }
+
+    public void setProperties(Map<String, String> properties, Object o) {
+        setProperties(properties, o, null);
+    }
+
+    public PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+        if (targetClass == null)
+            return null;
+
+        return (PropertyDescriptor) internalGetPropertyDescriptors(targetClass).get(propertyName);
+    }
+
+    public void copy(Object from, Object to, Map<String, Object> context,
+                     Collection<String> exclusions, Collection<String> inclusions) {
+        if (from == null || to == null) {
+            LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
+
+            return;
+        }
+
+        PropertyDescriptor[] fromPds;
+        PropertyDescriptor[] toPds;
+
+        try {
+            fromPds = getPropertyDescriptors(from);
+            toPds = getPropertyDescriptors(to);
+        } catch (IntrospectionException e) {
+            LOG.error("An error occured", e);
+
+            return;
+        }
+
+        Map<String, PropertyDescriptor> toPdHash = new HashMap<String, PropertyDescriptor>();
+
+        for (PropertyDescriptor toPd : toPds) {
+            toPdHash.put(toPd.getName(), toPd);
+        }
+
+        for (PropertyDescriptor fromPd : fromPds) {
+            if (fromPd.getReadMethod() != null) {
+                boolean copy = true;
+                if (exclusions != null && exclusions.contains(fromPd.getName())) {
+                    copy = false;
+                } else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
+                    copy = false;
+                }
+
+                if (copy == true) {
+                    PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
+                    if ((toPd != null) && (toPd.getWriteMethod() != null)) {
+                        try {
+                            String expr = fromPd.getName();
+                            Object value = getValue(expr, context, from);
+                            setValue(expr, context, to, value);
+                        } catch (Exception e) {
+                            // ignore, this is OK
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Looks for the real target with the specified property given a root Object which may be a
+     * CompoundRoot.
+     *
+     * @return the real target or null if no object can be found with the specified property
+     */
+    public Object getRealTarget(String property, Map<String, Object> context, Object root) {
+        //special keyword, they must be cutting the stack
+        if ("top".equals(property)) {
+            return root;
+        }
+
+        if (root instanceof CompoundRoot) {
+            // find real target
+            CompoundRoot cr = (CompoundRoot) root;
+
+            try {
+                for (Object target : cr) {
+                    if (
+                            hasSetProperty(context, target, property)
+                                    ||
+                                    hasGetProperty(context, target, property)
+                                    ||
+                                    getIndexedPropertyType(context, target.getClass(), property) != INDEXED_PROPERTY_NONE
+                            ) {
+                        return target;
+                    }
+                }
+            } catch (IntrospectionException ex) {
+                throw new ReflectionException("Cannot figure out real target class", ex);
+            }
+
+            return null;
+        }
+
+        return root;
+    }
+
+    /**
+     * Determines the index property type, if any. Returns <code>INDEXED_PROPERTY_NONE</code> if
+     * the property is not index-accessible as determined by OGNL or JavaBeans. If it is indexable
+     * then this will return whether it is a JavaBeans indexed property, conforming to the indexed
+     * property patterns (returns <code>INDEXED_PROPERTY_INT</code>) or if it conforms to the
+     * OGNL arbitrary object indexable (returns <code>INDEXED_PROPERTY_OBJECT</code>).
+     */
+    public int getIndexedPropertyType(Map<String, Object> context, Class sourceClass, String name) {
+        int result = INDEXED_PROPERTY_NONE;
+
+        try {
+            PropertyDescriptor pd = getPropertyDescriptor(sourceClass, name);
+            if (pd != null) {
+                if (pd instanceof IndexedPropertyDescriptor) {
+                    result = INDEXED_PROPERTY_INT;
+                } else {
+                    if (pd instanceof ognl.ObjectIndexedPropertyDescriptor) {
+                        result = INDEXED_PROPERTY_OBJECT;
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            throw new ReflectionException("problem determining if '" + name + "' is an indexed property", ex);
+        }
+        return result;
+    }
+
+    public final boolean hasSetProperty(Map<String, Object> context, Object target, Object oname)
+            throws IntrospectionException {
+        Class targetClass = (target == null) ? null : target.getClass();
+        String name = oname.toString();
+
+        return hasSetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name);
+    }
+
+    public final boolean hasSetMethod(Map<String, Object> context, Object target, Class targetClass, String propertyName)
+            throws IntrospectionException {
+        return isMethodAccessible(context, target, getSetMethod(targetClass, propertyName), propertyName);
+    }
+
+    public boolean isMethodAccessible(Map<String, Object> context, Object target, Method method, String propertyName) {
+        return (method != null) && _memberAccess.isAccessible(context, target, method, propertyName);
+    }
+
+    public final boolean hasGetProperty(Map<String, Object> context, Object target, Object oname)
+            throws IntrospectionException {
+        Class targetClass = (target == null) ? null : target.getClass();
+        String name = oname.toString();
+
+        return hasGetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name);
+    }
+
+    public boolean hasGetMethod(Map<String, Object> context, Object target, Class targetClass, String propertyName)
+            throws IntrospectionException {
+        return isMethodAccessible(context, target, getGetMethod(targetClass, propertyName), propertyName);
+    }
+
+    public boolean hasField(Map<String, Object> context, Object target, Class inClass, String propertyName) {
+        Field f = getField(inClass, propertyName);
+
+        return (f != null) && isFieldAccessible(context, target, f, propertyName);
+    }
+
+    public boolean isFieldAccessible(Map<String, Object> context, Object target, Class inClass, String propertyName) {
+        return isFieldAccessible(context, target, getField(inClass, propertyName), propertyName);
+    }
+
+    public boolean isFieldAccessible(Map<String, Object> context, Object target, Field field, String propertyName) {
+        return _memberAccess.isAccessible(context, target, field, propertyName);
+    }
+
+
+    public void setProperty(String name, Object value, Object o, Map<String, Object> context) {
+        setProperty(name, value, o, context, false);
+    }
+
+    public void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) {
+        try {
+            setValue(name, context, o, value);
+        } catch (Exception e) {
+            String msg = "Caught an Exception while setting property '" + name + "' on type '" + o.getClass().getName() + "'.";
+
+            if (throwPropertyExceptions) {
+                throw new ReflectionException(msg, e);
+            } else {
+                if (devMode) {
+                    LOG.warn(msg, e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates a Map with read properties for the given source object.
+     * <p/>
+     * If the source object does not have a read property (i.e. write-only) then
+     * the property is added to the map with the value <code>here is no read method for property-name</code>.
+     *
+     * @param source the source object.
+     * @return a Map with (key = read property name, value = value of read property).
+     * @throws IntrospectionException is thrown if an exception occurs during introspection.
+     */
+    public Map getBeanMap(Object source) throws IntrospectionException {
+        Map beanMap = new HashMap();
+        PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(source);
+        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+            String propertyName = propertyDescriptor.getDisplayName();
+            Method readMethod = propertyDescriptor.getReadMethod();
+            if (readMethod != null) {
+                Object value = getValue(propertyName, Collections.EMPTY_MAP, source);
+                beanMap.put(propertyName, value);
+            } else {
+                beanMap.put(propertyName, "There is no read method for " + propertyName);
+            }
+        }
+        return beanMap;
+    }
+
+    public Object getValue(String expression, Map<String, Object> context, Object root)
+            throws ReflectionException {
+        Method method = getGetMethod(root.getClass(), expression);
+
+        if (method != null) {
+            try {
+                return method.invoke(root, NoArguments);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            throw new ReflectionException("Unable to find get method for [" + expression + "]");
+        }
+
+    }
+
+    public void setValue(String expression, Map<String, Object> context, Object root,
+                         Object value) throws ReflectionException {
+        Method method = getSetMethod(root.getClass(), expression);
+        if (method != null) {
+            if (value != null) {
+                //convert type if needed
+                Class expectedType = method.getParameterTypes()[0];
+                Class valueType = value.getClass();
+                if (!expectedType.isAssignableFrom(valueType)) {
+                    value = xworkConverter.convertValue(context, value, expectedType);
+                }
+            }
+
+            try {
+                method.invoke(root, value);
+            } catch (Exception e) {
+                throw new ReflectionException(e);
+            }
+        } else {
+            throw new ReflectionException("Unable to find set method for [" + expression + "]");
+        }
+    }
+
+    public Map internalGetPropertyDescriptors(Class targetClass) throws IntrospectionException {
+        Map result;
+
+        synchronized (_propertyDescriptorCache) {
+            if ((result = (Map) _propertyDescriptorCache.get(targetClass)) == null) {
+                PropertyDescriptor[] pda = Introspector.getBeanInfo(targetClass).getPropertyDescriptors();
+
+                result = new HashMap(101);
+                for (int i = 0, icount = pda.length; i < icount; i++) {
+                    // workaround for Introspector bug 6528714 (bugs.sun.com)
+                    if (pda[i].getReadMethod() != null && !isMethodCallable(pda[i].getReadMethod())) {
+                        pda[i].setReadMethod(findClosestMatchingMethod(targetClass, pda[i].getReadMethod(), pda[i].getName(),
+                                pda[i].getPropertyType(), true));
+                    }
+                    if (pda[i].getWriteMethod() != null && !isMethodCallable(pda[i].getWriteMethod())) {
+                        pda[i].setWriteMethod(findClosestMatchingMethod(targetClass, pda[i].getWriteMethod(), pda[i].getName(),
+                                pda[i].getPropertyType(), false));
+                    }
+
+                    result.put(pda[i].getName(), pda[i]);
+                }
+
+                findObjectIndexedPropertyDescriptors(targetClass, result);
+                _propertyDescriptorCache.put(targetClass, result);
+            }
+        }
+
+        return result;
+    }
+
+    static Method findClosestMatchingMethod(Class targetClass, Method m, String propertyName,
+                                            Class propertyType, boolean isReadMethod) {
+        List methods = getDeclaredMethods(targetClass, propertyName, !isReadMethod);
+
+        for (int i = 0; i < methods.size(); i++) {
+            Method method = (Method) methods.get(i);
+
+            if (method.getName().equals(m.getName())
+                    && m.getReturnType().isAssignableFrom(m.getReturnType())
+                    && method.getReturnType() == propertyType
+                    && method.getParameterTypes().length == m.getParameterTypes().length) {
+                return method;
+            }
+        }
+
+        return m;
+    }
+
+    static void findObjectIndexedPropertyDescriptors(Class targetClass, Map intoMap)
+            throws ReflectionException {
+        Map allMethods = getMethods(targetClass, false);
+        Map pairs = new HashMap(101);
+
+        for (Iterator it = allMethods.keySet().iterator(); it.hasNext();) {
+            String methodName = (String) it.next();
+            List methods = (List) allMethods.get(methodName);
+
+            /*
+             * Only process set/get where there is exactly one implementation of the method per
+             * class and those implementations are all the same
+             */
+            if (indexMethodCheck(methods)) {
+                boolean isGet = false, isSet = false;
+                Method m = (Method) methods.get(0);
+
+                if (((isSet = methodName.startsWith(SET_PREFIX)) || (isGet = methodName.startsWith(GET_PREFIX)))
+                        && (methodName.length() > 3)) {
+                    String propertyName = Introspector.decapitalize(methodName.substring(3));
+                    Class[] parameterTypes = getParameterTypes(m);
+                    int parameterCount = parameterTypes.length;
+
+                    if (isGet && (parameterCount == 1) && (m.getReturnType() != Void.TYPE)) {
+                        List pair = (List) pairs.get(propertyName);
+
+                        if (pair == null) {
+                            pairs.put(propertyName, pair = new ArrayList());
+                        }
+                        pair.add(m);
+                    }
+                    if (isSet && (parameterCount == 2) && (m.getReturnType() == Void.TYPE)) {
+                        List pair = (List) pairs.get(propertyName);
+
+                        if (pair == null) {
+                            pairs.put(propertyName, pair = new ArrayList());
+                        }
+                        pair.add(m);
+                    }
+                }
+            }
+        }
+
+        for (Iterator it = pairs.keySet().iterator(); it.hasNext();) {
+            String propertyName = (String) it.next();
+            List methods = (List) pairs.get(propertyName);
+
+            if (methods.size() == 2) {
+                Method method1 = (Method) methods.get(0), method2 = (Method) methods.get(1), setMethod = (method1
+                        .getParameterTypes().length == 2) ? method1 : method2, getMethod = (setMethod == method1) ? method2
+                        : method1;
+                Class keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType();
+
+                if (keyType == setMethod.getParameterTypes()[0]) {
+                    if (propertyType == setMethod.getParameterTypes()[1]) {
+                        ObjectIndexedPropertyDescriptor propertyDescriptor;
+
+                        try {
+                            propertyDescriptor = new ObjectIndexedPropertyDescriptor(propertyName, propertyType,
+                                    getMethod, setMethod);
+                        } catch (Exception ex) {
+                            throw new ReflectionException("creating object indexed property descriptor for '" + propertyName
+                                    + "' in " + targetClass, ex);
+                        }
+                        intoMap.put(propertyName, propertyDescriptor);
+                    }
+                }
+
+            }
+        }
+    }
+
+    public static Map getMethods(Class targetClass, boolean staticMethods) {
+        ClassCache cache = (staticMethods ? _staticMethodCache : _instanceMethodCache);
+        Map result;
+
+        synchronized (cache) {
+            if ((result = (Map) cache.get(targetClass)) == null) {
+                cache.put(targetClass, result = new HashMap(23));
+
+                for (Class c = targetClass; c != null; c = c.getSuperclass()) {
+                    Method[] ma = c.getDeclaredMethods();
+
+                    for (int i = 0, icount = ma.length; i < icount; i++) {
+                        // skip over synthetic methods
+
+                        if (!isMethodCallable(ma[i]))
+                            continue;
+
+                        if (Modifier.isStatic(ma[i].getModifiers()) == staticMethods) {
+                            List ml = (List) result.get(ma[i].getName());
+
+                            if (ml == null)
+                                result.put(ma[i].getName(), ml = new ArrayList());
+
+                            ml.add(ma[i]);
+                        }
+                    }
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private static final boolean indexMethodCheck(List methods) {
+        boolean result = false;
+
+        if (methods.size() > 0) {
+            Method fm = (Method) methods.get(0);
+            Class[] fmpt = getParameterTypes(fm);
+            int fmpc = fmpt.length;
+            Class lastMethodClass = fm.getDeclaringClass();
+
+            result = true;
+            for (int i = 1; result && (i < methods.size()); i++) {
+                Method m = (Method) methods.get(i);
+                Class c = m.getDeclaringClass();
+
+                // Check to see if more than one method implemented per class
+                if (lastMethodClass == c) {
+                    result = false;
+                } else {
+                    Class[] mpt = getParameterTypes(fm);
+                    int mpc = fmpt.length;
+
+                    if (fmpc != mpc) {
+                        result = false;
+                    }
+                    for (int j = 0; j < fmpc; j++) {
+                        if (fmpt[j] != mpt[j]) {
+                            result = false;
+                            break;
+                        }
+                    }
+                }
+                lastMethodClass = c;
+            }
+        }
+        return result;
+    }
+
+    public PropertyDescriptor[] getPropertyDescriptors(Object source) throws IntrospectionException {
+        return (PropertyDescriptor[]) internalGetPropertyDescriptors(source.getClass()).values().toArray(EMPTY_PROPERTY_DESCRIPTORS_ARRAY);
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/MemberAccess.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/MemberAccess.java
new file mode 100644
index 0000000..1f9301f
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/MemberAccess.java
@@ -0,0 +1,26 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+
+/**
+ * Taken from OGNL
+ */
+public interface MemberAccess
+{
+    /**
+        Sets the member up for accessibility
+     */
+    public Object setup(Map context, Object target, Member member, String propertyName);
+
+    /**
+        Restores the member from the previous setup call.
+     */
+    public void restore(Map context, Object target, Member member, String propertyName, Object state);
+
+    /**
+        Returns true if the given member is accessible or can be made accessible
+        by this object.
+     */
+	public boolean isAccessible(Map context, Object target, Member member, String propertyName);
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ObjectIndexedPropertyDescriptor.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ObjectIndexedPropertyDescriptor.java
new file mode 100644
index 0000000..c12a72b
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ObjectIndexedPropertyDescriptor.java
@@ -0,0 +1,39 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.beans.PropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.lang.reflect.Method;
+
+/**
+ * Taken from OGNL
+ */
+public class ObjectIndexedPropertyDescriptor extends PropertyDescriptor
+{
+    private Method indexedReadMethod;
+    private Method          indexedWriteMethod;
+    private Class           propertyType;
+
+    public ObjectIndexedPropertyDescriptor(String propertyName, Class propertyType, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException
+    {
+        super(propertyName, null, null);
+        this.propertyType = propertyType;
+        this.indexedReadMethod = indexedReadMethod;
+        this.indexedWriteMethod = indexedWriteMethod;
+    }
+
+    public Method getIndexedReadMethod()
+    {
+        return indexedReadMethod;
+    }
+
+    public Method getIndexedWriteMethod()
+    {
+        return indexedWriteMethod;
+    }
+
+    public Class getPropertyType()
+    {
+        return propertyType;
+    }
+}
+
diff --git a/struts2-uel-plugin/src/main/resources/struts-plugin.xml b/struts2-uel-plugin/src/main/resources/struts-plugin.xml
index 5147ebe..89314f6 100644
--- a/struts2-uel-plugin/src/main/resources/struts-plugin.xml
+++ b/struts2-uel-plugin/src/main/resources/struts-plugin.xml
@@ -8,7 +8,7 @@
     <bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="uel"
           class="org.apache.struts2.uelplugin.UelValueStackFactory"/>
     <bean type="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name="uel"
-          class="org.apache.struts2.uelplugin.UelReflectionProvider"/>
+          class="org.apache.struts2.uelplugin.reflection.GenericReflectionProvider"/>
     <bean type="com.opensymphony.xwork2.util.reflection.ReflectionContextFactory" name="uel"
           class="org.apache.struts2.uelplugin.UelReflectionContextFactory"/>
 
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java
new file mode 100644
index 0000000..15630f1
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java
@@ -0,0 +1,89 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.XWorkTestCase;
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.inject.Factory;
+import com.opensymphony.xwork2.inject.Context;
+import com.opensymphony.xwork2.inject.Scope;
+import com.opensymphony.xwork2.test.StubConfigurationProvider;
+import com.opensymphony.xwork2.config.ConfigurationManager;
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import org.springframework.mock.web.MockServletContext;
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.uelplugin.reflection.GenericReflectionProvider;
+import org.apache.struts2.util.StrutsTypeConverter;
+import org.apache.commons.beanutils.converters.DateConverter;
+
+import javax.servlet.ServletContextEvent;
+import javax.el.ExpressionFactory;
+import java.util.Map;
+import java.text.ParseException;
+import java.text.DateFormat;
+
+
+public abstract class AbstractUelBaseTest extends XWorkTestCase {
+    private ExpressionFactory factory = ExpressionFactory.newInstance();
+    protected XWorkConverter converter;
+    protected CompoundRoot root;
+    protected UelValueStack stack;
+    protected DateFormat format = DateFormat.getDateInstance();
+    protected ReflectionProvider reflectionProvider;
+
+    private class DateConverter extends StrutsTypeConverter {
+
+        @Override
+        public Object convertFromString(Map context, String[] values, Class toClass) {
+            try {
+                return format.parseObject(values[0]);
+            } catch (ParseException e) {
+                return null;
+            }
+        }
+
+        @Override
+        public String convertToString(Map context, Object o) {
+            return format.format(o);
+        }
+
+    }
+
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        loadConfigurationProviders(new StubConfigurationProvider() {
+            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
+                builder.factory(ValueStack.class, UelValueStack.class);
+                builder.factory(ValueStackFactory.class, UelValueStackFactory.class);
+                builder.factory(ReflectionProvider.class, GenericReflectionProvider.class);
+            }
+        });
+
+        converter = container.getInstance(XWorkConverter.class);
+        reflectionProvider = container.getInstance(ReflectionProvider.class);
+        converter.registerConverter("java.util.Date", new DateConverter());
+        this.root = new CompoundRoot();
+        this.stack = new UelValueStack(container);
+        stack.setRoot(root);
+        stack.getContext().put(ActionContext.CONTAINER, container);
+
+        MockServletContext servletContext = new MockServletContext();
+        ActionContext context = new ActionContext(stack.getContext());
+        ActionContext.setContext(context);
+        ServletActionContext.setServletContext(servletContext);
+
+        //simulate start up
+        UelServletContextListener listener = new UelServletContextListener();
+        listener.contextInitialized(new ServletContextEvent(servletContext));
+    }
+}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java
index 14e3df7..9be211f 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java
@@ -15,11 +15,7 @@
 import javax.servlet.ServletContextEvent;
 
 
-public class BuiltinFunctionsTest extends XWorkTestCase {
-    private XWorkConverter converter;
-    private CompoundRoot root;
-    private UelValueStack stack;
-
+public class BuiltinFunctionsTest extends AbstractUelBaseTest {
     public void testGetText() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         TestAction action = new TestAction();
         stack.push(action);
@@ -27,25 +23,4 @@
 
         assertEquals("This is the key!", stack.findValue("${getText('key')}"));
     }
-
-
-
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        converter = container.getInstance(XWorkConverter.class);
-        this.root = new CompoundRoot();
-        this.stack = new UelValueStack(converter);
-        stack.setRoot(root);
-        stack.getContext().put(ActionContext.CONTAINER, container);
-
-        MockServletContext servletContext = new MockServletContext();
-        ActionContext context = new ActionContext(stack.getContext());
-        ActionContext.setContext(context);
-        ServletActionContext.setServletContext(servletContext);
-
-        //simulate start up
-        UelServletContextListener listener = new UelServletContextListener();
-        listener.contextInitialized(new ServletContextEvent(servletContext));
-    }
 }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/DummyTypeConverter.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/DummyTypeConverter.java
new file mode 100644
index 0000000..73be98d
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/DummyTypeConverter.java
@@ -0,0 +1,18 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.conversion.TypeConverter;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+
+import org.apache.struts2.util.StrutsTypeConverter;
+
+public class DummyTypeConverter extends StrutsTypeConverter {
+    public Object convertFromString(Map context, String[] values, Class toClass) {
+        return "converted";
+    }
+
+    public String convertToString(Map context, Object o) {
+        return "converted-tostring";
+    }
+}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java
index 44e3a93..9ed8fb0 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java
@@ -13,11 +13,7 @@
 import java.util.HashMap;
 
 
-public class JuelMethodInvocationTest extends XWorkTestCase {
-    private XWorkConverter converter;
-    private CompoundRoot root;
-    private UelValueStack stack;
-
+public class JuelMethodInvocationTest extends AbstractUelBaseTest {
     public void testBasicMethods() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         assertEquals("text", stack.findValue("${' text '.trim()}"));
         assertEquals(3, stack.findValue("${'123'.length()}"));
@@ -28,28 +24,9 @@
         assertEquals("123456", stack.findValue("${'123'.concat('456')}"));
     }
 
-      public void testMethodsWithParamsAndContextReference() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-          stack.getContext().put("s0", "Lex");
-          stack.getContext().put("s1", "Luthor");
-          assertEquals("Lex Luthor", stack.findValue("${#s0.concat(' ').concat(#s1)}"));
-      }
-
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        converter = container.getInstance(XWorkConverter.class);
-        this.root = new CompoundRoot();
-        this.stack = new UelValueStack(converter);
-        stack.setRoot(root);
-        stack.getContext().put(ActionContext.CONTAINER, container);
-
-        MockServletContext servletContext = new MockServletContext();
-        ActionContext context = new ActionContext(stack.getContext());
-        ActionContext.setContext(context);
-        ServletActionContext.setServletContext(servletContext);
-
-        //simulate start up
-        UelServletContextListener listener = new UelServletContextListener();
-        listener.contextInitialized(new ServletContextEvent(servletContext));
+    public void testMethodsWithParamsAndContextReference() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        stack.getContext().put("s0", "Lex");
+        stack.getContext().put("s1", "Luthor");
+        assertEquals("Lex Luthor", stack.findValue("${#s0.concat(' ').concat(#s1)}"));
     }
 }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java
new file mode 100644
index 0000000..886b365
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java
@@ -0,0 +1,85 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.Action;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.lang.reflect.InvocationTargetException;
+
+
+public class ParametersTest extends AbstractUelBaseTest {
+    public void testWriteList() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        //not null
+        List list = new ArrayList();
+        TestObject obj = new TestObject();
+        obj.setList(list);
+        assertNotNull(obj.getList());
+        root.push(obj);
+
+        stack.setValue("list[0]", "val");
+        assertEquals(1, list.size());
+        assertEquals("val", list.get(0));
+
+        //null list
+        obj.setList(null);
+        assertNull(obj.getList());
+        stack.setValue("list[0]", "val");
+        assertNotNull(obj.getList());
+        assertEquals("val", stack.findValue("list[0]"));
+
+        //test out of index
+        obj.setList(null);
+        stack.setValue("list[3]", "val");
+        assertEquals(4, obj.getList().size());
+        assertEquals("val", obj.getList().get(3));
+
+        //test type determiner
+        obj.setTypedList(null);
+        stack.setValue("typedList[1].value", "val");
+        assertEquals(2, obj.getTypedList().size());
+        assertEquals("val", obj.getTypedList().get(1).getValue());
+    }
+
+    public void testAnnotatedTypeConverter() {
+        TestAction action = new TestAction();
+        assertNull(action.getConverted());
+        root.push(action);
+
+        stack.setValue("converted", "someval");
+        assertEquals("converted", action.getConverted());
+    }
+
+
+    public void testSetPropertiesOnNestedNullObject() {
+        TestObject obj = new TestObject();
+        assertNull(obj.getInner());
+        root.push(obj);
+
+        //inner is null, it will be catched bye the CompoundRoolELResolver
+        stack.setValue("inner.value", "val");
+        assertNotNull(obj.getInner());
+        assertEquals("val", obj.getInner().getValue());
+
+
+        //second nested property null
+        stack.setValue("inner.inner.value", "val");
+        assertNotNull(obj.getInner().getInner());
+        assertEquals("val", obj.getInner().getInner().getValue());
+    }
+
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Map context = stack.getContext();
+
+        ReflectionContextState.setCreatingNullObjects(context, true);
+        ReflectionContextState.setDenyMethodExecution(context, true);
+        ReflectionContextState.setReportingConversionErrors(context, true);
+    }
+}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ReflectionProviderTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ReflectionProviderTest.java
deleted file mode 100644
index 8fa8fe2..0000000
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ReflectionProviderTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.apache.struts2.uelplugin;
-
-import com.opensymphony.xwork2.XWorkTestCase;
-import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
-
-import java.util.HashMap;
-
-public class ReflectionProviderTest extends XWorkTestCase {
-    private ReflectionProvider reflectionProvider;
-
-    public void testGetSimple() {
-        TestObject obj = new TestObject();
-        TestObject obj2 = new TestObject();
-        obj2.setAge(100);
-        obj.setInner(obj2);
-
-        assertSame(obj2, reflectionProvider.getValue("inner", new HashMap(), obj));
-        assertEquals(100, reflectionProvider.getValue("inner.age", new HashMap(), obj));
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        reflectionProvider = new UelReflectionProvider();
-        container.inject(reflectionProvider);
-    }
-}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java
index 10226c1..03b06de 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java
@@ -1,6 +1,30 @@
 package org.apache.struts2.uelplugin;
 
 import com.opensymphony.xwork2.ActionSupport;
+import com.opensymphony.xwork2.conversion.annotations.Conversion;
+import com.opensymphony.xwork2.conversion.annotations.ConversionType;
+import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
 
+@Conversion
 public class TestAction extends ActionSupport {
+    private TestObject object;
+
+    private String converted;
+
+    @TypeConversion(type = ConversionType.APPLICATION, converter = "org.apache.struts2.uelplugin.DummyTypeConverter")
+    public String getConverted() {
+        return converted;
+    }
+
+    public void setConverted(String converted) {
+        this.converted = converted;
+    }
+
+    public TestObject getObject() {
+        return object;
+    }
+
+    public void setObject(TestObject object) {
+        this.object = object;
+    }
 }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java
index 5583f89..e0517fa 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java
@@ -2,6 +2,7 @@
 
 import java.util.Date;
 import java.util.Map;
+import java.util.List;
 
 
 public class TestObject {
@@ -11,6 +12,24 @@
     private TestObject inner;
     private Map parameters;
     private Object object;
+    private List list;
+    private List<TestObject> typedList;
+
+    public List<TestObject> getTypedList() {
+        return typedList;
+    }
+
+    public void setTypedList(List<TestObject> typedList) {
+        this.typedList = typedList;
+    }
+
+    public List getList() {
+        return list;
+    }
+
+    public void setList(List list) {
+        this.list = list;
+    }
 
     public TestObject() {
     }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java
index fd63336..28fabec 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java
@@ -2,7 +2,21 @@
 
 import com.opensymphony.xwork2.ActionContext;
 import com.opensymphony.xwork2.XWorkTestCase;
+import com.opensymphony.xwork2.ActionProxyFactory;
+import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.inject.Scope;
+import com.opensymphony.xwork2.config.ConfigurationManager;
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+import com.opensymphony.xwork2.util.LocalizedTextUtil;
 import com.opensymphony.xwork2.util.CompoundRoot;
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.uelplugin.UelServletContextListener;
@@ -16,49 +30,17 @@
 import java.text.ParseException;
 import java.util.*;
 
-public class UelTest extends XWorkTestCase {
-    private ExpressionFactory factory = ExpressionFactory.newInstance();
-    private XWorkConverter converter;
-    private DateFormat format = DateFormat.getDateInstance();
-    private CompoundRoot root;
-    private UelValueStack stack;
+public class UelTest extends AbstractUelBaseTest {
 
-    private class DateConverter extends StrutsTypeConverter {
+    public void testReadList() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        TestObject obj = new TestObject();
+        root.push(obj);
 
-        @Override
-        public Object convertFromString(Map context, String[] values, Class toClass) {
-            try {
-                return format.parseObject(values[0]);
-            } catch (ParseException e) {
-                return null;
-            }
-        }
-
-        @Override
-        public String convertToString(Map context, Object o) {
-            return format.format(o);
-        }
-
-    }
-
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        converter = container.getInstance(XWorkConverter.class);
-        converter.registerConverter("java.util.Date", new DateConverter());
-        this.root = new CompoundRoot();
-        this.stack = new UelValueStack(converter);
-        stack.setRoot(root);
-        stack.getContext().put(ActionContext.CONTAINER, container);
-
-        MockServletContext servletContext = new MockServletContext();
-        ActionContext context = new ActionContext(stack.getContext());
-        ActionContext.setContext(context);
-        ServletActionContext.setServletContext(servletContext);
-
-        //simulate start up
-        UelServletContextListener listener = new UelServletContextListener();
-        listener.contextInitialized(new ServletContextEvent(servletContext));
+        //list
+        List someList = new ArrayList(3);
+        obj.setObject(someList);
+        someList.add(10);
+        assertEquals(10, stack.findValue("object[0]"));
     }