Skip to main content

Quick-and-dirty Java programming

FESI provides all the power of the Java platform with the flexibility of scripting

Sing Li (westmakaha@yahoo.com), Author, Wrox Press
Photo of Sing Li
Sing Li is the author of Early Adopter JXTA and Professional Jini, as well as numerous other books with Wrox Press. He is a regular contributor to technical magazines and is an active evangelist of the P2P evolution. Sing is a consultant and freelance writer and can be reached at westmakaha@yahoo.com.

Summary:  The Java language is a great high-level programming language, allowing us to stay close to the problem we want to solve while effortlessly experimenting with different solutions. But in the real world of everyday computing (and development), there are many situations where "whipping up a Java program" to perform a task is either impractical or too time consuming. This article takes you into the underground world of FESI (Free EcmaScript Interpreter), where deploying the Java language in a quick-and-dirty fashion is the norm rather than the exception.

Date:  03 Jul 2001
Level:  Introductory
Activity:  2463 views

From concept to product, the Java platform and the Java programming language facilitate software production by providing high-level features and comprehensive APIs not found in any other development environment. These features and APIs significantly enhance productivity and shorten product cycles.

As versatile as the Java programming language is, however, it remains a formal, compiled language. A Java development cycle requires careful advance planning. In a developer's day-to-day life, there are many situations where he or she may need to create simple, one-off programs where development using the Java language may not fit the bill (and could be overkill). Such situations occur frequently in the areas of creating code-testing harnesses; creating sequencing code for multiple programs/actions that may only be used once; and creating quick prototypes of application concepts. Instead of using the Java language, many developers have turned to quick-and-dirty scripting solutions such as shell scripts or Win32 batch files. Unfortunately, these solutions are non-portable, and do not have the same features and support as solutions using the Java platform.

Enter FESI, a script interpreter written natively in the Java language, which makes it portable to all operating systems and hardware systems supporting the Java platform. FESI supports a dialect of JavaScript called EcmaScript, and it has access to the full capabilities of the hosting JDK. That is, you can code your EcmaScript program to take advantage of the comprehensive APIs of the underlying JDK. You get all the power of the Java platform, and you can control it via quick-and-dirty scripting.

EcmaScript and JavaScript

EcmaScript is a portable subset of the JavaScript programming language, the very same programming language embedded within every single modern Web browser. In fact, EcmaScript is the result of an attempt to standardize on a single dialect of the JavaScript language -- enabling code on Web pages to work across browsers from all vendors (but mainly between Netscape and Microsoft browsers because they own a lion's share of the install base). ECMA is the European Computer Manufacturers Association, a consortium that manages many standards. EcmaScript is the ECMA standard number 262.

In many ways, EcmaScript is a lowest-common-denominator solution to the scripting code compatibility problem among popular Web browsers. Even though both Microsoft Explorer 5.5 and Netscape Navigator 6.0 both support EcmaScript, that doesn't mean that JavaScript code created on one will run without change in the other.

To understand why this is the case, we must examine the constituents of a JavaScript host environment. Figure 1 illustrates this.


Figure 1. JavaScript host environment
Figure 1. JavaScript host environment

The EcmaScript component offers only the core language features -- the primitive data types, expression evaluation, flow control, etc. To do anything useful, you must use the language to manipulate objects exposed by the host environment to the script language. You perform this manipulation by invoking methods of the objects or changing their property values. Each JavaScript host must expose its own set of host objects to be useful (for example, a browser may expose an object model for interacting with a Web page). EcmaScript itself mandates support of only a very small set of native objects (see Resources).

In fact, the major difference between the JavaScript host environment available within a browser and the one offered by FESI is the set of host objects exposed. FESI, through the JavaAccess extension, exposes almost all of the JDK objects for direct EcmaScript manipulation. In order words, we are free to use EcmaScript programs to control most of the JDK APIs. Other than the JavaAccess extension, FESI also provides special extension code in the following areas:

  • Database access via JDBC
  • File I/O
  • Basic I/O

These extensions make programming in the specific area even easier and more concise by helping you match the object support to the EcmaScript style of programming (instead of Java API style, which is often difficult to script). We will experience this first-hand in the sample programs discussed in this article. (See Resources to download the source used in this article.)

Before we go any further, see "Downloading and installing FESI" to get FESI up and running on your box.


Do you speak FESI?

Use the command fesidev at the command line to start the FESI interactive interpreter environment. You will now be able to easily interact with the EcmaScript interpreter and get immediate results.

There are a few built-in commands, which are not part of EcmaScript or FESI, that the interactive interpreter understands. You access these commands by entering @help on the command line.

Here is a description of the most frequently used commands:

  • @clear -- Clears the interactive display area

  • @describe -- Gets detailed descriptions of variables or objects

  • @exit -- Exits from the interactive environment

  • @listall -- Shows all the properties of an object

You can enter an EcmaScript program interactively into FESI, or use the File|Open menu to open it in a text file containing an existing EcmaScript program. Text-based script files can run in the batch mode. The convention is to use the .es extension for EcmaScript source and the .ew extension for EcmaScript source that uses GUI support. To process EcmaScript files in batch mode, use the fesi.bat and fesiw.bat files you generated during installation.


Experimenting with EcmaScript

To seasoned Java developers, EcmaScript will feel raw and unstructured. For example, you do not have to declare variables before using them. Object definitions are quite ad hoc. The best way to learn EcmaScript is to use the interactive FESI interpreter. In this section, we will examine some of the major differences between EcmaScript syntax and the Java language. We will look at late binding and loose typing (where you don't determine a variable's data type until you execute the actual code) -- as well as the lack of enforced, formal, object-oriented constructs.

To declare an object called myObject, we can type in:

> var myObj = new Object;

So far, so good. If we want to add a property to the object, here is what we do:

> myObj.name = "George";

This is similar to attaching properties to a Java object supporting a dynamic property list, but there is no primitive equivalent in the Java language itself. We can use the same technique to add a property of an integer type called age to myObj.

> myObj.age = 33;

We can see all the properties of the object using the @listall command discussed earlier.

>@listall myObj
age: [PRIMITIVE]: 33
name: [PRIMITIVE]: George

Finally, if we want to add some behavior to the object, we first define an EcmaScript function and then assign it as a property of the object.

> function intAdd(inParam) { writeln(inParam + this.age); }
> myObj.add = intAdd;
> @listall myObj
age: [PRIMITIVE]: 33
name: [PRIMITIVE]: George
add: [OBJECT]: function intAdd(inParam) { writeln (inParam + this.age ); }

To see the very late binding characteristic of EcmaScript, try this:

> myObj.add(5);
38

Note that a numeric addition has occurred because the input parameter to the add() function is bound to an int type argument at function execution time. Now, try this:

>myObj.add("5");
533

This time, a string concatenation has occurred within the same method, and the input parameter is now bound to a String type argument at function execution time.

To create an object definition from which you can repeatedly create new instances, you must first define its constructor. We have first created a myObjDef constructor below, and immediately created a new instance of myObjDef using the new operator.

> function myObjDef(inName, inAge) { this.name = inName; this.age = inAge; }
> myObj = new myObjDef("Joe", 7);
> @listall myObj
age: [PRIMITIVE]: 7 name: [PRIMITIVE]: Joe 

Arrays in EcmaScript are native objects. Being loosely typed, each element can contain any primitive type or object.

> myArray = new Array(2);
> myArray[0] = "A String";
> myArray[1] = new myObjDef("Joe", 7); 
> @listall myArray 
0: [PRIMITIVE]: A String 1: [OBJECT]: [object Object] length: [PRIMITIVE]: 2 
> @listall myArray[1]
age: [PRIMITIVE]: 7 name: [PRIMITIVE]: Joe 

Most of the control flow statements in EcmaScript should be familiar to Java programmers, as the statements were modeled after the Java programming language. EcmaScript also supports very limited forms of inheritance, containment, and aggregation -- none of which are enforced by the programming language itself (unlike the Java language). Interested readers should consult Resources for more information.

We will now begin working with a FESI program that uses some JDK features, using JDBC to access RDBMS data.


Working with RDBMS using FESI

FESI comes with a database access extension. This extension enables FESI programs to easily access the data contained within an RDBMS. Database access is performed through JDBC. While it is possible to use JDBC directly via the Java object access capability of FESI, the database access extension makes working with data in FESI significantly simpler.

Let's take a look at a simple program that will create a database table and populate the table with data values. We'll be using the popular mySQL server and the mm JDBC type 4 driver (see Resources) in our example. This example assumes you have create, drop, and insert access privilege on a database called devworks. You should modify the JDBC connect URL to reflect your own installation. If you would rather use the JDBC-ODBC bridge with a local MS Access database, see "Setting up a data source for JDBC" for more information. Listing 1 shows the FESI code; you can find it in the DBScript.es file included with the source distribution.


Listing 1. FESI code for database access
var db= new Database("org.gjt.mm.mysql.Driver");
db.connect("jdbc:mysql://192.168.23.38:3306/devworks?user=dbuser");
 var removeOLD = "drop table SimpContact;";
 var createNew = "create table SimpContact ( name char(30), age int(4));"
 var insertData1 = "insert into SimpContact values ('Joe', 33);";
 var insertData2 = "insert into SimpContact values ('Mary', 48);";
  result = db.executeCommand(removeOLD);
  result = db.executeCommand(createNew);
  result = db.executeCommand(insertData1);
  result = db.executeCommand(insertData2);
db.disconnect();

In Listing 1, the FESI database access extension provides the versatile Database object. You create an instance by specifying the JDBC driver you used to access your RDBMS. Next, you need to get a JDBC connection -- this is done via the connect() method of the Database object. Finally, we perform the Data Definition Language (DDL) statements using the executeCommand() method of the Database object. We will see a little later how to work with SQL SELECT query and the result set using the FESI database access extension.

While this simple program is usable, we can add substantial flexibility by:

  • Enabling the user to specify the JDBC driver and connection URL via an external configuration file

  • Reading in the commands to execute from a stand-alone SQL command file

We will see how to easily do this in FESI in the next section.

FESI JavaAccess and file I/O extensions

Let's take a look at how our enhanced EcmaScript program, DBScriptFlex.es, will read a file called dbinit.ini. Entries in the dbinit.ini file will determine which JDBC driver and URL you will use, and which SQL command file you will execute. The dbinit.ini file contains:

JDBCdriver=org.gjt.mm.mysql.Driver
DBConnURL=jdbc:mysql://192.168.23.38:3306/devworks?user=dbuser
SQLcmds=DBPop.sql

And the command file, DBPop.sql, contains just the SQL commands for execution:

drop table SimpContact;
create table SimpContact ( name char(30), age int(4));
insert into SimpContact values ('Joe', 33);
insert into SimpContact values ('Mary', 48);

And here is our more flexible data access program:


Listing 2. DBScriptFlex.es
 
var myProp = new java.util.Properties;
myProp.load(new java.io.FileInputStream("dbinit.ini"));
var DBDriver = myProp.getProperty("JDBCdriver");
var DBConn = myProp.getProperty("DBConnURL");
var SQLFile = myProp.getProperty("SQLcmds");
writeln("Using JDBC Driver " + DBDriver);
writeln("Using Connection URL " + DBConn);
var db = new Database(DBDriver);
db.connect(DBConn);
var cmdFile = File(SQLFile);
var allCmds = cmdFile.readAll();
var Cmds = allCmds.split("
");
for (i=0; i< Cmds.length; i++)
   result = db.executeCommand(Cmds[i]);
db.disconnect();

Note the use of a JavaAccess extension in creating a java.util.Properties object. We also create a java.io.FileInputStream using the same technique. We use this stream to load in the properties from the dbinit.ini file. Alternatively, we can use the Packages object of the JavaAccess extension to access any Java package. For example, to create an instance of a Java class called com.ibm.devworks.ScanProp, we can use:

var myScanProp = new Packages.com.ibm.devworks.ScanProp;

Listing 2 also shows the use of the file I/O extension for FESI. We first create a file object using the SQLcmds property value. We read the content of this file into an EcmaScript String using the readAll() method of the file object. Then we use the split() method of EcmaScript String to split the content into separate lines. We store each line in its own array element -- in an array called Cmds. Finally, we loop through the Cmds array, and call the executeCommand() method on each element of the array. We can now use this DBScriptFlex.es FESI program to create a table and populate data on any RDBMS that supports JDBC.


Working with the Swing GUI

You can use FESI to create interactive GUI applications through the JavaAccess extension's support for the AWT or Swing library. This 23-line program will be the most complex FESI program we will explore in this article -- but you should find it rather straightforward to understand. The source code is in GUIViewer.es and is reproduced below.

First, we get a reference to the javax.swing package using the Packages object of JavaAccess extension. Now we can use Swing.xxx to refer to any classes within this package.

var Swing = Packages.javax.swing;

Next, we connect to the database as we have done previously. This time, we perform the SQL query via the select * from SimpContact statement. We use the executeRetrieval() method of the database object in the FESI database extension for this purpose. This call will return a result set of data that we will iterate through.

var db= new Database("org.gjt.mm.mysql.Driver");
db.connect("jdbc:mysql://192.168.23.38:3306/devworks?user=dbuser");
var res = db.executeRetrieval("select * from SimpContact");

Now we're ready to build the Swing-based GUI. We begin by creating a JTable inside a JScrollPane and adding it to a JPanel. We will create the enclosing frame at the end of the program.

var maxCols = res.getColumnCount();
var dbPanel = new Swing.JPanel;
var dbTable = new Swing.JTable(5, maxCols);
dbPanel.add(new Swing.JScrollPane(dbTable));

Next, using JavaAccess we create a vector of column names, fetched from the metadata of the result set using the getColumnName() method. We then use this vector to set the column headings of the JTable. Note that the result set manipulation method works on a 1-based index, unlike the 0-base index of EcmaScript arrays.

var dbVec = new java.util.Vector;
for (col=0; col<maxCols; col++)
  dbVec.add(res.getColumnName(col+1));
dbTable.getModel.setColumnIdentifiers(dbVec);

We then traverse through every column value of every row in the result set and set the corresponding value in the JTable:

row = 0;
while (res.next()) {
  for (col=0; col<maxCols; col++)
     dbTable.setValueAt(res.getColumnItem(col+1), row, col);
  row++;
}

GUI event handling

Now we are ready to create the JFrame that will become our application frame. We then add the JPanel together with the JTable to the JFrame, and we hook up an event handler for the WindowClosing event. Note the simple syntax. Once the FESI run time receives the WindowClosing event, the EcmaScript code fragment assigned to onWindowClosing will be executed.

var dbFrame = new Swing.JFrame("FESI DB Viewer App");
dbFrame.getContentPane().add(dbPanel);
dbFrame.pack();
dbFrame.setVisible(true);
dbFrame.onWindowClosing = "dbFrame.dispose(); exit();";

If you run the GUIViewer.es script now, you should see the database content as displayed in Figure 2. If you have a problem running it, remember to make sure that the JDBC driver is in the classpath of the VM running FESI.


Figure 2. FESI GUIViewer for RDBMS Data
Figure 2. FESI GUIViewer for RDBMS Data

Integrating the script engine into your applications

Thus far, our discussion has centered around using EcmaScript to simply and easily leverage the APIs of the Java platform. What we have not seen is how we can embed FESI itself into our Java applications. This approach will allow us to:

  • Create Java applications that use a combination of Java and EcmaScript to implement the internal logic. We can easily customize the core logic of these applications during configuration time, or even dynamically modify it during run time.

  • Create Java applications that feature support for EcmaScript as a scripting language -- typically called a scripting host.

You can harness the features of FESI by using the FESI.jslib library package inside the fesi.jar archive. We will be using the following classes:

  • FESI.jslib.JSGlobalObject -- Reference to the global object representing an instance of a FESI interpreter

  • FESI.jslib.JSUtil -- A utility class, including factory method for creating FESI evaluators (interpreter instances)

This sample Java program, called EmbedScript.java, incorporates the functionality of both DBScriptFlex.es and GUIViewer.es internally. It presents a user interface with two buttons, as shown in Figure 3. When you click either button, you execute the corresponding functionality. In fact, the program creates a FESI interpreter internally, and interprets the associated EcmaScript file.


Figure 3. A Java program that embeds FESI
Figure 3. A Java program that embeds FESI

Let's review the source code of EmbedScript.java. First, we need to interface to FESI to import the FESI.jslib package:

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import FESI.jslib.*;

The JSGlobalObject reference, FESIInst, will contain our instance of the FESI interpreter. The two constants we defined earlier correspond to the action logic of the two buttons. We code each of the pieces of logic in EcmaScript:

public class EmbedScript extends Frame implements ActionListener {
  static JSGlobalObject FESIInst = null;
  static final String POPULATE_RDBMS = "DBScriptFlex.es";
  static final String GUIVIEW_RDBMS = "GUIViewer.es";
  private Panel basePanel;
  private Button action1Button;
  private Button action2Button;
  // extensions[] contains the FESI extension that we will load with our 
  // FESI evaluator.
  String[] extensions = new String[] {"FESI.Extensions.JavaAccess",
                                      "FESI.Extensions.BasicIO",
                                      "FESI.Extensions.FileIO",
                                      "FESI.Extensions.Database"};

Next, the constructor builds the GUI, and adds ActionListeners for the two buttons:

  public EmbedScript() {
    super("Java Application with Embedded FESI");
    action1Button = new Button("Create & Populate RDBMS");
    action2Button = new Button("View RDBMS Data");
    action1Button.addActionListener(this);
    action2Button.addActionListener(this);
    addWindowListener( new WindowAdapter() {
       public void windowClosing(WindowEvent ev) { System.exit(0); }
      });
    basePanel = new Panel();
    basePanel.setLayout(new FlowLayout());
    basePanel.add(action1Button);
    basePanel.add(action2Button);
    add(basePanel);
   }

The button action event handler, shown next, uses the runFESI method to run the EcmaScript programs. Note that the program uses the JSUtil class to create a FESI evaluator (interpreter instance) if one does not already exist. The JSUtil class is the factory class for creating FESI interpreters. In this method, we create a BufferedReader and read the entire EcmaScript file into a String, then we pass the String to the FESI evaluator to interpret. This is how you can process EcmaScript within your Java code.

  private void runFESI(String scriptFileName) {
    try {
       if (FESIInst == null_)
            FESIInst = JSUtil.makeEvaluator(extensions);
        BufferedReader inFile = 
          new BufferedReader(new FileReader(scriptFileName)); 
        String scriptCode = ""; 
        String line;
        while ((line = inFile.readLine()) != null) { 
                scriptCode += line; 
        }
       FESIInst.eval(scriptCode);
       } catch (Exception ex) {
       ex.printStackTrace();
    }
  }

Next, the button action event handler simply calls the runFESI() method with the appropriate EcmaScript file name:

  public void actionPerformed(ActionEvent evt) {
    if (evt.getSource() == action1Button) {
        runFESI(POPULATE_RDBMS);
         }
    if (evt.getSource() == action2Button) {
        runFESI(GUIVIEW_RDBMS);
       }
    }
  public static void main (String args[])
    throws Exception
  {
    EmbedScript myFESI = new EmbedScript();
    myFESI.pack();
    myFESI.show();   
  } // of main
} 


Caveat emptor

FESI harnesses the power of the Java platform and delivers it with the simplicity of a scripting language. We can use FESI productively to provide cross-platform solutions to problem areas where the Java language may not be appropriate, but such power can be abused. EcmaScript is not a language designed for writing large programs. The ad hoc style of programming that it encourages, while convenient, can create code that is cryptic, contains side effects, and may be difficult to maintain. This is the essence of quick-and-dirty programming. In other words, practice discretion when using FESI in your project. More specifically, my recommendation is to develop a viable EcmaScript coding guideline internally and to consider rewriting any script that may exceed a page in length in the Java language.


Resources

About the author

Photo of Sing Li

Sing Li is the author of Early Adopter JXTA and Professional Jini, as well as numerous other books with Wrox Press. He is a regular contributor to technical magazines and is an active evangelist of the P2P evolution. Sing is a consultant and freelance writer and can be reached at westmakaha@yahoo.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10561
ArticleTitle=Quick-and-dirty Java programming
publish-date=07032001
author1-email=westmakaha@yahoo.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers