You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by ma...@apache.org on 2009/12/27 19:01:09 UTC
svn commit: r894087 [20/46] - in /struts/xwork/trunk: ./ assembly/
assembly/src/ assembly/src/main/ assembly/src/main/assembly/
assembly/src/main/resources/ core/ core/src/ core/src/main/
core/src/main/java/ core/src/main/java/com/ core/src/main/java/c...
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,485 @@
+/*
+ * 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 com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+
+/**
+ * A timer stack.
+ *
+ * <p />
+ *
+ * <!-- START SNIPPET: profilingAspect_struts2 -->
+ *
+ * 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>
+ *
+ * <!-- END SNIPPET: profilingAspect_struts2 -->
+ *
+ *
+ * <!-- START SNIPPET: profilingAspect_xwork -->
+ *
+ * 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>
+ *
+ * <!-- END SNIPPET: profilingAspect_xwork -->
+ *
+ *
+ * <!-- START SNIPPET: activationDescription -->
+ *
+ * Activating / Deactivating of the profiling feature could be done through:-
+ *
+ * <!-- END SNIPPET: activationDescription -->
+ *
+ * <p/>
+ *
+ * System properties:- <p/>
+ * <pre>
+ * <!-- START SNIPPET: activationThroughSystemProperty -->
+ *
+ * -Dxwork.profile.activate=true
+ *
+ * <!-- END SNIPPET: activationThroughSystemProperty -->
+ * </pre>
+ *
+ * <!-- START SNIPPET: activationThroughSystemPropertyDescription -->
+ *
+ * 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)
+ *
+ * <!-- END SNIPPET: activationThroughSystemPropertyDescription -->
+ *
+ * <p/>
+ * Code :- <p/>
+ * <pre>
+ * <!-- START SNIPPET: activationThroughCode -->
+ *
+ * UtilTimerStack.setActivate(true);
+ *
+ * <!-- END SNIPPET: activationThroughCode -->
+ * </pre>
+ *
+ *
+ *
+ * <!-- START SNIPPET: activationThroughCodeDescription -->
+ *
+ * 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.
+ *
+ * <!-- END SNIPPET: activationThroughCodeDescription -->
+ *
+ * <p/>
+ * Parameter:-
+ *
+ * <pre>
+ * <!-- START SNIPPET: activationThroughParameter -->
+ *
+ * <action ... >
+ * ...
+ * <interceptor-ref name="profiling">
+ * <param name="profilingKey">profiling</param>
+ * </interceptor-ref>
+ * ...
+ * </action>
+ *
+ * or
+ *
+ * <action .... >
+ * ...
+ * <interceptor-ref name="profiling" />
+ * ...
+ * </action>
+ *
+ * through url
+ *
+ * http://host:port/context/namespace/someAction.action?profiling=true
+ *
+ * through code
+ *
+ * ActionContext.getContext().getParameters().put("profiling", "true);
+ *
+ * <!-- END SNIPPET: activationThroughParameter -->
+ * </pre>
+ *
+ *
+ * <!-- START SNIPPET: activationThroughParameterDescription -->
+ *
+ * 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.
+ *
+ * <!-- END SNIPPET: activationThroughParameterDescription -->
+ *
+ * <p/>
+ * Warning:<p/>
+ * <!-- START SNIPPET: activationThroughParameterWarning -->
+ *
+ * Profiling activation through a parameter requires the following:
+ *
+ * <ul>
+ * <li>Profiling interceptor in interceptor stack</li>
+ * <li>dev mode on (struts.devMode=true in struts.properties)
+ * </ul>
+ *
+ * <!-- END SNIPPET: activationThroughParameterWarning -->
+ *
+ * <p/>
+ *
+ * <!-- START SNIPPET: filteringDescription -->
+ *
+ * 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.
+ *
+ * <!-- END SNIPPET: filteringDescription -->
+ *
+ * <pre>
+ * <!-- START SNIPPET: filteringCode -->
+ *
+ * -Dxwork.profile.mintime=10000
+ *
+ * <!-- END SNIPPET: filteringCode -->
+ * </pre>
+ *
+ * <!-- START SNIPPET: methodDescription -->
+ *
+ * One could extend the profiling feature provided by Struts2 in their web application as well.
+ *
+ * <!-- END SNIPPET: methodDescription -->
+ *
+ * <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>
+ *
+ * or
+ *
+ * <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>
+ *
+ *
+ * <!-- START SNIPPET: profileLogFile -->
+ *
+ * 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.
+ *
+ * <!-- END SNIPPET: profileLogFile -->
+ *
+ * @version $Date$ $Id$
+ */
+public class UtilTimerStack
+{
+
+ // A reference to the current ProfilingTimerBean
+ protected static ThreadLocal<ProfilingTimerBean> current = new ThreadLocal<ProfilingTimerBean>();
+
+ /**
+ * 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 = LoggerFactory.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 = (ProfilingTimerBean) 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 " + currentTimer.getResource() + ", instead got " + 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/>
+ *
+ * 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.
+ *
+ * @version $Date$ $Id$
+ *
+ * @param <T>
+ */
+ public static interface ProfilingBlock<T> {
+
+ /**
+ * Method that execute the code subjected to profiling.
+ *
+ * @return profiles Type
+ * @throws Exception
+ */
+ T doProfiling() throws Exception;
+ }
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/UtilTimerStack.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html Sun Dec 27 18:00:13 2009
@@ -0,0 +1 @@
+<body>Classes to enable profiling of action execution.</body>
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/profiling/package.html
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java Sun Dec 27 18:00:13 2009
@@ -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 );
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextFactory.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+
+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 {
+
+ public static final String CURRENT_PROPERTY_PATH="current.property.path";
+ public static final String FULL_PROPERTY_PATH="current.property.path";
+ 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 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<Object, Map<Object, Object>>();
+ 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);
+ }
+
+ }
+
+
+ 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";
+
+
+
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionContextState.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java Sun Dec 27 18:00:13 2009
@@ -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
+ }
+
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionException.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java Sun Dec 27 18:00:13 2009
@@ -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);
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionExceptionHandler.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java Sun Dec 27 18:00:13 2009
@@ -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, 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, 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, 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;
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProvider.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java Sun Dec 27 18:00:13 2009
@@ -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);
+ }
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/util/reflection/ReflectionProviderFactory.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2002-2007 by OpenSymphony
+ * All rights reserved.
+ */
+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;
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Added: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java
URL: http://svn.apache.org/viewvc/struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java?rev=894087&view=auto
==============================================================================
--- struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java (added)
+++ struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java Sun Dec 27 18:00:13 2009
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.validator;
+
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.validator.validators.VisitorFieldValidator;
+
+import java.io.IOException;
+import java.io.InputStream;
+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 = LoggerFactory.getLogger(AnnotationActionValidatorManager.class);
+
+ private ValidatorFactory validatorFactory;
+ private ValidatorFileParser validatorFileParser;
+
+ @Inject
+ public void setValidatorFactory(ValidatorFactory fac) {
+ this.validatorFactory = fac;
+ }
+
+ @Inject
+ public void setValidatorFileParser(ValidatorFileParser parser) {
+ this.validatorFileParser = parser;
+ }
+
+ public synchronized List<Validator> getValidators(Class clazz, String context) {
+ return getValidators(clazz, context, null);
+ }
+
+ public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
+ final String validatorKey = buildValidatorKey(clazz);
+
+ if (validatorCache.containsKey(validatorKey)) {
+ if (FileManager.isReloadingConfigs()) {
+ validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, true, null));
+ }
+ } else {
+ validatorCache.put(validatorKey, buildValidatorConfigs(clazz, context, false, null));
+ }
+
+ // get the set of validator configs
+ List<ValidatorConfig> cfgs = validatorCache.get(validatorKey);
+
+ ValueStack stack = ActionContext.getContext().getValueStack();
+
+ // create clean instances of the validators for the caller's use
+ ArrayList<Validator> validators = new ArrayList<Validator>(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);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Running validator: " + validator + " for object " + object + " and method " + method);
+ }
+
+ FieldValidator fValidator = null;
+ String fullFieldName = null;
+
+ if (validator instanceof FieldValidator) {
+ fValidator = (FieldValidator) validator;
+ fullFieldName = new InternalValidatorContextWrapper(fValidator.getValidatorContext()).getFullFieldName(fValidator.getFieldName());
+
+ if ((shortcircuitedFields != null) && shortcircuitedFields.contains(fullFieldName)) {
+ if (LOG.isDebugEnabled()) {
+ 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<String>(fieldErrors);
+ }
+ }
+ } else if (validatorContext.hasActionErrors()) {
+ Collection<String> actionErrors = validatorContext.getActionErrors();
+
+ if (actionErrors != null) {
+ errs = new ArrayList<String>(actionErrors);
+ }
+ }
+
+ validator.validate(object);
+
+ if (fValidator != null) {
+ if (validatorContext.hasFieldErrors()) {
+ Collection<String> errCol = validatorContext.getFieldErrors().get(fullFieldName);
+
+ if ((errCol != null) && !errCol.equals(errs)) {
+ if (LOG.isDebugEnabled()) {
+ 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)) {
+ if (LOG.isDebugEnabled()) {
+ 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) {
+ ActionInvocation invocation = ActionContext.getContext().getActionInvocation();
+ ActionProxy proxy = invocation.getProxy();
+
+ //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
+ StringBuilder sb = new StringBuilder(clazz.getName());
+ sb.append("/");
+ sb.append(proxy.getConfig().getName());
+ sb.append("|");
+ sb.append(proxy.getMethod());
+ 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<ValidatorConfig>(loadFile(fileName, aClass, checkFile));
+
+ AnnotationValidationConfigurationBuilder builder = new AnnotationValidationConfigurationBuilder(validatorFactory);
+
+ List<ValidatorConfig> annotationResult = new ArrayList<ValidatorConfig>(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<ValidatorConfig>();
+
+ if (checked == null) {
+ checked = new TreeSet<String>();
+ } 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();
+
+ if ((checkFile && FileManager.fileNeedsReloading(fileName, clazz)) || !validatorFileCache.containsKey(fileName)) {
+ InputStream is = null;
+
+ try {
+ is = FileManager.loadFile(fileName, clazz);
+
+ if (is != null) {
+ retList = new ArrayList<ValidatorConfig>(validatorFileParser.parseActionValidatorConfigs(validatorFactory, is, fileName));
+ }
+ } catch (Exception e) {
+ LOG.error("Caught exception while loading file " + fileName, e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ LOG.error("Unable to close input stream for " + fileName, e);
+ }
+ }
+ }
+
+ validatorFileCache.put(fileName, retList);
+ } else {
+ retList = validatorFileCache.get(fileName);
+ }
+
+ return retList;
+ }
+
+
+
+ /**
+ * An {@link com.opensymphony.xwork2.validator.ValidatorContext} wrapper that
+ * returns the full field name
+ * {@link com.opensymphony.xwork2.validator.AbstractActionValidatorManager.InternalValidatorContextWrapper#getFullFieldName(String)}
+ * by consulting it's parent if its an {@link com.opensymphony.xwork2.validator.validators.VisitorFieldValidator.AppendingValidatorContext}.
+ * <p/>
+ * Eg. if we have nested Visitor
+ * AddressVisitor nested inside PersonVisitor, when using the normal #getFullFieldName, we will get
+ * "address.somefield", we lost the parent, with this wrapper, we will get "person.address.somefield".
+ * This is so that the key is used to register errors, so that we don't screw up short-curcuit feature
+ * when using nested visitor. See XW-571 (nested visitor validators break short-circuit functionality)
+ * at http://jira.opensymphony.com/browse/XW-571
+ */
+ protected class InternalValidatorContextWrapper {
+ private ValidatorContext validatorContext = null;
+
+ InternalValidatorContextWrapper(ValidatorContext validatorContext) {
+ this.validatorContext = validatorContext;
+ }
+
+ /**
+ * Get the full field name by consulting the parent, so that when we are using nested visitors (
+ * visitor nested inside visitor etc.) we still get the full field name including its parents.
+ * See XW-571 for more details.
+ * @param field
+ * @return String
+ */
+ public String getFullFieldName(String field) {
+ if (validatorContext instanceof VisitorFieldValidator.AppendingValidatorContext) {
+ VisitorFieldValidator.AppendingValidatorContext appendingValidatorContext =
+ (VisitorFieldValidator.AppendingValidatorContext) validatorContext;
+ return appendingValidatorContext.getFullFieldNameFromParent(field);
+ }
+ return validatorContext.getFullFieldName(field);
+ }
+
+ }
+}
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: struts/xwork/trunk/core/src/main/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL