如何为字节码检测设置Intellij / jar清单?

时间:2017-09-27 10:46:11

标签: java unit-testing intellij-idea gradle bytecode

开始研究Java Bytecode Instrumentation。

和往常一样,Hello World就是我的开始。

短版:我构建了一个打印Hello World的项目,我现在想要修改该类,以便打印Hello ASM。为此,我实现了一个虚拟代理,我后来打算对其进行修改,以便进行必要的更改。如何修改build.gradle文件以便我可以使用/测试它?

[编辑:git clone https://bitbucket.org/RKor/helloasm.git]

长版

package helloasm;

import helloasm.instrumentation_targets.HelloWorld;

public class Main {
    public static void main(String[] args) {
        new HelloWorld().doSomething();
    }
}

,其中

package helloasm.instrumentation_targets;

public class HelloWorld {
    public String doSomething(){
        String output = "Hello World";
        System.out.println(output);
        return output;
    }
}

我让它返回输出字符串,这样我们就可以在JUnit中将它与一个简单的测试用例

进行比较
package helloasm.instrumentation_targets;

import org.junit.Test;

import static org.junit.Assert.*;

public class HelloWorldTest {
    @Test
    public void doSomething() throws Exception {
        assertEquals("Hello ASM",new HelloWorld().doSomething());
    }

}

现在显然,测试将失败,程序将固执地打印Hello World。让我们改变它。

Java字节码检测需要以.jar格式提供代理。 当我使用Intellij时,我创建了一个新模块," HelloWorldAgent",并且在 build.gradle文件中,我添加了

implementation "org.ow2.asm:asm:5.2"

作为依赖。

现在我们添加一个代理

package helloasm.agents

import java.lang.instrument.Instrumentation;

public class Agent {
    public static void premain(String args, Instrumentation instrumentation){
        HelloWorldInterceptor transformer = new HelloWorldInterceptor();
        instrumentation.addTransformer(transformer);
    }
}

和变压器

package helloasm.agents

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

public class HelloWorldInterceptor implements ClassFileTransformer{
    @Override
    public byte[] transform(
            ClassLoader loader,
            String className,
            Class<?> classBeingRedefined,
            ProtectionDomain protectionDomain,
            byte[] classfileBuffer
    ) throws IllegalClassFormatException {
        System.out.println(className + " loaded");
        return classfileBuffer;
    }
}

还没有做任何令人兴奋的事情,但我想在继续之前先让它全部运行。

我们添加到子模块build.gradle

jar {
    archiveName = "${rootProject.name}-${rootProject.version}-agent.jar"
    manifest {
        attributes(
                'Premain-Class': 'ch.ethz.koradir.helloasm.agents.Agent',
                'Can-Redefine-Classes': 'true',
                'Can-Retransform-Classes': 'true',
                'Can-Set-Native-Method-Prefix': 'true',
                'Implementation-Title': "HelloWorldInterceptor",
                'Implementation-Version': rootProject.version
        )
    }
}

现在怎样?回到根项目的build.gradle,我需要以某种方式指定

  • 它应该将子模块HelloWorldAgent编译到jar,首先是
  • 它应该运行(或者有一个选项可以运行)-javaagents标志,提供jar
  • 它应该使用检测代码
  • 执行(或具有执行选项)测试

我该怎么做?

1 个答案:

答案 0 :(得分:1)

  • settings.gradle
  • 删除HelloWorldAgent文件
  • 通过将include 'HelloWorldAgent'添加到根settings.gradle文件
  • ,使您的构建成为多项目构建
  • rootProject中的三个HelloWorldAgent/build.gradle更改为project
  • 将以下内容添加到您的根build.gradle
evaluationDependsOn 'HelloWorldAgent'

test {
    def jarTask = project('HelloWorldAgent').tasks.jar
    dependsOn jarTask
    afterEvaluate {
        jvmArgs "-javaagent:$jarTask.archivePath"
    }
}

您的测试仍然会失败,但您会看到变压器的输出。