package org.hotswap.agent.plugin.hotswapper;
import org.hotswap.agent.logging.AgentLogger;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Hotswapper command must run in application classloader because tools.jar dependency can be easier added to
* the application classloader than to java classpath.
*
* @author Jiri Bubnik
*/
public class HotswapperCommand {
private static AgentLogger LOGGER = AgentLogger.getLogger(HotswapperCommand.class);
// HotSwapperJpda will connect to JPDA on first hotswap command and remain connected.
// The HotSwapperJpda class from javaassist is copied to the plugin, becuse it needs to reside
// in the application classloader to avoid NoClassDefFound error on tools.jar classes.
private static HotSwapperJpda hotSwapper = null;
public static synchronized void hotswap(String port, final HashMap<Class<?>, byte[]> reloadMap) {
// synchronize on the reloadMap object - do not allow addition while in process
synchronized (reloadMap) {
if (hotSwapper == null) {
LOGGER.debug("Starting HotSwapperJpda agent on JPDA transport socket - port {}, classloader {}", port, HotswapperCommand.class.getClassLoader());
try {
hotSwapper = new HotSwapperJpda(port);
} catch (IOException e) {
LOGGER.error("Unable to connect to debug session. Did you start the application with debug enabled " +
"(i.e. java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000)", e);
} catch (Exception e) {
LOGGER.error("Unable to connect to debug session. Please check port property setting '{}'.", e, port);
}
}
if (hotSwapper != null) {
LOGGER.debug("Reloading classes {}", Arrays.toString(reloadMap.keySet().toArray()));
// convert to Map Class name -> bytecode
// We loose some information here, reload use always first class name that it finds reference to.
Map<String, byte[]> reloadMapClassNames = new HashMap<String, byte[]>();
for (Map.Entry<Class<?>, byte[]> entry : reloadMap.entrySet()) {
reloadMapClassNames.put(entry.getKey().getName(), entry.getValue());
}
// actual hotswap via JPDA
hotSwapper.reload(reloadMapClassNames);
reloadMap.clear();
LOGGER.debug("HotSwapperJpda agent reload complete.");
}
}
}
}