Thursday, October 01, 2009

Flex Client Side Error Handling with Domino...

Handling errors within the context of Flex poses a few challenges.

  1. Errors are generated on the client. Your server and logs have no idea that a user experienced an error within the flash runtime
  2. No "out of the box" way to post the errors back to our favorite application server (Domino)
The way I chose to handle solving this problem was via a custom event. This custom event (LogError) gives you the ability to post the error to the server thus enabling you to view the errors in a central location, and dispatch that an error occurred throughout the rest of your application. Dispatching the LogError enables the developer to manage the view state of the application if something blows up during a critical process or method call (i.e. disable a button, or a data grid, load a popup to gain user input on what the user was doing etc...). The LogError custom event uses the Rest class to post the stack trace, or any other information needed. I've added the LogError custom event to the FlexContacts project, along w/ a Logger lotuscript class to consume the posted data and drop it into a notes document. Over time, as I find new techniques developing flex w/ domino, I will be building these elements out into the FlexContacts project (sort of as a running example for anyone interested).

Let's take a look at how a developer would call the LogError custom event in a method (located in contact form and called by Test Error button):

private function blowUp():void{
try{
//generate a null pointer..
var o:Object;
o.someAttribute="test";

}catch(err:Error){
var logErr:LogError = new LogError(LogError.EVENT_TYPE);
//this posts the log to the server.
logErr.log("FlexContacts.mxml","blowUp",err.getStackTrace());
//here choose to dispatch the log event that occured to any event listeners.
this.systemManager.dispatchEvent(logErr);
}
}


The processing sequence for the above is as follows:
  1. Exception occurs
  2. LogError posts the log to the domino server (via http/s)
  3. A notes form captures the posted log data (fmLog)
  4. A WebQuery save agent HandleFlexError is called from the form to append the log
  5. The agent calls a LotusScript object called FlexError
  6. FlexError creates a log entry on the back end notes doc which looks like:
User Id:
888888
Date Created:
02/08/2009 04:00 PM

Type:
FLEX_ERROR

Log(s):
FlexContacts.mxml.blowUp 2/8/2009 4:00:45 PM TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.notesui::ContactForm/blowUp()
at com.notesui::ContactForm/__btnTestErrorLog_click()


That is it... Flex's ability to easily make HTTP POSTS/GETS makes logging client side errors a snap. Please feel free to download the updated FlexContacts project from here. If you need tips on how to deploy, please see the previous blog entry on FlexingDomino...Also if you have any questions please feel free to post comments on this blog.

cheers,

Mark

webcam snapshots and lotus/domino


Download FlexContacts(WebCamPics).zip
I was recently tasked with creating an application that can take a picture with a webcam and post it back to a server. The first tool that came to mind during the initial POC was flex/flash as it already has built in support for webcams. I ran through and did a google search to see if anyone else had gone through this and found a nice example on Adobe's Developer Connection (adobe sample). The sample from adobe was a great start. The next step was to tie it into a lotus/domino solution.
On the Flex Side the solution steps are:

  1. Add an mx:VideoPlayer control onto the stage and add some init code to bind it to the webcam.
  2. Create an mx:Image control to capture the frame from the webcam as a bitmap to review the captured image
  3. Encode the bitmap image to the desired type (PNG, or JPEG are currently supported encoders)
  4. Base64 encode the image data
  5. Post the base64 image data to the back-end Domino server and into a richtext field
  6. Have an agent consume the base64 data and create an image file attachment in the notes document

I've created an abstraction to get a handle on the picture using a factory class called PictureFactory. PictureFactory handles the capture and conversion of the image and returns an object implementing a Pic interface. Below are screen pics of the UI and the function takePicture which uses the picture factory to take the snap shot. You can find this function in the CamWrapper.mxml file under the notesui folder in flex builder, and the pic code is found in com.pics actionscript package.


Prompt to take the picture

Captured Picture in the Contact Form
(see CamWrapper.mxml)
private function takePicture():void{
//get an instance of PictureFactory (singleton factory class)
var factory:PictureFactory = PictureFactory.getInstance();

//get an instance of Pic (JPEGPic) which has a few different methods associated w/ it
//notice how we are passing in the camView (note it can be any UIComponent)
var pic:Pic = factory.createPicture(this.camView,PictureFactory.TYPE_JPEG);

//create a new DataXferEvent to store the picture object
var event:DataXferEvent = new DataXferEvent(DataXferEvent.EVENT_TAKE_PIC);

//store the picture object in our custom event
event.data=pic;

//dispatchEvent sends the event along w/ the picture data out to any listeners.
this.systemManager.dispatchEvent(event);
}

Handles the DataXferEvent and binds the picture's bitMap to mx:Image to show the picture in the UI (see ContactForm.mxml)
-->
private function bindPicture(event:DataXferEvent):void{
this.pic=Pic(event.data);
this.contactPhoto.source=pic.bitMap;
}

Responsible for posting the contact object to domino (see ContactForm.mxml)
-->
private function postContact():void{
//build the parameter object
var params:Object = new Object();
params.pictureData = this.pic.base64Data;
params.firstName=this.firstName.text;
params.lastName=this.lastName.text;
params.address=this.address.text;
params.eMail=this.eMail.text;
params.country = this.country.value;
params.lat=this.lat.text;
params.lon=this.lon.text;
params.fileUploadUNID=this.fileUploadUNID;

var url:String = Cfg.buildUrl("fmContact?CreateDocument");

//if we have a unid in the formData XML then we use the SaveDocument else
//we use the create document.
if(formData!=null && formData.unid!=null){
url = Cfg.buildUrl("0/" + formData.unid + "?SaveDocument");
}
rest.doRestXmlCall(url,handleResponse,handleFault,"POST",params);
}

On The Domino Side
Now that we have our picture data ready to be posted, we need to have something in place on the Domino side to convert that base64 data into a JPEG again and attach the file to the contact document. Normally I'd use a java object to process the base64 data back to binary, but decided to look and see if there were LotusScript alternatives. I was very happy to find a nice little function in the NotesMimeEntity class called SetContentFromText that can convert base64 data back to binary. I created a single LotusScript class called PictureUtils containing one method to process the base64 data (see below code fragment)

Public Function processBase64Data() As String
On Error Goto MyErr

Dim session As NotesSession
Dim stream As NotesStream
Dim parent As NotesMimeEntity
Dim child As NotesMimeEntity
Dim item As NotesItem
Dim header As NotesMimeHeader
Dim picName As Variant
Dim fileName As String

picName=Evaluate("@Unique")
fileName = picName(0) & ".jpg"

Set session = doc.ParentDatabase.Parent

session.ConvertMime=False

Set stream = session.CreateStream

'get a handle on the picture data in the rich text item pictureData
Set item = doc.GetFirstItem("pictureData")

'write the text data into the stream
Call stream.WriteText(item.Text)

'create the new mime entity in the document
Set parent = doc.GetMIMEEntity("pictures")
If parent Is Nothing Then
Set parent = doc.CreateMIMEEntity("pictures")
End If

'create the new child
Set child = parent.CreateChildEntity()

'set the header information so we can name the file "picture.jpg"
Set header = child.CreateHeader("Content-Disposition")
Call header.SetHeaderVal({attachment; filename="} & fileName & {"})

'calling SetContentFromText will attach the file into the document.
Call child.SetContentFromText(stream, "image/jpeg", ENC_BASE64)
Call child.DecodeContent()

'remove the picture data as we don't need it anymore.
Call doc.RemoveItem("pictureData")

'save the changes.
Call doc.Save(False,False)

stream.Close

processBase64Data = fileName

Exit Function
MyErr:
Dim errBody As String
errBody = "Error = " & Lsi_info(2) & " " & Str(Err) & ": " & Error$ & " on line: " & Erl()
Call errLog.GenErrorLog("utils.PictureUtils",errBody,"")
Exit Function
End Function



One More Thing...
While I was creating this post, another thought came to mind. How cool would it be if a user experienced an error and in the exception handling a screen pic of what they are viewing at the time of the error is sent back to the server? This can be achieved by using the PictureFactory and passing in any UIComponent reference. I updated the code behind the "Test Error Log" button to capture an image of the current contact form when the exception is generated (see screen pic below).

Flex Error Document w/ Pictures attached

Picture captured using Test Error Log (in error handler)
That's it!
Below is a link to the .zip file containing both the flex project, and the .nsfs. You should be able to load the FlexContacts.nsf without issue to your domino server. Please let me know (via comments) if you run into any issues.