How to Crash an XPages server with 1 line of code
Devin Olson November 29 2012 05:37:24 AM
NOTICE: I have been notified that an SPR has now been created for this issue and it is being worked on. Kudos to the team at IBM for jumping on this so quickly.
Thanks also to NTF for testing / verifying the problem exists in the lotus.domino.Document object itself. You rock, my friend.
Yesterday I was doing some testing and I crashed my server.
So I restarted the server, checked the logs, checked my code, made some adjustments to try and identify the cause of the crash, and resumed my testing...and crashed my server.
So I restarted the server, checked the logs, checked my code, made some adjustments to try and identify the cause of the crash, and resumed my testing...and crashed my server.
Rinse...repeat...rinse...repeat.
We've all been there, we all know the frustration of trying to find the cause of such a problem. This one was a doosie -nothing was showing up in log.nsf -not even a panic notation. The console helped me (my development server runs in a console window on Linux, the console remains open even after a server crash) to figure out which NSD file to reference.
There was a generic notation telling me to attach the NSD log file along with my normal log file when submitting a bug report, which doesn't really apply in this case as this is a DEVELOPMENT server -crashes are (while not really welcomed) an expected part of the development process. In almost all cases a server crash on a development server is due to a bug in the DEVELOPER's code; NOT the application environment itself. Sending the logs to IBM for analysis would be akin to calling out the Cumberland Fire Department to help turn off my gas stove.
Which meant I had to go through the NSD logs myself. If you have never had the pleasure of pouring through one you have no idea of what you are missing. They will cure insomnia better than an entire bottle of Vick's Nyquil -instant snoresville. Don't misunderstand me, these logs are extremely valuable and contain a ton of important and pertinent information -but there is simply so much that processing one "by hand" is exhausting.
I did eventually figure out the cause of my crashes, and in this case it is NOT caused by a bug in my code. The problem was caused by the Document.hasItem() method -or perhaps the SSJS to Java translation / conversion of the method. Hopefully my explanation (as it follows) and SOLUTION will help you to avoid this kind of error in your own environments.
First of a bit of an explanation of how XPages work is in order. In XPages all running code is Java. You may recall one of my previous posts where I where I went off on a bit of a rant about how important it is that you learn Java to write good XPages. This is a perfect example of why understanding Java can help. As you'll discover my solution is NOT Java -writing Java is not as important as understanding.
Ok, back from the rabbit trail. Three critical points to understand are:
1. In XPages all running code is Java. All of the XML and SSJS you create when you write an XPage is either converted to Java code during the build process or interpreted so it can interact with the Java objects at run-time. Wrapper code is generated for your SSJS to interact with.
2. Domino Java objects are true Java objects, and don't need to be converted.
3. When your SSJS interacts with a Domino Java object, it does so via the generated wrapper code from point 1 above.
What this means the awesome STRONG type-checking native to Java (and therefore the Domino Java objects) is, through necessity, somewhat short-circuited by the SSJS to Java conversion during the build. In most cases the run-time checks which are built into the code will handle type or null checking just fine, but every once in a while a coding pattern can occur which has not been accounted for -which can in turn lead to the aforementioned server-crash nastiness.
In this case the problem stems from a combination of the lotus.domino.Document object, it's implementation of the hasItem() method, and SSJS to Java translation. The method signature for hasItem() is:
public boolean hasItem(String name) throws NotesException
When you call this method you must pass in a String value. If you don't pass a String the compiler will complain. If you pass in null, the code will compile just fine, but when it runs it will break and throw a NotesException error -which is normal expected behavior.
This object and it's code is wrapped by the NotesXSPdocument object -which you reference in your SSJS. When you define a document data source for an XPage or custom control, that data source handle is a NotesXSPdocument object. This object has a hasItem() method, with the following signature:
hasItem(fieldName:string) : boolean
The thing that must be remembered here is that this is JavaScript, not Java. And JavaScript (even SSJS) is the exact opposite of a strongly typed language. Yes, it has built in typeof and instanceof methods, and has classes and objects -but you can change the type of any object at any time. When you write your SSJS and call document.hasItem(myVariable), you could make myVariable anything you want: a hat or a brooch or a pterodactyl. When the code gets built and the corresponding Java code is created certain wrapper functionality will be generated to convert whatever you pass in (via the SSJS argument) to a Java String object. And this is where things break down.
I honestly don't know if this is how the internally built code works or not; I don't have any special "inside knowledge" -this is merely my opinion on how this process could work. If you pass in a variable which at run-time happens to be null, the Java code will try to figure out it's String representation. There is a toString() method in the java.lang.Object class which returns a String object, and all other Java objects (even your custom ones) ultimately descend from this class; which means that calling this method from any Java object is generally a safe thing to do. Unless of course the thing from which you are trying to convert to a String is null. null is not an object, and therefore has no toString() method. My guess here is that the internal code is not doing a test for null (and returning an empty string), but is instead simply calling the toString() method.
As I said, I don't know how the internal conversion is actually performed, but I do know what happens when you pass a null to this method -the server kills itself.
Don't believe me? Check out the following code:
WHAT FOLLOWS IS FOR ILLUSTRATIVE PURPOSES ONLY
DO NOT PUT IT ON A SERVER
IF YOU DO YOUR SERVER WILL CRASH,
YOUR ADMINISTRATORS WILL BE VERY ANGRY AND VIOLENCE WILL FOLLOW.
SERIOUSLY,
DO NOT DO THIS
You've been warned.
var hasItem = currentDocument.hasItem(null);
That single line of code, if placed in an XPage or Custom control, will cause the server to immediately crash.
I have created a sample NSF you can use to play with, assuming you have a safe development environment you can repeatedly destroy. You can find this on my downloads page: http://www.azlighthouse.com/downloads.html
The good news is that there is a workaround in place that has been in my toolbox for some time. Using my @IsBlank function as a wrapper for the argument is really easy, and completely solves the problem. You can find this function in the ssjsTools library in the sample NSF, or just grab it from my previous "XPages SSJS Function: @IsBlank" blog entry.
The code to implement this solution is as simple as:
var itemname = null;
var hasItem = currentDocument.hasItem(@IsBlank(itemname)? "": itemname);
Ok, that is enough for today.
Hope this helps!
-Devin.
- Comments [0]