Domino 5.0.13 –> Domino 7.0

Taking advantage of the slowdown of traffic on the day of New Years eve I just upgraded one of our production servers from Domino 5.0.13 to Domino 7.0. The only issue was that the installer complained about being unable to uninstall the Windows service althrough the service was uninstalled correctly. After restarting the installation it completed without any other issues.

First impressions? Everything seems to be running so I cross my fingers…

Windows image-file 0-day exploit

A new 0-day Windows exploit is out. The exploit takes advantage of a built-in feature of the Windows Metafile image format that allows code to be executed if the Windows Metafile fails to load. Since this is a core Windows issue you will not be safe just resorting to Firefox. Actually just surfing to a website embedding a crafted image could infect your computer.

The workaround for now is to unregister the offending DLL on your Windows system. For more information refer to the show notes for the Sucurity Now! podcast episode 20.

Supplemental Resources and Links for Episode #20

Writing a LSX (LotusScript Extension)

Does anyone have any experience writing a custom LSX? The LSX is going to wrap some third-party/legacy C/C++ code.

So far I dug out an article from The VIEW (May/June 1998: “The Why and How of Building LotusScript Extensions with the LSX Toolkit”) but it would be really nice with a comment from someone who actually did it.

Is it pretty straight forward?, is the LSX Wizard the way to go?, should I stay away from it?

Please let me know if you have any experience to share…

Re: LotusScript Singletons

While the described approach is valid there is a caveat if the script library defining the singleton is loaded from multiple sources at the same time. Because of this the described approach, all though nice, doesn’t really solve the real problem with creating singletons in LotusScript. It is my opinion impossible to create a real singleton in LotusScript without resorting to document based synchronization, if the same script library is loaded from multiple sources at the same time.

The problems stems from the fact that all scripts are running within their own little memory space making it impossible to share memory between different instances of the same script.

The reason I am quite sure of this is that I spent considerable amounts of time in vain a month or so ago trying to do this. I needed to write a simple transaction manager for NotesDocument objects so that documents that were reserved where automatically rolled back if the calling document wasn’t saved.

Due to application requirements the active form had to have an embedded view showing the documents that had been checked out and hence was under “transaction control”, but since the LotusScript code in the embedded view would have no way of knowing when the document containing the embedded view was saved, the script library with the transaction manager needed to be loaded (Use’ed) from both the form and the embedded view. Using a real singleton should solve this problem just fine, but due to the LotusScript memory model this approach created two “singletons” which kinds of defied the point.

A simple way to demonstrate this behaviour is to have the singleton compute to unique value (e.g. @Unique) in its constructor and have it output the unique value at each method invocation. This way you would see two different singletons performing operations.

The solution was to have the transaction manager synchronize its state with an user-specific profile document before and after each operation. The performance of this approach is quite okay.

So be aware of this kind of behavour when creating singletons that may be loaded from multiple scripts at the same time.

Making changes to the OpenLog application

Logging is central to all applications to find out what’s going on and to help in development and debugging. The NotesLog class from LotusScript just doesn’t cut it in that regard which is why we should be happy for OpenLog. I find OpenLog to be a valuable additional to my LotusScript development tool arsenal.

Before we could start using it in production we had to make a few changes to the standard OpenLog application (thank God for OpenSource):

  1. Default location of openlog.nsf to be the same directory as the current database
  2. SEVERITY_DEBUG and filtering on severity level at runtime
  3. Grouping of log documents
  4. A small bug fix

Below I will discuss each of the changes made in its own section. I would really like some feedback on the changes so please let me know if you have any. Of cause the changes will also be sent to Julian so he can decide whether to incorporate them in the standard application template.

It’s so much more fun when we share… 🙂

Ad. 1: Default location of openlog.nsf…
We normally recommend our customers to organize the databases on their servers with one application per directory (e.g. one directory for helpdesk, one for sales force automation etc.) Because of this it really makes sense for us to have an Openlog database per application residing in the application directory and to have the default location of the openlog.nsf be the openlog.nsf in the current directory.

This means we can use the same “Openlog Functions” script library for all applications without having to modify the path to the log database (in the Initialize event). Instead of hardcoding the logDbName variable to “openlog.nsf” we compute the path based on the current database path.

'** this should be the name and path of the database that
'** you're logging everything to -- you need to explicitly name
'** it because it probably won't be the database that you're
'** running agents from
Dim session As New NotesSession
Dim sep As String
If Left(session.Platform, 7) = "Windows" Then
   sep = ||
Else
   sep = |/|
End If
logDbName = Strleftback(session.CurrentDatabase.FilePath, sep) + sep + "OpenLog.nsf"

Ad. 2: SEVERITY_DEBUG and filtering…
One of the things I really like about the log4j (and similar) frameworks is the concept of logging levels and the ability to filter on the logging levels at runtime. OpenLog has the concept of logging levels but not the filtering at runtime.

OpenLog currently has the following logging levels:

  • SEVERITY_HIGH
  • SEVERITY_MEDIUM
  • SEVERITY_LOW

These logging levels are fine but I really miss a SEVERITY_DEBUG. SEVERITY_DEBUG should be used to output information usable in troubleshooting situations so you can have an added log output without having to insert “Print” or “Msgbox” statements in the code.

The filtering at runtime means that the logging framework will filter the log records actually being written to the openlog.nsf database at runtime. The idea is that you specify a severity with every log statement in the application code, and a filter logging level to the logging framework. Below the filter logging level is set in the “OpenlogFunctions” script library but it could as well be taken from a configuration document in the OpenLog database
as well. There are pros and cons with each solution.

The default filter logging level should be SEVERITY_LOW which would make the logging framework ignore log requests of a lower severity that is log requests of severity SEVERITY_DEBUG. Setting the filter logging level to SEVERITY_HIGH would make the logging framework only output logging requests with SEVERITY_HIGH.

To incorporate this kind of functionality I have made the following changes (additions in italic).

Declarations
'** common severity types
Const SEVERITY_DEBUG = "-1"
Const SEVERITY_LOW = "0"
Const SEVERITY_MEDIUM = "1"
Const SEVERITY_HIGH = "2"

'** current severity level we log - we will log everything which is at the
'** specified level and higher
Dim logSeverityLevel As String


Initialize
'initialize logging filter severity level to LOW
logSeverityLevel = SEVERITY_LOW

LogErrorEx-method
Function LogErrorEx (msg As String, severity As String, doc As NotesDocument) As String
   '** log an error with some descriptive information
   On Error Goto processError
   If Cint(severity) < Cint(logSeverityLevel) Then Exit Function
   ...
   ...
End Sub

LogEvent-method
Function LogEvent (msg As String, severity As String, doc As NotesDocument) As Integer
   '** log a general event
   On Error Goto processError
   If Cint(severity) < Cint(logSeverityLevel) Then Exit Function
   ...
   ...
End Sub

Ad. 3: Grouping of log documents</b
We have a lot of agents that run multiple times a day. With the default behaviour of OpenLog it can be very difficult to distinguish the log documents from one execution from the log documents from another since they are listed together only separated by the time. To alleviate this we have added the concept of log document grouping.

Above the “Test LogGroup”-agent has been run 4 times at various times. The log documents for each run is grouped inside its own group (e.g. “Main loop (09-12-2005 08:37:48)”) which makes it easy to locate the log documents for a particular run. We’ve taken it a bit further though since you can nest groups within groups. Combining this nesting with SEVERITY_DEBUG you have the option of logically grouping log documents for document processing logic and only have it output when debugging.

The below example show the log documents for two separate runs. The first is run with a log filter of SEVERITY_LOW and the second with a filter of SEVERITY_DEBUG. As you can see the second run has a group of log documents for each of the 3 documents being processed. This is nice since many Domino agents will process documents and often it makes sense to group log documents based on the document they are based on.

The code that produces the above example is very simple and is shown below. As you can see the code uses two additional methods called EnterGroup(String) and LeaveGroup() which are not standard OpenLog methods. These are the methods that tell OpenLog when to start, nest and leave group so it can be output to the log documents.

Option Public
Option Explicit
Use "OpenLogFunctions"

Sub Initialize
   Dim i As Integer
   Dim j As Integer

   Call EnterGroup("Main loop (" & Now & ")")
   Call LogEvent("Starting to run...", SEVERITY_LOW, Nothing)
   For i=0 To 2
      Call EnterGroup("Interation - group " & i)
      For j=0 To 2
         Call LogEvent("Logging event " & j, SEVERITY_DEBUG, Nothing)
      Next
      Call LeaveGroup()
   Next
   Call LogEvent("Done running...", SEVERITY_LOW, Nothing)
End Sub

The handling og logging groups and nested logging groups is implemeted using a Stack. When entering a group, by calling the EnterGroup(String)-method the group name is pushed on the stack and when leaving a group (using the LeaveGroup()-method) the top group is popped from the Stack. Simple.

The Stack is from Johan Känngards website.

Apart from the Stack there is a small addition to the LogEntry class to allow the logGroup to be set before the log document is saved to the database.

Ad. 4: A small bug fix
When logging you sometimes need to log an error even though no “real” LotusScript occured (i.e. the Error function will return an empty string). When this happens the logged message in the OpenLog database will be blank since the error message is always taken from the Error function.

This is simple to fix though and can be done with 3 lines in the LogErrorEx()-method. The fix is simple – if there is no output from the Error function use the error text supplied to the method. The change in the snipped method code below is highlighted with italics.

Function LogErrorEx (msg As String, severity As String, doc As NotesDocument) As String
   ...
   ...

   With globalLogItem
      ...
      ...

      If (stackTrace.traceData = "") Then
         ...
      Else
         ...
      End If

      'if the method is called without an actual error having occured (e.g. a flaw in
      'necessary setup) Error will be empty. Test for this and set message
      'accordingly
      If .errMsg = "" Then
         .errMsg = .message
      End If
   End With

   ...
   ...

End Function

You want Members with your catalog?

Well I guess there is a reason why one of the first questions you ask in these kinds of situations is: “Did you recently change anything?” They told me that they had changed the path to the directory in their directory catalog configuration document since the specified path was wrong. Aha! A clue. The problem would probably have something to do with the directory catalog configuration.

After a rebuild of the directory catalog (load dircat dircat.nsf -r) without errors i checked one of the group documents in the directory catalog – it was empty. I checked the configuration document and saw that the list of fields to include didn’t mention the “Members” field. As mentioned in the help this is fine if the directory catalog is for mobile users since group expansion is done of the server. Here however, the directory catalog is on the server and is specified in the server document so the Domino server uses it to address e-mail.

This explained why no Non-delivery report was sent and why e-mail appeared to simply disappear. From the perspective of the Domino server the group was valid but was empty.

After adding the “Members” field to the list of fields to include and rebuilding the directory catalog yet again it worked. For some reason the wrong path to the directory combined with the error in the configuration document had masked the error.

Mental note to self: “Always make sure the Members field is included in server-side directory catalogs.”

Temporary solution to broken Domino Web Access in Firefox 1.5

I found a solution to my problems with Firefox 1.5 and Domino Web Access. The solution is simply to press the Esc-key once the window with the richtext editing has loaded. This allows you to write new and reply to e-mail. Probably not the way Lotus intended it to work but it doesn… Looks like I’ll have to file a PMR with Lotus Support.

Re: Domino Web Access broken in Firefox 1.5?

Well it appears I’m not the only one having problems with Domino Web Access in Firefox 1.5 and it appears I’m not alone. Chris and Richard are also having some problems with it…

I have been looking into it some more and have tried various combinations of Java. I thought it might be it since I recently downgraded my JRE from 1.5 to 1.4.2 due to some deployment issues with a J2EE application. But unfortunately it wasn’t it. After poking a little more I took a look at the CSS using the JavaScript console (in the Tools-menu and it appears there are numerous CSS errors there.

Considering that the styling of the richtext editing is probably being done with CSS it makes sense that CSS errors would cause havok. Misspellings in the CSS probably doesn’t help either… 🙂

In a VM I just rechecked all the previous versions leading to Firefox 1.5 (RC3, RC2, RC1, B2, B1) without any success. I guess I’ll have to downgrade to 1.0.7 to use Domino Web Access in Firefox.

Bummer – now I know what to ask Santa for Christmas… 🙂