Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Eclipse EMF Model Transaction Development Guide
Previous Page Home Next Page

Creating Transactions

A Transaction is a discrete unit of work in a ResourceSet. This work may be reading and writing the resource set contents or simply reading. Transactions provide model integrity guarantees:

  • isolation of concurrent threads to prevent data corruption
  • validation of changes on commit, rolling back (undoing those changes) automatically when data integrity is violated
  • automatic execution of triggers on commit, to proactively maintain data integrity

The EMF Transaction API is not a concurrent transactional environment. Transactions on multiple threads execute in strictly serial order.

Transactions are first-class object, maintaining all of the state required to ensure integrity, including recorded changes (for roll-back) and knowledge of which thread owns it. Most clients will never have to interact directly with the transaction object, but only with the editing domain.

Reading the Contents of the Resource Set

The default code generated by EMF is not thread-safe, even for reading. Lazy initialization is not protected by synchronization; multiple concurrent threads could all resolve the same proxy or create the same EList by reading the value of a previously uninitialized feature, resulting in multiple copies of the same data and inconsistent read and/or update experiences.

Transactional Reading API
[ as SVG]

The EMF Transaction API provides a solution in the form of read-only transactions. A read-only transaction gives a thread exclusive access to the resource set of a transactional editing domain for the purpose of reading (writing is not permitted). Other threads that wish to read will block in the attempt to start read-only transactions until the reading thread relinquishes its transaction, either by committing it or by other means.

To perform read the contents of a resource set in a read-only transaction, encapsulate the read operation in a Runnable and pass it to the TransactionalEditingDomain.runExclusive() method. The editing waits until it can start a read-only transaction on the current thread, then runs the runnable in that context.

This API works with plain Runnables, but when used with a RunnableWithResult, the runExclusive() method returns the result of the runnable. This is convenient for small read operations that simply need to find an object or compute some value and return it. The RunnableWithResult.Impl inner class is a convenient superclass for these read operations.

TransactionalEditingDomain domain;

// search for the Richmond branch of the library system
Library richmond = (Library) domain.runExclusive(new RunnableWithResult.Impl() {
    public void run() {
        TreeIterator iter = resource.getAllContents();
        
        while (iter.hasNext()) {
            Object next = iter.next();
            
            if (next instanceof Library) {
                Library lib = (Library) next;
                
                if ("Richmond Branch".equals(lib.getName()) {
                    setResult(lib);  // found it
                    break;
                }
            } else {
                iter.prune();
            }
        }
    }});
    
System.out.println("Found: " + richmond);

A RunnableWithResult also has a status, which indicates any problems in committing the transaction. Ordinarily, read-only transactions can never be rolled back because there are no changes to validate, but it can happen in rare cases of failure of concurrency controls.

Writing the Contents of the Resource Set

The other kind of transaction is a read/write transaction, in which a client thread makes changes to the contents of a resource set. As is recommended in the base EditingDomain of the EMF.Edit framework, all changes to data managed by a transactional editing domain are performed by Commands.

Transactional Commands API
[ as SVG]

As an EditingDomain has its CommandStack, so does a TransactionalEditingDomain have its TransactionalCommandStack. All EMF Commands executed on the transactional command stack are provided with read/write transactions in which to make their changes, first waiting if necessary for other reading or writing threads to commit or to relinquish their transactions by other means.

Library richmond = getLibrary("Richmond Branch");  // this would use a read-only transaction
TransactionalEditingDomain domain = getEditingDomain();

CommandStack stack = domain.getCommandStack();

// execute some change, just as in any EMF.Edit application
Command cmd = domain.createCommand(
       SetCommand.class,
       new CommandParameter(richmond, EXTLibraryPackage.Literals.LIBRARY__ADDRESS, "5600 Perth St."));

stack.execute(cmd);

In the transactional environment, data integrity mechanisms such as validation can cause a read/write transaction to roll back if it would violate integrity constraints. However, the basic command-stack API assumes that commands cannot fail, so it provides no feedback when rollback occurs. The TransactionalCommandStack provides an alternative API that throws RollbackException when the transaction is rolled back or InterruptedException if the transaction cannot even begin because the current thread is interrupted while waiting for the transaction to start. Modifying the example, above:

Library richmond = getLibrary("Richmond Branch");  // this would use a read-only transaction
TransactionalEditingDomain domain = getEditingDomain();

TransactionalCommandStack tstack = (TransactionalCommandStack) domain.getCommandStack();

Command cmd = domain.createCommand(
       SetCommand.class,
       new CommandParameter(richmond, EXTLibraryPackage.Literals.LIBRARY__ADDRESS, "5600 Perth St."));

try {
    tstack.execute(cmd, Collections.EMPTY_MAP);
} catch (InterruptedException e) {
    MessageDialog.openError(shell, "Command Failed",
            "Interrupted while waiting for a read/write transaction.");
} catch (RollbackException e) {
    ErrorDialog.openError(shell, "Command Failed", "Transaction rolled back",
            e.getStatus());
}

The overloaded execute() method is distinguished by an options map that supplies transaction options, described elsewhere. The RollbackException, should it occur, provides an IStatus carrying a human-readable reason for the rollback.

Using RecordingCommands

The transaction in which a Command is executed records all of the changes that the command performs in case it needs to roll back, undoing those changes. The RecordingCommand class is a convenient command implementation that takes advantage of this recorded information to provide automatic undo/redo support, without the necessity of composing complex nested command structures. This has the additional benefit that the command can determine what changes are required at the time of execution, rather than a priori.

TransactionalCommandStack stack;

stack.execute(new RecordingCommand() {
    protected void doExecute() {
        Iterator iter = resource.getAllContents();
        while (iter.hasNext()) {  // changes are determined on-the-fly
            Object next = iter.next();
            if (next instanceof Library) {
                ((Library) next).getBooks().add(
                        EXTLibraryFactory.eINSTANCE.createBook());
            }
        }
    }}, Collections.EMPTY_MAP);

The RecordingCommand requires only that subclasses implement the abstract doExecute() method. The rest of the Command API is implemented automatically, including undo() and redo().

Depending on potential data integrity violations, it is not absolutely certain that, as is assumed by the EMF.Edit framework, any command that was successfully undone can be redone. In consequence, the transaction API defines a ConditionalRedoCommand interface with a canRedo() query. All of the commands defined by the transaction API implement this interface.


Copyright (c) 2006, 2007 IBM Corporation and others. All Rights Reserved.


 
 
  Published under the terms of the Eclipse Public License Version 1.0 ("EPL") Design by Interspire