When they attempt to commit, Transactions use the
EMF Validation Framework to validate the changes that have occurred during the time that
they were active. If this validation finds problems with IStatus.ERROR
severity or worse, then the transaction rolls back instead of committing, and the commit
throws a
RollbackException
to the client.
However, client applications do not like it when their transactions are rolled back in this
way. The assurance of data integrity is good, but a user's workflow generally is diverted.
Given that these integrity constraints are essential to the health of the user's data, then,
an application needs a way to be proactive in ensuring that they will be met. This mechanism
is the pre-commit listener, better known as a trigger for its analogy to triggers in
relational database management systems.
[
as SVG]
When a client commits a read/write transaction, the transaction first notifies pre-commit listeners that
it is about to commit, invoking the transactionAboutToCommit(ResourceSetChangeEvent)
call-back. The listeners respond by returning trigger Commands,
if necessary, that the transaction executes to perform additional changes to bring the
resource set contents back into a consistent state.
Triggers only return commands to be executed by the transaction. They cannot directly
change the model because, like the
post-commit listeners, they
are called in a read-only transaction (nested within the read/write transaction that is
committing). When the transaction has received all of the trigger commands from its
listeners, it executes these commands in a
nested read/write
transaction. The pre-commit procedure is recursive; this transaction that executes the
trigger commands will, itself, invoke pre-commit listeners to ensure integrity of the
changes that the triggers effect. Also, all of the changes performed by triggers are
validated in the final stage of the original transaction's commit.
If, for some reason, a trigger cannot provide the commands that it needs to ensure data
integrity, then it may throw a RollbackException to force the
transaction to roll back. For example, the trigger may be intended to keep the EMF
resource set synchronized with some other data store. If that data store should become
inaccessible, then the trigger cannot perform its function.
For details of the ResourceSetChangeEvent and the
NotificationFilter API and statically registering listeners
on editing domains, see the discussion of
resource set listeners.
// trigger ensuring that all libraries have names
class MyListener extends ResourceSetListenerImpl {
MyListener() { // only interested in changes to Library objects
super(NotificationFilter.createNotifierTypeFilter(
EXTLibraryPackage.Literals.LIBRARY));
}
public Command transactionAboutToCommit(ResourceSetChangeEvent event)
throws RollbackException {
List commands = new ArrayList();
Iterator iter = event.getNotifications().iterator();
while (iter.hasNext()) {
Notification next = (Notification) iter.next();
Library library = (Library) next.getNotifier();
if (library.getName() == null)
commands.add(SetCommand.create(
event.getEditingDomain(), library,
EXTLibraryPackage.Literals.LIBRARY__NAME, "A library"));
}
return commands.isEmpty()? null : new CompoundCommand(commands);
}
}
As for
post-commit listeners, the EMF Transaction API provides
a convenient abstract class that dispatches Notifications one
by one to a trigger: the
TriggerListener.
A subclass needs only to implement the trigger() method to return
a command in response to a notification. The TriggerListener class
takes care of combining the results the results into an appropriate compound command.
class MyTriggerListener extends TriggerListener {
MyListener() { // only interested in changes to Library objects
super(NotificationFilter.createNotifierTypeFilter(
EXTLibraryPackage.Literals.LIBRARY));
}
protected Command trigger(TransactionalEditingDomain domain,
Notification notification) throws RollbackException {
Library library = (Library) next.getNotifier();
if (library.getName() == null) {
return SetCommand.create(domain, library,
EXTLibraryPackage.Literals.LIBRARY__NAME, "A library");
}
return null;
}
}
Copyright (c) 2006, 2007 IBM Corporation and others. All Rights Reserved.