Configuring Database Triggers

This document has been reviewed for eXist 1.2.

1. Overview

In this section, we discuss the types of database events that may be Triggered by eXist, as well as how Triggers are created and configured. It assumes readers have a basic understanding of XML, Java and XQuery.

Triggers may be configured by the User to respond to document and/or collection events. The current version of eXist defines six events, all of which are applicable to the collection configured with the Trigger:

2. Trigger Types

Triggers may be written in either XQuery or Java and will be Triggered twice, once pre-event and once post-event. The Trigger may respond to either or both the pre and post events.

2.1. XQuery Triggers

Triggers written in XQuery may be configuring by using the org.exist.collections.triggers.XQueryTrigger to fire the XQuery. The XQuery to be executed when the trigger is fired may either be placed in the collection.xconf itself or indicated by a URL.

The XQuery may make use of a number of external variables to determine why and for what the trigger was fired -

2.2. Java Triggers

Triggers written in Java must implement the org.exist.collections.triggers.Trigger interface or one of its extensions such as org.exist.collections.triggers.DocumentTrigger. The Java class for your trigger must be available on the class path, lib/user is a good place to copy your custom trigger to.

The DocumentTrigger interface provides a convenient starting place and provides the methods prepare() (fired pre-event) and finish() (fired post-event())

3. Configuring Triggers

Users configure Triggers using collection-specific configuration files. These files are stored as standard XML documents in the system collection: /db/system/config, which can be accessed using the Admin interface or Java Client. In addition to defining settings for Triggers the configuration document specifies other collection-specific settings such as indexes or default permissions.

The hierarchy of the system collection (/db/system/config) mirrors the hierarchical structure of the main collection. Configurations are therefore "inherited" by descendants in the hierarchy, (i.e. the configuration settings for the child collection are added to or override those set for the parent). It is furthermore possible for each collection in the hierarchy to have its own trigger creation policy defined by a configuration file.

To configure triggers for a given collection - for example: /db/foo - you must create a new .xconf configuration file and store it in the system collection (e.g. /db/system/config/db/foo). You can choose any name for this document so long as it has the .xconf extension, although collection.xconf is recommended. Note that since subcollections will inherit the configuration policy of their parent collections, you are not required to specify a configuration for every collection.

Note

You can store only ONE .xconf configuration document per collection in the system collection /db/system/config. For example, the collection /db/system/config/foo would contain one configuration file and/or other subcollections.

3.1. Configuration Structure and Syntax

Trigger configuration files are standard XML documents that have their elements and attributes defined by the eXist namespace:

http://exist-db.org/collection-config/1.0

All configuration documents have the <collection> root element. These documents also have a <triggers> element below the root element, which encloses the trigger configuration. Only ONE <triggers> element is permitted in a document.

In the <triggers> element are elements that define each trigger and the event(s) that it is fired for.

Each <trigger> element has two attributes, event which is a comma seperated list of events to fire for and class which is the name of the Java Class to fire on the event. It may also contain several <parameter> elements defining any parameters to send to the trigger.

Configuring an XQuery Trigger

When configuring an XQuery trigger there are a few parameters that may need to be set -

The following example shows two XQuery Triggers configured, the first executes an XQuery stored in the database whereas the second executes XQuery placed inline in the collection.xconf:

Example: XQuery Trigger Configuration

<collection xmlns="http://exist-db.org/collection-config/1.0">
    <triggers>
		<trigger event="store,update,remove" class="org.exist.collections.triggers.XQueryTrigger">
			<parameter name="url" value="xmldb:exist://localhost/db/myTrigger.xql"/>
		</trigger>
		
		<trigger event="store,update,remove" class="org.exist.collections.triggers.XQueryTrigger">
			<parameter name="query" value="util:log('debug', concat('Trigger fired at ', current-dateTime()))" />
		</trigger>
    </triggers>
</collection>

Configuring a Java Trigger

When configuring a Java Trigger any parameters defined will be passed in a named map to the configure function of the trigger.

The following example shows a Java trigger configured:

Example: Java Trigger Configuration

<collection xmlns="http://exist-db.org/collection-config/1.0">
    <triggers>
		<trigger event="store,update,remove" class="my.domain.testTrigger">
			<parameter name="myParam" value="myValue"/>
		</trigger>
    </triggers>
</collection>

4. Example Triggers

Here are some simple code examples of triggers

4.1. XQuery

Example: Simple Logging Trigger


xquery version "1.0";

(:
	A simple XQuery for an XQueryTrigger that
	logs all trigger events for which it is executed
	in the file /db/triggersLogs.xml
:)

declare namespace  xmldb="http://exist-db.org/xquery/xmldb";

declare variable $local:triggerEvent external;
declare variable $local:eventType external;
declare variable $local:collectionName external;
declare variable $local:documentName external;
declare variable $local:document external;

declare variable $local:triggersLogFile := "triggersLog.xml";


(: create the log file if it does not exist :)
if(not(doc-available($local:triggersLogFile)))then
(
	xmldb:store("/db", $local:triggersLogFile, <triggers/>)
)
else(),

(: log the trigger details to the log file :)
update insert <trigger event="{$local:triggerEvent}" eventType="{$local:eventType}" collectionName="{$local:collectionName}" documentName="{$local:documentName}" timestamp="{current-dateTime()}">{$local:document}</trigger>  into doc("/db/triggersLog.xml")/triggers

        			

4.2. Java

Example: Simple Logging Trigger


import java.io.File;
import java.io.FileOutputStream;

import org.exist.collections.triggers.FilteringTrigger;
import org.exist.collections.triggers.TriggerException;
import org.exist.dom.DocumentImpl;
import org.exist.storage.DBBroker;
import org.exist.storage.txn.Txn;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.value.DateTimeValue;

/**
 	A simple Java Trigger that
	logs all trigger events for which it is executed
	in the file triggersLog.xml in the systems temporary folder
*/

public class loggingTrigger extends FilteringTrigger
{
	private final static String triggersLogFile = "triggersLog.xml";
	private XmldbURI documentPath = null;
	
	
	//trigger prepare event
	public void prepare(int event, DBBroker broker, Txn transaction, XmldbURI documentPath, DocumentImpl existingDocument) throws TriggerException
	{	
		//log the trigger event
		log(event, "prepare", documentPath.getCollectionPath(), documentPath.toString());
		
		this.documentPath = documentPath;
	}
	
	//trigger finish event
	public void finish(int event, DBBroker broker, Txn transaction, XmldbURI documentPath, DocumentImpl document)
	{	
		//log the trigger event
		log(event, "prepare", documentPath.getCollectionPath(), documentPath.toString());
	}
	
	private void log(int triggerEvent, String eventType, String collectionName, String documentName)
	{
		try
		{
			//open log file for appending
			File loggingFile = new File(new File(System.getProperty("java.io.tmpdir")), triggersLogFile);
			FileOutputStream out = new FileOutputStream(loggingFile, true);
			
			//create an xml string
			String xml =
					"<trigger"
					+ " event=\"" + eventToString(triggerEvent)+ "\""
					+ " eventType=\"" + eventType + "\""		
					+ " collectionName=\"" + collectionName + "\""
					+ " documentName=\"" + documentName + "\""
					+ "timestamp=\"" + (new DateTimeValue()).getStringValue() + "\"" 
					+ "/>\n";
			
			//write the xml string to the log file
			out.write(xml.getBytes());
		
			//close the log file
			out.close();
		}
		catch(Exception e)
		{
			//do nothing for now
		}
	}
	
    private static String eventToString(int event)
    {
    	switch(event)
    	{
    		case STORE_DOCUMENT_EVENT : return "STORE"; 
    		case UPDATE_DOCUMENT_EVENT : return "UPDATE";
    		case REMOVE_DOCUMENT_EVENT : return "REMOVE";
    		case CREATE_COLLECTION_EVENT : return "CREATE";
    		case RENAME_COLLECTION_EVENT : return "RENAME";
    		case DELETE_COLLECTION_EVENT : return "DELETE";
    		default : return null;
    	}
    }
}


					

5. Provided Triggers

eXist provides some Triggers out of the box that may be used

5.1. HistoryTrigger

This collection trigger will save all old versions of documents before they are overwritten or removed. The old versions are kept in the 'history root' which is by default /db/history, but can be changed with the parameter root. You need to configure this trigger for every collection whose history you want to preserve.

Example: History Trigger collection.xconf

<collection xmlns='http://exist-db.org/collection-config/1.0'>
     <triggers>
       <trigger event="update,remove" class="org.exist.collections.triggers.HistoryTrigger"/>
     </triggers>
</collection>
                            

5.2. STX Transformer Trigger

STXTransformerTrigger applies an STX stylesheet to the input SAX stream, using Joost. The stylesheet location is identified by parameter "src". If the src parameter is just a path, the stylesheet will be loaded from the database, otherwise, it is interpreted as an URI.

September 2009
The eXist Project