« October 2005 »
SunMonTueWedThuFriSat
  
3
5
6
7
11
13
16
17
20
22
24
26
27
28
30
   
       
Today
XML

Blog::Navigation

GetJava Download Button
Get the Source
Personal Blog

Blog::Referers

Today's Page Hits: 1042

Powered by Roller Weblogger.
« Previous month (Sep 2005) | Main | Next page of month (Oct 2005) »
20051129 Tuesday November 29, 2005

Continuations in JavaScript

Rhino JavaScript engine (which is included in Mustang) supports Continuations. Rhino optimizer needs to be switched off to enable continuations -- i.e., scripts are run in interpreted mode and Rhino does not generate bytecodes for scripts. In Mustang, Rhino optimizer has been removed -- so Rhino in Mustang runs in interpreter mode always. So, continuations are enabled always.



( Nov 29 2005, 05:28:21 PM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051125 Friday November 25, 2005

finals in JavaScript

Java has final fields, parameters and local variables. And JavaScript 2.0 has const modifier. But, there is no support for finals or consts in JavaScript 1.x (although const and final are reserved words). We will see how we can implement equivalent of finals in JavaScript (in Mustang's JavaScript engine).



function finals() {
    var map = new Object();
    return new JSAdapter() {
        __put__ : sync(function (name, value) {
            // already exists, do not allow assignment again
            if (name in map) {
                throw "can't modify final field";   
            }
            // does not exist in map, allow first assignment
            map[name] = value;
        }),
        __get__ : sync(function (name) {
            return map[name];
        }),
        __has__ : sync(function (name) {
            return name in map;
        })
     }
}


Note that the sync built-in is used to wrap the methods so that the object returned by finals function is MT-safe. Let us assume that the above code is stored in a file named "finals.js". Now, we can start jrunscript using the following command line:

jrunscript -f finals.js -f -

The interactive jrunscript session is shown below:

js> var constants = finals()
com.sun.script.javascript.JSAdapter@a8c488
js> constants.x = 3
3.0
js> constants.x = 34

script error: sun.org.mozilla.javascript.internal.EvaluatorException: can't modify
final field (finals.js#7) (<STDIN>#1) in <STDIN> at line number 1

js> constants.y = 4
4.0
js> constants.y
4.0
js> constants.x
3.0
js> constants.y = 5656

script error: sun.org.mozilla.javascript.internal.EvaluatorException: can't modify
final field (finals.js#7) (<STDIN>#1) in <STDIN> at line number 1

js> quit()

A finals objects like 'constants' may be used with JavaScript with statement so that the properties may be accessed like variables (without the obj.field syntax).



( Nov 25 2005, 06:27:20 PM IST ) Permalink Comments [2] del.icio.us | furl | simpy | slashdot | technorati | digg

20051123 Wednesday November 23, 2005

Using jhat to analyze finalizer queue

Java objects of classes that override Object.finalize() method put lot of pressure on garbage collector. There was a nice talk on finalization in JavaOne 2005 (TS-3281). Important advice in this talk was avoid finalize() if you can!

But, you may have an application (or use a library) that uses finalize() methods. So, you may want to analyze your heap and see which objects, if any, are queued for finalization. Using jhat in Mustang build 61+, you can analyze finalizer queue. When you run jhat against your heap dump, there is a link called Show finalizer summary in the first page. If you click on this link, you can see histogram of objects waiting for finalization. i.e., it shows classwise count of objects pending finalization. Also, OQL now supports a method called finalizables on the built-in heap object. This method returns an iterator for objects pending finalization. The resulting iterator may be used with built-in functions operating on arrays/iterators.



( Nov 23 2005, 10:24:58 AM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051121 Monday November 21, 2005

SCRIPT tag for Swing HTML?

With Mustang, Rhino JavaScript engine is included in JRE. Now, do you want <SCRIPT> support (and DOM) for Swing's JEditorPane? If so, please take the survey at http://java.sun.com/webapps/survey/display?survey_id=5370. And how about using javax.script framework, so that we can have support for language="<your-favorite-language>" and not just JavaScript (assuming ofcourse there will JSR 223 compliant script engine for your favorite language!).



( Nov 21 2005, 11:57:39 AM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051119 Saturday November 19, 2005

using namespace in JavaScript

This is continuation of my earlier post on namespaces. We saw how to implement namespace for JavaScript. How about a feature equivalent to Java's import (or C++ using)? We can implement something like:


/**
 * imports all or subset of names from given namespace.
 *
 * @param ns namespace
 * @param patten names matching this pattern will be imported
 * pattern is optional. If not specified, all names are 
 * imported (like Java's import package_name.*).
 */
function using(ns, pattern) {    
    if (pattern == undefined) {
        // import all
        for (var name in ns) {
            this[name] = ns[name];
        }
    } else {
        if (typeof(pattern) == 'string') {
            pattern = new RegExp(pattern);
        }
        // import only stuff matching given pattern
        for (var name in ns) {
            if (name.match(pattern)) {
                this[name] = ns[name];
            }
        }       
    }
}

Sample script that uses namespace and using:

var ns = namespace("function p() { print('p'); }; function s() { print('s'); }");
ns.p();    // call 'p' in namespace 'ns'
using(ns); // import all from namespace 'ns'
p();       // call ns.p()
s();       // call ns.s();

Another script sample that uses imports subset:

var ns = namespace("function p() { print('p'); }; function s() { print('s'); }");
ns.p();           // call 'p' in namespace 'ns'
using(ns, /p.*/); // import only the names matching 
p();              // call ns.p()
ns.s();           // call ns.s() -- but can only use qualified name

Thanks Alex. I had a typo (missing double quote) in namespace call



( Nov 19 2005, 10:50:50 AM IST ) Permalink Comments [1] del.icio.us | furl | simpy | slashdot | technorati | digg

20051118 Friday November 18, 2005

Namespaces in JavaScript

It is difficult to work with independently developed JavaScripts. There is no built-in Java "package" (or C++ "namespace")-like feature in JavaScript. You have to use script objects for everything. Yes, there is a namespace proposal in JavaScript 2.0. But, what do we do now?

Well designed JavaScript APIs (like TrimPath API) avoid polluting global namespace by exposing API as methods in few (may be just one!) objects. But, most of the useful JavaScript code out there may not have been written in that style. There will be many global variables and functions. You may still find those scripts useful. It is not always feasible or desirable to edit such scripts to avoid global namespace pollution. How about a simple namespace solution for JavaScript (in the context of Mustang's JavaScript engine)?


function namespace(code) {
    // new global scope for the new namespace
    var myGlobals = new Packages.javax.script.SimpleBindings();

    // use 'engine' variable to execute code with
    // new global scope

    // 'engine' variable is of type javax.script.ScriptEngine    
    // This is exposed in jrunscript tool. But, it is easy to expose this
    // from your Java programs as well. Please refer below...
    engine.eval(code, myGlobals);

    // return a wrapper object that exposes
    // globals of the new namespace 

    return new JSAdapter() {
        __has__: function (name) {
            return myGlobals.containsKey(name);
        },
        __get__: function (name) {
            return myGlobals.get(name);
        },
        __getIds__ : function () {
            return myGlobals.keySet().toArray();
        }
    }
}

Exposing engine variable from Java code


      ScriptEngineManager m = new ScriptEngineManager();
      ScriptEngine engine = m.getEngineByName("js");
      // add a global variable called "engine"
      engine.put("engine", engine);

With the above definition of namespace (I'd have preferred 'package', but that is a JavaScript reserved word!), we can write:

var ns = namespace('var s = 3; function hello() { print('hello'); }');

// print 3 - value from namesapce
print(ns.s); 

// print 'hello' by calling hello in namespace ns
ns.hello();  

For better package/namespace level encapsulation, we may want to expose only functions from the namespace and hide all package-level variables. It is easy to modify above namespace function as shown below:


function namespace(code) {
    var myGlobals = new Packages.javax.script.SimpleBindings();
    engine.eval(code, myGlobals);

    return new JSAdapter() {
        __has__: function (name) {
            // do not expose non-function type stuff!
            var res = myGlobals.containsKey(name);
            if (res) {
                return typeof(myGlobals.get(name)) == 'function';
            }
        },
        __get__: function (name) {
            // do not expose non-function type stuff!
            var val = myGlobals.get(name);
            return typeof(val) == 'function'? val : undefined;
        },
        __getIds__ : function () {
            return myGlobals.keySet().toArray();           
        }
    }
}

Now, we have namespace. What about namespace using or package import-like feature? Well, that is a topic for another blog or an exercise to reader :-)



( Nov 18 2005, 11:05:04 AM IST ) Permalink Comments [1] del.icio.us | furl | simpy | slashdot | technorati | digg

20051115 Tuesday November 15, 2005

Synchronized methods in JavaScript

How do we write synchronized methods in JavaScript? Rhino JavaScript shell supports a global function called sync. sync function creates a synchronized function from an existing function. The new function synchronizes on the "this" object of its invocation. Scripts executing in the shell have access this sync function. But, in Mustang's JavaScript engine, the sync function is available always (i.e., global scope is always initialized with this function).


var obj = { f: sync(function () { 
                     print('I am synchronized!');
               }
          };

// 'f' is a "synchronized" method.
obj.f(); 

If you just need mutual exclusion, then sync function is enough. But, what if you need wait, notify and notifyAll? We can add these as function properties to Object.prototype as shown below:

Object.prototype.wait = function() {
     var objClazz = java.lang.Class.forName('java.lang.Object');
     var waitMethod = objClazz.getMethod('wait', null);
     waitMethod.invoke(this, null);
}

Object.prototype.notify = function() {
     var objClazz = java.lang.Class.forName('java.lang.Object');
     var notifyMethod = objClazz.getMethod('notify', null);
     notifyMethod.invoke(this, null);
}

Object.prototype.notifyAll = function() {
     var objClazz = java.lang.Class.forName('java.lang.Object');
     var notifyAllMethod = objClazz.getMethod('notifyAll', null);
     notifyAllMethod.invoke(this, null);
}

We have added wait, notify and notifyAll to all JavaScript objects. Note that you can call the above methods only inside methods wrapped by sync. Or else you'll get IllegalMonitorStateException.

If you need multiple condition queues, read-write locks, semaphores, barriers and so on, then you can use java.util.concurrent API in JavaScript. (please refer to my earlier post )



( Nov 15 2005, 06:35:36 AM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051114 Monday November 14, 2005

java.net project to specify heap dump file format

As you may know already, Mustang's jmap utility can create heap dump from a live process or core dump. Also, hprof profiler can create heap dump with or without allocation site information. Heap dumps are binary format files that contain snapshot of Java heap. Mustang's jhat (Java Heap Analysis Tool) tools can be used to view and analyze the Java heap dumps. jhat includes interesting features such as OQL - Object Query Language.

Kelly Ohair has created a java.net project (https://heap-snapshot.dev.java.net/) to specify new Java heap dump file format. Please participate in this project and give us your feedback and suggestions.



( Nov 14 2005, 07:05:38 PM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051112 Saturday November 12, 2005

Configuration (Java)Scripts

Many Java applications use various configuration files. These are mostly of the form

With Mustang, JavaScript engine is part of JRE. With Mustang, Java programmers can make use of JavaScript files as "config" files. It is easy very easy to use ScriptEngine.eval method to parse the config file. The idea of using "scripting" language(s) for configuration is nothing new (for example, XEmacs uses Lisp)

For each "config parameter", you can define and initialize a script global variable in your script file. ScriptEngine.get method can be used to get the config parameter value from Java. If you want to be able to use your config. file(s) by other systems/programs without access to JavaScript engine as well, then you may want to consider JSON - JavaScript Object Notation. Because JSON uses subset of JavaScript object literal syntax -- so you can continue to use JavaScript engine to parse config file(s) in your Java programs.



( Nov 12 2005, 07:20:52 PM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051110 Thursday November 10, 2005

Easy-to-use XPath API for JavaScript

Yes, E4X is not included in Mustang's Rhino JavaScript engine. But, we have rich XML API in JDK. We can easily use Java API to create reusable JavaScript functions for XML. We will see how we can write nice XPath scripting API using javax.xml.xpath package.



function XPath(expr) {
    var global = XPath.__parent__;
    
    function isJSName(qname) {
        return qname.namespaceURI == 'http://www.mozilla.org/rhino/';
    }

    // We create a XPathVariableResolver that resolves
    // variables names as script global variables.
    function varResolver() {       
        return new Packages.javax.xml.xpath.XPathVariableResolver() {
            resolveVariable: function(qname) {
                if (isJSName(qname)) {
                    return global[qname.localPart];
                }
                return null;
            }
        }
    }  

    // We create a XPathFunctionResolver that resolves
    // function names as script global functions.
    function funcResolver() {
        return new Packages.javax.xml.xpath.XPathFunctionResolver() {
            resolveFunction: function(qname, arity) {
                // ignore arity...
                if (isJSName(qname) && 
                    typeof(global[qname.localPart]) == 'function') {

                    // implement XPathFunction that calls the underlying
                    // JavaScript function
                    return new Packages.javax.xml.xpath.XPathFunction() {
                        evaluate: function(argsList) {
                            var func = global[qname.localPart];
                            var argsArray = argsList.toArray();
                            
                            var args = new Array(argsArray.length);
                            for (var i in argsArray) {
                                args[i] = argsArray[i];
                            }                            
                            return func.apply(global, args);
                        }
                    }
                }
                return null;
            }
        }
    }

    // create a NamespaceContext
    // we map mozilla URL to "js" prefix
    function namespaceCtx() {
        return new Packages.javax.xml.namespace.NamespaceContext() {
            getNamespaceURI: function(prefix) {
                switch (prefix) {
                    case "xml":
                        return "http://www.w3.org/XML/1998/namespace";
                    case "xmlns":
                        return "http://www.w3.org/2000/xmlns/"
                    case "js":
                        return "http://www.mozilla.org/rhino/";
                    default:
                        return "";
                }
            },

            getPrefix: function(uri) {
                switch (uri) {
                    case "http://www.w3.org/XML/1998/namespace":
                        return "xml";
                    case "http://www.w3.org/2000/xmlns/":
                        return "xmlns"
                    case "http://www.mozilla.org/rhino/":
                        return "js";
                    default:
                        return "";
                }
            }
        }      
    }

    var xpath = Packages.javax.xml.xpath.XPathFactory.newInstance().newXPath();
    xpath.setXPathVariableResolver(varResolver());
    xpath.setXPathFunctionResolver(funcResolver());
    xpath.setNamespaceContext(namespaceCtx());
    return xpath.compile(expr);
}

function XMLSource(file) {
    return new Packages.org.xml.sax.InputSource(java.io.FileInputStream(file));
}


We'll use the following simple XML file ("t.xml") as example:

    <html>
    <head>
    <title>hello</title>
    <body>
    <img src="mustang.jpg"></img>
    </body>
    </head>
    </html>

Using the above XPath and XMLSource functions, we can write something like:



function toUpper(s) {
    return s.toUpperCase();
}

var src = XMLSource("t.xml");
var xp = XPath("js:toUpper(/html/head/title)");

print(xp.evaluate(src)); // prints "HELLO".

In the above code, we make use of user defined JavaScript function toUpper inside XPath expression. We can easily write useful script functions and call the same in XPath expressions. I've 'js' namespace prefix for JavaScript functions and variables. So, you need to use the prefix "js:" to call script functions. This prefix may be changed by editing XPath function given above. Also, as a convention, I've used Rhino URL as URI for "js" prefix.



( Nov 10 2005, 07:53:05 PM IST ) Permalink Comments [2] del.icio.us | furl | simpy | slashdot | technorati | digg

20051109 Wednesday November 09, 2005

Thread local storage in JavaScript

JSR 223 API allows exposing multliple scopes (called "Bindings") to scripts. ScriptEngine allows one or more Bindings to be specified while evaluating scripts via ScriptContext. Script engine will search these "scopes" while looking for global variables.

How can we have thread local storage in JavaScript?

Java program that executes JavaScript (by eval) can expose different ENGINE_SCOPE bindings for each Java thread. This way the script global variables accessed from script executing in different Java threads would be different.

But, what if the script itself creates Java thread(s)? In that case, script can make use of java.lang.ThreadLocal directly as shown below:



var tls = (function () {
    var map = new Object();
    return new JSAdapter() {
        __has__: function(name) {
            return map[name] != undefined;
        },
        __get__: function(name) {
            if (map[name] != undefined) {
                return map[name].get();
            } else {
                return undefined;
            }
        },
        __put__: sync(function(name, value) {
            if (map[name] == undefined) {
                var tmp = new java.lang.ThreadLocal();
                tmp.set(value);
                map[name] = tmp;
            } else {
                map[name].set(value);
            }
        }),
        __delete__: function(name) {
            if (map[name] != undefined) {
                map[name].set(null);
            }            
        }
    }
})();


With the above code, we can write:


tls.x = 10;
print(tls.x);    // prints 10


// create another thread and start it
var th = new java.lang.Thread(
            function () { 
                tls.x = 'hello'; 
                print(tls.x); // prints 'hello'
            });
th.start();

print(tls.x); // prints 10 -- value is thread local.


We can easily create multiple thread local variables by adding more properties to "tls" object. Also, we can use "tls" object in JavaScript with statement so that the scope uses thread local variables:


   with(tls) {
      print(x); // prints thread local x in each thread
   }




( Nov 09 2005, 07:28:00 PM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051108 Tuesday November 08, 2005

Multiple inheritance in JavaScript

We saw similarities between Self and JavaScript in my post titled Self, JavaScript and JSAdapter. One important difference between JavaScript and Self is that Self supports multiple inheritance. Self supports more than one parent slots per object. Note that parent slot of Self is similar to JavaScript's prototype (settable by __proto__ property). But, in JavaScript, there is only one rototype per object. So, how do we have multiple inheritance in JavaScript? We can use JSAdapter. We will code a JSAdaper based 'extend' function for multiple inheritance (I'd have preferred 'extends' -- but that is a reserved word in JavaScript!)



// first parameter is the object extended.
// rest of the parameters are 'super' objects.
// or in Self terminology, multiple 'parent' slot values.

function extend(object /*,proto1, proto2, ....*/) {
    if (arguments.length < 2) {
        throw "should extend from atleast one proto";
    }

    // collect all prototypes in an array
    var protos = new Array(arguments.length - 1);
    for (var i = 1; i < arguments.length; i++) {
        protos[i-1] = arguments[i];
    }

    // save the first param as 'self'
    var self = object;

    self.__proto__ = new JSAdapter() {

        // check the property in all prototypes
        __has__: function(name) {
            for(var i in protos) {
                if (name in protos[i]) {
                   return true;
                }
            }
            return false;
        },

        // check the property in all prototypes
        __get__: function(name) {
            for(var i in protos) {
                if (name in protos[i]) {
                    var p = protos[i];
                    var prop = p[name];
                    // treat function type properties differently
                    if (typeof(prop) == 'function') {
                        // we need to make the 'this' to be the 'self'
                        return function() { 
                            return p[name].apply(self, arguments); 
                        }
                    } else {
                        return prop;
                    }
                }
            }   
            return undefined;
        },
        __put__: function(name, value) {
            for(var i in protos) {
                if (name in protos[i]) {
                    protos[i][name] = value;
                    return;
                }
            }
        },
        __delete__: function(name) {
            for(var i in protos) {
                if (name in protos[i]) {
                    delete protos[i][name];
                    return;
                }
            }   
        },
        __getIds__: function() {
            var res = new Array();
            for (var i in protos) {
               for (var j in protos[i]) {
                   res[res.length] = j;
               }
            }
            return res;
        } 
    }
    return self;
}


Using the above extend function, we can use multiple inheritance as shown below:


var x = { b : 10, f: function() { print(this.b); } }
var y = { a : 12, add: function() { return this.b + this.a } }

var m = new Object;
extend(m, x, y);
print(m.b);      // prints 10
print(m.a);      // prints 12
m.f();           // prints 10 by calling x.f
print(m.add());  // prints 22!
m.a = -2;       
print(m.add());  // prints 8!


Wow! We really got multiple inheritance in JavaScript! Now, you may want to write reusable mixins in JavaScript!!



( Nov 08 2005, 02:17:30 PM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051104 Friday November 04, 2005

Field assignment watch in JavaScript

JavaScript supports watch method for every script Object. This method adds a watchpoint to a property of the object. Whenever a value is assigned to the mentioned property, it calls up a handler function allowing you to watch the new value assigned. If required, you can even alter the value assigned. But, this is supported only in SpiderMonkey, but not in Rhino. As you may know already, Mustang includes Rhino. So, what do we do? Well, we can use JSAdapter to implement a similar feature.


// creates a wrapper for a given object. The wrapper
// objects passes field access/assignment to the wrapped
// object except for the watched property. For the watched
// property, the wrapper calls a callback function.

function WatchWrapper(obj, prop, callback) {

    return new JSAdapter() {
       __has__: function(name) {
           return (name in obj);
       },
       __get__: function(name) {
           return obj[name];
       },

       // if property assigned is the watched property
       // then, call the callback function. Otherwise
       // just assign the field value.
       __put__: function(name, value) {
           if (name == prop) {
               callback(obj, name, value);
           } else {
               obj[name] = value;
           }
       }
    };
}

Let us say you want watch assignment to salary field in Employee objects:

function Employee(nm, sal) {
    this.name = nm;
    this.salary = sal;
}

We can change the above code as below:


function salaryWatcher(emp, name, newSal) {
    if (emp.name == 'sundararajan') {
        emp.salary = newSal * 100; // make it 100 times :-)
    } else {
        emp.salary = newSal;
    }
    return emp.salary;
}

function Employee(nm, sal) {
   return WatchWrapper(
       { name: nm, salary: sal },
       "salary",
       salaryWatcher);   
}

Note that the call sites that use Employee constructor need not change at all (whether or not field modification is watched). We can remove the watch by simply reverting back to the original code of the Employee function.



( Nov 04 2005, 01:28:13 PM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051102 Wednesday November 02, 2005

Encapsulation in JavaScript

In JavaScript, all members (fields and methods) of any object are public. There is no equivalent of private, protected access of Java. What if we want to have encapsulate fields in JavaScript? The trick is to use Closures. If you know what closures are, then skip next section.

Closures in JavaScript

In JavaScript, a nested function can access locals, arguments of enclosing function (can even assign to those -- unlike methods of Java anonymous/local class. Java anonymous/local class methods can only access 'final' locals and arguments of the enclosing method)

function f(a) {
  function g() {
      // we access argument 'a' of enclosing function 'f'
      return a++;
  }
  // we return the function 'g' here 
  return g;
}

x = f(10);
print(x()); // prints 10
print(x()); // prints 11
print(x()); // prints 12

In the example, 'x' acts as generator of sequence with 10 as starting point

Using closure to implement private fields

You may want to read Douglas Crockford's "Private Members in JavaScript" to see how closures are used to implement private instance fields.

Example with private instance fields:

function Employee(name, salary) {
    // name and salary are private instance fields
    this.getName = function() { 
        return name;
    };    
    this.getSalary = function() {
        return salary;
    };
    this.setSalary = function(amount) {
        salary = amount;
    };
    this.increaseSalary = function(increment) {
        if (increment < 0.0) {
            throw "Huh! can't decrement!";
        }
        salary += increment;
    };
}

var e = new Employee('Mr. X', 10000);
print(e.getSalary());
e.increaseSalary(1000);
print(e.getSalary());
e.increaseSalary(-100);


The above looks lot similar to the way objects are defined in the programming language E.

Okay, now we have got private field and getter/setter methods or getter method only (for read-only field such as name in the above example). But, the caller has to use method calls instead of 'natural' field access syntax. What if we want to have field access like syntax and still encaptulate the access by getter/setter? Yes, we can use JSAdapter



function GetterSetterWrapper(obj) {

   return new JSAdapter() {
       __has__ : function(name) {
           // check whether obj has the property or getter method
           return name in obj ||
                  typeof(obj['get' + name]) == 'function';
       },
       __get__: function(name) {
           if (name in obj) {
               return obj[name];
           } else {
               var getter = 'get' + name;
               // if there is getter method, then call it
               if (typeof(obj[getter]) == 'function') {
                   return obj[getter]();
               } else {
                   return undefined;
               }
           }
       },
       __put__: function(name, value) {
           var setter = 'set' + name;
           // if there is setter method, call it
           if (typeof(obj[setter]) == 'function') {
               return obj[setter](value);
           } else {
               // just set the property
               return obj[name] = value;
           }
       }
   }
}


With above wrapper function, we can write:


// wrap the employee object with get-set wrapper
var e = GetterSetterWrapper(new Employee('Mr. X', 10000));

print(e.getSalary());

// or equivalently
print(e.Salary);

e.increaseSalary(1000); 
// or equivalently
e.Salary += 1000;

print(e.getSalary());  
// or equivalently
print(e.Salary);

e.increaseSalary(-100);
// or equivalently
e.Salary -= 100;


Ah! It looks like we can have the cake and eat it as well! Note: I've not implemented JavaBean convention for property names (Note the use of e.Salary instead of e.salary). As usual, that is left as an exercise to reader :-) (hint: modify __get__, __put__ and __has__ methods above to implement JavaBean convention).



( Nov 02 2005, 11:52:06 AM IST ) Permalink del.icio.us | furl | simpy | slashdot | technorati | digg

20051101 Tuesday November 01, 2005

Implementing Java interfaces in JavaScript

With Mustang's JavaScript engine, there are atleast 4 ways to implement Java interfaces in script:

  1. JavaAdapter function
    
        var r = new JavaAdapter(java.lang.Runnable,
                   { 
                       run: function() { print('hello'); } 
                   });
        r.run();
    
    
    This feature may also be used with Java anonymous class-like syntax as shown below:
    
        var r = new java.lang.Runnable() { 
                   run: function() { 
                       print('hello') 
                   }
                };
        r.run();
    
    
    Unlike, Rhino's JavaAdapter, Mustang's JavaAdapter does not support implementing multiple Java interfaces. Also, extending a super class is not allowed.
  2. using engine variable in jrunscript (or exposing ScriptEngine object as global variable and using it in script)
    
        var x = { run: function() { print('hello') } }
        var r = engine.getInterface(x, java.lang.Runnable);
        r.run();
    
    
  3. pass script function whenever a Java interface type parameter is required! (this works from Rhino 1.6R2 onwards -- so, you have to wait for Mustang build 59 or above for this)
    
        function run() { 
            print('hello'); 
        }
        // script function is wrapped automatically
        var t = new java.lang.Thread(run); 
        t.start();   
    
    
    This feature works only if all the methods of the interface have the same signature or there is only one method in the interface. For interface with multiple methods, script function receives the name of the method as first parameter.
  4. using JSAdapter and JavaAdapter.
    
        function Invoker(obj) {
            return new JSAdapter() {
                __has__: function(name) {
                    return true;
                },
                __get__: function(name) {
                    return function() {
                        return obj.invoke(name, arguments);
                    }
                }
            }
        }
    
        var r = { 
               invoke: function(name, args) { 
                   print(name + " called"); 
               }
            };
        var x = new JavaAdapter(java.lang.Runnable, Invoker(r));
        x.run(); // this calls r.invoke('run');
    
    
    This scheme works even if interface methods don't have identical signature. But, invoke method has to handle variable number of arguments passed as args array (second argument). The first argument is the name of the interface method.



( Nov 01 2005, 08:33:22 AM IST ) Permalink Comments [1] del.icio.us | furl | simpy | slashdot | technorati | digg

Copyright (C) 2005, A. Sundararajan's Weblog