JVM-Sandbox–Application of Drainage Playback in Testing Technology

Key point refinement

  • Sandbox briefly describes the principle: use virtualization technology to create an isolated environment, use bytecode enhancement to add the mocked method to the isolated environment; when called, Sandbox finds the method through the reflection mechanism, and uses the proxy object Forward it to the virtual machine in Sandbox for execution. After the execution of the Sandbox is completed, the result is returned to the main program.
  • Related principles: understand the class loading mechanism, reflection mechanism, proxy mechanism?.
  • Test application: Drainage playback – collect real online traffic, and replay the traffic (mock middleware, DB, etc.) in the prevention/test environment to find bugs.

1. What is JVM-Sandbox?

Ali’s Jvm-Sandbox is a Java application sandbox. Sandbox (Sandbox) refers to an isolated environment, similar to a closed box, in which programs or applications can be run without affecting the system Actions for other sections. In this environment, programs can only access authorized resources and functions, but not other system resources. The purpose of the sandbox technology is to protect the security and stability of the system, but also to provide a better application development and testing environment. By isolating the program in an independent environment, it can prevent it from accessing resources and data that should not be accessed, thereby reducing security risks.

2. What problems can it solve?

  • Sometimes a problem comes up suddenly, and you need to enter the parameters to complete the positioning, but there are no logs, and even appear in other people’s code. I really want to develop a tool that can dynamically add logs as needed, and it is best to filter according to the business ID. .

  • There are many tools that can be used for abnormal simulation between systems, but how to do abnormal simulation within the system, add a switch or use AOP to implement it in the development system, I really want to develop a more elegant abnormal simulation tool that can simulate abnormal between systems , and can simulate anomalies in the system.

  • I really want to obtain line call link data, which can be used to identify scenarios, coverage statistics, etc. The coverage statistics tool cannot natively support it, and the statistical link data is not accurate. I want to develop a tool to obtain line link data by myself.

  • I want to develop tools such as recording and playback, fault simulation, dynamic log, and link acquisition. Even if I complete the development, the underlying principles of these tools are the same, and they are used at the same time. How do I eliminate the gap between these tools? Impact, how to ensure that these tools are dynamically loaded, how to ensure that other tools will not be affected after dynamic loading/unloading, and how to ensure that when there is a problem with the tool, the impact is quickly eliminated and the code is restored.

3. Implementation principle
The implementation principle of Sandbox mainly depends on Java’s virtualization technology and bytecode enhancement technology.
First, Sandbox uses Java’s virtualization technology to create multiple isolated virtual machines (called Isolates) in the same process. Each Isolate has its own independent class loader, memory space and thread pool, in which different Java applications can be run, thereby achieving isolation between applications.
Then, Sandbox uses Java bytecode enhancement technology to dynamically modify the classes that need to be Mocked. Specifically, Sandbox will use the bytecode enhancement tool to generate a new class and method for the class and method that needs to be mocked, and add it to the virtual machine of Sandbox. In this way, new classes and methods can be called in Sandbox to realize the function of Mock.
Finally, when communicating and interacting between Sandbox and the main program, Sandbox adopts a method based on Java reflection and proxy. Specifically, when the main program needs to call a method in the Sandbox, the Sandbox finds the method through the reflection mechanism, and uses the proxy object to forward it to the virtual machine in the Sandbox for execution. After the execution of the Sandbox is completed, the result is returned to the main program.
Use sandbox to implement mock
Commonly used Mock frameworks include Mockito, EasyMock, etc. These frameworks usually use technologies such as bytecode enhancement or dynamic proxy to realize the Mock of classes and methods.

Features:

1. Isolation: Sandbox can isolate different Java applications to run in different virtual machines, avoiding mutual influence and interference between applications.

2. Security: Sandbox can implement security restrictions and monitoring on Java applications to prevent malicious code execution and attacks.

3. Lightweight: Sandbox uses lightweight virtualization technology, making its overhead smaller than traditional virtual machines.

4. Flexibility: Sandbox can customize different virtual machine environments according to different needs to meet different application scenarios.

5. High reliability: Sandbox uses a variety of technical means to ensure the stability and reliability of the virtual machine, such as snapshot and recovery.

Fourth, the specific implementation steps:
1. Create a Sandbox environment and load the classes and methods that need to be mocked.
2. Create a Mock object in the Sandbox environment and register it in the main program.
3. Call the Mock object in the main program, and the Mock object will return the preset result.
4. After the test is completed, destroy the Sandbox environment and release resources.
It should be noted that using Sandbox to implement Mock requires a certain understanding and mastery of the operating mechanism, class loading mechanism, and reflection mechanism of the Java virtual machine. In addition, due to the large performance overhead of Sandbox, it needs to be fully tested and evaluated in actual use to ensure that it will not have an excessive impact on system performance.

Five, implement the code

Goal: Mock off the add method in a class named Calculator

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

1. First, you need to import the dependent library of Sandbox and create a Sandbox environment:

Create a SandboxBuilder object, and add the jar package path where the Mock class needs to be located through the addClassPathEntry method. Then, we add the classes and methods that need to be mocked into the Sandbox environment through the addTransferModel method

SandboxBuilder sandboxBuilder = new SandboxBuilder();
sandboxBuilder.addClassPathEntry("path/to/calculator.jar");
// Add classes and methods that need to be mocked
sandboxBuilder.addTransferModel(new TransferModel.Builder(Calculator.class.getName())
                .methodName("add")
                .parameterTypes(int.class, int.class)
                .build());
Sandbox sandbox = sandboxBuilder. buildSandbox();

2. Create a Mock object in the Sandbox environment and register it in the main program:

Create a Mock object in the Sandbox environment through the sandbox.getObject method, and use the when.thenReturn method to preset the behavior of the Mock object. Then, we register the Mock object into the main program

// Create a Mock object
Object mockObj = sandbox. getObject(Calculator. class. getName());
when(mockObj.add(1, 2)).thenReturn(3);
// Register the Mock object to the main program
sandbox.getApplicationClassLoader().getTransletClasses().put(Calculator.class.getName(), mockObj.getClass());

3. Call the Mock object in the main program:

The add method in the Calculator class is called. According to the previously preset Mock behavior, the result should be output here: 3

Calculator calculator = new Calculator();
int result = calculator. add(1, 2);
System.out.println("result: " + result);

4. Destroy the Sandbox environment after the test is complete:

sandbox.destroy();

Full code:

Mock the add method in the Calculator class and output the result. It should be noted that due to the high performance overhead of Sandbox, it needs to be fully tested and evaluated in actual use to ensure that it will not have an excessive impact on system performance

import com.alibaba.jvm.sandbox.api.Sandbox;
import com.alibaba.jvm.sandbox.api.SandboxBuilder;
import com.alibaba.jvm.sandbox.api.model.TransferModel;

import static org.mockito.Mockito.*;

public class MockWithSandboxDemo {
    public static void main(String[] args) {
        SandboxBuilder sandboxBuilder = new SandboxBuilder();
        sandboxBuilder.addClassPathEntry("path/to/calculator.jar");
        // Add classes and methods that need to be mocked
        sandboxBuilder.addTransferModel(new TransferModel.Builder(Calculator.class.getName())
                .methodName("add")
                .parameterTypes(int.class, int.class)
                .build());
        Sandbox sandbox = sandboxBuilder. buildSandbox();

        try {
            // Create a Mock object
            Object mockObj = sandbox. getObject(Calculator. class. getName());
            when(mockObj.add(1, 2)).thenReturn(3);
            // Register the Mock object to the main program
            sandbox.getApplicationClassLoader().getTransletClasses().put(Calculator.class.getName(), mockObj.getClass());

            // Call the Mock object
            Calculator calculator = new Calculator();
            int result = calculator. add(1, 2);
            System.out.println("result: " + result);
        } finally {
            // Destroy the Sandbox environment
            sandbox. destroy();
        }
    }

    static class Calculator {
        public int add(int a, int b) {
            return a + b;
        }
    }
}