Managing deployment
Overview
Most server-based applications are really an aggregation of various interacting components spread over multiple machines. Even if you are managing the deployment of a single component, it is likely that you must maintain deployments of it in several environments. Each environment may require adjusting the configuration of the component to suit its context. In increasingly common cases, you maintain deployments for more than one component, each component running within its own container or execution environment.

This document walks you through an example that shows you how to introduce a Tomcat-based deployment to the ControlTier framework. Once added, Tomcat deployments can be coordinated via the ControlTier framework.
This document assumes you have familiarized yourself with basic concepts and have setup ProjectBuilder as well as finished the interfacing a build.
Introducing Services
Before we get to the implementation, let's first introduce a key framework base type, Service. A Service is an object that interfaces with an underlying application platform to coordinate both the deployment of release artifacts, as well as, the runtime state of a long running process. A Service provides a consistent interface to the rest of the framework, and hides the variation of server platform functionality and control. A Service defines a set of standard commands that are used to define the life cycle the framework expects, and also defines a set of attributes needed to parameterize the calls to the underlying tools.
The following illustration shows a user calling the startService command on SimpleTomcatService, which in turn, invokes the command startup.sh. SimpleTomcatService is a Service, and as such, possesses the command, startService, one of several standard commands available in any Service.
As a subtype of Service, SimpleTomcatService can override generic Service commands. To be a special Service interfacing Tomcat, SimpleTomcatService overrides the startService command to use its own implementation.
As mentioned earlier, the role of a Service is to coordinate the deployment life cycle for application components. All these actions are driven from a standard command workflow, Update which in turn executes a sequence of other standard commands: Stop, Packages-Install, Configure and Start.

Services also have a relationship to another framework base type, Package. A Package represents any kind of build artifact that will be used during deployment. For each Package dependency, the Service invokes the Package's install command. Services, in effect, are consumers of Packages.

Preparing the Tomcat installation
Before we can begin managing Tomcat, an installation of it must first be obtained and prepared.
Obtain the software
Go to tomcat.apache.org and download either 4.x or 5.x. For the purposes of this document, either version will work fine.
Install
Extract the Tomcat distribution archive into the directory, $HOME/simple/tomcat. Ensure that the files in $HOME/simple/tomcat/bin are all set executable. Configure the new Tomcat installation to listen on unused ports (e.g, the connector and server ports). Run the command: netstat -an|grep LISTEN to find out what ports are in use.
Assuming you are trying this on a machine which already has the default Tomcat configuration running (perhaps for Workbench) you can increment each port number by 1. The following is an excerpt from the server.xml file with changes in bold:
<Server port="8006" shutdown="SHUTDOWN" debug="0"> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8081" minProcessors="5" maxProcessors="75" enableLookups="true" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" useURIValidationHack="false" disableUploadTimeout="true" /> <Connector className="org.apache.coyote.tomcat4.CoyoteConnector" port="8010" minProcessors="5" maxProcessors="75" enableLookups="true" redirectPort="8443" acceptCount="10" debug="0" connectionTimeout="0" useURIValidationHack="false" protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/>
Noteworthy settings
It is important to point out some key settings we will use when defining management commands.
| Setting | Example | Description |
|---|---|---|
| Tomcat install root (CATALINA_HOME) | $HOME/simple/tomcat | Path to the tomcat installation. (used by startService, stopService) |
| Tomcat basedir (CATALINA_BASE) | $HOME/simple/tomcat | Path to the tomcat base dir where webapps reside (used by Packages-Install) |
| http port | 8081 | TCP port (used by assertServiceIsUp and assertServiceIsDown) |
A Service for Jakarta Tomcat
With a bit of familiarity with Services, we are ready to proceed with actually defining a simple Service. This section will show you how to make "SimpleTomcatService", a Service subtype that will interface with Tomcat.
The class diagram below shows the inheritance hierarchy of the SimpleTomcatService type. It inherits a number of commands from its super types but will override several of them.

The commands in italics are hook commands that must be overidden by subtypes while commands in regular text are functional by any object of that type or subtype.
There is a typical type development cycle used when introducing a new type. The process is broken down into several routine steps:
- Define the type
- Define an instance of the type
- Test a type command
- Repeat
Two tools will be used during the process. ProjectBuilder to create model artifacts and a text editor to edit type.xml, the type definition file.
Define the SimpleTomcatService Type
Create the type
Use ProjectBuilder's create-type command to initialize the SimpleAntBuilder type. You can run create-type without a full set of arguments and it will prompt you for the remaining needed input (input is in bold):
ctl -p default -t ProjectBuilder -o tutorial -c create-type -- -supertype Service
Name of type:
SimpleTomcatService
Description of type:
A simple Service to interface Tomcat
Creating module definition files in directory: /home/tutorial/simple/src ...
Initializing type module from template dir:
/home/tutorial/ctl/depots/default/modules/ProjectBuilder/templates/boilerplate ...
Created dir: /home/tutorial/simple/src/modules/SimpleTomcatService
Copying 1 file to /home/tutorial/simple/src/modules/SimpleTomcatService
Creating directory structure...
Created dir: /home/tutorial/simple/src/modules/SimpleTomcatService/bin
Created dir: /home/tutorial/simple/src/modules/SimpleTomcatService/commands
Created dir: /home/tutorial/simple/src/modules/SimpleTomcatService/objects
Created dir: /home/tutorial/simple/src/modules/SimpleTomcatService/templates
Copying /home/tutorial/ctl/depots/default/modules/ProjectBuilder/templates/types/Managed-Entity
to /home/tutorial/simple/src/modules/SimpleTomcatService ...
Initializing type module from template dir:
/home/tutorial/ctl/depots/default/modules/ProjectBuilder/templates/types/Managed-Entity ...
Copying 2 files to /home/tutorial/simple/src/modules/SimpleTomcatService
Define commands and attributes in this file: /home/tutorial/simple/src/modules/SimpleTomcatService/type.xml
A new directory called "SimpleTomcatService" will be created in the basedir $HOME/simple/src/modules and populated with various files and subdirectories. The last line refers you to the type.xml file that contains the initial type definition for SimpleTomcatService. A snippet is shown below:
<type
name="SimpleTomcatService"
role="concrete"
uniqueInstances="true">
<!-- description of the Type -->
<description>A simple Service to interface Tomcat</description>
<!-- supertype/typereference: defines the supertype of the current Type -->
<supertype>
<typereference name="Service"/>
</supertype>
...
Open the $HOME/simple/src/modules/SimpleTomcatService/type.xml file in your favorite text editor.
Define attribute defaults
Attribute defaults are used to provide default values used to parameterize commands. These appear as entity.attribute.<attribute-name> properties and these properties are often used as default command option values. Cut and paste the following fragment into the type.xml replacing the <attributes> ... </attributes> content already there. Ensure that you have made the content a sub-element of <type>.
<attributes> <attribute name="basedir" type-property="deployment-basedir"/> <attribute name="install-root" type-property="deployment-install-root"/> <attribute-default name="catalinaHttpPort" value="8081"/> </attributes>
Add an allowed dependency type for war Packages
Dependency-constraints define which other types of objects can be child or parents of instances of SimpleTomcatService. Here we modify the constraints section to add the war type as an allowed child dependency. This will mean that we can then assign war packages as child dependencies of the service so they are installed during the Deploy sequence, and also that the Updater introduced in the next tutorial section (build and deployment) will be able to automatically reconfigure the SimpleTomcatService's package dependencies.
<constraints>
<dependency-constraint enforced="false" kind="child">
<allowedtypes>
<typereference name="FilePath"/>
<typereference name="war"/>
</allowedtypes>
</dependency-constraint>
<dependency-constraint enforced="false" kind="parent">
<allowedtypes>
<typereference name="Deployment"/>
<typereference name="Node"/>
</allowedtypes>
</dependency-constraint>
</constraints>
Define the Service commands
The initial type.xml produced by create-type has a number of comments that show command definition examples. To define a command, you use the <command> tag. Cut and paste the following fragment into the type.xml replacing the <commands>... </commands> content already there. Ensure that you have made the content a sub-element of <type>.
<commands>
<command name="assertServiceIsDown"
description="checks if process is down"
command-type="AntCommand"
>
<implementation>
<fail message="Service is not DOWN (listening on port=${opts.port})">
<condition>
<socket port="${opts.port}" server="localhost"/>
</condition>
</fail>
<echo>The service is DOWN</echo>
</implementation>
<opts>
<opt parameter="port" description="http listen port"
required="false" property="opts.port" type="string"
defaultproperty="entity.attribute.catalinaHttpPort"/>
</opts>
</command>
<command name="assertServiceIsUp"
description="checks if process is running"
command-type="AntCommand"
>
<implementation>
<fail message="Service is not UP (not listening on port=${opts.port})">
<condition>
<not><socket port="${opts.port}" server="localhost"/></not>
</condition>
</fail>
<echo>The service is UP</echo>
</implementation>
<opts>
<opt parameter="port" description="http listen port"
required="false" property="opts.port" type="string"
defaultproperty="entity.attribute.catalinaHttpPort"/>
</opts>
</command>
<command name="startService"
description="start the service process"
command-type="AntCommand"
daemonized="false"
>
<implementation>
<exec osfamily="unix" executable="${opts.catalinaHome}/bin/startup.sh">
<env key="CATALINA_HOME" value="${opts.catalinaHome}"/>
<env key="CATALINA_BASE" value="${opts.catalinaBase}"/>
</exec>
<exec osfamily="windows" executable="${opts.catalinaHome}/bin/startup.bat">
<env key="CATALINA_HOME" value="${opts.catalinaHome}"/>
<env key="CATALINA_BASE" value="${opts.catalinaBase}"/>
</exec>
</implementation>
<opts>
<opt parameter="catalinaHome" description="catalina home"
required="false" property="opts.catalinaHome" type="string"
defaultproperty="entity.attribute.install-root" />
<opt parameter="catalinaBase" description="catalina base"
required="false" property="opts.catalinaBase" type="string"
defaultproperty="entity.attribute.basedir" />
</opts>
</command>
<command name="stopService"
description="stops the service process"
command-type="AntCommand"
daemonized="false"
>
<implementation>
<exec osfamily="unix" executable="${opts.catalinaHome}/bin/shutdown.sh">
<env key="CATALINA_HOME" value="${opts.catalinaHome}"/>
<env key="CATALINA_BASE" value="${opts.catalinaBase}"/>
</exec>
<exec osfamily="windows" executable="${opts.catalinaHome}/bin/shutdown.bat">
<env key="CATALINA_HOME" value="${opts.catalinaHome}"/>
<env key="CATALINA_BASE" value="${opts.catalinaBase}"/>
</exec>
</implementation>
<opts>
<opt parameter="catalinaHome" description="catalina home"
required="false" property="opts.catalinaHome" type="string"
defaultproperty="entity.attribute.install-root" />
<opt parameter="catalinaBase" description="catalina base"
required="false" property="opts.catalinaBase" type="string"
defaultproperty="entity.attribute.basedir" />
</opts>
</command>
</commands>
The XML above defines four commands in the SimpleTomcatService type. The startService and stopService commands are defined as simple wrappers around the Tomcat startup and shutdown scripts.
The assertServiceIsUp and assertServiceIsDown implementations are both based on the same test, checking to see that the Tomcat server is listening on its port. Since these commands will run locally to the Tomcat deployments we can get away with hardcoding the server attribute to localhost.
The example script code shown above uses Ant property references of the form, opts.<param-name>. The CTL task, get-opts, parses the command line and sets these properties. These properties can then be referenced by the command.
Build the type
Use the ProjectBuilder's build-type command to build the SimpleTomcatService type. Specify the optional arguments -upload and -deploy to also upload the model artifact to Workbench, and then deploy it back to your working context.
ctl -p default -t ProjectBuilder -o tutorial -c build-type -- -type SimpleTomcatService -upload -deploy
Created dir: /home/tutorial/simple/target/modules
Building type using the buildmodule.xml via classloader
converting type.xml for module: SimpleTomcatService
generating handlers...
packaging module: SimpleTomcatService
Copying 1 file to /home/tutorial/simple/src/modules/SimpleTomcatService
Copying 1 file to /home/tutorial/simple/src/modules/SimpleTomcatService
Deleting: /home/tutorial/simple/src/modules/SimpleTomcatService/module.properties.temp
Building jar: /home/tutorial/simple/target/modules/SimpleTomcatService-1.jar
Uploading built module to server ...
processing files in directory: /home/tutorial/simple/target/modules
scanning for files matching pattern: (SimpleTomcatService)-([0-9]+)\.jar
Uploading jar: /home/tutorial/simple/target/modules/SimpleTomcatService-1.jar to server: 'localhost' ...
Installing new build of "SimpleTomcatService" module from server ...
Getting: http://localhost:8080/jackrabbit/repository/workbench/default/publish/modules/SimpleTomcatService-head.jar
To: /ctier/ctl/var/tmp/downloads/default/SimpleTomcatService-head.jar
Created dir: /ctier/ctl/depots/default/modules/SimpleTomcatService
Expanding: /ctier/ctl/var/tmp/downloads/default/SimpleTomcatService-head.jar
into /ctier/ctl/depots/default/modules/SimpleTomcatService
The SimpleTomcatService type has now been loaded into the server and is now part of the greater project model. If you like, go to Workbench and view the type there. If you click on the "Commands" tab, you should see the four command defined.

Register a SimpleTomcatService Object
With the SimpleTomcatService type defined, all that is left before we can test it, is to define an instance of it. To do that use the Register command.
ctl -p default -t SimpleTomcatService -o simple -c Register -- \
-basedir '${user.home}/simple/tomcat' -installroot '${user.home}/simple/tomcat' -install
The Register command takes the input from the command line and registers an instance of SimpleTomcatService in the project model. The -install option will also deploy it to CTL project. After registration you can check its detail via the Get-Properties command:
$ ctl -p default -t SimpleTomcatService -o simple -c Get-Properties -- -print [MULTI_LINE] # simple [SimpleTomcatService] # A simple Service to interface with Tomcat ## Attributes ## * basedir: "/home/tutorial/simple/tomcat" * catalinaHttpPort: "8081" * install-root: "/home/tutorial/simple/tomcat" ## Dependencies ## ### Parent Dependencies ### 1. localhost [Node] ### Child Dependencies ### - - - [/MULTI_LINE]
You can also view the object in Workbench. Navigate back to the SimpleTomcatService type and you will see the new object in the "Objects" tab.

The SimpleTomcatService object is now ready for use.
Test commands
The SimpleTomcatService type has been defined and an instance of it has been created and deployed to your working CTL project. You are ready to try the commands. Try assertServiceIsDown command first:
ctl -p default -t SimpleTomcatService -o simple -c assertServiceIsDown The service is DOWN
Assuming you haven't started Tomcat after installation, this command should have completed successfully, printing the "Service is DOWN" message. Since the assertServiceIsDown command executed cleanly, it's complement command, assertServiceIsUp command should fail:
ctl -p default -t SimpleTomcatService -o simple -c assertServiceIsUp Command failed: Service is not UP (not listening on port=8081)
Now that we are confident the Tomcat service is not running, go ahead and try the startService command:
ctl -p default -t SimpleTomcatService -o simple -c startService Using CATALINA_BASE: /Users/ctier/simple/tomcat Using CATALINA_HOME: /Users/ctier/simple/tomcat Using CATALINA_TMPDIR: /Users/ctier/simple/tomcat/temp Using CATALINA_OUT: /Users/ctier/simple/tomcat/logs/catalina.out Using JAVA_HOME: /usr
As defined, the command should have successfully invoked $CATALINA_HOME/bin/startup.sh and Tomcat is now listening on 8081 (eg http://localhost:8081). Test the assertServiceIsUp command once more:
ctl -p default -t SimpleTomcatService -o simple -c assertServiceIsUp The service is UP
Lastly, test the stopService command:
ctl -p default -t SimpleTomcatService -o simple -c stopService
This time, $CATALINA_HOME/bin/shutdown.sh should have successfully completed.
ctl -p default -t SimpleTomcatService -o simple -c assertServiceIsDown The service is DOWN
The Service life cycle worklow commands
The previous section tested the four hook commands declared by the Service type. These four commands must always be overridden anytime a new kind of long running process must be managed during deployment. These commands are called by the Service type's life cycle workflow commands: Status, Start and Stop.
In each of these life cycle workflows, a sequence of commands are invoked and if an error occurs, an error handler carries out the configured action. The logic of each of these workflows are discussed below:
The Status workflow calls the assertServiceIsUp command and if the command exits successfully, Status exits successfully. If assertServiceIsUp fails, the Status workflow's error handler will execute a fail.

The Start workflow calls the assertServiceIsUp command and if the command exits successfully, Start exits successfully. If assertServiceIsUp fails, the Start workflow's error handler will execute the startService command.

The Stop workflow calls the assertServiceIsDown command and if the command exits successfully, Stop exits successfully. If assertServiceIsDown fails, the Stop workflow's error handler will execute the stopService command.

Both the Start and Stop workflow commands exhibit similar logic. They each run a command that try to assert a condition, and if the condition is met, the workflow completes.
These three workflows also participate in the Deploy workflow command responsible for the deployment life cycle.

Run the Deploy command to exercise the whole deployment life cycle.
ctl -p default -t SimpleTomcatService -o simple -c Deploy begin workflow command (1/4) -> "Stop " ... begin workflow command (1/1) -> "assertServiceIsDown " ... The service is DOWN end workflow command (1/1) -> "assertServiceIsDown " end workflow command (1/4) -> "Stop " begin workflow command (2/4) -> "Packages-Install " ... Dispatching command 'assertPackageIsVerified' to objects: ... end workflow command (2/4) -> "Packages-Install " begin workflow command (3/4) -> "Configure " ... begin workflow command (1/1) -> "Docs-Generate " ... end workflow command (1/1) -> "Docs-Generate " end workflow command (3/4) -> "Configure " begin workflow command (4/4) -> "Start " ... begin workflow command (1/1) -> "assertServiceIsUp " ... Running handler command: startService end workflow command (1/1) -> "assertServiceIsUp " end workflow command (4/4) -> "Start "
Next step: Coordination
Coordinating build and deployment


