JeeWiz Home  
The Model-Driven System Builder

JeeWiz Architect's Guide
 
Contents  >   7.  Finding Values and Files
 


7.4 Finding Files in Template Directories

In processing a build, JeeWiz must find a number of files - the build files, the component.properties, and any files referred to in the build files. The main section describes the techniques JeeWiz uses to do this.

JeeWiz provides a wealth of features for finding build files, which can create long search chains. To reduce search times, the results of all searches (files found or not found) are cached, so the search process is only executed once for a given template name/meta-class combination. This means that you should not let the length of the search path for template files deter you from using stacked meta-model directories. In practice, the length of this search chain does not make an appreciable difference in normal use of 10-30 directories in the chain.
JeeWiz has a special loader that follows the template directory chain to find these files. The template directory chain starts with the templateDir property you specify in the build properties *.jwp file and then follows the parent model properties chain.

Within each model, the file must be looked for against a particular meta-class, so we use a template directory name based on the xml-style name of the object type - like 'application' or 'ejb-jar' - below the model's control directory. The full path search for therefore is
$modelControlDirectory / $elementXMLName / $filename
The search process stops when it finds a file of the right name: any other files that may exist further down the chain are ignored.

For example, say we are processing an 'application' object in a WebLogic6 build. If we look for the build.xml file, the paths searched will be
        jeewiz/resources/weblogic6/control/application/build.xml
        jeewiz/resources/j2ee/control/application/build.xml
and the file is found in the j2ee model - it produces the standard 'application.xml' required by J2EE.

In describing this search path,
  • jeewiz/resources/weblogic6/control is the control directory for the WebLogic 6 model
  • jeewiz/resources/weblogic6/control/application is the template directory for the application object in the WebLogic 6 model
  • jeewiz/resources/j2ee/control is the control directory for the J2EE model
  • jeewiz/resources/j2ee/control/application is the template directory for the application object in the J2EE model
It is possible, and desirable, to override implementations of the scripts by adding your own control directory at the front of the chain. (It is much better to do this than to start changing the JeeWiz standard implementations, although there is nothing to stop you doing this.)

It is obvious how this can be done for a single application server - you just create your own 'overriding' model by creating its control directory and in the 'model.properties' define the parent= property to be the single application server's model (as in its control directory).

But how can you provide a local project's override for many application servers without duplicating a lot of scripts? This will be necessary if you are using a combination of application servers, e.g. JBoss on the desktop and WebLogic for deployment.

The answer is to
  • define a build properties *.jwp file for each application server you want to target
  • point the templateDir at your own override directory
  • also define a 'model=' property in the build properties file, appropriate to the app server (e.g. 'model=jboss3')
  • in the model.properties in your template directory, say
    parent=$jwhome/resources/${model}/control

 7.4.1  The Template Property
 7.4.2  The template.properties File
 7.4.3  Using Rendering Files From Other Objects
 7.4.4  #parse and the 'super' feature
 7.4.5  Using 'super' in build.xml
 7.4.6  Using Overriding Files

7.4.1  The Template Property
The 'template' property (available on all meta-classes) allows you to change the starting point for searching for rendering information. This is a fundamental JeeWiz property: you can set it on any model object.

(To emphasise: the 'template' property affects the script search path for both templates and patterns. This is a historical name.)

Remember (from the previous section) the search for a given file is defined by
$modelControlDirectory / $elementXMLName / $filename
where $elementXMLName is the XML name of the meta-class's element.

If we specify a template, then this changes to
$modelControlDirectory / $template / $filename
where $template is the value of the template property.

In other words, the rendering is not defined by the meta-class - the directory underneath the control directory is defined by the 'template' property. For example, if we have
<jwclass name="SomeFactory" template="specialised-rendering"/>
then the 'Jwclass' meta-class will be used for holding values, but JeeWiz will search for the rendering files in the /specialised-rendering directory, not the /jwclass directory. This gives an easy way to define different renderings for the same meta-classes.

For example, implementation classes in the business object model have a specialised rendering. Therefore, there is a template directory called 'impl-class' in the /bizobject control directory, which holds the templates for the rendering. This is accessed by putting a 'template="impl-class"' on the object.

Note that meta-classes with a 'template' property do not by default inherit the rendering from the meta-class - there may be good reasons to completely divorce the new rendering from it. Of course, it is possible to inherit the rendering, simply by adding a template.properties file with a 'goto' line giving the name of the underlying object.
7.4.2  The template.properties File
The search order described in the previous section can be altered by 'diversion signs' given in a template.properties file in the control directory of an object's rendering. This allows the search to be switched from one object's directory to another object's.

There are two types of lines you can specify in the template.properties file:
include=[template] Transfers unsatisfied file searches to the named template, but if the file is still not found, continues processing lines in the template.properties file.

The common scenario for using includes in a template.properties is one 'include' line followed by a 'goto' line.

The include feature can be turned off for the current object type (or directed-to directory) by an 'include=' line in the template.properties file. Once a 'goto=' is encountered, includes will be honoured again. For example, some EJB features are added in by an 'include=ejb' entry, which is used by session, entity and message-driven meta-classes. These occur (in current implementations) in the template.properties files in the session, message-driven and ejb-entity directories in the J2EE meta-model. When EJBs are not to be used (but servlets are), an 'include=' line can be inserted in the template.properties file in a 'higher' meta-model to prevent inclusion of the EJB features. For example, in a customer's meta-model, you could add a 'session/template.properties' with the 'include=' line. This would turn off the ejb features for the session object only.
goto=[template] Transfers unsatisfied file searches to the named template. This will be the last line processed in the template.properties file. For example, there is commonality between business-methods and methods. In some cases, the business-method provides a template; in other cases, the template may be the same between business-method and method. If an unsatisfied search is switched from the business-method to the method object, then the method object's templates can be re-used.

The template can be omitted from the 'goto' - the line is simply 'goto='. This is interpreted as a request to stop looking any further (it's going nowhere!). In other words, no further searching is done for a template file. This is commonly used to stop further templates being picked up from a less-precedent meta-models.
The 'goto' and 'object' diversion signs operate on any file, even including the special files 'build.xml', 'component.properties', '(pre)includeSpec.vm' and 'uptodate.vm'. What it says to the generator is: "if you haven't find the file you are searching for after this directory, start the search afresh using this new meta-class".

As a hypothetical example, say our stack of models is
myCompany
weblogic6
j2ee
bizobject
object
and we are processing 'business-method' and searching for the files 'x.vm' and 'y.vm'. The effect of putting a 'template.properties' file in the 'bizobject' with goto=method is to alter the searched directories as follows:

myCompany / business-method
weblogic6 / business-method
j2ee / business-method
bizobject / business-method
<<-- template.properties processed here
myCompany / method
weblogic6 / method
j2ee / smethod
bizobject / method
object / method

Note that the generator effectively restarts the search so the 'myCompany' model is revisited to start the search. This search process occurs for the file 'x.vm' and then is repeated for the file 'y.vm'.

It is possible to have many diversions (there is no limit) during the search for a given file. The 'goto' therefore gives you a way of doing controlled inheritance of templates: you define the search path chain along templates. This is in contrast to the way that Java inheritance works: if a Java object inherits from another object, the search for fields and methods always follows the inheritance order. Although the meta-classes underlying the specifications use Java inheritance, we do not automatically use the same inheritance for searching for templates.
7.4.3  Using Rendering Files From Other Objects
There are times when you have a derived class that wants to use the scripts from a base class. It is possible to do this by prepending the base class name to the filename, separated by '/'. For example, the J2EE business method's build.xml uses scripts from the method object. It has Velocity tasks like:
<jwVelocity   template="method/implementationSnippet.vm"
            property="implementationSnippet"
            />
This searches for the implementation snippet using the 'method' as its meta-class name, rather than 'business-object'.
7.4.4  #parse and the 'super' feature
You can use Velocity's 'parse' feature. If you use '#parse("filename")' in a Velocity template, the referenced filename is included and interpreted, as though the text had been written in the original file. In other words, you can use variable substitution, logic and further #parse() calls.

[ You can also use Velocity's #include() feature, but this is less useful because it is a purely textual include: it doesn't do variable substitution. ]

As you would expect, the "filename" value in the #parse is searched for using the search procedures described in this section (i.e. both through template directories and rendering inheritance between meta-classes via template.properties).

#parse() allows you to break a script into parts, promoting modularity and also allowing ease of overriding.

An important facility is the ability to use "super" as the file to parse. With "super", JeeWiz looks from the current file and returns the file that was next in line to be found. In other words, the '#parse("super")' will find the file that would have been found if the current file was not present. This is a JeeWiz-specific extension to Velocity. Note that it means that "super" is a reserved name and shouldn't be used to name template and pattern files.

The 'super' facility is very useful for embellishing existing functionality and can be used in both patterns and templates. A customer-specific extension can add some functionality, then call the super method via '#parse("super")' to continue with the original functionality.

Normally, #parse() requires the target file to be present; a run-time exception results if it is not. However, when "super" is the target file, there does not need to be a 'next in line' file, because JeeWiz, in this special case only, will use a 0-length (empty) file. The effect is to create a no-operation: the #parse() does nothing. This is useful because it means that you can use #parse("super") in an override pattern without wondering whether there is a next-in-line file.

When writing patterns, it is highly advisable to start or end each pattern with #parse("super"), unless there is a good reason not to. When functionality is enhanced by adding further layers of patterns, a pattern without #parse("super") effectively hides any lower layers. When the pattern is first written, this may well be acceptable: the problem only appears later when functionality is added in a lower position in the search order.
7.4.5  Using 'super' in build.xml
You can also use the 'super' feature in the build.xml for a component. When you do this, it refers to the build.xml overridden by the current build.xml.

For example, say you want to override the build.xml for a component, but then also do the original build.xml. In other words, the current build.xml is a strict addition to the existing build.xml.

To use this feature, the overriding build.xml would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<project            name="buildComponent"  default="build" >

  <target           name="build">
    <jwAnt          antfile="super"
                    target="build"
                    required="false"
                    >
    </jwAnt>
    
    <!-- do additional tasks here -->
    
  </target>
  
</project>
The 'jwAnt' task executes the super build job, and then carries on with your additional task.

Note that you can't say 'depends="super"', nice though that would be, because the special meaning for 'super' is implemented by the jwAnt task, whereas 'depends' on a target is done by the normal Ant, which does not know about JeeWiz's overriding feature.

You may want to pay attention to whether the 'super' file should be forced to exist. In the example above, we have show 'super' file not being required - because the 'required' flag on the jwAnt task is set to false. You can also force the super file to exist by setting the 'required' flag on the jwAnt task to true (or leaving it out - it is true by default). In this case, the build will fail if the super file does not exist. If you don't know (or don't care) if there is another build file, set 'required="false".
7.4.6  Using Overriding Files
The implication of the search order is that you can insert your own scripts and properties by defining your own template directory.

In large organisations, you might even want to go further than this, and define a standard set of models for company, project and personal overrides. A full WebLogic stack of models would them be

Personal
Project
Company
WebLogic6
J2EE
BizObject
Object
You create the stack of 'models' (with your own overrides) by pointing to the 'control' directory for the model, and in the model.properties file point to its parent. A very common usage for this approach is to change naming standards. All you need to do this is to define overrides for the naming standards you want to change in component.properties files in the appropriate directories beneath the control directory. It is quite normal for a model to consist of just component.properties.

You can even pick out optional overrides by using an optional build job. This is supported by the jwAnt Ant task. This means an extra part of the build can be done if necessary for the target environment. This is useful in generating EJB Jars and J2EE applications. Containers require application server-specific configuration files for their optional ejb features and the RDBMS mapping, so these things can be put in an override. See the 'containerSpecificBuild.xml' files for the WebLogic and JBoss.

This is an optional use of overrides. There are also required overrides. If a model is marked as 'abstract', it is an indication that the model should not be rendered. Instead, it needs an overriding model to fill in the blanks. This makes sense: the J2EE model must be deployed into a specific application container.  


Copyright (c) 2001-2008 New Technology/enterprise Ltd.