Embedding ‘Scripting Host’ in Business Applications

Madhukumar Seshadri, www.cognizant.com

With web browsers, continuing to rule the human interface for applications, creating value by being ubiquitous, few adoptions of rich technology that’s getting brewed underneath, will help designing business applications even though most of the current ones are made with bird’s eye of the underlying technologies.

JavaScript is a scripting language invented and developed by Netscape. The language was primarily designed for creating lightweight programming for web browser extensions by exposing the Document Object Model of an HTML page to the scripts. JavaScript is becoming object oriented and getting adopted for server-side scripting.

JavaScript is also becoming a standard in the scripting world as Netscape is working closely with ECMA (European Computer Manufacturers Association) to make it as a standard scripting language for the script world. The standards are published as ECMA Script.

JavaScript originally designed for exposing the DOM (Document Object Model) standardized by World Wide Web consortium (W3C), to help web page designers to control and manipulate the pages dynamically. ‘JavaScript’ engines were embedded in the browsers and they execute those portions of the code embedded in the HTML pages.

In short, JavaScript engine embedded in the browser allowed extensions or manipulations for DOM Object run time for the HTML page by executing the ‘scripts’ associated with them. In other words, browser exposes its DOM object for the page to scripts for extensions and dynamic manipulations of the same, using a language that the script interpreter understands.

Can I do the same for my application by exposing my custom business objects written in my middle-tier? Can I allow user to my write JavaScript extensions for my objects and also be host for executing those scripts?

JavaScript host runs times are available as binaries written in major languages. Check out www.mozilla.org/js. Spider Monkey and Rhino are open source JavaScripting engines available from mozilla.

Microsoft implementation of ECMA Script (ECMA Script is based on core JavaScript, created by Netscape) is called JScript. Microsoft binaries of jscript engine can be downloaded from http://msdn.microsoft.com/scripting/.

This document doesn’t explain the JavaScript language in detail but explains how these scripting engines can be used as host to expose business objects in the middle-tier and how the user of these applications can extend it if needed using JavaScript.

The scripting engine Rhino (www.mozilla.org/rhino), a javascript engine purely written in Java is one that I am going to use for the testing the above.

Let us set some simple goals,

- Execute a plain ‘JavaScript’ code and use static Java object
- Instantiate external objects (written in Java) from the script and use them within the script

Let us write a simple Javscript to test the above set goals,

Fig 1 – jshosttest.js

/* Test 1 */
/* Use a static Java Object in the script */

function test1() {
var str;
str = '"Hello World";
return str;
}

var str = test1( );
//out is expected to be Java Object exposed to the script scope
out.println ("JavaScript - Test 1 - " + str);

/* Test 2 */

/* Instantiate a Javaobject for this scope and use it */

function test2(){
// create a Java string buffer object from JavaScript and use its java instance
// This uses an another Java object created for creating new objects within Java and
// brings the same for JavaScript execution scope
// Refer _create.java for more information
create.getInstance("java.lang.StringBuffer","buffer");
//JavaScript refers the java object instance as ‘buffer’
out.println(buffer.toString());
buffer.append("I am a javaobject dynamically created and executed in JavaScript");
return buffer.toString();
}

var str = test2();
out.println("From JavaScript - Test 2 " + str);

Let us write a simple Javahost Object using the Rhino engine to execute the above script,

Fig 2.1 - JSHost.java

/**
* @author Madhukumar Seshadri
* @version
*/

import org.mozilla.javascript.*;
import java.io.*;
import java.lang.*;
// import com.xxx.xxx.*;

public class JSHost extends Object {
/** Creates new JSHost */
public JSHost() {
}

/*** Executes .js file ***/
public Object executeJS (String jsfname){
//You can also use evaluateReader
File fp = new File(jsfname);
String str_buff =null;
try {
FileReader fr = new FileReader(jsfname);
int length = (int) fp.length();
char cbuff[] = new char[(char)length];
fr.read(cbuff);
str_buff = new String(cbuff);
} catch(Exception e) {
e.printStackTrace();
}

//Execute the .js file content
return executeJSSource(str_buff);
}

/*** Executes javascript source ***/
public Object executeJSSource (String jsbuff){
Object any=null;
try{
//Enter the Context
// Refer http://www.mozilla.org/js/rhino/tutorial.html
Context context = Context.enter();
// Get the execution scope
Scriptable scope = context.initStandardObjects(null);

//----------- For Test 1 - Get System.out in scope
//Scriptable jObj1 = Context.toObject(System.out, scope);
scope.put("out", scope, jObj1);

//------------ For Test 2 - Instantiate Create Object and get that in scope
//Allow JScript to create Java Objects
//Bring the _create object to context
_create create = new _create( );
//Register this context and scope to this create object instance
create.registerContext(context,scope);
//Scriptable jObj2 = Context.toObject(_create, scope);
scope.put("create",scope,create);
//Evaluate (or execute js)
//Refer http://www.mozilla.org/js/rhino/tutorial.html
any = context.evaluateString(scope, jsbuff, "", 1, null);
//Exit the Context
context.exit( );
}

catch ( JavaScriptException jse) {
jse.printStackTrace();
}

return any;

}

}

Let us write a class for creating new Java objects and bringing them to this script execution scope,

Fig 2.2 – _create.java

/**

* @author Madhukumar

*/

import java.lang.Class;

import org.mozilla.javascript.*;

public class _create extends Object {

static Context ptr = null;

static Scriptable scope =null;

public _create () { }

public void registerContext(Context cptr, Scriptable sc){

ptr = cptr;

scope = sc;

}

public void getInstance(String classname,String jsclassname) {

Object any=null;

try {

Class thisclass = Class.forName(classname);

any = thisclass.newInstance();

}

catch(Exception e){

e.printStackTrace();

}

if( ptr != null) {

if (scope !=null) {

//register created object for this execution scope

scope.put(jsclassname,scope,any);

}

}

}

}

It is time to test the code, so let us write a small object that will use the JSHost object,

Fig 3 - JSHosttest.java

/**

* @author Madhukumar

* @version

*/

public class JSHosttest extends Object {

/** Creates new JSHostTest*/

public JSHosttest() {

}

public static void main (String args[]){

if (args.length < 1) {

System.out.println("Usage - Java JSHosttest.class <js source file>");

return;

}

JSHost jsh = new JSHost();

System.out.println("Executing JavaScript file - " + args[0]);

Object result = jsh.executeJS(args[0]);

if (result instanceof String){

System.out.println("Results - " + result);

}

}

}

For more explanations on the code execution, please refer embedding tutorial http://www.mozilla.org/js/rhino/tutorial.html and for all documentation and examples on Rhino visit http://www.mozilla.org/rhino/doc.html.