Persistence in Confluence

Available:

Confluence 2.2 and later

Changed:

In Confluence 3.3 and later, plugins can define their own contexts using the KeyedBandanaContext interface.

There are three main persistence APIs which are used in Confluence. Each of these APIs is discussed on this page:

  1. Bandana – XML persistence, easy to use in plugins.
  2. Hibernate – database persistence, difficult to extend.
  3. Content properties – database persistence for properties associated with a piece of Confluence content.

Because Bandana is the primary persistence API used by plugin developers, it will be covered in more detail than the other two APIs.

Bandana

Bandana is an Atlassian framework for persistence of arbitrary Java objects. The concepts used in Bandana are very simple:

  • Bandana stores data in contexts. Confluence defines one global context and one context per space. The relevant class is ConfluenceBandanaContext. In Confluence 3.3 and later, plugins can define their own contexts using the KeyedBandanaContext interface.
  • Each context stores key-value pairs. The key is a String and the value can be any Object. It should typically implement Serializable. If the key or value types are defined within a plugin, the class should have a no-argument constructor to avoid class loading issues.

Based on this design, the BandanaManager has methods for storing and retrieving values from a context by key:

  • void setValue(BandanaContext context, String key, Object value) – store a value against a key in the Bandana context.
  • Object getValue(BandanaContext context, String key) – get a key's value from the Bandana context. Returns null if no matching context and key exists.
  • void removeValue(BandanaContext context, String key) – remove a key and value from the Bandana context. (Available in Confluence 3.3 and later.)
  • Object getValue(BandanaContext context, String key, boolean lookUp) – same as above, except if lookUp is true and the context is a space context, this method will also check the global context if no matching key is found in the space context.
  • Iterable<String> getKeys(BandanaContext context) – provides an iterable to allow enumeration of all keys within a context. (Available in Confluence 3.3 and later.)

For plugins which use a context not provided by the application, we recommend that you use a context for your Bandana values that includes the full package name of your plugin. For example, a theme plugin might use a context like org.acme.confluence.mytheme.importantPreference.

Serialization

By default, Bandana uses XStream to convert objects into XML for storage. It is however possible to provide your own method of serialisation. If your BandanaContext implements the BandanaSerializerFactory interface (available in Confluence 3.3 and later) it will be used to create an serialiser to serialise and deserialise your objects.

Data storage

Prior to Confluence 2.3, this XML was written to the filesystem in the Confluence home directory. The file config/confluence-global.bandana.xml stores the global context, and there is a file config/spaceKey/confluence-space.bandana.xml with the configuration for each space. In Confluence 2.3 and later, Bandana data is written to the BANDANA table in the database, with three columns for context, key and a serialized value.

Getting access to BandanaManager

To get access to the BandanaManager from your plugin code, normally you only need to include a private BandanaManager field with an associated constructor parameter. Spring will construct your object and pass in the required component.

Hibernate

Confluence uses the open source persistence framework Hibernate. Confluence 2.2.x uses Hibernate version 2.1.8.

Each object to be persisted has a *.hbm.xml file which sits in the same directory as the associated class in the Confluence web application. For example, Label.class has an associated Label.hbm.xml which describes how label objects will be persisted. The particular details vary from class to class, but typically include:

  • the database table used to hold the data (Confluence bootstrap creates these tables if they do not exist)
  • the column names and mappings to class attributes
  • any special queries used for functionality in Confluence (for example, to retrieve a list of personal labels)

All this data is expressed in the standard Hibernate mapping format. In some cases, there is a single mapping file for all subclasses of a particular class. For example, ContentEntityObject.hbm.xml includes mappings for pages, news, mail and space descriptions.

The Hibernate mapping files are listed in mappingResources bean in applicationContext.xml. The list of Hibernate types (and mapping files) cannot be extended dynamically by plugins.

Although it might be possible to extend Confluence's database through Hibernate, this is not recommended. There are a few downfalls with extending our Hibernate configuration:

  1. You need to maintain your forked copy of the Hibernate mappings file against each new version of Confluence.
  2. Your new Hibernate objects will not be protected from (or necessarily upgraded to) any changes we make in the schema in future versions.
  3. Unless you really understand our code, something weird will happen.

Avoid using Confluence's database to store custom data – use content properties or Bandana instead.

Content properties

Another form of persistence, content properties are key-value pairs associated with a ContentEntityObject and stored in the database.

Content properties are accessed through the ContentPropertyManager like this:

You should get the ContentPropertyManager and PageManager injected into your macro, servlet, etc. using the techniques outlined on Accessing Confluence Components from Plugin Modules (also demonstrated in the section above).

Powered by Confluence and Scroll Viewport