使用ByteBuddy为代理程序创建一个添加类的转换器

huangapple go评论68阅读模式
英文:

Creating a transformer to add a class from an agent using ByteBuddy

问题

我正在尝试从使用ByteBuddy实现的代理中加载类。我在代理中定义了一个类,并希望在目标程序中加载它。以下是我的转换器的代码示例:

public class ClassLoaderTransformer implements AgentBuilder.Transformer {
    private final Class<?> targetClass;

    public ClassLoaderTransformer(Class<?> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
        try {
            final Class<?> aClass = Class.forName(typeDescription.getName());

            resolveClassLoadingStrategy(aClass).load(classLoader, singletonMap(
                    new TypeDescription.ForLoadedType(targetClass),
                    ClassFileLocator.ForClassLoader.read(targetClass)
            ));
        } catch (Exception e){
            System.out.println("Something went terribly wrong: " + e.getMessage());
        }

        return builder;
    }

    private static ClassLoadingStrategy<ClassLoader> resolveClassLoadingStrategy(Class<?> targetClass) throws IllegalAccessException {
        if ( !ClassInjector.UsingLookup.isAvailable() ) {
            return new ClassLoadingStrategy.ForUnsafeInjection(targetClass.getProtectionDomain() );
        }

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(targetClass, lookup);

        return ClassLoadingStrategy.UsingLookup.of( privateLookup );
    }
}

然而,这段代码并没有正常工作。它抛出了以下错误:

tech.ikora.seleniumagent.helpers.SourcePageFetcher must be defined in the same package as org.openqa.selenium.remote.RemoteWebDriver

并且堆栈跟踪如下:

java.lang.IllegalArgumentException: tech.ikora.seleniumagent.helpers.SourcePageFetcher must be defined in the same package as org.openqa.selenium.remote.RemoteWebDriver
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1414)
    at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:110)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:492)
    at tech.ikora.seleniumagent.ClassLoaderTransformer.transform(ClassLoaderTransformer.java:27)
    ...

在此示例中,SourcePageFetchertargetClassRemoteWebDriver 是由 TypeDescription 提供的类。

这个类加载器的目标是加载一些辅助于我创建的一些 Advice 的类,以对我的代码进行处理。我应该尝试以另一种方式使用 ClassInjector 吗?还是我对问题的理解有误,或者我应该尝试使用另一种 ClassLoadingStrategy

英文:

I am trying to load classes from an agent implemented using ByteBuddy. I have a class defined in the agent and want to load it in the target program. here is what my transformer looks like:

public class ClassLoaderTransformer implements AgentBuilder.Transformer {
    private final Class&lt;?&gt; targetClass;

    public ClassLoaderTransformer(Class&lt;?&gt; targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public DynamicType.Builder&lt;?&gt; transform(DynamicType.Builder&lt;?&gt; builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
        try {
            final Class&lt;?&gt; aClass = Class.forName(typeDescription.getName());

            resolveClassLoadingStrategy(aClass).load(classLoader, singletonMap(
                    new TypeDescription.ForLoadedType(targetClass),
                    ClassFileLocator.ForClassLoader.read(targetClass)
            ));
        } catch (Exception e){
            System.out.println(&quot;Something went terribly wrong: &quot; + e.getMessage());
        }

        return builder;
    }

    private static ClassLoadingStrategy&lt;ClassLoader&gt; resolveClassLoadingStrategy(Class&lt;?&gt; targetClass) throws IllegalAccessException {
        if ( !ClassInjector.UsingLookup.isAvailable() ) {
            return new ClassLoadingStrategy.ForUnsafeInjection(targetClass.getProtectionDomain() );
        }

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandles.Lookup privateLookup = MethodHandles.privateLookupIn(targetClass, lookup);

        return ClassLoadingStrategy.UsingLookup.of( privateLookup );
    }
}

However, this code is not working. It throws the following error:

tech.ikora.seleniumagent.helpers.SourcePageFetcher must be defined in the same package as org.openqa.selenium.remote.RemoteWebDriver

and the stack traces looks like this:

java.lang.IllegalArgumentException: tech.ikora.seleniumagent.helpers.SourcePageFetcher must be defined in the same package as org.openqa.selenium.remote.RemoteWebDriver
at net.bytebuddy.dynamic.loading.ClassInjector$UsingLookup.injectRaw(ClassInjector.java:1414)
at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:110)
at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$UsingLookup.load(ClassLoadingStrategy.java:492)
at tech.ikora.seleniumagent.ClassLoaderTransformer.transform(ClassLoaderTransformer.java:27)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:10364)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10302)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access$1600(AgentBuilder.java:10068)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:10761)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$Java9CapableVmDispatcher.run(AgentBuilder.java:10699)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:389)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:10258)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$ByteBuddy$ModuleSupport.transform(Unknown Source)
at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:563)
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:515)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:423)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:417)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:689)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:416)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at org.apache.maven.surefire.booter.IsolatedClassLoader.loadClass(IsolatedClassLoader.java:97)
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:515)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:423)
at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:417)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:689)
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:416)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at org.apache.maven.surefire.booter.IsolatedClassLoader.loadClass(IsolatedClassLoader.java:97)
at base.BaseTest.setup(BaseTest.java:31)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:132)
at org.testng.internal.MethodInvocationHelper.invokeMethodConsideringTimeout(MethodInvocationHelper.java:61)
at org.testng.internal.ConfigInvoker.invokeConfigurationMethod(ConfigInvoker.java:366)
at org.testng.internal.ConfigInvoker.invokeConfigurations(ConfigInvoker.java:320)
at org.testng.internal.TestMethodWorker.invokeBeforeClassMethods(TestMethodWorker.java:176)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:122)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.testng.TestRunner.privateRun(TestRunner.java:764)
at org.testng.TestRunner.run(TestRunner.java:585)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:384)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:378)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:337)
at org.testng.SuiteRunner.run(SuiteRunner.java:286)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1218)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1140)
at org.testng.TestNG.runSuites(TestNG.java:1069)
at org.testng.TestNG.run(TestNG.java:1037)
at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:77)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:159)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:99)
at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:106)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.plugin.surefire.InPluginVMSurefireStarter.runSuitesInProcess(InPluginVMSurefireStarter.java:80)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeProvider(AbstractSurefireMojo.java:724)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAllProviders(AbstractSurefireMojo.java:682)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.executeAfterPreconditionsChecked(AbstractSurefireMojo.java:648)
at org.apache.maven.plugin.surefire.AbstractSurefireMojo.execute(AbstractSurefireMojo.java:586)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:210)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:156)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:148)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:957)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:289)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:193)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:282)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:225)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:406)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:347)

In this example, SourcePageFetcher is the targetClass and RemoveWebDriver is the class provided by the TypeDescription.

The goal of this class loader is to load classes that would be helpers to some of the Advice that I created to instrument my code. Should I try to use the ClassInjector in another way? Or am I seeing the problem in the wrong way or should I just use another ClassLoadingStrategy?

答案1

得分: 2

根据 @RafaelWinterhalter 的建议,对我有效的解决方案是使用带有 UsingUnsafe 的 ClassInjector。这导致了以下的可工作代码:

public class ClassLoaderTransformer implements AgentBuilder.Transformer {
    private final Class<?> targetClass;

    public ClassLoaderTransformer(Class<?> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
        try {
            ClassInjector.UsingUnsafe.ofBootLoader().inject(singletonMap(
                    new TypeDescription.ForLoadedType(targetClass),
                    ClassFileLocator.ForClassLoader.read(targetClass)
            ));
        } catch (Throwable e){
            System.out.println("Something went terribly wrong: " + e.getMessage());
        }

        return builder;
    }
}

需要注意的是,在这种情况下,类加载在引导类加载器中进行,因此我们应该尽量保持在那里加载的类尽可能简单。此外,在这里加载的类似乎无法访问由子类加载器加载的类,因此,我必须尽量将这些类保持得越简单越好。

英文:

Following @RafaelWinterhalter suggestion, The solution that worked for me was to use the ClassInjector with UsingUnsafe. This lead to the following working code:

public class ClassLoaderTransformer implements AgentBuilder.Transformer {
private final Class&lt;?&gt; targetClass;
public ClassLoaderTransformer(Class&lt;?&gt; targetClass) {
this.targetClass = targetClass;
}
@Override
public DynamicType.Builder&lt;?&gt; transform(DynamicType.Builder&lt;?&gt; builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
try {
ClassInjector.UsingUnsafe.ofBootLoader().inject(singletonMap(
new TypeDescription.ForLoadedType(targetClass),
ClassFileLocator.ForClassLoader.read(targetClass)
));
} catch (Throwable e){
System.out.println(&quot;Something went terribly wrong: &quot; + e.getMessage());
}
return builder;
}
}

Note that in this case, the class is loading in the boot class loader, hence we should try to keep the classes loaded there as simple as possible. Furthermore, the classes loaded here seem to not have access to classes loaded by the children class loader, so again, I had to keep the classes to the bear minimal.

答案2

得分: 1

错误信息提示:查找只允许您在指定查找类的包中定义类。如果包与您的情况不同:

tech.ikora.seleniumagent.helpers
org.openqa.selenium.remote

JVM 将不允许。但是,从代理中,您还可以使用UsingUnsafe 策略,因为Instrumentation 实例可以让您访问 JVM 内部。否则,您需要将注入钩子到正确包中的类。

请注意,您可以直接使用ClassInjector,而不是使用ClassLoadingStrategy

英文:

As the error message suggests: a lookup only allows you to define classes in packages of the specified lookup's class. If the package differs as it does for you:

tech.ikora.seleniumagent.helpers 
org.openqa.selenium.remote

the JVM would not allow for it. From an agent, you can however also use the UsingUnsafe strategy as the Instrumentation instance can grant you access to the JVM internals. Otherwise, you'd need to hook your injection to a class in the right package.

Note that you can rather use the ClassInjector directly instead of using a ClassLoadingStrategy.

huangapple
  • 本文由 发表于 2020年8月18日 23:19:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/63471729.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定