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:31 UTC

[31/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/util/profiling/UtilTimerStack.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java b/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java
new file mode 100644
index 0000000..7fb7ae2
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 2002-2003, Atlassian Software Systems Pty Ltd All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *     * Neither the name of Atlassian Software Systems Pty Ltd nor the names of
+ * its contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.opensymphony.xwork2.util.profiling;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+
+/**
+ * A timer stack.
+ * <p/>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: profilingAspect_struts2 -->
+ * <p/>
+ * Struts2 profiling aspects involves the following :-
+ * <ul>
+ * <li>ActionContextCleanUp</li>
+ * <li>FreemarkerPageFilter</li>
+ * <li>DispatcherFilter</li>
+ * <ul>
+ * <li>Dispatcher</li>
+ * <ul>
+ * <li>creation of DefaultActionProxy</li>
+ * <ul>
+ * <li>creation of DefaultActionInvocation</li>
+ * <ul>
+ * <li>creation of Action</li>
+ * </ul>
+ * </ul>
+ * <li>execution of DefaultActionProxy</li>
+ * <ul>
+ * <li>invocation of DefaultActionInvocation</li>
+ * <ul>
+ * <li>invocation of Interceptors</li>
+ * <li>invocation of Action</li>
+ * <li>invocation of PreResultListener</li>
+ * <li>invocation of Result</li>
+ * </ul>
+ * </ul>
+ * </ul>
+ * </ul>
+ * </ul>
+ * <p/>
+ * <!-- END SNIPPET: profilingAspect_struts2 -->
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: profilingAspect_xwork -->
+ * <p/>
+ * XWork2 profiling aspects involves the following :-
+ * <ul>
+ * <ul>
+ * <li>creation of DefaultActionProxy</li>
+ * <ul>
+ * <li>creation of DefaultActionInvocation</li>
+ * <ul>
+ * <li>creation of Action</li>
+ * </ul>
+ * </ul>
+ * <li>execution of DefaultActionProxy</li>
+ * <ul>
+ * <li>invocation of DefaultActionInvocation</li>
+ * <ul>
+ * <li>invocation of Interceptors</li>
+ * <li>invocation of Action</li>
+ * <li>invocation of PreResultListener</li>
+ * <li>invocation of Result</li>
+ * </ul>
+ * </ul>
+ * </ul>
+ * </ul>
+ * <p/>
+ * <!-- END SNIPPET: profilingAspect_xwork -->
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: activationDescription -->
+ * <p/>
+ * Activating / Deactivating of the profiling feature could be done through:-
+ * <p/>
+ * <!-- END SNIPPET: activationDescription -->
+ * <p/>
+ * <p/>
+ * <p/>
+ * System properties:- <p/>
+ * <pre>
+ * <!-- START SNIPPET: activationThroughSystemProperty -->
+ *
+ *  -Dxwork.profile.activate=true
+ *
+ * <!-- END SNIPPET: activationThroughSystemProperty -->
+ * </pre>
+ * <p/>
+ * <!-- START SNIPPET: activationThroughSystemPropertyDescription -->
+ * <p/>
+ * This could be done in the container startup script eg. CATALINA_OPTS in catalina.sh
+ * (tomcat) or using "java -Dxwork.profile.activate=true -jar start.jar" (jetty)
+ * <p/>
+ * <!-- END SNIPPET: activationThroughSystemPropertyDescription -->
+ * <p/>
+ * <p/>
+ * Code :- <p/>
+ * <pre>
+ * <!-- START SNIPPET: activationThroughCode -->
+ *
+ *  UtilTimerStack.setActivate(true);
+ *
+ * <!-- END SNIPPET: activationThroughCode -->
+ * </pre>
+ * <p/>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: activationThroughCodeDescription -->
+ * <p/>
+ * This could be done in a static block, in a Spring bean with lazy-init="false",
+ * in a Servlet with init-on-startup as some numeric value, in a Filter or
+ * Listener's init method etc.
+ * <p/>
+ * <!-- END SNIPPET: activationThroughCodeDescription -->
+ * <p/>
+ * <p/>
+ * Parameter:-
+ * <p/>
+ * <pre>
+ * <!-- START SNIPPET: activationThroughParameter -->
+ *
+ * &lt;action ... &gt;
+ *  ...
+ *  &lt;interceptor-ref name="profiling"&gt;
+ *      &lt;param name="profilingKey"&gt;profiling&lt;/param&gt;
+ *  &lt;/interceptor-ref&gt;
+ *  ...
+ * &lt;/action&gt;
+ *
+ * or
+ *
+ * &lt;action .... &gt;
+ * ...
+ *  &lt;interceptor-ref name="profiling" /&gt;
+ * ...
+ * &lt;/action&gt;
+ *
+ * through url
+ *
+ * http://host:port/context/namespace/someAction.action?profiling=true
+ *
+ * through code
+ *
+ * ActionContext.getContext().getParameters().put("profiling", "true);
+ *
+ * <!-- END SNIPPET: activationThroughParameter -->
+ * </pre>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: activationThroughParameterDescription -->
+ * <p/>
+ * To use profiling activation through parameter, one will need to pass in through
+ * the 'profiling' parameter (which is the default) and could be changed through
+ * the param tag in the interceptor-ref.
+ * <p/>
+ * <!-- END SNIPPET: activationThroughParameterDescription -->
+ * <p/>
+ * <p/>
+ * Warning:<p/>
+ * <!-- START SNIPPET: activationThroughParameterWarning -->
+ * <p/>
+ * Profiling activation through a parameter requires the following:
+ * <p/>
+ * <ul>
+ * <li>Profiling interceptor in interceptor stack</li>
+ * <li>dev mode on (struts.devMode=true in struts.properties)
+ * </ul>
+ * <p/>
+ * <!-- END SNIPPET: activationThroughParameterWarning -->
+ * <p/>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: filteringDescription -->
+ * <p/>
+ * One could filter out the profile logging by having a System property as follows. With this
+ * 'xwork.profile.mintime' property, one could only log profile information when its execution time
+ * exceed those specified in 'xwork.profile.mintime' system property. If no such property is specified,
+ * it will be assumed to be 0, hence all profile information will be logged.
+ * <p/>
+ * <!-- END SNIPPET: filteringDescription -->
+ * <p/>
+ * <pre>
+ * <!-- START SNIPPET: filteringCode -->
+ *
+ *  -Dxwork.profile.mintime=10000
+ *
+ * <!-- END SNIPPET: filteringCode -->
+ * </pre>
+ * <p/>
+ * <!-- START SNIPPET: methodDescription -->
+ * <p/>
+ * One could extend the profiling feature provided by Struts2 in their web application as well.
+ * <p/>
+ * <!-- END SNIPPET: methodDescription -->
+ * <p/>
+ * <pre>
+ * <!-- START SNIPPET: method1 -->
+ *
+ *    String logMessage = "Log message";
+ *    UtilTimerStack.push(logMessage);
+ *    try {
+ *        // do some code
+ *    }
+ *    finally {
+ *        UtilTimerStack.pop(logMessage); // this needs to be the same text as above
+ *    }
+ *
+ * <!-- END SNIPPET: method1 -->
+ * </pre>
+ * <p/>
+ * or
+ * <p/>
+ * <pre>
+ * <!-- START SNIPPET: method2 -->
+ *
+ *   String result = UtilTimerStack.profile("purchaseItem: ",
+ *       new UtilTimerStack.ProfilingBlock<String>() {
+ *            public String doProfiling() {
+ *               // do some code
+ *               return "Ok";
+ *            }
+ *       });
+ *
+ * <!-- END SNIPPET: method2 -->
+ * </pre>
+ * <p/>
+ * <p/>
+ * <!-- START SNIPPET: profileLogFile -->
+ * <p/>
+ * Profiled result is logged using commons-logging under the logger named
+ * 'com.opensymphony.xwork2.util.profiling.UtilTimerStack'. Depending on the underlying logging implementation
+ * say if it is Log4j, one could direct the log to appear in a different file, being emailed to someone or have
+ * it stored in the db.
+ * <p/>
+ * <!-- END SNIPPET: profileLogFile -->
+ *
+ * @version $Date$ $Id$
+ */
+public class UtilTimerStack {
+
+    // A reference to the current ProfilingTimerBean
+    protected static ThreadLocal<ProfilingTimerBean> current = new ThreadLocal<>();
+
+    /**
+     * System property that controls whether this timer should be used or not.  Set to "true" activates
+     * the timer.  Set to "false" to disactivate.
+     */
+    public static final String ACTIVATE_PROPERTY = "xwork.profile.activate";
+
+    /**
+     * System property that controls the min time, that if exceeded will cause a log (at INFO level) to be
+     * created.
+     */
+    public static final String MIN_TIME = "xwork.profile.mintime";
+
+    private static final Logger LOG = LogManager.getLogger(UtilTimerStack.class);
+
+    /**
+     * Initialized in a static block, it can be changed at runtime by calling setActive(...)
+     */
+    private static boolean active;
+
+    static {
+        active = "true".equalsIgnoreCase(System.getProperty(ACTIVATE_PROPERTY));
+    }
+
+    /**
+     * Create and start a performance profiling with the <code>name</code> given. Deal with
+     * profile hierarchy automatically, so caller don't have to be concern about it.
+     *
+     * @param name profile name
+     */
+    public static void push(String name) {
+        if (!isActive()) {
+            return;
+        }
+
+        //create a new timer and start it
+        ProfilingTimerBean newTimer = new ProfilingTimerBean(name);
+        newTimer.setStartTime();
+
+        //if there is a current timer - add the new timer as a child of it
+        ProfilingTimerBean currentTimer = (ProfilingTimerBean) current.get();
+        if (currentTimer != null) {
+            currentTimer.addChild(newTimer);
+        }
+
+        //set the new timer to be the current timer
+        current.set(newTimer);
+    }
+
+    /**
+     * End a preformance profiling with the <code>name</code> given. Deal with
+     * profile hierarchy automatically, so caller don't have to be concern about it.
+     *
+     * @param name profile name
+     */
+    public static void pop(String name) {
+        if (!isActive()) {
+            return;
+        }
+
+        ProfilingTimerBean currentTimer = current.get();
+
+        //if the timers are matched up with each other (ie push("a"); pop("a"));
+        if (currentTimer != null && name != null && name.equals(currentTimer.getResource())) {
+            currentTimer.setEndTime();
+            ProfilingTimerBean parent = currentTimer.getParent();
+            //if we are the root timer, then print out the times
+            if (parent == null) {
+                printTimes(currentTimer);
+                current.set(null); //for those servers that use thread pooling
+            } else {
+                current.set(parent);
+            }
+        } else {
+            //if timers are not matched up, then print what we have, and then print warning.
+            if (currentTimer != null) {
+                printTimes(currentTimer);
+                current.set(null); //prevent printing multiple times
+                LOG.warn("Unmatched Timer. Was expecting {}, instead got {}", currentTimer.getResource(), name);
+            }
+        }
+    }
+
+    /**
+     * Do a log (at INFO level) of the time taken for this particular profiling.
+     *
+     * @param currentTimer profiling timer bean
+     */
+    private static void printTimes(ProfilingTimerBean currentTimer) {
+        LOG.info(currentTimer.getPrintable(getMinTime()));
+    }
+
+    /**
+     * Get the min time for this profiling, it searches for a System property
+     * 'xwork.profile.mintime' and default to 0.
+     *
+     * @return long
+     */
+    private static long getMinTime() {
+        try {
+            return Long.parseLong(System.getProperty(MIN_TIME, "0"));
+        } catch (NumberFormatException e) {
+            return -1;
+        }
+    }
+
+    /**
+     * Determine if profiling is being activated, by searching for a system property
+     * 'xwork.profile.activate', default to false (profiling is off).
+     *
+     * @return <tt>true</tt>, if active, <tt>false</tt> otherwise.
+     */
+    public static boolean isActive() {
+        return active;
+    }
+
+    /**
+     * Turn profiling on or off.
+     *
+     * @param active
+     */
+    public static void setActive(boolean active) {
+        if (active) {
+            System.setProperty(ACTIVATE_PROPERTY, "true");
+        } else {
+            System.clearProperty(ACTIVATE_PROPERTY);
+        }
+        UtilTimerStack.active = active;
+    }
+
+
+    /**
+     * A convenience method that allows <code>block</code> of code subjected to profiling to be executed
+     * and avoid the need of coding boiler code that does pushing (UtilTimeBean.push(...)) and
+     * poping (UtilTimerBean.pop(...)) in a try ... finally ... block.
+     * <p/>
+     * <p/>
+     * <p/>
+     * Example of usage:
+     * <pre>
+     * 	 // we need a returning result
+     *   String result = UtilTimerStack.profile("purchaseItem: ",
+     *       new UtilTimerStack.ProfilingBlock<String>() {
+     *            public String doProfiling() {
+     *               getMyService().purchaseItem(....)
+     *               return "Ok";
+     *            }
+     *       });
+     * </pre>
+     * or
+     * <pre>
+     *   // we don't need a returning result
+     *   UtilTimerStack.profile("purchaseItem: ",
+     *       new UtilTimerStack.ProfilingBlock<String>() {
+     *            public String doProfiling() {
+     *               getMyService().purchaseItem(....)
+     *               return null;
+     *            }
+     *       });
+     * </pre>
+     *
+     * @param <T>   any return value if there's one.
+     * @param name  profile name
+     * @param block code block subjected to profiling
+     * @return T
+     * @throws Exception
+     */
+    public static <T> T profile(String name, ProfilingBlock<T> block) throws Exception {
+        UtilTimerStack.push(name);
+        try {
+            return block.doProfiling();
+        } finally {
+            UtilTimerStack.pop(name);
+        }
+    }
+
+    /**
+     * A callback interface where code subjected to profile is to be executed. This eliminates the need
+     * of coding boiler code that does pushing (UtilTimerBean.push(...)) and poping (UtilTimerBean.pop(...))
+     * in a try ... finally ... block.
+     *
+     * @param <T>
+     * @version $Date$ $Id$
+     */
+    public static interface ProfilingBlock<T> {
+
+        /**
+         * Method that execute the code subjected to profiling.
+         *
+         * @return profiles Type
+         * @throws Exception
+         */
+        T doProfiling() throws Exception;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html b/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html
new file mode 100644
index 0000000..c0b1f7f
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html
@@ -0,0 +1 @@
+<body>Classes to enable profiling of action execution.</body>

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java
new file mode 100644
index 0000000..704b5af
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java
@@ -0,0 +1,15 @@
+package com.opensymphony.xwork2.util.reflection;
+
+import java.util.Map;
+
+public interface ReflectionContextFactory {
+    /**
+     * Creates and returns a new standard naming context for evaluating an OGNL
+     * expression.
+     *
+     * @param root the root of the object graph
+     * @return a new Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
+     *         set appropriately
+     */
+    Map createDefaultContext( Object root );
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java
new file mode 100644
index 0000000..9de464b
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java
@@ -0,0 +1,173 @@
+/*
+ * 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.reflection;
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Manages variables in the reflection context and returns values
+ * to be used by the application.
+ *
+ * @author Gabe
+ */
+public class ReflectionContextState {
+
+	private static final String GETTING_BY_KEY_PROPERTY = "xwork.getting.by.key.property";
+	private static final String SET_MAP_KEY = "set.map.key";
+
+    public static final String CURRENT_PROPERTY_PATH="current.property.path";
+    public static final String FULL_PROPERTY_PATH="current.property.path";
+	public static final String CREATE_NULL_OBJECTS = "xwork.NullHandler.createNullObjects";
+	public static final String DENY_METHOD_EXECUTION = "xwork.MethodAccessor.denyMethodExecution";
+	public static final String DENY_INDEXED_ACCESS_EXECUTION = "xwork.IndexedPropertyAccessor.denyMethodExecution";
+
+    public static boolean isCreatingNullObjects(Map<String, Object> context) {
+		//TODO
+		return getBooleanProperty(ReflectionContextState.CREATE_NULL_OBJECTS, context);
+	}
+
+	public static void setCreatingNullObjects(Map<String, Object> context, boolean creatingNullObjects) {
+		setBooleanValue(ReflectionContextState.CREATE_NULL_OBJECTS, context, creatingNullObjects);
+	}
+
+	public static boolean isGettingByKeyProperty(Map<String, Object> context) {
+		return getBooleanProperty(GETTING_BY_KEY_PROPERTY, context);
+	}
+	
+	public static void setDenyMethodExecution(Map<String, Object> context, boolean denyMethodExecution) {
+		setBooleanValue(ReflectionContextState.DENY_METHOD_EXECUTION, context, denyMethodExecution);
+	}
+	
+	public static boolean isDenyMethodExecution(Map<String, Object> context) {
+		return getBooleanProperty(ReflectionContextState.DENY_METHOD_EXECUTION, context);
+	}
+
+	public static void setGettingByKeyProperty(Map<String, Object> context, boolean gettingByKeyProperty) {
+		setBooleanValue(GETTING_BY_KEY_PROPERTY, context, gettingByKeyProperty);
+	}	
+	
+	public static boolean isReportingConversionErrors(Map<String, Object> context) {
+		return getBooleanProperty(XWorkConverter.REPORT_CONVERSION_ERRORS, context);
+	}
+
+	public static void setReportingConversionErrors(Map<String, Object> context, boolean reportingErrors) {
+		setBooleanValue(XWorkConverter.REPORT_CONVERSION_ERRORS, context, reportingErrors);
+	}
+
+	public static Class getLastBeanClassAccessed(Map<String, Object> context) {
+		return (Class)context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+	}
+
+	public static void setLastBeanPropertyAccessed(Map<String, Object> context, String property) {
+		context.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, property);
+	}
+
+	public static String getLastBeanPropertyAccessed(Map<String, Object> context) {
+		return (String)context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+	}
+
+	public static void setLastBeanClassAccessed(Map<String, Object> context, Class clazz) {
+		context.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, clazz);
+	}
+	/**
+	 * Gets the current property path but not completely.
+	 * It does not use the [ and ] used in some representations
+	 * of Maps and Lists. The reason for this is that the current
+	 * property path is only currently used for caching purposes
+	 * so there is no real reason to have an exact replica.
+     *
+	 * <p/>So if the real path is myProp.myMap['myKey'] this would
+	 * return myProp.myMap.myKey.
+     * 
+	 * @param context
+	 */
+	public static String getCurrentPropertyPath(Map<String, Object> context) {
+		return (String)context.get(CURRENT_PROPERTY_PATH);
+	}
+
+	public static String getFullPropertyPath(Map<String, Object> context) {
+		return (String)context.get(FULL_PROPERTY_PATH);
+	}
+
+	public static void setFullPropertyPath(Map<String, Object> context, String path) {
+		context.put(FULL_PROPERTY_PATH, path);
+
+	}
+
+	public static void updateCurrentPropertyPath(Map<String, Object> context, Object name) {
+		String currentPath=getCurrentPropertyPath(context);
+		if (name!=null) {
+			if (currentPath!=null) {
+                StringBuilder sb = new StringBuilder(currentPath);
+                sb.append(".");
+                sb.append(name.toString());
+				currentPath = sb.toString();
+			}	else {
+				currentPath = name.toString();
+			}
+			context.put(CURRENT_PROPERTY_PATH, currentPath);
+		}
+	}
+
+	public static void setSetMap(Map<String, Object> context, Map<Object, Object> setMap, String path) {
+		Map<Object, Map<Object, Object>> mapOfSetMaps=(Map)context.get(SET_MAP_KEY);
+		if (mapOfSetMaps==null) {
+			mapOfSetMaps = new HashMap<>();
+			context.put(SET_MAP_KEY, mapOfSetMaps);
+		}
+		mapOfSetMaps.put(path, setMap);
+	}
+
+	public static Map<Object, Object> getSetMap(Map<String, Object> context, String path) {
+		Map<Object, Map<Object, Object>> mapOfSetMaps=(Map)context.get(SET_MAP_KEY);
+		if (mapOfSetMaps==null) {
+			return null;
+		}
+		return mapOfSetMaps.get(path);
+	}
+
+	private static boolean getBooleanProperty(String property, Map<String, Object> context) {
+		Boolean myBool=(Boolean)context.get(property);
+		return (myBool==null)?false:myBool.booleanValue();
+	}
+
+	private static void setBooleanValue(String property, Map<String, Object> context, boolean value) {
+		context.put(property, new Boolean(value));
+	}
+
+	/**
+	 *
+	 */
+	public static void clearCurrentPropertyPath(Map<String, Object> context) {
+		context.put(CURRENT_PROPERTY_PATH, null);
+
+	}
+
+    public static void clear(Map<String, Object> context) {
+        if (context != null) {
+            context.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED,null);
+            context.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED,null);
+    
+            context.put(CURRENT_PROPERTY_PATH,null);
+            context.put(FULL_PROPERTY_PATH,null);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java
new file mode 100644
index 0000000..a6d107d
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java
@@ -0,0 +1,41 @@
+package com.opensymphony.xwork2.util.reflection;
+
+import com.opensymphony.xwork2.XWorkException;
+
+public class ReflectionException extends XWorkException {
+
+    public ReflectionException() {
+        // TODO Auto-generated constructor stub
+    }
+
+    public ReflectionException(String s) {
+        super(s);
+        // TODO Auto-generated constructor stub
+    }
+
+    public ReflectionException(String s, Object target) {
+        super(s, target);
+        // TODO Auto-generated constructor stub
+    }
+
+    public ReflectionException(Throwable cause) {
+        super(cause);
+        // TODO Auto-generated constructor stub
+    }
+
+    public ReflectionException(Throwable cause, Object target) {
+        super(cause, target);
+        // TODO Auto-generated constructor stub
+    }
+
+    public ReflectionException(String s, Throwable cause) {
+        super(s, cause);
+        // TODO Auto-generated constructor stub
+    }
+
+    public ReflectionException(String s, Throwable cause, Object target) {
+        super(s, cause, target);
+        // TODO Auto-generated constructor stub
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java
new file mode 100644
index 0000000..b5c7d8a
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java
@@ -0,0 +1,14 @@
+package com.opensymphony.xwork2.util.reflection;
+
+/**
+ * Declares a class that wants to handle its own reflection exceptions
+ */
+public interface ReflectionExceptionHandler {
+
+    /**
+     * Handles a reflection exception
+     * 
+     * @param ex The reflection exception
+     */
+    void handle(ReflectionException ex);
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java
new file mode 100644
index 0000000..c48292f
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java
@@ -0,0 +1,141 @@
+package com.opensymphony.xwork2.util.reflection;
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+public interface ReflectionProvider {
+    
+    Method getGetMethod(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException;
+    
+    Method getSetMethod(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException;
+    
+    Field getField(Class inClass, String name);
+    
+    /**
+     * Sets the object's properties using the default type converter, defaulting to not throw
+     * exceptions for problems setting the properties.
+     *
+     * @param props   the properties being set
+     * @param o       the object
+     * @param context the action context
+     */
+    void setProperties(Map<String, ?> props, Object o, Map<String, Object> context);
+
+    /**
+     * Sets the object's properties using the default type converter.
+     *
+     * @param props                   the properties being set
+     * @param o                       the object
+     * @param context                 the action context
+     * @param throwPropertyExceptions boolean which tells whether it should throw exceptions for
+     *                                problems setting the properties
+     */
+    void setProperties(Map<String, ?> props, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException;
+    
+    /**
+     * Sets the properties on the object using the default context, defaulting to not throwing
+     * exceptions for problems setting the properties.
+     *
+     * @param properties
+     * @param o
+     */
+    void setProperties(Map<String, ?> properties, Object o);
+    
+    /**
+     *  This method returns a PropertyDescriptor for the given class and property name using
+     * a Map lookup (using getPropertyDescriptorsMap()).
+     */
+    PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException;
+
+    /**
+     * Copies the properties in the object "from" and sets them in the object "to"
+     * using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
+     * is specified.
+     *
+     * @param from       the source object
+     * @param to         the target object
+     * @param context    the action context we're running under
+     * @param exclusions collection of method names to excluded from copying ( can be null)
+     * @param inclusions collection of method names to included copying  (can be null)
+     *                   note if exclusions AND inclusions are supplied and not null nothing will get copied.
+     */
+    void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions);
+    
+    /**
+     * Looks for the real target with the specified property given a root Object which may be a
+     * CompoundRoot.
+     *
+     * @return the real target or null if no object can be found with the specified property
+     */
+    Object getRealTarget(String property, Map<String, Object> context, Object root) throws ReflectionException;
+    
+    /**
+     * Sets the named property to the supplied value on the Object,
+     *
+     * @param name    the name of the property to be set
+     * @param value   the value to set into the named property
+     * @param o       the object upon which to set the property
+     * @param context the context which may include the TypeConverter
+     * @param throwPropertyExceptions boolean which tells whether it should throw exceptions for
+     *                                problems setting the properties
+     */
+    void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions);
+
+    /**
+     * Sets the named property to the supplied value on the Object, defaults to not throwing
+     * property exceptions.
+     *
+     * @param name    the name of the property to be set
+     * @param value   the value to set into the named property
+     * @param o       the object upon which to set the property
+     * @param context the context which may include the TypeConverter
+     */
+    void setProperty(String name, Object value, Object o, Map<String, Object> context);
+    
+    /**
+     * Creates a Map with read properties for the given source object.
+     * <p/>
+     * If the source object does not have a read property (i.e. write-only) then
+     * the property is added to the map with the value <code>here is no read method for property-name</code>.
+     * 
+     * @param source   the source object.
+     * @return  a Map with (key = read property name, value = value of read property).
+     * @throws IntrospectionException is thrown if an exception occurs during introspection.
+     */
+    Map<String, Object> getBeanMap(Object source) throws IntrospectionException, ReflectionException;
+    
+    /**
+     * Evaluates the given OGNL expression to extract a value from the given root
+     * object in a given context
+     *
+     * @param expression the OGNL expression to be parsed
+     * @param context the naming context for the evaluation
+     * @param root the root object for the OGNL expression
+     * @return the result of evaluating the expression
+     */
+    Object getValue( String expression, Map<String, Object> context, Object root ) throws ReflectionException;
+    
+    /**
+     * Evaluates the given OGNL expression to insert a value into the object graph
+     * rooted at the given root object given the context.
+     *
+     * @param expression the OGNL expression to be parsed
+     * @param root the root object for the OGNL expression
+     * @param context the naming context for the evaluation
+     * @param value the value to insert into the object graph
+     */
+    void setValue( String expression, Map<String, Object> context, Object root, Object value ) throws ReflectionException;
+    
+    /**
+     * Get's the java beans property descriptors for the given source.
+     * 
+     * @param source  the source object.
+     * @return  property descriptors.
+     * @throws IntrospectionException is thrown if an exception occurs during introspection.
+     */
+    PropertyDescriptor[] getPropertyDescriptors(Object source) throws IntrospectionException;
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java
new file mode 100644
index 0000000..a05b7d6
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java
@@ -0,0 +1,10 @@
+package com.opensymphony.xwork2.util.reflection;
+
+import com.opensymphony.xwork2.ActionContext;
+
+public class ReflectionProviderFactory {
+
+    public static ReflectionProvider getInstance() {
+        return ActionContext.getContext().getContainer().getInstance(ReflectionProvider.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java b/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java
new file mode 100644
index 0000000..6650817
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2002-2007,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.validator;
+
+import java.util.List;
+
+/**
+ * ActionValidatorManager is the main interface for validation managers (regular and annotation based).
+ *
+ * @author Rainer Hermanns
+ */
+public interface ActionValidatorManager {
+
+    /**
+     * Returns a list of validators for the given class, context, and method. This is the primary
+     * lookup method for validators.
+     *
+     * @param clazz the class to lookup.
+     * @param context the context of the action class - can be <tt>null</tt>.
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @return a list of all validators for the given class and context.
+     */
+    List<Validator> getValidators(Class clazz, String context, String method);
+
+    /**
+     * Returns a list of validators for the given class and context. This is the primary
+     * lookup method for validators.
+     *
+     * @param clazz the class to lookup.
+     * @param context the context of the action class - can be <tt>null</tt>.
+     * @return a list of all validators for the given class and context.
+     */
+    List<Validator> getValidators(Class clazz, String context);
+
+    /**
+     * Validates the given object using action and its context.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    void validate(Object object, String context) throws ValidationException;
+
+    /**
+     * Validates an action give its context and a validation context.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param validatorContext the validation context to use
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException;
+
+    /**
+     * Validates the given object using an action, its context, and the name of the method being invoked on the action.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    void validate(Object object, String context, String method) throws ValidationException;
+
+    /**
+     * Validates an action give its context and a validation context.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param validatorContext the validation context to use
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException;
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java b/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java
new file mode 100644
index 0000000..e98f8ee
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java
@@ -0,0 +1,380 @@
+/*
+ * 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.validator;
+
+
+import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ClassLoaderUtil;
+import com.opensymphony.xwork2.util.ValueStack;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * AnnotationActionValidatorManager is the entry point into XWork's annotations-based validator framework.
+ * Validation rules are specified as annotations within the source files.
+ *
+ * @author Rainer Hermanns
+ * @author jepjep
+ */
+public class AnnotationActionValidatorManager implements ActionValidatorManager {
+
+    /**
+     * The file suffix for any validation file.
+     */
+    protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
+
+    private final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private static final Logger LOG = LogManager.getLogger(AnnotationActionValidatorManager.class);
+
+    private ValidatorFactory validatorFactory;
+    private ValidatorFileParser validatorFileParser;
+    private FileManager fileManager;
+    private boolean reloadingConfigs;
+
+    @Inject
+    public void setValidatorFactory(ValidatorFactory fac) {
+        this.validatorFactory = fac;
+    }
+
+    @Inject
+    public void setValidatorFileParser(ValidatorFileParser parser) {
+        this.validatorFileParser = parser;
+    }
+
+    @Inject
+    public void setFileManagerFactory(FileManagerFactory fileManagerFactory) {
+        this.fileManager = fileManagerFactory.getFileManager();
+    }
+
+    @Inject(value = XWorkConstants.RELOAD_XML_CONFIGURATION, required = false)
+    public void setReloadingConfigs(String reloadingConfigs) {
+        this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs);
+    }
+
+    public List<Validator> getValidators(Class clazz, String context) {
+        return getValidators(clazz, context, null);
+    }
+
+    public List<Validator> getValidators(Class clazz, String context, String method) {
+        final String validatorKey = buildValidatorKey(clazz, context);
+        final List<ValidatorConfig> cfgs;
+
+        if (validatorCache.containsKey(validatorKey)) {
+            if (reloadingConfigs) {
+                validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
+            }
+        } else {
+            validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
+        }
+
+        // get the set of validator configs
+        cfgs = new ArrayList<ValidatorConfig>(validatorCache.get(validatorKey));
+
+        ValueStack stack = ActionContext.getContext().getValueStack();
+
+        // create clean instances of the validators for the caller's use
+        ArrayList<Validator> validators = new ArrayList<>(cfgs.size());
+        for (ValidatorConfig cfg : cfgs) {
+            if (method == null || method.equals(cfg.getParams().get("methodName"))) {
+                Validator validator = validatorFactory.getValidator(
+                        new ValidatorConfig.Builder(cfg)
+                                .removeParam("methodName")
+                                .build());
+                validator.setValidatorType(cfg.getType());
+                validator.setValueStack(stack);
+                validators.add(validator);
+            }
+        }
+
+        return validators;
+    }
+
+    public void validate(Object object, String context) throws ValidationException {
+        validate(object, context, (String) null);
+    }
+
+    public void validate(Object object, String context, String method) throws ValidationException {
+        ValidatorContext validatorContext = new DelegatingValidatorContext(object);
+        validate(object, context, validatorContext, method);
+    }
+
+    public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
+        validate(object, context, validatorContext, null);
+    }
+
+    public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
+        List<Validator> validators = getValidators(object.getClass(), context, method);
+        Set<String> shortcircuitedFields = null;
+
+        for (final Validator validator : validators) {
+            try {
+                validator.setValidatorContext(validatorContext);
+
+                LOG.debug("Running validator: {} for object {} and method {}", validator, object, method);
+
+                FieldValidator fValidator = null;
+                String fullFieldName = null;
+
+                if (validator instanceof FieldValidator) {
+                    fValidator = (FieldValidator) validator;
+                    fullFieldName = fValidator.getValidatorContext().getFullFieldName(fValidator.getFieldName());
+
+                    if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
+                        LOG.debug("Short-circuited, skipping");
+                        continue;
+                    }
+                }
+
+                if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
+                    // get number of existing errors
+                    List<String> errs = null;
+
+                    if (fValidator != null) {
+                        if (validatorContext.hasFieldErrors()) {
+                            Collection<String> fieldErrors = validatorContext.getFieldErrors().get(fullFieldName);
+
+                            if (fieldErrors != null) {
+                                errs = new ArrayList<>(fieldErrors);
+                            }
+                        }
+                    } else if (validatorContext.hasActionErrors()) {
+                        Collection<String> actionErrors = validatorContext.getActionErrors();
+
+                        if (actionErrors != null) {
+                            errs = new ArrayList<>(actionErrors);
+                        }
+                    }
+
+                    validator.validate(object);
+
+                    if (fValidator != null) {
+                        if (validatorContext.hasFieldErrors()) {
+                            Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
+
+                            if ((errCol != null) && !errCol.equals(errs)) {
+                                LOG.debug("Short-circuiting on field validation");
+
+                                if (shortcircuitedFields == null) {
+                                    shortcircuitedFields = new TreeSet<String>();
+                                }
+
+                                shortcircuitedFields.add(fullFieldName);
+                            }
+                        }
+                    } else if (validatorContext.hasActionErrors()) {
+                        Collection<String> errCol = validatorContext.getActionErrors();
+
+                        if ((errCol != null) && !errCol.equals(errs)) {
+                            LOG.debug("Short-circuiting");
+                            break;
+                        }
+                    }
+
+                    continue;
+                }
+
+                validator.validate(object);
+            } finally {
+                validator.setValidatorContext(null);
+            }
+
+        }
+    }
+
+    /**
+     * Builds a key for validators - used when caching validators.
+     *
+     * @param clazz the action.
+     * @return a validator key which is the class name plus context.
+     */
+    protected static String buildValidatorKey(Class clazz, String context) {
+        ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
+        ActionProxy proxy = invocation.getProxy();
+        ActionConfig config = proxy.getConfig();
+
+        StringBuilder sb = new StringBuilder(clazz.getName());
+        sb.append("/");
+        if (StringUtils.isNotBlank(config.getPackageName())) {
+            sb.append(config.getPackageName());
+            sb.append("/");
+        }
+
+        // the key needs to use the name of the action from the config file,
+        // instead of the url, so wild card actions will have the same validator
+        // see WW-2996
+
+        // UPDATE:
+        // WW-3753 Using the config name instead of the context only for
+        // wild card actions to keep the flexibility provided
+        // by the original design (such as mapping different contexts
+        // to the same action and method if desired)
+        String configName = config.getName();
+        if (configName.contains(ActionConfig.WILDCARD)) {
+            sb.append(configName);
+            sb.append("|");
+            sb.append(proxy.getMethod());
+        } else {
+            sb.append(context);
+        }
+        
+        return sb.toString();
+    }
+
+    private List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
+        String fileName = aClass.getName().replace('.', '/') + "-" + context.replace('/', '-') + VALIDATION_CONFIG_SUFFIX;
+
+        return loadFile(fileName, aClass, checkFile);
+    }
+
+
+    protected List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
+
+        String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
+
+        List<ValidatorConfig> result = new ArrayList<>(loadFile(fileName, aClass, checkFile));
+
+        AnnotationValidationConfigurationBuilder builder = new AnnotationValidationConfigurationBuilder(validatorFactory);
+
+        List<ValidatorConfig> annotationResult = new ArrayList<>(builder.buildAnnotationClassValidatorConfigs(aClass));
+
+        result.addAll(annotationResult);
+
+        return result;
+
+    }
+
+    /**
+     * <p>This method 'collects' all the validator configurations for a given
+     * action invocation.</p>
+     * <p/>
+     * <p>It will traverse up the class hierarchy looking for validators for every super class
+     * and directly implemented interface of the current action, as well as adding validators for
+     * any alias of this invocation. Nifty!</p>
+     * <p/>
+     * <p>Given the following class structure:
+     * <pre>
+     *   interface Thing;
+     *   interface Animal extends Thing;
+     *   interface Quadraped extends Animal;
+     *   class AnimalImpl implements Animal;
+     *   class QuadrapedImpl extends AnimalImpl implements Quadraped;
+     *   class Dog extends QuadrapedImpl;
+     * </pre></p>
+     * <p/>
+     * <p>This method will look for the following config files for Dog:
+     * <pre>
+     *   Animal
+     *   Animal-context
+     *   AnimalImpl
+     *   AnimalImpl-context
+     *   Quadraped
+     *   Quadraped-context
+     *   QuadrapedImpl
+     *   QuadrapedImpl-context
+     *   Dog
+     *   Dog-context
+     * </pre></p>
+     * <p/>
+     * <p>Note that the validation rules for Thing is never looked for because no class in the
+     * hierarchy directly implements Thing.</p>
+     *
+     * @param clazz     the Class to look up validators for.
+     * @param context   the context to use when looking up validators.
+     * @param checkFile true if the validation config file should be checked to see if it has been
+     *                  updated.
+     * @param checked   the set of previously checked class-contexts, null if none have been checked
+     * @return a list of validator configs for the given class and context.
+     */
+    private List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set<String> checked) {
+        List<ValidatorConfig> validatorConfigs = new ArrayList<>();
+
+        if (checked == null) {
+            checked = new TreeSet<>();
+        } else if (checked.contains(clazz.getName())) {
+            return validatorConfigs;
+        }
+
+        if (clazz.isInterface()) {
+            Class[] interfaces = clazz.getInterfaces();
+
+            for (Class anInterface : interfaces) {
+                validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
+            }
+        } else {
+            if (!clazz.equals(Object.class)) {
+                validatorConfigs.addAll(buildValidatorConfigs(clazz.getSuperclass(), context, checkFile, checked));
+            }
+        }
+
+        // look for validators for implemented interfaces
+        Class[] interfaces = clazz.getInterfaces();
+
+        for (Class anInterface1 : interfaces) {
+            if (checked.contains(anInterface1.getName())) {
+                continue;
+            }
+
+            validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
+
+            if (context != null) {
+                validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
+            }
+
+            checked.add(anInterface1.getName());
+        }
+
+        validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
+
+        if (context != null) {
+            validatorConfigs.addAll(buildAliasValidatorConfigs(clazz, context, checkFile));
+        }
+
+        checked.add(clazz.getName());
+
+        return validatorConfigs;
+    }
+
+    private List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
+        List<ValidatorConfig> retList = Collections.emptyList();
+
+        URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
+
+        if ((checkFile && fileManager.fileNeedsReloading(fileUrl)) || !validatorFileCache.containsKey(fileName)) {
+            try (InputStream is = fileManager.loadFile(fileUrl)) {
+                if (is != null) {
+                    retList = new ArrayList<>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
+                }
+            } catch (IOException e) {
+                LOG.error("Caught exception while loading file {}", fileName, e);
+            }
+
+            validatorFileCache.put(fileName, retList);
+        } else {
+            retList = validatorFileCache.get(fileName);
+        }
+
+        return retList;
+    }
+}