Reputation: 3736
I am using jdk1.6. I have loaded a jar at runtime successfully. Codes are as follow:
vm = VirtualMachine.attach(vid);
vm.loadAgent(agentPath);
Now I wannt to unload this agent at runtime. There is no API DOC to do that. Can anyone give me some advices? Thanks.
EDIT
More Codes
GlobalVariables.vm = VirtualMachine.attach(vid);
// Check to see if transformer agent is installed
if(!GlobalVariables.vm.getSystemProperties().contains("demo.agent.installed")) {
System.out.println("Load agent");
GlobalVariables.vm.loadAgent(agentPath);
}
GlobalVariables.connectorAddress = GlobalVariables.vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress", null);
if(null == GlobalVariables.connectorAddress) {
// It's not, so install the management agent
String javaHome = GlobalVariables.vm.getSystemProperties().getProperty("java.home");
File managementAgentJarFile = new File(javaHome + File.separator + "lib" + File.separator + "management-agent.jar");
GlobalVariables.vm.loadAgent(managementAgentJarFile.getAbsolutePath());
System.out.println("Load Management agent");
GlobalVariables.connectorAddress = GlobalVariables.vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress", null);
// Now it's installed
}
// Now connect and transform the classnames provided in the remaining args.
//JMXConnector connector = null;
try {
// This is the ObjectName of the MBean registered when loaded.jar was installed.
//ObjectName on = new ObjectName("transformer:service=DemoTransformer");
GlobalVariables.on = new ObjectName("transformer:service=DemoTransformer");
// Here we're connecting to the target JVM through the management agent
GlobalVariables.connector = JMXConnectorFactory.connect(new JMXServiceURL(GlobalVariables.connectorAddress));
GlobalVariables.server = GlobalVariables.connector.getMBeanServerConnection();
System.out.println("MBean Server connection...");
// Call transformClass on the transformer MBean
GlobalVariables.server.invoke(GlobalVariables.on, "transformClass", new Object[]{className, args}, new String[]{String.class.getName(), String.class.getName()});
} catch (Exception ex) {
ex.printStackTrace(System.err);
} finally {
if(GlobalVariables.connector!=null) try { GlobalVariables.connector.close(); } catch (Exception e) {}
GlobalVariables.vm.detach();
System.out.println("Has disconnected");
}
What did I do then
I run the above code again to load those two agents again. But I got errors.
Errors
error in current vm
com.sun.tools.attach.AgentInitializationException: Agent JAR loaded but agent failed to initialize
at sun.tools.attach.HotSpotVirtualMachine.loadAgent(HotSpotVirtualMachine.java:103)
at com.sun.tools.attach.VirtualMachine.loadAgent(VirtualMachine.java:508)
at faultinjectionaction.AttachClass.attachAgent(AttachClass.java:111)
at org.apache.jsp.fjstep3_jsp._jspService(fjstep3_jsp.java:350)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)
error in target vm
Exception in thread "Attach Listener" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:323)
at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:348)
Caused by: javax.management.InstanceAlreadyExistsException: transformer:service= DemoTransformer
at com.sun.jmx.mbeanserver.Repository.addMBean(Repository.java:453)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.internal_addObject(DefaultMBeanServerInterceptor.java:1484)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerDynamicMBean(DefaultMBeanServerInterceptor.java:963)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerObject(DefaultMBeanServerInterceptor.java:917)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:312)
at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:482)
at faultinjectionagent.AgentMain.agentmain(AgentMain.java:28)
... 6 more
Aim
To do the same job with no errors. I think I need to unload the agents when I finished my job every time. But I failed to unload those agents.
EDIT GlobleVariables
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import com.sun.tools.attach.VirtualMachine;
public class GlobalVariables{
public static MBeanServerConnection server;
public static ObjectName on;
public static JMXConnector connector = null;
public static String connectorAddress = null;
public static VirtualMachine vm;
}
Upvotes: 1
Views: 1679
Reputation: 44032
What do you mean by unload the agent? You cannot unload the classes as it implicitly says in the documentation:
the specified JAR file is added to the system class path (of the target virtual machine)
The only way arround this might be some custom class loader magic, but I would not recommend that.
Update: After looking at your extended question, I think your problem is actually something else. At some point you are invoking
JMXConnectorFactory.connect(new JMXServiceURL(GlobalVariables.connectorAddress))
in order to create a JMXConnector
. I guess that within your GlobalVariables.server.invoke
call, you are registering a MBean by new ObjectName("transformer:service=DemoTransformer")
. This name must be unique and when you are running the code a second time, this name is already taken as suggested by javax.management.InstanceAlreadyExistsException: transformer:service= DemoTransformer
. What you needed to do is:
MBeanServerConnection.close(new ObjectName("transformer:service=DemoTransformer"))
before detaching from the remote JVM in order to make the name available again.You might have assumed that by detaching, all state on the remote machine was reset. This is however not true. You added an MBean with a name and than you tried to do this again. This error can be understood as if you added a two values with the same key to a map. Other than with the map, you will however not override values but cause the exception you observe above.
By the way: You should call JMXConnector.close
explicitly when the connection to the remote server is not longer required.
PS: You might find this article interesting.
Update 2: After discussion in the chat and after getting the MBean naming conflict out of the way, I think this is what caused the problem:
When a Java Agent is loaded a second time, the classes that come with the agent (represented in managementAgentJarFile
) are already loaded in the target JVM. This means, that no class initializers will be run again and state changes that are represented by static
variables will still be represented. Additionally, it is not possible to load classes with the same name but changed implementation. This will cause LinkageError
s and the agent loading will fail. The solution is to avoid static
state such that an agent cannot inflict with itself and to create separate name spaces for different agents. Otherwise, agent classes can be unloaded by using custom class loaders. More information on this matter can be found on many places and here:
Upvotes: 2