|
|
|
The Model-Driven System Builder
|
|
JeeWiz Architect's Guide
|
|
|
|
Contents >
8. Templates and Velocity Features
|
|
|
8.1 Template-only Features
The features described here are for templates only; they do not apply to patterns.
8.1.1 Introduction
|
We write templates to generate further products in the generation process.
JeeWiz runs a particular Velocity task to process a script that creates an output file, and/or possibly outputs.
JeeWiz can create any text file artifact that is based on a template - i.e. Velocity script - with substitution of values. The most common artifacts produced by JeeWiz are source code. In the whole J2EE process, we also create
- XML deployment descriptors, like ejb-jar.xml required for J2EE deployment
- second-stage build jobs, which can be run outside of JeeWiz
- JeeWiz specifications - the model descriptions in this manual are generated using this technique.
This chapter describes how to write the various components to produce these scripts.
8.1.2 The Velocity Task
|
<jwVelocity> Ant task is a JeeWiz-provided task that converts an input 'script' into output text,
using the current model object as its context for substitution values.
It is only usable in JeeWiz projects, from the build.xml for a meta-class.
The parameters are as follows:
| template
| the name of the template file (the 'script'). The format must be either
- a simple, relative filename (normally of the form X.vm).
This file is searched for along the chain of models, as described in the
Finding Files section.
- 'object/scriptname', where 'object' is used as the name of the object directory to use instead of the current model object,
as described in Using Files From Other Objects
This property allows you to change the rendering style for an object.
For example, if a class is to be generated as a queue (as opposed to a simple class)
then the modeler can specify 'template=queue' for the object, to change the initial directory to be searched for rendering files.
Note that the underlying Java object is determined by the type of XML element,
which equates to the stereotype in UML models.
The 'template' property allows the modeler to specify a different rendering on an existing meta-model class.
If the rendering should inherit some aspects of the rendering of the existing meta-model class,
the meta-programmer must define a 'template.properties'
file with an appropriate 'goto' property.
|
| file
| the output file. This will normally be expressed using properties in the environment to locate the file.
For example, the EJB jar's task to write the ejb-jar.xml file is:
<jwVelocity template="ejbJarXml.vm"
file="${genDir}/META-INF/ejb-jar.xml" />
|
The logic for writing out the resulting file checks the contents of the existing output file.
If this exists and has the same contents what has been generated, the file is left unchanged.
The program produces an informational log:
Velocity script page.vm did not change status of [file]
See the section Handling Of The Same Result From A Script below for more details.
If the output is 0-length - the whole script is bracketed with a false-valued '#if' - then the resulting file will be deleted.
The technique of using Velocity scripts bracketed with '#if's is the preferred way to express conditional generation of files,
because it ensures that if the condition changes, the file is correctly deleted.
See Null Scripts below for further discussion.
When the "file" parameter is specified in the Velocity task, the absolute path, in the native format of the current platform
(i.e. with '\' path separators under Windows), is placed in the context under the name 'velocityOutputFile'.
This means that the path can be referenced by the Velocity expression '$velocityOutputFile'.
|
| property
| this produces a 'snippet' of code that is put in the 'properties' HashMap of the model object.
(Even if the 'object/scriptname' format for the template is used, this still gets attached to the current model object.)
The name of the property should be a standard variable name like 'snippet' (e.g. 'property="snippet").
What is the point of this?
A snippet stays around after the build job has been completed, because it is attached to the current model object, which stays in place for the life of the build. This means it can be used by higher level objects. For example, here is some Velocity script from a constructor:
#foreach( $param in ${parameterList} )
#foreach( $constraint in ${param.constraintList} )
$constraint.snippet
#end
#end
|
This loops through each constraint for each parameter on the constructor and pulls out any snippets of constraint code.
The primary reason for using snippets is they are easier to manage than the other alternatives, which are in-line code (which is viable if there will only ever be one reference to the snippet) and macros. In theory, you would think that macros would just as easy to work with, but it turns out that snippets are more modular and are easier to understand in the overall JeeWiz structure.
Snippets have an important role in building systems using patterns as described in the next chapter.
|
| mergeByUID
| mergeByUID is false, by default: with this setting, the output file is overwritten in its entirety.
When mergeByUID is true, the output file is a merge of the existing file and the newly-generated file.
This feature is relevant to user-coded program files, where the code is hand-written by a developer but the class definition and
the method signatures will be driven by the specification (in XML, or the UML model).
These must be merged to keep the file in step.
The merge is done by UID - methods are recognised by UID, enclosed in a comment (/*UID: ...*/), preceding the method signature.
This setting only makes sense, and is only relevant to, output files rather than properties.
'mergeByUID' is normally applied to Java files, but it can also be applied to other types of generated files using a plug-in mechanism.
A plugin for another language - or a replacement for Java even - is defined by placing an entry in a system.properties file.
For example, here is the entry for Java, from jeewiz/resources/java/control/system.properties:
### The merge processor for Java is an existing engine file
MergeProcessorClass.java = uk.co.nte.jw.engine.MergeProcessorJava
|
All entries for a merge process must begin with "MergeProcessorClass."; this is then followed by the (case-sensitive) file extension.
(If you need to handle upper and lower case extensions, put two 'MergeProcessorClass.*' entries.)
The class can be any class (i.e. it does not have to be in the engine package) but it must be on the classpath -
which will typically be put in a 'components.jar' for a template directory, e.g.
jeewiz/resources/java/control/components.jar.
Finally, the class must implement IMergeProcessor - see jeewiz/engine/src/uk/co/nte/jw/engine/IMergeProcessor.java.
|
| traceStyle
| The traceStyle affects how parse tracing will be generated while a particular script is being processed,
when tracing is in effect.
(#parse tracing follows the course of Velocity 'includes' to process a script; see
the build properties
traceTemplates and
trace for details.)
The output from trace parsing is normally determined by the file extension:
- 'code' files - ending in '.java', '.cs', '.h' and '.cpp' - have traces added as Java/C comments.
Similarly, snippets have trace lines added as Java/C comments, on the assumption that most snippets end up in code.
- 'xml' files - ending in '.xml', '.xmi', 'sp' (for jsp and asp) and '.aspx' - have traces added as XML comments
- 'jsp' files - ending in '.jsp' or '.asp' - have traces added as JSP/ASP comments.
- Other files have no tracing.
To change the default settings, use traceStyle. The three values are 'code', 'xml' and 'none'.
This only affects comments generated automatically by the trace* processing of the JeeWiz engine.
You may want to have appropriate comments in your scripts, or generate them as part of the script processing.
This is viable and reasonable ... and completely independent of the traceStyle feature described here.
|
| parameter
| You can pass a single value into Velocity build using the 'parameter' property.
This sets a local variable into the evaluation context for the script, with the name 'velocityParameter' - which is case-sensitive.
(The local variable is named 'velocityParameter' rather than 'parameter' to reduce the probability of conflicts,
as 'parameter' is more likely to be the name of an object property or another local variable than 'velocityParameter'.)
For example, if you say
<jwVelocity template="..."
file="..."
parameter="P1"
/>
it is as though you had
#set( $velocityParameter="P1" )
as the first line of the Velocity script.
Note that only one parameter is allowed, its name is fixed as 'velocityParameter' and it is effective only for the duration of the Velocity script.
|
| outputEncoding
| this overrides the global setting of outputEncoding for this
transformation only. The default output encoding for subsequent <jwVelocity> task invocations will revert to
the global setting.
|
As a convenience, if a snippet ends with a line-feed (which is common), the line-feed is removed before storage.
This is done because otherwise you have to take special measures to avoid a spare line creeping into the renderings.
Normally only one output is produced - either 'file or 'property' is specified.
However, you can produce both outputs by specifying both 'file and 'property' if that makes sense.
8.1.3 Handling Of The Same Result From A Script
|
If you run a velocity task and the output is the same as the contents of the target file, the target is not updated.
| This may be confusing when you look at the output files - you can do a run and they will not have changed. In fact, you may have trouble finding any changed output files if you have only made changes to a small number of source files. If you are trying to see if a run has produced any output in the right place, remember to look for files that should have changed. Alternatively, run JeeWiz in verbose mode ('ant -Dpass=-verbose'), which will tell you the disposition of files.
|
The reason for doing this is to avoid unnecessary processing at later stages, such as recompilations. Many of the standard Ant tasks, like <javac> and some JeeWiz tasks provide built-in dependency checking; by not changing the date on the file if the contents are unchanged, the dependency-checking can take effect and reduce build-times.
8.1.4 Null Scripts
|
Sometimes you will have a script that should only produce output if a certain condition is present.
For example, in Velocity we would say
#if( $needThisFile )
...
#end
This indicates that there is a boolean-valued property in the scope of the current object
that tells whether the file is neede.
There is a special JeeWiz feature that will ensure that files that have no contents do not exist. In the case that there is no remote home, this condition will be triggered: if the file does not already exist, no file will be written; if it does exist, it will be deleted.
| You might be tempted to put the conditional for this in an Ant build file using the 'if' property of the target, as in the following:
<target name="generateFile"
if="needThisFile">
<jwVelocity template="X.vm"
file="${dest}/${X}.java"
/>
</target>
There are actually two gotchas here.
Ant uses different semantics for testing existence of a property, which can cause trouble.
You might think the above sequence says 'if the file is not needed, the <jwVelocity> task will not be executed'.
Unfortunately, Ant does a "property" lookup - which returns a string -
so if the 'getGenerateFile()' method on the current model object returns boolean false,
the value returned to Ant will be converted to the String "false".
Ant then sees a value, not null, so the 'if' condition is met and the velocity task is executed.
To avoid this problem, put the conditional in the Velocity script as described above.
The other gotcha is, if the condition changes, this approach won't delete the file.
It is better to put the condition in Velocity script, so that if the condition fails, JeeWiz will delete an existing file.
|
 |
|
 |
Copyright (c) 2001-2008 New Technology/enterprise Ltd.
| |