AnnotationTypeRuntimeAssumptionTest.java revision 7683:e4ce6502eac0
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 consistent parsing of ex-RUNTIME annotations that 27 * were changed and separately compiled to have CLASS retention 28 */ 29 30import sun.misc.IOUtils; 31 32import java.io.IOException; 33import java.io.InputStream; 34import java.lang.annotation.Retention; 35import java.lang.annotation.RetentionPolicy; 36 37import static java.lang.annotation.RetentionPolicy.CLASS; 38import static java.lang.annotation.RetentionPolicy.RUNTIME; 39 40/** 41 * This test simulates a situation where there are two mutually recursive 42 * {@link RetentionPolicy#RUNTIME RUNTIME} annotations {@link AnnA_v1 AnnA_v1} 43 * and {@link AnnB AnnB} and then the first is changed to have 44 * {@link RetentionPolicy#CLASS CLASS} retention and separately compiled. 45 * When {@link AnnA_v1 AnnA_v1} annotation is looked-up on {@link AnnB AnnB} 46 * it still appears to have {@link RetentionPolicy#RUNTIME RUNTIME} retention. 47 */ 48public class AnnotationTypeRuntimeAssumptionTest { 49 50 @Retention(RUNTIME) 51 @AnnB 52 public @interface AnnA_v1 { 53 } 54 55 // An alternative version of AnnA_v1 with CLASS retention instead. 56 // Used to simulate separate compilation (see AltClassLoader below). 57 @Retention(CLASS) 58 @AnnB 59 public @interface AnnA_v2 { 60 } 61 62 @Retention(RUNTIME) 63 @AnnA_v1 64 public @interface AnnB { 65 } 66 67 @AnnA_v1 68 public static class TestTask implements Runnable { 69 @Override 70 public void run() { 71 AnnA_v1 ann1 = TestTask.class.getDeclaredAnnotation(AnnA_v1.class); 72 if (ann1 != null) { 73 throw new IllegalStateException( 74 "@" + ann1.annotationType().getSimpleName() + 75 " found on: " + TestTask.class.getName() + 76 " should not be visible at runtime"); 77 } 78 AnnA_v1 ann2 = AnnB.class.getDeclaredAnnotation(AnnA_v1.class); 79 if (ann2 != null) { 80 throw new IllegalStateException( 81 "@" + ann2.annotationType().getSimpleName() + 82 " found on: " + AnnB.class.getName() + 83 " should not be visible at runtime"); 84 } 85 } 86 } 87 88 public static void main(String[] args) throws Exception { 89 ClassLoader altLoader = new AltClassLoader( 90 AnnotationTypeRuntimeAssumptionTest.class.getClassLoader()); 91 92 Runnable altTask = (Runnable) Class.forName( 93 TestTask.class.getName(), 94 true, 95 altLoader).newInstance(); 96 97 altTask.run(); 98 } 99 100 /** 101 * A ClassLoader implementation that loads alternative implementations of 102 * classes. If class name ends with "_v1" it locates instead a class with 103 * name ending with "_v2" and loads that class instead. 104 */ 105 static class AltClassLoader extends ClassLoader { 106 AltClassLoader(ClassLoader parent) { 107 super(parent); 108 } 109 110 @Override 111 protected Class<?> loadClass(String name, boolean resolve) 112 throws ClassNotFoundException { 113 if (name.indexOf('.') < 0) { // root package is our class 114 synchronized (getClassLoadingLock(name)) { 115 // First, check if the class has already been loaded 116 Class<?> c = findLoadedClass(name); 117 if (c == null) { 118 c = findClass(name); 119 } 120 if (resolve) { 121 resolveClass(c); 122 } 123 return c; 124 } 125 } 126 else { // not our class 127 return super.loadClass(name, resolve); 128 } 129 } 130 131 @Override 132 protected Class<?> findClass(String name) 133 throws ClassNotFoundException { 134 // special class name -> replace it with alternative name 135 if (name.endsWith("_v1")) { 136 String altName = name.substring(0, name.length() - 3) + "_v2"; 137 String altPath = altName.replace('.', '/').concat(".class"); 138 try (InputStream is = getResourceAsStream(altPath)) { 139 if (is != null) { 140 byte[] bytes = IOUtils.readFully(is, -1, true); 141 // patch class bytes to contain original name 142 for (int i = 0; i < bytes.length - 2; i++) { 143 if (bytes[i] == '_' && 144 bytes[i + 1] == 'v' && 145 bytes[i + 2] == '2') { 146 bytes[i + 2] = '1'; 147 } 148 } 149 return defineClass(name, bytes, 0, bytes.length); 150 } 151 else { 152 throw new ClassNotFoundException(name); 153 } 154 } 155 catch (IOException e) { 156 throw new ClassNotFoundException(name, e); 157 } 158 } 159 else { // not special class name -> just load the class 160 String path = name.replace('.', '/').concat(".class"); 161 try (InputStream is = getResourceAsStream(path)) { 162 if (is != null) { 163 byte[] bytes = IOUtils.readFully(is, -1, true); 164 return defineClass(name, bytes, 0, bytes.length); 165 } 166 else { 167 throw new ClassNotFoundException(name); 168 } 169 } 170 catch (IOException e) { 171 throw new ClassNotFoundException(name, e); 172 } 173 } 174 } 175 } 176} 177