Вставьте пользовательский класс в загрузчик загрузчика классов в модуль java.base

#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;
        }
    }
}