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.
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.
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
the code from the database design. So we needed to develop our own solution for JavaScript code execution and code/resource loading from NSF.
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:
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:
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.
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:
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:
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 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.