#!/bin/bash
# ---------------------------------------------------------------------------
# JOnAS: Java(TM) Open Application Server
# Copyright (C) 2006 Bull S.A.S.
# Contact: jonas-team@objectweb.org
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
# USA
#
# Initial developer(s) 4.8.2: Jerome Pioux - Benoit Pelletier
#
# Modifications for 4.8.3
# -----------------------
# Jerome - 10/17/2006
#  - Make this bash script to work for ksh. ie:
#      (first line may need to be altered on some sites)...
#    * change all <declare> with <typeset>
#    * Add a prefix (Do) in the start,stop,kill,status function names
#    * Other modifications are flagged as Jerome - 10/17/2006 in the code
#
# ---------------------------------------------------------------------------
# $Id: $
# ---------------------------------------------------------------------------

# Status|Start|Stop|Kill JONAS cluster node(s)
# Usage: cl [-n<node>] [status] | < [-f] kill | [-d] start | stop>"

# If XWINDOWS=yes or YES exists in the user env., then use -win on startup
XWINDOWS=${XWINDOWS:-no}

# Default values
JCL_NUMBER_OF_NODES=${JCL_NUMBER_OF_NODES:-40}
JCL_BASE_PREFIX=${JCL_BASE_PREFIX:-$JCL_CLUSTER_DAEMON_DIR/jb}
JCL_DOMAIN_NAME=${JCL_DOMAIN_NAME:-sampleCluster2Domain}
JCL_EAR_FILE=${JCL_EAR_FILE:-$JONAS_ROOT/examples/sampleCluster2/output/apps/sampleCluster2.ear}
JCL_HA_EAR_FILE=${JCL_HA_EAR_FILE:-$JONAS_ROOT/examples/sampleCluster2/output/apps/haTransactions.ear}
JCL_NODE_NAME_PREFIX=${JCL_NODE_NAME_PREFIX:-node}
JCL_CLUSTER_DAEMON_DIR=${JCL_CLUSTER_DAEMON_DIR:-cd}
JCL_DB_DIR=${JCL_DB_DIR:-db}
JCL_DB_NAME=${JCL_DB_NAME:-db}
JCL_MASTER_DIR=${JCL_MASTER_DIR:-master}
JCL_MASTER_NAME=${JCL_MASTER_NAME:-master}

# ------------------------------------------
# NO NEED TO MODIFY ANYTHING BELOW THIS LINE
# ------------------------------------------
JCL_BASE_DEBUG_PORT=${JCL_BASE_DEBUG_PORT:-414}

# Delay in seconds to apply between nodes startup/shutdown
DELAY1_AFTER_NODE_START=12
DELAY2_AFTER_NODE_START=3
DELAY_BETWEEN_NODE_STOP=1

CLUSTER=""
jonasbase=""
typeset -i nnodes=0
typeset -i wholeCluster=0
osname=`uname -s`


# ------
# ATEXIT
# ------
atexit()
{
	printf "\n"
}

# -----
# USAGE
# -----
usage()
{
	printf "\n"
	printf "usage: $(basename $0) [ [-n<node>] [status]] | < [-f] kill | [-d] start | stop> ]\n"
	printf "\n"
	printf " With node: clusterd (or cd), $JCL_MASTER_NAME (or ms), $JCL_DB_NAME, node number(s)\n"
	printf "   * Form1: Multiple nodes can be specified between commas (no space)\n"
	printf "   * Form2: Range(s) can be entered using a dash between node numbers (no space)\n"
	printf "   * Any combination between form1 and form2 is allowed\n"
	printf "     Ex: -n $JCL_DB_NAME,1-3 applies to the $JCL_DB_NAME node and nodes 1,2 and 3\n"
	printf "  Notes:\n"
	printf "   * Do not specify -n or use -nall to address all nodes in the cluster\n"
	printf "   * A node number for your cluster must be between 1 and $JCL_NUMBER_OF_NODES\n"
	printf "\n"
	printf " Use \"export XWINDOWS=yes\" if you want to start the JVMs in dedicated xterms\n";
	printf "\n"
	exit 1
}

# ---------
# DOPADDING
# ---------
DoPadding()
{
	# Use to align fields on the screen
	typeset -i m1=$1
	typeset -i m2=$2
	typeset -i p=m2-m1

	typeset -i i=1
	while [[ $i -le $p ]]; do
		printf " "
		i=i+1
	done
	printf "   "
	return
}

# ------------
# SETJONASBASE
# ------------
SetJonasBase()
{
	# Do not add any echo or printf in this routine
	# besides the one before the return
	case $1 in
		clusterd)
			base=$JCL_CLUSTER_DAEMON_DIR
			;;
		$JCL_MASTER_NAME)
			base=$JCL_MASTER_DIR
			;;
		 $JCL_DB_NAME)
			base=$JCL_DB_DIR
			;;
		*)
			# Jerome - 10/17/2006 - char pos in string doesn't work in ksh
			#typeset -i l=${#JCL_NODE_NAME_PREFIX}
			#base=$JCL_BASE_PREFIX${node:$l}
			base=$JCL_BASE_PREFIX${node#*$JCL_NODE_NAME_PREFIX}
			;;
	esac
	echo $base
	return
}

# ----------
# SETCLUSTER
# ----------
SetCluster()
{
str=${1:-all}
typeset -i n=1

# --- All nodes
# We are in control so order nodes "startup-ready"
if [[ $str = 0 || $str = all ]]; then
	wholeCluster=1
	cluster="clusterd $JCL_DB_NAME $JCL_MASTER_NAME"
	while [[ $n -le $JCL_NUMBER_OF_NODES ]]; do
		cluster="$cluster $JCL_NODE_NAME_PREFIX$n"
		n=n+1
	done
	CLUSTER=${cluster#\ *}
	return
fi

# --- Partial nodes list
# Respect order chosen by user
typeset -i err=0
typeset -i numnode
OLDIFS=$IFS

# For nodes between commas
IFS=,
for substr in $str; do
   	n="$(expr $substr : '.*-')"

	# No dash-formed node
   	if [[ $n -eq 0 ]]; then
		# Non numeric node
		if ( echo "$substr" | grep -q [^[:digit:]] ); then
			# Abbrev.
			[[ $substr = cd ]] && substr=clusterd
			[[ $substr = ms ]] && substr=$JCL_MASTER_NAME
			if [[ $substr != clusterd &&
			      $substr != $JCL_MASTER_NAME   &&
			      $substr != $JCL_DB_NAME ]]; then
				err=1
			fi
           	cluster="$cluster $substr"
		# Numeric
		else
			numnode=$substr
			# numnode must be less then JCL_NUMBER_OF_NODES
			[[ $numnode -gt $JCL_NUMBER_OF_NODES ]] && err=1
           	cluster="$cluster $JCL_NODE_NAME_PREFIX$substr"
		fi

	# dash-formed node
	else
       	n1=${substr%%-*}
       	n2=${substr##*-}
		# Both dash-formed nodes must be numeric
		if ( echo "$n1" | grep -q [^[:digit:]] ||
		 	 echo "$n2" | grep -q [^[:digit:]] ); then
			err=1
		else
       		typeset -i i=$n1 && typeset -i imax=$n2
			# First dash-formed node must be less or equal the second
			# which in turn must be less then JCL_NUMBER_OF_NODES
			[[ $i -gt $imax || $imax -gt $JCL_NUMBER_OF_NODES ]] && err=1
       		while [[ $i -le $imax ]]; do
				cluster="$cluster $JCL_NODE_NAME_PREFIX$i"
				i=i+1
			done
		fi
	fi
done
IFS=$OLDIFS

# Remove heading space if any
cluster=${cluster#\ *}

# Raise error on duplicates
if [[ $err -eq 0 ]]; then
	for node1 in $cluster; do
		n=0
		for node2 in $cluster; do
			[[ $node1 = $node2 ]] && n=n+1
		done
		[[ $n -gt 1 ]] && err=1 && break
	done
fi

# Error - empty cluster
[[ $err -eq 1 ]] && cluster=""
CLUSTER=$cluster
return
}

# ---------------
# CLUSTER DAEMON
# ---------------
ClusterDaemon()
{
	case ${1:-na} in
		start)
    		printf "\n\n*** Starting JOnAS cluster daemon ... \n"
			# Jerome - 10/17/2006 - Change regex below for ksh support
			#if [[ $XWINDOWS =~ '[Yy][Ee][Ss]' ]]; then
			if [[ $XWINDOWS = [Yy][Ee][Ss] ]]; then
      			xterm -title $HOSTNAME:clusterd -geometry 110x16 -sb -e jclusterd start &
    		else
      			jclusterd start &
    		fi
			sleep $DELAY2_AFTER_NODE_START
			;;

		stop)
    		echo -e "\n\n*** Stopping JOnAS cluster daemon ..."
    		jclusterd stop
			sleep $DELAY_BETWEEN_NODE_STOP
			;;
	esac
	return
}

# -------
# SOSTART
# -------
Dostart()
{
	typeset -i delay
	typeset -i n=1

	# Jerome - 10/17/2006 - Change regex below for ksh support
	#[[ $XWINDOWS =~ '[Yy][Ee][Ss]' ]] && WIN="-win" || WIN=""
	[[ $XWINDOWS = [Yy][Ee][Ss] ]] && WIN="-win" || WIN=""

	# Startup cluster; If we are in control of the whole cluster, the nodes
	# are already in the correct order for startup
	for node in $CLUSTER; do
		jonasbase=$(SetJonasBase $node)
    	export JONAS_BASE=$jonasbase
    	export CATALINA_BASE=$JONAS_BASE
		DBG=""

		# Set debug and startup delay
		case $node in
			clusterd)
				ClusterDaemon start
				continue
				;;

			$JCL_MASTER_NAME | $JCL_DB_NODE)
				delay=$DELAY1_AFTER_NODE_START
				;;

			*)
				delay=$DELAY2_AFTER_NODE_START
				# Jerome - 10/17/2006 - char pos in string doesn't work in ksh
				#typeset -i l=${#node}
    			#[[ $DEBUG = true ]] && DBG="-debug -p $JCL_BASE_DEBUG_PORT${node:$l}"
    			[[ $DEBUG = true ]] && DBG="-debug -p $JCL_BASE_DEBUG_PORT${node#*$JCL_NODE_NAME_PREFIX}"
				;;
		esac

		# Save and clean up logs
		rm -rf $JONAS_BASE/logs/old/*
		mkdir -p $JONAS_BASE/logs/old
		mv $JONAS_BASE/logs/* $JONAS_BASE/logs/old 2>/dev/null

		# Eventually deploy apps
		if [[ "$JCL_EAR_FILE" != "NULL" && -f $JCL_EAR_FILE ]]; then
			cp $JCL_EAR_FILE $JONAS_BASE/apps/autoload 2>/dev/null
		fi
		if [[ "$JCL_HA_EAR_FILE" != "NULL" && -f $JCL_HA_EAR_FILE ]]; then
			cp $JCL_HA_EAR_FILE $JONAS_BASE/apps/autoload 2>/dev/null
		fi

		cmd="jonas start $WIN $DBG -n $node -Ddomain.name=$JCL_DOMAIN_NAME"
    	printf "\n\n*** Starting JOnAS $node instance ... \n($cmd)\n"
		$cmd

		# Wait delay seconds between nodes startup
		if [[ $n -lt $nnodes && $WIN = "-win" ]]; then
			 printf "\n\tWaiting ${delay}s before starting next node ..."
			 sleep $delay
		fi
		n=n+1
	done
	return
}

# ------
# DOSTOP
# ------
Dostop()
{
	typeset -i delay=$DELAY_BETWEEN_NODE_STOP

	# Stop cluster; If we are in control of the whole cluster, the nodes
	# are in the correct order for startup which is the reverse order of
	# how we would like to stop them; so let's build a "reverse" cluster
	if [[ $wholeCluster -eq 1 ]]; then
		for node in $CLUSTER; do
			cluster2="$node $cluster2"
		done
		CLUSTER=$cluster2
	fi

	# Stop cluster
	for node in $CLUSTER; do
		jonasbase=$(SetJonasBase $node)
    	export JONAS_BASE=$jonasbase
    	export CATALINA_BASE=$JONAS_BASE

		if [[ $node = clusterd ]]; then
			ClusterDaemon stop
			continue
		fi

		cmd="jonas stop -n $node"
    	printf "\n\n*** Stopping JOnAS $node instance ...\n($cmd)\n"
		$cmd

		sleep $delay
	done
	return
}

# ------
# DOKILL
# ------
Dokill()
{
	# Kill cluster
	for node in $CLUSTER; do

                # flegare+vma - little adaptation to make it run under solaris:
	        # this ps does not truncate its output to 80 chars but does not accept
	        # the '-o' param
	        if [[ $osname = "SunOS" ]]; then
	            pid=$(/usr/ucb/ps aww | grep $strToMatch | grep -v grep | cut -d' ' -f2)
	        else
	            # Jerome - 10/17/2006 - Make ps formatting variables to please AIX and LINUX
	            proc=$(ps -$psfmt "%p|%a" | grep Djonas.name=$node | grep -v grep | sed -e 's/  *//g')
	            # Jerome - 10/17/2006 - The pipe below must be protected in ksh
	            pid=${proc%%\|*}
	        fi

		if [[ ! -z $pid ]]; then
			cmd="/bin/kill -$force $pid"
    		printf "\n*** Killing JOnAS $node instance ...\n($cmd)\n"
			$cmd
		fi
	done
    return
}

# --------
# DOSTATUS
# --------
Dostatus()
{
	typeset -i l
	typeset -i lnode
	typeset -i ljonasbase
	typeset -i maxlnode=0
	typeset -i maxljonasbase=0

	# Get longest strings length
	# (to use in field alignment for display below)
	for node in $CLUSTER; do
		jonasbase=$(SetJonasBase $node)

		lnode=${#node}
		ljonasbase=${#jonasbase}
		[[ $lnode -gt $maxlnode ]]           && maxlnode=$lnode
		[[ $ljonasbase -gt $maxljonasbase ]] && maxljonasbase=$ljonasbase
	done

	printf "\n"

	# Display status
	for node in $CLUSTER; do
		jonasbase=$(SetJonasBase $node)

		lnode=${#node}
		ljonasbase=${#jonasbase}

		printf " node=%-s" $node
		DoPadding $lnode $maxlnode

		printf "base=%-s" $jonasbase
		DoPadding $ljonasbase $maxljonasbase

		printf "%0.s-> "

		if [[ $node = clusterd ]]; then
			strToMatch=ClusterDaemon
		else
			strToMatch=Djonas.name=$node
		fi

	        # flegare+vma - little adaptation to make it run under solaris:
	        # this ps does not truncate its output to 80 chars but does not accept
	        # the '-o' param
	        if [[ $osname = "SunOS" ]]; then
	            pid=$(/usr/ucb/ps aww | grep $strToMatch | grep -v grep | cut -d' ' -f2)
	        else
	            # Jerome - 10/17/2006 - Make ps formatting variables to please AIX and LINUX
	            proc=$(ps -$psfmt "%p|%a" | grep Djonas.name=$node | grep -v grep | sed -e 's/  *//g')
	            # Jerome - 10/17/2006 - The pipe below must be protected in ksh
	            pid=${proc%%\|*}
	        fi

		if [[ ! -z $pid ]]; then
      			printf "running [$pid]\n"
		else
      			printf "not running\n"
		fi
	done
	return
}

# -----
# MAIN
# -----
trap "atexit" exit

# Jerome - 10/17/2006
# On AIX, the ww is not supported (and not needed)
if [[ $(uname -s) = AIX ]]; then
    XTERM=aixterm
    psfmt=eo
else
    XTERM=xterm
    psfmt=ewwo
fi

typeset -i n
typeset -i force=15
debug=false

# Show stopper
if [[ ! -n $JONAS_ROOT ]]; then
	printf "$0: JONAS_ROOT is not defined\n"
	exit 2
fi
if ( ! echo $PATH | grep $JONAS_ROOT/bin/unix >/dev/null 2>&1 ) then
	printf "$0: JONAS_ROOT/bin/unix is not in your PATH\n"
	exit 3
fi

# Read inputs
if [[ $# -ne 0 ]]; then
	# linux/bash help way
	[[ "$1" = --help ]] && usage

	while getopts ?fhn:d c
	do
		case $c in
			# Nodes
			n)
				node=$OPTARG
				;;
			f)
				force=9
				;;
			d)
				debug=true
				;;
			*)
				usage
				;;
		esac
    done
    shift `expr $OPTIND - 1`
    action="$@"
fi

# Define the cluster
SetCluster $node
if [[ -z $CLUSTER ]]; then
	printf "$0: illegal -n parameters\n"
	usage
else
	# Remember the number of nodes in the cluster
	for node in $CLUSTER; do
		nnodes=nnodes+1
	done
fi

# Do the action
[[ "$action" = "" ]] && action=status
case "$action" in
	start|stop|kill|status)
		Do$action
		;;
	*)
		usage
		;;
esac
exit 0

