This is a set of libraries for running node.js scripts inside Java.
Trireme runs Node.js scripts inside the JVM. This is important because there is a lot of software out there (including our own) that is built in Java and isn't going to get rewritten in JavaScript now or in the future.
Trireme is specifically designed to be embeddable within any Java program. There is a lot of support inside Trireme for this specific case:
For a more detailed introduction, see our intro presentation:
If neither of those reasons apply to you, then stick with "regular node!"
sudo npm install -g trireme
trireme -h
trireme <your script name here>
The NPM package for Trireme lets you run it on the command line just like "node".
Unfortunately, Trireme does not support the "repl" yet (and it's hard since Java gives us limited control over the TTY) so just running "trireme" with no arguments produces an error right now.
The best reason to use Trireme is because it's important to embed Node.js code inside an existing Java application. In that case you will use the modules under "io.apigee.trireme" on Maven Central:
The "module map" later in this document shows which modules to use in which cases.
See the releases page to download the latest release files.
"trireme-x.y.z.jar" is always a stand-alone jar that you can run just like "node":
java -jar trireme-x.y.z.jar script.js
Trireme supports two versions of Node.js:
Support for Node.js 4.0 depends on more complete ES6 code in Rhino. The Rhino community is making progress on this but it will be quite some time before we are ready to support 4.0.
If you installed Trireme using NPM, just run:
trireme <script name>
Trireme will execute your script just like Node.
In addition, the environment variable TRIREME_CLASSPATH may be used to add extra JARs or directories to the classpath used to run Trireme. Anything on this path will be appended to the classpath used to launch Trireme. This allows you to add JDBC drivers, etc.
For help, use:
trireme -h
The "jar" module builds a self-contained JAR that may be used to launch Trireme on the command line just like regular Node.js:
mvn install
java -jar jar/target/trireme.X.Y.Z.jar <script name>
(and with no arguments it will launch the "repl" but that implementation is not complete)
Node.js apps on Apigee Edge run inside Trireme using a few specific settings that make them slightly different from the standard Trireme runtime. In particular, an "HTTP adaptor" is used, which is not 100% the same as the standard HTTP module. This does not affect most applications, but it may affect applications that depend on undocumented or internal functions and properties of the default HTTP module. (While this is a poor programming practice, it's quite common.)
In order to test applications that will run on Apigee Edge in the most compatible environment, the module "samples/apigee-edge-like-runner" will build a self-contained JAR that sets up Trireme this way. Use it just like the "Using Java" link above:
mvn install
java -jar samples/apigee-edge-like-runner/target/apigee-edge-like-launcher-X-Y-Z-SNAPSHOT.jar <script name>
The war sample is a sample that shows how to assemble a Node.js application into a WAR file. It uses the trireme-servlet module to link the servlet to the Node.js script. Any script that operates as an HTTP server using the "http" module can be embedded in this way.
There is JavaDoc for the "NodeEnvironment" and "NodeScript" classes, and many other features. Here are the basics:
import io.apigee.trireme.core.NodeEnvironment;
import io.apigee.trireme.core.NodeScript;
// The NodeEnvironment controls the environment for many scripts
NodeEnvironment env = new NodeEnvironment();
// Pass in the script file name, a File pointing to the actual script, and an Object[] containg "argv"
NodeScript script = env.createScript("my-test-script.js",
new File("my-test-script.js"), null);
// Wait for the script to complete
ScriptStatus status = script.execute().get();
// Check the exit code
System.exit(status.getExitCode());
The all-in-one JAR, and the "trireme" NPM package, include code for both Node.js 0.10 and 0.12. To select a version from the command-line, use the "--node-version" option, like this:
trireme --node-version=0.12 foo.js
When embedding Trireme, select the version using the "setNodeVersion" method in the NodeScript class.
With this scheme, both versions of Node can run inside the same JVM.
The version numbers are "semver-style" although they do not support every single feature of semver. The best bet is to use "0.10" and "0.12" to select each.
There are a few NPM modules that only work in Trireme. These allow access to features of the Java platform that are normally accessed via native code in regular Node.js. These modules are as follows:
Trireme uses slf4j for logging the stuff that is happening in Java. The pre-built JAR, and the NPM "trireme" wrapper, use "slf4j-simple". To turn on debug logging, set the system property "org.slf4j.simpleLogger.defaultLogLevel" to "debug". (Or "trace" for even more output.)
When embedding trireme, you can use any SLF4J-compatible logging framework you wish, such as logback.
Trireme supports most of the Node.js APIs and passes much of the Node.js test suite.
The table below shows each module and its status. "Complete" means that a module is functionally complete, although it may not necessarily pass all the node.js tests.
| Module | Status | Source |
| assert | Complete | node.js |
| child_process | Partial | Trireme |
| cluster | Not Implemented Yet | node.js |
| console | Complete | node.js |
| crypto | Complete | node.js + Trireme |
| debugger | Not Supported | |
| dgram | Complete | node.js + Trireme |
| dns | Complete | Trireme |
| domain | Complete | node.js + Trireme |
| events | Complete | node.js |
| fs | Complete | node.js + Trireme |
| globals | Complete | node.js + Trireme |
| http | Complete | node.js + Trireme |
| https | Complete but See Notes | Trireme |
| module | Complete | node.js |
| net | Complete | node.js + Trireme |
| os | Partial | Trireme |
| path | Complete | node.js |
| process | Complete | Trireme |
| punycode | Complete | node.js |
| querystring | Complete | node.js |
| readline | Partial | node.js |
| repl | Not Supported | node.js |
| stream | Complete | node.js |
| string_decoder | Complete | node.js |
| timers | Complete | node.js + Trireme |
| tls | Complete but See Notes | Trireme |
| tty | Complete | Trireme |
| url | Complete | node.js |
| util | Complete | node.js |
| vm | Complete | Trireme |
| zlib | Complete | Trireme |
A few of the modules are different, some in major ways:
Trireme runs in the JVM on Rhino, which is the most complete JavaScript implementation for the JVM and the one that works on the largest variety of Java distributions. The latest version of Rhino has many of the new JavaScript language features that Node users are used to, but still does not have all the features that are supported when the "--harmony" flag is set.
For specifics, see the Rhino compatibility table. Trireme is currently using RHino 1.7.7.
Most of the time the differences between V8 and Rhino do not affect Node.js code, but occasionally there is a problem. We would love some help from the Rhino community to start to address these differences.
Trireme uses Java's standard "SSLEngine" for TLS/SSL and HTTPS support, whereas standard Node.js uses OpenSSL. The TLS implementation in Node.js is a fairly thin layer on top of OpenSSL and we chose not to try and replicate this in Java.
For the most part, TLS and HTTPS in Trireme will work just like they do in Node.js. However, they SSLEngine and OpenSSL are not exactly the same. There are a few differences:
1) Most notably, especially with Java 7, SSLEngine supports a different set of cipher suites, particularly the various elliptical curve ciphers. There are ciphers in common (otherwise almost everything will break) but there are many that are not. Many Node.js tests that rely on older cipher suites using DES or RC4 will not run on Trireme because many of these older and weaker cipher suites are disabled by default in Java. However, "OpenSSL style" names work in Trireme just as they do in regular Node and if the JVM supports a particular cipher suite from OpenSSL, you will get the same one in Trireme.
2) Java handles SSL sessions differently, and gives the user less control about it. Right now, Trireme is unable to support the ability of a TLS or HTTPS client to retrieve the session from an existing connection and re-use it for another TCP connection.
3) Java also will produce different certificate validation errors than OpenSSL does. The errors will still come in the same places and for the same reasons, but if your code depends on a specific error message, it will likely get a different one.
4) Java's SSLEngine relies on its own "keystore" files, whereas OpenSSL can operate on a variety of files but typically processes PEM files. Trireme handles this disparity by using the "Bouncy Castle" crypto framework to translate PEM files into keys and certificates that SSLEngine can understand. In addition, you can also use regular Java keystore files, as described below.
In order to support TLS and HTTPS using PEM files, the "trireme-crypto" module and its dependencies (Bouncy Castle) must be in the class path. If they are not present, then TLS is still available, but it will only work with Java keystore files (see below) or without using any keys at all. Trireme checks for this dependency at runtime, so it is simply a matter of including it on the class path, since it will fail at runtime if the dependency is needed, and work otherwise.
(For instance, Trireme can still execute a Node program that acts as an HTTPS client using only default certificates without requiring trireme-crypto. But if it needs to validate a particular CA certificate or if it needs to use a client-side certificate then trireme-crypto is also necessary.)
In addition, the TLS and HTTPS-related methods in Trireme can use a Java keystore instead of PEM files. There are three parameters that are relevant here:
$ claude mcp add trireme \
-- python -m otcore.mcp_server <graph>