Script Your Scala Application with JRuby, Jython, Groovy and JavaScript

This tutorial shows how you can script a Scala application using the Java 6 scripting engines features (JSR 223 Scripting APIs). It also proves the level of power Scala gets from running on the JVM and from being able to use Java APIs. Along with a syntax that tries hard not to alienate Java and C# programmers this power gives Scala a jump start in the race for wining a place in programmers’ minds.

In a previous tutorial I described how you can script your Java application.
Since Scala runs on the standard JVM and uses all the Java APIs, it is possible to write scriptable Scala applications.

To demonstrate this I am going to port the Java scriptable code written for the “Script Thy Java App” tutorial to Scala.

The idea is to provide an “object model” accessible from the scripts and a “plugin interface” that will be implemented by the scripts. The demo application will load and initialize the scripts and then will run them. When run, the scripts will access the “object model”.

The object model is just a trivial counter object (Counter.scala):

package com.littletutorials.om

class Counter {
    private var i: Int = 0

    def getI = i

    def setI(i: Int) = {
        this.i = i
    }
}

The scripts will implement an application specific trait/interface (Plugin.scala):

package com.littletutorials.om

trait Plugin {
    def execute
}

At this point we have everything we need for our customers: an API (the object model – Counter) and a standard trait/interface for their scripts (the Plugin trait). Internally we also need a way to load the scripts and execute them. Of course for a real application you can implement a very sophisticated architecture for script configuration, discovery, initialization and execution. But for this tutorial we are going to implement a very simple solution based on restrictions:

  • all the scripts will reside in one folder
  • only one script of each type is allowed
  • all the scripts will be called “plugin.ext” where the extension is language dependent (js, py, rb, groovy…)

The name is used to identify a plugin script and the extension to identify the scripting engine.

Here is our script plugin manager combined with our application (ScriptPluginApp.scala):

package com.littletutorials.scripting

import java.io._
import javax.script._
import scala.collection.mutable._
import com.littletutorials.om._

object ScriptPluginApp {
    val PLUGIN_PREFIX = "plugin"
    val VAR_COUNTER = "counter"
    val VAR_PLUGIN = "plugin"

    // prepare the application object model
    val counter = new Counter
    // prepare plug-in repository (just a list)
    val plugins = new ArrayBuffer[Plugin]

    def loadPlugins(pluginDir: File) = {
        val pluginScripts: Array[File] = pluginDir.listFiles(new FilenameFilter() {
            override def accept(dir: File, name: String) = name.startsWith(PLUGIN_PREFIX)
        })

        // create the script engine manager
        val factory = new ScriptEngineManager

        for (val script <- pluginScripts) {
            val name = script.getName
            val ext = name.substring(name.indexOf(".") + 1, name.length)

            val engine = factory.getEngineByExtension(ext)
            if (engine != null) {
                // pass the object model
                engine.put(VAR_COUNTER, counter)
                // evaluate the plug-in script
                engine.eval(new FileReader(script.getAbsolutePath))
                // store the plug-in
                plugins += engine.get(VAR_PLUGIN).asInstanceOf[Plugin]
            }
        }
    }

    def executePlugins {
        plugins.foreach(p => p.execute)

        // check the effect
        println("Final counter = " + counter.getI);
    }

    def main(args: Array[String]) = {
        if (args.length != 1) {
            println("Wrong number of arguments! " + args.length + " present, 1 expected.")
            System.exit(1)
        }

        val pluginDir = new File(args(0))
        if (! pluginDir.isDirectory) {
            println("Plugin folder does not exist: " + pluginDir.getAbsolutePath)
            System.exit(2)
        }

        // load plug-ins
        loadPlugins(pluginDir)
        // execute plug-ins
        executePlugins
    }
}

To test this code you need to compile it by hand with the scalac compiler (download the Scala distribution from http://www.scala-lang.org/downloads/index.html) or you can use the Eclipse plugin found at http://www.scala-lang.org/tools/eclipse/index.html if you like Eclipse (I do).

You also need to:

When you have everything ready run the application like this:

scala -cp {the big class path with all the jars} com.littletutorials.scripting.ScriptPluginApp {plugins folder}

You should, again, enjoy an output like this:

Initialize Jython plugin
Initialize Groovy plugin
Initialize JavaScript plugin
Initialize JRuby plugin
Jython initial counter = 0
Groovy initial counter = 1
JavaScript initial counter = 2
JRuby initial counter = 3
Final counter = 4

I think this example proves the power given to Scala programmers by some of the decisions made by the language creators. Starting to use Scala is a matter of understanding concepts and learning syntax as usually with a new language. But a Java programmer for instance can feel at home pretty fast since:

  • Scala is object oriented
  • supports an imperative style of coding
  • uses a familiar syntax
  • offers access to all Java libraries

2 thoughts on “Script Your Scala Application with JRuby, Jython, Groovy and JavaScript”

Comments are closed.