Wouldn’t it be cool if when your ec2 instance starts it has your java web application automatically deployed? This will be especially suitable for those who want to take advantage of AWS Auto Scaling feature.

It’s fairly simple to achieve this. First thing you need to do is to designate a bucket in S3 to keep your builds – war files. You can upload all of your builds here. Each time a new build is added to the bucket you will also update a file – called latest-version.txt – in the bucket which will contain the latest version. (You don’t have to use S3 for this purpose, you can use whatever repository you are using as long as you can keep latest-version.txt there with your war files)


You will need to create an Amazon Machine Image with the following software installed:
1) JDK 1.6 (or the latest version)
2) Tomcat (latest version)
3) Groovy (latest version)
4) Apache or nginx in case you are going to use one of them in front of your jee server. If not, the jee sever should be configured to run at port 80.

The script needs some libraries that are not necessarily available in the maven repository. Please download the libraries from the websites given below and copy them to /home/yourusername/.groovy/lib. Installing groovy will not create this folder. You will have to manually create the folder.
1) jets3t-0.7.1.jar – For accessing S3 through groovy (or java). Download from here.
2) logback-classic-0.9.9.jar Download from here.
3) jcl-over-slf4j-1.4.2.jar Download it from here.

The version may not be that important – especially for the logging libraries. I mentioned the versions with which I have tested the script.

The following groovy script fetches latest version of your application’s war from the specified S3 bucket, explodes it as ROOT in your tomcat’s webapp directory. You can configure all the directories by searching for REPLACE in the script.

Let’s name this script as Deployer.groovy. Note that it takes three arguments – Your AWS Access Key, Secret Key and the name of the S3 bucket where the war file for your app resides.

#!/usr/bin/groovy
import ch.qos.logback.classic.LoggerContext
import org.slf4j.LoggerFactory
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.Level
import org.jets3t.service.impl.rest.httpclient.RestS3Service
import org.jets3t.service.model.S3Object
import org.jets3t.service.utils.Mimetypes
import org.jets3t.service.security.AWSCredentials
import org.jets3t.service.utils.Mimetypes
import org.jets3t.service.acl.AccessControlList
awsAccessKeyId = args[0]
secretAccessKey =args[1]
s3BucketName = args[2]
versionFilename = "latest-version.txt"
version = getLatestVersion()
// REPLACE the value below with your war file name
warName = "myappname-${version}.war"
// REPLACE the value below your app server deployment directory
destinationFolder="/MyTomcat/webapp"
// REPLACE with the name of your app's exploded folder
appDir = "ROOT"
setLogLevel(Level.INFO)
stopTomcat()
downloadAndUnzipWarFile()
startTomcat()
def downloadAndUnzipWarFile() {
    // delete ROOT folder from tomcat webapps
    runCommand("rm -rf ${destinationFolder}/${appDir}", 5000)
    // download war from S3 bucket
    def s3Service = new RestS3Service(new AWSCredentials(awsAccessKeyId, secretAccessKey))
    def s3Bucket = s3Service.getBucket(s3BucketName)
    def s3Object = s3Service.getObject(s3Bucket, warName)
    println "Downloading ${warName}..."
    InputStream input = s3Object.getDataInputStream().getWrappedInputStream()
    OutputStream output = new FileOutputStream(new File(destinationFolder + "/" + warName))
    byte[] buf = new byte[3072]
    int len
    while ((len = input.read(buf)) > 0) {
        output.write(buf, 0, len);
    }
    input.close();
    output.close();
    s3Object.closeDataInputStream()
    println "unzipping ${destinationFolder}/${warName} ..."
    runCommand("mkdir ${destinationFolder}/${appDir}", 30000)
    runCommand("unzip ${destinationFolder}/${warName} -d ${destinationFolder}/ROOT", 30000)
    println "Deleting the war file..."
    runCommand("rm ${destinationFolder}/${warName}", 5000)
}
def startTomcat() {
    "/etc/init.d/tomcat6 start".execute()
}
def stopTomcat() {
    println "stopping tomcat..."
    def proc = runCommand("/etc/init.d/tomcat6 stop", 35000)
    println "checking if the tomcat process is killed or not.."
    proc = "cat /var/run/tomcat6.pid".execute()
    def out = new StringBuffer()
    def err = new StringBuffer()
    proc.consumeProcessOutput(out, err)
    proc.waitForOrKill(15000)
    if (out.toString().length() > 0) {
        println "killing tomcat with pid ${out.toString()}"
        "kill ${out.toString()}".execute()
    } else {
        println "Tomcat process shutdown complete."
    }
}
def getLatestVersion() {
    def s3Service = new RestS3Service(new AWSCredentials(awsAccessKeyId, secretAccessKey))
    def s3Bucket = s3Service.getBucket(s3BucketName)
    def s3Object = s3Service.getObject(s3Bucket, versionFilename)
    BufferedReader reader = new BufferedReader(new InputStreamReader(s3Object.getDataInputStream().getWrappedInputStream()))
    String value = reader.readLine();
    reader.close()
    s3Object.closeDataInputStream()
    return value
}
def runCommand(command, waitime) {
    def proc = command.execute()
    proc.waitForOrKill(waitime)
    return proc
}
def printResponse(proc) {
    def out = new StringBuffer()
    def err = new StringBuffer()
    proc.consumeProcessOutput(out, err)
    println "our: $out, err:$err"
}
def setLogLevel(level) {
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory()
    def logger = loggerContext.getLogger("org.apache")
    logger.setLevel(level)
    logger = loggerContext.getLogger("httpclient")
    logger.setLevel(level)
    logger = loggerContext.getLogger("org.jets3t")
    logger.setLevel(level)
}

Now we need to make this script execute when the instance boots. In order to do that , let’s create a small shell script that is LSB compliant (Assuming that you are using Ubuntu)

#!/bin/sh
#
# /etc/init.d/deploy-my-webapp -- startup script for the deployment of the visitor market
#
# Written by Vaibhav Puranik
#
### BEGIN INIT INFO
# Provides:          deploy-my-webapp
# Required-Start:    $ALL
# Required-Stop:     $ALL
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Deploy Script.
# Description:       Start the deployment of my webapp.
### END INIT INFO
# Runs the deployer script.
# REPLACE these values
JAVA_HOME=<MY_JAVA_HOME>
GROOVY_HOME=<MY_GROOVY_HOME>
AWSACCESSKEY=<YOUR_ACCESS_KEY>
AWSSECRET=<YOUR_AWS_SECRET>
S3RELEASEBUCKET=<MY_S3_BUCKET_NAME>
case "$1" in
    start)
        echo "Starting My Webapp Deployment... "
        # REPLACE PATH with the actual path of Deploy.groovy on your image
        PATH/Deploy.groovy $AWSACCESSKEY $AWSSECRET $S3RELEASEBUCKET >> /var/log/myapp/deploy.log 2>&1 &
        ;;
    stop)
        echo "not yet implemented"
        ;;
    restart|force-reload)
        $0 stop
        $0 start
        ;;
    status)
        echo "not yet implemented"
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

Copy the above script in your /etc/init.d directory. Let’s name is deploy-my-app. Make it executable by executing

chmod +x /etc/init.d/deploy-my-app

Now let’s install it as an init script

update-rc.d deploy-my-app defaults 95

This command will create the necessary symlinks int rc.d folders.

That’s it. Bundle the image, register it and create an ec2 instance of it. When it boots up, you should have your webapp deployed! If something goes wrong you can look at the following log: /var/log/myapp/deploy.log

Share and Enjoy:
  • Sphinn
  • Twitter
  • Digg
  • Reddit
  • del.icio.us
  • Facebook
  • LinkedIn
  • StumbleUpon