Thursday, December 3, 2009

Helper-functions for debugging XPages

Update: Modified the class to support HTML in the message
Update 2: Made the code compatible with Domino 8.5.2
Update 3: Added methods to display message/exception on page
Update 4: Modified to send to current user. If on localhost, the exception/message is thrown -> xpage shows stack trace.

Since there are no good tools for debugging XPages, I created a couple of helper-methods (Server Side Javascript) of my own.

Debug.message sends a (MIME/HTML) mail with the specified message

Debug.exception sends a stack trace of an exception (used in a try/catch). This is useful because not all exceptions crash the application (for instance AfterRenderResponse code).

// Helper-class for debugging
var Debug = {
// Send a stack trace of an exception
exception: function( exception ){
// If on localhost/public db - throw exception
if( this.getUserName() === 'Anonymous' ){ throw exception; }

this.message( this.getExceptionString( exception ), 'Exception!' );
},

// Add exception to page
exceptionToPage: function( exception ){
this.setPageDebugMessage( 'Exception: ' + this.getExceptionString( exception ) );
},

getExceptionString: function( exception ){
var errorMessage = exception.message;

if( typeof exception.printStackTrace !== 'undefined' ){
var stringWriter = new java.io.StringWriter();
exception.printStackTrace( new java.io.PrintWriter( stringWriter ) );
errorMessage = stringWriter.toString();
}

if( typeof exception === 'com.ibm.jscript.InterpretException' ){
errorMessage = exception.getNode().getTraceString() + '\n\n' + errorMessage;
}

return errorMessage;
},

getUserName: function(){
return @Name( '[CN]', @UserName() );
},

// Send a message (supports HTML)
message: function( message, subject ){
// If on localhost/public db - throw exception
if( this.getUserName() === 'Anonymous' ){ throw 'Not logged in. Could not send message: ' + message; }

session.setConvertMime( false );
var doc:NotesDocument = database.createDocument();
doc.replaceItemValue( 'Form', 'Memo' );
doc.replaceItemValue( 'Subject', subject || 'Debug..' );
doc.replaceItemValue( 'SendTo', this.getUserName() );

var body:NotesMIMEEntity = doc.createMIMEEntity();

var contentStream = session.createStream();
// Set preferred styling
contentStream.writeText( '' );

// Convert linefeeds to <br>s
contentStream.writeText( message.replace( '\n', '<br />' ) );
body.setContentFromText( contentStream, 'text/html;charset=ISO-8859-1',
lotus.domino.MIMEEntity.ENC_NONE );
doc.send();

session.setConvertMime( true );
},

// Add message to page
messageToPage: function( message ){
this.setPageDebugMessage( message );
},

// Adds message to the bottom of the page in a dynamically created xp:text
setPageDebugMessage: function( message ){
var messageControl = getComponent( 'global-debug-messages' );
if( !messageControl ){
messageControl = new com.ibm.xsp.component.xp.XspOutputText();
messageControl.setId( 'global-debug-messages' );
messageControl.setEscape( false );
messageControl.setStyleClass( 'xspMessage' );

var valueBinding = facesContext.getApplication().createValueBinding( '#{requestScope.debugMessages}' );
messageControl.setValueBinding( 'value', valueBinding );

view.getChildren().add( messageControl );
}

var currentMessages = requestScope.debugMessages;
if( typeof currentMessages !== 'string' ){ currentMessages = ''; }
requestScope.put( 'debugMessages', message + '<br />' + currentMessages );
}
}

7 comments:

Thomas Adrian said...

Nice,

another way is to use a print statement which write to the server console - and create an event handler in events4.nsf which will send you a mail ;-)


http://xpages.notessidan.se/blogg.nsf/xstart.xsp?post=718BA0FD3D9D08A7C1257673002BBDDE

- Thomas

Anonymous said...

What do I do with this code?

Tommy Valand said...

Put it inside a SSJS library.

In your functions, add try/catch. When an error occurs, you'll get a mail.

Example:
function someFunction(){
try {
/*the code*/
} catch(e){ Debug.exception(e); }
}

Or, if you're curious about a value or something..

/*code*/
Debug.message( param.get( 'some_parameter' ) );
/*code*/

The script library containing Debug have to be added to the page you're using it in.

Hope this helps. :)

In our production environment, we use a customized Java script/an extended Debug "class", so that the stack traces go to an OpenLog db. This is better when putting applications into production, so that the user/you won't get hammered by error messages

Patpicos said...

Pretty cool stuff. I really like how you create an additional control on the page if it doesnt already exist and then bind it to a requestScope variable, then add it to the Xpage.
Very neat.

Tommy Valand said...

Thanks :)

I always try to make my "APIs" as simple to use as possible.

Rashid Azar said...

When I am calling Debug.messageToPage in beforePageLoad event it is throwing an exception
com.ibm.xsp.component.xp.XspOutputText incompatible with com.ibm.xsp.component.UIScriptCollector

Tommy Valand said...

If I remember correctly, the XSP Component tree isn't created until afterPageLoad, so that might be why it fails.