From d5926b7a2196fab0735acac094b6438bb7cf4a94 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 25 Jan 2018 12:27:54 +0100 Subject: [PATCH 1/5] Searches hierarchy down to Object class for annotation --- .../com/opensymphony/xwork2/util/AnnotationUtils.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 42c113f347..c371035267 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java @@ -139,16 +139,13 @@ public static String resolvePropertyName(Method method) { */ public static T findAnnotation(Class clazz, Class annotationClass) { T ann = clazz.getAnnotation(annotationClass); - while (ann == null && clazz != null) { + if (ann == null && clazz != Object.class) { ann = clazz.getAnnotation(annotationClass); if (ann == null) { ann = clazz.getPackage().getAnnotation(annotationClass); } - if (ann == null) { - clazz = clazz.getSuperclass(); - if (clazz != null) { - ann = clazz.getAnnotation(annotationClass); - } + if (ann == null && clazz != Object.class) { + ann = findAnnotation(clazz.getSuperclass(), annotationClass); } } From a3180e3069b225ec857d39d66c3629cd757345ce Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Thu, 25 Jan 2018 12:28:09 +0100 Subject: [PATCH 2/5] Adds test to cover searching down the hierarchy --- .../xwork2/util/AnnotationUtilsTest.java | 8 ++++++ .../xwork2/util/annotation/Dummy3Class.java | 25 +++++++++++++++++++ .../xwork2/util/annotation/MyAnnotation2.java | 2 ++ .../pkg1/AbstractAbstractDummyAction.java | 7 ++++++ .../annotation/pkg1/AbstractDummyAction.java | 4 +++ 5 files changed, 46 insertions(+) create mode 100644 core/src/test/java/com/opensymphony/xwork2/util/annotation/Dummy3Class.java create mode 100644 core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractAbstractDummyAction.java create mode 100644 core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractDummyAction.java 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 c1d7ecafa8..94323831ad 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java @@ -19,9 +19,11 @@ 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.MyAnnotation; +import com.opensymphony.xwork2.util.annotation.MyAnnotation2; import junit.framework.TestCase; /** @@ -41,4 +43,10 @@ public void testFindAnnotationOnPackage() { assertEquals("package-test", ns.value()); } + public void testFindAnnotationOnParents() { + MyAnnotation2 ns = AnnotationUtils.findAnnotation(Dummy3Class.class, MyAnnotation2.class); + assertNotNull(ns); + assertEquals("abstract-abstract", ns.value()); + } + } 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 0000000000..f25c852c79 --- /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/MyAnnotation2.java b/core/src/test/java/com/opensymphony/xwork2/util/annotation/MyAnnotation2.java index baeef0da86..c3700551b3 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 @@ @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 0000000000..1ecc47cf53 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractAbstractDummyAction.java @@ -0,0 +1,7 @@ +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 0000000000..54c1b5d998 --- /dev/null +++ b/core/src/test/java/com/opensymphony/xwork2/util/annotation/pkg1/AbstractDummyAction.java @@ -0,0 +1,4 @@ +package com.opensymphony.xwork2.util.annotation.pkg1; + +public class AbstractDummyAction extends AbstractAbstractDummyAction { +} From a9899b662212b10a73ebc8b9e15a3e95785bf0fe Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Fri, 26 Jan 2018 11:47:10 +0100 Subject: [PATCH 3/5] Introduces new method find all annotations --- .../xwork2/util/AnnotationUtils.java | 32 ++++++++++++++++++- .../xwork2/util/AnnotationUtilsTest.java | 25 ++++++++++++--- .../pkg1/AbstractAbstractDummyAction.java | 18 +++++++++++ .../annotation/pkg1/AbstractDummyAction.java | 18 +++++++++++ .../PackageBasedActionConfigBuilder.java | 15 ++++----- .../PackageBasedActionConfigBuilderTest.java | 6 +++- 6 files changed, 100 insertions(+), 14 deletions(-) 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 c371035267..567c9fd794 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java @@ -21,6 +21,7 @@ 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 +130,7 @@ public static String resolvePropertyName(Method method) { } /** - * 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 @@ -151,4 +152,33 @@ public static T findAnnotation(Class clazz, Class a return ann; } + + /** + * 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 + * @param clazz The class to search for the annotation. + * @param annotationClass The Class of the annotation. + * @return The annotation or null. + */ + public static List findAnnotations(Class clazz, Class annotationClass) { + List anns = new ArrayList<>(); + + T ann = clazz.getAnnotation(annotationClass); + if (ann != null) { + anns.add(ann); + } + + ann = clazz.getPackage().getAnnotation(annotationClass); + if (ann != null) { + anns.add(ann); + } + + if (clazz.getSuperclass() != Object.class) { + anns.addAll(findAnnotations(clazz.getSuperclass(), annotationClass)); + } + + 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 94323831ad..67f1901261 100644 --- a/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/util/AnnotationUtilsTest.java @@ -22,13 +22,15 @@ import com.opensymphony.xwork2.util.annotation.Dummy3Class; import com.opensymphony.xwork2.util.annotation.DummyClass; 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() { @@ -49,4 +51,19 @@ public void testFindAnnotationOnParents() { assertEquals("abstract-abstract", ns.value()); } + public void testFindAnnotationsOnAll() { + List annotations = AnnotationUtils.findAnnotations(DummyClass.class, MyAnnotation.class); + + assertThat(annotations) + .isNotNull() + .isNotEmpty() + .hasSize(2); + + Set values = new HashSet<>(); + for (MyAnnotation annotation : annotations) { + values.add(annotation.value()); + } + assertThat(values).contains("class-test", "package-test"); + } + } 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 index 1ecc47cf53..8221b98f83 100644 --- 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 @@ -1,3 +1,21 @@ +/* + * 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; 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 index 54c1b5d998..88f20a2936 100644 --- 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 @@ -1,3 +1,21 @@ +/* + * 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/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java b/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java index a4e7eceb89..bc586d4ac1 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 @@ protected void buildConfiguration(Set classes) { } 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 @@ protected void createActionConfig(PackageConfig.Builder pkgCfg, Class actionC 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 @@ protected void createActionConfig(PackageConfig.Builder pkgCfg, Class actionC 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 @@ protected List buildExceptionMappings(ExceptionMapping[] 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 6c97dec38d..d9e60f63b4 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 Container getContainer() { 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"); From 46462b3d00f5fb5843ca9a748ea214934ffcd4b5 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Mon, 29 Jan 2018 08:51:17 +0100 Subject: [PATCH 4/5] Rephrases JavaDoc to better express function's meaning --- .../java/com/opensymphony/xwork2/util/AnnotationUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 567c9fd794..1273b5036e 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java @@ -154,13 +154,13 @@ public static T findAnnotation(Class clazz, Class a } /** - * 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. + * 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 The annotation or null. + * @return List of the annotations or an empty list. */ public static List findAnnotations(Class clazz, Class annotationClass) { List anns = new ArrayList<>(); From 72058bf4c6dc7491aee48a519c0e0eb5961b10a5 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Mon, 29 Jan 2018 09:04:03 +0100 Subject: [PATCH 5/5] Reverts findAnnotation to its previous state --- .../com/opensymphony/xwork2/util/AnnotationUtils.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 1273b5036e..c08841c632 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/AnnotationUtils.java @@ -140,13 +140,16 @@ public static String resolvePropertyName(Method method) { */ public static T findAnnotation(Class clazz, Class annotationClass) { T ann = clazz.getAnnotation(annotationClass); - if (ann == null && clazz != Object.class) { + while (ann == null && clazz != null) { ann = clazz.getAnnotation(annotationClass); if (ann == null) { ann = clazz.getPackage().getAnnotation(annotationClass); } - if (ann == null && clazz != Object.class) { - ann = findAnnotation(clazz.getSuperclass(), annotationClass); + if (ann == null) { + clazz = clazz.getSuperclass(); + if (clazz != null) { + ann = clazz.getAnnotation(annotationClass); + } } }