#!/bin/bash
#
# Copyright (c) 2015, 2018 Oracle and/or its affiliates. All rights reserved.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0, which is available at
# http://www.eclipse.org/legal/epl-2.0.
#
# This Source Code may also be made available under the following Secondary
# Licenses when the conditions for such availability set forth in the
# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
# version 2 with the GNU Classpath Exception, which is available at
# https://www.gnu.org/software/classpath/license.html.
#
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
#

APP_LIST=(mbw-text-plain \
          mbw-json-moxy mbw-json-jackson \
          mbw-xml-moxy mbw-xml-jaxb \
          mbw-custom-provider \
          mbw-kryo \
          param-srl \
          filter-global filter-name filter-dynamic \
          interceptor-global interceptor-name interceptor-dynamic \
          proxy-injection)

WARM_UP_SECONDS=300
WAIT_FOR_APP_STARTUP_SEC=5
WAIT_FOR_APP_RUNNING_SEC=60
CHECK_RUNNER_INTERVAL=5
CHECK_TERM_INTERVAL=10

JMX_URI_TEMPLATE="service:jmx:rmi:///jndi/rmi://SERVER_MACHINE:11112/jmxrmi"
SAMPLES=30

#uncomment for debug purposes:
#WARM_UP_SECONDS=3
#WAIT_FOR_APP_STARTUP_SEC=2
#WAIT_FOR_APP_RUNNING_SEC=1
#CHECK_RUNNER_INTERVAL=1
#CHECK_TERM_INTERVAL=2
#SAMPLES=3
#/debug

MODULES_TO_BUILD=""
for app in ${APP_LIST[*]}; do
  MODULES_TO_BUILD="$MODULES_TO_BUILD,tests/performance/test-cases/$app"
done
MODULES_TO_BUILD=`echo $MODULES_TO_BUILD|sed -e's/,//'`

SERVER_LIST=()
CLIENT_LIST_ALL=()


function waitForGroupStatus() {
  echo "########### Waiting for group status: $*"
  RUNNER_ID=$1
  shift
  GROUP_ID=$1
  shift
  STATUS=$1

  echo "#`date`" > $STATUS_DIR/.runner.$RUNNER_ID.waiting.$GROUP_ID.$STATUS
  FILE="$STATUS_DIR/.group.$GROUP_ID.running.$RUNNER_ID.$STATUS"

  rm -f $STATUS_DIR/.group.$GROUP_ID.running.$RUNNER_ID.*

  available=false
  while [ "$available" != true ]; do
    if [ -e "$FILE" ]; then
      available=true
    else
      sleep 1
    fi
  done
}

function releaseRunnerAndGroup() {
  echo "########### Release Runner and Group: $*"
  RUNNER_ID=$1
  shift
  GROUP_ID=$1

  echo "#`date`" > $STATUS_DIR/.runner.$RUNNER_ID.available
  rm -f $STATUS_DIR/.group.$GROUP_ID.running.$RUNNER_ID.* $STATUS_DIR/.runner.$RUNNER_ID.running
}

function checkWaitingRunners() {
  echo "########### Check Waiting Runners"
  for waiting_file in `ls $STATUS_DIR/.runner.*.waiting.*.lock 2> /dev/null`; do
    filename_array=(`basename $waiting_file | tr "." " "`)

    runner_id=${filename_array[1]}
    group_id=${filename_array[3]}
    status=${filename_array[4]}

    _files=($STATUS_DIR/.group.$group_id.running.*)
    if [ ! -f "${_files}" ]; then
      echo "#`date`" > "$STATUS_DIR/.group.$group_id.running.$runner_id.lock"
      rm $waiting_file
    fi
  done

  for waiting_file in `ls $STATUS_DIR/.runner.*.waiting.*.open 2> /dev/null`; do
    filename_array=(`basename $waiting_file | tr "." " "`)

    runner_id=${filename_array[1]}
    group_id=${filename_array[3]}
    status=${filename_array[4]}

    _files=($STATUS_DIR/.group.$group_id.running.*.lock)
    if [ ! -f "${_files}" ]; then
      echo "#`date`" > "$STATUS_DIR/.group.$group_id.running.$runner_id.open"
      rm $waiting_file
    fi
  done
}

function createMachineFiles {
  echo "########### Creating machine files in $STATUS_DIR for: $*"
  RUNNER_ID=$1
  shift
  GROUP_ID=$1
  shift
  SERVER_MACHINE=$1
  shift
  CLIENT_LIST=($@)

  echo ${GROUP_ID} > $STATUS_DIR/.runner.$RUNNER_ID.group
  echo "#`date`" > $STATUS_DIR/.runner.$RUNNER_ID.available
  echo ${SERVER_MACHINE} > $STATUS_DIR/.runner.$RUNNER_ID.server
  echo ${CLIENT_LIST[@]} > $STATUS_DIR/.runner.$RUNNER_ID.clients

  SERVER_LIST=("${SERVER_LIST[@]}" "$SERVER_MACHINE")
  CLIENT_LIST_ALL=("${CLIENT_LIST_ALL[@]}" "${CLIENT_LIST[@]}")
}

function waitForTerminator {
  echo "########### Waiting for finish"
  # wait for the last round to finish
  terminated=false
  while [ "$terminated" != true ]; do
    checkWaitingRunners
    _files=($STATUS_DIR/.runner.*.running)
    if [ ! -f "${_files}" ]; then
      terminated=true
    fi
    if [ "$terminated" != true ]; then
      echo "########### Terminated tests: $terminated, waiting $CHECK_TERM_INTERVAL sec..."
      sleep $CHECK_TERM_INTERVAL
    fi
  done

  echo "DONE!"

  wait
  sleep 4
  wait
}

function testLoop {
  # Following is the main measurement loop
  # MEASUREMENT_DATA is the input data in the following format:
  # application directory name|command line to generate load on client machines|JMX URI for the application|MBean name|output filename

  echo "########### Let's test it, reading from $MEASUREMENT_DATA file"

  TOTAL_COUNT=`cat $MEASUREMENT_DATA | grep -v "^#" | grep "^." | wc -l | sed -e 's/ *//g'`
  INDEX=0
  cat $MEASUREMENT_DATA | grep -v "^#" | grep "^." | while IFS="\|" read app ab_cmdline app_class agent_param mbean filename
  do
    INDEX=`expr $INDEX + 1`
    echo "========================================= DATA [$INDEX/$TOTAL_COUNT] =============================================="
    echo "app       = $app"
    echo "app_class = $app_class"
    echo "ab_cmdline= $ab_cmdline"

    spawned=false
    while [ "$spawned" != true ]; do
      for runner_file in `ls $STATUS_DIR/.runner.*.available 2> /dev/null`; do
        if [ "$spawned" != true ]; then
          filename_array=(`basename $runner_file | tr "." " "`)
          actual_runner=${filename_array[1]}
          echo "#`date`" > $STATUS_DIR/.runner.$actual_runner.running
          rm $runner_file

          SERVER_MACHINE=`cat $STATUS_DIR/.runner.$actual_runner.server`
          APP_CONTEXT=$app
          CLIENT_LIST=(`cat $STATUS_DIR/.runner.$actual_runner.clients`)
          ab_cmdline=`echo $ab_cmdline | sed -e"s/SERVER_MACHINE/$SERVER_MACHINE/" | sed -e"s/SERVER_PORT/$SERVER_PORT/" | sed -e"s/APP_CONTEXT/$APP_CONTEXT/"`
          JMX_URI=`echo $JMX_URI_TEMPLATE | sed -e"s/SERVER_MACHINE/$SERVER_MACHINE/"`
          group_id=`cat $STATUS_DIR/.runner.$actual_runner.group`

          echo "#[$INDEX/$TOTAL_COUNT]" >> $STATUS_DIR/.runner.$actual_runner.running
          echo "$app" >> $STATUS_DIR/.runner.$actual_runner.running
          echo "$app_class" >> $STATUS_DIR/.runner.$actual_runner.running
          echo "$ab_cmdline" >> $STATUS_DIR/.runner.$actual_runner.running
          echo "$filename" >> $STATUS_DIR/.runner.$actual_runner.running
          echo "$mbean" >> $STATUS_DIR/.runner.$actual_runner.running
          echo "$agent_param" >> $STATUS_DIR/.runner.$actual_runner.running

          spawned=true
          singleTest &
        fi
      done
      checkWaitingRunners
      if [ "$spawned" != true ]; then
        sleep $CHECK_RUNNER_INTERVAL
      fi
    done
  done
}

function removeOldCapturedData {
  rm -f $WORKSPACE/*.properties
}

function retrieveJmxClient {
  echo "########### Retrieving JMX client"
  scp jerseyrobot@${SERVER_LIST[0]}:jmxclient.jar .
}

function prepareClients {
  echo "########### Copy client files to each client"
  for CLIENT_MACHINE in ${CLIENT_LIST_ALL[@]}; do
    echo "... copy client files to ${CLIENT_MACHINE}"
    scp $WORKSPACE/jersey/tests/performance/etc/client/* jerseyrobot@${CLIENT_MACHINE}:. &
  done

  wait
}

function buildTestAppOnServers {
  echo "########### Building test applications on each server"
  # git fetch jersey on the server machine and build all apps there:
  for SERVER_MACHINE in ${SERVER_LIST[@]}; do
    ssh -n jerseyrobot@${SERVER_MACHINE} '(cd $HOME/workspace/jersey && '$GIT_FETCH_COMMAND' && mvn -pl '$MODULES_TO_BUILD' -am -Dskip.tests=true clean install)' &
  done

  wait
}

function cleanupHudsonSlave {
  rm -f $STATUS_DIR/.runner.* $STATUS_DIR/.group.*
}

function cleanupServer {
  echo "########### Kill java processes on server $1"
  ssh -n jerseyrobot@$1 'if ! test -e `ps h o pid -Cjava`; then kill -s TERM `ps h o pid -Cjava` ; fi'
}

function cleanupServers {
  echo "########### Kill all java processes on each server"
  for SERVER_MACHINE in ${SERVER_LIST[@]}; do
    cleanupServer ${SERVER_MACHINE}
  done
}


trap "cleanupHudsonSlave; cleanupServers" EXIT SIGTERM SIGINT

#uncomment for debug purposes:
#trap 'echo "[$BASH_SOURCE:$LINENO] $BASH_COMMAND" >> .debug; tail -10 .debug > .debug.swap; mv .debug.swap .debug' DEBUG
