Skip to main content
    Country/region select      Terms of use
     Home      Products      Services & solutions      Support & downloads      My account     

developerWorks  >  Lotus  >  Forums & community  >  Best Practice Makes Perfect

Best Practice Makes Perfect

A collaboration with Domino developers about how to do it and how to get it right in Domino

As I have time, I'm working on a couple of the tools I mentioned previously -- the design element search and the toolkit of useful functions for developers. As I go along, I'm finding there are little bits that people might be able to reuse as workarounds or augmentations.

My latest dilemma: I needed data written into a stream using an appropriate character set. The process I'm using (MIME decoding of base64 data) was writing binary data into the stream and I knew it was really UTF-8 character data, but there was no way to tell the stream that, so that the data could be read out as text. The problem: you can only associate a character set with a stream by connecting the stream to a file. Clearly, then, we need a temporary file somewhere. But where to put it? The natural solution is to make it in a temp directory somewhere. I also want it to work on all the supported Notes client platforms, and each of these has a different way to locate temporary directories, as well as different rules for constructing filepaths. So it boils down to three problems:
1.        Find a temporary folder to put the files.

2.        Construct a filepath by adding a unique filename to the filepath.

3.        Manage the resulting file and stream.

I decided to write my temp file in the Notes temp folder, using a filename supplied by the @Unique function. To locate that folder, there is no built-in LotusScript function, but there's a C call in the 'notes' library that you can call. You have to declare the function using the right library name for each of the platforms you want to support, then at runtime, call the right one for your platform.

Declare Function w32_OSGetSystemTempDirectory Lib "nnotes" Alias "OSGetSystemTempDirectory" ( Byval S As String, Byval wBufLen As Integer) As Integer
Declare Function mac_OSGetSystemTempDirectory Lib "NotesLib" Alias "OSGetSystemTempDirectory" ( Byval S As String, Byval wBufLen As Integer) As Integer

Declare Function linux_OSGetSystemTempDirectory Lib "" Alias "OSGetSystemTempDirectory" ( Byval S As String, Byval wBufLen As Integer) As Integer

Const ERR_UNSUPPORTED_PLATFORM = 20300 ' or other value you choose.

Function GetNotesTempDirectory() As String

' Returns the path of the temporary directory used by Notes.

' Not same as system or user temp dir that you can get e.g. with Environ("TEMP") in Windows.

' Main reasons to use this instead: works crossplatform, and partitioned servers each need

' their own temp dir to avoid interfering with each other.

Dim session As New NotesSession

Dim d As String * 256

Dim s%

Select Case session.Platform

Case "Linux"

  s% = linux_OSGetSystemTempDirectory(d, 255)

Case "Macintosh"

  s% = mac_OSGetSystemTempDirectory(d, 255)

Case "Windows/32"

  s% = w32_OSGetSystemTempDirectory(d, 255)

Case Else

  Error ERR_UNSUPPORTED_PLATFORM, "In GetNotesTempDirectory, platform not supported: " & session.Platform

End Select

GetNotesTempDirectory = Left$(d, s%)

End Function

Similarly, building a filepath requires different code depending on the OS. In each case, we can append the filename to the folder path, but must delimit them with some character which is different on different platforms.

Function PathDelimiter() As String
Static SdirChar$

If SdirChar = "" Then

  Dim session As New NotesSession

  Select Case session.platform

  Case "Macintosh"

     SdirChar = "/"

  Case "OS/2v1", "OS/2v2", "MS-DOS", "Windows/16", "Windows/32"

     SdirChar = "\"

  Case "UNIX", "Linux", "OS/400"

     SdirChar = "/"

  Case Else

     Error 13445, "Unknown platform: " & session.Platform

  End Select

End If

PathDelimiter = SdirChar

End Function

Now, to manage the stream object and to make sure the temporary file gets deleted when we no longer need it, a class. I use OOP here to keep everything about managing the temp file and stream together in one place, and to take advantage of the Delete event, which runs even if the code terminates abnormally, to close and get rid of the temp file.

Class TempFileStream
m_stream As NotesStream

m_filepath As String

Sub New(char_set As String, Byval suffix As String)

   Dim session As New NotesSession

   If suffix = "" Then suffix = "dat"

   Set m_stream = session.CreateStream

   Dim unik

   unik = Evaluate({@Unique})

   Dim strTempPath$, strDelim$

   strTempPath = GetNotesTempDirectory()

   strDelim = PathDelimiter

   If Right(strTempPath, 1) <> strDelim Then strTempPath = strTempPath & strDelim

   m_filepath = strTempPath & unik(0) & "." & suffix

   m_stream.Open m_filepath, char_set


End Sub

Public Property Get Stream As NotesStream

   Set Stream = m_stream

End Property

Public Property Get Text As String

   m_stream.Position = 0

   Text = m_stream.ReadText

End Property

Sub Delete

   On Error Resume Next


   Delete m_stream  ' note this will clear the stream object from all variables, in case you made a copy.

   Kill m_filepath

End Sub

End Class

Once you've got all this, it's easy to create the stream and associate a particular character set.

Dim tfs As New TempFileStream("UTF-8", "xml")
Dim stream As NotesStream

Set stream = tfs.Stream

You must keep the TempFileStream object in scope while using the stream it creates. Since its destructor deletes the temp file and stream, copies of the Stream property become invalid at that time.

Andre Guirard | 30 September 2007 10:14:24 PM ET | Plymouth, MN, USA | Comments (7)


1) Maybe I’m not remembering this clearly...
Nathan T. Freeman | 10/1/2007 6:52:20 AM

...but I thought all major OS platforms currently support "/" as a directory structure separator.

Also, this sounds like a great change to request of the Notes Stream class.

Lastly, did you try the Environ function to see if you can just get the OS environment variable? Much safer than all that platform-dependent C library stuff.

2) hurmm...
Kerr | 10/1/2007 9:58:07 AM

Andre, can you fill in a little more of the last snippet of code? I'm a bit lost as to why you need to do this and a fuller example might fill in the gaps. How are you getting the base64 encoded binary and where are you trying to write it too?

3) AIX?
Brian Miller | 10/1/2007 10:19:02 AM

Would this work if I added an extra line for AIX? I might have a use for this on the server.

4) OSGetSystemTempDirectory
Craig Schumann | 10/4/2007 4:03:38 PM

You might want to mention that the function OSGetSystemTempDirectory is not officially documented or supported..... at least not as far as I can tell in the Notes 8 CAPI....

Any more helpful functions you want to leak out? :)

5) TempFileStream class
YCI | 4/21/2008 4:54:43 AM

I once met the same issue with an in memory stream. I was fighting to get it as an UTF-8 coded stream. I used the same workaround : save the stream as a file and the read the file as an UTF-8 stream. It worked fine but what a mess for performance ! I finally won the battle by writing a space to the in memory stream before appending the rest of the stream. The stream was properly intialized as UTF-8 just by writing a space : no more need to store it as a file !

6) OSGetSystemTempDirectory params wrong
Markus Seitz | 6/8/2012 3:25:58 AM

takes length of input string as second param. seems to work fine for 32bit (guess there's some large number on the stack where this function reads the param) but fails on 64bit.

7) Re: OSGetSystemTempDirectory params wrong
Andre Guirard | 6/13/2012 7:44:45 PM

Marcus, thanks for the correction, code in post is corrected. I didn't do anything for 64bit as yet.

 Add a Comment
Comment:  (No HTML - Links will be converted if prefixed http://)
Remember Me?     Cancel

Search this blog 


    About IBM Privacy Contact