• Print options
Home > Learning Center > NSF Classloader - Execution of code stored in NSF databases

NSF Classloader - Execution of code stored in NSF databases

ShowTable of Contents

Introduction


This article covers two XPages2Eclipse components that basic users of the product will most probably never see and never use: an alternative JavaScript engine for code execution and the so called NSF classloader.
 
If you develop a standard XPages application, using Dojo and the "XSP" client side JavaScript library, and you want to access the XPages2Eclipse API in your application (e.g. when the user clicks a button) you can simply drag the XPages2Eclipse control from Domino Designer's control palette into an XPage and access the "X2E" bean in server side JavaScript code.
This process is described in detail in the Getting started tutorial.
 
In this simple scenario, your SSJS code and any custom Java code called from JavaScript are loaded by the XPages runtime and its own classloader (that reads the code from the database design) and executed by the XPages JavaScript engine.
 
Unfortunately, letting the existing XPages runtime do all the work is not a solution for more advanced use cases.This article describes a technique that you can use in those situations.

Use cases


When we were designing the XPages2Eclipse product, our main goal was to provide a way for XPages developers to leverage Notes Client APIs in their XPages applications. Being able to call Notes Client APIs from an XPages application solves about 70-80% of the use cases. But there are situations, were your code needs to be called by the framework.
Typical uses cases for such a callback scenario are:
Our idea was to store this callback code in SSJS library design elements. The benefit for you is that you can use the standard JavaScript editor in Domino Designer to create the libraries. There is no need for an alternative IDE or code editor.
 
Please note that this article only contains technical information and no sample application. Refer to the links above to get an idea how to use this feature for your own purposes.

Implementation

IBM does not provide programming interfaces for their own JavaScript engine that we could use to execute code stored in SSJS libraries without having the UI of an XPages application visible on screen. There even is no public API to be able to read the code from the database design. So we needed to develop our own solution for JavaScript code execution and code/resource loading from NSF.

Our goal was to mimic the XPages runtime as closely as possible. You should not only be able to write your application code in JavaScript, but also to call any Java classes and read file contents that are stored in the database design (e.g. via the Java perspective of Domino Designer) and referenced in the NSF project's build path.

JavaScript engine


We picked the Rhino JavaScript engine for the JavaScript execution. It's a very mature Java based Open Source engine that supports the JavaScript 1.7 standard with ongoing work for 1.8 compatibility.
While most of the standard SSJS code will just work under the Rhino engine, there are a few things that will not work as expected. The following sections will highlight the most relevant differences.

Typed variable declaration


Rhino does not yet support the declaration syntax for strongly typed variables, e.g.
var txt:String="Hello world!";

You need to remove the variable type to make the code snippet work in both engines:
var txt="Hello world!";

Scope access


Since the JavaScript code is not executed in the context of an XPages application, there is no access to JSF/XPages scope maps like sessionScope, viewScope, requestScope or applicationScope.

However for some use cases there are user-based cache maps available, e.g. if you are handling web requests, you can store user related data in the HttpSession retrieved by calling HttpServletRequest#getSession().

Another cache map has an even more global scope than the ones provided by IBM: the PlatformScope.
var platformScope=com.mindoo.xpages2eclipse.tools.PlatformScope.getInstance();
platformScope.put("akey", "avalue");

Data stored in the PlatformScope map will persist until the JVM of the client/server is restarted.
The scope is accessible from normal SSJS code and code running in the Rhino/NSF Classloader context and can be used to share data across the system.

Please note that storing large amounts of data in the PlatformScope can lead to bad server performance. It is the developers responsibility to clean up data that is not used anymore.

Access to Domino data


As in IBM's JavaScript engine, you can access the Domino environment by using the global variables "session" and "database" which we instantiate and pass to your code. Calling session.getCurrentDatabase() will return a null value, because the session does not really get created in the context of a database.

Formula language


IBM's formula language JavaScript constructs are proprietary/closed-source and therefore not supported when code is executed with the Rhino JavaScript engine. Use the command session.evaluate() to get the result of a formula execution.

Using Java classes and resources stored in the NSF


As said above, the classloader used by the Rhino engine supports loading Java classes and resources stored in the NSF database. Just add them to the NSF project's build path as if you would for standard SSJS content.
The NSF Classloader used to load code and resources from the NSF is passed to the executed JavaScript code as a global variable "nsfclassloader".

Inclusion of code from other libraries


By using the following line in your JavaScript code, you can include code from another SSJS library:
include("libraryName");

Includes are evaluated when the SSJS library content is loaded, not when it is executed. That's why it's not possible to only include a library if a certain condition is true. Actually, the include statement is replaced with a simple string comparison: we trim each SSJS line to remove whitespace characters, check if the line starts with the string include(" and extract the library name of the library to be included.

When include statements are used, the NSF classloader will collect a list of code signers of all included libraries. The code will only execute if all of the signers have a valid developer license key.

Performance


This section explains in detail how code and data is cached by XPages2Eclipse, how these caches can be controlled and it introduces a way how you can greatly improve the performance of your application using a class cache file.

Code and data caching

The JavaScript engine and classloader implementations are highly optimized for performance and make heavy use of caching techniques.
Both the SSJS library code and any custom Java classes that are loaded during code execution are cached without being loaded/checked again until the JVM is restarted - which means a restart of the Notes Client/Domino Designer or the command "restart task http" in the Domino server console for web access.

To make development easier, you can use the following OSGi console commands to manually flush the caches for single databases and force a data reload:
 
SSJS library content:
x2ejs show      =>  displays a list of cached libraries
x2ejs drop <n>  =>  remove entry number n displayed via the show command
x2ejs drop all  =>  remove all entries from the cache

Java Classloader content:
x2ecl show      =>  displays a list of databases with cached files
x2ecl drop <n>  =>  remove entry number n displayed via the show command
x2ecl drop all  =>  remove all entries from the cache

To enter the OSGi console commands in the Notes Client, you need to start the client in console mode.
Details can be found here.

On the Domino server, the syntax for the OSGi commands is a bit different:
 
SSJS library content:
tell http osgi x2ejs show      =>  displays a list of cached libraries
tell http osgi x2ejs drop <n>  =>  remove entry number n displayed via the show command
tell http osgi x2ejs drop all  =>  remove all entries from the cache

Java Classloader content:
tell http osgi x2ecl show      =>  displays a list of databases with cached files
tell http osgi x2ecl drop <n>  =>  remove entry number n displayed via the show command
tell http osgi x2ecl drop all  =>  remove all entries from the cache

Creating class cache file (WAR)


When you are dealing with many small Java or resource files in your application like a newer Dojo build than your current Notes version provides (managing the files in the Java perspective), you may have already experienced pretty bad performance when working with standard XPiNC applications (XPages in the Notes Client) stored on a Domino server.
The reason is that every single file is stored as a document in the database design and needs to be pulled over the wire the first time it is requested during a user session.

Things get even worse when you analyze in detail which classnames a JavaScript engine tries to resolve when it needs to instantiate custom Java classes:
For a simple line like var obj=new com.mindoo.test.MyClass();, the engine tries to resolve com, com.mindoo, com.mindoo.test and finally com.mindoo.test.MyClass as a class, to make sure that you don't instantiate an inner class of another Java class. The file resource design elements, that contain the binary data, store the full path name in an item called "$TITLE" - there is no tree-like structure in the design. The consequence is that the classloader can't be sure that com.mindoo does not exist, if a search for com already failed before and it needs to perform a search operation for every part of the string in the database design.

Since our approach uses the same data structures as IBM's XPages runtime, the XPages2Eclipse classloader has the same issue. However, we provide a way to manually compress all files and resources into one single archive (a file with extension "war"), which will then be used instead of searching for single files in the design. This improves performance a lot.

To create or update the WAR file, use the action "Create XPages2Eclipse class cache" in the context menu of the application navigator in the Domino Designer perspective or in the Java perspective:

Context menu action in Domino Designer
 
This action analyzes the project's build path and compresses all classes and resource files into a new archive that is stored in the base directory of the database:

WAR file in Java Perspective
 
Already existing class cache files are automatically deleted during this operation.

Security


Code privileges


JavaScript and Java code execute will be done with plugin privileges, because the classloader is part of the XPages2Eclipse plugin and transfers the plugin permissions to the loaded code.

Code signature check


XPages2Eclipse will only execute JavaScript and Java code stored in NSF databases, when the corresponding design elements (e.g. XPages, SSJS libraries and Java files created in the Java perspective) are signed with a Notes ID for which a Developer license key has been added to the keystore database.
The same is true for loading resources from the database design via the NSF classloader.

Code execution and resource loading will fail with an exception if any used code/resource has an unknown signature.
 
For more information about the keystore database, please refer to the Installation Guide.


This Version: 14 June 29, 2011 3:34:20 PM by Karsten Lehmann  Mindoo
Originally Added: Version 1 June 4, 2011 4:30:38 PM by Karsten Lehmann  Mindoo
   

Add Comment

Name:
Comments: