Writing a Module with ProjectBuilder
Overview
This document describes how to create CTL modules using ProjectBuilder. ProjectBuilder is a CTL module containing a set of commands that let you define new command modules.
Central to the definition of modules is the type.xml file. The type.xml file supports a set of XML tags that declare commands, their options and a set of data attributes.
Modules have the following directory layout:
module_name | |--- module.properties // file containing module metadata |--- commands.properties // file containing command metadata |--- type.properties // file containing type metadata |--- type.xml // file containing command definitions | +--- bin // optional binaries, shell scripts, etc. | +--- commands // contains generated commands | +--- lib // optional resource files
Defining new modules is simple and follows a typical edit, build, run cycle as shown in the figure below:

1. Run create type
Run the create-type command accepting the defaults (input what is shown in bold text):
$ ctl -m ProjectBuilder -c create-type Base directory where module source files reside [/tmp/ctier/ctl/src] Target directory where build files are generated [/tmp/ctier/target] Name of supertype: [Managed-Entity] Name of type: poly Description of type: Says hi in different languages Creating module definition files in directory: /tmp/ctier/ctl/src ... Initializing type module from template dir: /tmp/ctier/ctl/depots/default/modules/ProjectBuilder/templates/boilerplate ... Created dir: /tmp/ctier/ctl/src/modules/poly Copying 1 file to /tmp/ctier/ctl/src/modules/poly Creating directory structure... Created dir: /tmp/ctier/ctl/src/modules/poly/bin Created dir: /tmp/ctier/ctl/src/modules/poly/commands Created dir: /tmp/ctier/ctl/src/modules/poly/objects Created dir: /tmp/ctier/ctl/src/modules/poly/templates Copying /tmp/ctier/ctl/depots/default/modules/ProjectBuilder/templates/types/Deployment to /tmp/ctier/ctl/src/modules/poly ... Initializing type module from template dir: /tmp/ctier/ctl/depots/default/modules/ProjectBuilder/templates/types/Deployment ... Copying 2 files to /tmp/ctier/ctl/src/modules/poly Define commands and attributes in this file: /tmp/ctier/ctl/src/modules/poly/type.xml
You might be wondering about the supertype default that you accepted. The Managed-Entity is the CTL base type.
Edit type.xml to define command
As mentioned earlier, the type.xml file is used to define all aspects of the type, including its commands. See the type.xml reference document for information about all its tags. In this continuing example, the type.xml file for the "poly" module is $CTL_BASE/src/modules/poly/type.xml.
Define a command named "slither":
<command name="slither" description="Say something with python."
command-type="BsfCommand" is-static="true">
<script language="jython">print "python says: %s" % project.getProperty('opts.message')</script>
<opts>
<opt parameter="message" description="message to print" required="false"
property="opts.message" type="string" default="ssss"/>
</opts>
</command>
This example command was defined as a BsfCommand command-type. Other command-types are shown later in this document.
2. Run build-type
Run the build-type command to generate the module files for your new command (accept the defaults again):
$ ctl -m ProjectBuilder -c build-type -- -type poly -deploy Base directory where module source files reside [/tmp/ctier/ctl/src] Target directory where build files are generated [/tmp/ctier/target] Building type using the buildmodule.xml via classloader converting type.xml for module: poly generating handlers... packaging module: poly Copying 1 file to /tmp/ctier/ctl/src/modules/poly Moving 1 file to /tmp/ctier/ctl/src/modules/poly Building jar: /tmp/ctier/target/modules/poly-1.jar deploying new build of poly module to local installation ... Extracting /tmp/ctier/target/modules/poly-1.jar to: /tmp/ctier/ctl/depots/default/modules/poly Expanding: /tmp/ctier/target/modules/poly-1.jar into /tmp/ctier/ctl/depots/default/modules/poly
The module was built and deployed and is ready for use.
You should now see it in the CTL listing:
ctl poly: Says hi in different languages commands: [slither] -- snip --
To see more detail about the command add the -m poly arguments:
$ ctl -m poly poly: Says hi in different languages commands: [slither]
3. Run your command
Now run the new command once without -message to show the default being used and once with one specified:
$ ctl -m poly -c slither python says: ssss $ ctl -m poly -c slither -- -message "'some nice mice?'" python says: some nice mice?
You may be curious about the double hyphen (eg, --) in the example. The double hyphen is used to separate the ctl command line arguments from the command-specific ones (in this case "-message").
Defining other command types
This section describes how to define other command types.
System shell script command
System shellscripts are simple to define and are made up of an "executable" and an "argument string" An easy one to define is an "echo" command. Add a new command to your type.xml file
<command name="echo" description="second command."
command-type="Command" is-static="true">
<execution-string>bash</execution-string>
<argument-string>echo bash says ${opts.message}</argument-string>
<opts>
<opt parameter="message" description="option name" required="false"
property="opts.message" type="string" default="hi there!"/>
</opts>
</command>
Ant command
Ant commands are defined using Ant tasks. CTL includes ant-contrib and its own CTL-specific tasks, too.
<command name="emit" description="say something with Ant."
command-type="AntCommand" is-static="true">
<implementation>
<echo message="ant says ${opts.message}"/>
</implementation>
<opts>
<opt parameter="message" description="option name" required="false"
property="opts.message" type="string" default="creepers"/>
</opts>
</command>
If you are an Ant user, take a look at the CTL for Ant pages.
Bean Scripting Framework command
The first example shown here was a BsfCommand type. These commands use the Apache Bean Scripting Framework (BSF) to execute script code of various langauges. Here's another example showing ruby...
<command name="shine" description="ruby BSF command."
command-type="BsfCommand" is-static="true">
<script language="ruby"><![CDATA[
print 'ruby says ', $project.getProperty('opts.message'), "\n"
]]></script>
<opts>
<opt parameter="message" description="option name" required="false"
property="opts.message" type="string" default="dazzle"/>
</opts>
</command>
... and yet another this time in groovy:
<command name="groovy" description="groovy BSF command."
command-type="BsfCommand" is-static="true">
<script language="groovy"><![CDATA[
println "groovy says ${project.properties['opts.message']} \n"
]]></script>
<opts>
<opt parameter="message" description="option name" required="false"
property="opts.message" type="string" default="peace"/>
</opts>
</command>
Properties defined in the type.xml will be replaced before the BSF script is evaluated. In this example, the property opts.message will be replaced by its value.
Programmatic Access to the Framework
CTL has a core object called the Framework that provides an interface to accessing framework resources. This Java object is accessible to BsfCommands.
A named reference is defined that allows you to look it up as shown below:
com.controltier.ctl.common.Framework framework =
project.getReference("com.controltier.ctl.common.Framework.instance")
The Framework provides programmatic access to looking up resources in the framework like project depots, modules, commands, and their properties.
com.controltier.ctl.common.Framework framework =
project.getReference("com.controltier.ctl.common.Framework.instance")
List depots = framework.getDepotResourceMgr().listDepots()
Here's an example using groovy:
<command name="list-depots" description="List all the project depots."
command-type="BsfCommand" is-static="true">
<script language="groovy"><![CDATA[
def framework = project.getReference("com.controltier.ctl.common.Framework.instance")
def depots = framework.getDepotResourceMgr().listDepots()
depots.each { d ->
println "${d.name} \n";
}
]]></script>
</command>
Workflow command
CTL lets you call commands in a sequence in a command called a workflow.
<command name="stammer" description="first workflow command"
command-type="WorkflowCommand" is-static="true"
error-handler-type="FAIL"
>
<workflow threadcount="1">
<command name="slither"/>
<command name="echo"/>
<command name="emit"/>
<command name="shine"/>
<command name="groovy"/>
</workflow>
<opts>
<opt parameter="message" description="option name" required="false"
property="opts.message" type="string" default="duh...?"/>
</opts>
</command>
Run build-type
ctl -m ProjectBuilder -c build-type -- -type poly -deploy
Run the command
ctl -m poly -c stammer -- -message cheers Start: "first workflow command" commands: slither,echo,emit,shine,groovy begin workflow command (1/5) -> "slither -message cheers" ... python says: cheers end workflow command (1/5) -> "slither -message cheers" begin workflow command (2/5) -> "echo -message cheers" ... bash says cheers end workflow command (2/5) -> "echo -message cheers" begin workflow command (3/5) -> "emit -message cheers" ... ant says cheers end workflow command (3/5) -> "emit -message cheers" begin workflow command (4/5) -> "shine -message cheers" ... ruby says cheers end workflow command (4/5) -> "shine -message cheers" begin workflow command (5/5) -> "groovy -message cheers" ... groovy says cheers end workflow command (5/5) -> "groovy -message cheers" [command.timer.default.poly.stammer: 16.130 sec] Workflow completed. execution time: 16.130 sec



