Adds annotation to support AllowedMethods

Project: http://git-wip-us.apache.org/repos/asf/struts/repo
Commit: http://git-wip-us.apache.org/repos/asf/struts/commit/ab5fb27d
Tree: http://git-wip-us.apache.org/repos/asf/struts/tree/ab5fb27d
Diff: http://git-wip-us.apache.org/repos/asf/struts/diff/ab5fb27d

Branch: refs/heads/master
Commit: ab5fb27d368f8857e137bc3a319c21fc85b519ee
Parents: dd406fb
Author: Lukasz Lenart <lukasz.len...@gmail.com>
Authored: Thu Sep 3 13:03:55 2015 +0200
Committer: Lukasz Lenart <lukasz.len...@gmail.com>
Committed: Thu Sep 3 13:03:55 2015 +0200

----------------------------------------------------------------------
 .../xwork2/config/entities/PackageConfig.java   | 13 ++++-
 .../PackageBasedActionConfigBuilder.java        | 30 ++++++++--
 .../convention/annotation/AllowedMethods.java   | 30 ++++++++++
 .../PackageBasedActionConfigBuilderTest.java    | 60 ++++++++++++++++++--
 .../ClassLevelAllowedMethodsAction.java         | 10 ++++
 .../PackageLevelAllowedMethodsAction.java       |  7 +++
 .../actions/allowedmethods/package-info.java    | 23 ++++++++
 .../PackageLevelAllowedMethodsChildAction.java  |  9 +++
 8 files changed, 171 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java 
b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
index 19ce36f..093985c 100644
--- 
a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
+++ 
b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java
@@ -531,7 +531,18 @@ public class PackageConfig extends Located implements 
Comparable, Serializable,
         }
 
         public Set<String> getGlobalAllowedMethods() {
-            return target.globalAllowedMethods;
+            Set <String> allowedMethods = target.globalAllowedMethods;
+            allowedMethods.addAll(getParentsAllowedMethods(target.parents));
+            return allowedMethods;
+        }
+
+        public Set<String> getParentsAllowedMethods(List<PackageConfig> 
parents) {
+            Set<String> allowedMethods = new HashSet<>();
+            for (PackageConfig parent : parents) {
+                allowedMethods.addAll(parent.globalAllowedMethods);
+                
allowedMethods.addAll(getParentsAllowedMethods(parent.getParents()));
+            }
+            return allowedMethods;
         }
 
         public Builder addGlobalAllowedMethods(Set<String> allowedMethods) {

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
 
b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
index b4a8be9..64a0e98 100644
--- 
a/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
+++ 
b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
@@ -42,6 +42,7 @@ import org.apache.logging.log4j.Logger;
 import org.apache.struts2.StrutsConstants;
 import org.apache.struts2.StrutsException;
 import org.apache.struts2.convention.annotation.*;
+import org.apache.struts2.convention.annotation.AllowedMethods;
 
 import java.io.IOException;
 import java.lang.reflect.Method;
@@ -652,6 +653,8 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
             String actionPackage = actionClass.getPackage().getName();
             LOG.debug("Processing class [{}] in package [{}]", 
actionClass.getName(), actionPackage);
 
+            Set<String> allowedMethods = getAllowedMethods(actionClass);
+
             // Determine the default namespace and action name
             List<String> namespaces = determineActionNamespace(actionClass);
             for (String namespace : namespaces) {
@@ -692,7 +695,7 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
 
                     // Build the default
                     if (!found) {
-                        createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, DEFAULT_METHOD, null);
+                        createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, DEFAULT_METHOD, null, allowedMethods);
                     }
                 }
 
@@ -706,14 +709,14 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
                                     actionClass, action);
                         }
 
-                        createActionConfig(pkgCfg, actionClass, 
defaultActionName, method, action);
+                        createActionConfig(pkgCfg, actionClass, 
defaultActionName, method, action, allowedMethods);
                     }
                 }
 
                 // some actions will not have any @Action or a default method, 
like the rest actions
                 // where the action mapper is the one that finds the right 
method at runtime
                 if (map.isEmpty() && mapAllMatches && actionAnnotation == null 
&& actionsAnnotation == null) {
-                    createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, null, actionAnnotation);
+                    createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, null, actionAnnotation, allowedMethods);
                 }
 
                 //if there are @Actions or @Action at the class level, create 
the mappings for them
@@ -721,9 +724,9 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
                 if (actionsAnnotation != null) {
                     List<Action> actionAnnotations = 
checkActionsAnnotation(actionsAnnotation);
                     for (Action actionAnnotation2 : actionAnnotations)
-                        createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, methodName, actionAnnotation2);
+                        createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, methodName, actionAnnotation2, allowedMethods);
                 } else if (actionAnnotation != null)
-                    createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, methodName, actionAnnotation);
+                    createActionConfig(defaultPackageConfig, actionClass, 
defaultActionName, methodName, actionAnnotation, allowedMethods);
             }
         }
 
@@ -736,6 +739,15 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
         }
     }
 
+    private Set<String> getAllowedMethods(Class<?> actionClass) {
+        AllowedMethods annotation = 
AnnotationUtils.findAnnotation(actionClass, AllowedMethods.class);
+        if (annotation == null) {
+            return Collections.emptySet();
+        } else {
+            return TextParseUtil.commaDelimitedStringToSet(annotation.value());
+        }
+    }
+
     /**
      * Interfaces, enums, annotations, and abstract classes cannot be 
instantiated.
      * @param actionClass class to check
@@ -896,7 +908,7 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
      * @param annotation   The ActionName annotation that might override the 
action name and possibly
      */
     protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?> 
actionClass, String actionName,
-                                      String actionMethod, Action annotation) {
+                                      String actionMethod, Action annotation, 
Set<String> allowedMethods) {
        String className = actionClass.getName();
         if (annotation != null) {
             actionName = annotation.value() != null && 
annotation.value().equals(Action.DEFAULT_VALUE) ? actionName : 
annotation.value();
@@ -909,6 +921,12 @@ public class PackageBasedActionConfigBuilder implements 
ActionConfigBuilder {
         ActionConfig.Builder actionConfig = new 
ActionConfig.Builder(pkgCfg.getName(), actionName, className);
         actionConfig.methodName(actionMethod);
 
+        if (!allowedMethods.contains(actionMethod)) {
+            actionConfig.addAllowedMethod(actionMethod);
+        }
+        actionConfig.addAllowedMethod(allowedMethods);
+        actionConfig.addAllowedMethod(pkgCfg.getGlobalAllowedMethods());
+
         if (LOG.isDebugEnabled()) {
             LOG.debug("Creating action config for class [{}], name [{}] and 
package name [{}] in namespace [{}]",
                     actionClass.toString(), actionName, pkgCfg.getName(), 
pkgCfg.getNamespace());

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java
 
b/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java
new file mode 100644
index 0000000..8e3c9ab
--- /dev/null
+++ 
b/plugins/convention/src/main/java/org/apache/struts2/convention/annotation/AllowedMethods.java
@@ -0,0 +1,30 @@
+package org.apache.struts2.convention.annotation;
+
+import com.opensymphony.xwork2.config.entities.ActionConfig;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <!-- START SNIPPET: javadoc -->
+ * <p>
+ * This annotation allows actions to specify allowed action methods
+ * to limit access to any other public action's methods
+ * </p>
+ *
+ * <p>
+ * This annotation can be used directly on Action classes or
+ * in the <strong>package-info.java</strong> class in order
+ * to specify global allowed methods for all sub-packages.
+ * </p>
+ * <!-- END SNIPPET: javadoc -->
+ */
+@Target({ElementType.TYPE, ElementType.PACKAGE})
+@Retention(value = RetentionPolicy.RUNTIME)
+public @interface AllowedMethods {
+
+    String value() default ActionConfig.DEFAULT_METHOD;
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
 
b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
index 38403a0..61ed3eb 100644
--- 
a/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
+++ 
b/plugins/convention/src/test/java/org/apache/struts2/convention/PackageBasedActionConfigBuilderTest.java
@@ -29,6 +29,7 @@ import com.opensymphony.xwork2.factory.DefaultResultFactory;
 import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.inject.Scope.Strategy;
 import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
+import com.opensymphony.xwork2.util.TextParseUtil;
 import com.opensymphony.xwork2.util.fs.DefaultFileManager;
 import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
 import com.opensymphony.xwork2.util.reflection.ReflectionException;
@@ -37,6 +38,9 @@ import 
org.apache.struts2.convention.actions.DefaultResultPathAction;
 import org.apache.struts2.convention.actions.NoAnnotationAction;
 import org.apache.struts2.convention.actions.Skip;
 import org.apache.struts2.convention.actions.action.*;
+import 
org.apache.struts2.convention.actions.allowedmethods.ClassLevelAllowedMethodsAction;
+import 
org.apache.struts2.convention.actions.allowedmethods.PackageLevelAllowedMethodsAction;
+import 
org.apache.struts2.convention.actions.allowedmethods.sub.PackageLevelAllowedMethodsChildAction;
 import org.apache.struts2.convention.actions.chain.ChainedAction;
 import 
org.apache.struts2.convention.actions.defaultinterceptor.SingleActionNameAction2;
 import 
org.apache.struts2.convention.actions.exception.ExceptionsActionLevelAction;
@@ -125,8 +129,10 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
                 new ResultTypeConfig.Builder("chain",
                         
ActionChainResult.class.getName()).defaultResultParam("actionName").build()};
 
+        Set<String> globalAllowedMethods = 
TextParseUtil.commaDelimitedStringToSet("execute,browse,cancel,input");
+
         PackageConfig strutsDefault = makePackageConfig("struts-default", 
null, null, "dispatcher",
-                defaultResults, defaultInterceptors, defaultInterceptorStacks);
+                defaultResults, defaultInterceptors, defaultInterceptorStacks, 
globalAllowedMethods);
 
         PackageConfig packageLevelParentPkg = 
makePackageConfig("package-level", null, null, null);
         PackageConfig classLevelParentPkg = makePackageConfig("class-level", 
null, null, null);
@@ -151,6 +157,16 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
             "/parentpackage", packageLevelParentPkg, null);
         PackageConfig packageLevelSubPkg = 
makePackageConfig("org.apache.struts2.convention.actions.parentpackage.sub#package-level#/parentpackage/sub",
             "/parentpackage/sub", packageLevelParentPkg, null);
+
+        // Unexpected method call build(class 
org.apache.struts2.convention.actions.allowedmethods.PackageLevelAllowedMethodsAction,
 null, "package-level-allowed-methods", PackageConfig: 
[org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods]
 for namespace [/allowedmethods] with parents [[PackageConfig: [struts-default] 
for namespace [] with parents [[]]]]):
+        PackageConfig packageLevelAllowedMethodsPkg = 
makePackageConfig("org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods",
+                "/allowedmethods", strutsDefault, null);
+        PackageConfig packageLevelAllowedMethodsSubPkg = 
makePackageConfig("org.apache.struts2.convention.actions.allowedmethods.sub#struts-default#/allowedmethods/sub",
+                "/allowedmethods/sub", strutsDefault, null);
+
+        PackageConfig classLevelAllowedMethodsPkg = 
makePackageConfig("org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods",
+                "/allowedmethods", strutsDefault, null);
+
         PackageConfig differentPkg = 
makePackageConfig("org.apache.struts2.convention.actions.parentpackage#class-level#/parentpackage",
             "/parentpackage", classLevelParentPkg, null);
         PackageConfig differentSubPkg = 
makePackageConfig("org.apache.struts2.convention.actions.parentpackage.sub#class-level#/parentpackage/sub",
@@ -261,6 +277,11 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
         expect(resultMapBuilder.build(ClassLevelParentPackageAction.class, 
null, "class-level-parent-package", differentPkg)).andReturn(results);
         
expect(resultMapBuilder.build(ClassLevelParentPackageChildAction.class, null, 
"class-level-parent-package-child", differentSubPkg)).andReturn(results);
 
+        /* org.apache.struts2.convention.actions.allowedmethods */
+        expect(resultMapBuilder.build(ClassLevelAllowedMethodsAction.class, 
null, "class-level-allowed-methods", 
classLevelAllowedMethodsPkg)).andReturn(results);
+        expect(resultMapBuilder.build(PackageLevelAllowedMethodsAction.class, 
null, "package-level-allowed-methods", 
packageLevelAllowedMethodsPkg)).andReturn(results);
+        
expect(resultMapBuilder.build(PackageLevelAllowedMethodsChildAction.class, 
null, "package-level-allowed-methods-child", 
packageLevelAllowedMethodsSubPkg)).andReturn(results);
+
         /* org.apache.struts2.convention.actions.result */
         expect(resultMapBuilder.build(ClassLevelResultAction.class, null, 
"class-level-result", resultPkg)).andReturn(results);
         expect(resultMapBuilder.build(ClassLevelResultsAction.class, null, 
"class-level-results", resultPkg)).andReturn(results);
@@ -450,7 +471,7 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
         verifyActionConfig(pkgConfig, "", 
org.apache.struts2.convention.actions.idx.Index.class, "execute", 
pkgConfig.getName());
         verifyActionConfig(pkgConfig, "index", 
org.apache.struts2.convention.actions.idx.Index.class, "execute", 
pkgConfig.getName());
         verifyActionConfig(pkgConfig, "idx2", 
org.apache.struts2.convention.actions.idx.idx2.Index.class, "execute",
-            
"org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2");
+                
"org.apache.struts2.convention.actions.idx.idx2#struts-default#/idx/idx2");
 
         /* org.apache.struts2.convention.actions.defaultinterceptor */
         pkgConfig = 
configuration.getPackageConfig("org.apache.struts2.convention.actions.defaultinterceptor#struts-default#/defaultinterceptor");
@@ -514,6 +535,33 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
         verifyActionConfig(pkgConfig, "package-level-parent-package-child", 
PackageLevelParentPackageChildAction.class, "execute", pkgConfig.getName());
         assertEquals("package-level", pkgConfig.getParents().get(0).getName());
 
+        /* org.apache.struts2.convention.actions.allowedmethods class level */
+        pkgConfig = 
configuration.getPackageConfig("org.apache.struts2.convention.actions.allowedmethods#struts-default#/allowedmethods");
+        assertNotNull(pkgConfig);
+        assertEquals(2, pkgConfig.getActionConfigs().size());
+        verifyActionConfig(pkgConfig, "class-level-allowed-methods", 
ClassLevelAllowedMethodsAction.class, "execute", pkgConfig.getName());
+        assertEquals("struts-default", 
pkgConfig.getParents().get(0).getName());
+
+        ActionConfig actionConfig = 
pkgConfig.getActionConfigs().get("class-level-allowed-methods");
+        assertEquals(actionConfig.getAllowedMethods().size(), 5);
+        assertTrue(actionConfig.getAllowedMethods().contains("execute"));
+        assertTrue(actionConfig.getAllowedMethods().contains("end"));
+        assertTrue(actionConfig.getAllowedMethods().contains("input"));
+
+        /* org.apache.struts2.convention.actions.allowedmethods.sub package 
level */
+        pkgConfig = 
configuration.getPackageConfig("org.apache.struts2.convention.actions.allowedmethods.sub#struts-default#/allowedmethods/sub");
+        assertNotNull(pkgConfig);
+        assertEquals(1, pkgConfig.getActionConfigs().size());
+        verifyActionConfig(pkgConfig, "package-level-allowed-methods-child", 
PackageLevelAllowedMethodsChildAction.class, "execute", pkgConfig.getName());
+        assertEquals("struts-default", 
pkgConfig.getParents().get(0).getName());
+
+        actionConfig = 
pkgConfig.getActionConfigs().get("package-level-allowed-methods-child");
+        assertEquals(actionConfig.getAllowedMethods().size(), 6);
+        assertTrue(actionConfig.getAllowedMethods().contains("execute"));
+        assertTrue(actionConfig.getAllowedMethods().contains("home"));
+        assertTrue(actionConfig.getAllowedMethods().contains("start"));
+        assertTrue(actionConfig.getAllowedMethods().contains("input"));
+
         /* org.apache.struts2.convention.actions.result */
         pkgConfig = 
configuration.getPackageConfig("org.apache.struts2.convention.actions.result#struts-default#/result");
         assertNotNull(pkgConfig);
@@ -631,12 +679,12 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
 
     private PackageConfig makePackageConfig(String name, String namespace, 
PackageConfig parent,
             String defaultResultType, ResultTypeConfig... results) {
-        return makePackageConfig(name, namespace, parent, defaultResultType, 
results, null, null);
+        return makePackageConfig(name, namespace, parent, defaultResultType, 
results, null, null, null);
     }
 
     private PackageConfig makePackageConfig(String name, String namespace, 
PackageConfig parent,
             String defaultResultType, ResultTypeConfig[] results, 
List<InterceptorConfig> interceptors,
-            List<InterceptorStackConfig> interceptorStacks) {
+            List<InterceptorStackConfig> interceptorStacks, Set<String> 
globalAllowedMethods) {
         PackageConfig.Builder builder = new PackageConfig.Builder(name);
         if (namespace != null) {
             builder.namespace(namespace);
@@ -663,6 +711,10 @@ public class PackageBasedActionConfigBuilderTest extends 
TestCase {
             }
         }
 
+        if (globalAllowedMethods != null) {
+            builder.addGlobalAllowedMethods(globalAllowedMethods);
+        }
+
         return new MyPackageConfig(builder.build());
     }
 

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java
 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java
new file mode 100644
index 0000000..24ea489
--- /dev/null
+++ 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/ClassLevelAllowedMethodsAction.java
@@ -0,0 +1,10 @@
+package org.apache.struts2.convention.actions.allowedmethods;
+
+import org.apache.struts2.convention.annotation.AllowedMethods;
+
+@AllowedMethods("end")
+public class ClassLevelAllowedMethodsAction {
+
+    public String execute() { return null; }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java
 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java
new file mode 100644
index 0000000..eab0895
--- /dev/null
+++ 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/PackageLevelAllowedMethodsAction.java
@@ -0,0 +1,7 @@
+package org.apache.struts2.convention.actions.allowedmethods;
+
+public class PackageLevelAllowedMethodsAction {
+
+    public String execute() { return null; }
+
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java
 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java
new file mode 100644
index 0000000..461131f
--- /dev/null
+++ 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+@org.apache.struts2.convention.annotation.AllowedMethods("home,start")
+package org.apache.struts2.convention.actions.allowedmethods;
+

http://git-wip-us.apache.org/repos/asf/struts/blob/ab5fb27d/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java
----------------------------------------------------------------------
diff --git 
a/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java
 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java
new file mode 100644
index 0000000..4a6223d
--- /dev/null
+++ 
b/plugins/convention/src/test/java/org/apache/struts2/convention/actions/allowedmethods/sub/PackageLevelAllowedMethodsChildAction.java
@@ -0,0 +1,9 @@
+package org.apache.struts2.convention.actions.allowedmethods.sub;
+
+import 
org.apache.struts2.convention.actions.allowedmethods.PackageLevelAllowedMethodsAction;
+
+public class PackageLevelAllowedMethodsChildAction extends 
PackageLevelAllowedMethodsAction {
+
+    public String execute() { return null; }
+
+}

Reply via email to