#java #byte-buddy
Вопрос:
Я пытаюсь вставить свой пользовательский класс в загрузчик загрузчиков классов, чтобы иметь возможность вызывать sun.nio.ch.IOUtil#fdVal(FileDescriptor)
, чтобы получить значение описателя файлов и использовать его в своих советах (я завернул sun.nio.ch.FileChannelImpl
)
Я успешно вставил свой pbouda.agents.core.Utils
в загрузчик загрузочных классов.
public abstract class Utils {
public static final int ERROR_FD_VALUE = Integer.MIN_VALUE;
private static Method FD_VALUE_METHOD;
static {
try {
Class<?> clazz = Class.forName("sun.nio.ch.IOUtil");
FD_VALUE_METHOD = clazz.getMethod("fdVal", FileDescriptor.class);
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
}
}
public static int fdVal(FileDescriptor fd) {
if (FD_VALUE_METHOD != null) {
try {
return (int) FD_VALUE_METHOD.invoke(null, fd);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
return ERROR_FD_VALUE;
}
}
с помощью
public abstract class UtilsInitializer {
private static boolean INITIALIZED = false;
public static synchronized void initialize() {
if (!INITIALIZED) {
TypePool typePool = TypePool.Default.ofSystemLoader();
Map<TypeDescription, byte[]> types = new ByteBuddy()
.redefine(
typePool.describe("pbouda.agents.core.Utils").resolve(),
ClassFileLocator.ForClassLoader.ofSystemLoader())
.make()
.getAllTypes();
ClassInjector.UsingUnsafe.ofBootLoader()
.inject(types);
INITIALIZED = true;
}
}
}
Я это доказал!
~ jcmd 281091 VM.classloaders show-classes [21/09/21|11:50AM]
281091:
-- <bootstrap>
|
| Classes: java.lang.Object
| [Ljava.lang.Object;
| [[Ljava.lang.Object;
| java.io.Serializable
...
| pbouda.agents.core.Utils
| (1522 classes)
|
| Hidden Classes: java.lang.invoke.LambdaForm$DMH/0x0000000800d89c00
| java.lang.invoke.LambdaForm$DMH/0x0000000800d89800
К сожалению, это, конечно, в другом модуле 🙁
java.lang.IllegalAccessException: class pbouda.agents.core.Utils cannot access class sun.nio.ch.IOUtil
(in module java.base) because module java.base does not export sun.nio.ch to unnamed module @7e928e2f
at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
at java.base/java.lang.reflect.Method.invoke(Method.java:560)
at pbouda.agents.core.Utils.fdVal(Utils.java:26)
at java.base/sun.nio.ch.FileChannelImpl.open(FileChannelImpl.java:151)
at java.base/sun.nio.fs.UnixChannelFactory.newFileChannel(UnixChannelFactory.java:134)
at java.base/sun.nio.fs.UnixChannelFactory.newFileChannel(UnixChannelFactory.java:146)
at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:217)
at java.base/java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:484)
at java.base/java.nio.file.Files.newOutputStream(Files.java:228)
at java.base/java.nio.file.Files.newBufferedWriter(Files.java:3008)
at java.base/java.nio.file.Files.newBufferedWriter(Files.java:3056)
at pbouda.agents.file.FileTest.bufferedWriter(FileTest.java:38)
Знаете ли вы, как ввести его, java.base
чтобы избежать запуска программы с add-opens
помощью ?
Ответ №1:
Любой модуль заранее подготавливает свои пакеты. Чтобы добавить что-либо в java.base, необходимо добавить класс в пакет, который уже содержит java.base, например java.lang.
Это больше невозможно использовать Instrumentation.appendToBootSearchPath
, так как пакеты java.base направлены только на соответствующий jmod. Вместо этого вы можете открывать модули, в том числе java.base, с помощью нового modifyModule
API.
Если вы действительно хотели внедрить классы, это все еще возможно с помощью Unsafe
. Byte Buddy предлагает фабрику для этого в соответствии со своим AgentBuilder
. Посмотрите, ClassInjector.UsingUnsafe.Factory
какие предложения предлагают создать такой инжектор. Instrumentation
Экземпляр должен вернуться к внутреннему Ùnsafe
API JDKs после удаления полупубличного экземпляра.
Ответ №2:
Спасибо, Рафаэль!
Я поменял упаковку pbouda.agents.core.Utils
-> sun.nio.ch.Utils
И изменил инициализатор, и он работает!! (Неприятно, но это хорошее начало для реализации поиска утечек дескрипторов 🙂
public abstract class UtilsInitializer {
private static boolean INITIALIZED = false;
public static synchronized void initialize(Instrumentation inst) {
if (!INITIALIZED) {
TypePool typePool = TypePool.Default.ofSystemLoader();
Map<TypeDescription, byte[]> types = new ByteBuddy()
.redefine(
typePool.describe("sun.nio.ch.Utils").resolve(),
ClassFileLocator.ForClassLoader.ofSystemLoader())
.make()
.getAllTypes();
ClassInjector.UsingUnsafe.ofBootLoader()
.inject(types);
INITIALIZED = true;
}
}
}