1 /*
2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 8011940
27  * @summary Test inheritance, order and class redefinition behaviour of RUNTIME
28  *          class annotations
29  * @author plevart
30  * @modules java.base/java.lang:open
31  *          java.base/sun.reflect.annotation
32  */
33 
34 import sun.reflect.annotation.AnnotationParser;
35 
36 import java.lang.annotation.Annotation;
37 import java.lang.annotation.Inherited;
38 import java.lang.annotation.Retention;
39 import java.lang.annotation.RetentionPolicy;
40 import java.lang.reflect.Field;
41 import java.lang.reflect.InvocationTargetException;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.Collections;
45 import java.util.List;
46 import java.util.StringJoiner;
47 
48 public class AnnotationsInheritanceOrderRedefinitionTest {
49 
50     @Retention(RetentionPolicy.RUNTIME)
51     @Inherited
52     @interface Ann1 {
value()53         String value();
54     }
55 
56     @Retention(RetentionPolicy.RUNTIME)
57     @Inherited
58     @interface Ann2 {
value()59         String value();
60     }
61 
62     @Retention(RetentionPolicy.RUNTIME)
63     @Inherited
64     @interface Ann3 {
value()65         String value();
66     }
67 
68     @Ann1("A")
69     @Ann2("A")
70     static class A {}
71 
72     @Ann3("B")
73     static class B extends A {}
74 
75     @Ann1("C")
76     @Ann3("C")
77     static class C extends B {}
78 
main(String[] args)79     public static void main(String[] args) {
80 
81         StringBuilder msgs = new StringBuilder();
82         boolean ok = true;
83 
84         ok &= annotationsEqual(msgs, A.class, true,
85             ann(Ann1.class, "A"), ann(Ann2.class, "A"));
86         ok &= annotationsEqual(msgs, A.class, false,
87             ann(Ann1.class, "A"), ann(Ann2.class, "A"));
88         ok &= annotationsEqual(msgs, B.class, true,
89             ann(Ann3.class, "B"));
90         ok &= annotationsEqual(msgs, B.class, false,
91             ann(Ann1.class, "A"), ann(Ann2.class, "A"), ann(Ann3.class, "B"));
92         ok &= annotationsEqual(msgs, C.class, true,
93             ann(Ann1.class, "C"), ann(Ann3.class, "C"));
94         ok &= annotationsEqual(msgs, C.class, false,
95             ann(Ann1.class, "C"), ann(Ann2.class, "A"), ann(Ann3.class, "C"));
96 
97         Annotation[] declaredAnnotatiosA = A.class.getDeclaredAnnotations();
98         Annotation[] annotationsA = A.class.getAnnotations();
99         Annotation[] declaredAnnotatiosB = B.class.getDeclaredAnnotations();
100         Annotation[] annotationsB = B.class.getAnnotations();
101         Annotation[] declaredAnnotatiosC = C.class.getDeclaredAnnotations();
102         Annotation[] annotationsC = C.class.getAnnotations();
103 
104         incrementClassRedefinedCount(A.class);
105         incrementClassRedefinedCount(B.class);
106         incrementClassRedefinedCount(C.class);
107 
108         ok &= annotationsEqualButNotSame(msgs, A.class, true, declaredAnnotatiosA);
109         ok &= annotationsEqualButNotSame(msgs, A.class, false, annotationsA);
110         ok &= annotationsEqualButNotSame(msgs, B.class, true, declaredAnnotatiosB);
111         ok &= annotationsEqualButNotSame(msgs, B.class, false, annotationsB);
112         ok &= annotationsEqualButNotSame(msgs, C.class, true, declaredAnnotatiosC);
113         ok &= annotationsEqualButNotSame(msgs, C.class, false, annotationsC);
114 
115         if (!ok) {
116             throw new RuntimeException("test failure\n" + msgs);
117         }
118     }
119 
120     // utility methods
121 
annotationsEqualButNotSame(StringBuilder msgs, Class<?> declaringClass, boolean declaredOnly, Annotation[] oldAnns)122     private static boolean annotationsEqualButNotSame(StringBuilder msgs,
123             Class<?> declaringClass, boolean declaredOnly, Annotation[] oldAnns) {
124         if (!annotationsEqual(msgs, declaringClass, declaredOnly, oldAnns)) {
125             return false;
126         }
127         Annotation[] anns = declaredOnly
128                             ? declaringClass.getDeclaredAnnotations()
129                             : declaringClass.getAnnotations();
130         List<Annotation> sameAnns = new ArrayList<>();
131         for (int i = 0; i < anns.length; i++) {
132             if (anns[i] == oldAnns[i]) {
133                 sameAnns.add(anns[i]);
134             }
135         }
136         if (!sameAnns.isEmpty()) {
137             msgs.append(declaredOnly ? "declared " : "").append("annotations for ")
138                 .append(declaringClass.getSimpleName())
139                 .append(" not re-parsed after class redefinition: ")
140                 .append(toSimpleString(sameAnns)).append("\n");
141             return false;
142         } else {
143             return true;
144         }
145     }
146 
annotationsEqual(StringBuilder msgs, Class<?> declaringClass, boolean declaredOnly, Annotation... expectedAnns)147     private static boolean annotationsEqual(StringBuilder msgs,
148             Class<?> declaringClass, boolean declaredOnly, Annotation... expectedAnns) {
149         Annotation[] anns = declaredOnly
150                             ? declaringClass.getDeclaredAnnotations()
151                             : declaringClass.getAnnotations();
152         if (!Arrays.equals(anns, expectedAnns)) {
153             msgs.append(declaredOnly ? "declared " : "").append("annotations for ")
154                 .append(declaringClass.getSimpleName()).append(" are: ")
155                 .append(toSimpleString(anns)).append(", expected: ")
156                 .append(toSimpleString(expectedAnns)).append("\n");
157             return false;
158         } else {
159             return true;
160         }
161     }
162 
ann(Class<? extends Annotation> annotationType, Object value)163     private static Annotation ann(Class<? extends Annotation> annotationType,
164                                   Object value) {
165         return AnnotationParser.annotationForMap(annotationType,
166             Collections.singletonMap("value", value));
167     }
168 
toSimpleString(List<Annotation> anns)169     private static String toSimpleString(List<Annotation> anns) {
170         return toSimpleString(anns.toArray(new Annotation[anns.size()]));
171     }
172 
toSimpleString(Annotation[] anns)173     private static String toSimpleString(Annotation[] anns) {
174         StringJoiner joiner = new StringJoiner(", ");
175         for (Annotation ann : anns) {
176             joiner.add(toSimpleString(ann));
177         }
178         return joiner.toString();
179     }
180 
toSimpleString(Annotation ann)181     private static String toSimpleString(Annotation ann) {
182         Class<? extends Annotation> annotationType = ann.annotationType();
183         Object value;
184         try {
185             value = annotationType.getDeclaredMethod("value").invoke(ann);
186         } catch (IllegalAccessException | InvocationTargetException
187             | NoSuchMethodException e) {
188             throw new RuntimeException(e);
189         }
190         return "@" + annotationType.getSimpleName() + "(" + value + ")";
191     }
192 
193     private static final Field classRedefinedCountField;
194 
195     static {
196         try {
197             classRedefinedCountField = Class.class.getDeclaredField("classRedefinedCount");
198             classRedefinedCountField.setAccessible(true);
199         } catch (NoSuchFieldException e) {
200             throw new Error(e);
201         }
202     }
203 
incrementClassRedefinedCount(Class<?> clazz)204     private static void incrementClassRedefinedCount(Class<?> clazz) {
205         try {
206             classRedefinedCountField.set(clazz,
207                 ((Integer) classRedefinedCountField.get(clazz)) + 1);
208         } catch (IllegalAccessException e) {
209             throw new RuntimeException(e);
210         }
211     }
212 }
213