Copyright © 2008
New Technology / enterprise Ltd.
Objectives
To see how modellers can use UML and similar visual languages to create specification, and what facilities IT architects can provide in JeeWiz to help them. As this is the last part to the tutorial, I'll also add sections on troubleshooting and debugging, property stroage and retrieval, and a summary of the sections covered by the tutorial as a whole.
Separation of Modeller's Language from Specialist's Architecture
In large organisations, architect's and modellers are usually different people. The architect primarily wants to make sure that the final system fulfils the technical framework in use by the company. The modeller primarily wants to make sure that the business objectives used to justify specifying, generating, testing, installing, training, running and maintaining the system are fulfilled.
Business modellers want to model the business specification in a language that suits them, and for most that means a natural language, such as English, or something graphical. It's not that XML is too difficult, it's just that they need buy-in from other stakeholders who don't read specialist computer languages but can still look at the pictures. So as they are going to write specification anyway, if we can convert that specification into something JeeWiz can use, once again we are fulfilling our mission of getting what is wanted turned into a system as directly as possible. A graphical specification that can do both things, be validated by those who understand what is needed and still be used to generate a system is clearly highly desirable.
These days most graphical modelling tools allow some form of export into XML, and of course that's an ideal route into JeeWiz. But each route is slightly different. Some variation can be had reading variant XML directly into the object tree, for example by the use of convert, but this approach has its limits. We've already seen that a JeeWiz XML model tries to keep everything as simple as possible, with no XMI-style specification unless absolutely necessary. So rather than try and write a stack layer for the graphical tool and look to massage the XML on input, we run a precursor transform, changing the tool's output into a standard simplified XML. This means that whichever tool the user chooses the main transform remains the same.
However, it also means that the minimum extra the architect needs to provide for a graphical modeller is a transform that will take the output of their tool and turn it into simple XML.
Graphical Modelling
Although this tutorial is about code generation rather than modelling, it's worth understanding the rudiments of what the modeller needs. Different tools give you different graphical elements, including
- objects:
- attributes:
- methods:
- parameters:
- associations between objects:
- ends of associations:
So we might decide to model a page (or screen) as an object, an attribute as an attribute on the page object, an event as a directed association going from one page to another, and the code to be run on the event as a method on the association.
To do this, we would need several facilities. We'd have to be able to distinguish the object as being a page, rather than, say, an entity. We might want to be able to add fields to the page, distinguish the fact that it was an attribute, and then each field would need to be able to accept various property settings. We'd need to be able to link the objects using a graphical line. Ideally the direction could be done visually as an arrow. The method could just be a property, but then we'd need a way of adding parameters to properties and each parameter needs to have something like a name and type and possibly restrictions. That's a lot of facilities for even a fairly simple model. We also might want to show n-ary associations, rather than just one-to-one, dependencies, timings and so on.
While it's possible to buy tools that let you create a graphical Domain Specific Languages, where all the object types can have their own image and anything can be marked up anyway you choose, in general there has to be compromise. Many UML editors are available and some are suitable, with only a little compromise, to creating general purpose modelling diagrams.
For example class diagrams have objects for classes that can take attributes and methods, usually with parameters. The type of an object is handled as a stereotype, and the properties as tag values. Some even allow the definition of an object shape depending on the stereotype.
As an example in this tutorial, we'll use the common Rational System Modeller (RSM) product, based on Eclipse. This is not a cheap option, but as yet we don't know of a free UML editor that has the necessary items. (We hope that the Eclipse initiatives will provide one soon, or we might have to end up writing one!) There are certainly cheaper products and non-UML based ones. Nevertheless the general principles are the same. We'll see some of the compromises needed for RSM.
Modifying a UML Modelling Tool - Stereotypes and Tags
The first requirement we'll look at is support for stereotypes and tag values. Experience shows that without these, a UML tool really isn't up to the job. The reason is that we need to be able to specify any properties that we need to associate with a particular meta-model class. So a screen might have a property called title and the modeller needs to be able to specify it. Without using tag values, the modeller would have to be lucky enough that the tool happened to have a property available that he could use to enter the title.
So tools allow the free entry of property name value pairs, such as Visio. Others, such as RSM, let you create a list of tag values for any particular stereotype. When you mark up an object as <
As well as properties, we want to be able to specify lists of other child objects. This can be achieved in UML using a built-in mechanism for specifying attributes or methods on objects, we can use associations or we can use a combination. If we specify a dataview or a menu, the obvious way of specifying fields or menu items would be using attributes on the parent object. Again we would have to be able to stereotype the attributes, for example as menu-items, and add tag values to them. Similarly the obvious way of specifying a list of events on a page is to use associations between pages (or from the page to itself).
The use of arrows to model movement from page to page is very easy for a business user to understand and validate as what the business requires, but it's not the only way of doing it. It would be equally possible to model an event as an attribute on the page and have its default destination specified as a property. This would mirror the way we did it in XML.
Similarly if we want to add a list onto the child list item, handling the child as an attribute becomes problematic. In RSM you can't have an attribute of an attribute, or associate an attribute easily with an other object. So if we want to add a constraint list to the fields of a dataview, we could model each field as an object and use an association to attach it to the dataview. We could do the same thing with each constraint, or model the constraint as an attribute of the field.
But can you imagine a diagram with just ten linked dataviews done that way, each with an average of say ten fields? [Well I'm not going to draw it for you!] The point of a diagram is to get the information over visually, and that just wouldn't do it.
Other ways to handle the child list of a child list problem include using comma-separated String lists and denormalizing the repeated properties. For example we can add all the properties associated with a constraint to a field directly, and do it say three times each with a numbered suffix. This allows the field to be modelled as an attribute. We can then reconstruct the constraint list as part of the JeeWiz transformation later.
It's all compromise and different compromises are required for different tools and different objects.
It's really worth limiting the types of diagram you intend to support. UML 2 has a huge number of elements amongst the large number of diagram types, and it's the architect who has to transform that intent into meaning. By limiting the modeller to using say certain class diagram elements, you have a far easier job. Is that good enough for the modeller to express what is needed? That's for negotiation.
So once we've decided how we want to handle the meta-model objects graphically, we need to build up the stereotype and tag value list, called the modelling profile. This information can largely be taken from the meta-model definitions. If the meta-models tell us what objects can be represented in the object tree, and hence which stereotypes we'll need, their properties will tell us what the tag values are for each stereotype. But not every object we model has a meta-model, nor do we necessarily want all meta-model properties available to the modeller, so we need a way of massaging the information when creating the stereotype/tag-value lists.
There is a standard transform available to create an Eclipse plugin for RSM, not in the resources area but in ${jwhome}/designTransforms/RsmRsa/jw2ScreenMMPlugin. This generates a .epx plugin which is installed in RSM. Two others profiles in the same area are for Rational Rose and Enterprise Architect.
The build is controlled using the filter.xml, which forms the main specification file used to decide which meta-classes and properties are put into the profile. It also allows extra classes, fields and lists to be added which aren't meta-modelled. Details on manipulating the profile area available in the Architect's Guide.
If you plan to make another modelling environment or IDE available, you need to give serious thought as to whether you will write something similar. It's possible to use a tool where any stereotype or tag can be added as free text, but this significantly increases the skill level and training time required for the modellers.
Transforming to XML
Once the modeller has finished modelling, we need to transform the information to JeeWiz simple XML. Many modelling tools save or can export in a form of XMI, and it is fairly easy to write a transform that reads the XMI and turns it into JeeWiz XML. Again it's possible to change one of the standard ones provided for Rational or Enterprise Architect to work with a different tool's version of XMI.
In either case we have to think about the additional classes and properties we added to allow the modellers to specify things that weren't meta-modelled. We could turn those into meta-modelled classes using convert, or let them be represented by extraBuildComponent objects. We can also look to add defaults in at this point, so if someone hasn't stereotyped an object or an association, can we guess what the intent is? For example, the attributes of a dataview might normally be a field. In fact that might be the only thing they can be. So if the modeller hasn't stereotyped them, we can still treat them as a field. In the case of RSM, if you don't stereotype something, you don't get access to the tag values, but that's not the case with most modelling tools.
As an example you can look at the ${jwhome}\designTransforms\RsmRsa\screenModel2jw transform. This takes the .emx file that RSM uses to save the model. Have a look at the system.properties for the converts and some defaults. Also note that the style List is amalgamated here. The reason is that styles in the transforms have been treated as a single list, but each area of styles can only have one value. So in the RSM modelling tool the styles are broken down into areas, which can be displayed as single value dropdown selectors. The values are then recombined using the system.properties to a single list.
Other IDE Support
What you can provide will vary from tool to tool. For example, in RSM it's necessary to produce a list of supported data types, which is generated from the same transform as the profile plug in.
Other things that we have already done for RSM include help file plug-ins, a new JeeWiz project wizard (that sets up the directories, etc) and the ability to run a JeeWiz generation from within the Eclipse environment.
In other tools we have created images to distinguish different object types, such as clouds and decision boxes. It will be up to you as the architect to decide what it is you can provide to help the business modeller create a diagram that is as meaningful to the business as it is to the transform.
Summary of the Generation Steps
- The Creation of the Meta-Model: We dealt with this in part two of the tutorial. Here we define the language elements of the specification, what properties they have and what child elements they support. The specification is written in XM and a JeeWiz transform is used to create the java classes and parcel them together into jars in the various stack levels.
- Create UML Profile: We use the flattened meta-class definitions and a filter to define what should be made available to the UML programmer. We could create an XML schema this way too, but no one so far has requested it.
- Create a Simplified XML Model: The modeller draws a UML diagram, and the output is transformed to simple XML. Alternatively the modeller can model directly in XML and miss out the last two stages.
- Input the Specification into an Object Tree: Input the XML specification into one of the two jwrun or jwcall ant tasks to create the object tree. Use the assemblyDir and templateDir properties to specify the input files and the transform stack levels.
- Start: Before the patterns are run, the start scripts in each stack level are run. They can call and create methods and properties and controls
- The Three Pattern Phases: Each of the Pattern phases involves a tree walk. The first two, the pre-phase and the extra-phase use a top down walk through the object tree. The final phase involves a bottom-up walk. Each object type can run a series of pattern scripts for each phase. If these patterns create other objects on the tree, their patterns will be run, and if they have missed a phase, the missing phases' patterns will be run first.
- Produce Output: Another tree walk leads to a set of ant builds for each object in the tree that has one defined. Each object can create nothing, one or multiple outputs based on different templates.
- Assemble the Output Files into a System: Use some of the ant builds of the template walk to bring together elements of the output. This will probably include compilation and the creation of jar files. Automated deployment might follow.
Troubleshooting
Bug fixing methods are a matter of taste. Some people (like me) like to see the minimum amount of diagnostic that tells me what I want to know - the print out some variable solutions. Others like to see a massive output of everything so they can wander through it and see exactly what's going one. Still other people like to step through a debugger using breakpoints. JeeWiz doesn't provide a stepwise debugger, but it does provide a lot of other facilities.
The first thing to consider is when things have gone wrong. It is when the meta-classes are being created? Is it during the main generation? Or during the system runtime? Within the main generation is during start-up phase, one of the pattern phases, the template output phase or deployment? If you can work out when things are going wrong, it's a lot easier to pinpoint what's happening.
The generation of meta-classes is a JeeWiz transformation in its own right, so the techniques used to find problems in the main generation can be used here too. Remember that the models are chained together and a problem in creating the meta-classes for one level might actually be in the parent level.
Getting a simple value output can be approached in one of two ways, either you can create a comment in one of the artefacts or you can output to the console. Creating comments is fine if you are getting output items, but console output also works when the errors are stopping the artefacts from being created.
The easiest way to output is using $this.log() which we met briefly in an earlier tutorial. The log helper method is available on all meta model objects, so it doesn't matter which object $this is referring to. Variables and expressions are resolved in the script so it's easy enough to use something like
$this.log("The value of myVar is ${myVar}")
If that wasn't what you were expecting you could try to see where the variable came from. If you think the value should have been set as a model object property you can look at the object using
$this.xmlDump or $this.getXmlDump()
Which dumps out the object and its children in XML format. The value of properties defined in the meta-model, and those set using the helper method setX() are shown, but properties which have been added using #set and aren't defined in the meta-model aren't shown.
getXmlDump() is also useful if you aren't sure which object a variable is referring to. The information can be sent to the console using log - $this.log($object.xmlDump()).
Perhaps even better than using an xmlDump to find out what an object is, is to use getIdentifyingString, which gives the type and name of the object and that of its ancestors, so you can see where it is in the tree.
A full xmlDump of the entire embellished object tree after all patterns have been run and before the template output phase is available as part of an aggregate "dump" file. The file also contains a list of the start scripts, pattern files and templates used during the generation, a list of the tree objects created as part of the pattern phases, and the script methods used.
To create the dump file, set the dump variable as part of the ant build. You can do this from the command line using
ant -Ddump=aggregate.xml
if you are using jwcall in the build.xml, or
ant "-Dpass=-Ddump=aggregate.xml"
if you are using jwrun.
Full diagnostics can be sent to the console if Jeewiz is run in verbose mode using for jwcall or jwrun
ant -v ant -Dpass=-v
respectively. Actually this sends out so much information, you'll probably want to send it to a file,
ant -v > "verbose.txt"
(or the equivalent pipe in Unix). This will tell you everything that JeeWiz is doing during the generation in the order it does it.
If you just want to know what the effect of the scripting directives are, including which properties are #set, which branch of the #if directive is followed, which #parse files are included, you can use traceExecution. This isn't quite as big as a verbose output, but you'll probably still want to redirect it.
ant -DtraceExecution=true > "trace.txt"
More often you only want to know about what's going on in part of the build. And the #trace directive can be used in a script to turn tracing on and it will then continue to the end of the file or until it you switch it off with #trace(false). Actually all the traceExecution does is turn tracing on at the beginning of every script, so if you leave a #trace(false) in the script, traceExecution won't output for the rest of that script.
In the output phase you will sometimes find problems when ant is trying to compile generated java files (or whatever language you are generating). If the problem is that you see a dollar variable in the source code, that will probably be due to a script variable in the template being null, when you thought it was always going to have a value. You can use the techniques I described above to see what's happening in the when the template is used to output the source file. The same thing can happen at runtime in the case of interpreted code such as javascript, or code compiled just before use such as jsp java scriptlets, but be careful as some java frameworks use dollar variables legitimately as part of the page's output.
Property Storage
Getting and setting properties in JeeWiz can be done almost instinctively in most cases, as the code in the engine obscures a lot of what is going on behind the scenes, but occasionally you really need to know what is going on.
Properties can be stored
- As JeeWiz/Velocity script variables
- As ant script variables
- On the JeeWiz project
- On model objects - in two ways
- On control/helper objects
So let's run through when these properties can be set, from where and in which of the slots above.
Script Variables
Velocity variables are set on a script using the #set directive or as a method parameter. If there is no dot notation used on the variable name in #set, the value is stored as a local variable and thrown away when the script is complete (or when a method variable is no longer in scope).
Ant script variables work in a similar fashion to velocity variables. They are set using property directives in the project or task, or they can be passed in from the command line calling the build.
Project Properties
The values of variables stored on the project are set automatically by the JeeWiz engine. There is a whole group of properties that are set when the jwcall or jwrun tasks get called - these are referred to in the Architect's Guide as "Start of Day". The first picked up have the highest precedence (so a variable of the same name found subsequently will be ignored).
For jwcall, the ant properties are picked up automatically, whereas for jwrun they have to be passed through explicitly.
Next properties are pulled in from the various build property files. These let you change the default properties for the user, the installation, the run, etc.
Additional specification files (as used when you build meta-models that need to reference parent meta-models) set up the type of their root elements.
All the properties from the template directories, model.properties, system.properties, where the build.xml files are, etc, are loaded centrally.
Finally properties are set up to handle the locations of the specification files.
Properties in system.properties and model.properties can refer to properties earlier in the file, or that have been loaded previously, and the engine resolves the values and aggregates multiple settings, storing the value on the project.
Model Object Properties
Variables are set on the model objects either through setter methods described in the meta-model, or in a separate Map that all meta-model objects inherit. If a property name has a setter associated with it, that will be used in preference. For example, if the property is defined in the meta-model specification, it will create the property as an attribute of the meta-class, together with accessor and mutator methods, so setting this value would result in the attribute value being set, rather than a property on the Map.
Values are set when the objects are instantiated. These might be default values coded in the class, or they might be values in the XML specification.
Values are automatically set on objects from the relevant component.properties file, in much the same way as system.properties are loaded into the project. The values are loaded on to an object after its pattern pre-phase is run, but they don't override values that have already been set. That means that component.properties can be evaluated using values set in start.vm or preIncludeSpec.vm.
Values can be programmatically set on an object in start, pattern or template scripts. Typically this is achieved using #set and dot notation. Eg.
#set ($this.myProperty = "value") #set ($parent.firstGrandchild = $this.childList.get(0))
Note that $this, $parent, $self and $super are special variables, set by the engine, which shouldn't be tampered with.
There are other ways of setting manually setting values on objects, such as using the setX helper method (that also exposes the value of the variable to the aggregate diagnostics). Attributes can be set directly by running mutator methods on the object, or by placing the objects in the Map using the put helper method.
Control Properties
Properties are set on controls from the control definition files when the control is instantiated. Controls implement Map and the properties are stored in the map. As well as using a property line in the control, they can be stored there from a script, for example using #set ($myControl.myProperty = "value"), or #set ($self.myProperty = "value") from a method in the control.
Property Retrieval
If you know where a property is stored, you can always use a relevant method to access it, but the whole point of having a scheme where the properties can be set in various ways is that you shouldn't have to know where it's stored most of the time. You should just be able to use it.
We can evaluate properties in
- A Velocity or ant script
- A method or a properties file
- A control method or property
Scripts
If we look up value in a script using $myProperty, the engine will try to find the value as follows:
- Try the script environment variables (velocity or ant)
- Try the current model object's getter method
- Try the current model object's Map
- Try the parent's model object (both getter method and Map) recursively to the root
- Try the project values
If a null value is returned by the getter method (as opposed to just not finding a method at all), the result will normally be a null value. However, we can designate the property on the bean to be a delegated property and that means continuing the search with the parent if the getter returns null, and eventually the project values. It is also the case that if the getter returns null on one of the parent objects, the search will keep going, whether or not the property was designated as delegated on the parent.
If the value is requested as $this.myProperty, the search is much shorter, and it will normally just check the getter method and Map on the current object. Delegation still works in this case and the parent objects are checked if a null is found. The same is true for any reference to a property on any model object: $parent.myProperty and $anyObject.myProperty.
Retrieving a property that is on a control from a script is different. Using the syntax $myControl.myProperty results in checking the Map on the control first for a non-null value. If none is found, any relevantly named getter method, getMyProperty() is used.
Properties File
In the case of a properties file, we evaluate system.properties and component properties using a syntax which allows for previously defined properties to be available. That could be properties in the same file or properties on the current object.
property2 = another ${property1}
This creates a new property using the old value as part of the new one.
But if there is only a variable following the equal sign, the engine will simple point the new variable to the old object, rather than creating a clone.
property3 = ${property2}
The syntax officially insists that you always use the curly braces: so ${myProperty} or ${anObject.myProperty}, but it also seems to work without in the case of pointing to the old object.
As with script, it's possible to use values previously set in the current object or set in the file, but the apparent precedence differs. That's because the "environment" properties are actually being set on the current object and will only be set if they haven't been already. A value previously set on the project is used last.
Methods Files
Before mentioning how properties are found in JeeWiz object methods, it's worth mentioning the precedence order for accessing the methods. A call to a Jeewiz method will first use one defined in the aggregated component.methods if there is one, then it will look for one in the calling script file and finally in the system.methods. A property on an object method is evaluated as in a velocity script in the calling context, so for $myProperty the environment is checked before the object, etc. That means you can also use ${this.myProperty} in a system.methods file, as it will always be called in the context of a particular object which will be the $this, even if the script code is held at the project level. If you call $object.method, $this in the method will refer to the $object.
Control Properties and Methods
Control properties can be set using previously set property values in a similar way to component properties.
newProperty=$myProperty
References to $myProperty are first evaluated against the control's HashMap, then on any getter. If no value is found on the control a project value may be used.
When a control method is called, it's done in the context of a current object, in the same way as other methods. $myProperty in the control method checks:
- As a script environment variable
- As a property on the control Map
- As a getter on the control
- As a getter on the current object, $this
- As a Map property on the current object
- The parent's model object (both getter method and Map) recursively to the root
- Finally a property on the project.
Notice that the control's Map is checked for properties before any velocity getter, whereas the current objects java getter would be check before the object's property Map. It's also worth noting that the model object used to access the method getNewControl is irrelevant. The current object $this is still the current object of the tree-walk.
If you want to be certain you only want to look for a value on the control, use the syntax ${self.myProperty}. And if on the current object, use ${this.myProperty} as before.
Conclusion
My advice would be to use $myProperty when retrieving a value if it's a locally set variable (ie a script variable or a prior set property) or if you've set the value globally but want it to be overridable at the level of the model object. Otherwise consider using dot notation (eg $this.myProperty) to get it from a particular object, if necessary making the property a delegate. There are several sections in the Architect's Guide that this section has been condensed from, and if you really want to get the final word, look there.
Exercises
You now know enough to write your own transforms using JeeWiz. So what are you waiting for?
