diff --git a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java index 42c113f34..cbebc8003 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java @@ -18,9 +18,12 @@ */ package com.opensymphony.xwork2.util; +import org.apache.commons.lang3.ClassUtils; + import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; @@ -129,7 +132,7 @@ public class AnnotationUtils { } /** - * Returns the annotation on the given class or the package of the class. This searchs up the + * Returns the annotation on the given class or the package of the class. This searches up the * class hierarchy and the package hierarchy for the closest match. * * @param class type @@ -154,4 +157,36 @@ public class AnnotationUtils { return ann; } + + /** + * Returns a list of the annotation on the given class or the package of the class. + * This searches up the class hierarchy and the package hierarchy. + * + * @param class type + * @param clazz The class to search for the annotation. + * @param annotationClass The Class of the annotation. + * @return List of the annotations or an empty list. + */ + public static List findAnnotations(Class clazz, Class annotationClass) { + List anns = new ArrayList<>(); + + List> classes = new ArrayList<>(); + classes.add(clazz); + + classes.addAll(ClassUtils.getAllSuperclasses(clazz)); + classes.addAll(ClassUtils.getAllInterfaces(clazz)); + for (Class aClass : classes) { + T ann = aClass.getAnnotation(annotationClass); + if (ann != null) { + anns.add(ann); + } + + ann = aClass.getPackage().getAnnotation(annotationClass); + if (ann != null) { + anns.add(ann); + } + } + + return anns; + } } diff --git a/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java b/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java index c1d7ecafa..16fbab4fe 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java @@ -19,14 +19,19 @@ package com.opensymphony.xwork2.util; import com.opensymphony.xwork2.util.annotation.Dummy2Class; +import com.opensymphony.xwork2.util.annotation.Dummy3Class; import com.opensymphony.xwork2.util.annotation.DummyClass; +import com.opensymphony.xwork2.util.annotation.DummyClassExt; import com.opensymphony.xwork2.util.annotation.MyAnnotation; - +import com.opensymphony.xwork2.util.annotation.MyAnnotation2; import junit.framework.TestCase; -/** - * @author Dan Oxlade, dan d0t oxlade at gmail d0t c0m - */ +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.fest.assertions.Assertions.assertThat; + public class AnnotationUtilsTest extends TestCase { public void testFindAnnotationOnClass() { @@ -41,4 +46,25 @@ public class AnnotationUtilsTest extends TestCase { assertEquals("package-test", ns.value()); } + public void testFindAnnotationOnParents() { + MyAnnotation2 ns = AnnotationUtils.findAnnotation(Dummy3Class.class, MyAnnotation2.class); + assertNotNull(ns); + assertEquals("abstract-abstract", ns.value()); + } + + public void testFindAnnotationsOnAll() { + List annotations = AnnotationUtils.findAnnotations(DummyClassExt.class, MyAnnotation.class); + + assertThat(annotations) + .isNotNull() + .isNotEmpty() + .hasSize(5); + + Set values = new HashSet<>(); + for (MyAnnotation annotation : annotations) { + values.add(annotation.value()); + } + assertThat(values).contains("class-test", "package-test", "interface-test", "package2-test"); + } + } diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/Dummy3Class.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/Dummy3Class.java new file mode 100644 index 000000000..f25c852c7 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/Dummy3Class.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +package com.opensymphony.xwork2.util.annotation; + +import com.opensymphony.xwork2.util.annotation.pkg1.AbstractDummyAction; + +public class Dummy3Class extends AbstractDummyAction{ + +} diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java index 707b73592..bb65b5c6e 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/DummyClass.java @@ -18,8 +18,10 @@ */ package com.opensymphony.xwork2.util.annotation; +import com.opensymphony.xwork2.util.annotation.pkg2.Package2DummyInterface; + @MyAnnotation("class-test") -public class DummyClass { +public class DummyClass implements Package2DummyInterface { public DummyClass() { } diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotation2.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotation2.java index baeef0da8..c3700551b 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotation2.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotation2.java @@ -23,4 +23,6 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation2 { + + String value() default ""; } diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractAbstractDummyAction.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractAbstractDummyAction.java new file mode 100644 index 000000000..8221b98f8 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractAbstractDummyAction.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +package com.opensymphony.xwork2.util.annotation.pkg1; + +import com.opensymphony.xwork2.util.annotation.MyAnnotation2; + +@MyAnnotation2("abstract-abstract") +public class AbstractAbstractDummyAction { +} diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractDummyAction.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractDummyAction.java new file mode 100644 index 000000000..88f20a293 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractDummyAction.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +package com.opensymphony.xwork2.util.annotation.pkg1; + +public class AbstractDummyAction extends AbstractAbstractDummyAction { +} diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg2/Package2DummyInterface.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg2/Package2DummyInterface.java new file mode 100644 index 000000000..ea1aa1279 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg2/Package2DummyInterface.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +package com.opensymphony.xwork2.util.annotation.pkg2; + +import com.opensymphony.xwork2.util.annotation.MyAnnotation; + +@MyAnnotation("interface-test") +public interface Package2DummyInterface { +} diff --git a/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg2/package-info.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg2/package-info.java new file mode 100644 index 000000000..2eee0a823 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg2/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +@MyAnnotation("package2-test") +package com.opensymphony.xwork2.util.annotation.pkg2; + +import com.opensymphony.xwork2.util.annotation.MyAnnotation; \ No newline at end of file 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 a4e7eceb8..bc586d4ac 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 @@ -749,13 +749,13 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { } private Set getAllowedMethods(Class actionClass) { - AllowedMethods annotation = AnnotationUtils.findAnnotation(actionClass, AllowedMethods.class); - if (annotation == null) { + List annotations = AnnotationUtils.findAnnotations(actionClass, AllowedMethods.class); + if (annotations == null || annotations.isEmpty()) { return Collections.emptySet(); } else { Set methods = new HashSet<>(); - for (String method : annotation.value()) { - methods.add(method); + for (AllowedMethods allowedMethods : annotations) { + methods.addAll(Arrays.asList(allowedMethods.value())); } return methods; } @@ -924,7 +924,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { String actionMethod, Action annotation, Set allowedMethods) { String className = actionClass.getName(); if (annotation != null) { - actionName = annotation.value() != null && annotation.value().equals(Action.DEFAULT_VALUE) ? actionName : annotation.value(); + actionName = annotation.value().equals(Action.DEFAULT_VALUE) ? actionName : annotation.value(); actionName = StringUtils.contains(actionName, "/") && !slashesInActionNames ? StringUtils.substringAfterLast(actionName, "/") : actionName; if(!Action.DEFAULT_VALUE.equals(annotation.className())){ className = annotation.className(); @@ -960,7 +960,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { actionConfig.addParams(StringTools.createParameterMap(annotation.params())); //add exception mappings from annotation - if (annotation != null && annotation.exceptionMappings() != null) + if (annotation != null) actionConfig.addExceptionMappings(buildExceptionMappings(annotation.exceptionMappings(), actionName)); //add exception mapping from class @@ -997,8 +997,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder { exceptionMapping.result(), actionName); ExceptionMappingConfig.Builder builder = new ExceptionMappingConfig.Builder(null, exceptionMapping .exception(), exceptionMapping.result()); - if (exceptionMapping.params() != null) - builder.addParams(StringTools.createParameterMap(exceptionMapping.params())); + builder.addParams(StringTools.createParameterMap(exceptionMapping.params())); exceptionMappings.add(builder.build()); } 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 6c97dec38..d9e60f63b 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 @@ -544,10 +544,14 @@ public class PackageBasedActionConfigBuilderTest extends TestCase { assertEquals("struts-default", pkgConfig.getParents().get(0).getName()); ActionConfig actionConfig = pkgConfig.getActionConfigs().get("class-level-allowed-methods"); - assertEquals(actionConfig.getAllowedMethods().size(), 5); + assertEquals(7, actionConfig.getAllowedMethods().size()); assertTrue(actionConfig.getAllowedMethods().contains("execute")); assertTrue(actionConfig.getAllowedMethods().contains("end")); assertTrue(actionConfig.getAllowedMethods().contains("input")); + assertTrue(actionConfig.getAllowedMethods().contains("cancel")); + assertTrue(actionConfig.getAllowedMethods().contains("start")); + assertTrue(actionConfig.getAllowedMethods().contains("home")); + assertTrue(actionConfig.getAllowedMethods().contains("browse")); /* org.apache.struts2.convention.actions.allowedmethods.sub package level */ pkgConfig = configuration.getPackageConfig("org.apache.struts2.convention.actions.allowedmethods.sub#struts-default#/allowedmethods/sub");