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
Answertopia.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

Tutorial: EMF Model Transaction

Contents

Overview

The EMF transaction framework provides a means to manage multiple read/write threads on one EMF-style editing domain. Transactional editing domains can be registered with a unique identifier and later retrieved (or constructed). These editing domains conform to the standard EMF editing domain contract and can be used with little modification to client code to make it work in the context of regular EMF-style commands.

Listeners receive events in batches and can be registered against particular editing domain(s) so that they are automatically added once the editing domain is constructed. When a transaction is finished and about to commit listeners can be given the opportunity to append more changes onto the end of the transaction. If a transaction is rolled back then listeners are not given the notification of the post-commit.

[ back to top]

References

This tutorial assumes that the reader has knowledge of EMF and the eclipse PDE development environment. It is essential that the reader understands the basic reflective mechanisms of EMF as well as its adapter/notifier system for broadcasting events.

[ back to top]

Introduction

In order to demonstrate EMF Model Transaction, we will be making use of the library metamodel. This metamodel is a variant of the standard EMF example metamodel used in many of its tutorials.

The goal of this tutorial is to create a single standard transactional editing domain with a single listener. Once that is done, code will be written to get (or construct) the editing domain so that changes are made on multiple threads (sequentially, not concurrently). These changes will be undone and redone automatically because the changes were done in a recording command.

[ back to top]

Registering an Editing Domain and a Listener

The following XML declares a new transactional editing domain and a new listener for that editing domain. Both operations can be done programmatically using the TransactionalEditingDomain.Registry, TransactionalEditingDomain.Factory and the TransactionalEditingDomain.addResourceSetListener().

   <extension
         point="org.eclipse.emf.transaction.editingDomains">
      <editingDomain
            factory="transactionexample.MyEditingDomainFactory"
            id="myExample"/>
   </extension>
   <extension
         point="org.eclipse.emf.transaction.listeners">
      <listener class="transactionexample.MyListener">
      	<editingDomain id="myExample"/>
      </listener>
   </extension>

Here is the implementation of the editing domain factory:

	public class MyEditingDomainFactory
		implements Factory {
	
		public TransactionalEditingDomain createEditingDomain() {
			return TransactionalEditingDomain.Factory.INSTANCE.createEditingDomain();
		}
	
		public TransactionalEditingDomain createEditingDomain(ResourceSet rset) {
			return TransactionalEditingDomain.Factory.INSTANCE.createEditingDomain(rset);
		}
	
		public TransactionalEditingDomain getEditingDomain(ResourceSet rset) {
			return TransactionalEditingDomain.Factory.INSTANCE.getEditingDomain(rset);
		}
	}

The editing domain factory simply delegates to the singleton factory. If some customizations were needed for the editing domain then the factory could handle them here.

public class MyListener
   extends ResourceSetListenerImpl {
   
   public void resourceSetChanged(ResourceSetChangeEvent event) {
      System.out.println("A change has been made with "+event.getNotifications().size()+" notifications produced.");
   }
   
   public Command transactionAboutToCommit(ResourceSetChangeEvent event)
      throws RollbackException {
      
      List notifications = event.getNotifications();
      CompoundCommand cc = new CompoundCommand();
      Set handledLibraries = new HashSet();
      
      for (Iterator i = notifications.iterator(); i.hasNext();) {
         Notification n = (Notification)i.next();
         
         if (n.getNotifier() instanceof EObject && !handledLibraries.contains(n)) {
            EObject notifier = (EObject)n.getNotifier();
            if (notifier.eClass() == EXTLibraryPackage.eINSTANCE.getLibrary()) {
               final Library l = (Library)notifier;
               String name = l.getName();
               
               // Libraries should have some name
               if (name == null || name.equals("")) {
                  // We can use any EMF command here
                  cc.append(new SetCommand(event.getEditingDomain(),
                        l, EXTLibraryPackage.eINSTANCE.getLibrary_Name(), 
                        "SomeName"));
               }
               
               handledLibraries.add(l);
            }
         }
      }
      
      // It is important to return null if we have nothing to 
      //  contribute to this transaction.
      return cc.isEmpty() ? null : cc;
   }
}

The listener serves two purposes: automatically name Library objects if they have no name and report to the console how many notifications were batched at the completion of each root transaction. It declares that it is interested in both the post commit and pre commit events by overriding the isPrecommitOnly and isPostCommitOnly methods. Note that the listener makes no attempt to modify the state of any EObjects as this would constitute a protocol violation. The listener makes an attempt to modify EObjects only as a command that it returns in the transactionAboutToCommit. At no point did the listener attempt to execute this command however. As an optimization, the listener ensures that it will return null for transactionAboutToCommit if it has nothing to add to the transaction.

The listener could override the getFilter method in order to filter out any notifications that do not match the filter. For now, it will only filter out "touch" events.

[ back to top]

Obtaining the Editing Domain and Making Changes

The editing domain, once registered is made available through the transactional editing domain registry singleton.

final TransactionalEditingDomain domain = 
		TransactionalEditingDomain.Registry.INSTANCE.getEditingDomain("myExample");

Once we have the editing domain we can begin working with it and editing it. We must make changes inside a command that is executed on the editing domain's command stack. The editing domain will ensure that the command is executed exclusively from any other commands being executed in other threads.

final Resource r = domain.getResourceSet().createResource(URI.createURI("file://foo.extlibrary"));

// We execute this command on the command stack because otherwise, we will not
//  have write permissions on the editing domain.
domain.getCommandStack().execute(new RecordingCommand(domain) {
	protected void doExecute() {
		Library l = EXTLibraryFactory.eINSTANCE.createLibrary();
		r.getContents().add(l);
		l.setName("");
	}
});

We can have multiple threads attempting to access this editing domain. Each will be given exclusive access as long as we use the runExclusive() method on the editing domain.

Runnable getLibraryName = new Runnable() {
	public void run() {
		// Any reading that is done on the editing domain must be done inside an
		//  runExclusive call to ensure we have the read lock
		String libraryName = null;
		try {
			libraryName = (String)domain.runExclusive(new RunnableWithResult.Impl() {
				public void run() {
					// Find the library's name and pass it back to the caller.
					setResult(((Library)r.getContents().get(0)).getName());
				}
			});
		} catch (InterruptedException e) {
			// Handle the interrupted exception in an graceful way ...
		}
		
		// The library name won't be empty because our listener will
		//  give it a default name since we gave it an empty name.
		System.out.println(libraryName);
	}
};

Thread t1 = new Thread(getLibraryName);
Thread t2 = new Thread(getLibraryName);

t1.start();
t2.start();

[ back to top]

Summary

In this tutorial, we did the following:

  1. Declared a transactional editing domain with a unique identifier against the extension point
  2. Registered a listener against that transactional editing domain using the listeners extension point
  3. Made changes to the editing domain using a recording command that can undo/redo itself automatically
  4. Read information from the editing domain exclusively using RunnableWithResult so that we could more easily pass data back to the caller.

[ back to top]


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


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