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  */
31 
32 import sun.reflect.annotation.AnnotationParser;
33 
34 import java.lang.annotation.Annotation;
35 import java.lang.annotation.Inherited;
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.lang.reflect.Field;
39 import java.lang.reflect.InvocationTargetException;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.List;
44 import java.util.StringJoiner;
45 
46 public class AnnotationsInheritanceOrderRedefinitionTest {
47 
48     @Retention(RetentionPolicy.RUNTIME)
49     @Inherited
50     @interface Ann1 {
value()51         String value();
52     }
53 
54     @Retention(RetentionPolicy.RUNTIME)
55     @Inherited
56     @interface Ann2 {
value()57         String value();
58     }
59 
60     @Retention(RetentionPolicy.RUNTIME)
61     @Inherited
62     @interface Ann3 {
value()63         String value();
64     }
65 
66     @Ann1("A")
67     @Ann2("A")
68     static class A {}
69 
70     @Ann3("B")
71     static class B extends A {}
72 
73     @Ann1("C")
74     @Ann3("C")
75     static class C extends B {}
76 
main(String[] args)77     public static void main(String[] args) {
78 
79         StringBuilder msgs = new StringBuilder();
80         boolean ok = true;
81 
82         ok &= annotationsEqual(msgs, A.class, true,
83             ann(Ann1.class, "A"), ann(Ann2.class, "A"));
84         ok &= annotationsEqual(msgs, A.class, false,
85             ann(Ann1.class, "A"), ann(Ann2.class, "A"));
86         ok &= annotationsEqual(msgs, B.class, true,
87             ann(Ann3.class, "B"));
88         ok &= annotationsEqual(msgs, B.class, false,
89             ann(Ann1.class, "A"), ann(Ann2.class, "A"), ann(Ann3.class, "B"));
90         ok &= annotationsEqual(msgs, C.class, true,
91             ann(Ann1.class, "C"), ann(Ann3.class, "C"));
92         ok &= annotationsEqual(msgs, C.class, false,
93             ann(Ann1.class, "C"), ann(Ann2.class, "A"), ann(Ann3.class, "C"));
94 
95         Annotation[] declaredAnnotatiosA = A.class.getDeclaredAnnotations();
96         Annotation[] annotationsA = A.class.getAnnotations();
97         Annotation[] declaredAnnotatiosB = B.class.getDeclaredAnnotations();
98         Annotation[] annotationsB = B.class.getAnnotations();
99         Annotation[] declaredAnnotatiosC = C.class.getDeclaredAnnotations();
100         Annotation[] annotationsC = C.class.getAnnotations();
101 
102         incrementClassRedefinedCount(A.class);
103         incrementClassRedefinedCount(B.class);
104         incrementClassRedefinedCount(C.class);
105 
106         ok &= annotationsEqualButNotSame(msgs, A.class, true, declaredAnnotatiosA);
107         ok &= annotationsEqualButNotSame(msgs, A.class, false, annotationsA);
108         ok &= annotationsEqualButNotSame(msgs, B.class, true, declaredAnnotatiosB);
109         ok &= annotationsEqualButNotSame(msgs, B.class, false, annotationsB);
110         ok &= annotationsEqualButNotSame(msgs, C.class, true, declaredAnnotatiosC);
111         ok &= annotationsEqualButNotSame(msgs, C.class, false, annotationsC);
112 
113         if (!ok) {
114             throw new RuntimeException("test failure\n" + msgs);
115         }
116     }
117 
118     // utility methods
119 
annotationsEqualButNotSame(StringBuilder msgs, Class<?> declaringClass, boolean declaredOnly, Annotation[] oldAnns)120     private static boolean annotationsEqualButNotSame(StringBuilder msgs,
121             Class<?> declaringClass, boolean declaredOnly, Annotation[] oldAnns) {
122         if (!annotationsEqual(msgs, declaringClass, declaredOnly, oldAnns)) {
123             return false;
124         }
125         Annotation[] anns = declaredOnly
126                             ? declaringClass.getDeclaredAnnotations()
127                             : declaringClass.getAnnotations();
128         List<Annotation> sameAnns = new ArrayList<>();
129         for (int i = 0; i < anns.length; i++) {
130             if (anns[i] == oldAnns[i]) {
131                 sameAnns.add(anns[i]);
132             }
133         }
134         if (!sameAnns.isEmpty()) {
135             msgs.append(declaredOnly ? "declared " : "").append("annotations for ")
136                 .append(declaringClass.getSimpleName())
137                 .append(" not re-parsed after class redefinition: ")
138                 .append(toSimpleString(sameAnns)).append("\n");
139             return false;
140         } else {
141             return true;
142         }
143     }
144 
annotationsEqual(StringBuilder msgs, Class<?> declaringClass, boolean declaredOnly, Annotation... expectedAnns)145     private static boolean annotationsEqual(StringBuilder msgs,
146             Class<?> declaringClass, boolean declaredOnly, Annotation... expectedAnns) {
147         Annotation[] anns = declaredOnly
148                             ? declaringClass.getDeclaredAnnotations()
149                             : declaringClass.getAnnotations();
150         if (!Arrays.equals(anns, expectedAnns)) {
151             msgs.append(declaredOnly ? "declared " : "").append("annotations for ")
152                 .append(declaringClass.getSimpleName()).append(" are: ")
153                 .append(toSimpleString(anns)).append(", expected: ")
154                 .append(toSimpleString(expectedAnns)).append("\n");
155             return false;
156         } else {
157             return true;
158         }
159     }
160 
ann(Class<? extends Annotation> annotationType, Object value)161     private static Annotation ann(Class<? extends Annotation> annotationType,
162                                   Object value) {
163         return AnnotationParser.annotationForMap(annotationType,
164             Collections.singletonMap("value", value));
165     }
166 
toSimpleString(List<Annotation> anns)167     private static String toSimpleString(List<Annotation> anns) {
168         return toSimpleString(anns.toArray(new Annotation[anns.size()]));
169     }
170 
toSimpleString(Annotation[] anns)171     private static String toSimpleString(Annotation[] anns) {
172         StringJoiner joiner = new StringJoiner(", ");
173         for (Annotation ann : anns) {
174             joiner.add(toSimpleString(ann));
175         }
176         return joiner.toString();
177     }
178 
toSimpleString(Annotation ann)179     private static String toSimpleString(Annotation ann) {
180         Class<? extends Annotation> annotationType = ann.annotationType();
181         Object value;
182         try {
183             value = annotationType.getDeclaredMethod("value").invoke(ann);
184         } catch (IllegalAccessException | InvocationTargetException
185             | NoSuchMethodException e) {
186             throw new RuntimeException(e);
187         }
188         return "@" + annotationType.getSimpleName() + "(" + value + ")";
189     }
190 
191     private static final Field classRedefinedCountField;
192 
193     static {
194         try {
195             classRedefinedCountField = Class.class.getDeclaredField("classRedefinedCount");
196             classRedefinedCountField.setAccessible(true);
197         } catch (NoSuchFieldException e) {
198             throw new Error(e);
199         }
200     }
201 
incrementClassRedefinedCount(Class<?> clazz)202     private static void incrementClassRedefinedCount(Class<?> clazz) {
203         try {
204             classRedefinedCountField.set(clazz,
205                 ((Integer) classRedefinedCountField.get(clazz)) + 1);
206         } catch (IllegalAccessException e) {
207             throw new RuntimeException(e);
208         }
209     }
210 }
211