struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lukaszlen...@apache.org
Subject [37/57] [partial] struts git commit: Merges xwork packages into struts
Date Wed, 17 Jun 2015 21:09:37 GMT
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkEnumerationAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkEnumerationAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkEnumerationAccessor.java
new file mode 100644
index 0000000..84745e4
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkEnumerationAccessor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import ognl.EnumerationPropertyAccessor;
+import ognl.ObjectPropertyAccessor;
+import ognl.OgnlException;
+
+import java.util.Map;
+
+
+/**
+ * @author plightbo
+ */
+public class XWorkEnumerationAccessor extends EnumerationPropertyAccessor {
+
+    ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
+
+    @Override
+    public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
+        opa.setProperty(context, target, name, value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkIteratorPropertyAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkIteratorPropertyAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkIteratorPropertyAccessor.java
new file mode 100644
index 0000000..2a6184b
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkIteratorPropertyAccessor.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import ognl.IteratorPropertyAccessor;
+import ognl.ObjectPropertyAccessor;
+import ognl.OgnlException;
+
+import java.util.Map;
+
+
+/**
+ * @author plightbo
+ */
+public class XWorkIteratorPropertyAccessor extends IteratorPropertyAccessor {
+
+    ObjectPropertyAccessor opa = new ObjectPropertyAccessor();
+
+    @Override
+    public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
+        opa.setProperty(context, target, name, value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java
new file mode 100644
index 0000000..6201dae
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkListPropertyAccessor.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.ognl.OgnlUtil;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import ognl.ListPropertyAccessor;
+import ognl.OgnlException;
+import ognl.PropertyAccessor;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Overrides the list property accessor so in the case of trying
+ * to add properties of a given bean and the JavaBean is not present,
+ * this class will create the necessary blank JavaBeans.
+ *
+ * @author Gabriel Zimmerman
+ */
+public class XWorkListPropertyAccessor extends ListPropertyAccessor {
+
+    private XWorkCollectionPropertyAccessor _sAcc = new XWorkCollectionPropertyAccessor();
+    
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+    private OgnlUtil ognlUtil;
+    
+    @Inject("java.util.Collection")
+    public void setXWorkCollectionPropertyAccessor(PropertyAccessor acc) {
+        this._sAcc = (XWorkCollectionPropertyAccessor) acc;
+    }
+    
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+    
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+    
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+    
+    @Inject
+    public void setOgnlUtil(OgnlUtil util) {
+        this.ognlUtil = util;
+    }
+
+    @Override
+    public Object getProperty(Map context, Object target, Object name) throws OgnlException {
+
+        if (ReflectionContextState.isGettingByKeyProperty(context)
+                || name.equals(XWorkCollectionPropertyAccessor.KEY_PROPERTY_FOR_CREATION)) {
+            return _sAcc.getProperty(context, target, name);
+        } else if (name instanceof String) {
+            return super.getProperty(context, target, name);
+        }
+        ReflectionContextState.updateCurrentPropertyPath(context, name);
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+        
+        if (name instanceof Number
+                && ReflectionContextState.isCreatingNullObjects(context)
+                && objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,true)) {
+
+            List list = (List) target;
+            int index = ((Number) name).intValue();
+            int listSize = list.size();
+
+            if (lastClass == null || lastProperty == null) {
+                return super.getProperty(context, target, name);
+            }
+            Class beanClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
+            if (listSize <= index) {
+                Object result;
+
+                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);
+                }
+                return result;
+            } else if (list.get(index) == null) {
+                Object result;
+                try {
+                    list.set(index, result = objectFactory.buildBean(beanClass, context));
+                } catch (Exception exc) {
+                    throw new XWorkException(exc);
+                }
+                return result;
+            }
+        }
+        return super.getProperty(context, target, name);
+    }
+
+    @Override
+    public void setProperty(Map context, Object target, Object name, Object value)
+            throws OgnlException {
+
+        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, name);
+
+        if (name 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) target;
+            Object[] values = (Object[]) value;
+            for (Object v : values) {
+                try {
+                    Object o = objectFactory.buildBean(convertToClass, context);
+                    ognlUtil.setValue((String) name, context, o, v);
+                    c.add(o);
+                } catch (Exception e) {
+                    throw new OgnlException("Error converting given String values for Collection.", e);
+                }
+            }
+
+            // we don't want to do the normal list property setting now, since we've already done the work
+            // just return instead
+            return;
+        }
+
+        Object realValue = getRealValue(context, value, convertToClass);
+
+        if (target instanceof List && name instanceof Number) {
+            //make sure there are enough spaces in the List to set
+            List list = (List) target;
+            int listSize = list.size();
+            int count = ((Number) name).intValue();
+            if (count >= listSize) {
+                for (int i = listSize; i <= count; i++) {
+                    list.add(null);
+                }
+            }
+        }
+
+        super.setProperty(context, target, name, realValue);
+    }
+
+    private Object getRealValue(Map context, Object value, Class convertToClass) {
+        if (value == null || convertToClass == null) {
+            return value;
+        }
+        return xworkConverter.convertValue(context, value, convertToClass);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java
new file mode 100644
index 0000000..e30f07e
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessor.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import ognl.MapPropertyAccessor;
+import ognl.OgnlException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+
+/**
+ * Implementation of PropertyAccessor that sets and gets properties by storing and looking
+ * up values in Maps.
+ *
+ * @author Gabriel Zimmerman
+ */
+public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
+
+    private static final Logger LOG = LogManager.getLogger(XWorkMapPropertyAccessor.class);
+
+    private static final String[] INDEX_ACCESS_PROPS = new String[]{"size", "isEmpty", "keys", "values"};
+    
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+    
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+    
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+    
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+
+    @Override
+    public Object getProperty(Map context, Object target, Object name) throws OgnlException {
+        LOG.trace("Entering getProperty ({},{},{})", context, target, name);
+
+        ReflectionContextState.updateCurrentPropertyPath(context, name);
+        // if this is one of the regular index access
+        // properties then just let the superclass deal with the
+        // get.
+        if (name instanceof String && contains(INDEX_ACCESS_PROPS, (String) name)) {
+            return super.getProperty(context, target, name);
+        }
+
+        Object result = null;
+
+        try{
+            result = super.getProperty(context, target, name);
+        } catch (ClassCastException ex) {
+        }
+
+        if (result == null) {
+            //find the key class and convert the name to that class
+            Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+
+            String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+            if (lastClass == null || lastProperty == null) {
+                return null;
+            }
+            Object key = getKey(context, name);
+            Map map = (Map) target;
+            result = map.get(key);
+
+            if (result == null &&
+                    Boolean.TRUE.equals(context.get(ReflectionContextState.CREATE_NULL_OBJECTS))
+                    &&  objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,false)) {
+                Class valueClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, key);
+
+                try {
+                    result = objectFactory.buildBean(valueClass, context);
+                    map.put(key, result);
+                } catch (Exception exc) {
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param array
+     * @param name
+     */
+    private boolean contains(String[] array, String name) {
+        for (String anArray : array) {
+            if (anArray.equals(name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
+        LOG.trace("Entering setProperty({},{},{},{})", context, target, name, value);
+
+        Object key = getKey(context, name);
+        Map map = (Map) target;
+        map.put(key, getValue(context, value));
+     }
+
+    private Object getValue(Map context, Object value) {
+         Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+         String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+         if (lastClass == null || lastProperty == null) {
+             return value;
+         }
+         Class elementClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, null);
+         if (elementClass == null) {
+             return value; // nothing is specified, we assume it will be the value passed in.
+         }
+         return xworkConverter.convertValue(context, value, elementClass);
+    }
+
+    private Object getKey(Map context, Object name) {
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+        if (lastClass == null || lastProperty == null) {
+            // return java.lang.String.class;
+            // commented out the above -- it makes absolutely no sense for when setting basic maps!
+            return name;
+        }
+        Class keyClass = objectTypeDeterminer.getKeyClass(lastClass, lastProperty);
+        if (keyClass == null) {
+            keyClass = java.lang.String.class;
+        }
+
+        return xworkConverter.convertValue(context, name, keyClass);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessorTest.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessorTest.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessorTest.java
new file mode 100644
index 0000000..a746c7e
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMapPropertyAccessorTest.java
@@ -0,0 +1,54 @@
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.XWorkTestCase;
+import com.opensymphony.xwork2.util.Element;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+
+import java.util.Collections;
+import java.util.Map;
+
+public class XWorkMapPropertyAccessorTest extends XWorkTestCase {
+    public void testCreateNullObjectsIsFalseByDefault() {
+        ValueStack vs = ActionContext.getContext().getValueStack();
+        vs.push(new MapHolder(Collections.emptyMap()));
+        assertNull(vs.findValue("map[key]"));
+    }
+
+    public void testMapContentsAreReturned() {
+        ValueStack vs = ActionContext.getContext().getValueStack();
+        vs.push(new MapHolder(Collections.singletonMap("key", "value")));
+        assertEquals("value", vs.findValue("map['key']"));
+    }
+
+    public void testNullIsNotReturnedWhenCreateNullObjectsIsSpecified() {
+        ValueStack vs = ActionContext.getContext().getValueStack();
+        vs.push(new MapHolder(Collections.emptyMap()));
+        ReflectionContextState.setCreatingNullObjects(vs.getContext(), true);
+
+        Object value = vs.findValue("map['key']");
+        assertNotNull(value);
+        assertSame(Object.class, value.getClass());
+    }
+
+    public void testNullIsReturnedWhenCreateNullObjectsIsSpecifiedAsFalse() {
+        ValueStack vs = ActionContext.getContext().getValueStack();
+        vs.push(new MapHolder(Collections.emptyMap()));
+        ReflectionContextState.setCreatingNullObjects(vs.getContext(), false);
+        assertNull(vs.findValue("map['key']"));
+    }
+
+    private static class MapHolder {
+        private final Map map;
+
+        public MapHolder(Map m) {
+            this.map = m;
+        }
+
+        @Element(value = Object.class)
+        public Map getMap() {
+            return map;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java
new file mode 100644
index 0000000..7a05bc5
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkMethodAccessor.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import ognl.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.beans.PropertyDescriptor;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+
+/**
+ * Allows methods to be executed under normal cirumstances, except when {@link ReflectionContextState#DENY_METHOD_EXECUTION}
+ * is in the action context with a value of true.
+ *
+ * @author Patrick Lightbody
+ * @author tmjee
+ */
+public class XWorkMethodAccessor extends ObjectMethodAccessor {
+	
+	private static final Logger LOG = LogManager.getLogger(XWorkMethodAccessor.class);
+
+    /**
+     * @deprecated Use {@link ReflectionContextState#DENY_METHOD_EXECUTION} instead
+     */
+    @Deprecated public static final String DENY_METHOD_EXECUTION = ReflectionContextState.DENY_METHOD_EXECUTION;
+    /**
+     * @deprecated Use {@link ReflectionContextState#DENY_INDEXED_ACCESS_EXECUTION} instead
+     */
+    @Deprecated public static final String DENY_INDEXED_ACCESS_EXECUTION = ReflectionContextState.DENY_INDEXED_ACCESS_EXECUTION;
+
+
+    @Override
+    public Object callMethod(Map context, Object object, String string, Object[] objects) throws MethodFailedException {
+
+        //Collection property accessing
+        //this if statement ensures that ognl
+        //statements of the form someBean.mySet('keyPropVal')
+        //return the set element with value of the keyProp given
+
+        if (objects.length == 1 && context instanceof OgnlContext) {
+            try {
+              OgnlContext ogContext=(OgnlContext)context;
+              if (OgnlRuntime.hasSetProperty(ogContext, object, string))  {
+                  	PropertyDescriptor descriptor=OgnlRuntime.getPropertyDescriptor(object.getClass(), string);
+                  	Class propertyType=descriptor.getPropertyType();
+                  	if ((Collection.class).isAssignableFrom(propertyType)) {
+                  	    //go directly through OgnlRuntime here
+                  	    //so that property strings are not cleared
+                  	    //i.e. OgnlUtil should be used initially, OgnlRuntime
+                  	    //thereafter
+                  	    
+                  	    Object propVal=OgnlRuntime.getProperty(ogContext, object, string);
+                  	    //use the Collection property accessor instead of the individual property accessor, because 
+                  	    //in the case of Lists otherwise the index property could be used
+                  	    PropertyAccessor accessor=OgnlRuntime.getPropertyAccessor(Collection.class);
+                  	    ReflectionContextState.setGettingByKeyProperty(ogContext,true);
+                  	    return accessor.getProperty(ogContext,propVal,objects[0]);
+                  	}
+              }
+            }	catch (Exception oe) {
+                //this exception should theoretically never happen
+                //log it
+            	LOG.error("An unexpected exception occurred", oe);
+            }
+
+        }
+
+        //HACK - we pass indexed method access i.e. setXXX(A,B) pattern
+        if ((objects.length == 2 && string.startsWith("set")) || (objects.length == 1 && string.startsWith("get"))) {
+            Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_INDEXED_ACCESS_EXECUTION);
+            boolean e = ((exec == null) ? false : exec.booleanValue());
+            if (!e) {
+                return callMethodWithDebugInfo(context, object, string, objects);
+            }
+        }
+        Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_METHOD_EXECUTION);
+        boolean e = ((exec == null) ? false : exec.booleanValue());
+
+        if (!e) {
+            return callMethodWithDebugInfo(context, object, string, objects);
+        } else {
+            return null;
+        }
+    }
+
+    private Object callMethodWithDebugInfo(Map context, Object object, String methodName, Object[] objects) throws MethodFailedException {
+        try {
+            return super.callMethod(context, object, methodName, objects);
+		}
+		catch(MethodFailedException e) {
+			if (LOG.isDebugEnabled()) {
+				if (!(e.getReason() instanceof NoSuchMethodException)) {
+					// the method exists on the target object, but something went wrong
+                    LOG.debug("Error calling method through OGNL: object: [{}] method: [{}] args: [{}]", e.getReason(), object.toString(), methodName, Arrays.toString(objects));
+                }
+            }
+			throw e;
+		}
+	}
+
+    @Override
+    public Object callStaticMethod(Map context, Class aClass, String string, Object[] objects) throws MethodFailedException {
+        Boolean exec = (Boolean) context.get(ReflectionContextState.DENY_METHOD_EXECUTION);
+        boolean e = ((exec == null) ? false : exec.booleanValue());
+
+        if (!e) {
+            return callStaticMethodWithDebugInfo(context, aClass, string, objects);
+        } else {
+            return null;
+        }
+    }
+
+	private Object callStaticMethodWithDebugInfo(Map context, Class aClass, String methodName,
+			Object[] objects) throws MethodFailedException {
+		try {
+			return super.callStaticMethod(context, aClass, methodName, objects);
+		}
+		catch(MethodFailedException e) {
+			if (LOG.isDebugEnabled()) {
+				if (!(e.getReason() instanceof NoSuchMethodException)) {
+					// the method exists on the target class, but something went wrong
+					LOG.debug("Error calling method through OGNL, class: [{}] method: [{}] args: [{}]", e.getReason(), aClass.getName(), methodName, Arrays.toString(objects));
+				}
+			}
+			throw e;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java
new file mode 100644
index 0000000..5351401
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/accessor/XWorkObjectPropertyAccessor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.ognl.accessor;
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import ognl.ObjectPropertyAccessor;
+import ognl.OgnlException;
+
+import java.util.Map;
+
+/**
+ * @author Gabe
+ */
+public class XWorkObjectPropertyAccessor extends ObjectPropertyAccessor {
+    @Override
+    public Object getProperty(Map context, Object target, Object oname)
+            throws OgnlException {
+        //set the last set objects in the context
+        //so if the next objects accessed are
+        //Maps or Collections they can use the information
+        //to determine conversion types
+        context.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());
+        context.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, oname.toString());
+        ReflectionContextState.updateCurrentPropertyPath(context, oname);
+        return super.getProperty(context, target, oname);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/package.html b/core/src/main/java/com/opensymphony/xwork2/package.html
new file mode 100644
index 0000000..3794154
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/package.html
@@ -0,0 +1 @@
+<body>Main XWork interfaces and classes.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/result/ParamNameAwareResult.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/result/ParamNameAwareResult.java b/core/src/main/java/com/opensymphony/xwork2/result/ParamNameAwareResult.java
new file mode 100644
index 0000000..1067984
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/result/ParamNameAwareResult.java
@@ -0,0 +1,10 @@
+package com.opensymphony.xwork2.result;
+
+/**
+ * Accept parameter name/value to be set on {@link com.opensymphony.xwork2.Result}
+ */
+public interface ParamNameAwareResult {
+
+    boolean acceptableParameterName(String name, String value);
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
new file mode 100644
index 0000000..64592fa
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/security/AcceptedPatternsChecker.java
@@ -0,0 +1,82 @@
+package com.opensymphony.xwork2.security;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * Used across different interceptors to check if given string matches one of the excluded patterns.
+ */
+public interface AcceptedPatternsChecker {
+
+    /**
+     * Checks if value matches any of patterns on exclude list
+     *
+     * @param value to check
+     * @return object containing result of matched pattern and pattern itself
+     */
+    public IsAccepted isAccepted(String value);
+
+    /**
+     * Sets excluded patterns during runtime
+     *
+     * @param commaDelimitedPatterns comma delimited string with patterns
+     */
+    public void setAcceptedPatterns(String commaDelimitedPatterns);
+
+    /**
+     * Set excluded patterns during runtime
+     *
+     * @param patterns array of additional excluded patterns
+     */
+    public void setAcceptedPatterns(String[] patterns);
+
+    /**
+     * Sets excluded patterns during runtime
+     *
+     * @param patterns set of additional patterns
+     */
+    public void setAcceptedPatterns(Set<String> patterns);
+
+    /**
+     * Allow access list of all defined excluded patterns
+     *
+     * @return set of excluded patterns
+     */
+    public Set<Pattern> getAcceptedPatterns();
+
+    public final static class IsAccepted {
+
+        private final boolean accepted;
+        private final String acceptedPattern;
+
+        public static IsAccepted yes(String acceptedPattern) {
+            return new IsAccepted(true, acceptedPattern);
+        }
+
+        public static IsAccepted no(String acceptedPatterns) {
+            return new IsAccepted(false, acceptedPatterns);
+        }
+
+        private IsAccepted(boolean accepted, String acceptedPattern) {
+            this.accepted = accepted;
+            this.acceptedPattern = acceptedPattern;
+        }
+
+        public boolean isAccepted() {
+            return accepted;
+        }
+
+        public String getAcceptedPattern() {
+            return acceptedPattern;
+        }
+
+        @Override
+        public String toString() {
+            return "IsAccepted {" +
+                    "accepted=" + accepted +
+                    ", acceptedPattern=" + acceptedPattern +
+                    " }";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsChecker.java
new file mode 100644
index 0000000..00e9f79
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/security/DefaultAcceptedPatternsChecker.java
@@ -0,0 +1,76 @@
+package com.opensymphony.xwork2.security;
+
+import com.opensymphony.xwork2.XWorkConstants;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class DefaultAcceptedPatternsChecker implements AcceptedPatternsChecker {
+
+    private static final Logger LOG = LogManager.getLogger(DefaultAcceptedPatternsChecker.class);
+
+    public static final String[] ACCEPTED_PATTERNS = {
+            "\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['(\\w|[\\u4e00-\\u9fa5])+'\\])|(\\('(\\w|[\\u4e00-\\u9fa5])+'\\)))*"
+    };
+
+    private Set<Pattern> acceptedPatterns;
+
+    public DefaultAcceptedPatternsChecker() {
+        setAcceptedPatterns(ACCEPTED_PATTERNS);
+    }
+
+    @Inject(value = XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, required = false)
+    public void setOverrideAcceptedPatterns(String acceptablePatterns) {
+        LOG.warn("Overriding accepted patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
+                    XWorkConstants.OVERRIDE_ACCEPTED_PATTERNS, acceptablePatterns);
+        acceptedPatterns = new HashSet<>();
+        for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
+            acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
+        }
+    }
+
+    @Inject(value = XWorkConstants.ADDITIONAL_ACCEPTED_PATTERNS, required = false)
+    public void setAdditionalAcceptedPatterns(String acceptablePatterns) {
+        LOG.warn("Adding additional global patterns [{}] to accepted patterns!", acceptablePatterns);
+        for (String pattern : TextParseUtil.commaDelimitedStringToSet(acceptablePatterns)) {
+            acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
+        }
+    }
+
+    public void setAcceptedPatterns(String commaDelimitedPatterns) {
+        setAcceptedPatterns(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns));
+    }
+
+    public void setAcceptedPatterns(String[] additionalPatterns) {
+        setAcceptedPatterns(new HashSet<>(Arrays.asList(additionalPatterns)));
+    }
+
+    public void setAcceptedPatterns(Set<String> patterns) {
+        LOG.trace("Sets accepted patterns [{}]", patterns);
+        acceptedPatterns = new HashSet<>(patterns.size());
+        for (String pattern : patterns) {
+            acceptedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
+        }
+    }
+
+    public IsAccepted isAccepted(String value) {
+        for (Pattern acceptedPattern : acceptedPatterns) {
+            if (acceptedPattern.matcher(value).matches()) {
+                LOG.trace("[{}] matches accepted pattern [{}]", value, acceptedPattern);
+                return IsAccepted.yes(acceptedPattern.toString());
+            }
+        }
+        return IsAccepted.no(acceptedPatterns.toString());
+    }
+
+    public Set<Pattern> getAcceptedPatterns() {
+        return acceptedPatterns;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsChecker.java
new file mode 100644
index 0000000..f6d48cd
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/security/DefaultExcludedPatternsChecker.java
@@ -0,0 +1,77 @@
+package com.opensymphony.xwork2.security;
+
+import com.opensymphony.xwork2.XWorkConstants;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.TextParseUtil;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+public class DefaultExcludedPatternsChecker implements ExcludedPatternsChecker {
+
+    private static final Logger LOG = LogManager.getLogger(DefaultExcludedPatternsChecker.class);
+
+    public static final String[] EXCLUDED_PATTERNS = {
+        "(^|.*#)(dojo|struts|session|request|application|servlet(Request|Response)|parameters|context|_memberAccess)(\\.|\\[).*",
+        "^(action|method):.*"
+    };
+
+    private Set<Pattern> excludedPatterns;
+
+    public DefaultExcludedPatternsChecker() {
+        setExcludedPatterns(EXCLUDED_PATTERNS);
+    }
+
+    @Inject(value = XWorkConstants.OVERRIDE_EXCLUDED_PATTERNS, required = false)
+    public void setOverrideExcludePatterns(String excludePatterns) {
+        LOG.warn("Overriding excluded patterns [{}] with [{}], be aware that this affects all instances and safety of your application!",
+                    XWorkConstants.OVERRIDE_EXCLUDED_PATTERNS, excludePatterns);
+        excludedPatterns = new HashSet<Pattern>();
+        for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
+            excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
+        }
+    }
+
+    @Inject(value = XWorkConstants.ADDITIONAL_EXCLUDED_PATTERNS, required = false)
+    public void setAdditionalExcludePatterns(String excludePatterns) {
+        LOG.debug("Adding additional global patterns [{}] to excluded patterns!", excludePatterns);
+        for (String pattern : TextParseUtil.commaDelimitedStringToSet(excludePatterns)) {
+            excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
+        }
+    }
+
+    public void setExcludedPatterns(String commaDelimitedPatterns) {
+        setExcludedPatterns(TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns));
+    }
+
+    public void setExcludedPatterns(String[] patterns) {
+        setExcludedPatterns(new HashSet<>(Arrays.asList(patterns)));
+    }
+
+    public void setExcludedPatterns(Set<String> patterns) {
+        LOG.trace("Sets excluded patterns [{}]", patterns);
+        excludedPatterns = new HashSet<>(patterns.size());
+        for (String pattern : patterns) {
+            excludedPatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
+        }
+    }
+
+    public IsExcluded isExcluded(String value) {
+        for (Pattern excludedPattern : excludedPatterns) {
+            if (excludedPattern.matcher(value).matches()) {
+                LOG.trace("[{}] matches excluded pattern [{}]", value, excludedPattern);
+                return IsExcluded.yes(excludedPattern);
+            }
+        }
+        return IsExcluded.no(excludedPatterns);
+    }
+
+    public Set<Pattern> getExcludedPatterns() {
+        return excludedPatterns;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java b/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java
new file mode 100644
index 0000000..5a3bc07
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/security/ExcludedPatternsChecker.java
@@ -0,0 +1,82 @@
+package com.opensymphony.xwork2.security;
+
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * Used across different interceptors to check if given string matches one of the excluded patterns.
+ */
+public interface ExcludedPatternsChecker {
+
+    /**
+     * Checks if value matches any of patterns on exclude list
+     *
+     * @param value to check
+     * @return object containing result of matched pattern and pattern itself
+     */
+    public IsExcluded isExcluded(String value);
+
+    /**
+     * Sets excluded patterns during runtime
+     *
+     * @param commaDelimitedPatterns comma delimited string with patterns
+     */
+    public void setExcludedPatterns(String commaDelimitedPatterns);
+
+    /**
+     * Sets excluded patterns during runtime
+     *
+     * @param patterns array of additional excluded patterns
+     */
+    public void setExcludedPatterns(String[] patterns);
+
+    /**
+     * Sets excluded patterns during runtime
+     *
+     * @param patterns set of additional patterns
+     */
+    public void setExcludedPatterns(Set<String> patterns);
+
+    /**
+     * Allow access list of all defined excluded patterns
+     *
+     * @return set of excluded patterns
+     */
+    public Set<Pattern> getExcludedPatterns();
+
+    public final static class IsExcluded {
+
+        private final boolean excluded;
+        private final String excludedPattern;
+
+        public static IsExcluded yes(Pattern excludedPattern) {
+            return new IsExcluded(true, excludedPattern.pattern());
+        }
+
+        public static IsExcluded no(Set<Pattern> excludedPatterns) {
+            return new IsExcluded(false, excludedPatterns.toString());
+        }
+
+        private IsExcluded(boolean excluded, String excludedPattern) {
+            this.excluded = excluded;
+            this.excludedPattern = excludedPattern;
+        }
+
+        public boolean isExcluded() {
+            return excluded;
+        }
+
+        public String getExcludedPattern() {
+            return excludedPattern;
+        }
+
+        @Override
+        public String toString() {
+            return "IsExcluded { " +
+                    "excluded=" + excluded +
+                    ", excludedPattern=" + excludedPattern +
+                    " }";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java b/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java
new file mode 100644
index 0000000..f1137d9
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/spring/SpringObjectFactory.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.spring;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.inject.Inject;
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.UnsatisfiedDependencyException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple implementation of the ObjectFactory that makes use of Spring's application context if one has been configured,
+ * before falling back on the default mechanism of instantiating a new class using the class name. <p/> In order to use
+ * this class in your application, you will need to instantiate a copy of this class and set it as XWork's ObjectFactory
+ * before the xwork.xml file is parsed. In a servlet environment, this could be done using a ServletContextListener.
+ *
+ * @author Simon Stewart (sms@lateral.net)
+ */
+public class SpringObjectFactory extends ObjectFactory implements ApplicationContextAware {
+    private static final Logger LOG = LogManager.getLogger(SpringObjectFactory.class);
+
+    protected ApplicationContext appContext;
+    protected AutowireCapableBeanFactory autoWiringFactory;
+    protected int autowireStrategy = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
+    private final Map<String, Object> classes = new HashMap<>();
+    private boolean useClassCache = true;
+    private boolean alwaysRespectAutowireStrategy = false;
+    /**
+     * This is temporary solution, after validating can be removed
+     * @since 2.3.18
+     */
+    private boolean enableAopSupport = false;
+
+    @Inject(value="applicationContextPath",required=false)
+    public void setApplicationContextPath(String ctx) {
+        if (ctx != null) {
+            setApplicationContext(new ClassPathXmlApplicationContext(ctx));
+        }
+    }
+
+    @Inject(value = "enableAopSupport", required = false)
+    public void setEnableAopSupport(String enableAopSupport) {
+        this.enableAopSupport = BooleanUtils.toBoolean(enableAopSupport);
+    }
+
+    /**
+     * Set the Spring ApplicationContext that should be used to look beans up with.
+     *
+     * @param appContext The Spring ApplicationContext that should be used to look beans up with.
+     */
+    public void setApplicationContext(ApplicationContext appContext) throws BeansException {
+        this.appContext = appContext;
+        autoWiringFactory = findAutoWiringBeanFactory(this.appContext);
+    }
+
+    /**
+     * Sets the autowiring strategy
+     *
+     * @param autowireStrategy
+     */
+    public void setAutowireStrategy(int autowireStrategy) {
+        switch (autowireStrategy) {
+            case AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT:
+                LOG.info("Setting autowire strategy to autodetect");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_BY_NAME:
+                LOG.info("Setting autowire strategy to name");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE:
+                LOG.info("Setting autowire strategy to type");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:
+                LOG.info("Setting autowire strategy to constructor");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            case AutowireCapableBeanFactory.AUTOWIRE_NO:
+                LOG.info("Setting autowire strategy to none");
+                this.autowireStrategy = autowireStrategy;
+                break;
+            default:
+                throw new IllegalStateException("Invalid autowire type set");
+        }
+    }
+
+    public int getAutowireStrategy() {
+        return autowireStrategy;
+    }
+
+
+    /**
+     * If the given context is assignable to AutowireCapbleBeanFactory or contains a parent or a factory that is, then
+     * set the autoWiringFactory appropriately.
+     *
+     * @param context
+     */
+    protected AutowireCapableBeanFactory findAutoWiringBeanFactory(ApplicationContext context) {
+        if (context instanceof AutowireCapableBeanFactory) {
+            // Check the context
+            return (AutowireCapableBeanFactory) context;
+        } else if (context instanceof ConfigurableApplicationContext) {
+            // Try and grab the beanFactory
+            return ((ConfigurableApplicationContext) context).getBeanFactory();
+        } else if (context.getParent() != null) {
+            // And if all else fails, try again with the parent context
+            return findAutoWiringBeanFactory(context.getParent());
+        }
+        return null;
+    }
+
+    /**
+     * Looks up beans using Spring's application context before falling back to the method defined in the {@link
+     * ObjectFactory}.
+     *
+     * @param beanName     The name of the bean to look up in the application context
+     * @param extraContext
+     * @return A bean from Spring or the result of calling the overridden
+     *         method.
+     * @throws Exception
+     */
+    @Override
+    public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
+        Object o;
+        
+        if (appContext.containsBean(beanName)) {
+            o = appContext.getBean(beanName);
+        } else {
+            Class beanClazz = getClassInstance(beanName);
+            o = buildBean(beanClazz, extraContext);
+        }
+        if (injectInternal) {
+            injectInternalBeans(o);
+        }
+        return o;
+    }
+
+    /**
+     * @param clazz
+     * @param extraContext
+     * @throws Exception
+     */
+    @Override
+    public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
+        Object bean;
+
+        try {
+            // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies
+            if (alwaysRespectAutowireStrategy) {
+                // Leave the creation up to Spring
+                bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);
+                injectApplicationContext(bean);
+                return injectInternalBeans(bean);
+            } else if (enableAopSupport) {
+                bean = autoWiringFactory.createBean(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
+                bean = autoWireBean(bean, autoWiringFactory);
+                bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
+                return bean;
+            } else {
+                bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
+                bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());
+                bean = autoWiringFactory.initializeBean(bean, bean.getClass().getName());
+                bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());
+                return autoWireBean(bean, autoWiringFactory);
+            }
+        } catch (UnsatisfiedDependencyException e) {
+            LOG.error("Error building bean", e);
+            // Fall back
+            return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);
+        }
+    }
+
+    public Object autoWireBean(Object bean) {
+        return autoWireBean(bean, autoWiringFactory);
+    }
+
+    /**
+     * @param bean
+     * @param autoWiringFactory
+     */
+    public Object autoWireBean(Object bean, AutowireCapableBeanFactory autoWiringFactory) {
+        if (autoWiringFactory != null) {
+            autoWiringFactory.autowireBeanProperties(bean, autowireStrategy, false);
+        }
+        injectApplicationContext(bean);
+
+        injectInternalBeans(bean);
+
+        return bean;
+    }
+
+    private void injectApplicationContext(Object bean) {
+        if (bean instanceof ApplicationContextAware) {
+            ((ApplicationContextAware) bean).setApplicationContext(appContext);
+        }
+    }
+
+    public Class getClassInstance(String className) throws ClassNotFoundException {
+        Class clazz = null;
+        if (useClassCache) {
+            synchronized(classes) {
+                // this cache of classes is needed because Spring sucks at dealing with situations where the
+                // class instance changes
+                clazz = (Class) classes.get(className);
+            }
+        }
+
+        if (clazz == null) {
+            if (appContext.containsBean(className)) {
+                clazz = appContext.getBean(className).getClass();
+            } else {
+                clazz = super.getClassInstance(className);
+            }
+
+            if (useClassCache) {
+                synchronized(classes) {
+                    classes.put(className, clazz);
+                }
+            }
+        }
+
+        return clazz;
+    }
+
+    /**
+     * This method sets the ObjectFactory used by XWork to this object. It's best used as the "init-method" of a Spring
+     * bean definition in order to hook Spring and XWork together properly (as an alternative to the
+     * org.apache.struts2.spring.lifecycle.SpringObjectFactoryListener)
+     * @deprecated Since 2.1 as it isn't necessary
+     */
+    @Deprecated public void initObjectFactory() {
+        // not necessary anymore
+    }
+
+    /**
+     * Allows for ObjectFactory implementations that support
+     * Actions without no-arg constructors.
+     *
+     * @return false
+     */
+    @Override
+    public boolean isNoArgConstructorRequired() {
+        return false;
+    }
+
+    /**
+     *  Enable / disable caching of classes loaded by Spring.
+     *
+     * @param useClassCache
+     */
+    public void setUseClassCache(boolean useClassCache) {
+        this.useClassCache = useClassCache;
+    }
+
+    /**
+     * Determines if the autowire strategy is always followed when creating beans
+     *
+     * @param alwaysRespectAutowireStrategy True if the strategy is always used
+     */
+    public void setAlwaysRespectAutowireStrategy(boolean alwaysRespectAutowireStrategy) {
+        this.alwaysRespectAutowireStrategy = alwaysRespectAutowireStrategy;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java b/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java
new file mode 100644
index 0000000..f249f5f
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/spring/SpringProxyableObjectFactory.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.spring;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.context.ApplicationContext;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SpringProxyableObjectFactory.
+ *
+ * @author Jason Carreira
+ */
+public class SpringProxyableObjectFactory extends SpringObjectFactory {
+    
+    private static final Logger LOG = LogManager.getLogger(SpringProxyableObjectFactory.class);
+
+    private List<String> skipBeanNames = new ArrayList<>();
+
+    @Override
+    public Object buildBean(String beanName, Map<String, Object> extraContext) throws Exception {
+        LOG.debug("Building bean for name {}", beanName);
+        if (!skipBeanNames.contains(beanName)) {
+            ApplicationContext anAppContext = getApplicationContext(extraContext);
+            try {
+                LOG.debug("Trying the application context... appContext = {},\n bean name = {}", anAppContext, beanName);
+                return anAppContext.getBean(beanName);
+            } catch (NoSuchBeanDefinitionException e) {
+                LOG.debug("Did not find bean definition for bean named {}, creating a new one...", beanName);
+                if (autoWiringFactory instanceof BeanDefinitionRegistry) {
+                    try {
+                        Class clazz = Class.forName(beanName);
+                        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) autoWiringFactory;
+                        RootBeanDefinition def = new RootBeanDefinition(clazz, autowireStrategy, true);
+                        def.setScope(BeanDefinition.SCOPE_SINGLETON);
+                        LOG.debug("Registering a new bean definition for class {}", beanName);
+                        registry.registerBeanDefinition(beanName,def);
+                        try {
+                            return anAppContext.getBean(beanName);
+                        } catch (NoSuchBeanDefinitionException e2) {
+                            LOG.warn("Could not register new bean definition for bean {}", beanName);
+                            skipBeanNames.add(beanName);
+                        }
+                    } catch (ClassNotFoundException e1) {
+                        skipBeanNames.add(beanName);
+                    }
+                }
+            }
+        }
+        LOG.debug("Returning autowired instance created by default ObjectFactory");
+        return autoWireBean(super.buildBean(beanName, extraContext), autoWiringFactory);
+    }
+
+    /**
+     * Subclasses may override this to return a different application context.
+     * Note that this application context should see any changes made to the
+     * <code>autoWiringFactory</code>, so the application context should be either
+     * the original or a child context of the original.
+     *
+     * @param context  provided context.
+     */
+    protected ApplicationContext getApplicationContext(Map<String, Object> context) {
+        return appContext;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
new file mode 100644
index 0000000..fe02ca5
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/ActionAutowiringInterceptor.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.xwork2.spring.interceptor;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
+import com.opensymphony.xwork2.spring.SpringObjectFactory;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.web.context.WebApplicationContext;
+
+/**
+ * <!-- START SNIPPET: description -->
+ * TODO: Give a description of the Interceptor.
+ * <!-- END SNIPPET: description -->
+ *
+ * <!-- START SNIPPET: parameters -->
+ * TODO: Describe the paramters for this Interceptor.
+ * <!-- END SNIPPET: parameters -->
+ *
+ * <!-- START SNIPPET: extending -->
+ * TODO: Discuss some possible extension of the Interceptor.
+ * <!-- END SNIPPET: extending -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: example -->
+ * &lt;!-- TODO: Describe how the Interceptor reference will effect execution --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *      TODO: fill in the interceptor reference.
+ *     &lt;interceptor-ref name=""/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ * <!-- END SNIPPET: example -->
+ * </pre>
+ * 
+ * Autowires action classes to Spring beans.  The strategy for autowiring the beans can be configured
+ * by setting the parameter on the interceptor.  Actions that need access to the <code>ActionContext</code>
+ * can implements the <code>ApplicationContextAware</code> interface.  The context will also be placed on
+ * the action context under the APPLICATION_CONTEXT attribute.
+ *
+ * @author Simon Stewart
+ * @author Eric Hauser
+ */
+public class ActionAutowiringInterceptor extends AbstractInterceptor implements ApplicationContextAware {
+    private static final Logger LOG = LogManager.getLogger(ActionAutowiringInterceptor.class);
+
+    public static final String APPLICATION_CONTEXT = "com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor.applicationContext";
+
+    private boolean initialized = false;
+    private ApplicationContext context;
+    private SpringObjectFactory factory;
+    private Integer autowireStrategy;
+
+    /**
+     * @param autowireStrategy
+     */
+    public void setAutowireStrategy(Integer autowireStrategy) {
+        this.autowireStrategy = autowireStrategy;
+    }
+
+    /**
+     * Looks for the <code>ApplicationContext</code> under the attribute that the Spring listener sets in
+     * the servlet context.  The configuration is done the first time here instead of in init() since the
+     * <code>ActionContext</code> is not available during <code>Interceptor</code> initialization.
+     * <p/>
+     * Autowires the action to Spring beans and places the <code>ApplicationContext</code>
+     * on the <code>ActionContext</code>
+     * <p/>
+     * TODO Should this check to see if the <code>SpringObjectFactory</code> has already been configured
+     * instead of instantiating a new one?  Or is there a good reason for the interceptor to have it's own
+     * factory?
+     *
+     * @param invocation
+     * @throws Exception
+     */
+    @Override public String intercept(ActionInvocation invocation) throws Exception {
+        if (!initialized) {
+            ApplicationContext applicationContext = (ApplicationContext) ActionContext.getContext().getApplication().get(
+                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
+
+            if (applicationContext == null) {
+                LOG.warn("ApplicationContext could not be found.  Action classes will not be autowired.");
+            } else {
+                setApplicationContext(applicationContext);
+                factory = new SpringObjectFactory();
+                factory.setApplicationContext(getApplicationContext());
+                if (autowireStrategy != null) {
+                    factory.setAutowireStrategy(autowireStrategy.intValue());
+                }
+            }
+            initialized = true;
+        }
+
+        if (factory != null) {
+            Object bean = invocation.getAction();
+            factory.autoWireBean(bean);
+    
+            ActionContext.getContext().put(APPLICATION_CONTEXT, context);
+        }
+        return invocation.invoke();
+    }
+
+    /**
+     * @param applicationContext
+     * @throws BeansException
+     */
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        context = applicationContext;
+    }
+
+    /**
+     * @return context
+     */
+    protected ApplicationContext getApplicationContext() {
+        return context;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html
new file mode 100644
index 0000000..6579e80
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/spring/interceptor/package.html
@@ -0,0 +1 @@
+<body>Spring specific interceptor classes.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/spring/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/spring/package.html b/core/src/main/java/com/opensymphony/xwork2/spring/package.html
new file mode 100644
index 0000000..91d2d95
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/spring/package.html
@@ -0,0 +1 @@
+<body>Spring ObjectFactory classes.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java
new file mode 100644
index 0000000..309db06
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/test/StubConfigurationProvider.java
@@ -0,0 +1,36 @@
+package com.opensymphony.xwork2.test;
+
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.inject.ContainerBuilder;
+import com.opensymphony.xwork2.util.location.LocatableProperties;
+
+public class StubConfigurationProvider implements ConfigurationProvider {
+
+    public void destroy() {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void init(Configuration configuration) throws ConfigurationException {
+        // TODO Auto-generated method stub
+    }
+
+    public void loadPackages() throws ConfigurationException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public boolean needsReload() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public void register(ContainerBuilder builder, LocatableProperties props)
+            throws ConfigurationException {
+        // TODO Auto-generated method stub
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
new file mode 100644
index 0000000..08cae03
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <code>AnnotationUtils</code>
+ *
+ * Various utility methods dealing with annotations
+ *
+ * @author Rainer Hermanns
+ * @author Zsolt Szasz, zsolt at lorecraft dot com
+ * @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m
+ * @version $Id$
+ */
+public class AnnotationUtils {
+
+    private static final Pattern SETTER_PATTERN = Pattern.compile("set([A-Z][A-Za-z0-9]*)$");
+    private static final Pattern GETTER_PATTERN = Pattern.compile("(get|is|has)([A-Z][A-Za-z0-9]*)$");
+
+
+
+    /**
+     * Adds all fields with the specified Annotation of class clazz and its superclasses to allFields
+     *
+     * @param annotationClass
+     * @param clazz
+     * @param allFields
+     */
+    public static void addAllFields(Class<? extends Annotation> annotationClass, Class clazz, List<Field> allFields) {
+
+        if (clazz == null) {
+            return;
+        }
+
+        Field[] fields = clazz.getDeclaredFields();
+
+        for (Field field : fields) {
+            Annotation ann = field.getAnnotation(annotationClass);
+            if (ann!=null) {
+                allFields.add(field);
+            }
+        }
+        addAllFields(annotationClass, clazz.getSuperclass(), allFields);
+    }
+
+    /**
+     * Adds all methods with the specified Annotation of class clazz and its superclasses to allFields
+     *
+     * @param annotationClass
+     * @param clazz
+     * @param allMethods
+     */
+    public static void addAllMethods(Class<? extends Annotation> annotationClass, Class clazz, List<Method> allMethods) {
+
+        if (clazz == null) {
+            return;
+        }
+
+        Method[] methods = clazz.getDeclaredMethods();
+
+        for (Method method : methods) {
+            Annotation ann = method.getAnnotation(annotationClass);
+            if (ann!=null) {
+                allMethods.add(method);
+            }
+        }
+        addAllMethods(annotationClass, clazz.getSuperclass(), allMethods);
+    }
+
+    /**
+     *
+     * @param clazz
+     * @param allInterfaces
+     */
+    public static void addAllInterfaces(Class clazz, List<Class> allInterfaces) {
+        if (clazz == null) {
+            return;
+        }
+
+        Class[] interfaces = clazz.getInterfaces();
+        allInterfaces.addAll(Arrays.asList(interfaces));
+        addAllInterfaces(clazz.getSuperclass(), allInterfaces);
+    }
+
+	/**
+	 * For the given <code>Class</code> get a collection of the the {@link AnnotatedElement}s 
+	 * that match the given <code>annotation</code>s or if no <code>annotation</code>s are 
+	 * specified then return all of the annotated elements of the given <code>Class</code>. 
+	 * Includes only the method level annotations.
+	 * 
+	 * @param clazz The {@link Class} to inspect
+	 * @param annotation the {@link Annotation}s to find
+	 * @return A {@link Collection}&lt;{@link AnnotatedElement}&gt; containing all of the
+	 *  method {@link AnnotatedElement}s matching the specified {@link Annotation}s
+	 */
+	public static Collection<Method> getAnnotatedMethods(Class clazz, Class<? extends Annotation>... annotation){
+        Collection<Method> toReturn = new HashSet<>();
+
+        for (Method m : clazz.getMethods()) {
+            if (org.apache.commons.lang3.ArrayUtils.isNotEmpty(annotation) && isAnnotatedBy(m, annotation)) {
+                toReturn.add(m);
+            } else if (org.apache.commons.lang3.ArrayUtils.isEmpty(annotation) && org.apache.commons.lang3.ArrayUtils.isNotEmpty(m.getAnnotations())) {
+                toReturn.add(m);
+            }
+		}
+		
+		return toReturn;
+	}
+
+	/**
+	 * Varargs version of <code>AnnotatedElement.isAnnotationPresent()</code>
+	 * @see AnnotatedElement
+	 */
+	public static boolean isAnnotatedBy(AnnotatedElement annotatedElement, Class<? extends Annotation>... annotation) {
+        if (org.apache.commons.lang3.ArrayUtils.isEmpty(annotation)) {
+            return false;
+        }
+
+		for( Class<? extends Annotation> c : annotation ){
+			if( annotatedElement.isAnnotationPresent(c) ) return true;
+		}
+
+		return false;
+	}
+
+    /**
+     * Returns the property name for a method.
+     * This method is independent from property fields.
+     *
+     * @param method The method to get the property name for.
+     * @return the property name for given method; null if non could be resolved.
+     */
+    public static String resolvePropertyName(Method method) {
+
+        Matcher matcher = SETTER_PATTERN.matcher(method.getName());
+        if (matcher.matches() && method.getParameterTypes().length == 1) {
+            String raw = matcher.group(1);
+            return raw.substring(0, 1).toLowerCase() + raw.substring(1);
+        }
+
+        matcher = GETTER_PATTERN.matcher(method.getName());
+        if (matcher.matches() && method.getParameterTypes().length == 0) {
+            String raw = matcher.group(2);
+            return raw.substring(0, 1).toLowerCase() + raw.substring(1);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the annotation on the given class or the package of the class. This searchs up the
+     * class hierarchy and the package hierarchy for the closest match.
+     *
+     * @param   clazz The class to search for the annotation.
+     * @param   annotationClass The Class of the annotation.
+     * @return  The annotation or null.
+     */
+    public static <T extends Annotation> T findAnnotation(Class<?> clazz, Class<T> annotationClass) {
+        T ann = clazz.getAnnotation(annotationClass);
+        while (ann == null && clazz != null) {
+            ann = clazz.getAnnotation(annotationClass);
+            if (ann == null) {
+                ann = clazz.getPackage().getAnnotation(annotationClass);
+            }
+            if (ann == null) {
+                clazz = clazz.getSuperclass();
+                if (clazz != null) {
+                    ann = clazz.getAnnotation(annotationClass);
+                }
+            }
+        }
+
+        return ann;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java b/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java
new file mode 100644
index 0000000..99f4ab6
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/ArrayUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2006,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m
+ * @deprecated Can be replaced eith ArrayUtils from lang3 package --> org.apache.commons.lang3.ArrayUtils
+ */
+@Deprecated
+public class ArrayUtils {
+
+    public static boolean isEmpty(Object[] array) {
+        return null == array || array.length == 0;
+    }
+
+    public static boolean isNotEmpty(Object[] array) {
+        return !isEmpty(array);
+    }
+
+    /**
+     * Return a collection from the comma delimited String.
+     *
+     * @param commaDelim the comma delimited String.
+     * @return A collection from the comma delimited String. Returns <tt>null</tt> if the string is empty.
+     */
+    public static Collection<String> asCollection(String commaDelim) {
+        if (commaDelim == null || commaDelim.trim().length() == 0) {
+            return null;
+        }
+        return TextParseUtil.commaDelimitedStringToSet(commaDelim);
+    }
+
+    public static <T> Set<T> asSet(T... element) {
+        HashSet<T> elements = new HashSet<T>(element.length);
+        Collections.addAll(elements, element);
+        return elements;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java b/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java
new file mode 100644
index 0000000..919f5af
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2002-2003,2009 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.opensymphony.xwork2.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+
+/**
+ * This class is extremely useful for loading resources and classes in a fault tolerant manner
+ * that works across different applications servers.
+ * <p/>
+ * It has come out of many months of frustrating use of multiple application servers at Atlassian,
+ * please don't change things unless you're sure they're not going to break in one server or another!
+ *
+ */
+public class ClassLoaderUtil {
+
+    /**
+     * Load all resources with a given name, potentially aggregating all results 
+     * from the searched classloaders.  If no results are found, the resource name
+     * is prepended by '/' and tried again.
+     *
+     * This method will try to load the resources using the following methods (in order):
+     * <ul>
+     *  <li>From Thread.currentThread().getContextClassLoader()
+     *  <li>From ClassLoaderUtil.class.getClassLoader()
+     *  <li>callingClass.getClassLoader()
+     * </ul>
+     *
+     * @param resourceName The name of the resources to load
+     * @param callingClass The Class object of the calling object
+     */
+     public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
+
+         AggregateIterator<URL> iterator = new AggregateIterator<>();
+
+         iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
+
+         if (!iterator.hasNext() || aggregate) {
+             iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
+         }
+
+         if (!iterator.hasNext() || aggregate) {
+             ClassLoader cl = callingClass.getClassLoader();
+
+             if (cl != null) {
+                 iterator.addEnumeration(cl.getResources(resourceName));
+             }
+         }
+
+         if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 
+             return getResources('/' + resourceName, callingClass, aggregate);
+         }
+
+         return iterator;
+     }
+
+    /**
+     * Load a given resource.
+     * <p/>
+     * This method will try to load the resource using the following methods (in order):
+     * <ul>
+     * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
+     * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
+     * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
+     * </ul>
+     *
+     * @param resourceName The name of the resource to load
+     * @param callingClass The Class object of the calling object
+     */
+    public static URL getResource(String resourceName, Class callingClass) {
+        URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
+
+        if (url == null) {
+            url = ClassLoaderUtil.class.getClassLoader().getResource(resourceName);
+        }
+
+        if (url == null) {
+            ClassLoader cl = callingClass.getClassLoader();
+
+            if (cl != null) {
+                url = cl.getResource(resourceName);
+            }
+        }
+
+        if ((url == null) && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) { 
+            return getResource('/' + resourceName, callingClass);
+        }
+
+        return url;
+    }
+
+    /**
+    * This is a convenience method to load a resource as a stream.
+    *
+    * The algorithm used to find the resource is given in getResource()
+    *
+    * @param resourceName The name of the resource to load
+    * @param callingClass The Class object of the calling object
+    */
+    public static InputStream getResourceAsStream(String resourceName, Class callingClass) {
+        URL url = getResource(resourceName, callingClass);
+
+        try {
+            return (url != null) ? url.openStream() : null;
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Load a class with a given name.
+     * <p/>
+     * It will try to load the class in the following order:
+     * <ul>
+     * <li>From {@link Thread#getContextClassLoader() Thread.currentThread().getContextClassLoader()}
+     * <li>Using the basic {@link Class#forName(java.lang.String) }
+     * <li>From {@link Class#getClassLoader() ClassLoaderUtil.class.getClassLoader()}
+     * <li>From the {@link Class#getClassLoader() callingClass.getClassLoader() }
+     * </ul>
+     *
+     * @param className    The name of the class to load
+     * @param callingClass The Class object of the calling object
+     * @throws ClassNotFoundException If the class cannot be found anywhere.
+     */
+    public static Class loadClass(String className, Class callingClass) throws ClassNotFoundException {
+        try {
+            return Thread.currentThread().getContextClassLoader().loadClass(className);
+        } catch (ClassNotFoundException e) {
+            try {
+                return Class.forName(className);
+            } catch (ClassNotFoundException ex) {
+                try {
+                    return ClassLoaderUtil.class.getClassLoader().loadClass(className);
+                } catch (ClassNotFoundException exc) {
+                    return callingClass.getClassLoader().loadClass(className);
+                }
+            }
+        }
+    }
+
+    /**
+     * Prints the current classloader hierarchy - useful for debugging.
+     */
+    public static void printClassLoader() {
+        System.out.println("ClassLoaderUtils.printClassLoader");
+        printClassLoader(Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * Prints the classloader hierarchy from a given classloader - useful for debugging.
+     */
+    public static void printClassLoader(ClassLoader cl) {
+        System.out.println("ClassLoaderUtils.printClassLoader(cl = " + cl + ")");
+
+        if (cl != null) {
+            printClassLoader(cl.getParent());
+        }
+    }
+
+
+
+    /**
+     * Aggregates Enumeration instances into one iterator and filters out duplicates.  Always keeps one
+     * ahead of the enumerator to protect against returning duplicates.
+     */
+    static class AggregateIterator<E> implements Iterator<E> {
+
+        LinkedList<Enumeration<E>> enums = new LinkedList<>();
+        Enumeration<E> cur = null;
+        E next = null;
+        Set<E> loaded = new HashSet<E>();
+
+        public AggregateIterator<E> addEnumeration(Enumeration<E> e) {
+            if (e.hasMoreElements()) {
+                if (cur == null) {
+                    cur = e;
+                    next = e.nextElement();
+                    loaded.add(next);
+                } else {
+                    enums.add(e);
+                }
+            }
+            return this;
+        }
+
+        public boolean hasNext() {
+            return (next != null);
+        }
+
+        public E next() {
+            if (next != null) {
+                E prev = next;
+                next = loadNext();
+                return prev;
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        private Enumeration<E> determineCurrentEnumeration() {
+            if (cur != null && !cur.hasMoreElements()) {
+                if (enums.size() > 0) {
+                    cur = enums.removeLast();
+                } else {
+                    cur = null;
+                }
+            }
+            return cur;
+        }
+
+        private E loadNext() {
+            if (determineCurrentEnumeration() != null) {
+                E tmp = cur.nextElement();
+                int loadedSize = loaded.size();
+                while (loaded.contains(tmp)) {
+                    tmp = loadNext();
+                    if (tmp == null || loaded.size() > loadedSize) {
+                        break;
+                    }
+                }
+                if (tmp != null) {
+                    loaded.add(tmp);
+                }
+                return tmp;
+            }
+            return null;
+
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}


Mime
View raw message