#!/bin/ksh
# ---------------------------------------------------------------------------
# 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: jcl.ksh 15476 2008-10-09 07:58:28Z pelletib $
# ---------------------------------------------------------------------------

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

# 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/cluster/output/apps/sampleCluster2.ear}
JCL_HA_EAR_FILE=${JCL_HA_EAR_FILE:-$JONAS_ROOT/examples/cluster/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 | halt> ]\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/deploy 2>/dev/null
    fi
    if [[ "$JCL_HA_EAR_FILE" != "NULL" && -f $JCL_HA_EAR_FILE ]]; then
      cp $JCL_HA_EAR_FILE $JONAS_BASE/deploy 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
}


# ------
# DOHALT
# ------
Dohalt()
{
    typeset -i delay=$DELAY_BETWEEN_NODE_STOP

    # Halt 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 halt -n $node"
        printf "\n\n*** Halting 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|halt)
    Do$action
    ;;
  *)
    usage
    ;;
esac
exit 0

