Package com.orchestranetworks.schema.trigger

# Class TableTrigger

java.lang.Object
com.orchestranetworks.schema.trigger.TableTrigger

Direct Known Subclasses:
  `BuiltInTrigger_launchWorkflow`

public abstract class TableTrigger extends Object
Specifies methods that are automatically executed when certain operations are performed on a table. This is the EBX® equivalent of database triggers.

### Definition in the data model

The trigger must be declared under the element `xs:annotation/xs:appinfo`:

>      <osd:trigger class="com.foo.MyTableTrigger" />

where `com.foo.MyTableTrigger` is the fully qualified name of the class implementing this interface. It is also possible to set additional JavaBean properties:

>      <osd:trigger class="com.foo.MyTableTrigger">
>              <param1>...</param1>
>              <param2>...</param2>
>      </osd:trigger>

where `param1` and `param2` are JavaBean properties of the specified class.

For more information, see the JavaBean specification.

### Life cycle

1. When the data model is loaded:
   - the specified class is instantiated through its default constructor and the JavaBean property setters are called (in the example above, `setParam1(...)` and `setParam2(...)`);
   - the method `setup(TriggerSetupContext)` is called on the new instance.
2. During the operational phase: the methods `handle...` are called every time the associated operation is executed.

If several operations are defined for the same table, they are not executed in any particular order.

### Restrictions

Table triggers are not invoked in the following contexts:

- If triggers have been explicitly deactivated using the method `ProcedureContext.setTriggerActivation(false)`.
- In the context of a merge operation (see `ProcedureContext.doMergeToParent(com.onwbp.adaptation.AdaptationHome)`).
- In the context of an archive import (see `ProcedureContext.doImportArchive(ArchiveImportSpec)`).
- In the context of a dataset copy (see `ProcedureContext.doCopy(com.onwbp.adaptation.Adaptation)`).

### Transaction Management

When a record operation is performed, the sequence is the following:

1. the method `handleBefore<Operation>` is invoked,
2. the core record operation is performed (Create, Modify or Delete),
3. the method `handleAfter<Operation>` is invoked.

Each trigger method can impact any system, persistent or not, including the EBX® repository. The main cases are outlined below:

1. the trigger modifies the same dataspace, in which case it must be performed in the same transaction and use the same `ProcedureContext`.
2. the trigger modifies another dataspace in the same EBX® repository, in which case it must be performed in another `Procedure`.
3. the trigger modifies an external system, for example a registry in the Java memory, an RDBMS or a file system.

CAUTION: in the latter two cases, the system integrity cannot be fully ensured in various cases, for example a sudden server shutdown. Consequently, the system must be carefully designed so as to support such degraded situations. At least, for cases where a Java exception has been thrown during the transaction, the method `handleBeforeTransactionCancel(BeforeTransactionCancelContext)` can be implemented to rollback the modifications done in the other dataspace or in the other system.

During a single transaction (the first case described above), it is possible to perform a sequence of nested updates. In the following example, `table1` has an `afterModify` trigger which performs a record creation in `table2`:

- Start: Modify a record of `table1`
  - `handleBeforeModify` in `table1`
  - Core record modification in `table1`
  - `handleAfterModify` in `table1`
    - Start: Create record in `table2`
    - `handleBeforeCreate` in `table2`
    - Core record creation in `table2`
    - `handleAfterCreate` in `table2`
    - End: Create record in `table2`
- End: Modify a record of `table1`

Known Limitation: A "before trigger" only allows updating the current record content (no access to `ProcedureContext` is provided). If, at this point, more advanced updates are necessary, a specific service should be used instead of triggers.

### Functional guard and exceptions

In the context of a method `handleBefore...`, it is possible to implement a guard by throwing an `OperationException` if some functional conditions on the data are not satisfied. This will cancel the current transaction.

Throwing an `OperationException` instead of a `RuntimeException` provides the end-user with the benefit of a well-localizable and a more user-friendly error message, when the error happens in the context of the user interface (in contrast, this behavior does not apply to `handleBeforeTransactionCancel`). In all cases, the current transaction is aborted.

Note: In further detail, if a `ProcedureContext` raises an exception, any preceding updates made on the repository during nested update executions are first cancelled before the corresponding update method catches the exception. This additional feature is for ensuring that the repository and cache remain consistent, even if the corresponding update method catches the exception.

See Also:
  - `Procedure.execute(ProcedureContext)`

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `TableTrigger()`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `handleAfterCreate(AfterCreateOccurrenceContext aContext)`
  This method is called after the creation of a record.
  `void`
  `handleAfterDelete(AfterDeleteOccurrenceContext aContext)`
  This method is called after deleting a record.
  `void`
  `handleAfterModify(AfterModifyOccurrenceContext aContext)`
  This method is called after the modification of a record.
  `void`
  `handleBeforeCreate(BeforeCreateOccurrenceContext aContext)`
  This method is called before the creation of a record.
  `void`
  `handleBeforeDelete(BeforeDeleteOccurrenceContext aContext)`
  This method is called before deleting a record.
  `void`
  `handleBeforeModify(BeforeModifyOccurrenceContext aContext)`
  This method is called before the modification of a record.
  `void`
  `handleBeforeTransactionCancel(BeforeTransactionCancelContext handlingContext)`
  This method is called whenever a transaction has updated the associated table, and this transaction gets cancelled due to an exception.
  `void`
  `handleBeforeTransactionCommit(BeforeTransactionCommitContext aContext)`
  This method is called before committing the updates which happened during procedure execution.
  `void`
  `handleNewContext(NewTransientOccurrenceContext aContext)`
  This method is called when a transient record context is created.
  `abstract void`
  `setup(TriggerSetupContext aContext)`
  This method is called when the data model is loaded, to check that this rule is consistent with the data model that defines it and to prepare this instance.

  ### Methods inherited from class java.lang.Object

  `equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### TableTrigger

    public TableTrigger()

- ## Method Details

  - ### setup

    public abstract void setup(TriggerSetupContext aContext)
    This method is called when the data model is loaded, to check that this rule is consistent with the data model that defines it and to prepare this instance.

    Do note that during this step, all models may not be properly loaded. Thus, the repository should not be accessed during this step.

  - ### handleNewContext

    public void handleNewContext(NewTransientOccurrenceContext aContext)
    This method is called when a transient record context is created. A transient record context contains temporary values used when creating a new record or when duplicating an existing one. This method gives the opportunity to add specific values to this context. When duplicating an existing record, it is called just after copying the values. Make sure that this method does not unintentionally override values.

    For the user interface, this method is called whenever the user uses the record 'Create' or 'Duplicate' action, and before the form is displayed, so that the form to submit has some fields with values preset by the trigger, if any. This method is not called in the following cases:

    - When importing from XML or CSV;
    - If triggers have been disabled using the method `ProcedureContext.setTriggerActivation(boolean)`;
    - If triggers have been locally disabled by setting the corresponding parameter in the method `ProcedureContext.getContext(boolean, ValueContextForUpdate, AdaptationTable)`;

    The default implementation of this method does nothing.

    ##### Duplicating a record with read-only or hidden fields

    If the method is invoked in the context of a record duplication, every read-only or hidden fields will be reset to their default value (or to `null` if no default value is set) after this method is called. This is to avoid triggering a node permission exception on submit.

    Should a node be duplicated regardless of permissions, it is possible to grant special privileges using `ValueContextForUpdate.setPrivilegeForNode(com.orchestranetworks.schema.Path)`. When using this method, it is important to note that:

    - this privilege will last until the create operation is finished and the record persisted.
    - this privilege will not apply to the UI, in which the node display will always depend on the node permission.
    See Also:
      - `ProcedureContext.getContextForNewOccurrence(AdaptationTable)`
      - `ProcedureContext.getContextForNewOccurrence(Adaptation, AdaptationTable)`

  - ### handleBeforeCreate

    public void handleBeforeCreate(BeforeCreateOccurrenceContext aContext) throws OperationException
    This method is called before the creation of a record.

    This method will not be called if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    The default implementation does nothing.

    See Transaction Management.

    ##### Limitations

    It is important to note that this method can also provide values for primary key field(s). As a consequence, this method is called before each of three important internal checks:

    1. Before the internal check for primary key completeness;
    2. Before the internal check for record uniqueness;
    3. Before the internal permissions checks (the primary key must be complete so that the data model properties `mayCreateRoot` and `mayCreateOverwriting` can be evaluated).

    Implementations of this method must be aware that the primary key may be incomplete or inconsistent, in which case the transaction will be aborted.

    Throws:
      `OperationException` - to be thrown as a functional guard, or when an unexpected exception occurs. As a consequence, the whole transaction is always aborted.
    See Also:
      - `handleNewContext(NewTransientOccurrenceContext)`
      - `handleAfterCreate(AfterCreateOccurrenceContext)`

  - ### handleAfterCreate

    public void handleAfterCreate(AfterCreateOccurrenceContext aContext) throws OperationException
    This method is called after the creation of a record. It gives the opportunity to notify a third-party system.

    This method will not be called if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    The default implementation does nothing.

    See Transaction Management.

    Throws:
      `OperationException` - to be thrown only when an unexpected exception occurs. As a consequence, the whole transaction is always aborted. For a functional guard, it is strongly recommended to implement it in `handleBeforeCreate`.
    See Also:
      - `handleBeforeCreate(BeforeCreateOccurrenceContext)`

  - ### handleBeforeModify

    public void handleBeforeModify(BeforeModifyOccurrenceContext aContext) throws OperationException
    This method is called before the modification of a record.

    This method will not be called if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    If no modification to the initial record content takes place, this trigger will not be executed.

    The default implementation does nothing.

    See Transaction Management.

    Throws:
      `OperationException` - to be thrown as a functional guard, or when an unexpected exception occurs. As a consequence, the whole transaction is always aborted.
    See Also:
      - `handleAfterModify(AfterModifyOccurrenceContext)`

  - ### handleAfterModify

    public void handleAfterModify(AfterModifyOccurrenceContext aContext) throws OperationException
    This method is called after the modification of a record. It gives the opportunity to notify a third-party system.

    This method will not be called if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    If no modification to the initial record content takes place, this trigger will not be executed.

    The default implementation does nothing.

    See Transaction Management.

    Throws:
      `OperationException` - to be thrown only when an unexpected exception occurs. As a consequence, the whole transaction is always aborted. For a functional guard, it is strongly recommended to implement it in `handleBeforeModify`.
    See Also:
      - `handleBeforeModify(BeforeModifyOccurrenceContext)`

  - ### handleBeforeDelete

    public void handleBeforeDelete(BeforeDeleteOccurrenceContext aContext) throws OperationException
    This method is called before deleting a record.

    This method will not be called if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    The default implementation does nothing.

    See Transaction Management.

    Throws:
      `OperationException` - to be thrown as a functional guard, or when an unexpected exception occurs. As a consequence, the whole transaction is always aborted.
    See Also:
      - `handleAfterDelete(AfterDeleteOccurrenceContext)`

  - ### handleAfterDelete

    public void handleAfterDelete(AfterDeleteOccurrenceContext aContext) throws OperationException
    This method is called after deleting a record.

    This method will not be called if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    The default implementation does nothing.

    See Transaction Management.

    Throws:
      `OperationException` - to be thrown only when an unexpected exception occurs. As a consequence, the whole transaction is always aborted. For a functional guard, it is strongly recommended to implement it in `handleBeforeDelete`.
    See Also:
      - `handleBeforeDelete(BeforeDeleteOccurrenceContext)`

  - ### handleBeforeTransactionCommit

    public void handleBeforeTransactionCommit(BeforeTransactionCommitContext aContext) throws OperationException
    This method is called before committing the updates which happened during procedure execution. This method is called even if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    The default implementation does nothing.

    Throws:
      `OperationException` - when an exception occurs; as a consequence, the current transaction is aborted.
    See Also:
      - Transaction Management

  - ### handleBeforeTransactionCancel

    public void handleBeforeTransactionCancel(BeforeTransactionCancelContext handlingContext)
    This method is called whenever a transaction has updated the associated table, and this transaction gets cancelled due to an exception. This method is invoked even if `ProcedureContext.setTriggerActivation(boolean)` has been set to `false`.

    The default implementation does nothing.

    If the execution of this method itself throws a `Throwable`, it is caught by the caller method, an error message with the stack trace is logged into the `kernel` category, then the remainder of the cancel process is executed (including other potential triggers `handleBeforeTransactionCancel`).