JVM languages on VMS

Content:

  1. Why?
  2. Available
  3. Standalone compiled
  4. Standalone interpreted
  5. Calling Java code
  6. Java calling compiled code
  7. Embedded in Java - specific API's
  8. Embedded in Java - standard API (JSR223)

Why?

Many years ago the creator of C++ Bjarne Stroustrup said "Java isn't platform independent; it is a platform". At the time most Java people felt offended by the statement. Java was being sold as being platform independent - and Java was like 99% of the JVM world. Seen from where we are now, then he was absolutely right. Java is a platform that can support many languages and many types of languages.

Today the JVM world is way more diversified language wise even though Java is still a clear number one. There exist dozens of JVM languages and many of them are actually used.

Such languages are even more relevant on VMS. Lot of languages does not get ported to VMS. But if there is a JVM implementation, then it may actually run on VMS.

So in this article we will look at 18 JVM languages that runs on VMS. Some important languages, some niche languages and some obscure languages. There are probably a few more languages, but I have taken those that I know about.

All the code is super trivial - we are talking hello world level here. But it should be sufficient to illustrate how to do things. Without too much code distracting from the points.

Available:

Language Implementation Standalone compile Standalone interpret Can call Java code Compiled code can be
called from Java code
Embedded script can be
interpreted in Java code
(using language specific API)
Embedded script can be
interpreted in Java code
(using standard JSR 223 API)
Comments
Java VSI or HP(E) Yes No Yes Yes No No x86-64: version 8 (17 coming soon)
Itanium: version 5, 6 and 8
Alpha: version 5
Kotlin standard distribution Yes No Yes Yes No No Require source in RFM STMLF
Scala standard distribution or VSI distribution Yes No Yes Yes No No
Groovy standard distribution Yes Yes Yes Yes No Yes
Python 2.x Jython No Yes Yes No Yes Yes Jython will likely never support Python 3.x
Replacement GraalPy does not support VMS
JavaScript builtin (Java 6: Rhino, Java 8: Nashorn)
Rhino
Nashorn
No Yes Yes No Yes Yes Builtin is only for embedded usage not standalone usage
Rhino is pretty old version wise
Ruby JRuby Yes Yes Yes Yes Yes Yes Compile on PC due to no compile script for VMS
PHP 5.x Quercus No Yes Yes No Yes Yes Quercus will never support PHP 7.x or 8.x
Lua LuaJ Yes Yes No No Yes Yes LuaJ compilation compiles to Lua byte code not to Java byte code
jLuasScript which is a layer on top of LuaJ supposedly supports using Java libraries
Tcl JACL No Yes Yes No Yes Yes Basic embedded script works, but not context
Lisp ABCL No Yes Yes No Yes No JSR223 works on Windows and *nix
Beanshell Beanshell standard distribution No Yes Yes No Yes Yes Basically Java as script
Dartmouth Basic JBasic No Yes No No No No Dartmouth Basic is just for fun remembering the 60's
Oberon Gardens Point Component Pascal for JVM Yes No Yes Yes No No Require source in RFM STMLF (my wrapper converts if needed)
Calling Java libraries require using j2cps utility (*)
Ada JGnat Yes No Yes Yes No No Runtime only - compile on PC
Be aware of ACT licensing
Calling Java libraries require using jvm2ada utility (*)
Object Pascal FPC-JVM Yes No Yes Yes No No Runtime only - compile on PC
R Renjin No No No No Yes Yes Normal interpret and calling Java code work on other platforms
JSR223 does not work with context
Rexx NetRexx Yes Yes Yes Yes Yes Yes Basic embedded script works, but not context

*) which I have not tested.

When Java 17 for VMS show up then Perl via Perlito5 should work as well (with JSR223 support).

Category Languages
Primary implementations Java, Kotlin, Scala, Groovy, BeanShell
Secondary implementations Nashorn, JRuby, LuaJ, JACL, ABCL, Component Pascal, JGnat, FPC-JVM, Renjin, NetRexx
Obsolete secondary implementations Jython, Rhino, Quercus
Just for fun secondary implementations JBasic

Standalone compiled:

A simple program that print 3 times hello world.

public class Hello {
    public static void main(String[] args) {
        for(int i = 0; i < 3; i++) {
            System.out.println("Hello world from Java!");
        }
    }
}

Build and run:

$ javac Hello.java
$ java "Hello"
fun main() {
    for(i in 1..3) {
        println("Hello world from Kotlin!")
    }
}

Build and run:

$ kotlinc "Hello.kt"
$ java  -cp .:/disk0/net/kotlin/kotlinc/lib/* "HelloKt"
object Hello {
  def main(args: Array[String]): Unit = {
    for(i <- 1 to 3) {
      println("Hello world from Scala!")
    }
  }
}

Build and run:

$ scalac "Hello.scala"
$ java -cp .:/disk0/net/scala/scala-2.13.11/lib/* "Hello"
for(i in 1..3) {
    println("Hello world from Groovy!")
}

Build and run:

$ groovyc "Hello.groovy"
$ java  -cp .:/disk0/net/groovy/groovy-4.0.12/lib/* "Hello"
for i in 1..3 do
    puts 'Hello world from Ruby!'
end

Build (PC) and run (VMS):

jrubyc hello.rb
jar cvf jruby_hello.jar hello.class
$ java -cp jruby_hello.jar:../libs/jruby.jar "hello"
for i = 1,3
do
    print("Hello world from Lua!")
end

Build and run:

$ java -cp ../libs/luaj-jse-3_0_2.jar:../libs/bcel-5_2.jar "luajc" hello.lua
$ java -cp ../libs/luaj-jse-3_0_2.jar:. "lua" -l hello
MODULE Hello;
  IMPORT CPmain, Console;

VAR
  i : INTEGER;

BEGIN
  FOR i := 1 TO 3 DO
    Console.WriteString("Hello world from Oberon!");
    Console.WriteLn;
  END;
END Hello.

Build and run:

$ gpcpc "Hello"
$ gpcp "Hello"
with Ada.Text_IO;

use Ada.Text_IO;

procedure Hello is

begin
    for I in 1..3 loop
        Put_Line("Hello world from Ada!");
    end loop;
end Hello; 

Build (PC) and run (VMS):

jgnatmake Hello.adb
jar cvf jgnat_hello.jar *hello*.class
$ java -cp jgnat_hello.jar:../libs/jgnat.jar "hello"

Note that this is tested with old JGnat 1.1P not latest JGnat 2013. I had problems with JGnat 2013.

program hello(input, output);

{$include fpcjvm.inc}

var
   i : integer;
   
begin
   for i := 1 to 3 do begin
      writeln('Hello world from Object Pascal!');
   end;
end.

Build (PC) and run (VMS):

ppcjvm hello.pas
jar cvf fpcjvm_hello.jar hello.class
$ java -cp fpcjvm_hello.jar:../libs/fpcjvm.jar "hello"
loop i=1 to 3
  say 'Hello world from Rexx!'
end i

Build and run:

$ java -cp ../libs/netrexxc.jar org.netrexx.process.NetRexxC -verbose0 hello.rexx
$ java -cp .:../libs/netrexxc.jar "hello"

Standalone interpreted:

A simple script that print 3 times hello world.

for(i in 1..3) {
    println("Hello world from Groovy!")
}

Run:

$ groovy "Hello.groovy"
for i in range(3):
    print('Hello world from Python!')

Run:

$ java "-Dpython.console=org.python.util.InteractiveConsole" -cp ../libs/jython-standalone-2_7_3.jar "org.python.util.jython" hello.py
for(i = 0; i < 3; i++) {
    print("Hello world from JavaScript!")
}

Run:

$ java -cp ../libs/rhino-1_7_14.jar "org.mozilla.javascript.tools.shell.Main" hello.js
for i in 1..3 do
    puts 'Hello world from Ruby!'
end

Run:

$ java -cp ../libs/jruby.jar "org.jruby.Main" hello.rb
<?php

for($i = 0; $i < 3; $i++) {
    echo "Hello world from PHP!\r\n";
}

?>

Run:

$ java -cp ../libs/quercus.jar "com.caucho.quercus.CliQuercus" hello.php
for i = 1,3
do
    print("Hello world from Lua!")
end

Run:

$ java -cp ../libs/luaj-jse-3_0_2.jar "lua" hello.lua
for {set i 0} {$i < 3} {incr i} {
    puts "Hello world from Tcl!"
}

Run:

$ java -cp ../libs/com_springsource_tcl_lang_jacl-1_4_1.jar:../libs/com_springsource_tcl_lang-1_4_1.jar tcl.lang.Shell hello.tcl
(dotimes (n 3)
    (format t "Hello world from Lisp!~%"))

Run:

$ java -jar ../libs/abcl.jar --noinform --batch --load hello.lisp
for(int i = 0; i < 3; i++) {
    System.out.println("Hello world from BeanShell!");
}

Run:

$ java -cp ../libs/bsh-2_1_1.jar "bsh.Interpreter" hello.bsh
10 for i = 1 to 3
20   print "Hello world from Basic!"
30 next i
40 end

Run:

$ java -jar ../libs/jbasic.jar hello.bas
loop i=1 to 3
  say 'Hello world from Rexx!'
end i

Run:

$ java -cp ../libs/netrexxc.jar org.netrexx.process.NetRexxC -verbose0 -exec hello.rexx

Calling Java code:

Java comes with a huge eco-systsem of libraries and it is obviously relevant to be able to utilize those.

We will see examples of calling a static method in a Java class located in a jar file. Most languages also supports calling non-static methods, but static methods are more general than non-static methods as they also fit well with procedural programming.

Note that there is really no difference between Java and any other language able to generate Java byte code. The library could just as well have been written in Kotlin, Scala etc..

Library code:

package somelib;

public class X {
    public static void test(String lang, int v) {
        System.out.printf("%s : v = %d\n", lang, v);
    }
}

(for FPC the package is somelib.copy not somelib as the version of FPC tested has a problem with one level package names!)

import somelib.X;

public class Call {
    public static void main(String[] args) {
        X.test("Java", 123);
    }
}

Build and run:

$ javac -cp somelib.jar Call.java
$ java -cp .:somelib.jar "Call"
import somelib.*

fun main() {
    X.test("Kotlin", 123)
}

Build and run:

$ kotlinc "Call.kt" "-cp" "somelib.jar"
$ java  -cp .:somelib.jar:/disk0/net/kotlin/kotlinc/lib/* "CallKt"
import somelib.X

object Call {
  def main(args: Array[String]): Unit = {
    X.test("Scala", 123)
  }
}

Build and run:

$ scala_cp = "somelib.jar"
$ scalac "Call.scala"
$ java -cp .:somelib.jar:/disk0/net/scala/scala-2.13.11/lib/* "Call"
import somelib.*

X.test("Groovy", 123)

Build and run:

$ groovy_cp = "somelib.jar"
$ groovy Call.groovy
program call(input, output);

{$include fpcjvm.inc}

type
   X = class external 'somelib.copy' name 'X'
      class procedure test(lang : jlstring; v : integer); static;
   end;
   
begin
   X.test('Object Pascal', 123);
end.

Build (PC) and run (VMS):

ppcjvm call.pas
jar cvf fpcjvm_call.jar call.class
$ java -cp fpcjvm_call.jar:../libs/fpcjvm.jar:. "call"
import somelib.X

X.test('Rexx', 123)

Build and run:

$ java -cp somelib.jar:../libs/netrexxc.jar org.netrexx.process.NetRexxC -verbose0 -exec call.rexx
from somelib import X

X.test('Python', 123)

Run:

$ java "-Dpython.console=org.python.util.InteractiveConsole" -cp ../libs/jython-standalone-2_7_3.jar "org.python.util.jython" call.py
importClass(Packages.somelib.X);

X.test("JavaScript", 123);

Run:

$ java -cp somelib.jar:../libs/rhino-1_7_14.jar "org.mozilla.javascript.tools.shell.Main" call.js
java_import 'somelib.X'

X.test('Ruby', 123)

Run:

$ java -cp somelib.jar:../libs/jruby.jar "org.jruby.Main" call.rb
<?php

import somelib.X;

X::test('PHP', 123);

?>

Run:

$ java -cp somelib.jar:../libs/quercus.jar "com.caucho.quercus.CliQuercus" call.php
package require java

java::import somelib.X

java::call X test "Tcl" 123

Run:

$ java -cp somelib.jar:../libs/com_springsource_tcl_lang_jacl-1_4_1.jar:../libs/com_springsource_tcl_lang-1_4_1.jar tcl.lang.Shell call.tcl
(java:jstatic "test" "somelib.X" "Lisp" 123)

Run:

$ java -cp somelib.jar:../libs/abcl.jar "org.armedbear.lisp.Main" --noinform --batch --load call.lisp
import somelib.X;

X.test("BeanShell", 123);

Run:

$ java -cp somelib.jar:../libs/bsh-2_1_1.jar "bsh.Interpreter" call.bsh

Java calling compiled code:

Now we try Java code (or code in any other JVM language) calling a library in the other language.

The big four:

Caller:

public class Test {
    public static void main(String[] args) {
        XJava ojava = new XJava();
        ojava.test("Java", 123);
        XKotlin okotlin = new XKotlin();
        okotlin.test("Kotlin", 123);
        XScala oscala = new XScala();
        oscala.test("Scala", 123);
        XGroovy ogroovy = new XGroovy();
        ogroovy.test1("Groovy (dynamic)", 123);
        ogroovy.test2("Groovy (static)", 123);
    }
}

Called:

public class XJava {
    public void test(String lang, int v) {
        System.out.printf("%s : v = %d\n", lang, v);
    }
}
class XKotlin {
    fun test(lang: String, v: Int) {
        println("$lang : v = $v");
    }
}
class XScala {
  def test(lang: String, v: Int): Unit = {
    println(s"$lang : v = $v")
  }
}
class XGroovy {
    def test1(lang, v) {
        println("$lang : v = $v")
    }
    void test2(String lang, int v) {
        println("$lang : v = $v")
    }
}

Build and run:

$ javac XJava.java
$ kotlinc "XKotlin.kt"
$ scalac "XScala.scala"
$ groovyc "XGroovy.groovy"
$ javac -cp .:/disk0/net/groovy/groovy-4.0.12/lib/* Test.java
$ java -cp .:/disk0/net/kotlin/kotlinc/lib/*:/disk0/net/scala/scala-2.13.11/lib/*:/disk0/net/groovy/groovy-4.0.12/lib/* "Test"

Ruby:

Caller:

public class TestRuby {
    public static void main(String[] args) {
        XRuby oruby = new XRuby();
        oruby.test("Ruby", 123);
    }
}

Called:

class XRuby
   def test(lang, v)
      printf "#{lang} : v = #{v}\n"
   end
end;

PC:

jrubyc --java XRuby.rb
echo Edit XRuby.rb and hit enter
pause
javac -cp jruby.jar XRuby.java
jar cvf jruby_xruby.jar XRuby.class

Note that there is a bug in jrubyc at least on Windows so that one has to fix some line breaks.

VMS:

$ javac -cp .:jruby_xruby.jar:../libs/jruby.jar TestRuby.java
$ java -cp .:jruby_xruby.jar:../libs/jruby.jar "TestRuby"

Ada:

Caller:

public class TestAda {
    public static void main(String[] args) {
        ada_dummy.adainit();
        xada.test("Ada".getBytes(), 0, 0, 123);
        ada_dummy.adafinal();
    }
}

Called:

package XAda is
    procedure test(lang : in String; v : in Integer);
end XAda;
with Ada.Text_IO, Ada.Integer_Text_IO;

use Ada.Text_IO, Ada.Integer_Text_IO;

package body XAda is
    procedure test(lang : in String; v : in Integer) is

    begin
        Put(lang);
        Put(" : v = ");
        Put(v, 1);
        New_Line;
    end test;
end XAda;
with Ada.Text_IO, Ada.Integer_Text_IO;

use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Dummy is

begin
    Put("X");
    Put(0);
end Dummy; 

The dummy code is used to initialize Ada IO.

Note that it is possible to create a non-static medthod in JGnat using tagged type.

PC:

jgnatmake XAda.adb
jgnatmake Dummy.adb
jar cvf jgnat_xada.jar *xada*.class *dummy*.class

VMS:

$ javac -cp .:jgnat_xada.jar TestAda.java
$ java -cp .:jgnat_xada.jar:../libs/jgnat.jar "TestAda"

Pascal:

Caller:

public class TestPascal {
    public static void main(String[] args) {
        xpascal.test("Object Pascal", 123);
    }
}

Called:

unit xpascal;

interface

{$include fpcjvm.inc}

procedure test(lang : jlstring; v : integer);

implementation

procedure test(lang : jlstring; v : integer);

begin
   write(lang);
   write(' : v = ');
   write(v);
   writeln;
end;

end.

PC:

ppcjvm xpascal.pas
jar cvf fpcjvm_xpascal.jar xpascal.class

VMS:

$ javac -cp .:fpcjvm_xpascal.jar:../libs/fpcjvm.jar TestPascal.java
$ java -cp .:fpcjvm_xpascal.jar:../libs/fpcjvm.jar "TestPascal"

Rexx:

Caller:

import netrexx.lang.Rexx;

public class TestRexx {
    public static void main(String[] args) {
        XRexx orexx = new XRexx();
        orexx.test(new Rexx("Rexx"), new Rexx(123));
    }
}

Called:

class XRexx
    method test(lang, v)
        say lang || ' : v = ' || v

Build and run:

$ java -cp ../libs/netrexxc.jar org.netrexx.process.NetRexxC -verbose0 XRexx.rexx
$ javac -cp .:../libs/netrexxc.jar TestRexx.java
$ java -cp .:../libs/netrexxc.jar "TestRexx"

Embedded in Java - specific API's:

A common usage of script languages is to embed them in a program written in a compiled language.

Here are examples of Java code (or code in any other JVM language) interpreting script code stored in a String variable.

import org.python.util.PythonInterpreter;

public class EmbeddedJython {
    public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) {
        PythonInterpreter i = new PythonInterpreter();
        i.set("n", 3);
        i.set("o",  new Data(123, "ABC"));
        i.exec("for i in range(n):\r\n" +
               "    print(\"Hi from Python!\")\r\n" +
               "o.iv = o.iv + 1\r\n" +
               "o.sv = o.sv + 'X'\r\n" +
               "o.m()\r\n");
        Data o = (Data)i.get("o").__tojava__(Data.class);
        System.out.println(o);
        i.close();
    }

}

Run:

$ java -cp .:../libs/* "EmbeddedJython"
import org.mozilla.javascript.Context;
import org.mozilla.javascript.tools.shell.Global;

public class EmbeddedRhino {
    public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) {
        Context ctx = Context.enter();
        Global scope = new Global(ctx);
        scope.defineProperty("n", 3, 0);
        scope.defineProperty("o",  new Data(123, "ABC"), 0);
        ctx.evaluateString(scope, "for(i = 0; i < n; i++) {\r\n" +
                                  "    print(\"Hi from JavaScript!\")\r\n" +
                                  "}\r\n" +
                                  "o.iv = o.iv + 1;\r\n" +
                                  "o.sv = o.sv + \"X\";\r\n" +
                                  "o.m();\r\n", "global", 0, null);
        Data o = (Data)Global.getProperty(scope, "o");
        System.out.println(o);
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedRhino"
import org.jruby.embed.LocalVariableBehavior;
import org.jruby.embed.ScriptingContainer;

public class EmbeddedJRuby {
   public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) {
        ScriptingContainer cont = new ScriptingContainer(LocalVariableBehavior.PERSISTENT);
        cont.put("n", 3);
        cont.put("o", new Data(123, "ABC"));
        cont.runScriptlet("for i in 1..n do\r\n" +
                          "    puts 'Hi from Ruby!'\r\n" +
                          "end\r\n" +
                          "o.iv = o.iv + 1\r\n" +
                          "o.sv = o.sv + 'X'\r\n" +
                          "o.m()\r\n");
        Data o = (Data)cont.get("o");
        System.out.println(o);
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedJRuby"
import java.io.IOException;

import com.caucho.quercus.QuercusEngine;

public class EmbeddedQuercus {
    public static void main(String[] args) throws IOException {
        QuercusEngine eng = new QuercusEngine();
        eng.execute("<?php\r\n" +
                    "for($i = 0; $i < 3; $i++) {\r\n" +
                    "    echo \"Hi from PHP!\\r\\n\";\r\n" +
                    "}\r\n" +
                    "?>\r\n");
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedQuercus"
import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.CoerceJavaToLua;
import org.luaj.vm2.lib.jse.JsePlatform;

public class EmbeddedLuaJ {
   public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) {
        Globals glbs = JsePlatform.standardGlobals();
        glbs.set("n", 3);
        glbs.set("o", CoerceJavaToLua.coerce(new Data(123, "ABC")));
        LuaValue code = glbs.load("for i = 1,n\r\n" +
                                  "do\r\n" +
                                  "    print(\"Hi from Lua!\")\r\n" +
                                  "end\r\n" +
                                  "o.iv = o.iv + 1\r\n" +
                                  "o.sv = o.sv .. \"X\"\r\n" +
                                  "o:m()\r\n");
        code.call();
        Data o = (Data)glbs.get("o").touserdata();
        System.out.println(o);
    }

}

Run:

$ java -cp .:../libs/* "EmbeddedLuaJ"
import tcl.lang.Interp;
import tcl.lang.TCL;
import tcl.lang.TclException;
import tcl.lang.TclInteger;

public class EmbeddedJACL {
    public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) throws TclException {
        Interp i = new Interp();
        i.setVar("n", null, 3, TCL.NAMESPACE_ONLY);
        Data o = new Data(123, "ABC");
        i.setVar("iv", null, o.iv, TCL.NAMESPACE_ONLY);
        i.setVar("sv", null, o.sv, TCL.NAMESPACE_ONLY);
        i.eval("for {set i 0} {$i < $n} {incr i} {\r\n" +
                "    puts \"Hi from Tcl!\"\r\n" +
                "}\r\n" +
                "incr iv\r\n" +
                "set sv ${sv}X\r\n");
        o.iv = TclInteger.get(i, i.getVar("iv", TCL.NAMESPACE_ONLY));
        o.sv = i.getVar("sv", TCL.NAMESPACE_ONLY).getInternalRep().toString();
        System.out.println(o);
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedJACL"
import org.armedbear.lisp.Interpreter;

public class EmbeddedABCL {
    public static void main(String[] args) {
        Interpreter i = Interpreter.createInstance();
        i.eval("(dotimes (n 3)\r\n" +
                   "    (format t \"Hi from Lisp!~%\"))\r\n");
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedABCL"
import bsh.EvalError;
import bsh.Interpreter;

public class EmbeddedBeanShell {
    public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) throws EvalError {
        Interpreter i = new Interpreter(); 
        i.set("n", 3);
        i.set("o", new Data(123, "ABC"));
        i.eval("for(int i = 0; i < n; i++) {\r\n" +
               "    System.out.println(\"Hi from BeanShell!\");\r\n" +
               "}\r\n" +
               "o.iv = o.iv + 1;\r\n" +
               "o.sv = o.sv + \"X\";\r\n" +
               "o.m();\r\n");
        Data o = (Data)i.get("o");
        System.out.println(o);
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedBeanShell"
import javax.script.ScriptException;

import org.renjin.eval.Context;
import org.renjin.script.RenjinScriptContext;
import org.renjin.script.RenjinScriptEngine;
import org.renjin.sexp.ExternalPtr;

public class EmbeddedR {
    public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public int getIv() {
            return iv;
        }
        public void setIv(int iv) {
            this.iv = iv;
        }
        public String getSv() {
            return sv;
        }
        public void setSv(String sv) {
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void main(String[] args) throws ScriptException {
        RenjinScriptEngine eng = new RenjinScriptEngine();
        eng.eval("for (i in 1:3) {\r\n" +
                 "    print(\"Hi from R!\")\r\n" +
                 "}\r\n");
        eng.put("n", 3);
        eng.put("o", new Data(123, "ABC"));
        eng.eval("for (i in 1:n) {\r\n" +
                 "    print(\"Hi from R!\")\r\n" +
                 "}\r\n" +
                 "o$iv = o$iv + 1L\r\n" +
                 "o$sv = paste(o$sv, \"X\", sep=\"\")\r\n" +
                 "o$m()\r\n");
        Data o = ((ExternalPtr<Data>)eng.get("o")).getInstance();
        System.out.println(o);
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedR"
import org.netrexx.process.NetRexxA;

public class EmbeddedRexx {
    public static void main(String[] args) {
        NetRexxA i = new NetRexxA();
        i.interpret("exec", "loop i=1 to 3\r\n" +
                            "  say 'Hi from Rexx!'\r\n" +
                            "end i\r\n");
    }
}

Run:

$ java -cp .:../libs/* "EmbeddedRexx"

Embedded in Java - standard API (JSR223):

The code for different script languages shown in previous section are actually rather similar, so to make it even easier, then Java 1.6 introduced a standard API for interpreting scripts, so that both script language name and script code can be String variables.

That API is usually known as JSR223 API.

Simple - no context:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class EmbeddedScript {
    public static void test(String language, String source) throws ScriptException {
        System.out.println(language + ":");
        System.out.print(source);
        ScriptEngineManager sem = new ScriptEngineManager();
        ScriptEngine se = sem.getEngineByName(language);
        se.eval(source);
    }
    public static void main(String[] args) throws ScriptException {
        test("javascript", "for(i = 0; i < 3; i++) {\r\n" +
                           "    print(\"Hi from JavaScript!\")\r\n" +
                           "}\r\n");
        test("python", "for i in range(3):\r\n" +
                       "    print(\"Hi from Python!\")\r\n");
        test("ruby", "for i in 1..3 do\r\n" +
                     "    puts 'Hi from Ruby!'\r\n" +
                     "end\r\n");
        test("php", "<?php\r\n" +
                    "for($i = 0; $i < 3; $i++) {\r\n" +
                    "    echo \"Hi from PHP!\\r\\n\";\r\n" +
                    "}\r\n" +
                    "?>\r\n");
        test("groovy", "for(i in 1..3) {\r\n" +
                       "    println \"Hi from Groovy!\"\r\n" +
                       "}\r\n");
        test("beanshell", "for(int i = 0; i < 3; i++) {\r\n" +
                          "    System.out.println(\"Hi from BeanShell!\");\r\n"+
                          "}\r\n");
        test("lua", "for i = 1,3\r\n" +
                    "do\r\n" +
                    "    print(\"Hi from Lua!\")\r\n" +
                    "end\r\n");
        // abcl fails because not on *nix or Windows
        test("tcl", "for {set i 0} {$i < 3} {incr i} {\r\n" +
                     "    puts \"Hi from Tcl!\"\r\n" +
                     "}\r\n");
        test("Renjin", "for (i in 1:3) {\r\n" +
                       "    print(\"Hi from R!\")\r\n" +
                       "}\r\n");
        test("NetRexx", "loop i=1 to 3\r\n" +
                        "  say 'Hi from Rexx!'\r\n" +
                        "end i\r\n");
    }
}

With context:

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

import org.luaj.vm2.script.LuajContext;

public class EmbeddedScript2 {
    public static class Data {
        public int iv;
        public String sv;
        public Data(int iv, String sv) {
            this.iv = iv;
            this.sv = sv;
        }
        public void m() {
            iv++;
            sv += "X";
        }
        @Override
        public String toString() {
            return String.format("(%d,%s)", iv, sv);
        }
    }
    public static void test(String language, String source) throws ScriptException {
        System.out.println(language + ":");
        System.out.print(source);
        ScriptEngineManager sem = new ScriptEngineManager();
        ScriptEngine se = sem.getEngineByName(language);
        ScriptContext ctx;
        switch(language) {
            case "lua":
                ctx = new LuajContext(); // LuaJ insist on getting a LuajContext which is actually a sub class of SimpleScriptContext
                break;
            default:
                ctx = new SimpleScriptContext();
                break;
        }
        ctx.setAttribute("n", 3, ScriptContext.ENGINE_SCOPE);
        ctx.setAttribute("o", new Data(123, "ABC"), ScriptContext.ENGINE_SCOPE);
        se.eval(source, ctx);
        Data o = (Data)ctx.getAttribute("o");
        System.out.println(o);
    }
    public static void main(String[] args) throws ScriptException {
        test("javascript", "for(i = 0; i < n; i++) {\r\n" +
                           "    print(\"Hi from JavaScript!\")\r\n" +
                           "}\r\n" +
                           "o.iv = o.iv + 1;\r\n" +
                           "o.sv = o.sv + \"X\";\r\n" +
                           "o.m();\r\n");
        test("python", "for i in range(n):\r\n" +
                       "    print(\"Hi from Python!\")\r\n" +
                       "o.iv = o.iv + 1\r\n" +
                       "o.sv = o.sv + 'X'\r\n" +
                       "o.m()\r\n");
        test("ruby", "for i in 1..n do\r\n" +
                     "    puts 'Hi from Ruby!'\r\n" +
                     "end\r\n" +
                     "o.iv = o.iv + 1\r\n" +
                     "o.sv = o.sv + 'X'\r\n" +
                     "o.m()\r\n");
        test("php", "<?php\r\n" +
                    "for($i = 0; $i < $n; $i++) {\r\n" +
                    "    echo \"Hi from PHP!\\r\\n\";\r\n" +
                    "}\r\n" +
                    "$o->iv = $o->iv + 1;\r\n" +
                    "$o->sv = $o->sv . 'X';\r\n" +
                    "$o->m();\r\n" +
                    "?>\r\n");
        test("groovy", "for(i in 1..n) {\r\n" +
                       "    println \"Hi from Groovy!\"\r\n" +
                       "}\r\n" +
                       "o.iv = o.iv + 1\r\n" +
                       "o.sv = o.sv + \"X\"\r\n" +
                       "o.m()\r\n");
        test("beanshell", "for(int i = 0; i < n; i++) {\r\n" +
                          "    System.out.println(\"Hi from BeanShell!\");\r\n"+
                          "}\r\n" +
                          "o.iv = o.iv + 1;\r\n" +
                          "o.sv = o.sv + \"X\";\r\n" +
                          "o.m();\r\n");
        test("lua", "for i = 1,n\r\n" +
                    "do\r\n" +
                    "    print(\"Hi from Lua!\")\r\n" +
                    "end\r\n" +
                    "o.iv = o.iv + 1\r\n" +
                    "o.sv = o.sv .. \"X\"\r\n" +
                    "o:m()\r\n");
        // abcl fails because not on *nix or Windows
        // I cannot get jacl to work with context
    }
}

Build and run:

$ javac -cp .:../libs/* EmbeddedScript.java
$ java -cp .:../libs/* EmbeddedScript
$ javac -cp .:../libs/* EmbeddedScript2.java
$ java -cp .:../libs/* EmbeddedScript2

Article history:

Version Date Description
1.0 September 5th 2024 Initial version

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj