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