mindok
Killing java processes started from Port.open
Hi all, my current work involves interacting with MS Project files. The binary file format is pretty tedious to interact with. The best third party library I have found for reading the files is written in Java.
I have written a small test Java project that I have interoperating with a test Elixir project following guidance from this post https://www.theerlangelist.com/article/outside_elixir from @sasajuric.
The only problem is that the java program doesn’t die when the owning BEAM process dies or BEAM is terminated. This is discussed here: http://erlang.org/pipermail/erlang-questions/2016-November/090910.html, and it sounds like it all relies on the program invoked by Port.open being “well-behaved” and shutting down if the stdin channel is killed.
I have trawled the Java docs and aside from realising how convoluted the process is for writing anything useful in Java (compared with Elixir), I haven’t found anything to help.
So the questions are:
- has anyone managed to get a well-behaved interop between Java and Elixir using Ports.
- If so, what does your main System.in reading code look like / how did you solve the problem of shutting down the java program without explicit termination commands from the Elixir side
- If not, what are the preferred options for Java interop. I am aware of JInterface but wanted to keep operational complexity down as far as possible and also minimise the amount of Java code.
I know this is probably a Java problem rather than an Elixir problem, but the Ports style interop for long-running processes through stdin/stdout does seem to be unique to Erlang/Elixir.
Edit: Only tries on Windows so far. Will year on Linux tomorrow.
Thanks!
Marked As Solved
sasajuric
Yes, if you want proper cleanup, the external program has to be well-behaved. I’ve briefly touched on that in the article, in the “program termination” section. So basically, you need to make the Java program terminate if you receive EOF on stdin. No idea how this can be done in Java, but I’d be surprised if this wasn’t possible ![]()
If you don’t control the external program, you could use erlport, which will automatically terminate the external program if the owner beam process (or the entire beam node) stops.
Also Liked
jayjun
Creator of Rambo here. Don’t use libraries if you’re writing your own port. Libraries are for working around external programs that cannot be changed.
Your minimal program is a good start, just need to read (block on) standard input on another thread so your MS Project job can run.
If you don’t care about JVM startup time, just run your port with System.cmd on demand. Otherwise if your Java process is long-lived, you’ll need to consider the communication protocol between your Java and Erlang processes.
mindok
OK, problem exists between chair & keyboard. My main loop was badly behaved. I stripped it back to the most basic stdin read loop and it terminates ok. Time to start building it up again and see where it fails.
For reference, here’s a minimal Java program that works nicely over ports, terminating when the process or BEAM terminates (including brutally killing from Windows task manager):
package com.thing;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
int ch;
while(true) {
ch = System.in.read();
if (ch == -1) {break;} // This is the EOF signal in Java
if (ch == 'q') {break;}
if (ch == 't') {System.out.println("tasks");}
System.out.println(" got " + ch + " - " + (char)ch);
}
}
}
jayjun
Thanks for the mention. To clarify, Rambo only requires the Rust compiler if you are not using Linux, macOS or Windows. Bundled binaries should cover most users.







