You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2015/06/17 23:09:37 UTC
[37/57] [partial] struts git commit: Merges xwork packages into struts
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 -->
+ * <!-- TODO: Describe how the Interceptor reference will effect execution -->
+ * <action name="someAction" class="com.examples.SomeAction">
+ * TODO: fill in the interceptor reference.
+ * <interceptor-ref name=""/>
+ * <result name="success">good_result.ftl</result>
+ * </action>
+ * <!-- 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}<{@link AnnotatedElement}> 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();
+ }
+ }
+}