将自定义文件夹添加到Bazel Java测试中的类路径 [英] Add custom folders to classpath in bazel java tests

查看:48
本文介绍了将自定义文件夹添加到Bazel Java测试中的类路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将大型代码库从maven迁移到bazel,我发现某些测试写入了target/classestarget/test-classes,生产代码将其读取为类路径上的资源.这是因为默认情况下,maven surefire/failsafe从模块目录运行,并将target/classestarget/test-classes添加到类路径中. 对于我来说,要迁移这么大的代码库,唯一合理的解决方案是创建target,target/classes和target/test-classes文件夹,并将最后两个文件夹添加到测试的类路径中. 关于如何实现这一目标的任何想法?

I'm trying to migrate a large codebase from maven to bazel and I've found that some of the tests write to target/classes and target/test-classes and the production code reads it as resources on the classpath. This is because maven surefire/failsafe run by default from the module directory and add target/classes and target/test-classes to the classpath. For me to migrate this large codebase the only reasonable solution is to create target, target/classes and target/test-classes folders and add the last two to the classpath of the tests.
Any ideas on how this can be achieved?

谢谢

推荐答案

另一种方法.创建一个自定义的javaagent和一个自定义的类加载器,而不是生成测试套件.使用jvm_flags进行设置和配置.

Another line of approach. Instead of generating a test suite, create a custom javaagent and a custom class loader. Use jvm_flags to setup and configure it.

javaagent有一个premain方法.这听起来像是一个自然的地方,可以处理发生在常规main方法之前的事情,即使它们与类检测,调试,coverage收集或javaagents的任何其他常规用法没有任何关系.

The javaagent has a premain method. This sounds like a natural place to do things that happen before the regular main method, even if they don't have anything to do with class instrumentation, debugging, coverage gathering, or any other usual uses of javaagents.

自定义javaagent读取系统属性extra.dirs并创建在那里指定的目录.然后它将读取属性extra.link.path并按照此处指定的方式创建符号链接,因此我可以将资源放置在测试需要它们的位置,而无需复制它们.

The custom javaagent reads system property extra.dirs and creates directories specified there. It then reads property extra.link.path and creates the symbolic links as specified there, so I can place resources where the tests expect them, without having to copy them.

需要Classloader,这样我们才能在运行时修改classpath而不会受到黑客的攻击.很大的优势是该解决方案可在Java 10上运行.

Classloader is needed so that we can amend the classpath at runtime without hacks. Great advantage is that this solution works on Java 10.

自定义类加载器读取系统属性extra.class.path并(实际上)在java.class.path中的内容之前添加它.

The custom classloader reads system property extra.class.path and (in effect) prepends it before what is in java.class.path.

以这种方式处理意味着可以使用标准的bazel规则.

Doing things this way means that standard bazel rules can be used.

runtime_classgen_dirs = ":".join([
            "target/classes",
            "target/test-classes",
])
java_test(
    ...,
    jvm_flags = [
        # agent
        "-javaagent:$(location //tools:test-agent_deploy.jar)",
        "-Dextra.dirs=" + runtime_classgen_dirs,
        # classloader
        "-Djava.system.class.loader=ResourceJavaAgent",
        "-Dextra.class.path=" + runtime_classgen_dirs,
    ],
    ,,,,
    deps = [
        # not runtime_deps, cause https://github.com/bazelbuild/bazel/issues/1566
        "//tools:test-agent_deploy.jartest-agent_deploy.jar"
    ],
    ...,
)

工具/已构建

java_binary(
    name = "test-agent",
    testonly = True,
    srcs = ["ResourceJavaAgent.java"],
    deploy_manifest_lines = ["Premain-Class: ResourceJavaAgent"],
    main_class = "ResourceJavaAgent",
    visibility = ["//visibility:public"],
)

tools/ResourceJavaAgent.java

import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

// https://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime
public class ResourceJavaAgent extends URLClassLoader {
    private final ClassLoader parent;

    public ResourceJavaAgent(ClassLoader parent) throws MalformedURLException {
        super(buildClassPath(), null);
        this.parent = parent; // I need the parent as backup for SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        System.out.println("initializing url classloader");
    }

    private static URL[] buildClassPath() throws MalformedURLException {
        final String JAVA_CLASS_PATH = "java.class.path";
        final String EXTRA_CLASS_PATH = "extra.class.path";
        List<String> paths = new LinkedList<>();
        paths.addAll(Arrays.asList(System.getProperty(EXTRA_CLASS_PATH, "").split(File.pathSeparator)));
        paths.addAll(Arrays.asList(System.getProperty(JAVA_CLASS_PATH, "").split(File.pathSeparator)));
        URL[] urls = new URL[paths.size()];
        for (int i = 0; i < paths.size(); i++) {
            urls[i] = Paths.get(paths.get(i)).toUri().toURL(); // important only for resource url, really: this url must be absolute, to pass getClass().getResource("/users.properties").toURI()) with uri that isOpaque == false.
//            System.out.println(urls[i]);
        }
        // this is for spawnVM functionality in tests
        System.setProperty(JAVA_CLASS_PATH, System.getProperty(EXTRA_CLASS_PATH, "") + File.pathSeparator + System.getProperty(JAVA_CLASS_PATH));
        return urls;
    }

    @Override
    public Class<?> loadClass(String s) throws ClassNotFoundException {
        try {
            return super.loadClass(s);
        } catch (ClassNotFoundException e) {
            return parent.loadClass(s);  // we search parent second, not first, as the default URLClassLoader would
        }
    }

    private static void createRequestedDirs() {
        for (String path : System.getProperty("extra.dirs", "").split(File.pathSeparator)) {
            new File(path).mkdirs();
        }
    }

    private static void createRequestedLinks() {
        String linkPaths = System.getProperty("extra.link.path", null);
        if (linkPaths == null) {
            return;
        }
        for (String linkPath : linkPaths.split(",")) {
            String[] fromTo = linkPath.split(":");
            Path from = Paths.get(fromTo[0]);
            Path to = Paths.get(fromTo[1]);
            try {
                Files.createSymbolicLink(from.toAbsolutePath(), to.toAbsolutePath());
            } catch (IOException e) {
                throw new IllegalArgumentException("Unable to create link " + linkPath, e);
            }
        }
    }

    public static void premain(String args, Instrumentation instrumentation) throws Exception {
        createRequestedDirs();
        createRequestedLinks();
    }
}

这篇关于将自定义文件夹添加到Bazel Java测试中的类路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆