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