One Java init script to rule them all


Need an init script for a Java service?  Like tomcat, liferay, or jboss?

Regular Linux init scripts often don’t work so well.  e.g. Java apps typically want to be run by a particular user (e.g. tomcat or liferay).  e.g. They may require a ‘special’ shutdown mechanism (e.g. with Tomcat sending a signal to a particular port).

So over the years I have had a handy-dandy script that can control most of my most commonly used Java apps (liferay, tomcat, jboss).

It does things like start up the app as a particular user.  Checks to ensure the app is responding on a URL before saying it is stared.  Waits for the app to shutdown gracefully, else forces it to quit.  And provides kill and killstart commands when you’re developing and just want the thing restarted!

This morning I updated it to use the lsb logging methods.  So the output is a bit prettier than the ‘old’ echo’s we used to use.

The current version of the file will live at http://proj.ri.mu/javainitscript

To use it wget that to the /etc/init.d directory on your server.  Then rename (or symlink) it to the service you want to control.  Currently the tomcat, liferay and jboss names are supported.

Some excerpts (if you’re using the script, alway use the one at http://proj.ri.mu/javainitscript) :

#!/bin/bash
# Startup script for Jakarta Tomcat, Lferay, JBoss, or potentially other java apps
if [ -e /etc/debian_version ]; then
    . /lib/lsb/init-functions
elif [ -e /etc/init.d/functions ] ; then
    . /etc/init.d/functions
fi

# else we include the crucial log_* methods for lsb-deprived distros
# how long to wait for the app to startup before saying 'its probably up'
STARTWAITTIMES=45

# figure out what to do based on the name of this script
if echo $0 | grep -qai tomcat; then
HOMEDIR=/usr/local/tomcat
TOMCAT_USER=tomcat
APPNAME=Tomcat
elif echo $0 | grep -qai jboss; then
....
else
log_failure_msg "Unknown startup script name $0"
exit 1
fi

# something so we can test if the app is fully started
TESTURL=http://127.0.0.1:8080/
if [ -e $HOMEDIR/initscript/testurl ]; then
TESTURL=$(cat $HOMEDIR/initscript/testurl)
fi

# makes it a bit easier to find if the process is running.  e.g. put a -Dsomeval=Y
# into the JAVA_OPTS for the program.  e.g. in catalina.sh or setenv.sh or run.conf
if [ -e $HOMEDIR/initscript/grepstring ]; then
GREPSTRING=$(cat $HOMEDIR/initscript/grepstring)
fi 

#Necessary environment variables
export JAVA_HOME=/usr/java/jdk
#export LD_KERNEL_ASSUME="2.2.5"

if [ "$APPNAME" = "Tomcat"  -o "$APPNAME" = "Liferay" -o "$APPNAME" = "Alfresco" ]; then
    export CATALINA_HOME=$HOMEDIR
    INITSCRIPT=$HOMEDIR/bin/catalina.sh
    RUNCOMMAND="export CATALINA_HOME=$CATALINA_HOME; $INITSCRIPT start"
    STOPCOMMAND="$INITSCRIPT stop"
    STARTGREPTEXT="[o]rg.apache.catalina.startup.Bootstrap start"
    LOGFILE=$HOMEDIR/logs/catalina.out
elif [ "$APPNAME" = "JBoss" ]; then
   ....
else
    log_failure_msg "Only JBoss and Tomcat are recognised.  Not $APPNAME"
    exit 1
fi

setpslist() {
	PSLIST=$(ps a --width=1000 --User "$TOMCAT_USER" -o  pid,user,command  | grep "$GREPSTRING" | grep -v PID | awk '{printf $1 " "}')
}

start() {
    setpslist
    log_daemon_msg "Starting" "$APPNAME"
    if [ ! -z "$PSLIST" ]; then
        log_warning_msg "$APPNAME already running, can't start it"
        log_end_msg 1
        return 1
    fi
    chown -R $TOMCAT_USER $HOMEDIR
    exec su - -p --shell=/bin/sh $TOMCAT_USER -c "cd $(dirname $INITSCRIPT); $RUNCOMMAND >\"$LOGFILE\"" 2>&1 &
    local starttime=$(date +"%s")
    # wait a bit for the app to start up
    while true; do
        sleep 3
        local now=$(date +"%s")
        if wget --tries=1 --timeout=1 --server-response -O - $TESTURL 2>&1 | grep -qai " HTTP/1.1 "; then
          log_end_msg 0
          break
        fi
        # process not starting (cf. http response not happening)
        if [ $(($now - 15 )) -gt $starttime ]; then
            setpslist
            if [ -z "$PSLIST" ]; then
                log_failure_msg "Java process not starting.  Last few lines from the startup log follow:"
                log_failure_msg "$(tail -n 4 $LOGFILE)"
                log_end_msg 1
                return 1
            fi
        fi
        if [ $(($now - $STARTWAITTIMES)) -gt $starttime ]; then
            log_warning_msg "$APPNAME startup taking too long, not getting a response on $TESTURL, giving up"
            log_end_msg 0
            return 0
        fi
        log_progress_msg .
    done
}

killprocesses() {
    log_daemon_msg "Killing" "$APPNAME"
    setpslist
    if [ -z "$PSLIST" ]; then
        log_progress_msg "$APPNAME not running, no need to kill it"
        log_end_msg 0
    fi
    kill -9 $PSLIST
    log_end_msg 0
}

stop() {
    log_daemon_msg "Stopping" "$APPNAME"
    setpslist
    if [ -z "$PSLIST" ]; then
        log_progress_msg "$APPNAME not running, no need to stop it"
        log_end_msg 0
    fi
    waslistening=N
    needtokill=N
    if wget --tries=1 --timeout=1 --server-response -O - $TESTURL 2>&1 | grep -qai " HTTP/1.1 "; then
       waslistening=Y
    fi

    suoutput=$(su - --shell=/bin/bash -p $TOMCAT_USER -c "$STOPCOMMAND" 2>&1)
    local starttime=$(date +"%s")
    # wait a while for the app to shutdown gracefully, else kill it
    while true; do
        sleep 3
        local now=$(date +"%s")
        setpslist
        if [ -z "$PSLIST" ]; then
            log_end_msg 0
            return 0
        fi
        if echo $suoutput | egrep -qai "(Refused|Address already in use)" ; then
            log_warning_msg "'stop' signal refused, killing $APPNAME."
            kill -SIGTERM $PSLIST
        elif [ $(($now - 50)) -gt $starttime ]; then
            log_warning_msg "Graceful shutdown taking too long, terminating it.";
            kill -SIGTERM $PSLIST
        elif [ $(($now - 50)) -gt $starttime ]; then
            log_warning_msg "Graceful shutdown taking too long, killing it.";
            kill -SIGKILL $PSLIST
        elif [ "$needtokill" = "Y" ]; then
            log_progress_msg "Killing. "
            kill -SIGKILL $PSLIST
        elif [ "$waslistening" = "Y" -a "$needtokill" = "N" ]; then
            if  ! wget --tries=1 --timeout=1 --server-response -O - $TESTURL 2>&1 | grep -qai " HTTP/1.1 " ; then
            log_progress_msg  "Stopped listening on http, but not shutting down fully. "
            needtokill=Y
            sleep 10
            fi
        fi
        # echo -n $(echo $PSLIST | wc -w) " "
    done
}

status() {
    setpslist
    if [ ! -z "$PSLIST" ]; then
        local MSG="$APPNAME ( PIDs $PSLIST ) is running."
        if wget --tries=1 --timeout=1 --server-response -O - $TESTURL 2>&1 | grep -qai " HTTP/1.1 "; then
            log_success_msg "$MSG  And listening on $TESTURL."
        else
            log_warning_msg "$MSG  But not responding on $TESTURL."
        fi
    else
        log_failure_msg "$APPNAME is not running"
    fi
}

case "$1" in
    start)
        start
        ;;
     stop)
        stop
        ;;
     restart)
        stop
        sleep 3
        start
        ;;
     kill)
        killprocesses
        ;;
     killstart)
        killprocesses
	start
        ;;
     status)
        status
        ;;
     *)
        echo "Usage: $0 {start|stop|restart|status|kill|killstart}"
     exit 1
esac
exit $?

3 responses to “One Java init script to rule them all”

  1. This sounds like a good idea, but before I can spend time evaluating whether it does what I need, I need to know if I can actually use it. Could you add a FOSS license to it? Apache 2.0 or BSD style would make it easy for anyone to use, and provide some protection for you by declaring it as-is use at your own risk etc….

  2. Heya Gus, anything we provide is free for use of any kind, there is no license at all, just take it and do what you want with it.