java agent project based on IDEA + maven

Posted by peuge on Mon, 16 Mar 2020 05:58:34 +0100

200316 idea + Maven zero infrastructure java agent project

Java Agent(java probe) has existed since jdk1.5, but for most business development javaer s, it is still quite magical and strange; although in actual business development, agent development is rarely involved, but every java development has been used, such as using idea to write a HelloWorld.java, and running it, and looking at the console output carefully

As an introduction to Java Agent, this article will teach you how to develop a Java Agent with time-consuming statistical methods

<!-- more -->

1. Java agent development

First of all, define our development environment, choose IDEA as editor, maven for package management

1. Core logic

Create a new project (or sub module), and then we create a new SimpleAgent class

public class SimpleAgent {

    /**
     * jvm Start in parameter form, run this method
     *
     * @param agentArgs
     * @param inst
     */
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("premain");
    }

    /**
     * Start in dynamic attach mode, run this method
     *
     * @param agentArgs
     * @param inst
     */
    public static void agentmain(String agentArgs, Instrumentation inst) {
        System.out.println("agentmain");
    }
}

Let's ignore the specific play methods of the above two methods. First, let's have a brief look at the differences between the two methods. The notes also say that

  • jvm parameter form: call premain method
  • attach method: call the agentmain method

The jvm mode, that is to say, to use the target application of this agent, when starting, you need to specify the jvm parameter - javaagent:xxx.jar, which can be used when the agent we provide belongs to the basic essential service

When the target application starts, we do not add Java agent to load our agent, but still hope that the target program uses our agent. At this time, we can use the attach method (the specific usage posture will be introduced later). Naturally, we will think that if our agent is used to debug and locate problems, we can use this method

2. pack

The above simple Agent has finished the core functions of our Agent (that is, it is so simple). Next, we need to print a Jar package

Through the maven plug-in, you can simply output a compliant java agent package. There are two common usage gestures

a. pom specified configuration

In the pom.xml file, add the following configuration, note the parameters in the manifest entries tab

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifestEntries>
                        <Premain-Class>com.git.hui.agent.SimpleAgent</Premain-Class>
                        <Agent-Class>com.git.hui.agent.SimpleAgent</Agent-Class>
                        <Can-Redefine-Classes>true</Can-Redefine-Classes>
                        <Can-Retransform-Classes>true</Can-Retransform-Classes>
                    </manifestEntries>
                </archive>
            </configuration>

            <executions>
                <execution>
                    <goals>
                        <goal>attached</goal>
                    </goals>
                    <phase>package</phase>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Then, we use the mvn assembly:assembly command to package. In the target directory, we can see a jar package with the suffix of jar with dependencies, which is our target

b. MANIFEST.MF configuration file

It may be more common through the configuration file MANIFEST.MF. Here is a brief introduction to using poses

  • Under resources, create a new directory META-INF
  • In the META-INF directory, create a new file, MANIFEST.MF

The contents of the document are as follows

Manifest-Version: 1.0
Premain-Class: com.git.hui.agent.SimpleAgent
Agent-Class: com.git.hui.agent.SimpleAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

Please note that the last blank line (if I don't show it above, it's mostly a mark down rendering problem), can't be less. In idea, when deleting the last line, there will be an error reminder

Then our pom.xml configuration needs to be modified accordingly

<build>
  <plugins>
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-assembly-plugin</artifactId>
          <configuration>
              <descriptorRefs>
                  <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
              <archive>
                  <manifestFile>
                      src/main/resources/META-INF/MANIFEST.MF
                  </manifestFile>
                  <!--<manifestEntries>-->
                      <!--<Premain-Class>com.git.hui.agent.SimpleAgent</Premain-Class>-->
                      <!--<Agent-Class>com.git.hui.agent.SimpleAgent</Agent-Class>-->
                      <!--<Can-Redefine-Classes>true</Can-Redefine-Classes>-->
                      <!--<Can-Retransform-Classes>true</Can-Retransform-Classes>-->
                  <!--</manifestEntries>-->
              </archive>
          </configuration>

          <executions>
              <execution>
                  <goals>
                      <goal>attached</goal>
                  </goals>
                  <phase>package</phase>
              </execution>
          </executions>
      </plugin>
  </plugins>
</build>

Also packaged with the mvn assembly:assembly command

2. Agent usage

Agent has it. Next, we need to test the use of agent. The above two methods are proposed, and we will explain them separately

1. jvm parameters

First, create a demo project and write a simple test class

public class BaseMain {

    public int print(int i) {
        System.out.println("i: " + i);
        return i + 2;
    }

    public void run() {
        int i = 1;
        while (true) {
            i = print(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        BaseMain main = new BaseMain();
        main.run();
        Thread.sleep(1000 * 60 * 60);
    }
}

In the test class, there is a dead cycle. Each 1s calls the print method. During the IDEA test, you can directly configure the class and add the jvm parameters, as follows

Please note that the content in the red box above is the absolute address of the agent packaged in the previous section: - javaagent: / users /.. / target / java-agent-1.0-snapshot-jar-with-dependencies.jar

After executing the main method, you will see the console output

Please note the premain above. This is the output of the premain method in our SimpleAgent above, and it is only output once

2. attach mode

When using attach mode, it can be simply understood that we want to inject our agent into the target application, so we need to start a program ourselves to accomplish this

public class AttachMain {
    public static void main(String[] args)
            throws IOException, AgentLoadException, AgentInitializationException, AttachNotSupportedException {
        // The attach method parameter is the process number of the target application
        VirtualMachine vm = VirtualMachine.attach("36633");
        // Please replace this with your own agent absolute address
        vm.loadAgent("/Users/......./target/java-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");
    }
}

The above logic is relatively simple. First, get the process number of the target application through jps-l

When the main method above is completed, the console will output two lines of logs similar to the following. It can be simply understood that I connected to the target application, lost an agent, and left without taking any clouds with a wave of sleeves

Connected to the target VM, address: '127.0.0.1:63710', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:63710', transport: 'socket'

Next, take a look at the output of BaseMain above. A line of agentmain is sandwiched in the middle, indicating that the agent has been successfully injected

3. summary

This paper introduces the whole process of developing and packaging a Java agent of hello world under the environment of maven + idea

Two methods

Method Explain Using posture
premain() Called when the agent is loaded in jvm mode, that is, when the target application is started, the agent is specified -javaagent:xxx.jar
agentmain() Called when the agent is running in attach mode and used when the target application is working normally VirtualMachine.attach(pid) to specify the target process number < br / > vm.loadagent ("... Jar") load agent

Two packing postures

When packaging as an available java agent, you need to pay attention to the configuration parameters. There are two ways provided above. One is to specify the configuration directly in pom.xml

<manifestEntries>
    <Premain-Class>com.git.hui.agent.SimpleAgent</Premain-Class>
    <Agent-Class>com.git.hui.agent.SimpleAgent</Agent-Class>
    <Can-Redefine-Classes>true</Can-Redefine-Classes>
    <Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>

The other is written in the configuration file META-INF/MANIFEST.MF (note that the last blank line is indispensable)

Manifest-Version: 1.0
Premain-Class: com.git.hui.agent.SimpleAgent
Agent-Class: com.git.hui.agent.SimpleAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

Of course, after reading the content of this article, you will find that the actual development of java agent is still unclear. Is agent just outputting a line of hello world in the front, which is totally different from what you think

In the next blog, I will teach you how to implement a java agent package with time-consuming method statistics. I will explain in detail how to use the interface Instrumentation to implement bytecode modification, so as to realize function enhancement

II. other

0. source code

1. A grey Blog: https://liuyueyi.github.io/hexblog

A grey personal blog, recording all the learning and working blog, welcome to visit

2. statement

The best letter is not as good as the above. It's just a one-of-a-kind remark. Due to my limited ability, there are inevitably omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct. Thank you very much

3. Scanning attention

A grey blog

Topics: Programming Java git Maven jvm