Version 3

    Writing Deployment Templates for the DeploymentService

     

    To create a new deployment template it is necessary to create a new

    subdirectory under the directory where templates are stored

    (./conf/templates by default), then create a template-config.xml

    file into that subdirectory. The deployment template takes its name from the

    subdirectory name, so ./conf/templates/jms-queue/template-config.xml

    yields the name: jms-queue. template-config.xml must be according

    to the XML Schema defined at

    jboss-template-config.xsd.

     

    Generated modules go to the UndeployDir and there are 2 possible

    module outcomes from "applying" a deployment configuration:

     

    1. Generate a deployment that consists of a single descriptor, for example,

      my-mbean-service.xml

    1. Generate a deployment that consists potentially of many descriptors and/or

      other files, e.g. jars. In this case the generated deployment corresponds

      to an unpacked directory module, for example, my-module.sar/,

      my-webapp.war/, etc.

       

    The "Hello World" of deployment templates

     

    The simplest template configuration is something like the following:

    <template-config template="vm/some-descriptor.vm" extension="-service.xml"></template-config>
    

    Remember that when a client calls createModule() on DeploymentService,

    it will pass as parameters a module name, a template name, and

    a HashMap of key/value pairs. For example:

       createModule("my-queue", "jms-queue", properties);
    

    The template name picks up a template configuration (e.g. jms-queue).

    The module name suggest a proposed name (e.g. my-queue) for the generated

    descriptor and the HashMap contains properties that are specific to a

    template configuration. 

     

    In the above example createModule() will cause the rendering of the velocity

    template ./conf/templates/jms-queue/vm/some-descriptor.vm. All parameters

    from the HashMap will be copied over to the VelocityContext

    (which is another HashMap more or less) and they will be accessible

    inside the velocity template using the $ (or simply $key) syntax. For example, assume that a required QueueName and an optional DestinationManager are needed by this particular template that generates an MBean descriptor for a simple JMS Queue:

    <server>
      <mbean code="org.jboss.mq.server.jmx.Queue"
          name="jboss.mq.destination:service=Queue,name=$QueueName">
    #if($DestinationManager)         
        <depends optional-attribute-name="DestinationManager">$DestinationManager</depends>
    #end    
      </mbean>
    </server>
    
    Notice the usage of the velocity if/end directive that can be use for conditional inclusion of a template part. For a detailed description of the velocity features read the velocity reference guide. The rendered template will be output to file my-queue-service.xml which is the resulting module name after applying the "-service.xml" extension. If the extension is in the module name already, it won't be applied again. Also, the extension is an optional attribute of the template-config.xml descriptor, but is a useful one to make sure that the resulting descriptor will be deployable.  

    Specifying Properties

    The term property here is interpreted as any Serializable java class. In the above example any property inside the HashMap of createModule() will be passed over to the velocity template where it will be accesible though its key. In the common case, all properties will be simple java.lang.String instances, and infact, when the property value is accessed in the template using the $ syntax, the value of key will be substituted by implicitly calling

    toString() on the instance. But it is possible to use any java type

    (e.g. a JavaBean) in order to pass more complex values and still be able

    to access them in a convenient way (e.g. $mybean.getPerson().getName()),

    while having some basic validation (e.g. a java.lang.Boolean can only

    have a valid true/false value).

     

     

    In addition to the above, we can enumerate the properties in the

    template-config.xml descriptor. This allows the DeploymentService

    to:

    1. check if the number and type of the values passed at runtime are correct

    2. apply default values if necessary

    3. provide metadata to generic tools that want to discover the properties

      supported by a particular deployment template at runtime and present to

      the user service creation forms, and the like.

    So the template.config.xml could be enhanced as follows:

    <template-config template="vm/jms-queue.xml.vm" extension="-service.xml">
    
      <property-list>
        <!-- if type not specified, java.lang.String is assumed -->
        <!-- a value must be supplied at runtime, or an exception will be thrown -->
        <property name="QueueName"></property>
        
        <!-- if a value is not supplied at runtime, the default will be used -->
        <property name="DestinationManager" type="javax.management.ObjectName">
          <description>
          The ObjectName of the DestinationManager factory to be used
          <default-value>jboss.mq:service=DestinationManager</default-value>
        </property>
      
    </template-config>
    

    A property element support the following attributes:

    • name - the name of the property (required)

    • type - the java type of the property, if not specified java.lang.String is assumed.

      Arrays are allowed, as well (e.g.

    [Ljava.lang.String;

    ).

    • optional - set it to true to indicate it is ok for the property to be missing.

      It is false by default so if the property is absent an exception will

      be thrown by the DeploymentService.

    And the following sub-elements:

    • description - An optional free-text description to accompany the property.

    • default-value - If the property is required, this value will be substituted

      whenever the property is missing. If the property is marked as optional

      the default-value will not be used. A PropertyEditor for the chosen

      type must exist, for this to work.

     

    Creating complex deployments

     

    A complex deployment is an unpacked directory structure containing virtually

    anything. On top of this structure one or more velocity templates can be

    rendered.

     

    You choose the complex deployment mode by omitting the template attribute

    from the template-config element, and using instead the template-list

    sub-element. For example:

    <template-config copydir="files">
      <template-list>
        <template input="vm/jboss-service.xml.vm"
                  output="console-mgr.sar/META-INF/jboss-service.xml"></template>
        <template input="vm/jboss-web.xml.vm"
                  output="web-console.war/WEB-INF/jboss-web.xml"></template>
      </template-list>
    </template-config>
    

    This example can be used to produce the ./deploy/management deployment that

    includes the web-console.war and the console-mgr.sar. The copydir attribute

    indicates that the file structure under ./conf/templates/template-name/files/

    will be deep copied verbatim to the the output module (e.g. ./undeploy/module-x),

    then the two specified velocity templates (specified with the input attribute)

    will be rendered using the same HashMap and written to the files specified

    by the corresponding output attributes. Simple?

     

    Handling errors

     

    The velocity templates can perform basic property validation (e.g. making

    sure an integer is within certain limits, or a string follows a certain format).

    For example:

       ...
    #if($PoolSize <= 0 || $PoolSize > 10)
    #set($template-error="Pool size must be between 1 and 10")
    #else
       <attribute name="PoolSize">$PoolSize</attribute>
    #end
    

    By assigning a non-null value to the variable template-error we

    tell the DeploymentService to throw an Exception back to the caller

    using the variable text as the exception message. In addition,

    any intermediate result from the generated module will be removed.

     

    The VelocityEngine withing DeploymentService has been configured

    to output log to the jboss logger under the category name

    org.jboss.services.deployment.DeploymentService.VelocityEngine,

    so it maybe possible to derive useful information when debugging

    a template.

     

    Velocity, macros and more.

     

    Velocity is a very simple scripting language and the reason it was

    chosen for the DeploymentService is because a template look very

    much like the end-result, so a jboss deployment descriptor template

    looks a lot like a jboss deployment descriptor.

     

    In addition, since the template is basically free text it can

    be used to generate deployment descriptors of all kinds

    (e.g. jboss-service.xml, web.xml, ejb-jar.xml, etc.)

    or even property files, or test data.

     

    On the other hand you can access the objects included in the velocity

    context like you would in Java, so assuming you have included

    a java bean, you can call methods on the instance:

    $mybean.getAddress().getZipCode().

     

    Another useful primitive that you may consider using is foreach.

    For example, having defined the following property as an array

    of ObjectName instances:

       <property name="DependsList" type="[Ljavax.management.ObjectName;" optional=true"></property>
    

    we could write in the template:

    #if($DependsList)
    #foreach($objectName in $DependsList)
       <depends>$objectName</depends>
    #end
    #end
    

    To avoid repeating velocity constructs and to clean-up the template

    you can create velocity macros that are shared by all templates.

    Those are stored at ./conf/template/VM_global_library.vm. An

    example macro is shown below:

    ## --------------------------------------------------------
    ## ifDefReplace
    ## 
    ## If $substr is contained in $string then produce $replace
    ##
    ## $string and $substr must be Strings
    ## --------------------------------------------------------
    #macro(ifDefReplace $string $substr $replace)
    #if($string.indexOf($substr) >= 0)
    $replace#end
    #end
    

    Using the macro is then easy:

    #ifDefReplace($role "read" 'read="true"')
    

     

    Comments/Suggestions

     

    Please, post your comments on the

    Deployers on JBoss

    forum.

     

    Referenced by: