1 /*
2  * Copyright (c) 2003, 2021, 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.io.*;
26 import java.lang.instrument.*;
27 import java.security.ProtectionDomain;
28 import java.util.*;
29 
30 
31 /**
32  * Simple tests for the TransformerManager
33  *
34  */
35 public abstract class
36 ATransformerManagementTestCase
37     extends AInstrumentationTestCase
38 {
39     private static final String redefinedClassName = "DummyClass";
40 
41     protected int   kModSamples = 2;
42     public final    ClassFileTransformer[] kTransformerSamples = new ClassFileTransformer[]
43         {
44             new MyClassFileTransformer(         Integer.toString(0)),
45             new MyClassFileTransformer( Integer.toString(1)),
46             new MyClassFileTransformer(         Integer.toString(2)),
47             new MyClassFileTransformer( Integer.toString(3)),
48             new MyClassFileTransformer(         Integer.toString(4)),
49             new MyClassFileTransformer( Integer.toString(5)),
50             new MyClassFileTransformer(         Integer.toString(6)),
51             new MyClassFileTransformer( Integer.toString(7)),
52             new MyClassFileTransformer(         Integer.toString(8)),
53             new MyClassFileTransformer( Integer.toString(9)),
54             new MyClassFileTransformer(         Integer.toString(10)),
55             new MyClassFileTransformer( Integer.toString(11)),
56             new MyClassFileTransformer( Integer.toString(12)),
57             new MyClassFileTransformer( Integer.toString(13)),
58             new MyClassFileTransformer(         Integer.toString(14)),
59             new MyClassFileTransformer( Integer.toString(15)),
60             new MyClassFileTransformer(         Integer.toString(16)),
61             new MyClassFileTransformer(         Integer.toString(17)),
62             new MyClassFileTransformer( Integer.toString(18)),
63         };
64 
65     private ArrayList           fTransformers;       // The list of transformers
66     private int                 fTransformerIndex;   // The number of transformers encountered
67     private String              fDelayedFailure;     // Set non-null if failed in transformer
68 
69 
70     /**
71      * Constructor for ATransformerManagementTestCase.
72      */
ATransformerManagementTestCase(String name)73     public ATransformerManagementTestCase(String name)
74     {
75         super(name);
76     }
77 
78 
79     /**
80      * Returns one of the sample transformers
81      * @return a random transformer
82      */
83     protected ClassFileTransformer
getRandomTransformer()84     getRandomTransformer()
85     {
86         int randIndex = (int)Math.floor(Math.random() * kTransformerSamples.length);
87         verbosePrint("Choosing random transformer #" + randIndex);
88         return kTransformerSamples[randIndex];
89     }
90 
91     /**
92      * Method addTransformerToManager.
93      * @param manager
94      * @param transformer
95      */
96     protected void
addTransformerToManager( Instrumentation manager, ClassFileTransformer transformer)97     addTransformerToManager(
98         Instrumentation         manager,
99         ClassFileTransformer    transformer)
100     {
101         addTransformerToManager(manager, transformer, false);
102     }
103 
104     /**
105      * Method addTransformerToManager.
106      * @param manager
107      * @param transformer
108      * @param canRetransform
109      */
110     protected void
addTransformerToManager( Instrumentation manager, ClassFileTransformer transformer, boolean canRetransform)111     addTransformerToManager(
112         Instrumentation         manager,
113         ClassFileTransformer    transformer,
114         boolean                 canRetransform)
115     {
116         if (transformer != null)
117         {
118             fTransformers.add(transformer);
119         }
120         // workaroud for JDK-8264667: create log message before addTransformer()
121         String msg = "Added transformer " + transformer
122             + " with canRetransform=" + canRetransform;
123         manager.addTransformer(transformer, canRetransform);
124         verbosePrint(msg);
125     }
126 
127     /**
128      * Remove transformer from manager and list
129      * @param manager
130      * @param transformer
131      */
132     protected void
removeTransformerFromManager( Instrumentation manager, ClassFileTransformer transformer)133     removeTransformerFromManager(
134         Instrumentation manager,
135         ClassFileTransformer transformer)
136     {
137         assertTrue("Transformer not found in manager ("+transformer+")", manager.removeTransformer(transformer));
138 
139         if (transformer != null)
140         {
141             fTransformers.remove(transformer);
142         }
143         verbosePrint("Removed transformer " + transformer);
144     }
145 
146     /**
147      * Decrements the transformer index as well as removes transformer
148      * @param fInst         manager
149      * @param transformer       transformer to remove
150      * @param decrementIndex    the tranformer index gets out of sync with transformers
151      *                          that are removed from the manager
152      */
153     protected void
removeTransformerFromManager( Instrumentation manager, ClassFileTransformer transformer, boolean decrementIndex)154     removeTransformerFromManager(   Instrumentation manager,
155                                     ClassFileTransformer transformer,
156                                     boolean decrementIndex)
157     {
158         removeTransformerFromManager(manager, transformer);
159         if (decrementIndex)
160         {
161             fTransformerIndex--;
162             verbosePrint("removeTransformerFromManager fTransformerIndex decremented to: " +
163                          fTransformerIndex);
164         }
165     }
166 
167     /**
168      * verify transformer by asserting that the number of transforms that occured
169      * is the same as the number of valid transformers added to the list.
170      * @param manager
171      */
172     protected void
verifyTransformers(Instrumentation manager)173     verifyTransformers(Instrumentation manager)
174     {
175         File f = new File(System.getProperty("test.classes", "."), redefinedClassName + ".class");
176         System.out.println("Reading test class from " + f);
177         try
178         {
179             InputStream redefineStream = new FileInputStream(f);
180             byte[] bytes = NamedBuffer.loadBufferFromStream(redefineStream);
181             ClassDefinition cd = new ClassDefinition(DummyClass.class, bytes);
182             fInst.redefineClasses(new ClassDefinition[]{ cd });
183             verbosePrint("verifyTransformers redefined " + redefinedClassName);
184         }
185         catch (IOException e)
186         {
187             fail("Could not load the class: " + redefinedClassName);
188         }
189         catch (ClassNotFoundException e)
190         {
191             fail("Could not find the class: " + redefinedClassName);
192         }
193         catch (UnmodifiableClassException e)
194         {
195             fail("Could not modify the class: " + redefinedClassName);
196         }
197 
198         // Report any delayed failures
199         assertTrue(fDelayedFailure, fDelayedFailure == null);
200 
201         assertEquals("The number of transformers that were run does not match the expected number added to manager",
202                         fTransformers.size(), fTransformerIndex);
203     }
204 
205 
206     /**
207      * Asserts that the transformer being checked by the manager is the correct
208      * one (as far as order goes) and updates the number of transformers that have
209      * been called.  Note that since this is being called inside of a transformer,
210      * a standard assert (which throws an exception) cannot be used since it would
211      * simply cancel the transformation and otherwise be ignored.  Instead, note
212      * the failure for delayed processing.
213      * @param ClassFileTransformer
214      */
215     private void
checkInTransformer(ClassFileTransformer transformer)216     checkInTransformer(ClassFileTransformer transformer)
217     {
218         verbosePrint("checkInTransformer: " + transformer);
219         if (fDelayedFailure == null)
220         {
221             if (fTransformers.size() <= fTransformerIndex)
222             {
223                 String msg = "The number of transformers that have checked in (" +(fTransformerIndex+1) +
224                     ") is greater number of tranformers created ("+fTransformers.size()+")";
225                 fDelayedFailure = msg;
226                 System.err.println("Delayed failure: " + msg);
227                 verbosePrint("Delayed failure: " + msg);
228             }
229             if (!fTransformers.get(fTransformerIndex).equals(transformer))
230             {
231                 String msg = "Transformer " + fTransformers.get(fTransformerIndex) +
232                     " should be the same as " + transformer;
233                 fDelayedFailure = msg;
234                 System.err.println("Delayed failure: " + msg);
235                 verbosePrint("Delayed failure: " + msg);
236             }
237             fTransformerIndex++;
238             verbosePrint("fTransformerIndex incremented to: " + fTransformerIndex);
239         }
240     }
241 
242     /**
243      * Create a new manager, a new transformer list, and initializes the number of transformers
244      * indexed to zero.
245      */
246     protected void
setUp()247     setUp()
248         throws Exception
249     {
250         super.setUp();
251         fTransformers = new ArrayList();
252         fTransformerIndex = 0;
253         fDelayedFailure = null;
254         verbosePrint("setUp completed");
255     }
256 
257     /**
258      * Sets the manager and transformers to null so that setUp needs to update.
259      */
260     protected void
tearDown()261     tearDown()
262         throws Exception
263     {
264         verbosePrint("tearDown beginning");
265         fTransformers = null;
266         super.tearDown();
267     }
268 
269     /*
270      *  Simple transformer that registers when it transforms
271      */
272     public class MyClassFileTransformer extends SimpleIdentityTransformer {
273         private final String fID;
274 
MyClassFileTransformer(String id)275         public MyClassFileTransformer(String id) {
276             super();
277             fID = id;
278         }
279 
toString()280         public String toString() {
281             return MyClassFileTransformer.this.getClass().getName() + fID;
282         }
283 
284         public byte[]
transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)285         transform(
286             ClassLoader loader,
287             String className,
288             Class<?> classBeingRedefined,
289             ProtectionDomain    protectionDomain,
290             byte[] classfileBuffer) {
291 
292             // The transform testing is triggered by redefine, ignore others
293             if (classBeingRedefined != null) checkInTransformer(MyClassFileTransformer.this);
294 
295             return super.transform(     loader,
296                                         className,
297                                         classBeingRedefined,
298                                         protectionDomain,
299                                         classfileBuffer);
300         }
301 
302         public byte[]
transform( Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)303         transform(
304             Module module,
305             ClassLoader loader,
306             String className,
307             Class<?> classBeingRedefined,
308             ProtectionDomain    protectionDomain,
309             byte[] classfileBuffer) {
310 
311             // The transform testing is triggered by redefine, ignore others
312             if (classBeingRedefined != null) checkInTransformer(MyClassFileTransformer.this);
313 
314             return super.transform(     module,
315                                         loader,
316                                         className,
317                                         classBeingRedefined,
318                                         protectionDomain,
319                                         classfileBuffer);
320         }
321     }
322 
323 
324     /**
325      * Class loader that does nothing
326      */
327     public class MyClassLoader extends ClassLoader
328     {
329         /**
330          * Constructor for MyClassLoader.
331          */
MyClassLoader()332         public MyClassLoader()
333         {
334             super();
335         }
336 
337     }
338 
debug_byteArrayToString(byte[] b)339     public String debug_byteArrayToString(byte[] b) {
340         if (b == null) return "null";
341 
342         StringBuffer buf = new StringBuffer();
343         buf.append("byte[");
344         buf.append(b.length);
345         buf.append("] (");
346         for (int i = 0; i < b.length; i++)
347         {
348             buf.append(b[i]);
349             buf.append(",");
350         }
351         buf.deleteCharAt(buf.length()-1);
352         buf.append(")");
353 
354         return buf.toString();
355     }
356 }
357