Sequential numbers, talk to any Domino developer and they will shudder when you bring up the subject. They are one of the hardest things to implement in a Domino application. There are many suggestions on how to do sequential numbers in domino apps and they all have advantages and disadvantages.
One of the most common ways is to use an agent which allows for documents to replicate to a central location before assigning them their number. The main problem with this approach is that the numbers don't get assigned till a later time and the users of the application will more then likely need the number immediately.
Another method involves using profile documents but this can fail because profile documents are cached on each users computer and it is even worse for web based applications because each HTTP thread has its own cache so even users on the same server generating sequential numbers could end up with a duplicate number.
With XPages, however, they way be a solution but before I go into details I'll stress that this is not a perfect solution because it is designed for a single application running on a single server and not for a replicated application.
In SSJS in XPages there is a feature called 'synchronized'. Any code inside a synchronized code block will only execute once at a time. If two people in two different sessions click on a button that runs the SSJS at the same time then whichever thread hits the SSJS code first will start running the synchronized block first while the other persons thread will wait till the first persons thread has finished and then once the first persons thread has finished the second persons thread will run the synchronized block of SSJS. Think of it as a car wash, it's only possible to wash one car at a time while all the other cars that need to use the car wash will queue up outside waiting for the car in front of them to be finished.
Here is a simple version of the code that I built, In my final version I broke out the initial loading and saving of the number store document into separate functions so that I didn't have to repeat the code multiple times. The code below is simplified for demo reasons.
function simplegetSequentialNumber(){
synchronized(applicationScope){
if (applicationScope.containsKey("seqNumber")){
var seqNNNN = @Right("0000" + @Text(applicationScope.get("seqNumber")),4);
applicationScope.put("seqNumber",applicationScope.get("seqNumber") + 1);
var seqView:NotesView = database.getView("vw_SequentialNumberStore");
var seqNumberDoc:NotesDocument = seqView.getFirstDocument();
seqNumberDoc.replaceItemValue("seqNumber",applicationScope.get("seqNumber"));
seqNumberDoc.replaceItemValue("seqYear",applicationScope.get("seqYear"));
seqNumberDoc.save(true,true);
} else {
var seqView:NotesView = database.getView("vw_SequentialNumberStore");
try {
var seqNumberDoc:NotesDocument = seqView.getFirstDocument();
applicationScope.put("seqNumber",seqNumberDoc.getItemValueInteger("seqNumber"));
var seqNNNN = @Right("0000" + @Text(applicationScope.get("seqNumber")),4);
applicationScope.put("seqNumber",applicationScope.get("seqNumber") + 1);
seqNumberDoc.replaceItemValue("seqNumber",applicationScope.get("seqNumber"));
seqNumberDoc.replaceItemValue("seqYear",applicationScope.get("seqYear"));
seqNumberDoc.save(true,true);
} catch(e) {
var seqNumberDoc:NotesDocument = database.createDocument();
seqNumberDoc.replaceItemValue("form","fm_SequentialNumberStore");
seqNumberDoc.replaceItemValue("seqNumber",1);
var seqNNNN = @Right("0000" + @Text(applicationScope.get("seqNumber")),4);
applicationScope.put("seqNumber",applicationScope.get("seqNumber") + 1);
seqNumberDoc.replaceItemValue("seqNumber",applicationScope.get("seqNumber"));
seqNumberDoc.replaceItemValue("seqYear",applicationScope.get("seqYear"));
seqNumberDoc.save(true,true);
}
}
}
return seqNNNN;
}
The first thing it does is check to see if the applicationScope contains the sequential number counter. if it does then it gets the number and then adds 1 to the applicationScope. it then finds the number store document and updates the value in it.
If the sequential number is not in the applicationScope it will open the view where the number store document and tried to get the first ( and only ) document in that view. If it finds the document it gets the value, puts it into the applicationScope and then increases it and resaves the new value. if it doesn't find the document it means that it has never existed so it creates a new document, sets the first sequential number to 1, increases it in the applicationScope and then saves the document.
The reason for saving the document each time is because the applicationScope only exists for the life of the application. If nobody uses the application for 60 minutes then the applicationScope gets removed from memory ( 60 is the default and can be changes as required ). So if you don;t save the value off to a document somewhere then you'd end up with numbers restarting at 1 every time the app hasn't been used for a while.
The code above is a simple version of what I finally implemented. You could make this code better by removing the need for the getView() and getFirstDocument() by using a fixed UNID and then just doing a database.getDocumentByUNID() call. You could possibly make it multi-server aware by opening a database on a central server for find/store the document but this relies on the network connection between servers being up and running and might not be worth the risk.
However you do your sequential numbers, if your using SSJS and XPages then make sure you make use of the synchronized block to make sure only one person at a time can increase the number...
Sequential Numbers. There's no need to be afraid of them any more...