ControlTier Inc. > Open.ControlTier
 
Font size:      

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.

Assorted deployment tools

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.

Note
For users looking for a ready solution for Tomcat deployment see the Elements Java Server Library.

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.

Service life cycle

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.

Service type

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)
Warning
Do not use the Tomcat installation used for running Workbench for this example. The examples in the document will stop Tomcat which will interfere with Workbench operation.

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.

SimpleTomcatService inheritance

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:

  1. Define the type
  2. Define an instance of the type
  3. Test a type command
  4. 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.

Note
In the next two steps, you will replace parts of the type.xml file with content specified in this document. The surest method to get the desired result is to replace the entire needed section. Also, be sure you are not pasting text inside a commented section.

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.

Screenshot

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.

Screenshot

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
Note
Tomcat requires a full JDK to operate. If the startService command fails with an error message like below. Ensure your JAVA_HOME is not set to the JRE directory. Error: The JAVA_HOME/bin/javac directory is missing or not executable. The JAVA_HOME environment variable is not defined correctly. This environment variable is needed to run this program. NB: JAVA_HOME should point to a JDK not a JRE. CTL defines the default JAVA_HOME variable for commands in $CTL_BASE/etc/profile.
Note
If you get a message like "Command failed: Execute failed: java.io.IOException: /home/tutorial/simple/tomcat/bin/startup.sh: cannot execute" make sure that the files in $HOME/simple/tomcat/bin/* are set to be executable (chmod +x $HOME/simple/tomcat/bin/*).

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.

SimpleTomcatService status flow

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.

SimpleTomcatService start flow

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.

SimpleTomcatService stop flow

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.

SimpleTomcatService update flow

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