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 }