Can different Java applications use the same javaagent? - java

I hava a javaagent Jar simpleAgent.jar. I used it to redifine classes in it and I cached some classes to avoid redifine
public class Premain {
private static Instrumentation instrumentation;
private static final Map<String, Class> allLoadClassesMap = new ConcurrentHashMap<>();
public static void premain(String agentArgs, Instrumentation inst) {
instrumentation = inst;
cacheAllLoadedClasses("com.example");
}
public static void cacheAllLoadedClasses(String prfixName) {
try {
Class[] allLoadClasses = instrumentation.getAllLoadedClasses();
for (Class loadedClass : allLoadClasses) {
if (loadedClass.getName().startsWith(prfixName)) {
allLoadClassesMap.put(loadedClass.getName(), loadedClass);
}
}
logger.warn("Loaded Class Count " + allLoadClassesMap.size());
} catch (Exception e) {
logger.error("", e);
}
}
}
I have three different application app1.jar, app2.jar, app3.jar, so when I start the three application can I use the same agent jar? Eg.:
java -javaagent:simpleAgent.jar -jar app1.jar
java -javaagent:simpleAgent.jar -jar app2.jar
java -javaagent:simpleAgent.jar -jar app3.jar
I don't know the javaagent's implementation, so I was scared that using the same javaagent can trigger in app1 or app2 or app3 crash.

Each JVM instance is separate and does not "know" about other JVMs unless you do something in application level. So, generally the answer is "yes, you can use the same jar either javaagent or not for as many JVM instances as you want."

A Javaaget is treated by the VM similarly to jar files on the class path. Those files are read only, all state is contained in the running VM such that they are safely shared among multiple processes.

Related

Java 9, compatability issue with ClassLoader.getSystemClassLoader

The following code adds jar file to the build path, it works fine with Java 8. However, it throws exception with Java 9, the exception is related to the cast to URLClassLoader. Any ideas how this can be solved? an optimal solution will edit it to work with both Java 8 & 9.
private static int AddtoBuildPath(File f) {
try {
URI u = f.toURI();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(urlClassLoader, u.toURL());
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | MalformedURLException | IllegalAccessException ex) {
return 1;
}
return 0;
}
You've run into the fact that the system class loader is no longer a URLClassLoader. As indicated by ClassLoader::getSystemClassLoader's return type, this was an implementation detail, albeit one that a non-negligible amount of code relied upon.
Judging by the comments, you are looking for a way to dynamically load classes at run time. As Alan Bateman points out, this can not be done in Java 9 by appending to the class path.
You should instead consider creating a new class loader for that. This has the added advantage that you'll be able to get rid of the new classes as they are not loaded into the application class loader. If you're compiling against Java 9, you should read up on layers - they give you a clean abstraction for loading an entirely new module graph.
I have stumbled over this issue a while ago. As many, I had used a method similar to that in the question
private static int AddtoBuildPath(File f)
to dynamically add paths to the classpath at runtime. The code in the question is probably bad style in multiple aspects: 1) assuming that ClassLoader.getSystemClassLoader() returns an URLClassLoader is an undocumented implementation detail and 2) using reflection to make addURL public is maybe another one.
Cleaner way to dynamically add classpaths
In case that you need to use the additional classpath URLs for class loading through „Class.forName“, a clean, elegant and compatible (Java 8 to 10) solution is the following:
1) Write your own class loader by extending URL classloader, having a public addURL method
public class MyClassloader extends URLClassLoader {
public MyClassloader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
2) Declare a (singleton/app wide) object of your classloader
private final MyClassloader classLoader;
and instanciate it via
classLoader = new MyClassloader(new URL[0], this.getClass().getClassLoader());
Note: The system class loader is the parent. Classes loaded though classLoader know those who can be loaded through this.getClass().getClassLoader() but not the other way around.
3) Add additional classpaths whenever needed (dynamically):
File file = new File(path);
if(file.exists()) {
URL url = file.toURI().toURL();
classLoader.addURL(url);
}
4) Instanciate objects or your app though your singleton classloader via
cls = Class.forName(name, true, classLoader);
Note: Since class loaders try a delegation to the parent class loader prior loading a class (and the parent to its parent), you have to make sure that the class to load is not visible to the parent class loader to make sure that it is loaded through the given class loader. To make this clearer: if you have ClassPathB on your system class path and later add ClassPathB and some ClassPathA to your custom classLoader, then classes under ClassPathB will be loaded through the system classloader and classes under ClassPathA are not known to them. However, if you remove ClassPathB from you system class path, such classes will be loaded through your custom classLoader, and then classes under ClassPathA are known to those under ClassPathB.
5) You may consider passing your class loader to a thread via
setContextClassLoader(classLoader)
in case that thread uses getContextClassLoader.
If you're just looking to read the current classpath, for example because you want to spin up another JVM with the same classpath as the current one, you can do the following:
object ClassloaderHelper {
def getURLs(classloader: ClassLoader) = {
// jdk9+ need to use reflection
val clazz = classloader.getClass
val field = clazz.getDeclaredField("ucp")
field.setAccessible(true)
val value = field.get(classloader)
value.asInstanceOf[URLClassPath].getURLs
}
}
val classpath =
(
// jdk8
// ClassLoader.getSystemClassLoader.asInstanceOf[URLClassLoader].getURLs ++
// getClass.getClassLoader.asInstanceOf[URLClassLoader].getURLs
// jdk9+
ClassloaderHelper.getURLs(ClassLoader.getSystemClassLoader) ++
ClassloaderHelper.getURLs(getClass.getClassLoader)
)
By default the final fields in the $AppClassLoader class cannot be accesed via reflection, an extra flag needs to be passed to the JVM:
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
I was given a spring boot application that runs in Java 8. I had the task to upgrade it to Java 11 version.
Issue faced:
Caused by: java.lang.ClassCastException: jdk.internal.loader.ClassLoaders$AppClassLoader (in module: java.base) cannot be cast to java.net.URLClassLoader (in module: java.base)
Way around used:
Create a class:
import java.net.URL;
/**
* This class has been created to make the code compatible after migration to Java 11
* From the JDK 9 release notes: "The application class loader is no longer an instance of
* java.net.URLClassLoader (an implementation detail that was never specified in previous releases).
* Code that assumes that ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
* need to be updated. Note that Java SE and the JDK do not provide an API for applications or
* libraries to dynamically augment the class path at run-time."
*/
public class ClassLoaderConfig {
private final MockClassLoader classLoader;
ClassLoaderConfig() {
this.classLoader = new MockClassLoader(new URL[0], this.getClass().getClassLoader());
}
public MockClassLoader getClassLoader() {
return this.classLoader;
}
}
Create Another class:
import java.net.URL;
import java.net.URLClassLoader;
public class MockClassLoader extends URLClassLoader {
public MockClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
Now set it in the current thread from your main class (Right at the beginning of your application)
Thread.currentThread().setContextClassLoader(new ClassLoaderConfig().getClassLoader());
Hope this solution works for your!!!
Shadov pointed to a thread at the oracle community. There is the correct answer:
Class.forName("nameofclass", true, new URLClassLoader(urlarrayofextrajarsordirs));
The caveats mentioned there are also important:
Caveats:
java.util.ServiceLoader uses the thread's ClassLoader context Thread.currentThread().setContextClassLoader(specialloader);
java.sql.DriverManager does honors the calling class' ClassLoader, -not- the Thread's ClassLoader. Create Driver directly using Class.forName("drivername", true, new URLClassLoader(urlarrayofextrajarsordirs).newInstance();
javax.activation uses the thread's ClassLoader context (important for javax.mail).
Referring to Edi's Solution this worked for me:
public final class IndependentClassLoader extends URLClassLoader {
private static final ClassLoader INSTANCE = new IndependentClassLoader();
/**
* #return instance
*/
public static ClassLoader getInstance() {
return INSTANCE;
}
private IndependentClassLoader() {
super(getAppClassLoaderUrls(), null);
}
private static URL[] getAppClassLoaderUrls() {
return getURLs(IndependentClassLoader.class.getClassLoader());
}
private static URL[] getURLs(ClassLoader classLoader) {
Class<?> clazz = classLoader.getClass();
try {
Field field = null;
field = clazz.getDeclaredField("ucp");
field.setAccessible(true);
Object urlClassPath = field.get(classLoader);
Method method = urlClassPath.getClass().getDeclaredMethod("getURLs", new Class[] {});
method.setAccessible(true);
URL[] urls = (URL[]) method.invoke(urlClassPath, new Object[] {});
return urls;
} catch (Exception e) {
throw new NestableRuntimeException(e);
}
}
}
Running within Eclipse, you need to set VM Arguments to JUnit Launch/Debug Configuration.
Running with maven via command line you have two options:
Option 1
Add following lines to pom.xml :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<argLine>--add-opens java.base/jdk.internal.loader=ALL-UNNAMED</argLine>
</configuration>
</plugin>
Option 2
run mvn test -DargLine="-Dsystem.test.property=--add-opens java.base/jdk.internal.loader=ALL-UNNAMED"
There's also this guys article that helped me.
I could not find the article but... here: https://github.com/CGJennings/jar-loader
Here's a part of guide inside there there's a jar at release you could read his guide & setup it up.
I just tried it myself download the jar file which include the class file
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarFile;
public final class classname{
public static void premain(String agentArgs, Instrumentation instrumentation) {
loadedViaPreMain = true;
agentmain(agentArgs,instrumentation);
}
public final static void addToClassPath(File jarfile)throws IOException{inst.appendToSystemClassLoaderSearch(new JarFile(jarfile));}
public final static void agentmain(String agentArgs, Instrumentation instrumentation) {
if (instrumentation == null){throw new NullPointerException("instrumentation");}
if (inst == null) {inst = instrumentation;}
}
private static Instrumentation inst;
private static boolean loadedViaPreMain = false;
}
I just try it out myself package these code as a package then start the application class with -javaagent:plugin......jar option then call this function.It doesn't change my classpath.I am probably missing some details here.
Hope you can make it work though.
i found this, and worked for me.
String pathSeparator = Syste .getProperty("path.separator");
String[] classPathEntries = System.getProperty("java.class.path") .split(pathSeparator);
from the web site https://blog.codefx.org/java/java-11-migration-guide/#Casting-To-URL-Class-Loader

How to set/get a value from another JVM

I have a class Normal with the following code:
public class Normal {
private static String myStr = "Not working...";
private static boolean running = true;
public static void main(String[] args) {
while(running) {
System.out.println(myStr);
}
}
}
And I have another class named Injector in another project. Its purpose is to change the values of Normal even though they are not in the same JVM:
public class Injector {
public static void main(String[] args) {
String PID = //Gets PID, which works fine
VirtualMachine vm = VirtualMachine.attach(PID);
/*
Set/Get field values for classes in vm?
*/
}
}
What I want to do is change the values myStr and running in the class Normal to "Working!" and false respectively without changing the code in Normal (Only in Injector).
Thanks in advance
You'll need two JARs:
One is Java Agent that uses Reflection to change the field value. Java Agent's main class should have agentmain entry point.
public static void agentmain(String args, Instrumentation instr) throws Exception {
Class normalClass = Class.forName("Normal");
Field myStrField = normalClass.getDeclaredField("myStr");
myStrField.setAccessible(true);
myStrField.set(null, "Working!");
}
You'll have to add MANIFEST.MF with Agent-Class attribute and pack the agent into a jar file.
The second one is a utility that uses Dynamic Attach to inject the agent jar into the running VM. Let pid be the target Java process ID.
import com.sun.tools.attach.VirtualMachine;
...
VirtualMachine vm = VirtualMachine.attach(pid);
try {
vm.loadAgent(agentJarPath, "");
} finally {
vm.detach();
}
A bit more details in the article.

Cannot expose Java Instrumentation for tomcat jvm

I am using a tomcat7 instance to run a Java application. My application needs the Java instrumentation exposed. This is done with a javaagent and I pass the agent at startup to the JVM in the setenv.bat script.
set JAVA_OPTS=%JAVA_OPTS% -javaagent:"C:\path\to\agent.jar"
In the manifest file I have the required section:
Premain-Class: package.name.agent.ExposeInstrumentation
In the premain method of the agent class assign the instrumentation provided by the JVM to a static variable accessible through a static method
public final class ExposeInstrumentation {
private static Instrumentation s_instrumentation;
public static void premain(String arguments, Instrumentation instrumentation) {
s_instrumentation = instrumentation;
}
public static Instrumentation getInstrumentation() {
return s_instrumentation;
}
}
But in my code when I do this:
Instrumentation instrumentation = ExposeInstrumentation.getInstrumentation();
getInstrumentation() returns null;
What is the problem?
UPDATE
I did some further debugging and premain gets executed and s_instrumentation receives the instrumentation, but when I call getInstrumentation later on in my code s_instumentation is set to null. This is strange I tought the value remains valid thought the life of the program.
I assume that you are loading the class ExposeInstrumentation twice. Once by the application class loader which is child-first (reverse-order) and once via the Java agent where the class is automatically loaded by the system class loader. As a result, the ExposeInstrumentation class is loaded twice where you access the one from your application where the field is not set.
You can solve this by explicitly accessing the class loaded by the system class loader:
class ExposeInstrumentation {
// public to assure accessability
public static Instrumentation s_instrumentation;
public static void premain(String arguments, Instrumentation inst) {
s_instrumentation = inst;
}
public static Instrumentation getInstrumentation() {
try {
return (Instrumentation) ClassLoader.getSystemClassLoader()
.loadClass(ExposeInstrumentation.class.getName())
.getDeclaredField("s_instrumentation")
.get(null);
} catch(Exception e) {
return null;
}
}
}
You can also check out the Byte Buddy Agent project that offers this functionality and more (runtime installation) of an agent. With Byte Buddy, you can simply call ByteBuddyAgent.getInstrumentation().

Do I need all classes on the client, server and registry for RMI to work?

I'm doing my first steps with RMI, and I have a simple question.
I have a .jar file which has the implementation of several methods from a library.
I want to call this methods in the .jar file using RMI.
What I'm trying is to create a kind of a wrapper to do it.
So, I'm working on something like this:
Interface class: This interface has the methods to be implemented by the remote object.
Implementation class: This class, has the implementation of the interface methods, each implementation calls the corresponding method in the .jar file. E.g., the jar file has a method called getDetails(), and it returns a "ResponseDetail" object. ResponseDetail is a response class I have in the .jar.
Server class: it binds the methods to the rmiregistry
Client class: it will consume the methods implemented in implementation.
So far so good? :)
Now, I have a lib folder where resides the .jar file.
In the server machine I have deployed the Interface, Implementation and Server classes. I've generated the stub, and I ran the rmiregistry successfully, but, with these details:
To start the rmiregistry, I had to set the classpath in the command line to reference the .jar files, otherwise I get the java.lang.NoClassDefFoundError. I did it with this .sh file:
THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
rmiregistry -J-classpath -J".:${THE_CLASSPATH}"
To start the server, I had to set the classpath as well to reference the .jar files, otherwise, I get the java.lang.NoClassDefFoundError. I've used something like this:
THE_CLASSPATH=
for i in `ls ./lib/*.jar` do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
java -classpath ".:${THE_CLASSPATH}" Server
Client machine:
To run the Client.class file from the client machine, I had to copy the .jar files to it, and make reference to them in the classpath, because otherwise, it does not run and I get the java.lang.NoClassDefFoundError. I had to use this on the client machine:
THE_CLASSPATH=
for i in `ls ./lib/*.jar`
do
THE_CLASSPATH=${THE_CLASSPATH}:${i}
done
java -classpath ".:${THE_CLASSPATH}" HelloClient
Is this ok? I mean, do I have to copy the .jar files to the client machine to execute methods through RMI?
Prior to JDK v5 one had to generate the RMI stubc using the rmic (RMI Compiler). This is done automatically from JDK v5 on. Moreover, you can start the RMI Registry from within the Java code as well. To start with a simple RMI application you may want to follow the following steps:
Create the interface:
import java.rmi.*;
public interface SomeInterface extends Remote {
public String someMethod1() throws RemoteException;
public int someMethod2(float someParameter) throws RemoteException;
public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException;
}
Implement the interface:
import java.rmi.*;
import java.rmi.server.*;
public class SomeImpl extends UnicastRemoteObject implements SomeInterface {
public SomeImpl() throws RemoteException {
super();
}
public String someMethod1() throws RemoteException {
return "Hello World!";
}
public int someMethod2( float f ) throws RemoteException {
return (int)f + 1;
}
public SomeStruct someStructTest(SomeStruct someStruct) throws RemoteException {
int i = someStruct.getInt();
float f = someStruct.getFloat();
someStruct.setInt(i + 1);
someStruct.setFloat(f + 1.0F);
return someStruct;
}
}
Implement a non-primitive serializable object that is to be passed between a client and the server:
import java.io.*;
public class SomeStruct implements Serializable {
private int i = 0;
private float f = 0.0F;
public SomeStruct(int i, float f) {
this.i = i;
this.f = f;
}
public int getInt() {
return i;
}
public float getFloat() {
return f;
}
public void setInt(int i) {
this.i = i;
}
public void setFloat(float f) {
this.f = f;
}
}
Implement the server:
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.net.*;
import java.io.*;
public class SomeServer {
public static void main(String args[]) {
String portNum = "1234", registryURL;
try{
SomeImpl exportedObj = new SomeImpl();
startRegistry( Integer.parseInt(portNum) );
// register the object under the name "some"
registryURL = "rmi://localhost:" + portNum + "/some";
Naming.rebind(registryURL, exportedObj);
System.out.println("Some Server ready.");
} catch (Exception re) {
System.out.println("Exception in SomeServer.main: " + re);
}
}
// This method starts a RMI registry on the local host, if it
// does not already exist at the specified port number.
private static void startRegistry(int rmiPortNum) throws RemoteException{
try {
Registry registry = LocateRegistry.getRegistry(rmiPortNum);
registry.list( );
// The above call will throw an exception
// if the registry does not already exist
} catch (RemoteException ex) {
// No valid registry at that port.
System.out.println("RMI registry is not located at port " + rmiPortNum);
Registry registry = LocateRegistry.createRegistry(rmiPortNum);
System.out.println("RMI registry created at port " + rmiPortNum);
}
}
}
Implement the client:
import java.io.*;
import java.rmi.*;
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
public class SomeClient {
public static void main(String args[]) {
try {
String hostName;
String portNum = "1234";
String registryURL = "rmi://localhost:" + portNum + "/some";
SomeInterface h = (SomeInterface)Naming.lookup(registryURL);
// invoke the remote method(s)
String message = h.someMethod1();
System.out.println(message);
int i = h.someMethod2(12344);
System.out.println(i);
SomeStruct someStructOut = new SomeStruct(10, 100.0F);
SomeStruct someStructIn = new SomeStruct(0, 0.0F);
someStructIn = h.someStructTest(someStructOut);
System.out.println( someStructIn.getInt() );
System.out.println( someStructIn.getFloat() );
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
A larger client-server application should be divided into three modules:client, server, and common (for classes shared between the server and client code, i.e. the remote interface and the non-primitive object in this example). The client application will then be created from client + common modules on the classpath and the server from server + common modules on the classpath.
I used this example many years ago to learn basics of RMI and it still works. However it is far from being perfect (default Java package used, incorrect exception handling, hostname and port parameters are hard-coded and not configurable, etc.)
Nevertheless, it is good for starters. All the files can be placed in one directory and compiled using the simple javac *.java command. The server application can then be started using the java SomeServer and the client one by launching the java SomeClient command.
I hope this helps to understand the Java RMI which is, in fact, far more complicated than just this.
You shouldn't be generating stubs (if you are following a tutorial, it is way old). you can run the client without necessarily having the jars locally (using remote classloading), but it's way easier to do it this with the jars available locally (i've personally done a fair bit of RMI and never actually deployed a system with remote classloading). typically, you want 2 jars, a "client" jar with just the remote interfaces (and any Serializable classes used by those interfaces) and a "server" jar which includes the implementation classes. you would then run the server with the server jar, and the rmiregistry/client with the client jars.
This is a pretty good (up to date and simple) getting started guide.
To say it in short what the other answers elaborated:
The client needs only the common interfaces (and the client classes), not the server implementation.
The server needs interfaces and implementation (and your server main class).
The rmiregistry needs only the interfaces.
(Actually, you can start your own registry inside the server process - then you don't need the rmiregistry at all. Have a look at the createRegistry methods in the java.rmi.registry.LocateRegistry class.)
"Interfaces" here means both the remote interfaces and any (serializable) classes used by these as parameter or argument types.
How you distribute these classes to jar files is independent of this.

Using JDWP and JVMTI to obtain information of the running Java application

We are developing an application for obtaining the information of a running java application using JDWP and JVMTI. Sun Java comes with a reference implementation of JDWP so using agentlib -jdwp: will use the reference implementation. Our aim is to study JVMTI and write JVMTI agents to get specific detail. Also creating a Front end using which the user can request for specific information about the running java application. For this we would have to write our implementation for JDWP. Though we were able to write JVMTI agents and use it to attach to the java application from the command line. But our aim is to send this information to another java process (Front End).
So we have a Java application- Front end querying for information from the JVMTI Agents (Back-End). These agents should be able to attach to the running java application we are going to inspect dynamically. Any ideas on how this can be done? Or has anyone tried this before?
The JDK has a built-in Java api for remote/local debug (com.sun.jdi.).
To quickly start using the api, you can look at examples in $JDK_DIR\demo\jpda\examples.jar. [More details]
Since the official examples are not so clear, here is reusable example (inspired by this blog post):
VMAcquirer.java (Connection manager)
public class VMAcquirer {
public VirtualMachine connect(String host,int port) throws IOException {
String strPort = Integer.toString(port);
AttachingConnector connector = getConnector();
try {
VirtualMachine vm = connect(connector,host, strPort);
return vm;
} catch (IllegalConnectorArgumentsException e) {
throw new IllegalStateException(e);
}
}
private AttachingConnector getConnector() {
VirtualMachineManager vmManager = Bootstrap.virtualMachineManager();
for (Connector connector : vmManager.attachingConnectors()) {
if("com.sun.jdi.SocketAttach".equals(connector.name()))
return (AttachingConnector) connector;
}
throw new IllegalStateException();
}
private VirtualMachine connect(AttachingConnector connector,String host,String port)
throws IllegalConnectorArgumentsException, IOException {
Map<String, Connector.Argument> args = connector.defaultArguments();
Connector.Argument portArg = args.get("port");
portArg.setValue(port);
Connector.Argument addressArg = args.get("hostname");
addressArg.setValue(host);
return connector.attach(args);
}
}
Monitor.java (Actual monitoring)
class Monitor {
public static void main(String[] args){
VirtualMachine vm = new VMAcquirer().connect("192.168.0.x", 2600);
System.out.println("name="+vm.name()); //Info about the remote VM
System.out.println("description="+vm.description());
EventRequestManager erm = vm.eventRequestManager();
[...] //Send request using erm instance
loopEventQueue(vm); //Start a loop to listen to the events received
}
public static void loopEventQueue(VirtualMachine vm) throws InterruptedException {
EventQueue eventQueue = vm.eventQueue();
while (true) {
EventSet eventSet = eventQueue.remove();
for (Event ev : eventSet) {
if(ev instanceof MethodEntryEvent) {
handleMethodEntryEvent(ev);
}
[...]
}
}
}
}
Required by the application being monitor
java -Xdebug -Xrunjdwp:transport=dt_socket,address=2600,server=y,suspend=n ...
Informations available through JDWP
Tracing method call and return (possibly for doing profiling or logging)
Field value changes
VM information (see name() and description() in the example)
Evaluate expression to execute arbitrary code

Categories