Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I run NodeJS from a Java application?

Tags:

I'm writing a Java library, actually, a Clojure library, but for this question, what matters is that it runs on the JVM. This library needs to execute some JavaScript. I tried with Nashorn but I encounter some limitations that might be too hard to overcome. As an alternative, I want to try NodeJS.

I want my library to be self contained, to not depend on the system running NodeJS independently and thus requiring a particular deployment mechanism to place the Java and NodeJS artifacts in the right places to be picked up by the two different network servers. This approach, though, brings some issues.

I will be talking to NodeJS over HTTP but I don't want NodeJS to open a specific port. I want to find a random unused one so there's no collisions. I also want to control where the logs from NodeJS go, as to keep them with the rest of my application. Lastly, my app should be able to detect when NodeJS crashed and re-run it or report an error with information.

What's the best way to approach this? Are there any Java libraries to help manage child process in this fashion? Anything in particular I should do from the NodeJS side (I'm very new to NodeJS, I never used it before).

like image 243
pupeno Avatar asked Sep 16 '15 11:09

pupeno


People also ask

Can I use node js with Java?

You can intergrate NodeJS with Java using node-java.

How do you call a node JS API in Java?

JS app running on an available port and the Java app can communicate with it via tcp sockets. Or you can create an http server and expose a rest service which your Java app can consume. Or as md_5 says, you can use Runtime. exec and then call getInputStream on the resulting process.

Do we need Java to run node JS?

This requires to install npm to run Node. js. It requires JRE to run Java. It is used for small projects and server-side interactions.


2 Answers

My solution in the end was to use ProcessBuilder like this:

(defn create-process-builder [js-engine]
  (doto (ProcessBuilder. ["node" (:path js-engine)
                          "--port-file" (:port-file js-engine)
                          "--default-ajax-host" (:default-ajax-host js-engine)
                          "--default-ajax-port" (str (:default-ajax-port js-engine))])
    .inheritIO))

and then call start in it. inheritIO causes the output of it to go to the output of the current process which effectively merges stdout and stderr.

On top of that NodeJS opens a random port by specifying 0 as the port number and writes it to a file:

(let [app (-> (express)
              (.use (cookie-parser))
              (.get "/" (fn [_req res] (.send res "Universal JavaScript engine for server side pre-rendering single page applications.")))
              (.get "/render" render))
      server (.createServer http app)]
  (.listen server 0 (fn [] (.writeFile fs (:port-file options) (.-port (.address server)))))))))

which then is opened by the Java side (waiting for it to appear):

(defn get-port-number [js-engine]
  (or (with-timeout
        (:start-timeout js-engine)
        (loop [port-number (read-port-file js-engine)]
          (if (string/blank? port-number)
            (if (is-running? js-engine)
              (do (Thread/sleep 100)
                  (recur (read-port-file js-engine)))
              (throw (Exception. (str "While waiting for port number, process died: " (:path js-engine)))))
            port-number)))
      (throw (Exception. (str "Waited for " (:start-timeout js-engine) " for " (:path js-engine) " to start and report its port number but it timed out.")))))
like image 185
pupeno Avatar answered Sep 19 '22 18:09

pupeno


There is a pretty good answer here on how to run javascript in java. Would something like that be doable for your case? If not here are some resources:

  • Random port in nodejs You could hit yet another service to find an open port during the build, or have your node app fire an http request to your java server based on the port it grabs.
  • Winston is the best logging library I've found, you shouldn't have any issues logging to the same path.
  • Forever and PM2 are the common node process managers which keep node running. I currently prefer forever (not sure why)

It sounds like you will be using a lot of cpu within node. If that is the case you will probably want to use the cluster module (so nodejs can utilize multiple cores). If you block the event loop (which cpu based processing will, then you will only be able to perform 1 concurrent request per forked process).

like image 37
Cody Gustafson Avatar answered Sep 20 '22 18:09

Cody Gustafson