diff options
author | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2005-06-21 16:39:16 +0000 |
---|---|---|
committer | chiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3> | 2005-06-21 16:39:16 +0000 |
commit | aa0900d4cf6479bc598951ea446ea3f5f95a23b6 (patch) | |
tree | 50c3f9505a0df1d86805bae0c29073d3bc909959 /src | |
parent | 5de7634f05f343ff9573e140ea78a224d4ae3aa4 (diff) | |
download | javassist-aa0900d4cf6479bc598951ea446ea3f5f95a23b6.tar.gz javassist-aa0900d4cf6479bc598951ea446ea3f5f95a23b6.zip |
HotSwap support
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@184 30ef5769-5b8d-40dd-aea6-55b5d6557bb3
Diffstat (limited to 'src')
-rw-r--r-- | src/main/javassist/tool/Dump.java (renamed from src/main/javassist/Dump.java) | 4 | ||||
-rw-r--r-- | src/main/javassist/tool/HotSwapper.java | 250 | ||||
-rw-r--r-- | src/main/javassist/tool/package.html | 6 |
3 files changed, 258 insertions, 2 deletions
diff --git a/src/main/javassist/Dump.java b/src/main/javassist/tool/Dump.java index 69ba4625..f8a1a5dd 100644 --- a/src/main/javassist/Dump.java +++ b/src/main/javassist/tool/Dump.java @@ -13,7 +13,7 @@ * License. */ -package javassist; +package javassist.tool; import java.io.*; import javassist.bytecode.ClassFile; @@ -25,7 +25,7 @@ import javassist.bytecode.ClassFileWriter; * the class file is broken. * * <p>For example, - * <ul><pre>% java javassist.Dump foo.class</pre></ul> + * <ul><pre>% java javassist.tool.Dump foo.class</pre></ul> * * <p>prints the contents of the constant pool and the list of methods * and fields. 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 <it>HotSwap</code>. + * It works only with JDK 1.4 and later. + * + * <p><b>Note:</b> 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. + * + * <p>To use this class, the JVM must be launched with the following + * command line options: + * + * <ul> + * <p>For Java 1.4,<br> + * <pre>java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000</pre> + * <p>For Java 5,<br> + * <pre>java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000</pre> + * </ul> + * + * <p>Note that 8000 is the port number used by <code>HotSwapper</code>. + * Any port number can be specified. Since <code>HotSwapper</code> does not + * launch another JVM for running a target application, this port number + * is used only for inter-thread communication. + * + * <p>Furthermore, <code>JAVA_HOME/lib/tools.jar</code> must be included + * in the class path. + * + * <p>Using <code>HotSwapper</code> is easy. See the following example: + * + * <ul><pre> + * CtClass clazz = ... + * byte[] classFile = clazz.toBytecode(); + * HotSwapper hs = new HostSwapper(8000); // 8000 is a port number. + * hs.reload("Test", classFile); + * </pre></ul> + * + * <p><code>reload()</code> + * first unload the <code>Test</code> class and load a new version of + * the <code>Test</code> class. + * <code>classFile</code> is a byte array containing the new contents of + * the class file for the <code>Test</code> class. The developers can + * repatedly call <code>reload()</code> on the same <code>HotSwapper</code> + * 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 <code>String</code> and the type of the + * class files is <code>byte[]</code>. + */ + 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 @@ +<html> +<body> +Utility classes. + +</body> +</html> |