'

Using scripts for application configuration

Sun Microsystems' Lee Chuk-Munn delves into script languages and how they can interface to the Java platform.

In a previous article, I talked about JSR-223 which introduces a standard way for script languages to interface to the Java platform. With JSR-223, you can use JavaScript, for example, to write a Swing application. Here is a simple Java script application that will create a JFrame with a single button with a listener:

importPackage(java.awt.event)
    importPackage(javax.swing)

    //Create a JButton
    var b = new JButton("Press me")

    //Create a listener and add it to the button
    var act = { actionPerformed: function() {
            JOptionPane.showMessageDialog(null, "Button pressed")
    }};
    b.addActionListener(new ActionListener(act))

    //Create a panel and add the button to it
    var p = new JPanel()
    p.add(b)

    //Create a frame and add teh panel to it
    var f = new JFrame()
    f.add(p)
    f.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
    f.setLocationRelativeTo(null)

    f.pack();
    f.visible = true

You can also invoke a script file from within a Java application and allow the script file to manipulate any arbitrary Java object passed in from the calling Java application. Scripting support is available in JDK 6 which you can download from here. A list of JSR-223 compliant script engines can be found here.

So what can we use this scripting feature? One new and exciting way to use this new scripting feature is in the area of storing application configuration.

Application configuration
Most non-trivial Java applications are configurable; these configurations, whether they are server port numbers, logging sensitiviy or location of databases, are typically stored in a text file that is read by the application when it starts up. The longstanding solution for storing application configurations has been to use property files. Over the years property files have been supercede by XML. Witness the popularity of XML in JavaEE deployment descriptors and in open source frameworks like Spring and Struts frameworks. Examples of these is shown in the following table:

With configuration files With XML files
com.acme.browser.timeout = 5000
<acme:connection>
   <acme:timeout value="5"/>
<acme:connection>

One drawback using XML and/or property files is that all the data read in are text/String so if your configuration is an interger value (eg. port number), then you will have to convert the text* into an integer using Integer.parseInt() method. The method may also throw a NumberFormatException if the text value is not an integer. The second drawback is that configuration values cannot be dynamically set. Let's say you have a timeout value for a network application. On a slower network, you may want to set the value large, while on a faster network, smaller. It would be great if the value can be determined at 'reading' time by invoking a function like so:


 com.acme.browser.timeout = calcluateTimeout()

where calcluateTimeout() is a predefined function. However doing this is not easy with either configuration or XML files.

The benefits of using a script language to hold configuration related data is the imperative nature of these configuration files (as oppose to the declarative properties and XML files). We could leverage JavaScript as a programming language and embed logic in these configuration files to dynamically set the values.

Here is what we do; we create a JavaBean to hold the configuration values. The JavaBean is then exported into a JavaScript where the latter is used to set the appropriate values in the JavaBean. Let's go through this:

Write a Java bean to hold all our configurations. Here is an example of a JavaBean call ApplicationConfiguration which holds three values:


public class ApplicationConfiguration {
        private String server = "localhost";
        private int port = 8080;
        private long timeout = 3000;

        //Accessors and mutators
        public void setServer(String s) { server = s; }
        public String getServer() { return (server); }
        ...
    }

Now we create an instance of JavaScript engine binding the JavaBean to the engine:


//Get an instance of JavaScript engine
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");

    //Instantiate a configuration object
    ApplicationConfiguration appConfig =
new ApplicationConfiguration();     //Bind appConfig to the JavaScript engine calling int 'config'     engine.put("config", appConfig);     //Execute our configuration file     try {         engine.eval(new FileReader("config.js"));     } catch (Exception e) {         //Configuration error, handle it         return;     }     //Read our configuration file successfully     //Now go use it     ...

After we have evaluated config.js, appConfig now holds the configuration which we can use in our application.

Now we turn our attention to config.js which is the JavaScript configuration file. This is how it might look:


importPackage(javax.swing)

//Assume isOnline() and determineTimeout() are predefined functions

//Check if we are online, if not set server to localhost
if (isOnline()) {
config.host = "myserver.com"
config.port = 80

config.timeout = determineTimeout();
} else
JOptionPane.showMessageDialog(null, "Not online, reverting to local")

In this particular configuration file, if the current system is not online, then we set the host to a local server. Can this be done in Java? Yes, but it is easier to do it outside of your application. Afterall, how the application is configured should not be tied to how it works.

One beneficial side effect of using script as configuration file is you get the data types for free! Unlike property or XML files, configuration values must be of the correct type when we assign them to their respective configurations. For example, if we try to assign the text eighty to config.port, JavaScript will complain about the error but not 0x50.

Remi Forax's blog, he went further by proposing that we should look at writing build files (eg. Ant's build.xml) with script simply because it is easier to write, read and debug. ,a href="http://raven.rubyforge.org/" target="_blank">Raven, a Ruby based is a prime example of this.

In the next article we will look at other uses for scripting, in particular using macros and reverse macros.

* This is not strictly true with XML. The Java platform has an API call JAXB (XML Binding) that allows you to generate a XML to Java marshaller/unmarshaller framework. However, to use this, you will need to first define a XML Schema for your configuration file. To find out more about using JAXB, see this tutorial.

Lee Chuk-Munn has been programming in the Java language since 1996, when he first joined Sun Microsystems in Hong Kong. He currently works as a senior developer consultant and technology evangelist for Technology Outreach at Sun in Singapore. Chuk's focus is in Java APIs, Java EE, Java SE, and Java ME. Chuk graduated in 1987 from the Royal Melbourne Institute of Technology in Melbourne, Australia, where his favorite subject was compiler theory.