AndroMDA Metafacades

AndroMDA Metafacades are facades that are used to provide access to models loaded by a repository. These "metafacades" shield us from the underlying meta model implementation. Meta models are MOF modules such as UML 1.4, UML 2.0, etc. Metafacades are generated by the andromda-meta-cartridge.

Metafacades (as explained below) are the object-oriented API that allows access to the model from within the templates. Using metafacades, the templates become much simpler and straightforward because the intelligence and responsibility for code transformation and generation is centralized in Java objects, not in the template script language.

Introduction: The basics of metamodeling

The MOF repository instantiates a Java object for each element in your model that you load into AndroMDA. The class of that object is part of the UML metamodel. In MDA-speak, the instantiated graph of metamodel objects is called an "abstract syntax tree (AST)".

Example: When you model the attribute of a class, an instance of the metaclass "Attribute" is instantiated. The "birth:Date" attribute of a model element "Person" will be instantiated as:

  1. one object of class "Attribute" with its attribute "name" set to "birth"
  2. one object of class "UMLClass" with its attribute "name" set to "Person" and a reference to the Attribute object that has the name "birth".

Look at this UML diagram to get the idea:

images/PersonExample.jpg

To see the complete UML metamodel, read UML14Metamodel.pdf. You will see the metamodel of UML 1.4, documented in UML itself. The "Attribute" class and all the other interesting metaclasses are documented there.

Metaclasses are 'data only'

Now that we have all these nice little AST objects representing our model, we notice that they all have very little behavior - they are just data holders for the metainformation from our model. It would certainly be nice to attach behavior to them to support code generation. For example: A UMLClass metaobject that represents an <<Entity>> could validate itself and check if it has at least one Attribute metaobject representing a <<Identifier>> - this would be plain, clear and simple, right? Now, why don't we simply write a class EntityClass that extends the class UMLClass? Just like this:

Another example:

We could then use the class SpecialAttribute instead of the class Attribute and write $att.getterName in our template script. The template engine would call the getGetterName() method shown above.

Nice, object-oriented way to do something, right? Reality is not that simple, though. Keep on reading...

Classes that cannot be extended

Well, extending metaclasses would be nice, indeed! However, there is no source or JAR file for those classes because Netbeans MDR instantiates them on the fly through byte code generation (absolutely magic!).

For those of you who want to see the magic, here are two interesting links into the MDR source files:

So what can we do here? Since inheritance is impossible and the interface of a UML metaobject is quite complex, we use the GoF facade pattern and shield that complexity behind a quite lean interface that can easily be understood. We also abstract from the particular version of a metamodel (e.g. UML 1.4, UML 1.3, UML 2.0, etc.) because we want to be independent of them when we process input from different CASE tools. Enter metamodel facades, or "metafacades" for short!

Metamodel facades = metafacades

This picture shows you how the AndroMDA metamodel facades are related to the metaclass interfaces and their implementation in bytecode:

images/FacadePrinciples.jpg

You can see that the facade hides the metaobject and enhances it with its own methods. The next figure shows how the templates interact with the metafacades and how the metafacades inside a cartridge relate to the basic metafacades that ship with the AndroMDA distribution (in the subdirectory "metafacades"):

images/FacadeLayer.jpg

Templates within a cartridge call metafacades. Those can be contained in the same cartridge or in the base metafacades package. The base metafacades define common interfaces for metafacades and implement these interfaces in separate classes which are specific for a certain version of the metamodel (UML in this case, but any metamodel like CWM, etc. will do).

Almost a real world example: EJB metafacades

Metafacades carry behavior that supports code generation. The following picture shows you a concrete example for this: operations of metafacades used for EJB code generation.

images/EJBFacades.jpg

At the top of the diagram, you see a simplified version of the base metafacade ClassifierFacade. (By the way: Classifier is a metamodel superclass of UMLClass). You see that this facade supports a getFullyQualifiedName() method which is handy for templates: A template can invoke this method using the $class.fullyQualifiedName syntax.

You can also see the method getAttributesAsList(). This method returns a string that can be used for generating superclass constructor calls or ejbCreate() calls. For a class with attributes a and b, the method returns "(a, b)".

Using metafacades from a template

Now, how would a template look like that uses these metafacades? Let's have a look at a template from the EJB cartridge which generates an entity bean. There is a section in the template that generates business methods into the bean class - I omit a lot of complexity here and reduce it to the essence of method generation:

In this template code sample, you can see that the variable $op iterates over a collection called $class.operations. When the template engine processes the foreach statement, it calls the ClassifierFacade.getOperations() method which corresponds directly to the aggregation operations that you see in the UML diagram above. So, $op is now of type OperationFacade.

The next expression that the template engine evaluates is $op.signature. This invokes the method OperationFacade.getSignature(). The facade for operations contains quite a bit of code inside this method as it collects all parameters of the operation, extracts their type and their name and concatenates them, using comma as a separator.

Last example: The most interesting statement in the code snippet above is:

This template engine invokes two methods, here: OperationFacade.getReturnType() and ClassifierFacade.getFullyQualifiedName(). Plain, clear and simple, right? Well, it's really simple except that the description in this paragraph contains a few simplifying lies :-) but continue to read and you will see what I mean...

Where to go from here

If you followed the text until here, you will want to write metafacades for your own cartridge. In that case, you will most likely have (at least) these questions in mind:

  • How do I create a metafacade class?
  • Where do I put it in the source code tree?
  • How do I make AndroMDA instantiate it at the right time?
  • Do I have to rewrite my metafacades each time I use a different version of UML?

OK, I admit that only smart people will have the last question in mind but I guess you're smart, right? So, please read the next chapters Developing metafacades to see how to create them and Configuring metafacades to make AndroMDA use them at the right time.