This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.

Entity History Implementation

The entry point for the history implementation is the HistoryService. This class is a ContextCreationListener, which means that it will be notified if a new Context is created.

In order for this to work properly it needs to be loaded eagerly so that the service can register itself with the ContextManager when the application is initialized.

Whenever a new Context is created, several listeners will be added to the new context (all these listeners are removed again when the context is closed):

HistoryEntityFacadeListener

This listener is an EntityFacadeListener, which will be notified about all entity changes during a transaction.

Every entity that is modified during the transaction is stored in the context attribute history-snapshots-entities. Before the entity is stored, it is checked whether it should be included in the history using the HistoryConfiguration (a particular entity model might be excluded from the history or the history might be disabled entirely).

PrepareHistoryDataListener

This listeners is a TransactionAware that is registered by the HistoryService with every newly created transaction.

The prepareCommit method of the TransactionAware is called after all other listeners have already ran, so we can be sure that all changes have been captured by the HistoryEntityFacadeListener at this point.

All entities that have been saved in the history-snapshots-entities attribute will be converted into a DetachedEntity, which contains all necessary information for the snapshot that will be saved to the database. This has to be done before the commit, as some information is no longer available after commit (changed fields for example).

Which fields and relations will be included in the snapshot depends on the entity model and is defined by the HistoryConfiguration.

All the created DetachedEntity are stored in the context attribute history-detached-entities (the attribute history-snapshots-entities will be removed at this point).

HistoryWriter

This CommitListener is executed after the transaction is committed (to make sure that the history is only written when the transaction has been committed successfully). All the DetachedEntity instances are read from the context attribute history-detached-entities and converted to an XML format using the XStream library. The generation of the XML is always executed in the null business unit. The XML structure is defined in the DetachedEntityMarshaller and is exactly the same as in the previous history implementation (in order to be compatible with older history entries). The XML String is gzipped before it is saved to save storage space.

The compressed XML data (along with other data like the username and ip address) are passed to the HistoryDataStore where they are persisted in a dedicated history postgresql database. This is done asynchronously in a separate thread for performance reasons.

HistoryConfiguration

The HistoryConfiguration contains all information whether the history is enabled for a certain entity model and if yes, which fields and relations should be included.

The history can be globally disabled using the nice2.persist.history.enabled property. In addition it can also be disabled for specific entity models using the IgnoredEntityModels contribution. Obviously no history entries will be created for session-only entities.

Which fields and relations are included in the snapshot is controlled by the EntityHistoryConfiguration. There are default implementations for standard (DefaultEntityHistoryConfig) and lookup entities (LookupEntityHistoryConfig). The IgnoredEntityModels contribution mentioned above can also be used the further refine the default implementations by removing certain fields and relations from the snapshot.

However it is also possible to completely customize the history snapshot with a custom implementation (see PageEntityHistoryConfiguration for example).