-1

I have this code:

try {
    // Create a temporary file
    Path tmpFilePath = Files.createTempFile("tmp-errors", ".log");

    // Create a ProcessBuilder and redirect the error stream to the temporary file
    ProcessBuilder pb = new ProcessBuilder("my-command")
            .redirectError(Redirect.appendTo(tmpFilePath.toFile()));

    // Start the process
    Process process = pb.start();

    // log the erros into this thread
    Files.readAllLines(tmpFilePath,StandardCharsets.ISO_8859_1).forEach(LOGGER::error);


    // Delete the temporary file after the process completes
    Files.delete(tmpFilePath);

} catch (IOException | InterruptedException e) {
    // Handle the exception
    e.printStackTrace();
}

I want to execute an external process using Process Builder, collect the errors coming from the external process using redirectError() method, collect these errors from the temporary file and log them in the current thread and finaly delete the temporary file.
But I keep getting this error:

The process cannot access the file because it is being used by another process

I think the file is still locked by the Process Builder, but I couldn't find how to release it!

SlimenTN
  • 3,383
  • 7
  • 31
  • 78
  • You need to wait for the process to finish, before it makes any sense to read its output. – Andreas is moving to Codidact May 18 '23 at 08:59
  • how to know it has finished or crashed? – SlimenTN May 18 '23 at 09:00
  • [`waitFor`](https://docs.oracle.com/javase/8/docs/api/java/lang/Process.html#waitFor--) – Sweeper May 18 '23 at 09:00
  • `pb.start()` starts the process asynchronously. Check the documentation for a function with a proper name. – Andreas is moving to Codidact May 18 '23 at 09:00
  • 1
    Does this answer your question? [ProcessBuilder and Process.waitFor(), how long does it wait?](https://stackoverflow.com/questions/29746304/processbuilder-and-process-waitfor-how-long-does-it-wait) – Adnan Isajbegovic May 18 '23 at 09:03
  • @Sweeper: `waitFor` is dangerous if the output and error buffers aren't consumed. https://www.infoworld.com/article/2071275/when-runtime-exec---won-t.html is old but still relevant. The only improvements are using `ProcessBuilder` in combination with `redirectErrorStream(true)`, that way only a single thread (possibly the current one) is needed to read from the output stream. – Rob Spoor May 18 '23 at 09:10
  • thanks all fot the answers but nothing worked for me, using process.waitFor() make the process stuck in an inifinte loop and doesn't finish... – SlimenTN May 18 '23 at 09:57
  • @SlimenTN did you read all content from the streams like the article I linked to suggested? Or otherwise using ` ProcessBuilder`? The answer given by DuncG shows an example of the latter. – Rob Spoor May 18 '23 at 14:17

2 Answers2

0

You invoke the ProcessBuilder.start() method. The method returns a Process instance, and in it's documentation you can read

By default, the created process does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). The parent process uses these streams to feed input to and get output from the process. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the process may cause the process to block, or even deadlock.

So it is likely your process is started by the OS but gets blocked due to I/O restrictions. Get around that by reading the STDOUT and STDERR streams until your process finishes. Or redirect the streams, like so:

ProcessBuilder pb = new ProcessBuilder("cmd.exe");
pb.redirectOutput(Redirect.appendTo(tmpFilePath.toFile()));
pb.redirectError(Redirect.appendTo(tmpFilePath.toFile()));
Process p = pb.start();
p.waitFor();
System.out.println("process exited with " + p.exitValue());

Once the process terminated you should be able to normally access or delete the temporary files.

Queeg
  • 7,748
  • 1
  • 16
  • 42
  • the file that I want to delete actually contain the errors from the external process (it's a C++ process), I'm creating this file by telling process builder to redirect the errors to it like this `.redirectError(Redirect.appendTo(tmpFilePath.toFile()))` if I do like you mentioned `.redirectError(ProcessBuilder.Redirect.INHERIT)` then no file will be created, hence no file to delete! – SlimenTN May 18 '23 at 11:03
  • I updated my example. But why do you write the logfile at all if you want to delete it immediately? – Queeg May 18 '23 at 11:04
  • no immidiately, I get the errors from it and log it in the current thread `Files.readAllLines(tmpFilePath,StandardCharsets.ISO_8859_1).forEach(LOGGER::error);`, however the code suggessted I've already tried it and the process get stuck in an infinite loop when calling `p.waitFor()` – SlimenTN May 18 '23 at 11:16
  • I doubt you are running an infite loop. What is the exact command you are launching? Are you sure that one does not wait for something, and that's why p.waitFor() cannot terminate? – Queeg May 18 '23 at 13:53
0

As mentioned in the comments + other answers your sub-process will be writing to the temp file until it exits, so you must call waitFor() to await process exit before attempting the delete.

If waitFor() does not return or the sub-process appears to freeze, then the likely follow-on issue is that stdout buffer is full. To ensure correct termination, add stdout consumer to read process.getInputStream() with wait for process, before reading+deleting the temp file:

// Create a temporary file
Path tmpFilePath = Files.createTempFile("tmp-errors", ".log");

// Create a ProcessBuilder and redirect the error stream to the temporary file
ProcessBuilder pb = new ProcessBuilder("my-command")
        .redirectError(Redirect.appendTo(tmpFilePath.toFile()));

// Start the process
Process process = pb.start();

// Consume STDOUT with another file, memory or:
process.getInputStream().transferTo(System.out);

// Wait for exit:
int rc = process.waitFor();

// log the erros into this thread
Files.readAllLines(tmpFilePath,StandardCharsets.ISO_8859_1).forEach(LOGGER::error);

// Delete the temporary file after the process completes
Files.delete(tmpFilePath);

Another cause of apparent freezes is when a sub-process makes use of console input. If this is the case you should also signal end of input after start() and before reading STDOUT:

try(OutputStream os = p.getOutputStream()) { 
    // empty
}

Given that you create the temp file each time, you can remove the use of append mode, replacing redirectError as .redirectError(tmpFilePath.toFile()).

DuncG
  • 12,137
  • 2
  • 21
  • 33