1 /* 2 * Copyright (c) 2014, 2017, 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 import java.lang.instrument.ClassDefinition; 26 import java.lang.instrument.Instrumentation; 27 import java.lang.instrument.UnmodifiableClassException; 28 import java.net.URL; 29 import java.net.URLClassLoader; 30 import java.io.File; 31 import java.security.CodeSigner; 32 import java.security.CodeSource; 33 import java.security.ProtectionDomain; 34 import sun.hotspot.WhiteBox; 35 36 public class InstrumentationApp { 37 static WhiteBox wb = WhiteBox.getWhiteBox(); 38 39 public static final String COO_CLASS_NAME = "InstrumentationApp$Coo"; 40 41 public static interface Intf { // Loaded from Boot class loader (-Xbootclasspath/a). 42 public String get(); 43 } 44 public static class Bar implements Intf { // Loaded from Boot class loader. 45 public String get() { 46 // The initial transform: 47 // change "buzz" -> "fuzz" 48 // The re-transform: 49 // change "buzz" -> "guzz" 50 return "buzz"; 51 } 52 } 53 public static class Foo implements Intf { // Loaded from AppClassLoader, or from a custom loader 54 public String get() { 55 // The initial transform: 56 // change "buzz" -> "fuzz" 57 // The re-transform: 58 // change "buzz" -> "guzz" 59 return "buzz"; 60 } 61 } 62 public static class Coo implements Intf { // Loaded from custom class loader. 63 public String get() { 64 // The initial transform: 65 // change "buzz" -> "fuzz" 66 // The re-transform: 67 // change "buzz" -> "guzz" 68 return "buzz"; 69 } 70 } 71 72 // This class file should be archived if AppCDSv2 is enabled on this platform. See 73 // the comments around the call to TestCommon.dump in InstrumentationTest.java. 74 public static class ArchivedIfAppCDSv2Enabled {} 75 76 public static boolean isAppCDSV2Enabled() { 77 return wb.isSharedClass(ArchivedIfAppCDSv2Enabled.class); 78 } 79 80 public static class MyLoader extends URLClassLoader { 81 public MyLoader(URL[] urls, ClassLoader parent, File jar) { 82 super(urls, parent); 83 this.jar = jar; 84 } 85 File jar; 86 87 @Override 88 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 89 synchronized (getClassLoadingLock(name)) { 90 // First, check if the class has already been loaded 91 Class<?> clz = findLoadedClass(name); 92 if (clz != null) { 93 return clz; 94 } 95 96 if (name.equals(COO_CLASS_NAME)) { 97 try { 98 byte[] buff = Util.getClassFileFromJar(jar, name); 99 return defineClass(name, buff, 0, buff.length); 100 } catch (Throwable t) { 101 t.printStackTrace(); 102 throw new RuntimeException("Unexpected", t); 103 } 104 } 105 } 106 return super.loadClass(name, resolve); 107 } 108 } 109 110 static int numTests = 0; 111 static int failed = 0; 112 static boolean isAttachingAgent = false; 113 static Instrumentation instrumentation; 114 115 public static void main(String args[]) throws Throwable { 116 System.out.println("INFO: AppCDSv1 " + (wb.isSharedClass(InstrumentationApp.class) ? "enabled" :"disabled")); 117 System.out.println("INFO: AppCDSv2 " + (isAppCDSV2Enabled() ? "enabled" : "disabled")); 118 119 String flagFile = args[0]; 120 File bootJar = new File(args[1]); 121 File appJar = new File(args[2]); 122 File custJar = new File(args[3]); 123 waitAttach(flagFile); 124 125 instrumentation = InstrumentationRegisterClassFileTransformer.getInstrumentation(); 126 System.out.println("INFO: instrumentation = " + instrumentation); 127 128 testBootstrapCDS("Bootstrap Loader", bootJar); 129 testAppCDSv1("Application Loader", appJar); 130 131 if (isAppCDSV2Enabled()) { 132 testAppCDSv2("Custom Loader (unregistered)", custJar); 133 } 134 135 if (failed > 0) { 136 throw new RuntimeException("FINAL RESULT: " + failed + " out of " + numTests + " test case(s) have failed"); 137 } else { 138 System.out.println("FINAL RESULT: All " + numTests + " test case(s) have passed!"); 139 } 140 } 141 142 static void waitAttach(String flagFile) throws Throwable { 143 if (!flagFile.equals("noattach")) { 144 File f = new File(flagFile); 145 long start = System.currentTimeMillis(); 146 while (f.exists()) { 147 long elapsed = System.currentTimeMillis() - start; 148 System.out.println(".... (" + elapsed + ") waiting for deletion of " + f); 149 Thread.sleep(1000); 150 } 151 System.out.println("Attach succeeded (child)"); 152 isAttachingAgent = true; 153 } 154 } 155 156 static void testBootstrapCDS(String group, File jar) throws Throwable { 157 doTest(group, new Bar(), jar); 158 } 159 160 static void testAppCDSv1(String group, File jar) throws Throwable { 161 doTest(group, new Foo(), jar); 162 } 163 164 static void testAppCDSv2(String group, File jar) throws Throwable { 165 URL[] urls = new URL[] {jar.toURI().toURL()}; 166 MyLoader loader = new MyLoader(urls, InstrumentationApp.class.getClassLoader(), jar); 167 Class klass = loader.loadClass(COO_CLASS_NAME); 168 doTest(group, (Intf)klass.newInstance(), jar); 169 } 170 171 static void doTest(String group, Intf object, File jar) throws Throwable { 172 Class klass = object.getClass(); 173 System.out.println(); 174 System.out.println("++++++++++++++++++++++++++"); 175 System.out.println("Test group: " + group); 176 System.out.println("Testing with classloader = " + klass.getClassLoader()); 177 System.out.println("Testing with class = " + klass); 178 System.out.println("++++++++++++++++++++++++++"); 179 180 // Initial transform 181 String f = object.get(); 182 assertTrue(f.equals("fuzz"), "object.get(): Initial transform should give 'fuzz'", f); 183 184 // Retransform 185 f = "(failed)"; 186 try { 187 instrumentation.retransformClasses(klass); 188 f = object.get(); 189 } catch (UnmodifiableClassException|UnsupportedOperationException e) { 190 e.printStackTrace(); 191 } 192 assertTrue(f.equals("guzz"), "object.get(): retransformation should give 'guzz'", f); 193 194 // Redefine 195 byte[] buff = Util.getClassFileFromJar(jar, klass.getName()); 196 Util.replace(buff, "buzz", "huzz"); 197 f = "(failed)"; 198 try { 199 instrumentation.redefineClasses(new ClassDefinition(klass, buff)); 200 f = object.get(); 201 } catch (UnmodifiableClassException|UnsupportedOperationException e) { 202 e.printStackTrace(); 203 } 204 assertTrue(f.equals("quzz"), "object.get(): redefinition should give 'quzz'", f); 205 206 System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++ (done)\n\n"); 207 } 208 209 private static void assertTrue(boolean expr, String msg, String string) { 210 numTests ++; 211 System.out.printf("Test case %2d ", numTests); 212 213 if (expr) { 214 System.out.println("PASSED: " + msg + " and we got '" + string + "'"); 215 } else { 216 failed ++; 217 System.out.println("FAILED: " + msg + " but we got '" + string + "'"); 218 } 219 } 220 }