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