Home » javassist-sources » javassist » util » [javadoc | source]

    1   /*
    2    * Javassist, a Java-bytecode translator toolkit.
    3    * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
    4    *
    5    * The contents of this file are subject to the Mozilla Public License Version
    6    * 1.1 (the "License"); you may not use this file except in compliance with
    7    * the License.  Alternatively, the contents of this file may be used under
    8    * the terms of the GNU Lesser General Public License Version 2.1 or later.
    9    *
   10    * Software distributed under the License is distributed on an "AS IS" basis,
   11    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   12    * for the specific language governing rights and limitations under the
   13    * License.
   14    */
   15   
   16   package javassist.util;
   17   
   18   import com.sun.jdi;
   19   import com.sun.jdi.connect;
   20   import com.sun.jdi.event;
   21   import com.sun.jdi.request;
   22   import java.io;
   23   import java.util;
   24   
   25   class Trigger {
   26       void doSwap() {}
   27   }
   28   
   29   /**
   30    * A utility class for dynamically reloading a class by
   31    * the Java Platform Debugger Architecture (JPDA), or <it>HotSwap</code>.
   32    * It works only with JDK 1.4 and later.
   33    *
   34    * <p><b>Note:</b> The new definition of the reloaded class must declare
   35    * the same set of methods and fields as the original definition.  The
   36    * schema change between the original and new definitions is not allowed
   37    * by the JPDA. 
   38    *
   39    * <p>To use this class, the JVM must be launched with the following
   40    * command line options:
   41    *
   42    * <ul>
   43    * <p>For Java 1.4,<br>
   44    * <pre>java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000</pre>
   45    * <p>For Java 5,<br>
   46    * <pre>java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000</pre>
   47    * </ul>
   48    *
   49    * <p>Note that 8000 is the port number used by <code>HotSwapper</code>.
   50    * Any port number can be specified.  Since <code>HotSwapper</code> does not
   51    * launch another JVM for running a target application, this port number
   52    * is used only for inter-thread communication.
   53    *
   54    * <p>Furthermore, <code>JAVA_HOME/lib/tools.jar</code> must be included
   55    * in the class path.
   56    *
   57    * <p>Using <code>HotSwapper</code> is easy.  See the following example:
   58    *
   59    * <ul><pre>
   60    * CtClass clazz = ...
   61    * byte[] classFile = clazz.toBytecode();
   62    * HotSwapper hs = new HostSwapper(8000);  // 8000 is a port number.
   63    * hs.reload("Test", classFile);
   64    * </pre></ul>
   65    *
   66    * <p><code>reload()</code>
   67    * first unload the <code>Test</code> class and load a new version of
   68    * the <code>Test</code> class.
   69    * <code>classFile</code> is a byte array containing the new contents of
   70    * the class file for the <code>Test</code> class.  The developers can
   71    * repatedly call <code>reload()</code> on the same <code>HotSwapper</code>
   72    * object so that they can reload a number of classes.
   73    *
   74    * @since 3.1
   75    */
   76   public class HotSwapper {
   77       private VirtualMachine jvm;
   78       private MethodEntryRequest request;
   79       private Map newClassFiles;
   80   
   81       private Trigger trigger;
   82   
   83       private static final String HOST_NAME = "localhost";
   84       private static final String TRIGGER_NAME = Trigger.class.getName();
   85   
   86       /**
   87        * Connects to the JVM.
   88        *
   89        * @param port	the port number used for the connection to the JVM.
   90        */
   91       public HotSwapper(int port)
   92           throws IOException, IllegalConnectorArgumentsException
   93       {
   94           this(Integer.toString(port));
   95       }
   96   
   97       /**
   98        * Connects to the JVM.
   99        *
  100        * @param port	the port number used for the connection to the JVM.
  101        */
  102       public HotSwapper(String port)
  103           throws IOException, IllegalConnectorArgumentsException
  104       {
  105           jvm = null;
  106           request = null;
  107           newClassFiles = null;
  108           trigger = new Trigger();
  109           AttachingConnector connector
  110               = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach");
  111   
  112           Map arguments = connector.defaultArguments();
  113           ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME);
  114           ((Connector.Argument)arguments.get("port")).setValue(port);
  115           jvm = connector.attach(arguments);
  116           EventRequestManager manager = jvm.eventRequestManager();
  117           request = methodEntryRequests(manager, TRIGGER_NAME);
  118       }
  119   
  120       private Connector findConnector(String connector) throws IOException {
  121           List connectors = Bootstrap.virtualMachineManager().allConnectors();
  122           Iterator iter = connectors.iterator();
  123           while (iter.hasNext()) {
  124               Connector con = (Connector)iter.next();
  125               if (con.name().equals(connector)) {
  126                   return con;
  127               }
  128           }
  129   
  130           throw new IOException("Not found: " + connector);
  131       }
  132   
  133       private static MethodEntryRequest methodEntryRequests(
  134                                   EventRequestManager manager,
  135                                   String classpattern) {
  136           MethodEntryRequest mereq = manager.createMethodEntryRequest();
  137           mereq.addClassFilter(classpattern);
  138           mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
  139           return mereq;
  140       }
  141   
  142       /* Stops triggering a hotswapper when reload() is called.
  143        */
  144       private void deleteEventRequest(EventRequestManager manager,
  145                                       MethodEntryRequest request) {
  146           manager.deleteEventRequest(request);
  147       }
  148   
  149       /**
  150        * Reloads a class.
  151        *
  152        * @param className		the fully-qualified class name.
  153        * @param classFile		the contents of the class file.
  154        */
  155       public void reload(String className, byte[] classFile) {
  156           ReferenceType classtype = toRefType(className);
  157           Map map = new HashMap();
  158           map.put(classtype, classFile);
  159           reload2(map, className);
  160       }
  161   
  162       /**
  163        * Reloads a class.
  164        *
  165        * @param classFiles	a map between fully-qualified class names
  166        *				and class files.  The type of the class names
  167        *				is <code>String</code> and the type of the
  168        *				class files is <code>byte[]</code>.
  169        */
  170       public void reload(Map classFiles) {
  171           Set set = classFiles.entrySet();
  172           Iterator it = set.iterator();
  173           Map map = new HashMap();
  174           String className = null;
  175           while (it.hasNext()) {
  176               Map.Entry e = (Map.Entry)it.next();
  177               className = (String)e.getKey();
  178               map.put(toRefType(className), e.getValue());
  179           }
  180   
  181           if (className != null)
  182               reload2(map, className + " etc.");
  183       }
  184   
  185       private ReferenceType toRefType(String className) {
  186           List list = jvm.classesByName(className);
  187           if (list == null || list.isEmpty())
  188               throw new RuntimeException("no such a class: " + className);
  189           else
  190               return (ReferenceType)list.get(0);
  191       }
  192   
  193       private void reload2(Map map, String msg) {
  194           synchronized (trigger) {
  195               startDaemon();
  196               newClassFiles = map;
  197               request.enable();
  198               trigger.doSwap();
  199               request.disable();
  200               Map ncf = newClassFiles;
  201               if (ncf != null) {
  202                   newClassFiles = null;
  203                   throw new RuntimeException("failed to reload: " + msg);
  204               }
  205           }
  206       }
  207   
  208       private void startDaemon() {
  209           new Thread() {
  210               private void errorMsg(Throwable e) {
  211                   System.err.print("Exception in thread \"HotSwap\" ");
  212                   e.printStackTrace(System.err);
  213               }
  214   
  215               public void run() {
  216                   EventSet events = null;
  217                   try {
  218                       events = waitEvent();
  219                       EventIterator iter = events.eventIterator();
  220                       while (iter.hasNext()) {
  221                           Event event = iter.nextEvent();
  222                           if (event instanceof MethodEntryEvent) {
  223                               hotswap();
  224                               break;
  225                           }
  226                       }
  227                   }
  228                   catch (Throwable e) {
  229                       errorMsg(e);
  230                   }
  231                   try {
  232                       if (events != null)
  233                           events.resume();
  234                   }
  235                   catch (Throwable e) {
  236                       errorMsg(e);
  237                   }
  238               }
  239           }.start();
  240       }
  241   
  242       EventSet waitEvent() throws InterruptedException {
  243           EventQueue queue = jvm.eventQueue();
  244           return queue.remove();
  245       }
  246   
  247       void hotswap() {
  248           Map map = newClassFiles;
  249           jvm.redefineClasses(map);
  250           newClassFiles = null;
  251       }
  252   }

Home » javassist-sources » javassist » util » [javadoc | source]