mindok

mindok

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 The Erlangelist - 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: [erlang-questions] How do you kill an OS process that was opened with open_port on a brutal_kill?, 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:

  1. has anyone managed to get a well-behaved interop between Java and Elixir using Ports.
  2. 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
  3. 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!

Showing Posts 1 to 10

sasajuric

sasajuric

Author of Elixir In Action

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 :slight_smile:

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.

NobbZ

NobbZ

Besides of the already mentioned erlport there are a couple of other projects available, like porcelain which relies on a wrapper written in Golang, which has to be installed seperately, or rambo which explicitely not only manages the lifecycle of the program, but also is able to wrap those programs that do only print their own reply after stdin got closed. Its wrapper is written in Rust, and as far as I remember you need to have rust installed to compile the library.

mindok

mindok OP

Thanks - I’m watching for EOF in my code and that doesn’t appear to be triggering. I’ll double-check and go to ErlPort if I can’t get it working.

mindok

mindok OP

Thanks @NobbZ - I have no problem receiving the inputs from the external process, it’s just shutting it down cleanly afterwards that’s the problem.

I’ll double-check receives of EOF first, then I’ll try on Linux to see if there is some weird Windows effect, then I’ll see if ErlPorts does a more aggressive shutdown, then I’ll take a look at the other options you presented.

Thanks to you both for your suggestions. I’ll let you know how I go.

mindok

mindok OP

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

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.

jayjun

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.

mindok

mindok OP

Thanks @jayjun - yes, the java process will be long lived. I’ve worked up a more extended example that uses Erlang ETF to transmit the payload back and forth. The Ericsson JInterface library has all the parsing and packaging routines. There are a couple of gotchas that I’ll write up (eg the OtpOutputStream doesn’t write the first byte - 131 - for some reason so you have to do that by hand), but it seems pretty straightforward otherwise. The Java side can respond synchronously to each call in my case which simplifies things on that side.

jayjun

jayjun

Cool, just be careful of the classic pipe buffer deadlock if you’re not continuously reading while writing. And you cannot terminate early if the port is closed or if the Erlang node dies.

mindok

mindok OP

Thanks @jayjun - that sounds like some hard-won experience right there!

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
subsaharancoder
I’ve followed the Phoenix LiveView file upload code here Uploads — Phoenix LiveView v1.0.0-rc.7 and so far everything works just fine wit...
New
mohsen
I’m using an Umbrella project for a Phoenix application, and I want to have one Ecto Repo and one PostgreSQL database shared by all apps....
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews