From aa0900d4cf6479bc598951ea446ea3f5f95a23b6 Mon Sep 17 00:00:00 2001 From: chiba Date: Tue, 21 Jun 2005 16:39:16 +0000 Subject: HotSwap support git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@184 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- src/main/javassist/Dump.java | 57 -------- src/main/javassist/tool/Dump.java | 57 ++++++++ src/main/javassist/tool/HotSwapper.java | 250 ++++++++++++++++++++++++++++++++ src/main/javassist/tool/package.html | 6 + 4 files changed, 313 insertions(+), 57 deletions(-) delete mode 100644 src/main/javassist/Dump.java create mode 100644 src/main/javassist/tool/Dump.java create mode 100644 src/main/javassist/tool/HotSwapper.java create mode 100644 src/main/javassist/tool/package.html (limited to 'src') diff --git a/src/main/javassist/Dump.java b/src/main/javassist/Dump.java deleted file mode 100644 index 69ba4625..00000000 --- a/src/main/javassist/Dump.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Javassist, a Java-bytecode translator toolkit. - * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved. - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. Alternatively, the contents of this file may be used under - * the terms of the GNU Lesser General Public License Version 2.1 or later. - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - */ - -package javassist; - -import java.io.*; -import javassist.bytecode.ClassFile; -import javassist.bytecode.ClassFileWriter; - -/** - * Dump is a tool for viewing the class definition in the given - * class file. Unlike the JDK javap tool, Dump works even if - * the class file is broken. - * - *

For example, - *

- * - *

prints the contents of the constant pool and the list of methods - * and fields. - */ -public class Dump { - private Dump() {} - - /** - * Main method. - * - * @param args args[0] is the class file name. - */ - public static void main(String[] args) throws Exception { - if (args.length != 1) { - System.err.println("Usage: java Dump "); - return; - } - - DataInputStream in = new DataInputStream( - new FileInputStream(args[0])); - ClassFile w = new ClassFile(in); - PrintWriter out = new PrintWriter(System.out, true); - out.println("*** constant pool ***"); - w.getConstPool().print(out); - out.println(); - out.println("*** members ***"); - ClassFileWriter.print(w, out); - } -} diff --git a/src/main/javassist/tool/Dump.java b/src/main/javassist/tool/Dump.java new file mode 100644 index 00000000..f8a1a5dd --- /dev/null +++ b/src/main/javassist/tool/Dump.java @@ -0,0 +1,57 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package javassist.tool; + +import java.io.*; +import javassist.bytecode.ClassFile; +import javassist.bytecode.ClassFileWriter; + +/** + * Dump is a tool for viewing the class definition in the given + * class file. Unlike the JDK javap tool, Dump works even if + * the class file is broken. + * + *

For example, + *

+ * + *

prints the contents of the constant pool and the list of methods + * and fields. + */ +public class Dump { + private Dump() {} + + /** + * Main method. + * + * @param args args[0] is the class file name. + */ + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: java Dump "); + return; + } + + DataInputStream in = new DataInputStream( + new FileInputStream(args[0])); + ClassFile w = new ClassFile(in); + PrintWriter out = new PrintWriter(System.out, true); + out.println("*** constant pool ***"); + w.getConstPool().print(out); + out.println(); + out.println("*** members ***"); + ClassFileWriter.print(w, out); + } +} diff --git a/src/main/javassist/tool/HotSwapper.java b/src/main/javassist/tool/HotSwapper.java new file mode 100644 index 00000000..4df6ccae --- /dev/null +++ b/src/main/javassist/tool/HotSwapper.java @@ -0,0 +1,250 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package javassist.tool; + +import com.sun.jdi.*; +import com.sun.jdi.connect.*; +import com.sun.jdi.event.*; +import com.sun.jdi.request.*; +import java.io.*; +import java.util.*; + +class Trigger { + void doSwap() {} +} + +/** + * A utility class for dynamically reloading a class by + * the Java Platform Debugger Architecture (JPDA), or HotSwap. + * It works only with JDK 1.4 and later. + * + *

Note: The new definition of the reloaded class must declare + * the same set of methods and fields as the original definition. The + * schema change between the original and new definitions is not allowed + * by the JPDA. + * + *

To use this class, the JVM must be launched with the following + * command line options: + * + *

+ * + *

Note that 8000 is the port number used by HotSwapper. + * Any port number can be specified. Since HotSwapper does not + * launch another JVM for running a target application, this port number + * is used only for inter-thread communication. + * + *

Furthermore, JAVA_HOME/lib/tools.jar must be included + * in the class path. + * + *

Using HotSwapper is easy. See the following example: + * + *

+ * + *

reload() + * first unload the Test class and load a new version of + * the Test class. + * classFile is a byte array containing the new contents of + * the class file for the Test class. The developers can + * repatedly call reload() on the same HotSwapper + * object so that they can reload a number of classes. + * + * @since 3.1 + */ +public class HotSwapper { + private VirtualMachine jvm; + private MethodEntryRequest request; + private Map newClassFiles; + + private Trigger trigger; + + private static final String HOST_NAME = "localhost"; + private static final String TRIGGER_NAME = "javassist.tool.Trigger"; + + /** + * Connects to the JVM. + * + * @param port the port number used for the connection to the JVM. + */ + public HotSwapper(int port) + throws IOException, IllegalConnectorArgumentsException + { + this(Integer.toString(port)); + } + + /** + * Connects to the JVM. + * + * @param port the port number used for the connection to the JVM. + */ + public HotSwapper(String port) + throws IOException, IllegalConnectorArgumentsException + { + jvm = null; + request = null; + newClassFiles = null; + trigger = new Trigger(); + AttachingConnector connector + = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); + + Map arguments = connector.defaultArguments(); + ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME); + ((Connector.Argument)arguments.get("port")).setValue(port); + jvm = connector.attach(arguments); + EventRequestManager manager = jvm.eventRequestManager(); + request = methodEntryRequests(manager, TRIGGER_NAME); + } + + private Connector findConnector(String connector) throws IOException { + List connectors = Bootstrap.virtualMachineManager().allConnectors(); + Iterator iter = connectors.iterator(); + while (iter.hasNext()) { + Connector con = (Connector)iter.next(); + if (con.name().equals(connector)) { + return con; + } + } + + throw new IOException("Not found: " + connector); + } + + private static MethodEntryRequest methodEntryRequests( + EventRequestManager manager, + String classpattern) { + MethodEntryRequest mereq = manager.createMethodEntryRequest(); + mereq.addClassFilter(classpattern); + mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); + return mereq; + } + + private void deleteEventRequest(EventRequestManager manager, + MethodEntryRequest request) { + manager.deleteEventRequest(request); + } + + /** + * Reloads a class. + * + * @param className the fully-qualified class name. + * @param classFile the contents of the class file. + */ + public void reload(String className, byte[] classFile) { + ReferenceType classtype = toRefType(className); + Map map = new HashMap(); + map.put(classtype, classFile); + reload2(map, className); + } + + /** + * Reloads a class. + * + * @param classFiles a map between fully-qualified class names + * and class files. The type of the class names + * is String and the type of the + * class files is byte[]. + */ + public void reload(Map classFiles) { + Set set = classFiles.entrySet(); + Iterator it = set.iterator(); + Map map = new HashMap(); + String className = null; + while (it.hasNext()) { + Map.Entry e = (Map.Entry)it.next(); + className = (String)e.getKey(); + map.put(toRefType(className), e.getValue()); + } + + if (className != null) + reload2(map, className + " etc."); + } + + private ReferenceType toRefType(String className) { + List list = jvm.classesByName(className); + if (list == null || list.isEmpty()) + throw new RuntimeException("no such a class: " + className); + else + return (ReferenceType)list.get(0); + } + + private void reload2(Map map, String msg) { + synchronized (trigger) { + startDaemon(); + newClassFiles = map; + request.enable(); + trigger.doSwap(); + request.disable(); + Map ncf = newClassFiles; + if (ncf != null) { + newClassFiles = null; + throw new RuntimeException("failed to reload: " + msg); + } + } + } + + private void startDaemon() { + new Thread() { + private void errorMsg(Throwable e) { + System.err.print("Exception in thread \"HotSwap\" "); + e.printStackTrace(System.err); + } + + public void run() { + EventSet events = null; + try { + events = waitEvent(); + EventIterator iter = events.eventIterator(); + while (iter.hasNext()) { + Event event = iter.nextEvent(); + if (event instanceof MethodEntryEvent) { + hotswap(); + break; + } + } + } + catch (Throwable e) { + errorMsg(e); + } + try { + if (events != null) + events.resume(); + } + catch (Throwable e) { + errorMsg(e); + } + } + }.start(); + } + + EventSet waitEvent() throws InterruptedException { + EventQueue queue = jvm.eventQueue(); + return queue.remove(); + } + + void hotswap() { + Map map = newClassFiles; + jvm.redefineClasses(map); + newClassFiles = null; + } +} diff --git a/src/main/javassist/tool/package.html b/src/main/javassist/tool/package.html new file mode 100644 index 00000000..1566e121 --- /dev/null +++ b/src/main/javassist/tool/package.html @@ -0,0 +1,6 @@ + + +Utility classes. + + + -- cgit v1.2.3