aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/main/java/org/dcevm/JDIRedefiner.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/main/java/org/dcevm/JDIRedefiner.java')
-rw-r--r--framework/src/main/java/org/dcevm/JDIRedefiner.java153
1 files changed, 153 insertions, 0 deletions
diff --git a/framework/src/main/java/org/dcevm/JDIRedefiner.java b/framework/src/main/java/org/dcevm/JDIRedefiner.java
new file mode 100644
index 00000000..6fc742d4
--- /dev/null
+++ b/framework/src/main/java/org/dcevm/JDIRedefiner.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+package org.dcevm;
+
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.VirtualMachineManager;
+import com.sun.jdi.connect.AttachingConnector;
+import com.sun.jdi.connect.Connector.Argument;
+import com.sun.jdi.connect.IllegalConnectorArgumentsException;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility class for performing class redefinition using JDI.
+ * </li>
+ * </ul>
+ *
+ * @author Thomas Wuerthinger
+ * @author Kerstin Breiteneder
+ * @author Christoph Wimberger
+ *
+ */
+public class JDIRedefiner implements Redefiner {
+
+ private static final String PORT_ARGUMENT_NAME = "port";
+ private static final String TRANSPORT_NAME = "dt_socket";
+
+ private VirtualMachine vm;
+
+
+ /** Port at which to connect to the agent of the VM. **/
+ public static final int PORT = 4000;
+
+ public JDIRedefiner(int port) throws IOException {
+ vm = connect(port);
+ }
+
+ @Override
+ public void close() throws IOException {
+ disconnect();
+ }
+
+ private VirtualMachine connect(int port) throws IOException {
+ VirtualMachineManager manager = Bootstrap.virtualMachineManager();
+
+ // Find appropiate connector
+ List<AttachingConnector> connectors = manager.attachingConnectors();
+ AttachingConnector chosenConnector = null;
+ for (AttachingConnector c : connectors) {
+ if (c.transport().name().equals(TRANSPORT_NAME)) {
+ chosenConnector = c;
+ break;
+ }
+ }
+ if (chosenConnector == null) {
+ throw new IllegalStateException("Could not find socket connector");
+ }
+
+ // Set port argument
+ AttachingConnector connector = chosenConnector;
+ Map<String, Argument> defaults = connector.defaultArguments();
+ Argument arg = defaults.get(PORT_ARGUMENT_NAME);
+ if (arg == null) {
+ throw new IllegalStateException("Could not find port argument");
+ }
+ arg.setValue(Integer.toString(port));
+
+ // Attach
+ try {
+ System.out.println("Connector arguments: " + defaults);
+ return connector.attach(defaults);
+ } catch (IllegalConnectorArgumentsException e) {
+ throw new IllegalArgumentException("Illegal connector arguments", e);
+ }
+ }
+
+ public void disconnect() {
+ if (vm != null) {
+ vm.dispose();
+ vm = null;
+ }
+ }
+
+ public void redefineClasses(Map<Class<?>, byte[]> classes) {
+ refreshAllClasses();
+ List<ReferenceType> references = vm.allClasses();
+
+ Map<ReferenceType, byte[]> map = new HashMap<ReferenceType, byte[]>(classes.size());
+ for (Map.Entry<Class<?>, byte[]> entry : classes.entrySet()) {
+ map.put(findReference(references, entry.getKey().getName()), entry.getValue());
+ }
+ vm.redefineClasses(map);
+ }
+
+ /**
+ * Call this method before calling allClasses() in order to refresh the JDI state of loaded classes.
+ * This is necessary because the JDI map of all loaded classes is only updated based on events received over JDWP (network connection)
+ * and therefore it is not necessarily up-to-date with the real state within the VM.
+ */
+ private void refreshAllClasses() {
+ try {
+ Field f = vm.getClass().getDeclaredField("retrievedAllTypes");
+ f.setAccessible(true);
+ f.set(vm, false);
+ } catch (IllegalArgumentException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (NoSuchFieldException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (SecurityException ex) {
+ Logger.getLogger(HotSwapTool.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ private static ReferenceType findReference(List<ReferenceType> list, String name) {
+ for (ReferenceType ref : list) {
+ if (ref.name().equals(name)) {
+ return ref;
+ }
+ }
+ throw new IllegalArgumentException("Cannot find corresponding reference for class name '" + name + "'" );
+ }
+}