Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inject a class into the java.lang package

When trying to inject a class which is in the java.lang namespace via java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch on a OpenJDK 11, nothing happens and no error is thrown. When placing the class to inject into a different package, it works as expected.

JarFile jar = new JarFile(new File("file/to/bootstrap.jar));
instrumentation.appendToBootstrapClassLoaderSearch(jar);
// throws ClassNotFoundException java/lang/Dispatcher
Class.forName("java.lang.Dispatcher", false, null);
bootstrap.jar
 └─ java/lang/Dispatcher.class

The reason I want to do this is to overcome issues with some OSGi containers. They typically restrict delegation to the bootstrap class loader to only certain packages. By default that obviously always includes java.* which is why I want to put my Dispatcher class there. I'm aware of org.osgi.framework.bootdelegation but that property only gets read during initialization. That means when attaching an agent at runtime, it's already too late to override this value.

An alternative would be to instrument all known OSGi class loaders and to white-list the agent classes. But doing that for each framework and test that for each version seems less feasible.

How can I inject a custom class like java.lang.Dispatcher into the bootstrap class loader? Are there other patterns or best practices to avoid OSGi bootdelegation issues?

To provide some more context:

My idea is to only inject this one Dispatcher class into the bootstrap class loader. The dispatcher basically just holds a static Map. The rest of the agent's classes would be loaded by a dedicated URLClassLoader which is a child of the bootstrap class loader. The agent would then register MethodHandles in the dispatcher's Map so that the injected byte code can get ahold of the MethodHandles which enable accessing the agent's classes loaded in the agent class loader.

like image 684
Felix Avatar asked Aug 11 '19 15:08

Felix


People also ask

What is @inject in Java?

Annotation Type Inject. @Target(value={METHOD,CONSTRUCTOR,FIELD}) @Retention(value=RUNTIME) @Documented public @interface Inject. Identifies injectable constructors, methods, and fields. May apply to static as well as instance members.

Why injection is used in Java?

Dependency injection enables you to turn regular Java classes into managed objects and to inject them into any other managed object. Using dependency injection, your code can declare dependencies on any managed object.

What is dependency injection in Java example?

The general concept behind dependency injection is called Inversion of Control. A Java class has a dependency on another class, if it uses an instance of this class. We call this a class dependency. For example, a class which accesses a logger service has a dependency on this service class.


1 Answers

It is possible by using unsafe API. Since Java 9, the boot class loader's implementation has changed to only check a designated jmod for a known package, but the boot search path is no longer checked.

Java 11 also removed the sun.misc.Unsafe#defineClass method but the same method is still available in jdk.internal.misc.Unsafe.

You do have to open that class's module which is internal. You can either do so by using sun.misc.Unsafe which allows you to write a field value (accessible) without accessibility checks or by using Instrumentation's official API.

If you are using Byte Buddy, have a look at the ClassInjector implementations which offer implementations for all approaches.

There is an open ticket for adressing the need of Java agents to inject helper classes but until it is resolved, this is a common workaround.

like image 133
Rafael Winterhalter Avatar answered Sep 28 '22 00:09

Rafael Winterhalter