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