package org.exist.collections.triggers; import org.exist.xmldb.XmldbURI; import org.exist.storage.txn.Txn; /** Finite State Machine, managing the state of a Running trigger; * allows to avoid infinite recursions by forbidding another trigger to run * where there is already one; feature trigger_update . * I implemented that when a trigger is running , another trigger in the same * Thread cannot be fired . * There is a second condition that when a trigger is running triggered by * some document d, even the same trigger cannot run on a different document . * maybe TODO: apply "state" design pattern */ public class TriggerStatePerThread { public static final int NO_TRIGGER_RUNNING = (0); public static final int TRIGGER_RUNNING_PREPARE = (1); public static final int TRIGGER_RUNNING_FINISH = (2); private static ThreadLocal triggerRunningState = new ThreadLocal() { protected synchronized Object initialValue() { return new TriggerState(NO_TRIGGER_RUNNING); } }; /** */ public static boolean verifyUniqueTriggerPerThreadBeforePrepare( DocumentTrigger trigger, XmldbURI modifiedDocument) { if (getTriggerRunningState() == NO_TRIGGER_RUNNING) { setTriggerRunningState(TRIGGER_RUNNING_PREPARE, trigger, modifiedDocument); return true; } else { return false; } } /** * @param modifiedDocument * the document whose modification triggered the trigger */ public static boolean verifyUniqueTriggerPerThreadBeforeFinish( DocumentTrigger trigger, XmldbURI modifiedDocument) { // another trigger is already running DocumentTrigger runningTrigger = getRunningTrigger(); if ( runningTrigger != null && trigger != runningTrigger ) { return false; } // current trigger is busy with another document if(getModifiedDocument() != null && !modifiedDocument.equals(getModifiedDocument())) { return false; } if (getTriggerRunningState() == TRIGGER_RUNNING_PREPARE) { setTriggerRunningState(TRIGGER_RUNNING_FINISH, trigger, modifiedDocument); return true; } else { return false; } } public static class TriggerState { private int state; private DocumentTrigger currentTrigger; private Txn transaction; private XmldbURI modifiedDocument; public TriggerState(int state) { super(); this.setState(state, null, null); } private void setState(int state, DocumentTrigger trigger, XmldbURI modifiedDocument) { this.state = state; if (state == NO_TRIGGER_RUNNING) { this.currentTrigger = null; this.setModifiedDocument(null); } else { this.currentTrigger = trigger; this.setModifiedDocument(modifiedDocument); } } private int getState() { return state; } void setTransaction(Txn transaction) { this.transaction = transaction; } Txn getTransaction() { return transaction; } public DocumentTrigger getTrigger() { return currentTrigger; } private void setModifiedDocument(XmldbURI modifiedDocument) { this.modifiedDocument = modifiedDocument; } private XmldbURI getModifiedDocument() { return modifiedDocument; } } public static int getTriggerRunningState() { return ((TriggerState)triggerRunningState.get()).getState(); } public static DocumentTrigger getRunningTrigger() { return ((TriggerState)triggerRunningState.get()).getTrigger(); } public static void setTriggerRunningState( int state, DocumentTrigger trigger, XmldbURI modifiedDocument ) { ((TriggerState)triggerRunningState.get()).setState(state, trigger, modifiedDocument); } public static Txn getTransaction() { return ((TriggerState)triggerRunningState.get()).getTransaction(); } public static void setTransaction(Txn transaction) { ((TriggerState)triggerRunningState.get()).setTransaction(transaction); } public static XmldbURI getModifiedDocument() { return ((TriggerState)triggerRunningState.get()).getModifiedDocument(); } }