Code and Stuff

Apr 18, 2012

Graphiti - EMF Transactions

When using Graphiti, changes to the model must be performed within a transaction. Noncompliance will result in an exception.
java.lang.IllegalStateException: Cannot modify resource set without a write transaction
You should not get this problem when only using features.

EMF Transactions are not only used by graphiti and all the information that follows is not Graphiti dependent.

How to use

To perform changes on the model you can use the org.eclipse.emf.transaction.RecordingCommand. This class stores all the changes performed in an editing domain during its execution (doExecute method). The command will have to be run in the command stack of the transaction domain. This is easier than it sounds:
TransactionalEditingDomain domain = TransactionUtils.getEditingDomain(model_object);
domain.getCommandStack().execute(new RecordingCommand(domain) {
   public void doExecute() {
      // do the changes here
   }
});
The TransactionalEditingDomain related classes can be found in the org.eclipse.emf.transaction package.

Rollback

Transactions can be interrupted with exceptions. If done so, all changes done in the doExecute method, up to the exception, are discarded.
try {
   TransactionalEditingDomain domain = TransactionUtils.getEditingDomain(model_object);

   domain.getCommandStack().execute(new RecordingCommand(domain) {
      public void doExecute() {
         // do the changes here

         // OperationCanceledException can be replaced with other runtime exception
         throw new OperationCanceledException("Please rollback");
      }
   });

   // if here model changed
 
} catch (OperationCanceledException exception) {
   // if here, model did not change because it was interrupted
}

Nesting

Transactions can be nested. Executing a recording command withing a recording command is possible. And so is interrupting the inner command without affecting the outer one.
final TransactionalEditingDomain domain = TransactionUtils.getEditingDomain(model_object);

domain.getCommandStack().execute(new RecordingCommand(domain) {
   public void doExecute() {

      // lets start the second transaction here
      try {
         domain.getCommandStack().execute(new RecordingCommand(domain) {
            public void doExecute() {
               // here we can throw an exception to cancel the inner transaction.
               throw new OperationCanceledException("Rollback please");
            }
         });
      } catch(OperationCanceledException exception) {
         // if exception not catched outer transaction is canceled too
      }
   }
});

Update: Threads

You might also get the IllegalStateException when trying to execute a nested RecordingCommand in a different thread than the transaction's one.

No comments: