Anonymous avatar Anonymous committed 5b88b49

XWork configuration using Annotation
o not documented feature
o no package information supplied

Issue Number: XW-403
Submitted by: Jecki

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/xwork_1-2@1391 e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (12)

tiger/src/java/com/opensymphony/xwork/config/XWorkAnnotationConfigurationProvider.java

+package com.opensymphony.xwork.config;
+
+import com.opensymphony.xwork.ObjectFactory;
+import com.opensymphony.xwork.config.annotations.*;
+import com.opensymphony.xwork.config.entities.ActionConfig;
+import com.opensymphony.xwork.config.entities.InterceptorConfig;
+import com.opensymphony.xwork.config.entities.PackageConfig;
+import com.opensymphony.xwork.config.entities.ResultConfig;
+import com.opensymphony.xwork.config.entities.ResultTypeConfig;
+import com.opensymphony.xwork.config.providers.InterceptorBuilder;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implementation of XWork's ConfigurationProvider which read configuration from annotation.
+ *
+ * @see com.opensymphony.xwork.config.ConfigurationProvider
+ */
+public class XWorkAnnotationConfigurationProvider implements ConfigurationProvider {
+    public static String DEFAULT_FILENAME = "xwork-actions.conf";
+
+    private String filename;
+
+    private Log log = LogFactory.getLog(XWorkAnnotationConfigurationProvider.class);
+
+    /**
+     * Creates a new XWorkAnnotationConfigurationProvider object.
+     */
+    public XWorkAnnotationConfigurationProvider() {
+        this(DEFAULT_FILENAME);
+    }
+
+    /**
+     * Creates a new XWorkAnnotationConfigurationProvider object.
+     *
+     * @param filename name of resource to read for list of Action class
+     */
+    public XWorkAnnotationConfigurationProvider(String filename) {
+        this.filename = filename;
+    }
+
+    /**
+     * @see com.opensymphony.xwork.config.ConfigurationProvider#destroy()
+     */
+    public void destroy() {
+    }
+
+    /**
+     * @see com.opensymphony.xwork.config.ConfigurationProvider#init(com.opensymphony.xwork.config.Configuration)
+     */
+    public void init(Configuration config) throws ConfigurationException {
+        InputStream in = getResourceAsStream(filename);
+
+        if (in == null) {
+            throw new ConfigurationException("no resource with named '" + filename
+                    + "' found in the classpath");
+        }
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        String line = null;
+
+        try {
+            while ((line = reader.readLine()) != null) {
+                try {
+                    Class actionClass = Class.forName(line);
+
+                    if ((actionClass.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT) {
+                        log.debug("Skipped. Class '" + line + "' is abstract");
+
+                        continue;
+                    }
+
+                    if (!com.opensymphony.xwork.Action.class
+                            .isAssignableFrom(actionClass)) {
+                        log.debug("Skipped. Class '" + line
+                                + "' does not implement 'com.opensymphony.xwork.Action'");
+
+                        continue;
+                    }
+
+                    Action action = (Action) actionClass.getAnnotation(Action.class);
+
+                    if ((action != null) && !isEmpty(action.name())) {
+                        processActionInType(actionClass, config);
+                    } else {
+                        processActionInMethod(actionClass, config);
+                    }
+                } catch (ClassNotFoundException e) {
+                    throw new ConfigurationException("No class named '" + line
+                            + "' found.'");
+                }
+            }
+        } catch (IOException e) {
+            throw new ConfigurationException(e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param resourceName DOCUMENT ME!
+     * @return DOCUMENT ME!
+     */
+    private InputStream getResourceAsStream(String resourceName) {
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
+
+        return loader.getResourceAsStream(resourceName);
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @param string DOCUMENT ME!
+     * @return DOCUMENT ME!
+     */
+    private boolean isEmpty(String string) {
+        return ((string == null) || "".equals(string.trim()));
+    }
+
+    /**
+     * process annotation which exist in class level
+     *
+     * @param actionClass class instance to process
+     * @param config      configuration instance
+     */
+    private void processActionInType(Class actionClass, Configuration config) {
+        Action action = (Action) actionClass.getAnnotation(Action.class);
+
+        PackageConfig pkgConfig = buildPackageConfig(config, action);
+
+        Results results = (Results) actionClass.getAnnotation(Results.class);
+        Map<String, ResultConfig> resultConfigs = buildResults(results, pkgConfig);
+
+        InterceptorRefs interceptorRefs = (InterceptorRefs) actionClass.getAnnotation(InterceptorRefs.class);
+        List<InterceptorConfig> interceptorRefConfigs = buildInterceptorRefs(interceptorRefs, pkgConfig);
+
+        try {
+            checkActionClassConstructable(actionClass);
+        } catch (Exception e) {
+            log.error("Action class [" + actionClass.getName()
+                    + "] not found, skipping action [" + action.name() + "]", e);
+
+            return;
+        }
+
+        ActionConfig actionConfig = new ActionConfig(null, actionClass, null, resultConfigs, interceptorRefConfigs);
+        pkgConfig.addActionConfig(action.name(), actionConfig);
+    }
+
+    /**
+     * build package config for the action.
+     *
+     * @param config           the configuration instance
+     * @param action           action annotation containing information about the action to create
+     * @param defaultNamespace default namespace if the action does not specify it
+     * @return package config instance
+     */
+    private PackageConfig buildPackageConfig(Configuration config, Action action,
+                                             String defaultNamespace) {
+        String namespace = isEmpty(action.namespace()) ? defaultNamespace : action.namespace();
+        PackageConfig pkgConfig = config.getPackageConfig(namespace);
+
+        if (pkgConfig == null) {
+            pkgConfig = new PackageConfig(namespace, namespace, false, null);
+            config.addPackageConfig(pkgConfig.getName(), pkgConfig);
+        }
+
+        PackageConfig defaultPackageConfig = config.getPackageConfig("default");
+
+        if (defaultPackageConfig != null) {
+            pkgConfig.addParent(defaultPackageConfig);
+        }
+
+        return pkgConfig;
+    }
+
+    /**
+     * calls buildPackageConfig(config, action, "").
+     *
+     * @param config the configuration instance
+     * @param action action annotation containing information about the action to create
+     * @return package config instance
+     * @see #buildPackageConfig(Configuration,Action,String)
+     */
+    private PackageConfig buildPackageConfig(Configuration config, Action action) {
+        return buildPackageConfig(config, action, "");
+    }
+
+    /**
+     * build interceptor refs for the specified package
+     *
+     * @param interceptorRefs interceptorRefs annotation containing information of the interceptorRef to create
+     * @param pkgConfig       the package config instance
+     * @return list of interceptor config
+     */
+    private List<InterceptorConfig> buildInterceptorRefs(InterceptorRefs interceptorRefs, PackageConfig pkgConfig) {
+        List<InterceptorConfig> list = null;
+
+        if (interceptorRefs == null) {
+            return Collections.EMPTY_LIST;
+        }
+
+        list = new ArrayList<InterceptorConfig>();
+
+        for (InterceptorRef interceptorRef : interceptorRefs.value()) {
+            List list2 = InterceptorBuilder.constructInterceptorReference(pkgConfig, interceptorRef.value(), null);
+            list.addAll(list2);
+        }
+
+        return list;
+    }
+
+    /**
+     * process annotation which exist in method level
+     *
+     * @param actionClass class instance to process
+     * @param config      configuration instance
+     */
+    private void processActionInMethod(Class actionClass, Configuration config) {
+        for (Method method : actionClass.getMethods()) {
+            if (!method.isAnnotationPresent(Action.class)
+                    || !checkMethodSignature(method)) {
+                continue;
+            }
+
+            Action classAction = (Action) actionClass.getAnnotation(Action.class);
+            Action action = method.getAnnotation(Action.class);
+            PackageConfig pkgConfig = buildPackageConfig(config, action, (classAction == null) ? "" : classAction.namespace());
+
+            Results defaultResults = (Results) actionClass.getAnnotation(Results.class);
+            Map<String, ResultConfig> defaultResultConfigs = buildResults( defaultResults, pkgConfig);
+
+            Results results = (Results) method.getAnnotation(Results.class);
+            Map<String, ResultConfig> resultConfigs = buildResults(results, pkgConfig);
+
+            // add all results which are declared in class level
+            for (Entry<String, ResultConfig> entry : defaultResultConfigs.entrySet()) {
+                if (resultConfigs.get(entry.getKey()) == null) {
+                    resultConfigs.put(entry.getKey(), entry.getValue());
+                }
+            }
+
+            InterceptorRefs interceptorRefs = (InterceptorRefs) method.getAnnotation(InterceptorRefs.class);
+            List<InterceptorConfig> interceptorRefConfigs = buildInterceptorRefs(interceptorRefs, pkgConfig);
+
+            // use interceptorRef which are declared in class level
+            if (interceptorRefConfigs.size() == 0) {
+                InterceptorRefs defaultInterceptorRefs = (InterceptorRefs) actionClass.getAnnotation(InterceptorRefs.class);
+                interceptorRefConfigs = buildInterceptorRefs(defaultInterceptorRefs, pkgConfig);
+            }
+
+            try {
+                checkActionClassConstructable(actionClass);
+            } catch (Exception e) {
+                log.error("Action class [" + actionClass.getName()
+                        + "] not found, skipping action [" + action.name() + "]", e);
+
+                return;
+            }
+
+            ActionConfig actionConfig = new ActionConfig(null, actionClass, null, resultConfigs, interceptorRefConfigs);
+            pkgConfig.addActionConfig(action.name(), actionConfig);
+        }
+    }
+
+    /**
+     * Check if this action is constructable. Not very sure about this. The guys in xwork team do this too :)
+     *
+     * @param actionClass
+     * @throws Exception
+     */
+    private void checkActionClassConstructable(Class actionClass)
+            throws Exception {
+        if (ObjectFactory.getObjectFactory().isNoArgConstructorRequired()) {
+            ActionConfig actionConfig = new ActionConfig(null, actionClass, null, null, null);
+            // use this for xwork 1.0
+            // ObjectFactory.getObjectFactory().buildAction(actionConfig);
+            ObjectFactory.getObjectFactory().buildAction(null, null, actionConfig, null);
+        }
+    }
+
+    /**
+     * Check whether this method is a valid xwork's "execute" method
+     *
+     * @param method method to check
+     * @return <code>true</code> if the return type is String and have no parameter
+     */
+    private boolean checkMethodSignature(Method method) {
+        return (String.class.equals(method.getReturnType()) && (method.getParameterTypes().length == 0));
+    }
+
+    /**
+     * @return always return <code>false</code>
+     * @see ConfigurationProvider#needsReload()
+     */
+    public boolean needsReload() {
+        return false;
+    }
+
+    /**
+     * build map of result configs for this package
+     *
+     * @param results   list result annotation containing information of the result config
+     * @param pkgConfig the PackageConfig instance
+     * @return map of result configs. empty map if no result created
+     */
+    private Map<String, ResultConfig> buildResults(Results results, PackageConfig pkgConfig) {
+        Map<String, ResultConfig> resultMap = new HashMap<String, ResultConfig>();
+
+        if (results == null) {
+            return resultMap;
+        }
+
+        for (Result result : results.value()) {
+            ResultTypeConfig resultTypeConfig = (ResultTypeConfig) pkgConfig.getAllResultTypeConfigs().get(result.type());
+
+            if (resultTypeConfig == null) {
+                throw new ConfigurationException(
+                        "There is no result type defined for type '" + result.type()
+                                + "' mapped with name '" + result.name() + "'");
+            }
+
+            Map<String, String> params = new HashMap<String, String>();
+
+            if (result.params().length == 0) {
+                try {
+                    String paramName = (String) resultTypeConfig.getDefaultResultParam(); // getClazz().getField("DEFAULT_PARAM").get(null);
+                    String paramValue = result.value();
+
+                    if (paramValue != null) {
+                        paramValue = paramValue.trim();
+                    }
+
+                    params.put(paramName, paramValue);
+                } catch (Throwable t) {
+                }
+            } else {
+                for (Param param : result.params()) {
+                    params.put(param.name(), param.value());
+                }
+            }
+
+            resultMap.put(result.name(), new ResultConfig(result.name(), resultTypeConfig.getClazz(), params));
+        }
+
+        return resultMap;
+    }
+}

tiger/src/java/com/opensymphony/xwork/config/annotations/Action.java

+package com.opensymphony.xwork.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target( { ElementType.TYPE, ElementType.METHOD })
+public @interface Action {
+  String name() default "";
+
+  String namespace() default "";
+}

tiger/src/java/com/opensymphony/xwork/config/annotations/InterceptorRef.java

+package com.opensymphony.xwork.config.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface InterceptorRef {
+  String value();
+}

tiger/src/java/com/opensymphony/xwork/config/annotations/InterceptorRefs.java

+package com.opensymphony.xwork.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target( { ElementType.TYPE, ElementType.METHOD })
+public @interface InterceptorRefs {
+  InterceptorRef[] value();
+}

tiger/src/java/com/opensymphony/xwork/config/annotations/Param.java

+package com.opensymphony.xwork.config.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Param {
+  String name();
+
+  String value();
+}

tiger/src/java/com/opensymphony/xwork/config/annotations/Result.java

+package com.opensymphony.xwork.config.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Result {
+  String name() default "";
+
+  String type() default "";
+
+  String value() default "";
+
+  Param[] params() default {};
+}

tiger/src/java/com/opensymphony/xwork/config/annotations/Results.java

+package com.opensymphony.xwork.config.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target( { ElementType.TYPE, ElementType.METHOD })
+public @interface Results {
+  Result[] value();
+}

tiger/src/test/com/opensymphony/xwork/config/TestOneAction.java

+package com.opensymphony.xwork.config;
+
+import com.opensymphony.xwork.ActionSupport;
+import com.opensymphony.xwork.config.annotations.Result;
+import com.opensymphony.xwork.config.annotations.Action;
+import com.opensymphony.xwork.config.annotations.Results;
+
+
+/**
+ * DOCUMENT ME!
+ * 
+ * $Id: $
+ * 
+ * @author $Author: $
+ * @version $Revision: $
+ */
+@Action(name = "TestOneAction", namespace = "/abc/def")
+public class TestOneAction extends ActionSupport {
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   * 
+   * @throws Exception
+   *           DOCUMENT ME!
+   */
+  @Override
+  @Results( {
+      @Result(name = "success", type = "mock", value = "/test/one/action.vm"),
+      @Result(name = "input", type = "mock", value = "/test/one/action.vm"),
+      @Result(name = "error", type = "mock", value = "/test/one/action.vm") })
+  public String execute() throws Exception {
+    return super.execute();
+  }
+}

tiger/src/test/com/opensymphony/xwork/config/TestTwoAction.java

+package com.opensymphony.xwork.config;
+
+import com.opensymphony.xwork.ActionSupport;
+import com.opensymphony.xwork.config.annotations.Results;
+import com.opensymphony.xwork.config.annotations.Action;
+import com.opensymphony.xwork.config.annotations.Result;
+
+/**
+ * DOCUMENT ME!
+ * 
+ * $Id: $
+ * 
+ * @author $Author: $
+ * @version $Revision: $
+ */
+@Action(namespace = "/abc/def")
+@Results( {
+    @Result(name = "success", type = "mock", value = "/test/one/action1.vm"),
+    @Result(name = "input", type = "mock", value = "/test/one/action2.vm"),
+    @Result(name = "error", type = "mock", value = "/test/one/action3.vm") })
+public class TestTwoAction extends ActionSupport {
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   * 
+   * @throws Exception
+   *           DOCUMENT ME!
+   */
+  @Action(name = "TestTwoAction")
+  public String runMe() throws Exception {
+    return SUCCESS;
+  }
+
+  /**
+   * DOCUMENT ME!
+   * 
+   * @return DOCUMENT ME!
+   * 
+   * @throws Exception
+   *           DOCUMENT ME!
+   */
+  @Action(name = "TestTwoAction2", namespace = "/abc/defg")
+  @Results( {
+      @Result(name = "success", type = "mock", value = "TestTwoAction"),
+      @Result(name = "input", type = "mock", value = "TestTwoAction") })
+  public String justRunIt() throws Exception {
+    return SUCCESS;
+  }
+}

tiger/src/test/com/opensymphony/xwork/config/XWorkAnnotationConfigurationTest.java

+package com.opensymphony.xwork.config;
+
+import com.opensymphony.xwork.ActionProxy;
+import com.opensymphony.xwork.ActionProxyFactory;
+import com.opensymphony.xwork.mock.MockResult;
+import com.opensymphony.xwork.config.entities.ActionConfig;
+import com.opensymphony.xwork.config.entities.PackageConfig;
+import com.opensymphony.xwork.config.entities.ResultConfig;
+import com.opensymphony.xwork.config.impl.DefaultConfiguration;
+import com.opensymphony.xwork.config.providers.XmlConfigurationProvider;
+
+import junit.framework.TestCase;
+
+import java.util.Map;
+
+/**
+ * DOCUMENT ME!
+ *
+ * $Id: $
+ *
+ * @author $Author: $
+ * @version $Revision: $
+ */
+public class XWorkAnnotationConfigurationTest extends TestCase {
+    /**
+     * DOCUMENT ME!
+     */
+    public void testAnnotationLoading() {
+        DefaultConfiguration config = new DefaultConfiguration();
+
+        XmlConfigurationProvider providerXml = new XmlConfigurationProvider("xwork-annotations.xml");
+        providerXml.init(config);
+
+        XWorkAnnotationConfigurationProvider provider = new XWorkAnnotationConfigurationProvider();
+        provider.init(config);
+
+        PackageConfig pkgConfig = config.getPackageConfig("/abc/def");
+        assertNotNull(pkgConfig);
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @throws Exception DOCUMENT ME!
+     */
+    public void testCreateAction() throws Exception {
+        XmlConfigurationProvider providerXml = new XmlConfigurationProvider("xwork-annotations.xml");
+        XWorkAnnotationConfigurationProvider provider = new XWorkAnnotationConfigurationProvider();
+        ConfigurationManager.addConfigurationProvider(providerXml);
+        ConfigurationManager.addConfigurationProvider(provider);
+
+        ActionProxy actionProxy = ActionProxyFactory.getFactory().createActionProxy("/abc/def", "TestOneAction", null);
+
+        assertNotNull(actionProxy);
+        assertEquals("/abc/def", actionProxy.getConfig().getPackageName());
+        assertEquals("TestOneAction", actionProxy.getActionName());
+        assertNotNull(actionProxy.getInvocation().getAction());
+        assertTrue(actionProxy.getInvocation().getAction() instanceof TestOneAction);
+
+        try {
+            ActionProxyFactory.getFactory().createActionProxy("/abc/d", "TestOneAction", null);
+            fail("should not go here");
+        } catch (Exception e) {
+        }
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @throws Exception DOCUMENT ME!
+     */
+    public void testConfiguration2() throws Exception {
+        DefaultConfiguration config = new DefaultConfiguration();
+
+        XmlConfigurationProvider providerXml = new XmlConfigurationProvider("xwork-annotations.xml");
+        providerXml.init(config);
+
+        XWorkAnnotationConfigurationProvider provider = new XWorkAnnotationConfigurationProvider();
+        provider.init(config);
+
+        PackageConfig pkgConfig = config.getPackageConfig("/abc/def");
+        ActionConfig actionConfig = (ActionConfig) pkgConfig.getActionConfigs().get("TestTwoAction");
+        Map results = actionConfig.getResults();
+        assertEquals(3, results.size());
+
+        ResultConfig resultConfig1 = (ResultConfig) results.get("success");
+        assertEquals(MockResult.class.getName(), resultConfig1.getClassName());
+        assertEquals("/test/one/action1.vm", resultConfig1.getParams().get(MockResult.DEFAULT_PARAM));
+
+        ResultConfig resultConfig2 = (ResultConfig) results.get("input");
+        assertEquals(MockResult.class.getName(), resultConfig2.getClassName());
+        assertEquals("/test/one/action2.vm", resultConfig2.getParams().get(MockResult.DEFAULT_PARAM));
+
+        pkgConfig = config.getPackageConfig("/abc/defg");
+
+        ActionConfig actionConfig2 = (ActionConfig) pkgConfig.getActionConfigs().get("TestTwoAction2");
+        Map results2 = actionConfig2.getResults();
+        assertEquals(3, results2.size());
+
+        ResultConfig resultConfig3 = (ResultConfig) results2.get("success");
+        assertEquals(MockResult.class.getName(), resultConfig3.getClassName());
+        assertEquals("TestTwoAction", resultConfig3.getParams().get(MockResult.DEFAULT_PARAM));
+
+        ResultConfig resultConfig4 = (ResultConfig) results2.get("error");
+        assertEquals(MockResult.class.getName(), resultConfig4.getClassName());
+        assertEquals("/test/one/action3.vm", resultConfig4.getParams().get(MockResult.DEFAULT_PARAM));
+    }
+}

tiger/src/test/xwork-actions.conf

+com.opensymphony.xwork.config.TestOneAction
+com.opensymphony.xwork.config.TestTwoAction

tiger/src/test/xwork-annotations.xml

+<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.1.1//EN" "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd">
+<xwork>
+    <package name="xwork-default">
+        <result-types>
+            <result-type name="chain" class="com.opensymphony.xwork.ActionChainResult" default="true"/>
+            <result-type name="void" class="com.opensymphony.xwork.VoidResult"/>
+            <result-type name="mock" class="com.opensymphony.xwork.mock.MockResult"/>
+        </result-types>
+
+        <interceptors>
+            <interceptor name="timer" class="com.opensymphony.xwork.interceptor.TimerInterceptor"/>
+            <interceptor name="logger" class="com.opensymphony.xwork.interceptor.LoggingInterceptor"/>
+            <interceptor name="chain" class="com.opensymphony.xwork.interceptor.ChainingInterceptor"/>
+            <interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>
+            <interceptor name="static-params" class="com.opensymphony.xwork.interceptor.StaticParametersInterceptor"/>
+            <interceptor name="model-driven" class="com.opensymphony.xwork.interceptor.ModelDrivenInterceptor"/>
+            <interceptor name="component" class="com.opensymphony.xwork.interceptor.component.ComponentInterceptor"/>
+            <interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
+            <interceptor name="alias" class="com.opensymphony.xwork.interceptor.AliasInterceptor"/>
+            <interceptor name="test" class="com.opensymphony.xwork.mock.MockInterceptor">
+                <param name="foo">expectedFoo</param>
+            </interceptor>
+            <interceptor name="reference-resolver"
+                         class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>
+
+            <interceptor-stack name="defaultStack">
+                <interceptor-ref name="reference-resolver"/>
+                <interceptor-ref name="static-params"/>
+                <interceptor-ref name="model-driven"/>
+                <interceptor-ref name="params"/>
+            </interceptor-stack>
+
+            <interceptor-stack name="debugStack">
+                <interceptor-ref name="timer"/>
+                <interceptor-ref name="logger"/>
+            </interceptor-stack>
+        </interceptors>
+    </package>
+
+    <package name="default" extends="xwork-default">
+    </package>
+
+</xwork>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.