A Bird's Eye view of AndroMDA

By Joel Kozikowski

The purpose of this document is to give a high level view of the entire AndroMDA system so you can quickly get up to speed in using it.  It is intended to be used by an experienced developer (but new to AndroMDA) as a road map to find their way around all of the components. It does not go into detail on any one aspect of the system.  Instead, it identifies common how does that work type questions, and then points you to material for further study. A basic understanding of UML is assumed. If you are completely new to UML, you may want to try one of the numerous books on the subject before tackling AndroMDA. A good crash course on UML can be found here: http://bdn.borland.com/article/0,1410,31863,00.html

System overview

AndroMDA is a code generation tool that takes a UML model as input and generates source code as output.  Using a series of template files (which you can customize if you wish), AndroMDA can produce source code from a UML model in any programming language.  Default templates exist to generate Java code (and in particular J2EE code).

There are two primary components used in the AndroMDA system:

  1. The AndroMDA code generation engine.
  2. Apache's Maven project builder and management system.

The AndroMDA code generator is actually a generic code generation engine. The engine is a platform that hosts code modules (called cartridges) that do the actual code generation.  An AndroMDA cartridge is a collection of source code template files and Java helper classes (called Metafacades) packaged into a .JAR file.  There are a variety of pre-existing cartridges that you can plug in to the engine to generate code a particular way.  The pre-existing cartridges all generate Java source code for various popular Open Source libraries (such as Spring, Hibernate, and J2EE). The engine is not limited to these however.  You can write your own cartridges, and (at least in theory) generate code from UML for any programming language.

An excellent explanation/demonstration of AndroMDA's capabilities can be found in the HowTo guide explaining how to model using the Spring Cartridge.  There, you can see examples of a UML model, and the resulting Java source code (very valuable to study).  For more details, see the Spring howto documentation .

Maven is actually an optional component.  The AndroMDA engine could be called directly from the command line, from within an IDE, or from a build script (such as from within Ant).  However, a series of Maven plug-ins already exist to greatly simplify the process of using AndroMDA. Unless you are trying to integrate AndroMDA into a pre-existing project, using Maven is highly recommended.  It is the normal way of accessing AndroMDA services.

If you are unfamiliar with Maven, learning it first (at least at the conceptual level) is highly recommended.  A good overview article of what Maven is and what it is capable of can be found here: http://www.devx.com/java/Article/17204

For a complete list of articles you can use to familiarize yourself with Maven, see Maven articles .

For instructions on how to install the necessary components to get AndroMDA up and running see Getting started .

Project development cycle

The following is a typical AndroMDA project's development cycle (for Java). Details for each step follow this section.

  1. Create a new project directory using the command maven andromdapp:generate (which calls a maven plug-in known as the maven app generator plug-in). 
  2. Configure the generated project to fit your specific needs
  3. Use MagicDraw to edit the file < projectDir >/mda/src/uml/< projectName >Model.xmi . This file is where you save your UML model.
  4. Generate code by issuing one of the following commands from your project's root directory:
    1. maven mda (re)generates source code from the model
    2. maven install (re)generates source code, including the database schema files, then builds all source modules.
    3. maven create-schema installs the SQL DDL to your database if you are using AndroMDA to generate your persistence layer (such as with Hibernate and/or Spring).  This command assumes the DDL file has already been created (using maven install )
  5. Hand code any methods requiring manual implementation in the source files found in < projectDir >/core/src/java .
  6. Compile and test your code
  7. As your project evolves, make refinements first to the UML model by re-starting the process from step 3 above.

A complete list of Maven goals for your project that you may want to use for step #4 above can be found in the file < projectDir >/readme.txt (which is created by the app generator).

Creating a new project

To create a new project, first change to the desired PARENT directory of your soon to be created project directory, and issue the maven andromdapp:generate command.  When asked for a project ID use a short name (without any spaces) that will represent the sub-directory's name.  For example, if in the \myProjects directory you generate a project with the ID myDemo, your project's root directory will be /myProjects/myDemo.

For more information, see My first AndroMDA project as well as the andromdapp plugin

Project Directory Structure

The most significant sub-directories in the project structure created by the Maven app generation plug-in are as follows:

< projectDir >/mda/src

Where the UML model file is located.

< projectDir >/mda/conf

Where the AndroMDA engine configuration file is located.

< projectDir >/core/target

Where the code generator places most of the resulting files.  The SQL schema files are found in this directory. Java source files are found in the src/ sub-directory. You may view this code for study, but do not make any modifications to it, as it will be overwritten on subsequent runs of the code generator.

<projectDir>/core/src

Source files that require manual implementation go here.  Files in this directory will NOT be overwritten on subsequent runs of the code generator.

For a complete description of project sub-directories, see the file < projectDir >/readme.txt

AndroMDA configuration

The AndroMDA engine is ultimately driven by the configuration information found in the file andromda.xml .  If using a project generated by the Maven app generator plug-in, this file is located in the < projectDir >/mda/conf sub-directory. This file contains, among other things, the name of the UML model file to be used by the engine, as well as configuration settings for the various AndroMDA Cartridges used by your project (these settings are segregated from each other in sections called namespaces).

For general information about the configuration information found in andromda.xml, see the configuration page

For information about cartridge specific configuration options, first go to the AndroMDA Cartridge page , select the desired cartridge from the Available Cartridges menu on the left, then select the Namespace option under the Documentation menu.

If you used the Maven app generator (and subsequently use Maven to actually build your project), many of the configuration values found in andromda.xml are actually delegated up to Maven build properties.

The file < projectDir >project.properties contains Maven properties that are passed down to andromda.xml (and other parts of the build scripts) that are project wide settings (regardless of which developer is currently building the system, and which version is being built).  These include things such as JDBC configuration settings, which database dialect you want Hibernate to use (e.g. Hypersonic, mySQL, etc.).

The file <projectDir>build.properties contains Maven properties that are specific to the version being built.

Maven build properties can also be overridden on a workstation (and/or developer) by workstation basis.  For more information about Maven build properties in general, see http://maven.apache.org/reference/properties.html

Creating your UML Model

In theory, AndroMDA can use the output of any UML modeling tool as its starting point for code generation, provided the UML modeling tool can output to the Object Management Group's industry standard XML Metadata Interchange (XMI) file format (see http://www.omg.org/technology/documents/formal/xmi.htm ).

In practice, you will save yourself a LOT of headache if you simply use MagicDraw as your UML tool (a free community edition is available at http://www.magicdraw.com ).  All of the developers on the AndroMDA team use MagicDraw (so obviously there will be better out of the box support for that particular tool).

Note from the editor: While it is true that AndroMDA works best with MagicDraw there is no dependency what-so-ever, not in the code, not in any of the transformations. AndroMDA works fine with other tools such as Poseidon, Aris UML, etc.. Sometimes there's a restriction due to the poor UML support in certain areas such as activity graphs, but that's not an AndroMDA problem, it's the tool not properly supporting UML and/or XMI.

The AndroMDA team is more than willing to make sure users can use their UML tool or preference, especially when that tool is open-source or free.

AndroMDA code generation is driven by UML stereotypes .  In a nutshell, the engine traverses your UML model, looking for classes tagged with a specific stereotype.  If it finds a stereotype it recognizes, the appropriate cartridge is called to generate code for that class. Which stereotypes are recognized depend on the AndroMDA Cartridge(s) being used. Stereotype recognition is case sensitive.

A key concept in the Model Driven Architecture methodology is the idea of the developer first creating a Platform Independent Model (the PIM) of the system.  By Platform Independent, that means a model devoid of any platform specific items (such as a class's attribute defined using a Java data type like java.lang.String).  Many UML tools are designed to generate Java code, so they use Java data types by default when modeling (violating the PIM concept). AndroMDA expects UML models to contain generic data types (specifically, UML data types).

To simplify modeling (insuring the proper UML data types and stereotypes are used), the AndroMDA UML Profile has been developed which can (and really must ) be included as part of your model. The profile is named andromda-profile-XXX.xml.zip (where XXX is the version of AndroMDA you are using).  The profile contains all of the UML data types, stereotypes, and tagged values that are known to work with AndroMDA and all of its Cartridges.  Any new UML model should begin by importing this file into your project (with MagicDraw, this is done by using the File_Use Profile/Module menu option). See the Frequently Asked Questions for more information.

If your project was generated using the Maven app generation plug-in, the pre-created project model file < projectDir >/mda/src/uml/< projectName >Model.xmi will already have included this profile.

The profile file itself can be found in the AndroMDA source distribution in the < sourceDir >/etc/profile/target sub-directory.  Frequently, when opening a UML file with MagicDraw, it will ask for you to specify the location of this file.  Copying the profile file from the AndroMDA source dir to MagicDraw's profile directory ( /Program Files/Magic Draw UML/profiles on a Windows XP installation) will allow MagicDraw to find the profile file on its own.

For general information on AndroMDA modeling, see Modeling in AndroMDA

For a tutorial on creating models that work with the Spring cartridge (the most popular AndroMDA plug-in), see the AndroMDA Spring cartridge howto

For information about cartridge specific stereotypes and tagged values, first go to the AndroMDA Cartridge page , select the desired cartridge from the Available Cartridges menu on the left, and then select the Profile option on the left hand menu.

For more information on the Model Driven Architecture methodology in general, see http://www.omg.org/mda/

How AndroMDA generates code

The following describes the process used by the AndroMDA engine to generate code.  Details of each step follow this section.

  1. All of the cartridges that the project requires are loaded by the engine.
  2. The engine then parses the UML model's XMI file, creating an object tree of the model for use by the engine and the cartridge templates.  In addition to the main object tree, helper classes (called Metafacades) are created to simplify the work of the cartridge templates.
  3. The engine traverses the object tree, looking for classes tagged with stereotypes it recognizes
  4. For each class located, the appropriate cartridge template(s) are identified and dispatched to generate code.  Multiple source code files may be generated for each tagged class in the model: more than one template in a given cartridge may generate code for any given class; more than one cartridge may generate code for any given class. The default templates for a cartridge may be used, or a customized version may be used, depending on your configuration.
  5. Step #4 is repeated for every class in the model.

Loading the Cartridges to use

When the AndroMDA engine is started, it first loads and activates the cartridges it will use.  For a particular cartridge to be used, the cartridge .JAR file must:

  1. Be in the Java  CLASSPATH of the AndroMDA engine
  2. Be activated by referencing its namespace in the andromda.xml configuration file.

To put the cartridge in the CLASSPATH of the engine if using the Maven plug-in, modify the < projectDir >/mda/project.xml file and add a <dependency> for the cartridge to the <dependencies> section.  For more information, see Using a cartridge at the cartridges index page

If using the Maven plug-in, the andromda.xml configuration file is located in the < projectDir >/mda/conf directory.  This file contains one or more <namespace> tags to activate the cartridge(s).

For more information on configuring a cartridge's namespace, see configuring AndroMDA namespaces

Loading the UML model

Just as there is a standardized object model to represent a parsed XML file (The Document Object Model) and popular Java implementations of the DOM API (such as Apache's Xerces), so is there a standardized object model for the metadata stored in a UML model.  The standardized object model that represents UML metadata is called The Meta Object Facility (the MOF - see http://www.omg.org/mof ).  One Java implementation of this API is Netbeans' MDR (MetaData Repository - see http://mdr.netbeans.org/ ).  The AndroMDA engine uses the Netbeans MDR to load the UML model into memory.

The act of generating code from the UML model requires some computation not easily handled by the simple scripting capabilities found in the template engine used by AndroMDA.  Things such as traversing the MDR looking for relationships among classes, testing for certain conditions in the model, formulating identifier names and package names based on attributes found in the UML model - these all require computation handled easier in Java code.  To simplify the source code templates, the Facade design pattern is used to create helper classes that shield the complexities of the MDR from the template.  These helper classes are called Metafacades (for meta data facades).  Each cartridge used by the AndroMDA engine usually contains its own set of Metafacades to aid in platform specific code generation.  It is the engine's responsibility to instantiate these Metafacades for use by the source code templates.

For more information about Metafacades, see the metafacades documentation

Matching Stereotypes to Cartridges

After initializing, the AndroMDA engine traverses the UML model, looking for classes marked with a specific stereotype.  When it finds a stereotype it recognizes (or more specifically, a stereotype that a cartridge is known to recognize), the appropriate cartridge(s) is called to generate the code.

A cartridge is actually a .JAR file.  In this .JAR file there is (amongst other things), a file named /META-INF/andromda/cartridge.xml. This file is called the Cartridge Descriptor. Inside the Cartridge Descriptor, there are <template> tags that specify the code generation templates available from the Cartridge.  Inside each <template> tag is a <modelElements> tag. This <modelElements> node will contain one or more <modelElement> nodes.  Each <modelElement> is used to specify (among other things) the stereotype the template maps to.  This specification is done in one of two ways:

  1. directly via an attribute of the <modelElement> tag named stereotype.  The value of the stereotype attribute specifies the stereotype the template maps to.
  2. indirectly via a <type> inner node.  The <type> node specifies the name of the Metafacade class the template uses. The Metafacade's definition, in turn, contains a definition of the stereotype the Metafacade maps to. The Metafacade's definition is found in the Metafacade Descriptor - a file in the cartridge .JAR file named /META-INF/andromda-metafacades.xml .  The Metafacade Descriptor contains <metafacade> tags for each metafacade used by the cartridge.  Each <metafacade> tag in turn contains a <stereotype> tag which defines the name of the stereotype the metafacade maps to.

For more information on the Cartridge Descriptor, see The Cartridge Descriptor at the cartridge index page .

For more information on the Metafacade Descriptor, see configuring metafacades

Generating code from templates

Once the AndroMDA engine has identified a class from the UML model and matched it to a cartridge, the appropriate templates are called to generate code.

By default, AndroMDA uses the Apache Software Foundation's Velocity template engine to generate code.  Mechanisms exist to allow other template engines to be used, but in practice, Velocity does most of the work.  If you are unfamiliar with Velocity, see http://jakarta.apache.org/velocity/docs/user-guide.html#What%20is%20Velocity? for a good overview.

To see all of the templates available from a particular cartridge, examine the contents of the cartridge's Cartridge Descriptor file ( /META-INF/andromda/cartridge.xml) .  This file can be found in one of two places:

  1. inside of the cartridge .JAR file itself
  2. if it is part of the default AndroMDA source distribution, in the /src/META-INF/andromda sub-directory of the cartridge's source directory ( < androMDASourceDir >/cartridges/< nameOfCartridge > )

Each template available for use in the cartridge is defined in a <template> node inside the Cartridge Descriptor.  The <template> node contains several pieces of important information:

  1. The path attribute specifies the complete path (inside of the cartridge .JAR file) to the template file
  2. The outlet attribute specifies (indirectly) the sub-directory where the source file will be written to.  An outlet is a conceptual name assigned to a sub-directory. It is actually the name of a property found in the cartridge's namespace in the andromda.xml configuration file.  For example, an outlet named config-files would mean there is a namespace property named config-files in andromda.xml that specifies the sub-directory where all configuration files are to be written to.
  3. The overwrite attribute specifies whether or not the template will overwrite pre-existing source files on subsequent runs of the code generator.  In general, source code that requires hand modification will have the overwrite attribute set to false and will have a different outlet than code that does not require hand modification.
  4. The <modelElements> tag defines the Metafacade used by the template.  The variable attribute of the <modelElements> tag defines the name of the instance variable to be used by the template.  That template variable will be an instance of the specified Metafacade.

The <modelElements> tag of a <template> definition contains a <modelElement> tag, which may in turn contain a <type> tag.  The <type> tag specifies the Metafacade class that the template will use.  The <type> tag may optionally contain <property> tags.  These <property> tags are used for conditional code generation.  If a value is specified for a property, that property must match that value for template code to be saved to disk.  If the <property> tag does not contain a value, then that property simply must be defined.  The property might be a namespace property defined in the andromda.xml config file (to allow for the code generation to be defined at configuration time).  The property may instead be a property set by the template after it has run.  This allows the template itself to determine if code should be generated at run-time.

For more information on how cartridges work in general, see the cartridge index page

The Velocity Template Language reference guide can be found here http://jakarta.apache.org/velocity/docs/vtl-reference-guide.html

For more details on the Velocity template engine in general, see http://jakarta.apache.org/velocity/index.html

Customizing code generation

There are several ways to customize the code generation process. 

  1. Override a template from a pre-existing cartridge with a custom version
  2. Customize the entire cartridge, re-compiling the .JAR file
  3. Write your own cartridge from scratch.

Overriding a cartridge's default template(s)

The simplest way to customize code generation is to slightly modify or simply re-write one or two templates in an existing cartridge.  Assuming a particular cartridge does mostly what you are looking for, but you need to change or extend its functionality, you can instruct the AndroMDA engine to use your template rather than the one from the cartridge's .JAR file.

  1. locate the original template file you wish to modify.  These files can be found in the /templates sub-directory of the cartridge's .JAR file. If you have the AndroMDA source distribution installed on your machine, you can also find them in the /src/templates sub-directory of the of the cartridge's source directory ( < androMDASourceDir >/cartridges/< nameOfCartridge > ).
  2. Copy the desired files to your project directory.  If your project conforms to the standard project structure using the Maven plug-ins, the best place to put your copy is in the /mda/src directory of your project (such as < projectDir >/mda/src/customTemplates /).  Since you may override templates from more than one cartridge, it is also a good idea to further segregate each cartridge into its own sub-directory.  Finally, it is important you honor the template structure found in the cartridge.  So, to override the hibernate.hbm.xml.vsl template for version 3, place a copy of that file in the sub-directory < projectDir >mda/src/customTemplates/andromda-hibernate/templates/hibernate3 .
  3. In the cartridge's <namespace> entry in the andromda.xml configuration file, specify a mergeLocation property. For our example Hibernate template overwrite, we would make the following addition in the andromda.xml file:
<
namespace
name=
"hibernate"
> ... <
properties
> ... <
property
name=
"mergeLocation"
>${maven.src.dir}/customTemplates/andromda-hibernate<
/property
> ... <
/properties
> ... <
/namespace
>

When you specify a mergeLocation property, the AndroMDA engine will first look in your mergeLocation directory when looking for a particular template file.  If it does not find it, it will fall back on the cartridge's .JAR file.

See Overriding cartridge resources at overriding cartridge resources for more details.

Customizing existing cartridges

The standard cartridges that come with AndroMDA can be modified and extended if you need more control than a simple template override can provide. The source directory for a cartridge comes with the AndroMDA source distribution.  The cartridge's root directory is < androMDASourceDir >/cartridges/< nameOfCartridge > . All the source files are located under that directory.  From the cartridge root directory, the command maven install will re-build and re-package the cartridge.

For more details on developing cartridges, see developing cartridges

Writing cartridges from scratch

Entire cartridges can also be developed.  The most difficult aspect of cartridge development is the probable need for custom Metafacades.  The good news is that AndroMDA can be used to develop metafacades.  You can model your metafacade with MagicDraw, then have AndroMDA generate the metafacade code.

For more details on developing metafacades, see developing metafacades