Communardo Software GmbH, Kleiststraße 10 a, D-01129 Dresden
0800 1 255 255

Confluence Persistenz mit Bandana - Fallstricke

Die Confluence API bie­tet über das Bandana-Framework eine ein­fa­che Möglichkeit zum Speichern belie­bi­ger Java Objekte an. Im Prinzip muss man zu dem Objekt nur einen einen Kontext (bezieht sich auf einen bestimm­ten Confluence Space oder ist glo­bal) und einen bezüg­lich die­ses Kontextes ein­deu­ti­gen Key ange­ben und der BandanaManager küm­mert sich um den Rest. Der Vorteil liegt auf der Hand: Man muss sich nicht mit dem Persistenzmodell von Confluence (basiert auf Hibernate) aus­ein­an­der set­zen. Davon rät Atlassian ohne­hin ab, Zitat:

Unless you really under­stand our code, some­thing weird will hap­pen.

Das Speichern von Daten über Bandana geschieht im Wesentlichen in zwei Schritten

  1. Serialisierung der zu per­sis­tie­ren­den Objekte mit­tels XStream
  2. Ablegen des Resultates in der Datenbank

Diese Form der Persistenz hat aller­dings auch ihre Tücken. Als ich vor Kurzem bei der Entwicklung eines Plugins mal wie­der Bandana ver­wen­dete bin ich über das fol­gende Verhalten gestol­pert:

Ich hatte zwei Container-Datenstrukturen gespei­chert, eine Map und eine List. Nachdem einige kurze Tests posi­tiv ver­lie­fen, wen­dete ich mich einer ande­ren Komponente des Plugins zu. Um diese zu tes­ten baute ich mein Plugin neu und instal­lierte es über die Weboberfläche (ohne etwas an mei­ner "Persistenzschicht" zu ändern). Allerdings musste ich nun fest­stel­len, dass eine der Datenstrukturen (die Map) nicht mehr gela­den wer­den konnte.  Ein wei­te­rer Test offen­bahrte noch wun­der­li­che­res Verhalten: Nachdem ich die Map erneut gespei­chert hatte, konnte ich sie wie­der aus­le­sen, aller­dings nur bis zum nächs­ten Deploy des Plugins.

Was war pas­siert? An einem fal­schen Kontext oder Key konnte es nicht lie­gen, da diese bereits zuvor das Laden der Map ver­hin­dert hät­ten. Der Verdacht viel schnell auf irgend­ei­nen Cache. Und tat­säch­lich, nach kur­zer Recherche bin ich auf diese Seite bei Atlassian gesto­ßen. Dort wird beschrie­ben, dass es eigens für Bandana einen Cache gibt. Meine erste Vermutung war nun, dass die Map aus irgend einem Grund nie den Weg durch den Cache in die Datenbank geschafft hatte, der dann bei der Neuinstallation des Plugins ver­fiehl. Ein kur­zer Blick in die Datenbank und ein wenig Debugging über­zeug­ten mich dann aber vom Gegenteil: Die Daten waren in der Datenbank, schaff­ten es von da aber nicht mehr in den Cache (kur­zer Hinweis: die Tabelle für die Bandana-Daten heißt tref­fen­der Weise BANDANA). Nach wei­te­rem Debugging stand fest, dass die Daten zwar noch aus der Datenbank geholt wur­den, im Anschluss aber die Deserialisierung fehl schlug. Nach einem erneu­ten Blick in die Datenbank war schnell klar warum. Ich hatte Enums als Schlüssel für die Map ver­wen­det. Diese waren feh­ler­haft seria­li­siert wur­den, so dass sie (und mit ihnen die Map) nicht wie­der­her­ge­stellt wer­den konn­ten. Das liegt wohl daran, dass Confluence die XStream Version 1.1.1 ver­wen­det aber Enums erst mit der Version 1.1.2 unter­stützt wer­den. Da die Daten im Cache immer unse­ria­li­siert gespei­chert wer­den, fiel die­ses Problem erst auf, als der Cache ver­fal­len war. Die Lösung war nun recht ein­fach: Nach dem Ersetzen der Enums durch Strings ver­hielt sich mein Plugin wie erwar­tet.

Enums sind nicht die ein­zi­gen Datenstrukturen, mit denen es Probleme gibt. So sollte man es auch ver­mei­den in einem Plugin defi­nierte Typen mit­tels Bandana zu spei­chern, wenn diese kei­nen Default-Konstruktor haben. Der Grund: Diese Typen wür­den, wie in die­sem Issue beschrie­ben, beim Deserialisieren mit dem ClassLoader der Web-Applikation gesucht wer­den. Der kann sie aber nur fin­den, wenn sie im Classpath der der Web-Applikation lie­gen, was bei über die Weboberfläche instal­lier­ten Plugins nicht der Fall ist (dafür hat jedes Plugin sei­nen eige­nen ClassLoader). Also dürf­ten in die­sem Fall eben­falls die oben beschrie­be­nen Probleme auf­tre­ten.

Related Posts

1 Kommentar

Hi,

Apologies that I am com­men­ting in English – but my German is quite rusty. You may find the fol­lowing arti­cle of inte­rest in rela­tion to enums and per­sis­ting such data in Bandana: http://tr.im/k5dv.

Also, and for­give me if I am misun­derstan­ding the post (rely­ing on Google trans­late!), it is pos­si­ble to store plugin defi­ned clas­ses wit­hin Bandana. In order to achieve this, it is necessary to set the class­loa­der of XStream to the class­loa­der of the plugin and con­vert the objects to be per­sis­ted into XML (as noted here: http://confluence.atlassian.com/display/DOC/Persistence+in+Confluence?focusedCommentId=185062#comment-185062):

// Store the object
XStream xStream = new XStream();
xStream.setClassLoader(getClass().getClassLoader());

String xml = xStream.toXml(myObject);
bandanaManager.setValue(ctx, key, xml);

// Later, retrieve the object
myObject = (MyObjectClass)xStream.fromXml(bandandaManager.getValue(ctx, key));

Comments are closed.

Pin It on Pinterest