Tuesday, November 12, 2013

Deploying Java Web Applications in Separate Jetty Instances

I recently have a requirement to deploy GitBlit and Nexus in separate Jetty Application Server instances. It sounds easy right? I could place a package of Jetty on our Linux box and just duplicate so I could have two Jetty instances. But, it was not so straight forward. There were a lot of stuff need to customize, like separate PID of each instance, separate web application directories, etc. I had to read the shell script to start Jetty and find out how to arrive at the set-up required for me to do.

Here's to save you time to research and do experimentation. I documented what to do and how to do things in deploying two web applications (or more than) in separate Jetty instances.

Requirements

Download Jetty package at http://download.eclipse.org/jetty/. Try to download the latest stable version. It's currently 9.0.6 when I wrote this post.

Prepare your Web Archive (WAR) files. For this post, I deploy GitBlit and Nexus WAR files.

Note: I deployed these WARs at a Linux box so if you are using other OS like Windows, you might need to do some stuff differently.

Procedure

Extract Jetty Package

In this procedure, you don't need to duplicate the Jetty package. You just need one package and I recommend the following directory structure for Linux systems.

drwxr-xr-x  3 root     root     4096 Nov 11 23:46 ./
drwxr-xr-x  3 root     root     4096 Nov 12 06:56 ../
lrwxrwxrwx  1 root     root        6 Nov 11 23:46 default -> latest/
drwxrwxr-x 14 rmaranan rmaranan 4096 Nov 13 07:32 jetty-distribution-9.0.6.v20130930/
lrwxrwxrwx  1 root     root       34 Nov 11 23:45 latest -> jetty-distribution-9.0.6.v20130930/

I based this install folder structure from the Java installation in Fedora. Basically in my work, I created two soft links latest and default. The latest links to the latest Jetty package and the default links to the latest soft link. With this set-up, I could easily switch between different Jetty packages in the future.

For those who are not familiar yet, here's how I extracted my Jetty package. Assuming you have root access to your Linux box.

cp jetty-distribution-9.0.7.v20130930.tar.gz /opt

cd /opt

tar xvf jetty-distribution-9.0.7.v20130930.tar.gz

ln -s jetty-distribution-9.0.7.v20130930 latest

ln -s latest default

Set-up Web Application Directories

I know advance Jetty users and Java programmers have already figured this out. The secret for having one package for two Jetty instances is having two separate web application directories. So here, we create directories gitblit-apps and nexus-apps for Gitblit and Nexus applications respectively.

cd /opt/jetty/default
mkdir gitblit-apps
mkdir nexus-apps

Then, we copy the WAR files for each respective directories.

cp ~/war-files/gitblit.war gitblit-apps
cp ~/war-files/nexus.war nexus-apps

Finally, we open to modify the Jetty deployment configuration. This is to make the web application directory configurable.

nano etc/jetty-deploy.xml

Inside jetty-deploy.xml, look for the webappprovider Jetty call. And, add the highlighted text below.

<Call id="webappprovider" name="addAppProvider">
    <Arg>
        <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
            <Set name="monitoredDirName">
                <Property name="jetty.home" default="." />
                <Property name="jetty.sudocode.webapps" default="/webapps" />
            </Set>
            <Set name="defaultsDescriptor">
                <Property name="jetty.home" default="."/>/etc/webdefault.xml
            </Set>
            <Set name="scanInterval">1</Set>
            <Set name="extractWars">true</Set>
            <Set name="configurationManager">
                <New class="org.eclipse.jetty.deploy.PropertiesConfigurationManager">
                <!-- file of context configuration properties
                    <Set name="file"><SystemProperty name="jetty.home"/>/etc/some.properties</Set>
                  -->
                  <!-- set a context configuration property
                  <Call name="put"><Arg>name</Arg><Arg>value</Arg></Call>
                  -->
                </New>
            </Set>
        </New>
    </Arg>
</Call>

The property jetty.sudocode.webapps will hold the web application directory path when we run each instances of Jetty for Gitblit and Nexus.

Create Customized Start-up Script

To make it more organized, We put the start-up scripts at the /etc/jetty/default/bin. And, we create sub-directories for Gitblit and Nexus start-up scripts.

mkdir bin/gitblit
mkdir bin/nexus

Then on each sub-directory we create separate start-up scripts but these scripts should contain same code except for the values of the parameters (which holds our Jetty run-time properties).

nano bin/gitblit/gitblit-starter

Notice that I have been using nano. If you are not so familiar with Linux, you could actually use any text editor which you are comfortable.

Inside the gitblit-starter script file, we write the following. Notice all the configurable parameters are at the top of the script.

#!/bin/sh

JETTY_BASE_DIR=/opt/jetty/default                                               # Jetty Installation folder
JAVA_EXEC=java                                                                  # java executable
JETTY_PORT=18080                                                                # HTTP Port
JETTY_HTTPS_PORT=18081                                                          # HTTPS Port
JETTY_STOP_PORT=18082                                                           # STOP Port
JETTY_STOP_KEY=ilovejetty                                                       # STOP Key
JETTY_SUDOCODE_WEBAPPS=/gitblit-apps                                            # Location of WAR under ${jetty.home}
JETTY_START_JAR=start.jar                                                       # Location of start.jar
JETTY_HOST=localhost                                                            # Jetty Host
JETTY_JVM_OPTS="-Xms192m -Xmx256m -XX:MaxPermSize=64m"                          # JVM Options

####################################################################################################################
# Don't touch the rest as much as possible
# This file must reside at ${jetty.home}
# Romeo H. Maranan Jr.
# Sudocode Systems Solutions, Inc.
JAVA_EXEC_PARAMS=""
JETTY_COMMAND=$1
JETTY_STOP=""
EXECUTOR_PWD=$(pwd)

changeDirToJettyHome() {
        cd $JETTY_BASE_DIR
}

changeDirBack() {
        cd $EXECUTOR_PWD
}
executeJetty() {
        JAVA_EXEC_PARAMS=" $JETTY_JVM_OPTS"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS -DSTOP.PORT=$JETTY_STOP_PORT"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS -DSTOP.KEY=$JETTY_STOP_KEY"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS -jar $JETTY_START_JAR"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS --module=http"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.port=$JETTY_PORT"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.https.port=$JETTY_HTTPS_PORT"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.sudocode.webapps=$JETTY_SUDOCODE_WEBAPPS"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.host=$JETTY_HOST"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS $JETTY_STOP"
        COMMAND="$JAVA_EXEC $JAVA_EXEC_PARAMS "
        $COMMAND &
}


case $JETTY_COMMAND in
        start)
                changeDirToJettyHome
                executeJetty
                changeDirBack
                ;;
        stop)
                changeDirToJettyHome
                JETTY_STOP="--stop"
                executeJetty
                changeDirBack
                ;;
        restart)
                changeDirToJettyHome
                executeJetty
                JETTY_STOP="--stop"
                executeJetty
                changeDirBack
                ;;
        *)
                echo "Usage: $(basename $0) (start | stop | restart)"
                ;;
esac

I'm not a "pro" in scripting so forgive me if you notice something awkward in my script. Here's the other one for the nexus-starter.

#!/bin/sh

JETTY_BASE_DIR=/opt/jetty/default                                               # Jetty Installation folder
JAVA_EXEC=java                                                                  # java executable
JETTY_PORT=18090                                                                # HTTP Port
JETTY_HTTPS_PORT=18091                                                          # HTTPS Port
JETTY_STOP_PORT=18092                                                           # STOP Port
JETTY_STOP_KEY=ilovejetty                                                       # STOP Key
JETTY_SUDOCODE_WEBAPPS=/nexus-apps                                              # Location of WAR under ${jetty.home}
JETTY_START_JAR=start.jar                                                       # Location of start.jar
JETTY_HOST=localhost                                                            # Jetty Host
JETTY_JVM_OPTS="-Xms256m -Xmx384m -XX:MaxPermSize=128m"                         # JVM Options

####################################################################################################################
# Don't touch the rest as much as possible
# This file must reside at ${jetty.home}
# Romeo H. Maranan Jr.
# Sudocode Systems Solutions, Inc.
JAVA_EXEC_PARAMS=""
JETTY_COMMAND=$1
JETTY_STOP=""
EXECUTOR_PWD=$(pwd)

changeDirToJettyHome() {
        cd $JETTY_BASE_DIR
}

changeDirBack() {
        cd $EXECUTOR_PWD
}

executeJetty() {
        JAVA_EXEC_PARAMS=" $JETTY_JVM_OPTS"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS -DSTOP.PORT=$JETTY_STOP_PORT"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS -DSTOP.KEY=$JETTY_STOP_KEY"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS -jar $JETTY_START_JAR"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS --module=http"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.port=$JETTY_PORT"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.https.port=$JETTY_HTTPS_PORT"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.sudocode.webapps=$JETTY_SUDOCODE_WEBAPPS"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS jetty.host=$JETTY_HOST"
        JAVA_EXEC_PARAMS="$JAVA_EXEC_PARAMS $JETTY_STOP"
        COMMAND="$JAVA_EXEC $JAVA_EXEC_PARAMS "
        $COMMAND &
}


case $JETTY_COMMAND in
        start)
                changeDirToJettyHome
                executeJetty
                changeDirBack
                ;;
        stop)
                changeDirToJettyHome
                JETTY_STOP="--stop"
                executeJetty
                changeDirBack
                ;;
        restart)
                changeDirToJettyHome
                executeJetty
                JETTY_STOP="--stop"
                executeJetty
                changeDirBack
                ;;
        *)
                echo "Usage: $(basename $0) (start | stop | restart)"
                ;;
esac

You will notice that we set different ports for properties JETTY_PORT, JETTY_HTTPS_PORT, JETTY_STOP_PORT and JETTY_SUDOCODE_WEBAPPS for each start-up script.

The JETTY_PORT will be set to jetty.port or the HTTP Port to be assigned to the Jetty instance. The JETTY_HTTPS_PORT will be set to the property jetty.https.port. The JETTY_STOP_PORT is for the STOP.PORT property which holds the value for the stop port - the port that will listen for stop commands. JETTY_SUDOCODE_WEBAPPS, finally, is for the property jetty.sudocode.webapps. This is a customized property that we set-up for our web directories.

Feel free to read the scripts so you may understand how things worked here. Also feel free to criticize. I'm very open to them since I'm not really a "pro" Linux user. That will be okay.

Now in able for these scripts to be executable, execute the chmod commands.

chmod +x bin/giblit/gitblit-starter
chmod +x bin/nexus/nexus-starter

Create Daemon Services

You could actually run the Jetty instances if you have finished the previous step.

bin/gitblit/gitblit-starter
bin/nexus/nexus-starter

But, if you are installing in an actual Linux server. You might need to create daemon services that will start-up your Jetty instances when the machine is booting up and will stop them when the machine is shutting down.

For this purpose, I created two more scripts. Using nano again:

nano bin/gitblit/gitblit-daemon

The script contains the following:

#!/bin/sh

JETTY_STARTER=/opt/jetty/default/bin/gitblit/gitblit-starter
JETTY_USER=rmaranan
JETTY_STARTER_COMMAND=

executeCommand() {
        start-stop-daemon -S -u $JETTY_USER -c $JETTY_USER -o -x $JETTY_STARTER $JETTY_STARTER_COMMAND
}

startCommand() {
        JETTY_STARTER_COMMAND=start
        executeCommand
}

stopCommand() {
        JETTY_STARTER_COMMAND=stop
        executeCommand
}

case $1 in
        start)
                startCommand
                ;;
        stop)
                stopCommand
                ;;
        restart)
                stopCommand
                startCommand
                ;;
        *)
                echo "Usage: $(basename $0) (start | stop | restart)"
                ;;
esac

Again, forgive my scripting. I hope you can see it here very straight forward.

The JETTY_STARTER parameter should be set to the respective start-up script. For gitblit-daemon, it should be /opt/jetty/default/gitblit/gitblit-starter.

For the nexus-daemon, it should look like the following:

#!/bin/sh

JETTY_STARTER=/opt/jetty/default/bin/nexus/nexus-starter
JETTY_USER=rmaranan
JETTY_STARTER_COMMAND=

executeCommand() {
        start-stop-daemon -S -u $JETTY_USER -c $JETTY_USER -o -x $JETTY_STARTER $JETTY_STARTER_COMMAND
}

startCommand() {
        JETTY_STARTER_COMMAND=start
        executeCommand
}

stopCommand() {
        JETTY_STARTER_COMMAND=stop
        executeCommand
}

case $1 in
        start)
                startCommand
                ;;
        stop)
                stopCommand
                ;;
        restart)
                stopCommand
                startCommand
                ;;
        *)
                echo "Usage: $(basename $0) (start | stop | restart)"
                ;;
esac

This time, the JETTY_STARTER points to /opt/jetty/default/nexus/nexus-starter.

Next step is to copy both daemon files at /etc/init.d.

Note that I'm using Ubuntu when I created the scripts. So start-stop-daemon command should have a counterpart at other Linux Distros. 

Anyway, let's move on. I leave the minor things to your research and development prowess.

We copy the daemon scripts to /etc/init.d

cp bin/gitblit/gitblit-daemon /etc/init.d
cp bin/nexus/nexus-daemon /etc/init.d

Then we execute the following commands:

update-rc.d gitblit-daemon defaults
update-rc.d nexus-daemon defaults

These commands will make our daemon scripts automatically started and stopped on boot-up and shutdown respectively.

Also, you could execute commands like start, stop and restart the following ways:

/etc/init.d/gitblit-daemon start

/etc/init.d/nexus-daemon start

/etc/init.d/gitblit-daemon stop

/etc/init.d/nexus-daemon stop

/etc/init.d/gitblit-daemon restart

/etc/init.d/nexus-daemon restart

That's all, blog readers!

Thank you for spending some time to read this blog. I hope you learned something. Also if you have comments or inquiries, I'll try my best as possible to answer them. I hope I could learn from you too.

No comments:

Post a Comment