java - 使用类路径在运行时编译类

标签 java compilation classpath runtime-compilation

我目前正在尝试在运行时编译一个类,但由于某种原因它只能在一个系统上运行。两个系统都使用完全相同的代码并安装了相同的 java 版本,但在一个系统上,我的 .java 文件编译为 .class,而在另一个系统上,我遇到了异常,因为某些类是或应该是在找不到类路径。

我用来编译它的代码是这样的:

private static File compile(File file) {
    try {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        List<String> optionList = new ArrayList<>();
        File jar = getJar(RuntimeCompiler.class);
        File pluginDirectory = new File(jar.getAbsolutePath().substring(0, jar.getAbsolutePath().length() - jar.getName().length()));
        String classes = buildClassPath(getJar(Bukkit.class).getAbsolutePath(), pluginDirectory.getAbsolutePath() + "/*");
        optionList.addAll(Arrays.asList("-classpath",classes));
        boolean success;
        try (StandardJavaFileManager fileManager = compiler.getStandardFileManager( null, null, null )) {
            Iterable<? extends JavaFileObject> units;
            units = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(file));
            JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, optionList, null, units);
            success = task.call();
        }
        if(success) {
            return new File(file.getAbsolutePath().substring(0, file.getAbsolutePath().length() - 5) + ".class");
        }
        else {
            return null;
        }
    } catch (IOException ex) {
        Logger.getLogger(RuntimeCompiler.class.getName()).log(Level.SEVERE, null, ex);
        return null;
    }
}
private static String buildClassPath(String... paths) {
    StringBuilder sb = new StringBuilder();
    for (String path : paths) {
        if (path.endsWith("*")) {
            path = path.substring(0, path.length() - 1);
            File pathFile = new File(path);
            for (File file : pathFile.listFiles()) {
                if (file.isFile() && file.getName().endsWith(".jar")) {
                    sb.append(path);
                    sb.append(file.getName());
                    sb.append(System.getProperty("path.separator"));
                }
            }
        } else {
            sb.append(path);
            sb.append(System.getProperty("path.separator"));
        }
    }
    String s = sb.toString();
    s = s.substring(0,s.length() - 1);
    return s;
}

类路径(optionList.toString()),其中 Core-1.0-SNAPSHOT.jar 包含必要的文件。

[-classpath, /usr/local/gpx/users/user/127.0.0.1:25702/spigot.jar:25702/plugins/Core-1.0-SNAPSHOT.jar]

堆栈跟踪

/usr/local/gpx/users/user/127.0.0.1:25702/plugins/debug/DebugClass.java:1: error: package     net.nowcraft.core does not exist
[14:04:10] [Server thread/WARN]: import net.nowcraft.core.core;
[14:04:10] [Server thread/WARN]:                         ^
[14:04:10] [Server thread/WARN]:     /usr/local/gpx/users/user/127.0.0.1:25702/plugins/debug/DebugClass.java:2: error: package net.nowcraft.core.RuntimeCompiler does not exist
[14:04:10] [Server thread/WARN]: import net.nowcraft.core.RuntimeCompiler.Debugger
[14:04:10] [Server thread/WARN]:                                         
[14:04:10] [Server thread/WARN]: /usr/local/gpx/users/user/127.0.0.1:25702/plugins/debug/DebugClass.java:7: error: method does not override or implement a method from a supertype
[14:04:10] [Server thread/WARN]:     @Override
[14:04:10] [Server thread/WARN]:     ^
[14:04:10] [Server thread/WARN]: 3 errors
[14:04:10] [Server thread/ERROR]: null
org.bukkit.command.CommandException: Unhandled exception executing command 'rd' in plugin NowCraftCore v1.0
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:46) ~[spigot.jar:git-Spigot-330d66b-fe41b01]
at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:141) ~[spigot.jar:git-Spigot-330d66b-fe41b01]
at org.bukkit.craftbukkit.v1_8_R1.CraftServer.dispatchCommand(CraftServer.java:642) ~[spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.PlayerConnection.handleCommand(PlayerConnection.java:1115) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.PlayerConnection.a(PlayerConnection.java:950) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.PacketPlayInChat.a(PacketPlayInChat.java:26) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.PacketPlayInChat.a(PacketPlayInChat.java:53) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.PacketHandleTask.run(SourceFile:13) [spigot.jar:git-Spigot-330d66b-fe41b01]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_25]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_25]
at net.minecraft.server.v1_8_R1.MinecraftServer.z(MinecraftServer.java:683) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.DedicatedServer.z(DedicatedServer.java:316) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.MinecraftServer.y(MinecraftServer.java:623) [spigot.jar:git-Spigot-330d66b-fe41b01]
at net.minecraft.server.v1_8_R1.MinecraftServer.run(MinecraftServer.java:526) [spigot.jar:git-Spigot-330d66b-fe41b01]
at java.lang.Thread.run(Thread.java:745) [?:1.8.0_25]

Caused by: java.lang.NullPointerException
at net.nowcraft.core.RuntimeCompiler.RuntimeCompiler.load(RuntimeCompiler.java:127) ~[?:?]
at net.nowcraft.core.RuntimeCompiler.RuntimeCompiler.loadHastebin(RuntimeCompiler.java:88) ~[?:?]
at net.nowcraft.core.RuntimeCompiler.RuntimeCompiler.debugFromHastebin(RuntimeCompiler.java:170) ~[?:?]
at net.nowcraft.core.commands.RuntimeDebug.onCommand(RuntimeDebug.java:35) ~[?:?]
at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[spigot.jar:git-Spigot-330d66b-fe41b01]
... 14 more

编辑: 我也在其他目录下测试过,没有成功。当我在本地 Windows 笔记本电脑上使用该代码时,它确实可以工作。

编辑2: 这似乎是 CentOS 的问题,因为它也无法在第二个 CentOS 系统上运行。

最佳答案

Stacktrace 以及周围的环境都提供了巨大的帮助。

这里的问题是Linux。所以您尝试访问的目录:

/usr/local/gpx/users/user/127.0.0.1:25702/plugins/debug/DebugClass.java

实际上是在寻找名为 127.0.0.1:25702 的目录(尽管 : 必须转义,因为大多数 shell 使用 :作为语法字符。

您的网络地址将映射到特定目录。您必须创建一个网络共享(不熟悉 CentOS,但通常非常简单)以消除对物理存在位置的网络引用。如果您可以从命令行cd到它,那么您的java运行时应该能够实际引用它。

Here is a reference用于在 CentOS 上安装远程文件系统。是的,我知道 127.0.0.1:25702 从技术上讲是“localhost”,但是命令 shell 和 java 运行时都不会知道如何按照您想要的方式解析网络地址。它适用于 Windows,因为 Windows API 更宽容。

关于java - 使用类路径在运行时编译类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27608408/

相关文章:

java - JAVA中的流结束

C++ 自定义编译时检查

java - eclipse从哪个目录启动jvm?

java - Apache Camel 找不到 servlet 类

java - 找出 pentaho kettle 中的一个步骤需要哪些 Jars

Java:是否有使用静态易失变量的正确方法?

java - Portletfilter 更改区域设置?

java - 通过socket java向多个客户端广播对象

python - 如何使用 setuptools 创建 exe 启动器

C++ 只打印一条语句