handy function for namespacing your scoped objects in server-side JavaScript
Java has always organized groups of classes into namespaces called "packages", collections of related classes from a single organizational author. Typically, a package name starts with the author organization's primary web domain in reverse order (i.e. "com.ibm") and ends with a period-delimited hierarchy describing the purpose of the classes the package contains (i.e. "com.ibm.designer.domino.xsp.editor"). This structure assists maintenance programmers in determining the exact purpose - and origin - of any given class, even if the application is comprised of an enormous amount of classes.
As web applications have grown increasingly complex over the past few years, it's become increasingly common for client-side JavaScript objects to be namespaced in a similar manner, though the source prefix is nearly always omitted (Yahoo is a notable exception to this: every web application they deploy now contains a main application object, which is always called "YAHOO.ApplicationName"). It's fairly common, therefore, for JavaScript frameworks to include a function to turn a period-delimited string (i.e. "alt.wesley.crusher") into an object hierarchy (i.e. {alt: {wesley: {crusher: {}}}}). This allows individual members to be added deep into a hierarchy without having to first manually verify that the full hierarchy exists.
The one area of XPages where I can foresee this becoming useful is with the scoped variables, which allow you to store object pointers in one of four different scopes:
- applicationScope: shared among all users of a given NSF
- sessionScope: shared across all requests within a single user session
- viewScope: shared across all requests within a single page - for example, if a user opens home.xsp, and their interaction with the page triggers some partial refresh events, each event has access to the same viewScope as the initial page load; navigating to another page or reloading the current page destroys the viewScope and creates a new one
- requestScope: shared across all controls rendered for a single request - for example, if a partial refresh event impacts multiple controls (i.e. several inputText controls within a single form panel), all controls within that request share the same requestScope... but do not have access to the requestScope from the initial page load; that request is already complete, so any objects stored during that request no longer exist
As handy as these are, the more complex your application and the greater your need to maximize performance, the more stuff you'll be cramming into one or more of these scopes. Which is fine; assuming your server has enough RAM, there's no danger of them running out of space. The real danger is the same as any variable naming: forgetting that you've already stored something by the same name and clobbering it with new data in the same container. Hence, I can foresee value in defining a namespace hierarchy for the keys used to store scope data.
Officially, the correct way to store and retrieve scope data is by using, respectively, put and get:
currentValue = sessionScope.get("something");
sessionScope.put("something", newValue);
Predictably, however, these behave similarly to the "extended class syntax" in the LotusScript NotesDocument class. Just as you really should always use .getItemValue() and .replaceItemValue() to "get" and "put" document item values, but upon occasion will choose to use doc.Subject to refer to a nonexistent Subject property of the NotesDocument class knowing that it will guess correctly that what you really want is the Subject item on the document to which doc is an object handle, the following variation on the above example will work:
currentValue = sessionScope.something;
sessionScope.something = newValue;
Officially, that's wrong. Furthermore, it's complete nonsense: sessionScope is a HashMap; the HashMap class doesn't have a public member named "something". But... because it's actually JavaScript we're writing that gets turned into Java objects, and JavaScript allows members to be added at any time via dot syntax, the above is perfectly valid JavaScript even though in Java it wouldn't even compile. Bear in mind that IBM prefers that we use get and put because there are some type fidelity issues with the dot syntax approach, but the latter is used all over the place in the Discussion template, so it must not be too terribly dangerous.
So here's a function you can add to a server-side JavaScript library that will allow you to do the same type of namespacing I described earlier for your scope variables:
var scopeNameSpace = function (packageString, targetScope) {
var currentSpace = targetScope;
var newSpace;
var newSpaceName;
var nameSpaces = packageString.split('.');
for (var i = 0; i < nameSpaces.length; i++) {
newSpace = new java.util.HashMap();
newSpaceName = nameSpaces[i];
if (!(currentSpace)) {
// targetScope is undefined, default to viewScope
currentSpace = viewScope;
}
if (!(currentSpace.containsKey(newSpaceName))) {
currentSpace.put(newSpaceName, newSpace);
}
currentSpace = currentSpace.get(newSpaceName);
}
return currentSpace;
/*
Example usage:
scopeNameSpace('alt.wesley.crusher', sessionScope);
sessionScope.alt.wesley.crusher.age = 36;
--OR--
scopeNameSpace('alt.wesley.crusher', sessionScope).age = 36;
*/
};

Comments
Also I wonder what happens with put if you try something like sessionScope.put('put', 'whoops') since it is directly exposing those properties.
Posted by Rich Waters At 05:26:47 PM On 03/10/2009 | - Website - |
viewScope.put('put', 'oops');
viewScope.put('displayText', viewScope.get('put'));
scopeNameSpace('alt.wesley.crusher');
A computed field bound to viewScope.displayText rendered 'oops'. I changed the second line to:
viewScope.put('displayText', viewScope.put);
The computed field then rendered:
{displayText=(this Map), alt={wesley={crusher={}}}, put=oops}
Apparently they foresaw the problem and put some sanity checks in.
Posted by Tim Tripcony At 05:48:16 PM On 03/10/2009 | - Website - |
Posted by Rich Waters At 07:57:31 PM On 03/10/2009 | - Website - |
Posted by Andrew Tjecklowsky At 01:37:42 PM On 03/11/2009 | - Website - |
scopeNameSpace('com.acme', applicationScope).myFunction = function(parameters) {function body};
You can then call the function by its full namespace:
applicationScope.com.acme.myFunction();
Primarily, though, the scope variables seem to be intended for storing data (i.e. results from database queries) to avoid having to retrieve the same data on each page load. Any function you would want stored in the application scope (implying it's available to all users) would probably be better defined in a script library. For example:
var com = {
acme: {
myFunction: function (parameters) {function body}
}
}
This way you can call the function via com.acme.myFunction() from anywhere that loads the library without having to actually store the function object in RAM until the next reboot, which is essentially what the applicationScope does.
Posted by Tim Tripcony At 05:50:04 PM On 03/11/2009 | - Website - |