Chapter 2: OnMethodEnter Advice

This article is an excerpt from the ebook titled “Java Interceptor Development with ByteBuddy: Fundamental ”. The excerpt is the chapter two of the ebook.

Table of Content
Chapter 1: Introduction

This chapter explains on how to implement the on method enter advice. This chapter also explains on how to build and execute the instrumentation process. This process will be repeated in every chapters of this book. The source code for this chapter is included in ChapterVisitOnMethodEnterAdvice Eclipse project. This is the project structure:

There are three packages: interceptor, plugin, and program.

Interceptor package contains the advice code, LogInterceptor.java.

Plugin package contains the plugin program that provides the interception
logic. In this example, InterceptorPlugin.java is the plugin program. Maven build process uses the plugin program to produce the instrumented code.

Program package contains the functional code, DataProducer.java, and one Java program, InterceptorProgram.java, which is used to execute the instrumented code, which can be used to test and view the result of the instrumentation.

target folder stores the result of maven build process, including Java
class file, instrumented code, and jar file.

sourceCodeView folder stores the instrumented code in Java file format.

In this project, the create method of DataProducer.java is the method that provides the functional code. The method prints a status “create data” on screen.

The project adds advice code to create method of DataProducer.class.The advice code applies the On Method Enter advice. This type of advice will add the advice code at the start of the execution of Java method:

Functional code

Instrumented code

The plugin program will transform the Java bytecode (Java class file), no the Java source code. Therefore, after the instrumentation process, the Java source code remains unchanged, but the content of Java class file will change: the class file contains both advice code and functional code. The explanation above uses Java source code to show the instrumented code for easy-to-understand purpose. The project will also store this Java source code in the sourceCodeView folder.

Executing the project

The project uses maven build process to produce the instrumented code. To start the maven build, executes the project using maven clean package -X command.

Follows these steps to execute clean package -X in Eclipse:
1. Right click on project root node ChapterVisitOnMethodEnter.
2. Select Run as -> Maven build. Eclipse will show the Edit Configuration
window.
3. Enter clean package -X in Goals textbox.

4. Click Run button to start the maven build process.

Once the maven build process completed, execute the InterceptorProgram.java:
1. Rigth click on InterceptorProgram.java, which is in the package explorer or project explorer of the Eclipse.
2. Select Run as -> Java application

The Eclipse will execute InterceptorProgram.java, and this is the result on Eclipse console:

Maven Build Process

The maven build process consists of these processes:
1. Maven cleans the project target folder.
2. Maven compiles all the java source codes available in the project, and stores the Java class file in project target folder.
3. Maven executes the ByteBuddy instrumentation process by invoking the InterceptorPlugin.java.
4. The plugin program scans all the Java class file one by one in the project target folder.
5. For each Java class file, the plugin program invokes matches method to find the functional code for interception.
6. If found, then the matches method return true, and the plugin program
continue to the apply method. If not found, then plugin program
repeats the process starts from step 5.
7. In apply method, InterceptorPlugin.java applys the advice code to the functional code.
8. InterceptorPlugin.java creates the instrumented code in bytecode (Java class file) format and stores the code in the project target folder.
9. Maven repeats step 4 to step 8 until InterceptorPlugin.java checks all the class files in the project.
10. Maven creates a jar file that package all the Java class file, including the instrumented code.

This is the ByteBuddy configuration in the pom.xml:

The plugin requires the byte-buddy-maven-plugin. To enable the instrumentation process, the <goal> tag must have a value of transform. Therefore, another name for ByteBuddy instrumentation process is transformation.

The <transformation> tag must have the fully qualified class name of the plugin program. The project uses com.millionstrengthknowledge.interceptorprj.plugin.InterceptorPlugin. The project can have one or more plugin programs.

InterceptorPlugin

InterceptorPlugin.java is the plugin program that performs the matching logic and interception logic. The InterceptorPlugin.java implements the Java interface of net.bytebuddy.build.Plugin, and three methods are derived from the interface: matches, apply, close:

The plugin program will check all the Java class files in the project target folder. The plugin program invokes matches method to execute the first level matching logic. This is the matching logic implemented in the InterceptorPlugin.java:

In line 7 to 9, the matching logic wants to find the DataProducer.class file. The logic compares the current Java class’s name with the DataProducer’s class name. The method returns true when the class name matches, otherwise the method prints a “Inspected code is not the target code” message and return false. The program will continue to apply method only when matches method return true.

The apply method provides the interception logic. The interception logic is the code that adds the advice code to functional code based on the state of second level matching logic. The second level matching logic further filters the Java class file found in matches method. The second level matching is based on the method name, field name, annotation, and other Java element of the found Java class file.

In ByteBuddy, most of the matching logic is created using net.bytebuddy.
matcher.ElementMatchers
. Chapter 17 will explain more example on the usage of ElementMatchers.

This is the implementation of apply method of InterceptorPlugin.java:

The first parameter, builder, is type of net.bytebuddy.dynamic
.DynamicType.Builder
. The method uses visit method of builder to add the advice code. Visit method will create the advice code that is in Java bytecode format, and then inlines the bytecode to the bytecode of the functional code.

Within visit method parenthesis, construct the advice configuration using Advice.to method. In this example, the Advice.to method specify the parameter value with “LogInterceptor.class”. LogInterceptor.java is the Java class that provides the advice code.

After that, specify the second level matching logic using on method.
The on method uses the ElementMatchers to specify the matching criteria. The criteria uses named method to match “create” method in
DataProducer.class. ByteBuddy will only inline the advice code when
the second level matching criteria returns true.

The named method finds the match based on exact name of Java element. Java element can be either method, field, generic type, and others. In this example, the named method is used to find the method with “create” name in DataProducer.class.

The plugin program does not use close method, which is one of the
derived method of Plugin interface. The close method will be invoked
only once after all the Java class files in the project target folder has been checked for the instrumentation. Therefore, the close method is suitable used to close any resources created in matches and apply method. This is the implementation of close method of InterceptorPlugin.java:

The method simply prints a message “InterceptorPlugin close method”
on screen. All the trace generated from the plugin program can be
viewed via Eclipse console.

Advice Code

This chapter uses on method enter advice. This is the implementation of advice code in LogInterceptor.java:

In LogInterceptor.java, the start method is the method that provides
the advice logic. The start method is very simple, the method simply
creates a log message states that the instrumented method is started.

@OnMethodEnter annotation must be annotated on this method so
that ByteBuddy knows the code segment that provides the advice logic. Besides that, the advice method must be static. Otherwise the instrumentation process will fail.

Therefore, ByteBuddy transforms the functional code into instrumented code via this process:
(1) Finds the static method with @OnMethodEnter annotation:

(2) Convert the method content into bytecode that is valid for Java execution. In this case, this will be:
From

To

(3) After that, ByteBuddy adds the generated bytecode into DataProducer.
class
: So, the result of instrumented code contains the non-functional
code and functional code:

Please be noticed that ByteBuddy only processes the Java bytecode in Java class format. This means the DataProducer.java remains unchanged, but the DataProducer.class will change. The explanation above uses Java source code to explain for easy-to-understand purpose.

Conclusion

This chapter explains:
• How to execute ByteBuddy instrumentation process in Eclipse
• The instrumentation process via Maven build process.
• How to execute generated bytecode via InterceptorProgram.java, and view its result on console
• The configuration of plugin in pom.xml
• How to create plugin program
• How to create On Method Enter advice
• How to inline bytecode of advice code into bytecode of functional code by using the visit method of net.bytebuddy.dynamic.DynamicType.Builder

Source code at github: https://github.com/MillionStrengthKnowledge/bytebuddyChpt02

Chapter 1: Introduction

I am very interested in Java, web app and mobile app development. I enjoy doing R&D in Java during my spare time

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store