Search code examples
javaprocessbuilder

ProcessBuilder - A potential bug


I am having a strange issue when trying to execute a block of code (more particularly the ProcessBuilder class in Java)

Code that works:

package modules.user.verify;

import java.io.*;
import java.util.*;
import java.net.*;

public class VerifyUser {
public static void main(String[] args) {
    boolean listening = true;

    try {
        ServerSocket server = new ServerSocket(20002);

        while(listening) {
            Socket client = server.accept();

            PrintWriter out = new PrintWriter(client.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));

            String input, store = "", request = "";

            // For all input received, write it to the request buffer.
            while((input = in.readLine()) != null) {
                request += input;
            }   // end while loop

            BufferedReader reader = new BufferedReader(new FileReader("store/address-book/address-book.xml"));

            while((input = reader.readLine()) != null) {
                store += input;
            }   // end while loop

            String acl2 = "(include-book \"modules/user/verify/verify-user\")" + 
                              "(in-package \"ACL2\")" +
                         "(set-state-ok t)" +
                              "(set-guard-checking :none)" + 
                          "(testUser \"" + request + "\" \"" + store + "\" state)";

            System.out.println("Executing ACL2 runtime...");
            ProcessBuilder processBuilder = new ProcessBuilder("acl2");
            File log = new File("logs/user/verify/acl2_log.txt");
            processBuilder.redirectErrorStream(true);
            processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
            Process process = processBuilder.start();
            PrintWriter procIn = new PrintWriter(process.getOutputStream());

            // Write the ACL2 to the process, exit ACL2 and close the socket
            procIn.println(acl2);
            procIn.println("(good-bye)");
            procIn.flush();
            procIn.close();
            out.close();
            in.close();
            client.close();
        }   // end while loop

        server.close();
        System.exit(0);
    } catch(Exception e) {
        e.printStackTrace();
    }   // end try/catch
}   // end function main
}   // end class VerifyUser

Code that doesn't work:

package modules.user.register;

import java.io.*;
import java.util.*;
import java.net.*;

public class RegisterUser {
public static void main(String[] args) {
    boolean listening = true;

    try {
        // Acquire the listening port for connection to client.
        ServerSocket server = new ServerSocket(20001);

        while(listening) {
            // Wait until the client connects
            Socket client = server.accept();

            // Handles for input and output streams relating to the socket connection
            PrintWriter out = new PrintWriter(client.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));

            // Buffers
            String input, store="", request="";

            // Read the input from the connection
            while((input = in.readLine()) != null) {
                request += input;
            }   // end while

            // Read the contents of the address-book currently stored
            BufferedReader reader = new BufferedReader(new FileReader("store/address-book/address-book.xml"));
            while((input = reader.readLine()) != null) {
                store += input;
            }   // end while

            // The ACL2 code to execute.
            String acl2 = "(include-book \"register-user\")" +
                          "(in-package \"ACL2\")"            +
                          "(registerUser \"" + request + "\" \"" + store + "\" state)";

            // Initialize ACL2 and dump its output to the log
            System.out.println("Executing ACL2 runtime for RegisterUser...");
            ProcessBuilder processBuilder = new ProcessBuilder("acl2");
            File log = new File("logs/user/register/acl2_log.txt");
            processBuilder.redirectErrorStream(true);
            processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(log));

            Process process = processBuilder.start();
            PrintWriter procIn = new PrintWriter(process.getOutputStream());

            // Write the ACL2 to the process, close ACL2
            //procIn.println(acl2);
            //procIn.println("(good-bye)");
            //procIn.flush();
            //procIn.close();

            // Old store is old address-book file and new store is newly generated
            File oldStore = new File("store/address-book/address-book.xml");
            File newStore = new File("store/address-book/temp_address-book.xml");

            // Response header information
            String response = "<?xml version='1.0'?>"                        + 
                              "<!DOCTYPE response SYSTEM 'dtd/reponse.dtd'>" +
                              "<response>";

            // Determine if there was a change.
            // If entry was added, the length > that old length.
            if(oldStore.length() < newStore.length()) {
                // Replace old file with new file
                oldStore.delete();
                newStore.renameTo(oldStore);

                // Extract data from request XML
                String name   = request.substring(request.indexOf("<name>")+6, request.indexOf("</name>")-7);
                String domain = request.substring(request.indexOf("<domain>")+8, request.indexOf("</domain>")-9);

                // Create the store directory for the user's emails
                File storeDirectory = new File("store/email/" + domain + "/" + name + "/");
                storeDirectory.mkdirs();

                response += "<message>ACCEPT</message>";
            } else {
                // Remove new file as it is pointless
                newStore.delete();
                response += "<message>REJECT</message>";
            }   // end if-else

            response += "</response>";

            // Writeback the response to the client
            out.print(response);
            out.flush();

            // Close all streams
            out.close();
            in.close();
            client.close();
        }   // end while
    } catch(Exception e) {
        e.printStackTrace();
    }   // end try/catch
}   // end function main
}   // end class RegisterUser

As you can see, I am not passing any arguments the the program. when I echo %PATH% on my windows DOS shell, it shows that C:\ACL2 is in my ENV vars (which is the folder acl2.exe is located). I've tried changing the acl2 to C:\ACL2\acl2.exe only to have the same results.

What is boggling me is why the first one works perfect and the second one (with almost the same exact code - same exact ProcessBuilder code) does not work.

It appears this block of code is where my problem is:

        System.out.println("Executing ACL2 runtime...");
        ProcessBuilder processBuilder = new ProcessBuilder("acl2");
        File log = new File("logs/user/register/acl2_log.txt");
        processBuilder.redirectErrorStream(true);
        processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
        Process process = processBuilder.start();
        PrintWriter procIn = new PrintWriter(process.getOutputStream());

The error:

Executing ACL2 runtime for RegisterUser...
java.io.IOException: Cannot run program "acl2": The system cannot find the path specified
        at java.lang.ProcessBuilder.start(Unknown Source)
    at modules.user.register.RegisterUser.main(RegisterUser.java:74)
Caused by: java.io.IOException: The system cannot find the path specified
    at java.lang.ProcessImpl.openForAtomicAppend(Native Method)
    at java.lang.ProcessImpl.newFileOutputStream(Unknown Source)
    at java.lang.ProcessImpl.start(Unknown Source)
    ... 2 more

What is supposed to happen, is that the Process that gets started is the ACL2 environment, code is sent to it which is executed and then the process is killed (via the (good-bye) command in ACL2). After that the code should exit after a little bit of Java, which is not related to the ACL2 process where the error occurs.

The VerifyUser program invokes ACL2, writes a response to a "server-response.xml" file and exits gracefully without incident.

The RegisterUser program should invoke ACL2, write a response and exit gracefully, and a little java code creates a directory for a user and deletes a store file and renames the newly generated one for user registration.


Solution

  • Since the exception is thrown at

    at java.lang.ProcessImpl.openForAtomicAppend(Native Method)
    

    AND you use two different paths:

    File log = new File("logs/user/verify/acl2_log.txt");
    File log = new File("logs/user/register/acl2_log.txt");
    

    it might be possible, that the acl command is found but that the redirection cannot be performed. In this case the error message is not quite helpful but - if you read between the lines - also not wrong.