Implementing a Java Agent (Part 1)

This is the first in a series of posts describing how to implement a Java agent. Java agents execute in the same Java Virtual Machine (JVM) as regular Java application, and provide insight into them as well as the ability to modify their behavior at run-time. All of the source code is available for download in the form of a Maven project.

Before we start with the actual implementation of the agent, let’s create an simple application that we will attach our agent to:

public class MyApplication {
    public static void main(String[] args) {
        long counter = 0l;
        while (!Thread.interrupted()) {
            try {
                System.out.println(counter++ % 2 == 0 ? "Tick" : "Tock");
                Thread.sleep(1000l);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

The main method of the application loops until the application is interrupted, printing “Tick” and “Tock”, respectively.

Next we will create a simple agent by creating a class which implements the premain method, as per the Java documentation. Similar to the main method in a regular application, this method will be called during creation of the JVM when our agent is loaded.

public class MyAgent {
    public static void premain(String args, Instrumentation inst) {
        System.out.println("MyAgent start: " + args);
    }    
}

The agent will be contained inside its own JAR file, and in order for the JVM to correctly load the MyAgent class, we need to specify the classname in the JAR’s manifest. While this can be done in a number of ways, I’ll only describe the approach using the Maven jar-plugin. To set the required manifest entry, include the following configuration in the pom-file for the agent module:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.6</version>
      <configuration>
        <archive>
          <manifestEntries>
            <Premain-Class>com.afqa123.example.MyAgent</Premain-Class>
          </manifestEntries>
        </archive>
      </configuration>
    </plugin>
  </plugins>
</build>

Similarly, modify the pom-file for the application module to set the Main-class manifest entry:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.6</version>
      <configuration>
        <archive>
	  <manifestEntries>
            <Main-class>com.afqa123.example.MyApplication</Main-class>
	  </manifestEntries>
        </archive>
      </configuration>
    </plugin>
  </plugins>    
</build>

After building the project, copy both JAR files to a directory of your choice. To run the application without the agent, execute the following:

java -jar MyApplication-0.0.1-SNAPSHOT.jar

Your output should match the expected message once per second:

Tick
Tock
...

Press Ctrl+C to stop the program. To start the agent when the JVM loads, run the following command:

java -javaagent:MyAgent-0.0.1-SNAPSHOT.jar -jar MyApplication-0.0.1-SNAPSHOT.jar

This time, you will see the agent startup message before the application enters the main loop:

MyAgent start: null
Tick
Tock
...

And that’s all! Check out the full sample project. In the next article in this series, I will demonstrate how to dynamically load a Java agent into a running JVM.

Leave a Reply

Your email address will not be published.