easy-agent

Easy proxy bytecode injector at the bytecode level.

License

License

GroupId

GroupId

software.fitz
ArtifactId

ArtifactId

easy-agent
Last Version

Last Version

0.4.0-RELEASE
Release Date

Release Date

Type

Type

pom
Description

Description

easy-agent
Easy proxy bytecode injector at the bytecode level.
Project URL

Project URL

https://github.com/joongsoo/easy-agent
Source Code Management

Source Code Management

https://github.com/joongsoo/easy-agent

Download easy-agent

How to add to project

<!-- https://jarcasting.com/artifacts/software.fitz/easy-agent/ -->
<dependency>
    <groupId>software.fitz</groupId>
    <artifactId>easy-agent</artifactId>
    <version>0.4.0-RELEASE</version>
    <type>pom</type>
</dependency>
// https://jarcasting.com/artifacts/software.fitz/easy-agent/
implementation 'software.fitz:easy-agent:0.4.0-RELEASE'
// https://jarcasting.com/artifacts/software.fitz/easy-agent/
implementation ("software.fitz:easy-agent:0.4.0-RELEASE")
'software.fitz:easy-agent:pom:0.4.0-RELEASE'
<dependency org="software.fitz" name="easy-agent" rev="0.4.0-RELEASE">
  <artifact name="easy-agent" type="pom" />
</dependency>
@Grapes(
@Grab(group='software.fitz', module='easy-agent', version='0.4.0-RELEASE')
)
libraryDependencies += "software.fitz" % "easy-agent" % "0.4.0-RELEASE"
[software.fitz/easy-agent "0.4.0-RELEASE"]

Dependencies

There are no dependencies for this project. It is a standalone project that does not depend on any other jars.

Project Modules

  • easy-agent-core
  • easy-agent-api

easy-agent

This library was created to easily develop a Java agent that injects proxy code into the class bytecode.

If you write code in Java without needing to know the bytecode instrumentation, it is injected into bytecode.

If you want see more info about this framework, See Github wiki documentation

Table of content


๐Ÿš€ Summary

From an architectural point of view, the Java agent is a good way to inject logic from outside your application.

Many applications are increasingly using cloud and virtualized environments. In that case, there are times when you need to write code that is infrastructure dependent.

For example, if you need to implement istio distributed tracing, You will write code to propagate HTTP headers inside your application.

However, if you write like this, your application will depend to istio.

If using java agent, infrastructure dependent logic can be managed at the infrastructure level. And independence and reusability increase, because it is injected at the infrastructure layer.

image

easy-agent helps you develop java agent easily.


๐Ÿš€ Feature

Fast

easy-agent uses ASM, it is low-level library. ASM provide high performance.

Easy

You don't need to know bytecode transform. You can easily inject a proxy with simple java code.

Productivity

Productivity is improved by placing infrastructure-dependent code at the infrastructure layer.

You don't have to think about infrastructure when developing and deploying applications. Because the infrastructure-dependent logic is injected by the agent.

Extension

easy-agent provides a easy-agent-api that can be easily extended. You can easily develop reusable plugins.

You can see more info in plugin wiki page.

Documentation

We try to give you the most detailed and friendly documentation possible. If there is any room for improvement in the document, please make a suggestion.


๐Ÿš€ Extend via plugin

You can make reusable easy-agent plugin. it is very simple. See more details


๐Ÿš€ Quick start

Just follow 6 simple steps.

(1) Add maven dependency to pom.xml

<dependency>
    <groupId>software.fitz</groupId>
    <artifactId>easy-agent-core</artifactId>
    <version>0.4.0-RELEASE</version>
</dependency>

(2) Implements AroundInterceptor

Override only necessary methods.

public class YourInterceptor implements AroundInterceptor {

    @Override
    public Object[] before(Object target, Method targetMethod, Object[] args) {
        // your proxy code
        if (args[0] instanceof String) {
            args[0] = args[0] + " hi"; // possible replace to target method arguments
        }
        return args;
    }

    @Override
    public Object after(Object target, Method targetMethod, Object returnedValue, Object[] args) {
        // your proxy code
        return returnedValue; // possible replace to return value
    }

    @Override
    public void thrown(Object target, Method targetMethod, Throwable t, Object[] args) {
        // your proxy code
    }
}

(3) Define your plugin & Register interceptor to your target class

public class YourPlugin implements Plugin {

    @Override
    public void setup(TransformerRegistry transformerRegistry) {

        transformerRegistry.register(
                TransformDefinition.builder()
                        // "+" means applies to all classes that extend this class
                        .transformStrategy(TransformStrategy.className("package.Class+")) 
                        .matchArgs(MethodDefinition.all("targetMethodName"))
                        .addInterceptor(YourInterceptor.class)
                        .build()
        );
    }
}

(4) Define premain class & Register your plugin

public class PremainClass {

    public static void premain(String agentArgs, Instrumentation instrumentation) {

        EasyAgentBootstrap.initialize(agentArgs, instrumentation)
                .addPlugin(new YourPlugin())
                .start();
    }
}

(5) Add maven plugin to your pom.xml

  1. Replace {your-jar-name} with the name of the jar you want.
  2. Replace {package.to.premain-class} with a class that implements the premain method.
<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2</version>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
            <configuration>
                <finalName>{your-jar-name}</finalName>
                <appendAssemblyId>false</appendAssemblyId>
                <archive>
                    <manifest>
                        <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                        <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                    </manifest>
                    <manifestEntries>
                        <Premain-Class>{package.to.premain-class}</Premain-Class>
                        <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        <Can-Redefine-Classes>true</Can-Redefine-Classes>
                        <Boot-Class-Path>{your-jar-name}.jar</Boot-Class-Path>
                    </manifestEntries>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
        </execution>
    </executions>
</plugin>

(6) Run with your application

Specify your agent jar in the -javaagent argument.

java -javaagent:/path/{your-agent}.jar -jar target-application.jar

๐Ÿš€ Example

Example of replacing method arguments.

You can see more examples in wiki page.

AroundInterceptor

public class YourInterceptor implements AroundInterceptor {

    @Override
    public Object[] before(Object target, Method targetMethod, Object[] args) {
        if (args[0] != null && args[0] instanceof String) {
            args[0] = args[0] + " Hi!";
        }
        return args;
    }
}

Plugin

The process of registering the module was omitted.

public class YourPlugin implements Plugin {

    @Override
    public void setup(TransformerRegistry transformerRegistry) {

        transformerRegistry.register(
                TransformDefinition.builder()
                        .transformStrategy(TransformStrategy.className("package.TargetClass")) 
                        .addTargetMethod(matchArgs("printName", String.class))
                        .addInterceptor(YourInterceptor.class)
                        .build()
        );
    }
}

Target class & Main class

public class TargetClass {
    
    public void printName(String name) {
        System.out.println(name); 
    }

    public static void main(String[] args) {
        new TargetClass().printName("joongsoo");
    }
}

Execute result

joongsoo Hi!

Versions

Version
0.4.0-RELEASE
0.3.0-RELEASE
0.1.0-RELEASE