KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > hook > ClassLoaderPatcher


1 /**************************************************************************************
2  * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

8 package org.codehaus.aspectwerkz.hook;
9
10 import com.sun.jdi.Bootstrap;
11 import com.sun.jdi.ReferenceType;
12 import com.sun.jdi.VirtualMachine;
13 import com.sun.jdi.connect.AttachingConnector;
14 import com.sun.jdi.connect.Connector;
15 import com.sun.jdi.connect.IllegalConnectorArgumentsException;
16
17 import java.io.BufferedOutputStream JavaDoc;
18 import java.io.ByteArrayOutputStream JavaDoc;
19 import java.io.DataOutputStream JavaDoc;
20 import java.io.File JavaDoc;
21 import java.io.FileOutputStream JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.lang.reflect.InvocationTargetException JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26 import java.net.ConnectException JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31
32 /**
33  * Utility methods to manipulate class redefinition of java.lang.ClassLoader in xxxStarter
34  *
35  * @author <a HREF="mailto:alex@gnilux.com">Alexandre Vasseur </a>
36  */

37 public class ClassLoaderPatcher {
38     /**
39      * Converts an input stream to a byte[]
40      */

41     public static byte[] inputStreamToByteArray(InputStream JavaDoc is) throws IOException JavaDoc {
42         ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
43         for (int b = is.read(); b != -1; b = is.read()) {
44             os.write(b);
45         }
46         return os.toByteArray();
47     }
48
49     /**
50      * Gets the bytecode of the modified java.lang.ClassLoader using given ClassLoaderPreProcessor class name
51      */

52     static byte[] getPatchedClassLoader(String JavaDoc preProcessorName) {
53         byte[] abyte = null;
54         InputStream JavaDoc is = null;
55         try {
56             is = ClassLoader.getSystemClassLoader().getParent().getResourceAsStream("java/lang/ClassLoader.class");
57             abyte = inputStreamToByteArray(is);
58         } catch (IOException JavaDoc e) {
59             throw new Error JavaDoc("failed to read java.lang.ClassLoader: " + e.toString());
60         } finally {
61             try {
62                 is.close();
63             } catch (Exception JavaDoc e) {
64                 ;
65             }
66         }
67         if (preProcessorName != null) {
68             try {
69                 ClassLoaderPreProcessor clpi = (ClassLoaderPreProcessor) Class.forName(preProcessorName).newInstance();
70                 abyte = clpi.preProcess(abyte);
71             } catch (Exception JavaDoc e) {
72                 System.err.println("failed to instrument java.lang.ClassLoader: preprocessor not found");
73                 e.printStackTrace();
74             }
75         }
76         return abyte;
77     }
78
79     /**
80      * Dump bytecode bytes in dir/className.class directory, created if needed
81      */

82     private static void writeClass(String JavaDoc className, byte[] bytes, String JavaDoc dir) {
83         String JavaDoc filename = dir + File.separatorChar + className.replace('.', File.separatorChar) + ".class";
84         int pos = filename.lastIndexOf(File.separatorChar);
85         if (pos > 0) {
86             String JavaDoc finalDir = filename.substring(0, pos);
87             (new File JavaDoc(finalDir)).mkdirs();
88         }
89         try {
90             DataOutputStream JavaDoc out = new DataOutputStream JavaDoc(new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(filename)));
91             out.write(bytes);
92             out.close();
93         } catch (IOException JavaDoc e) {
94             System.err.println("failed to write " + className + " in " + dir);
95             e.printStackTrace();
96         }
97     }
98
99     /**
100      * HotSwap className in target VM
101      */

102     private static void redefineClass(VirtualMachine vm, String JavaDoc className, byte[] bytes) {
103         // determine if VM support class HotSwap with introspection
104
try {
105             Method JavaDoc canM = VirtualMachine.class.getMethod("canRedefineClasses", new Class JavaDoc[]{});
106             if (((Boolean JavaDoc) canM.invoke(vm, new Object JavaDoc[]{})).equals(Boolean.FALSE)) {
107                 throw new Error JavaDoc("target JVM cannot redefine classes, please force the use of -Xbootclasspath");
108             }
109             List JavaDoc classList = vm.classesByName(className);
110             if (classList.size() == 0) {
111                 throw new Error JavaDoc("Fatal error: Can't find class " + className);
112             }
113             ReferenceType rt = (ReferenceType) classList.get(0);
114             Map JavaDoc map = new HashMap JavaDoc();
115             map.put(rt, bytes);
116             Method JavaDoc doM = VirtualMachine.class.getMethod(
117                     "redefineClasses", new Class JavaDoc[]{
118                         Map JavaDoc.class
119                     }
120             );
121             doM.invoke(
122                     vm, new Object JavaDoc[]{
123                         map
124                     }
125             );
126         } catch (NoSuchMethodException JavaDoc e) {
127             // java 1.3 or not HotSwap compatible JVM
128
throw new Error JavaDoc("target JVM cannot redefine classes, please force the use of -Xbootclasspath");
129         } catch (InvocationTargetException JavaDoc e) {
130             // java 1.4+ failure
131
System.err.println("failed to HotSwap " + className + ':');
132             e.getTargetException().printStackTrace();
133             throw new Error JavaDoc("try to force force the use of -Xbootclasspath");
134         } catch (IllegalAccessException JavaDoc e) {
135             // java 1.4+ failure
136
System.err.println("failed to HotSwap " + className + ':');
137             e.printStackTrace();
138             throw new Error JavaDoc("try to force force the use of -Xbootclasspath");
139         }
140     }
141
142     /**
143      * Patch java.lang.ClassLoader with preProcessorName instance and dump class bytecode in dir
144      */

145     public static void patchClassLoader(String JavaDoc preProcessorName, String JavaDoc dir) {
146         byte[] cl = getPatchedClassLoader(preProcessorName);
147         writeClass("java.lang.ClassLoader", cl, dir);
148     }
149
150     /**
151      * Patch java.lang.ClassLoader with preProcessorName instance and hotswap in target VM using a JDWP attaching
152      * connector Don't wait before connecting
153      */

154     public static VirtualMachine hotswapClassLoader(String JavaDoc preProcessorName, String JavaDoc transport, String JavaDoc address) {
155         return hotswapClassLoader(preProcessorName, transport, address, 0);
156     }
157
158     /**
159      * Patch java.lang.ClassLoader with preProcessorName instance and hotswap in target VM using a JDWP attaching
160      * connector
161      */

162     public static VirtualMachine hotswapClassLoader(String JavaDoc preProcessorName,
163                                                     String JavaDoc transport,
164                                                     String JavaDoc address,
165                                                     int secondsToWait) {
166         String JavaDoc name = null;
167         if ("dt_socket".equals(transport)) {
168             name = "com.sun.jdi.SocketAttach";
169         } else if ("dt_shmem".equals(transport)) {
170             name = "com.sun.jdi.SharedMemoryAttach";
171         }
172         AttachingConnector connector = null;
173         for (Iterator JavaDoc i = Bootstrap.virtualMachineManager().attachingConnectors().iterator(); i.hasNext();) {
174             AttachingConnector aConnector = (AttachingConnector) i.next();
175             if (aConnector.name().equals(name)) {
176                 connector = aConnector;
177                 break;
178             }
179         }
180         if (connector == null) {
181             throw new Error JavaDoc("no AttachingConnector for transport: " + transport);
182         }
183         Map JavaDoc args = connector.defaultArguments();
184         if ("dt_socket".equals(transport)) {
185             ((Connector.Argument) args.get("port")).setValue(address);
186         } else if ("dt_shmem".equals(transport)) {
187             ((Connector.Argument) args.get("name")).setValue(address);
188         }
189         try {
190             if (secondsToWait > 0) {
191                 try {
192                     Thread.sleep(1000 * secondsToWait);
193                 } catch (Exception JavaDoc e) {
194                     ;
195                 }
196             }
197
198             // loop 10 times, during 5 sec max. It appears some VM under Linux take time to accept
199
// connections
200
// this avoid to specifically set -Daspectwerkz.classloader.wait
201
VirtualMachine vm = null;
202             ConnectException JavaDoc vmConnectionRefused = new ConnectException JavaDoc("should not appear as is");
203             for (int retry = 0; retry < 10; retry++) {
204                 try {
205                     vm = connector.attach(args);
206                     break;
207                 } catch (ConnectException JavaDoc ce) {
208                     vmConnectionRefused = ce;
209                     try {
210                         Thread.sleep(500);
211                     } catch (Throwable JavaDoc t) {
212                         ;
213                     }
214                 }
215             }
216             if (vm == null) {
217                 throw vmConnectionRefused;
218             }
219             redefineClass(vm, "java.lang.ClassLoader", getPatchedClassLoader(preProcessorName));
220             return vm;
221         } catch (IllegalConnectorArgumentsException e) {
222             System.err.println("failed to attach to VM (" + transport + ", " + address + "):");
223             e.printStackTrace();
224             for (Iterator JavaDoc i = e.argumentNames().iterator(); i.hasNext();) {
225                 System.err.println("wrong or missing argument - " + i.next());
226             }
227             return null;
228         } catch (IOException JavaDoc e) {
229             System.err.println("failed to attach to VM (" + transport + ", " + address + "):");
230             e.printStackTrace();
231             return null;
232         }
233     }
234 }
Popular Tags