We’ve built a middleware solution for the non-profit world (mostly public-health data integration projects) on Phoenix/Elixir that executes “jobs” for our users based on specific events. As it currently stands, these jobs are executed in a Node.js VM—the essential job running code in Elixir is a System.cmd/3
call which executes a Node.js program with some arguments. It looks like this:
def perform(%Job{success: nil} = job) do
arguments = [
"core",
"execute",
"-e", job.expression_path,
"-l", "#{Application.get_env(:the_app, :language_packs_path)}/" <> job.language <> ".Adaptor",
"-s", job.state_path |
(if job.final_state_path, do: ["-o", job.final_state_path], else: [])
]
env = [
{"NODE_PATH", "./node_modules"},
{"NODE_ENV", to_string(Mix.env)},
{"PATH", "./node_modules/.bin:" <> System.get_env("PATH")}
]
Logger.debug [
"Executing with:\n",
"Environment: \n",
Enum.map(env, fn {k,v} -> " #{k}: #{v}\n" end),
"Command: \n ",
Enum.join(arguments, " ")
]
result = System.cmd("env", arguments, [ env: env, stderr_to_stdout: true ])
case result do
{stdout, 0} ->
%{ job | log: String.split(stdout, ~r{\n}), success: true, exit_code: 0 }
{stdout, exit_code} ->
%{ job | log: String.split(stdout, ~r{\n}), success: false, exit_code: exit_code }
end
end
It works very well, but I’m now trying to extend the functionality of our app so that we can run either that Node.js program (essentially System.cmd("node program.js execute -l ./lib/Adaptor -s ../tmp/state.json -o ../tmp/output.json -e ../tmp/expression.js")
) or run a Selenium & Sikuli (OpenCV-based) webdriver and image recognition tool for automating the use of webapps that lack an accessible API, don’t have accessible HTML elements since they live inside canvases, and are otherwise impossible to automate.
Using essentially the same structure, I’d like to run that .jar
with java rather than the .js
file with Node. It fails with the following error:
iex(1)> System.cmd("/lib/runsikulix -r /lib/SAP_VMware.sikuli --args /lib/SAP_VMware.sikuli/tmp/state2.json", [])
** (ErlangError) Erlang error: :enoent
:erlang.open_port({:spawn_executable, '/lib/runsikulix -r /lib/SAP_VMware.sikuli --args /lib/SAP_VMware.sikuli/tmp/state2.json'}, [:use_stdio, :exit_status, :binary, :hide, {:args, []}])
(elixir) lib/system.ex:611: System.cmd/3
The desired outcome is that the .jar
runs in a new process and reports back on its sucess or failure once it’s done, the same way our current System.cmd(node app.js)
setup works. Does anyone know how to acheive this? Or:
- Should an Elixir app be able to spawn and run a java executable program, in theory? Is this a job that should be handled by jInterface?
(http://erlang.org/doc/apps/jinterface/jinterface_users_guide.html) - What if that java program, in turn, tried to launch a headless browser on the server?
-
:enoent
usually means Erlang can’t find the file. I’ve tried playing around with the paths, and all but my current setup yield a simple:enoent
. The current setup yields this:spawn_executable
error and makes me think it is actually trying to run the jar file. What does:spawn_executable
mean?
Thanks in advance for considering this.