XPages: Creating save conflicts mixing DominoDocument and Document methods

by Bernd Hort,
assono GmbH, Standort Hamburg,

XPages

We heard some users complaining about some save conflicts in one of our XPages application. They got this irritating message:

Document has been saved by another user - Save created a new document as a response to that modified document.

The users have asked all their colleagues but no one had been working on the same document.

Taking a closer look at those documents we found that the conflict document has been created nearly at the same time as the original document had been saved. Both documents had been saved by the same user. The conclusion was obvious. There was something wrong with our code. The strange thing was that the conflict only happened very infrequently, which made searching for the problem a little difficult. wink.gif

Finally after a hint from Tony McGuckin of the Ireland XPages lab we were able to narrow it down to a timing issue when using the Java classes DominoDocument and Document together.

For demonstration purposes I wrote a test database with a simple Java method.

public void simplifiedSample(DominoDocument uidoc) {
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:MM:ss");
            uidoc.replaceItemValue("Status", "approved");
            uidoc.save();
            System.out.println(sdf.format(new Date()) + " uidoc.save() "
                    + uidoc.getDocumentId());

            java.lang.Thread.sleep(10000);

            Document backendDoc = uidoc.getDocument(true);
            Item authorItem = backendDoc.replaceItemValue("CurrentAuthor",
                    "CN=Bernd Hort/O=assono");
            authorItem.setAuthors(true);
            backendDoc.save();
            System.out.println(sdf.format(new Date()) + " backendDoc.save() "
                    + backendDoc.getUniversalID());

            java.lang.Thread.sleep(10000);

            uidoc.replaceItemValue("SomeOtherItem", "anotherValue");
            uidoc.replaceItemValue("Subject", "Test " + sdf.format(new Date()));
            uidoc.save();
            System.out.println(sdf.format(new Date()) + " uidoc.save() "
                    + uidoc.getDocumentId());

            java.lang.Thread.sleep(10000);

            backendDoc = uidoc.getDocument(true);
            Item readerItem = backendDoc.replaceItemValue("Supervisor",
                    "CN=Bernd Hort/O=assono");
            readerItem.setReaders(true);
            backendDoc.save();
            System.out.println(sdf.format(new Date()) + " backendDoc.save() "
                    + backendDoc.getUniversalID());

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

The actual code is way more complex. There are a lot of calls to different methods in business logic classes. Some of them have to use the back end classes Document (corresponding to NotesDocument in LotusScript) because the DominoDocument does not provide the needed methods (like defining an author item).

If I remove the java.lang.Thread.sleep(10000) calls in my simplefiedSample method, I don't get a save conflict. With the Thread.sleep calls there is a save conflict every time.

Looking at the log it is easy to spot where the save conflict occurs.

HTTP JVM: 2013-07-22 15:03:52 uidoc.save() 94E
HTTP JVM: 2013-08-22 15:03:02 backendDoc.save() DFB7E2DFD82C80F6C1257B36004D9FAA
HTTP JVM: 2013-08-22 15:03:12 uidoc.save() 952
HTTP JVM: 2013-08-22 15:03:22 backendDoc.save() 52EB22DEF8D31AC6C1257B36004DA77D

When the DominoDocument object (uidoc) is saved for the second time there must be some kind of timestamp comparison. The XPages runtime environment discovers the differences and saves the changes as a save conflict. Which by the way would explain why the users only get a error one out of ten times.

A fix for the problem is quite easy. If I skip the second save there is no save conflict.

            // uidoc.save();
            // System.out.println(sdf.format(new Date()) + " uidoc.save() "
            // + uidoc.getDocumentId());
            System.out.println("skip second uidoc save");

Here is the log for the test without the second uidoc.save():

HTTP JVM: 2013-15-22 15:03:44 uidoc.save() 95E
HTTP JVM: 2013-15-22 15:03:54 backendDoc.save() BB088DF6768831FDC1257B36004E5861
HTTP JVM: skip second uidoc save
HTTP JVM: 2013-16-22 15:03:14 backendDoc.save() BB088DF6768831FDC1257B36004E5861 

Sure the best way would be to work only with the back end classes. This would minimize the risk of any problems. wink.gif

Technical article JavaScript Java XPages

Sie haben Fragen zu diesem Artikel? Kontaktieren Sie uns gerne: blog@assono.de

Do you want an individual solution? Contact us

More interesting entries

Any questions? Contact us.

If you want to know more about our offers, you can contact us at any time. There are several ways to contact us for a non-binding first consultation.

We don’t sell your data. 100% guaranteed. See: Privacy Policy
assono GmbH

Location Kiel (headquarters)
assono GmbH
Lise-Meitner-Straße 1–7
24223 Schwentinental

Location Hamburg
assono GmbH
Bornkampsweg 58
22761 Hamburg

Phone numbers:
Human resources department: +49 4307 900 407
Technical department: +49 4307 900 403
Marketing department: +49 4307 900 402

E-Mail adresses:
contact@assono.de
bewerbung@assono.de