Kennen Sie schon unseren Podcast? Thomas Bahn diskutiert mit Gästen aus Wirtschaft und Wissenschaft über Künstliche Intelligenz und Chatbots. Jetzt reinhören

de · en

XPages: Speicher- und Replikationskonflikte bei gemeinsamer Verwendung von DominoDocument and Document

von Bernd,
assono GmbH, Standort Hamburg,

In einer unserer XPages Anwendungen gab es unregelmäßig ein Problem mit Speicher- und Replikationskonflikten. Die Anwender bekamen die irritierende Meldung:

Das Dokument wurde von einem anderen Benutzer gespeichert. Mit dem Speichern wurde ein neues Dokument als eine Antwort auf das geänderte Dokument erstellt.

Wenn die betroffenen Anwender herumgefragt haben, hat kein Kollege zu der Zeit an dem Dokument gearbeitet.

Als wir daraufhin die Dokumente näher untersucht haben, fanden wir heraus, dass das Speicherkonflikt-Dokument unmittelbar nach dem Speichern des Hauptdokumentes gespeichert worden war. Beide Dokumente wurden vom gleichen Anwender gespeichert. Offensichtlicherweise lag das Problem in unserem Code. Weil diese Speicherkonflikte aber nur sehr sporadisch auftauchten, war die Suche nach der Ursache sehr schwierig.

Letztendlich gab uns Tony McGuckin aus dem XPages Labor in Irland den entscheidenden Hinweis. Der Speicherkonflikt wurde verursacht durch ein Timing-Problem bei der gemischten Verwendung der Javaklassen DominoDocument und Document.

Für Demonstrationszwecke habe ich eine Testdatenbank mit einer einfachen Javaklasse geschrieben:

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);
 }
 }

Der eigentliche Produktionscode ist wesentlich komplexer. Es werden eine Reihe von Methoden mit entsprechender Business Logik aufgerufen. Einige dieser Methoden müssen die Backend-Klasse Dokument (entspricht NotesDocument in LotusScript) verwenden, weil in der Klasse DominoDocument die notwendigen Methoden nicht vorhanden sind, z.B. um ein Authorenfeld zu erzeugen.

Wenn in der obigen Beispielmethode die Aufrufe von java.lang.Thread.sleep(10000) auskommentiert werden, wird das Dokument ohne Speicher- und Replikationkonflikt abgespeichert. Mit den Thread.sleep-Aufrufen entsteht bei jedem Aufruf ein Speicherkonflikt.

Beim Blick auf die Serverkonsole kann sehr gut gesehen werden, wo der Speicherkonflikt entsteht.

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

Wenn das DominoDocument Objekt (uidoc) zum zweiten Mal gespeichert wird, werden die Zeitstempel verglichen. Die XPages Laufzeitumgebung stellt eine Abweichung zwischen Frontend- und Backend-Dokument fest und erzeugt den Speicherkonflikt. Weil in unseren XPage-Anwendung diese Abweichungen in den Zeitstempeln nur sehr selten auftraten, gab es auch nur wenige Meldungen.

Das Problem lässt sich sehr einfach beseitigen. Wenn der zweite Aufruf für uidoc.save() entfernt wird, gibt es keinen Speicherkonflikt und alle Feldänderungen werden ordnungsgemäß gespeichert.

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

Hier ist die Serverkonsole für den Test ohne das zweite 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 

Natürlich wäre es besser gewesen, von vorne herein nicht die Verwendung von DominoDocument und Document zu mischen. wink.gif

Fachbeitrag JavaScript Java XPages

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

Sie haben Interesse an diesem Thema?

Gerne bieten wir Ihnen eine individuelle Beratung oder einen Workshop an.

Kontaktieren Sie uns

Weitere interessante Artikel

Sie haben Fragen?

Wenn Sie mehr über unsere Angebote erfahren möchten, können Sie uns jederzeit kontaktieren. Gerne erstellen wir eine individuelle Demo für Sie.

assono GmbH

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

Standort Hamburg
assono GmbH
Bornkampsweg 58
22761 Hamburg

Telefonnummern:
Zentrale: +49 4307 900 407
Vertrieb: +49 4307 900 411

E-Mail-Adressen:
kontakt@assono.de
bewerbung@assono.de