In this article I'm going to present the solution I used to implement sequential numbering of documents in a highly distributed environment.
I noticed that many inexperienced Lotus developers tend to use unreliable document numbering solutions, usually employing view counting method.
To be honest, the first thing that came to my mind in my early days was view counting, but then, very soon it started yielding duplicate numbers...
Why "counting view documents" approach is not reliable way to go?
Let's analyze how that works:- new document receives save request from the user (QS) and calls the function that gets next number
- the function gets a database and a view (sorted descending by doc number)
- the function sets the reference to the top view document
- the function reads the value from the document field (or counts the number of docs in a view)
- the function returns last number incremented by 1
- new document receives value from the function and stores it in a field
- new document is saved
Now, what if there are multiple new documents being saved on, lets say 3 workstations, at the same time.
Notice that steps 4 to 7 are all critical. If someone saves his new document while you're on any of those steps, his document is going to receive the same sequence number as yours.
The point is that you need some kind of locking mechanism.If you want to read more about this subject search on:
- critical section
- thread (thread safe, semaphores, monitors)
- mutex
This is how the NotesDocument's Lock method works (available from version 6):- if the document is not locked, the method places the lock and returns True
- if the document is locked and the current user is one of the lock holders, the method returns True
- if the document is locked and the current user is not one of the lock holders, the method returns False
- if the document is modified by another user before the lock can be placed, the method raises an error
This approach requires that you either:- always have access to administration server (server that provides document locking)
- or use composite document key where one of the components would be location (OU - organizational unit), which would then produce sequential numbering on OU level, and that way globaly provide unique document keys
Design prereqs and implementation1. Create new ScriptLibrary (or open an existing one) and add this function to it:
Listing 1: Function for number generation
Function GenSeqNo( docType As String ) As
String ' mbonaci, 11.03.2006 ' - returns next
sequence number (as String) for document of type 'docType'
' - increments counter doc ' ' add your custom
error handling Dim s As New NotesSession Dim w As New
NotesUIWorkspace Dim db As NotesDatabase Dim v As
NotesView Dim doc As NotesDocument Dim nextNum As
Integer
' db that holds counter docs (see step 5 bellow) - it's best to store these
settings in a profile doc Set db = s.GetDatabase(
"your_server_name", "database_relative_path" )
' you may want to replace msgbox with something that best suits
your needs If db Is Nothing Then Msgbox "Lock db
doesn't exist" End If
' exit if document locking is
not enabled in db properties If Not db.IsDocumentLockingEnabled
Then Msgbox "Document locking not enabled" Exit
Function End If
Set v = db.GetView( "SeqNoView" )
' get the view containing the counter doc Set doc =
v.GetDocumentByKey( docType, True ) ' locate counter doc for
docType Call doc.Lock( True ) nextNum = Cint(
doc.strSeqNo(0) ) + 1 ' retrieve next number
doc.strSeqNo = Cstr( nextNum ) ' increment existing number
in counter doc Call doc.Save( True, False, False ) '
save should always return true - doc is locked by myself Call
doc.Unlock( )
GenSeqNo = Cstr( nextNum )
End Function |
2. In your db, on each form you want to implement numbering for:
- add hidden, text field
- strDocType
- use the script library you just created in Form's Globals > Options (Use "library_name")
- hard-code this in PostOpen event:
Listing 2: Form's PostOpen event - adding type to a document
Sub Postopen(Source As Notesuidocument)
' Adding value to type field of new documents If
source.IsNewDoc Then source.document.strDocType =
"document_type_name" End If End Sub |
- add this piece of code to QuerySave form event:
Listing 3: Form's QuerySave event - obtain new number when document is first saved
Sub QuerySave( Source As NotesUIDocument,
Continue As Variant ) Get sequence number from QuerySave, but
only if it hasn't yet been issued Dim newSeq As String
If source.document.sRegNo(0) = "" Then newSeq = GenSeqNo(
source.document.strDocType(0) )
source.Document.sRegNo = "composite_key_components-" & newSeq
End If End Sub |
5. Create new (blank) database that will hold counter docs (we'll call it "counter db")
6. On counter db:
- db Access control > Advanced tab > Administration server > select option "Server" and choose master lock server from the list
- db properties > first tab > check option "Allow document locking"
- create new form (counter doc) and add two text fields:
- docType holds document type name (value from your document's strDocType field)
- strSeqNo
holds last issued number for that doc type
- create new view that would:
- show all counter docs
- have first column show docType field, categorized, ascending
7. Create new counter document for each document type
- in docType field enter the same value you hard-coded in form's PostOpen event, in strDocType field
- in strSeqNo field enter 0 (or any other number you want start counting from)
That's it, now test...
To test your solution (whichever you choose to implement) you should create an agent that requests 1000 sequence numbers (by calling getSeqNo function 1000 times).
Then start the agent on at least three workstations simultaneously.
If all the numbers are unique you're good to go.