The primary plugin of the AndroMDA framework, cartridges
provide the ability to process model elements that have specified stereotypes
(i.e. <<Entity>>
, <<Enumeration>>
, etc.) or
model elements that meet certain conditions inferred by the model (all Actors that have a dependency
to a <<Service>> for example). Cartridges process these model elements using template
files defined within the cartridge
descriptor
.
This page describes the internal structure of an AndroMDA cartridge. After reading it, you will probably find it very easy to write your own cartridge or customize an existing cartridge for your needs.
If you want to use existing cartridge(s) for generation (i.e. the BPM4Struts Cartridge , etc.), simply place the cartridges on your classpath; using the AndroMDA Maven Plugin this is VERY simple, you just add the AndroMDA maven plugin and cartridge(s) as dependencies to your project.xml file and then run the plugin goal andromda:run, that's it!
<dependencies> ... <dependency> <groupId>${pom.groupId}</groupId> <artifactId>maven-andromda-plugin</artifactId> <version>3.0</version> <type>plugin</type> </dependency> <dependency> <groupId>${pom.groupId}</groupId> <artifactId>andromda-bpm4struts-cartridge</artifactId> <version>3.0</version> </dependency> <dependency> <groupId>${pom.groupId}</groupId> <artifactId>andromda-hibernate-cartridge</artifactId> <version>3.0</version> </dependency> ... </dependencies>
Sometimes you may want to override some of the resources a cartridge uses without touching
the internals of the cartridge: i.e. you want to add some more stuff to a JSP-page
that the
BPM4Struts Cartridge
generates. Easy to do: just specify a mergeLocation when declaring the cartridge-dependency
and put the replacement-files in that directory (if you generated your project using the Maven
andromdapp:generate
plugin you will need to do that in
<your-project>/mda/conf/andromda.xml
):
... <namespacename="bpm4struts"> <properties> ... <propertyname="mergeLocation">${maven.src.dir}/cartridge/custom</property> ... </properties> </namespace> ...
This way the cartridge will try to load resources (templates, images, etc.) from
${maven.src.dir}/cartridge/custom
prior to loading them from the cartridge-jar.
When loading resources from the merge location you will need to respect the same directory structure
as in the cartridge jar. This means that when the cartridge contains templates/SomeTemplate.vsl
and your merge location is ${maven.src.dir}/cartridge/custom
you will need to copy your new template in
${maven.src.dir}/cartridge/custom/templates/SomeTemplate.vsl
, like this you can override all cartridge resources.
Sometimes you may want to merge (or replace) things in a cartridge: this can be done in templates, within the cartridge.xml, or even within the metafacades.xml. For example within the BPM4Struts cartridge, the web.xml (or struts-config.xml) may not contain everything you need (i.e. you may need to add a new <servlet/> and <servlet-mapping/>, some <context-param/>, or even <error-page/> definitions).
This is easy to do: just specify a mergeMappingsUri namepace property when declaring the cartridge-dependency and create a new mappings file in which to map what you want merged.
... <namespacename="bpm4struts"> ... <propertyname="mergeMappingsUri">file:${maven.conf.dir}/mappings/Bpm4StrutsMergeMappings.xml</property> ... </namespace> ...
For example, this fragment below (from a mappings file) says we want to replace anything found matching <!-- error-page merge-point --> (defined in the <to/> definition) with the <error-page/> fragments (found in the from <from/> definition):
<mappingsname="Bpm4StrutsMergeMappings"> ... <mapping> <from><![CDATA[]]></from> <to><![CDATA[<404 /index.jsp 408 /index.jsp ]]> 400 /index.jsp /to> </mapping> ... </mappings>
In addition to the ability of merging literal strings into merge-points (the contents of the <to/> element). It's also possible to merge the entire contents of one or more files into a merge-point. For example, this fragment below (from a mappings file) says we want to replace anything matching # custom-messages merge-point with the combined contents of the files ../../../web/conf/resources/custom-resources1.properties and ../../../web/conf/resources/custom-resources2.properties (please take note of the nested <path/> elements, these tell AndroMDA to treat the contents of the to element as the concatinated contents of the files defined by the paths).
<mappingsname="Bpm4StrutsMergeMappings"> ... <mapping> <from><![CDATA[# custom-messages merge-point]]></from> <to> <path>../../../web/conf/resources/custom-resources1.properties</path> <path>../../../web/conf/resources/custom-resources2.properties</path> </to> </mapping> ... </mappings>
If you wanted to add resources (i.e. a new template, image, static file, etc) to a cartridge without modifying the contents of the cartridge, you could use the mergeMappingsUri in combination with the mergeLocation . You would use the mergeMappingsUri to define the new template in a cartridge, and use the mergeLocation to specify the actual template.
An AndroMDA cartridge is a resource (directory or jar file) on the classpath that consists of several items:
File | Contents | Required |
---|---|---|
/META-INF/andromda/namespace.xml
|
Because the cartridge ia a namespace component it MUST be registered within a namespace descriptor . This descriptor is what allows the cartridge's namespace to be "discovered" on the classpath. This namespace descriptor also registers the cartridge component within the AndroMDA core. | Yes |
/**/*.class
|
There could be a couple uses for this directory, placing cartridge specific metafacade classes, and/or other support classes (such as templateObjects | No |
/META-INF/andromda/cartridge.xml
|
Declarative cartridge descriptor (see below). | Yes |
/META-INF/andromda/metafacades.xml
|
Metafacades descriptor. This is used to specify metafacades for the underlying metamodel (UML 1.4, etc.). | No |
/templates/**/*.vsl (or any template extension depending on your
cartridge
template engine
).
|
Templates that tell the cartridge how to format the generated code. | Yes |
All items except the descriptor and the templates are optional. If you want to see a complete cartridge, have a look inside the andromda-bpm4struts-cartridge cartridge.
The AndroMDA core uses this descriptor to discover the capabilities of a cartridge: the
supported metafacades, stereotypes, outlets, templates, property references, etc.
The cartridge descriptor must reside in the META-INF/andromda
subdirectory and must be named cartridge.xml
.
Let's have a look at part of a typical cartridge descriptor:
<cartridge> <templateEngine><!-- library of macros used in template engine --><macrolibraryname="templates/EJB.vm"/> </templateEngine><!-- define the template objects that are made availble to the template --><templateObjectname="str"className="org.andromda.utils.StringUtilsHelper"/> <templateObjectname="transform"className="org.andromda.cartridges.ejb.EJBScriptHelper"> <propertyreference="viewType"/> </templateObject> <resourcepath="templates/*.properties"outlet="entity-beans"overwrite="true"> </resource> <templatepath="templates/EntityBean.vsl"outputPattern="{0}/{1}Bean.java"outlet="entity-beans"overwrite="true"> <modelElementsvariable="entity"> <modelElement> <typename="org.andromda.cartridges.ejb.metafacades.EJBEntity"/> </modelElement> </modelElements> </template> <templatepath="templates/EntityLocalIntf.vsl"outputPattern="{0}/{1}.java"outlet="entity-beans"overwrite="true"> <modelElementsvariable="entity"> <modelElement> <typename="org.andromda.cartridges.ejb.metafacades.EJBEntity"/> </modelElement> </modelElements> </template> <templatepath="templates/EntityHome.vsl"outputPattern="{0}/{1}LocalHome.java"outlet="entity-beans"overwrite="true"> <modelElementsvariable="entity"> <modelElement> <typename="org.andromda.cartridges.ejb.metafacades.EJBEntity"/> </modelElement> </modelElements> </template> <templatepath="templates/EntityBeanImpl.vsl"outputPattern="{0}/{1}BeanImpl.java"outlet="entity-impls"overwrite="false"> <modelElementsvariable="entity"> <modelElement> <typename="org.andromda.cartridges.ejb.metafacades.EJBEntity"/> </modelElement> </modelElements> </template> ... </cartridge>
Note the usage
of the "{0}/"
pattern. This will cause the target
java file to be generated into "com/mycompany/test"
if it was in the package "com.mycompany.test"
.
The <cartridge/>
element is the root of
the cartridge descriptor and also gives a name to the cartridge.
Attribute | Description | Required? |
---|---|---|
name | Specifies the name of the cartridge. | Yes |
The <property/>
element is used to specify
property references for a cartridge. The values of these references are defined
within the
AndroMDA Configuration
namespace elements.
For example, if you have the property reference securityEnabled
defined
(i.e. <property reference="securityEnabled"/>),
then the framework will expect the client to define a namespace property
named securityEnabled
when this cartridge is run. This property
(and it's value) will then be made available to the template during processing.
Attribute | Description | Required? |
---|---|---|
reference | Specifies the name of the reference. This is the name of a property that must be defined in the namespace descriptor and is the name of a variable that will be made available to the template during processing. | Yes |
The <templateObject/>
element is used to specify
helper objects within your cartridge. These must have a default constructor
and must be a class available to your cartridge (either internally or externally).
You can also define property references within your template objects. In order to do this, you'll need to make sure you have a java bean setter matching each reference name. For example:
<templateObjectname="hibernateUtils"className="org.andromda.cartridges.hibernate.HibernateUtils"> <propertyreference="hibernateVersion"/> </templateObject>
Attribute | Description | Required? |
---|---|---|
name | Specifies the name of the template object. This is the variable name under which the template object will be made available to your template. | Yes |
className | The fully qualified class name of the template object. | Yes |
The <template/>
element is used to describe
the template that will be used to generate source code.
Attribute | Description | Required? |
---|---|---|
path | Specifies the path (relative to the cartridge root) for a template (*.vsl) file to use for code generation. | Yes |
outputPattern |
Specifies a pattern in
java.text.MessageFormat
syntax.
You can use this pattern to tell AndroMDA how to construct
output file names. The pattern can consist of any ordinary
printable characters as well as some predefined placeholders
for things that AndroMDA already knows about: {0} stands for the package directory of the class.{1} stands for the class name.See example above. |
Yes |
outlet | Specifies the logical name of the outlet where the cartridge will write the output files caused by this template. | Yes |
overwrite |
Specifies whether the files already specified by the
outlet attribute should be overwritten
when AndroMDA runs the next time.
|
Yes |
generateEmptyFiles |
Specifies whether files should be generated even if the template did not produce
any output. This can be used by the cartridge developer to decide if a certain file should be generated
based on the information in the model. Note: If this property is set to "false", the
template produces no output, overwrite is set to "true", and an existing file is found
(probably generated by a previous run), then this file is removed.
|
No, default is false .
|
outputToSingleFile | Specifies whether you want to output all aggregated model elements to a single file. This is useful for example if you wanted to generated a SQL script that had tables for all entities in your model. This also allows you to group more than one type of model element into multiple collection variables which are then made available to your templates. |
No, default is false .
|
outputOnEmptyElements |
This attribute only makes sense when
outputToSingleFile
is set to true .
Indicates that when there are no elements aggregated
(i.e. an empty collection),
whether or not the file should still be output.
|
No, default is true .
|
The <modelElements/>
element is nested directly within
the
<template/>
element, it is used to
instruct each
<template/>
on which model
elements it should process. The <modelElements/>
ecloses the
<modelElement/>
.
Attribute | Description | Required? |
---|---|---|
variable | The variable which is made available to the template (i.e. a value of entity would make $entity available in a velocity template). | No |
The <modelElement/>
is nested directly within
the
<modelElements/>
, it is used to
within the
<modelElements/>
to instruct each
<template/>
on which model
elements it should process. For example, using this element you can specify both stereotypes and
or metafacade types (and optionally the metafacade's properties).
Attribute | Description | Required? |
---|---|---|
stereotype | Defines the name of the stereotype the model element must have in order to be made available to a template. | No, because a nested <type/> may be used instead. |
variable |
The variable which is made available to the template. This would only make sense to use
if you set the
outputToSingleFile
attribute to true, normally
the variable from the <modelElements/> attribute is sufficient for
your templates. In combination with the
outputToSingleFile
attribute
this would allow you to group model elements together and make them available to the template as collections.
The below example makes all model elements stereotyped with <<Entity>> and <<Service>>
as $entityEjbs and $sessionEjbs respectively (if you're using velocity for your
templating language). If you take a look at the enclosing <modelElements/> , you'll notice
the ejbs as the variable value, this will make ALL
entities and services available as $ejbs in a velocity template (since
outputToSingleFile
is set to true).
... < |
No |
The <type/>
element is nested directly within
a
<modelElement/>
, it is used to
specify in what
metafacade
type
the meta model element MUST be wrapped in order
to be made available to a
<template/>
.
For example, this could make each metafacade of type
org.andromda.cartridges.webservice.metafacades.WebService
available to a template:
... <templatepath="templates/webservice/wsdl/wrapped-wsdl.vsl"outputPattern="{0}/{1}.wsdl"outlet="wsdls"overwrite="true"> <modelElementsvariable="service"> <modelElement> <typename="org.andromda.cartridges.webservice.metafacades.WebService"/> </modelElement> </modelElements> </template> ...
Attribute | Description | Required? |
---|---|---|
name | The name of the metafacade type. (i.e. org.andromda.core.uml.ClassifierFacade, org.andromda.cartridges.webservice.metafacades.WebService, etc). | No, the stereotype attribute could be used instead. |
There are optional nested <property/>
elements
that may be placed directly within a
<type/>
. These properties
are used for two purposes:
<template/>
true
available to a template:
... <modelElementsvariable="enumeration"> <modelElement> <typename="org.andromda.metafacades.uml.ClassifierFacade"> <propertyname="enumeration">true</property> </type> </modelElement> </modelElements> ...
... <modelElementsvariable="entity"> <modelElement> <typename="org.andromda.metafacades.uml.Entity"> <propertyname="navigibleConnectingEnds"variable="associationEnd"/> </type> </modelElement> </modelElements> ...
Another VERY useful application of these properties, is to determine when and when not to output a file (for example you may only want to output a WSDL for a web service that has at least one exposed operation), so in that case you'd map a nested property checking for the exposed operations like the example directly below:
... <modelElementsvariable="services"> <modelElement> <typename="org.andromda.cartridges.webservice.metafacades.WebService"> <propertyname="exposedOperations"/> </type> </modelElement> </modelElements> ...
Its important to note that you are NOT required to specify a property's value when using the nested
<property/>
, if you do not specify the value attribute, then the
properties needs to be valid, meaning the follwing things: it's non-null, it has
one or more elements if its a collection type property, or has a value of true
if a boolean type.
Attribute | Description | Required? |
---|---|---|
name | The name of the property to check the exitence of (there must be a valid java bean accessor on the metafacade). | Yes |
The <resource/>
element is used to process files inside the
cartridge without passing them through any template engine. They will simply be copied.
Attribute | Description | Required? |
---|---|---|
path | Specifies the path (relative to the cartridge root) for a resource file to be copied to an outlet. You can use the asterisk wildcard to match multiple characters at once, similar to Ant filesets. The matching will start relatively to the root of the cartridge, similar to the values used in the path attribute of the <template/> tag. | Yes |
outputPattern |
Specifies a pattern in
java.text.MessageFormat
syntax.
You can use this pattern to tell AndroMDA how to construct
the resource output file names. The pattern can consist of any ordinary
printable characters as well as some predefined placeholders
for things that AndroMDA already knows about: {0} stands for the name of the resource
file contained within your cartridge.See example below. |
Yes. |
outlet | Specifies the logical name of the outlet where the cartridge will copy this resource. | Yes |
overwrite |
Specifies whether the files already specified by the
outlet attribute should be overwritten
when AndroMDA runs the next time.
|
Yes |
required | Specifies whether or not the resource is required during processing, if set to false, AndroMDA will ignore the fact that no outlet property has been defined in a namespace when running AndroMDA. Otherwise if its true, warnings will be issued notifying a cartridge user that they must define a property matching the outlet name. |
No, default is true
|
This example would copy all .jar
files to the
location defined by the resource outlet within
the lib
directory.
<resourcepath="**/*.jar"outputPattern="lib/{0}"outlet="resources"overwrite="false"/>