Monday, July 23, 2007

Customizable Business Rules using scripting on the Java Platform

Executing Javascript within the JVM has opened up a new avenue to add to your architectural paradigms. In today's enterprise applications lots of code (validations etc.) needs to be run both for the client and the server. Often we find repetitions in the form of Javascript for the client side code and Java for the server side. With server side Javascript being thrown up as an option in the JVM, the same client side code can be executed within the server using the ScriptEngine of Mustang. Rails people will suggest writing DSLs, which can be converted to Javascript and executed within the JVM.

This post is about using the new architectural paradigm in a project which we executed recently for a publishing house. It was a medium sized project where lots of stuff used to be run in VBScript within ASP.NET. The hapless users wanted to migrate to the JVM and sync up the application with the rest of their MIS running on big Java platforms. No additional functionality was needed to be developed. The VBScript code generates lots of SQL using complex business logic, which ran in the .NET server platforms.

Problem #1 : We do not know what the VBScript code does!

This was the first shocker that we received when visiting our client. The developers who had written the code have left - does this ring any bell? The usual problem of migration - and none of the existing MIS managers where prepared to explain us the flow of the business logic as they exist today.

Option #1 : Go dig it out ..

Struggle through the reams of VBScript code (no code comments - we are dynamic!) and enlighten yourself with the existing business process flow. The only thing that we realized was that all those VBScripts were being used to prepare tons of SQL queries that were later executed in the server. And that the logic code had the usual share of complicated if-then-else that sets up the SQL statements.

Problem #2 : We want externalization of business rules that exists today

The users today have the flexibility of manually tweaking SQL queries and minor business logic in the customary VBScript style of operations. And since some of the business rules change quite frequently, they need to have the same flexibility in the new system as well.

Wow! Rhino!

After shuddering in the thought of cost overrun that Option #1 would incur, we decided to look for an alternative. And there it was - the new scripting engine available for executing Javascript within the JVM. Without going into the details of the business logic, we took the route of converting the VBScript code to Javascript and then spitting out the result to the Rhino interpreter. This will also solve Problem #2 as well, since the same business logic will be available to the users today, only on a different scripting platform. The conversion process was made easier by googling some tools crafted by one generous soul. We wrote a script handler ourselves that we could invoke and execute all scripts from within the main Java application ..


boolean loadScriptFile(String fileName) {
  String jsFile = fileName + "." + scriptBean.getScriptExtension();
  InputStream is;
  Reader reader;

  try {
    is = this.getClass().getResourceAsStream(
      scriptBean.getScriptingSourcePath() + jsFile);
    reader = new InputStreamReader(is);
    engine.eval(reader);
    //..
    //..
  } catch (ScriptException e) {
    //.. handle
  }
  //..
  //..
}



The script handler had an initialization code that set the environment up for all executions to take place in the later stages .. we had developed a utility library which also needed to be bootstrapped ..


private void init() {
  ScriptEngineManager engineMgr =
      new ScriptEngineManager();
  ScriptEngine engine =
      engineMgr.getEngineByName(scriptBean.getScriptingEngine());

  try {
    // execute utility library
    InputStream is = this.getClass().getResourceAsStream(
      scriptBean.getScriptingSourcePath() + "defaultScripts.js");
    engine.eval(new InputStreamReader(is));

    // set up values in the state of the ScriptEngine
    engine.put(SCRIPT_RESULT_IDENTIFIER, engineResultList);
    engine.put(SCRIPT_MAP_RESULT_IDENTIFIER, engineResultMap);

  } catch (ScriptException e) {
    //.. handle
  }
}



In the above example snippet, the put() method allows assigning any variable used in the scripting environment from within Java code. Another alternative will be to use Bindings. Suppose we have a list of names in a java variable listOfNames, which we need to provide to a script to form an SQL statement ..


engine.put("namesKey", listOfNames);



and the script to be loaded contains the following ..


function performancePubReg() {
  //.. form the sql
  sqlStr = "select ...
  sqlStr = sqlStr + "where master.name in " + makeString(namesKey);
  //..
}



In course of the application, wherever we needed to execute queries, these sql queries were fetched by load-eval of Javascripts using the script handler implementation.

A Useful Extension to the Architectural Toolbox

In general, this approach proved to be a useful paradigm for small to medium sized migration projects. In an ideal scenario, these customizable business rules which generate SQL queries could be coded up as a DSL for the business users and then appropriately converted to any scripting platform. And the best part is that you need not always dig into the details of legacy code in situations like ours.

1 comment:

Anonymous said...

You may want to have a look at http://jssp.sourceforge.net. JSSP (JavaScript Server Pages) also uses Rhino and features embedded SQL statements and common code sections that execute on the server and in the browser - it's designed to make your business logic easier to maintain.
Leo