【问题标题】:Getting the .class filename and path after Executing ProcessBuilder in java在java中执行ProcessBuilder后获取.class文件名和路径
【发布时间】:2021-12-12 14:50:04
【问题描述】:

我创建了一个 API,它将获取有效负载 (code, language),它会生成一个随机名称来从 java 中保存文件(基于将选择扩展名的语言)。来自 UI 的有效负载在下方

{"code": "class Demo{  \r\n" + 
            "    public static void main(String args[]){  \r\n" + 
            "     System.out.println(\"Hello Java\");  \r\n" + 
            "    }  \r\n" + 
            "}",
"language": "java"}

文件保存按预期正常工作,我可以将文件保存在 C:\\temp 下,但是当我尝试使用 @ 执行这些保存的文件时出现问题987654324@在java中,得到Error: Could not find or load main class BNkHZk(因为执行下面代码后随机生成的文件名与class文件不同)。

ProcessBuilder processBuilder = new ProcessBuilder(new String[] { "javac", fullPath });
//fullPath is C:\temp\BNkHZk.java
Process process = processBuilder.start();

上述执行将创建一个新的 Demo.class 文件。 执行上述命令后,我正在执行以下代码

processBuilder = new ProcessBuilder(new String[] { "javac", fullPath });
Process process = processBuilder.start();
            if (process.getErrorStream().read() != -1) {
                System.out.println("Compilation Errors" + process.getErrorStream());
            }
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line = "";

            while ((line = reader.readLine()) != null) {
                System.out.Println(line);
            }

我收到以下错误 Error: Could not find or load main class BNkHZk

完整功能如下

    private void ExecuteCode(String language, String code){
    String tempFileName = RandomStringUtils.randomAlphanumeric(6);
    // Code to creating the file and save under "C:\\temp\\"
    String fullPath = "C:\\temp\\" + tempFileName+ "." + language;
    try {
            fileWriter = new FileWriter(new File(fullPath));
            fileWriter.write(code);
        } catch (Exception e) {
            System.out.println("IO Exception while creating new file");
            e.printStackTrace();
        } finally {
            if (fileWriter != null) {
                fileWriter.close();
            }
        }
    
    ProcessBuilder processBuilder = new ProcessBuilder(new String[] { "javac", fullPath });
                ////fullPath is C:\temp\BNkHZk.java
            Process process = processBuilder.start();

            if (process.getErrorStream().read() != -1) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                String line = "";

                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            }
 //the above execution will create a new **Demo.class** file.
            int exitcode = -1;
            try {
                exitcode = process.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (exitcode == 0) {
                (
                    processBuilder  = new ProcessBuilder(new String[] { "java","tempFileName"});//BNkHZk
                        Process process1 = processBuilder.start();
                        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                        String line = "";

                        while ((line = reader.readLine()) != null) {
                            System.out.println(line + "\n");
                        }
            }
            
}

我收到 error Error: Could not find or load main class BNkHZk

注意:由于我将以字符串形式获取文件的内容,因此我无法将 filename 保存为与 .class 名称相同的名称。是否有任何解决方法可以将文件名保存为与 .class 名称相同的文件名? 任何解决方法建议都表示赞赏和欢迎。提前致谢。

【问题讨论】:

    标签: java file-io processbuilder


    【解决方案1】:

    有两个不同的问题。第一个是类文件的命名。类的名称必须与文件的名称匹配。 class Demo -> Demo.java,这就是你不能使用随机文件名的原因。编译运行良好,但运行不正常。

    第二个是ProcessBuilder的处理。我建议以下模式:

    ProcessBuilder pb = new ProcessBuilder("javac.exe", "Demo.java");
    pb.directory(Paths.get("path_to_java_file").toFile());
    pb.redirectErrorStream(true);
    Process process = pb.start();
    try (var infoStream = process.getInputStream()) {
        infoStream.transferTo(System.out);
    }
    status = process.waitFor();
    

    现在你可以看到,如果编译运行正确。

    使用相同的模式来运行你编译的类。

    【讨论】:

    • 嗨@Thilo Schwarz,感谢您的快速回复。由于我将以字符串形式获取文件的内容,因此我无法将文件名保存为与 .class 名称相同的名称。
    • @UllasSharma 您将源代码作为文本获取,对吧?您应该将此代码保存为 java 文件 Demo.java。下一步就是用javac编译了,对吧?编译的结果是文件Demo.class。还是我误解了工作流程?
    • @Ullas Sharma 只需在生成步骤中创建一个临时目录,然后在其中创建预期的 Java 文件 - Path fullPath = Path.of("C:/temp", tmpFileName, "Demo." + language);,然后上述内容将在临时目录内部工作
    • @ Thilo Schwarz,我只获得文本形式的内容,但我不会获得文件的确切类名,因为它是动态内容。例如,如果您看到 JSON req,我将从 {"code": "class Sample{ public static void main(String args[]){ System.out.println(\"Hello Java\"); }", "language": "java"} 这样的 UI 获取,根据您的理解,文件名应该是 Sample.java,但我无法获取 className 本身,因为从动态文本内容。
    • @DuncG 我正在寻找使用 processbuilder 的解决方法来获取新创建的 .class 名称,内容仅为文本,但我不会获得文件的确切类名,因为它是动态内容。例如,如果您看到 JSON req,我将从 UI 中获得 {"code": "class Sample{ public static void main(String args[]){ System.out.println(\"Hello Java\"); }", "language": "java"},根据您的理解,文件名应该是 Sample.java,但我无法获取 className 本身,因为它具有挑战性动态文本内容。
    【解决方案2】:

    您可以通过两种方式处理。

    如果您有最新版本的 Java,您只需跳过“javac”步骤并启动“java”,即使类名与.java 文件。

    因此,如果您临时生成的 java 文件是 "\\Temp\\XYZ.java",那么它将编译并运行,打印 Hello Java

    Path fullPath = Path.of("/Temp", "XYZ.java").toAbsolutePath();  // wherever
    var pb = new ProcessBuilder(new String[] { "java", fullPath.toString() });
    ... 
    

    如果您没有最新版本的 Java,请保留“javac”步骤,但将文件写入新创建的目录,该目录仅用于生成的单个 Java 文件:

    Path fullPath = Path.of("/Temp", "wherever", "ANYNAME.java").toAbsolutePath();
    var pb = new ProcessBuilder(new String[] { "javac", fullPath });
    ...
    

    然后查看fullPath.getParent() 的内容并找到扩展名为.class 的文件,从文件扩展名之前的文件名中推断出类名,并使用带有派生类名和类路径目录集的“java”步骤到fullPath.getParent()。例如,第二阶段看起来像:

    String fn = Files.list(fullPath.getParent()).map(Path::getFileName).map(Path::toString).filter(s -> s.endsWith(".class")).findFirst().get();
    String className = fn.substring(0, fn.lastIndexOf('.'));
    var pb = new ProcessBuilder(new String[] { "java", "-cp", fullPath.getParent().toString(), className });
    ...
    

    【讨论】:

    • 感谢您的解决方法...它暂时解决了我的问题。
    最近更新 更多