Showing posts with label ssjs. Show all posts
Showing posts with label ssjs. Show all posts

Thursday, May 6, 2010

XPages: Simple string encrypter/decrypter

I needed a simple encryption routine today. I found this article with accompanying code.

Here's the routine ported to Server Side JavaScript:
function StringEncrypter( encryptionScheme:String, encryptionKey:String ){
try{
encryptionScheme = encryptionScheme || this.DESEDE_ENCRYPTION_SCHEME;
encryptionKey = encryptionKey || this.DEFAULT_ENCRYPTION_KEY;
if( encryptionKey.length < 24 ){
throw new java.lang.Exception(
'Encryption key was less than 24 characters.' );
}
var keyAsBytes = encryptionKey.getBytes( this.UNICODE_FORMAT );

switch( encryptionScheme ){
case this.DESEDE_ENCRYPTION_SCHEME:
this.keySpec = new javax.crypto.spec.DESedeKeySpec( keyAsBytes ); break;
case this.DES_ENCRYPTION_SCHEME:
this.keySpec = new javax.crypto.spec.DESKeySpec( keyAsBytes ); break;
default:
throw new java.lang.Exception(
"Encryption scheme not supported: " + encryptionScheme );
}

this.keyFactory = javax.crypto.SecretKeyFactory.getInstance( encryptionScheme );
this.cipher = javax.crypto.Cipher.getInstance( encryptionScheme );
} catch( e ){ /* your exception handling */ }
}

StringEncrypter.prototype = {
DESEDE_ENCRYPTION_SCHEME: "DESede",
DES_ENCRYPTION_SCHEME: "DES",
DEFAULT_ENCRYPTION_KEY:
"The ships hung in the sky in much the same way that bricks don't.",
UNICODE_FORMAT: "UTF8",

keySpec: null,
keyFactory: null,
cipher: null,

encrypt: function( unencryptedString ){
if( !unencryptedString ){ return ''; }

try {
var key = this.keyFactory.generateSecret( this.keySpec );
this.cipher.init( javax.crypto.Cipher.ENCRYPT_MODE, key );
var clearText = unencryptedString.getBytes( this.UNICODE_FORMAT );
var cipherText = this.cipher.doFinal( clearText );

var base64encoder = new sun.misc.BASE64Encoder();
return base64encoder.encode( cipherText );
} catch( e ){ /* your exception handling */ }
},
decrypt: function( encryptedString ){
if( !encryptedString ){ return ''; }

try {
var key = this.keyFactory.generateSecret( this.keySpec );
this.cipher.init( javax.crypto.Cipher.DECRYPT_MODE, key );
var base64decoder = new sun.misc.BASE64Decoder();
var clearText = base64decoder.decodeBuffer( encryptedString );
var cipherText = this.cipher.doFinal( clearText );

return new java.lang.String( cipherText );
} catch( e ){ /* your exception handling */ }
}
}
Example usage:

Encryption
var encrypter = new StringEncrypter();
encrypter.encrypt( unencryptedString );
Decryption
var encrypter = new StringEncrypter();
encrypter.decrypt( encryptedString );
As you can see from the constructor, you can specify your own encryption key/choose from two encryption methods.

I simply ported the code, so if you want more details, read the article from which I snagged the code.

Saturday, May 1, 2010

XPages: Dynamically binding and unbinding server side events

Back in my early XPages career, I asked the readers if it was possible to Dynamically bind server side events.

The only answer I got was this:
The short answer: NO.
The long: it is somewhere on the list.


I don't like those kinds of answers.

After todays experiment, I can firmly say: Yes, it's possible!
This is what you'll be able to do with the simple framework provided in the demo:

  • Dynamically bind/unbind custom events

  • Bind multiple event handlers to one event

  • Run the event in a specified scope

  • Use regular HTML elements as event triggers


This started out as an experiment into Server Side Event Delegation. A couple of nights later, I further improved on the functionality.

In the slumbering stage this morning, I discovered that I was on the path for something, but not what I initially thought I had started out for. The two first experiments lead to what would have been a complex unusable blob of functionality.

Today, I did some cleanup. I removed all the class attributes, and rewrote the framework code to be more flexible. The previous two version relied on an eventHandler node in the top of the XPage, which results in a generated script tag. This implementation doesn't add any script tags to the generated html.

The server side part of the code lets you specify what to do for certain events. The client side part of the code specifies what server side event to run, what data to send to the event handler (optional), and which part of the page should be updated after the event has run.

I did a little screen capture of the demoapp in action (not visible in Google Reader):





Simple Server Side Event Delegation:
The two first buttons toggles (when active) visibility of a couple of computed fields. The third button binds/unbinds the event handler for the two preceding buttons.

Multiple Event Handlers:
The first button triggers a custom event on the server. The second button adds an event handler to the event. As you can see from the demo, for each event handler added, the addition increases by 1.

Event handler running in a specified scope:
The event handler for the event runs in the scope of an object on the server. Then the event is triggered, a viewScope variable is created from a property fetched from the object ( viewScope.put( 'theAnswer', this.theAnswer ) )

In View Edit:
When the name of the person is clicked, an "edit" event is triggered. When the ok button is clicked, a "save" event is triggered.

If you add the event handlers afterRestoreView or before-/afterRendreResponse, an event handler is added for every partial refresh. Adding them in an event handler or in the afterPageLoad event should be safe.

>> Download Demoapp

If you don't like a lot of generated server side script tags for events/embedding the event code in control after control, this is definitely worth looking at.

If you find any bugs/want more info about something, leave me a comment.

Share and enjoy!

Wednesday, April 28, 2010

XPages: Code snippet for number/string conversion

The DecimalFormat object is cached in the application scope due to performance/memory concerns.

The default locale for the conversion seems to be the locale of the OS on the server, so if you want to support multiple locales, you'd have to extend the functionality.

Example usage (Norwegian locale):
String to number
NumberConverter.stringToNumber( '15,55', '0.00' ) -> java.lang.Double

Number to string
NumberConverter.numberToString( 15.926, '0.00' ) -> "15,93"

var NumberConverter = {
stringToNumber : function(numberString, patternString) {
try {
if( !numberString ){ return null; }

var formatter = NumberConverter.getFormatter(patternString);
return formatter.parse(numberString);
} catch( e ){ Debug.logException( e ); }
},
numberToString : function(number, patternString) {
try {
if( !number ){ return null; }

number = new java.lang.Double(number);
var formatter = NumberConverter.getFormatter(patternString);
return formatter.format(number);
} catch( e ){ Debug.logException( e ); }
},
getFormatter : function(patternString) {
try {
var cacheKey = 'numberFormatter' + patternString;
var numberFormatter = applicationScope[cacheKey];
if(!numberFormatter) {
numberFormatter = java.text.DecimalFormat(patternString);
applicationScope[cacheKey] = numberFormatter;
}

return numberFormatter;
} catch( e ){ Debug.logException( e ); }
}
}

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 );
}
}