Powered By Blogger

Thursday, February 24, 2011

How Javascript integrated with Java

Alfresco JavaScript Debugger

The Alfresco Server provides a built-in server-side JavaScript debugger which allows line by line step through, variable inspection and arbitrary script execution.

Note:

* The debugger executes in the Java VM of the Alfresco Server. It must be executed on the same machine as the Alfresco Server - remote debugging is not supported.
* The debugger does not function correctly on early SunJDK1.6 series JVMs - enabling it will crash the entire JVM.

To enable and disable the JavaScript debugger use the following URL (you must be logged in as an administrator):

http://:/alfresco/service/api/javascript/debugger

This URL displays the current status of the debugger and allows you toggle between enabled and disabled.

When enabled, the JavaScript Debugger window is displayed. Upon execution of any server-side JavaScript, the debugger will intercept and stop execution at the first JavaScript statement. You can then step through, view variables etc or resume execution. When disabled, server-side JavaScript is executed without interruption.


How is Scripting integrated into Alfresco?

The Script Service is a typical Alfresco repository service accessed via a Spring managed bean with the name of ScriptService.

Only developers will be interested in accessing the ScriptService directly, those more interested in simply writing scripts themselves should skip this section and jump to Scripting API.

The available scripting engines can be configured in the Alfresco Spring config file script-services-context.xml. You should download the Alfresco SDK to examine the structure of this file.

As of Alfresco 2.1 a mechanism for adding additional scripting engines has been available. It is possible to completely replace or augment the existing JavaScript implemenation with others such as PHP or Ruby. The correct script engine implementation will be automatically selected by the ScriptService when executing a script based on the file extension of the script being executed. If it cannot resolve the engine to use, then the developer can specify it explicitly when calling the ScriptService.

Further engines can be added by developers. Examining the script-services-context.xml for configuration examples. The engine must implement org.alfresco.service.cmr.repository.ScriptProcessor interface and it is recommend to extend the org.alfresco.repo.processor.BaseProcessor abstract class and override the various execute() methods.

A Java based PHP engine has been integrated and is a good example of this, it is available as an AMP download for addition into an existing Alfresco installation.


Adding Custom Script APIs

It is possible to create and add custom script API's implemented in Java and accessible as root objects in JavaScript. This provides an integration point for Alfresco extensions to provide custom API's where appropriate.

In order to implement a custom JavaScript API it is recommended that you develop a POJO (Plain Old Java Object) that extends the base class org.alfresco.repo.processor.BaseProcessorExtension. The public methods of your class will be those that will be accessable from JavaScript. (For example see org.alfresco.repo.jscript.ScriptLogger).

Once complete, you must then configure your bean in Spring. Make use of the baseJavaScriptExtension parent bean definition in order to ensure your object is automatically registered with the ScriptService framework. The name of the object as it will appear to script writers must also be specified in the bean definition.

The following example shows the bean definition for the ScriptLogger custom API.



logger



Since this is a standard Spring bean definition any additional services that are required can be injected as properties into the bean definition in the usual way.

Once this bean have been loaded the custom API object will be accessible directly in JavaScript by calling the public methods on the named object. For example the following shows how the log method can be called on the logger API we have defined above.

...
logger.log("This is a log message.");
...


Native Java API Access

In some cases you may find that you're unable to satisfy a requirement using the Javascript API, but that a Java API (perhaps the Alfresco Foundation Services API or AVMService for our WCM module) contains facilities that would allow you to do so.

Using the method described in Adding Custom Script APIs, it is possible to add additional root scope objects to Javascript. In the case of the Alfresco Foundation Services API the only object that needs to be injected is the ServiceRegistry (or a proxy of it) - an example implementation of this may be found at Configuring the ServiceRegistry as a Javascript Root Object.

As of Alfresco 2.1.3, Javascript scripts stored in the classpath (e.g. in the file system at the extension directory) can also leverage the Rhino Javascript interpreter's native Java integration facilities (see the Rhino documentation for more details).

However, for security reasons these mechanisms are completely disabled for Javascript scripts that are stored in the repository. This means that the Javascript file has to be stored in the filesystem to be able to access the Native Java API.

Note that there are some important things to keep in mind when calling native Java APIs from Javascript:

* Mixing the standard JavaScript APIs and Alfresco Java APIs is not recommended - the JavaScript API caches some values which may not be reflected when using Java APIs and vice-versa.
* Extreme care must be taken when using certain Alfresco APIs, for example the transaction APIs. This is because in many cases the Alfresco scripting framework includes logic that handles some of these "plumbing" concerns automatically - for example the Javascript script for a Web Script is automatically executed within an Alfresco transaction - programmatic transaction handling within the Javascript is not required and may interfere with the default behaviour.
* Native Java collections (Arrays, Maps, Lists etc.) do not get translated into their Javascript equivalents, so you cannot use Javascript idioms when accessing them. For example, instead of accessing a java.util.Map using code such as:

var value1 = javaMap["key1"]
var value2 = javaMap.key2

You must call the underlying Java methods instead:

var value1 = javaMap.get("key1")
var value2 = javaMap.get("key2")

Please note that these techniques are solely intended for developers who wish to take advantage of the productivity benefits of a scripted language while still having access to the full power of native Java APIs. They should not be used for end user scripting scenarios (eg. end user developed custom actions uploaded to the repository, workflow scripts, etc.).

No comments:

Post a Comment