Catching modeling events

When an application class is deleted, the expected behavior is that the JUnit module should automatically remove the test class that corresponds to a deleted class.

In practice, when the end-user deletes a class from his model the module is supposed to:

  • Check whether the deleted class has a corresponding test case class or not.
  • Delete the test case class if it exists.

Technically, this means that our JUnit module has to register itself to receive “Delete Element” events.

Later, when such events occur, the JUnit module is supposed to process these events and clean up the test model accordingly.

A bit of theory: module model events

In some particular circumstances, model events are sent by Modelio to modules that have registered themselves to receive these events.

For model changes, a ModelChangeEvent event is sent when a top-level transaction is committed. The ModelChangeEvent event contains a structured and optimized list of changes that have been applied to the model. By processing these changes, a Module is able to perform any processing it requires.

The structured list of changes contained in a ModelChangeEvent distinguishes between:

  • Created elements
  • Deleted elements
  • Updated elements
  • Moved elements

These lists are optimized by Modelio in order to reduce the amount of data to be processed by the module. For example, no update is reported for a deleted element.

Created elements

A created element is an element that has been added to the model during the transaction.

Optimizations

  • In the case of the creation of a parent and its children, only the parent creation is reported
  • A created element will not appear in either the updated elements list or the moved elements list
  • If an element is created and deleted by the transaction, no creation (and no deletion) is reported

Deleted elements

A deleted element is an element that has been removed from the model during the transaction.

Optimizations

  • In the case of the deletion of a parent and its children, only the parent deletion is reported
  • A deleted element will not appear in either the updated elements list or the moved elements list
  • If an element is created and deleted by the transaction, no deletion (and no creation) is reported

Updated elements

An updated element is an element which is not a created or deleted element and which has been modified by the transaction without being moved. The reordering of the child elements owned by a parent element makes this parent element appear in the updated elements list.

Optimizations

  • No matter how many changes have been made to an element, it is reported once in the updated list

Moved elements

An element E is considered as a moved element if its “parent” has changed. “Parent” here must be understood as the value of E.getCompositionOwner() .

Optimizations

  • Whatever the number of parent changes that have occurred during the transaction, only one report will be available in the moved elements list. The reported move will contain the initial (before the transaction) and last known (at the end of the transaction) parent.

Event handling or listening

Modelio provides two levels of notification :

  1. handler : handlers are triggered before listener and event handlers are allowed to modify the model
  2. listener : listeners are triggered last and their code can’t modify the model. Listeners are notified of modification events done by handlers.

Implementation example

The best place to register the JUnit to receive ModelChangeEvent is the module session services start() method (remember that the start() is called when a project is opened).

Similarly, we have to unregister the Module when the project is closed, in other words, in the stop() session services method, as no model change events should occur after having closed the project.

Let’s have a look at the modified code for the start and stop methods.

 1class JUnitSession {
 2. . .
 3private JUnitModelChangeHandler modelChangeHandler = null;
 4public boolean start ()throws MdacException
 5{
 6 IModelingSession session = Modelio.getInstance().getModelingSession();
 7
 8 modelChangeHandler = new JUnitModelChangeHandler();
 9 session.addModelHandler(modelChangeHandler);
10 . . .
11}
12public void stop ()throws MdacException
13{
14 IModelingSession session = Modelio.getInstance().getModelingSession();
15 session.removeModelHandler(modelChangeHandler);
16 modelChangeHandler = null;
17 ...
18}

The processing of the model changes is delegated to a JUnitModelChangeHandler object. This delegate is created and registered as a model change handler in the start() session service method. It is removed in the stop() method.

Handler is used rather than listener because, our code will modify the model to remove test case classes on model classes destruction.

The JUnitModelChangeHandler class must implement the IModelChangeHandler interface in order to be registered as a model change handler. This interface has only one method which will be called each time a top level transaction is successfully committed and which receives a ModelChangeEvent object as parameter.

In our case, we are only interested in deleted elements that are classes and that have a <<JUnit>> stereotype dependency to a test class. For these deleted elements, we wish to delete the existing test class.

Let’s suppose, in this case, that the TestModelUpdater class is a visitor that checks that the tested model is up-to-date (and that deletes elements that have to be deleted). It extends com.modeliosoft.modelio.api.model.utils.DefaultMetamodelVisitor and redefines the operation visitModelTree to remove class stereotyped JUnit without <<JunitDependency>> dependency.

 1class JUnitModelChangeHandler implements IModelChangeHandler
 2{
 3  void handleModelChange(IModelingSession session, IModelChangeEvent  event) {
 4   
 5   TestModelUpdater updater = new TestModelUpdater();
 6   ObList<IElement> elementsToDelete = new ObList<IElement> ();
 7  
 8   // Memorize the parents to be updated
 9   for (IElementDeletedEvent deletedElement : event.getDeleteEvents() )
10   {
11    if (!elementsToDelete.contains(deletedElement.getOldParent()))
12     elementsToDelete.add(deletedElement.getOldParent());
13   }
14  
15   // Visit the elements to delete
16   Transaction t = Modelio.getInstance().getModelingSession().
17           createTransaction("Update the test hierarchy");
18   boolean modelUpdated = false;
19   for ( Object res : elementsToDelete.map(updater) )
20   {
21    if ( (Boolean)res == true )
22     modelUpdated = true;
23   }
24  
25   // If the model has changed, the transaction is commited.
26   if ( modelUpdated )
27    Modelio.getInstance().getModelingSession().commit(t);
28   else
29    Modelio.getInstance().getModelingSession().rollback(t);
30 }
31}

For more information on Modelio events, see Listening to model changes on the wiki.


<< Previous Index Next >>