Script Beans?

When we expose Java objects (or other language objects!) to a scripting language, we may want flexibility. Most scripting languages on the Java platform support JavaBean conventions. For example, JavaScript allows property style access for "getXXX" methods. Some language engines (for example, Groovy) treat java.util.Map's specially to provide map.key_name style access. But, we may want more flexiblity in addition to bean conventions. For example, we may want java.sql.ResultSet to be accessed with the natual obj.column_name syntax. But, instead of such ad-hoc special cases, we may want to have a generic way.

Also, while jsr-223 has API support to call a specific script function or method, there is no way to "reflect" on script objects. For example, you can't find out the all methods and properties supported by a specific script object. There is no engine independent way to reflect script objects - you have to use Scriptable interface for Rhino , GroovyObject interface for Groovy and IRubyObject interface for JRuby and so on.

HotSpot Serviceability Agent (SA) is a core dump/hung process debugger for HotSpot JVM. It supports JavaScript based command line interface - much like dbx/gdb's shell-like scripting interface. I had used Mozilla Rhino API directly to implement that -- because it was done before the advent of jsr-223. I am porting this to use jsr-223 API. Previously, I had used Scriptable, ScriptableObject and Function interfaces from Mozilla Rhino. I had changed this to use the following interfaces:


 \* Any Java object supporting this interface can be
 \* accessed from scripts with "simpler" access pattern.
 \* For example, a script engine may support natural
 \* property/field access syntax for the properties exposed
 \* via this interface. We use this interface so that we
 \* can dynamically add/delete/modify fields exposed to
 \* scripts. Also, script engines may expose this interface for
 \* it's own objects.
public interface ScriptObject {
  // special value to denote no-result -- so that
  // null could be used as proper result value
  public static final Object UNDEFINED = new Object();
  // empty object array
  public static final Object[] EMPTY_ARRAY = new Object[0];

   \* Returns all property names supported by this object.
   \* Property "name" is either a String or an Integer".
  public Object[] getIds();

   \* Get the value of the named property.
  public Object get(String name);

   \* Get the value of the "indexed" property. 
   \* Returns UNDEFINED if the property does not exist.
  public Object get(int index);

   \* Set the value of the named property. 
  public void put(String name, Object value);

   \* Set the value of the indexed property. 
  public void put(int index, Object value);

   \* Returns whether the named property exists or not.
  public boolean has(String name);

   \* Returns whether the indexed property exists or not.
  public boolean has(int index);

   \* Deletes the named property. Returns true on success.
  public boolean delete(String name);

   \* Deletes the indexed property. Returns true on success.
  public boolean delete(int index);

 \* This interface is used to represent "function/method" valued
 \* properties in ScriptObjects.
public interface Callable {
   \* Call the underlying function passing the given
   \* arguments and return the result.
  public Object call(Object[] args) throws ScriptException;

Just to be sure: please note that I am thinking of ScriptObject and Callable as two way interfaces. Script engines would expose it's objects (all or some of it's objects) as ScriptObjects. Also, from Java code we can expose ScriptObject objects to script engines and the engines will support special obj.field and obj.method(...) syntax in the respective language.

Why can't we just use BeanUtils and avoid reinventing the wheel? For example, we can probably make use of BeanUtils API to expose objects to scripts and requires jsr-223 script engine implementers to treat DynaBeans specially to provide easier syntax. And optionally expose (some or all of) their own objects with DynaBean interface.

  • beanutils has the notion of DynaClass. Every DynaBean is queried to return it's DynaClass and from the DynaClass we get all properties supported. I think we can (and should?) avoid this when we have to deal with multiple languages. We would like to make minimal assumptions about objects -- allow prototype/delegation languages like JavaScript (where there is no notion of class) and allow for dynamically changing metaclass (like in Groovy, JRuby). So, in the above ScriptObject interface, we query the ScriptObject itself to return it's properties by calling getIds method. That returns the current snapshot of the properties of the object.
  • Also, beanutils API seems to concentrate only on "properties" only. There is nothing mentioned about "methods". (Did I miss anything in BeanUtils API that deals methods?). We have "Callable" - which is wrapper for any script method/function. ScriptObject.get(String name) would return a Callable object for method/function valued properties of the script object.
  • beanutils brings much more than what we need - we want to add just enough abstractions.

The ScriptObject and Callable interfaces could help in inter scripting language communication as well -- there is no need to specify a strongly typed Java interface to communicate b/w two dynamically typed languages (using say, Invocable.getInterface() ). For example, you can access Groovy object from a JRuby script and vice versa and both sides could use natural syntax to access ScriptObjects.Please let me know your comments. We can probably try this out with script engines @


Post a Comment:
Comments are closed for this entry.



« June 2016

No bookmarks in folder


No bookmarks in folder


No bookmarks in folder