Using JVM‑Sandbox for Exception Injection and Code Enhancement in Java Services
This article introduces JVM‑Sandbox, explains its non‑intrusive AOP capabilities, and provides a step‑by‑step tutorial on installing the sandbox, loading custom modules, performing code enhancement via instrumentation, and testing exception injection in a Java service.
Background – The current testing platform at ZhiZhuan uses JVM injection and traffic replay to mock hard‑to‑cover scenarios and reduce risk of code changes, both built on jvm‑sandbox technology. The author shares initial experiences.
Introduction – jvm‑sandbox installs a sandbox JVM inside the target JVM, loading proxy classes via a custom classloader to achieve AOP without restarting or intruding the application. It offers non‑intrusive, class‑isolated, pluggable, multi‑tenant, and highly compatible features for fault localization, flow control, fault simulation, method recording, dynamic logging, security monitoring, and data masking.
Step 1: Install the Sandbox – Add the -javaagent:/sandbox/lib/sandbox-agent.jar option when starting service A. The javaagent triggers AgentLauncher.premain , which calls install to load the sandbox into the target JVM. The installation process adds sandbox‑spy.jar to the BootstrapClassLoader, creates a custom SandboxClassLoader , instantiates CoreConfigure , and starts a Jetty server for HTTP‑based sandbox control.
java -javaagent:/sandbox/lib/sandbox-agent.jar public static void premain(String featureString, Instrumentation inst) {
// avoid affecting service startup
try {
LAUNCH_MODE = LAUNCH_MODE_AGENT;
install(toFeatureMap(featureString), inst);
} catch (Exception e) {
e.printStackTrace();
}
} private static synchronized InetSocketAddress install(final Map
featureMap, final Instrumentation inst) {
final String namespace = getNamespace(featureMap);
final String propertiesFilePath = getPropertiesFilePath(featureMap);
final String coreFeatureString = toFeatureString(featureMap);
try {
final String home = getSandboxHome(featureMap);
// inject Spy into BootstrapClassLoader
inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(getSandboxSpyJarPath(home))));
// create custom classloader
final ClassLoader sandboxClassLoader = loadOrDefineClassLoader(namespace, getSandboxCoreJarPath(home));
// load CoreConfigure class
final Class
classOfConfigure = sandboxClassLoader.loadClass(CLASS_OF_CORE_CONFIGURE);
final Object objectOfCoreConfigure = classOfConfigure.getMethod("toConfigure", String.class, String.class)
.invoke(null, coreFeatureString, propertiesFilePath);
// load CoreServer class
final Class
classOfProxyServer = sandboxClassLoader.loadClass(CLASS_OF_PROXY_CORE_SERVER);
final Object objectOfProxyServer = classOfProxyServer.getMethod("getInstance").invoke(null);
final boolean isBind = (Boolean) classOfProxyServer.getMethod("isBind").invoke(objectOfProxyServer);
if (!isBind) {
try {
classOfProxyServer.getMethod("bind", classOfConfigure, Instrumentation.class)
.invoke(objectOfProxyServer, objectOfCoreConfigure, inst);
} catch (Throwable t) {
classOfProxyServer.getMethod("destroy").invoke(objectOfProxyServer);
throw t;
}
}
return (InetSocketAddress) classOfProxyServer.getMethod("getLocal").invoke(objectOfProxyServer);
} catch (Throwable cause) {
throw new RuntimeException("sandbox attach failed.", cause);
}
}Step 2: Load Module B – jvm‑sandbox follows a plugin architecture. Modules (system or custom) are placed under /sandbox/bin/../module or /.sandbox-module . Custom modules implement the Module interface, declare a unique ID via @Information , and define event watchers and command handlers. In this example, exception injection logic resides in custom module B, loaded by ModuleJarClassLoader .
void load(final ModuleJarLoadCallback mjCb, final ModuleJarLoader.ModuleLoadCallback mCb) {
for (final File moduleJarFile : listModuleJarFileInLib()) {
try {
mjCb.onLoad(moduleJarFile);
new ModuleJarLoader(moduleJarFile, mode).load(mCb);
} catch (Throwable cause) {
logger.warn("loading module-jar occur error! module-jar={};", moduleJarFile, cause);
}
}
}Step 3: Code Enhancement – After loading, the module’s watch method registers a SandboxClassFileTransformer with the Instrumentation instance. All subsequent class loads pass through this transformer, which matches target classes, retransforms them via retransformClasses , and activates an EventListenerHandler to listen for sandbox commands.
Create SandboxClassFileTransformer and register it.
Instrumentation adds the transformer, affecting all future class loads.
Match and select classes that need enhancement.
Use retransformClasses to modify already loaded classes.
Activate EventListenerHandler to receive sandbox instructions.
The following image shows the decompiled class before exception injection, and the next image shows the class after injection where the Spy class weaves code into method entry, return, and exception paths.
Step 4: Test Service A – With the exception injection active, the file‑generation method always fails, demonstrating the sandbox’s ability to dynamically alter business logic.
Summary
The experience shows that by packaging desired enhancements as custom modules, placing them in the sandbox’s module directory, and installing jvm‑sandbox on a target service, developers can dynamically control and augment business code via HTTP requests, making jvm‑sandbox a flexible and easy‑to‑use code‑enhancement tool. Interested readers can try it at https://github.com/alibaba/jvm-sandbox .
转转QA
In the era of knowledge sharing, discover 转转QA from a new perspective.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.