#!/bin/bash
#
#    Licensed to the Apache Software Foundation (ASF) under one or more
#    contributor license agreements.  See the NOTICE file distributed with
#    this work for additional information regarding copyright ownership.
#    The ASF licenses this file to You under the Apache License, Version 2.0
#    (the "License"); you may not use this file except in compliance with
#    the License.  You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#

DIRNAME=$(dirname "${0}")
PROGNAME=$(basename "${0}")

#
# Sourcing environment settings for karaf similar to tomcats setenv
#
KARAF_SCRIPT="karaf"
export KARAF_SCRIPT
if [ -f "${DIRNAME}/setenv" ]; then
  . "${DIRNAME}/setenv"
fi

#
# Set up some easily accessible MIN/MAX params for JVM mem usage
#
if [ "x${JAVA_MIN_MEM}" = "x" ]; then
    JAVA_MIN_MEM=128M
    export JAVA_MIN_MEM
fi
if [ "x${JAVA_MAX_MEM}" = "x" ]; then
    JAVA_MAX_MEM=512M
    export JAVA_MAX_MEM
fi

#
# Check the mode that initiated the script
#
if [ "x${1}" != "x" ]; then
    MODE=${1}
fi

warn() {
    echo "${PROGNAME}: $*"
}

die() {
    warn "$*"
    exit 1
}

detectOS() {
    # OS specific support (must be 'true' or 'false').
    cygwin=false;
    darwin=false;
    aix=false;
    os400=false;
    case "$(uname)" in
        CYGWIN*)
            cygwin=true
            ;;
        Darwin*)
            darwin=true
            ;;
        AIX*)
            aix=true
            ;;
        OS400*)
            os400=true
            ;;
    esac
    # For AIX, set an environment variable
    if ${aix}; then
         export LDR_CNTRL=MAXDATA=0xB0000000@DSA
         echo ${LDR_CNTRL}
    fi
}

unlimitFD() {
    # Use the maximum available, or set MAX_FD != -1 to use that
    if [ "x${MAX_FD}" = "x" ]; then
        MAX_FD="maximum"
    fi

    # Increase the maximum file descriptors if we can
    if [ "${os400}" = "false" ] && [ "${cygwin}" = "false" ]; then
        if [ "${MAX_FD}" = "maximum" -o "${MAX_FD}" = "max" ]; then
            MAX_FD_LIMIT=$(ulimit -H -n)
            if [ $? -eq 0 ]; then
                # use the system max
                MAX_FD="${MAX_FD_LIMIT}"
            else
                warn "Could not query system maximum file descriptor limit: ${MAX_FD_LIMIT}"
            fi
        fi
        if [ "${MAX_FD}" != 'unlimited' ]; then
            ulimit -n "${MAX_FD}" > /dev/null
            if [ $? -ne 0 ]; then
                warn "Could not set maximum file descriptor limit: ${MAX_FD}"
            fi
        fi
     fi
}

locateHome() {
    if [ "x${KARAF_HOME}" = "x" ]; then
        # In POSIX shells, CDPATH may cause cd to write to stdout
        (unset CDPATH) >/dev/null 2>&1 && unset CDPATH
        # KARAF_HOME is not provided, fall back to default
        KARAF_HOME=$(cd "${DIRNAME}/.."; pwd)
    fi

    if [ ! -d "${KARAF_HOME}" ]; then
        die "KARAF_HOME is not valid: ${KARAF_HOME}"
    fi
}

locateBase() {
    if [ "x${KARAF_BASE}" != "x" ]; then
        if [ ! -d "${KARAF_BASE}" ]; then
            die "KARAF_BASE is not valid: ${KARAF_BASE}"
        fi
    else
        KARAF_BASE=${KARAF_HOME}
    fi
}

locateData() {
    if [ "x${KARAF_DATA}" != "x" ]; then
        if [ ! -d "${KARAF_DATA}" ]; then
            die "KARAF_DATA is not valid: ${KARAF_DATA}"
        fi
    else
        KARAF_DATA=${KARAF_BASE}/data
    fi
}

locateEtc() {
    if [ "x${KARAF_ETC}" != "x" ]; then
        if [ ! -d "${KARAF_ETC}" ]; then
            die "KARAF_ETC is not valid: ${KARAF_ETC}"
        fi
    else
        KARAF_ETC=${KARAF_BASE}/etc
    fi
}

setupNativePath() {
    # Support for loading native libraries
    LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${KARAF_BASE}/lib:${KARAF_HOME}/lib"

    # For Cygwin, set PATH from LD_LIBRARY_PATH
    if ${cygwin}; then
        LD_LIBRARY_PATH=$(cygpath --path --windows "${LD_LIBRARY_PATH}")
        PATH="${PATH};${LD_LIBRARY_PATH}"
        export PATH
    fi
    export LD_LIBRARY_PATH
}

pathCanonical() {
    local_dst="${1}"
    while [ -h "${local_dst}" ] ; do
        ls=$(ls -ld "${local_dst}")
        link=$(expr "${ls}" : '.*-> \(.*\)$')
        if expr "${link}" : '/.*' > /dev/null; then
            local_dst="${link}"
        else
            local_dst="$(dirname "${local_dst}")/${link}"
        fi
    done
    local_bas=$(basename "${local_dst}")
    local_dir=$(dirname "${local_dst}")
    if [ "${local_bas}" != "${local_dir}" ]; then
        local_dst="$(pathCanonical "${local_dir}")/${local_bas}"
    fi
    echo "${local_dst}" | sed -e 's#//#/#g' -e 's#/./#/#g' -e 's#/[^/]*/../#/#g'
}

locateJava() {
    # Setup the Java Virtual Machine
    if ${cygwin} ; then
        [ -n "${JAVA}" ] && JAVA=$(cygpath --unix "${JAVA}")
        [ -n "${JAVA_HOME}" ] && JAVA_HOME=$(cygpath --unix "${JAVA_HOME}")
    fi

    if [ "x${JAVA_HOME}" = "x" ] && [ "${darwin}" = "true" ]; then
        JAVA_HOME="$(/usr/libexec/java_home -v 1.8)"
    fi
    if [ "x${JAVA}" = "x" ] && [ -r /etc/gentoo-release ] ; then
        JAVA_HOME=$(java-config --jre-home)
    fi
    if [ "x${JAVA}" = "x" ]; then
        if [ "x${JAVA_HOME}" != "x" ]; then
            if [ ! -d "${JAVA_HOME}" ]; then
                die "JAVA_HOME is not valid: ${JAVA_HOME}"
            fi
            JAVA="${JAVA_HOME}/bin/java"
        else
            warn "JAVA_HOME not set; results may vary"
            JAVA=$(type java)
            JAVA=$(expr "${JAVA}" : '.* \(/.*\)$')
            if [ "x${JAVA}" = "x" ]; then
                die "java command not found"
            fi
        fi
    fi
    if [ "x${JAVA_HOME}" = "x" ]; then
        JAVA_HOME="$(dirname "$(dirname "$(pathCanonical "${JAVA}")")")"
    fi
}

detectJVM() {
    #echo "$(${JAVA} -version)"
    # This service should call $(java -version),
    # read stdout, and look for hints
    if ${JAVA} -version 2>&1 | grep "^IBM" ; then
        JVM_VENDOR="IBM"
    # on OS/400, java -version does not contain IBM explicitly
    elif ${os400}; then
        JVM_VENDOR="IBM"
    else
        JVM_VENDOR="SUN"
    fi
    # echo "JVM vendor is ${JVM_VENDOR}"
}

checkJvmVersion() {
    # echo "$(${JAVA} -version)"
    VERSION=$(${JAVA} -version 2>&1 | egrep '"([0-9].[0-9]\..*[0-9]).*"' | awk '{print substr($3,2,length($3)-2)}' | awk '{print substr($1, 3, 3)}' | sed -e 's;\.;;g')
    # echo ${VERSION}
    if [ "${VERSION}" -lt "60" ]; then
        echo "JVM must be greater than 1.6"
        exit 1;
    fi
}

setupDebugOptions() {
    if [ "x${JAVA_OPTS}" = "x" ]; then
        JAVA_OPTS="${DEFAULT_JAVA_OPTS}"
    fi
    export JAVA_OPTS

    if [ "x${EXTRA_JAVA_OPTS}" != "x" ]; then
        JAVA_OPTS="${JAVA_OPTS} ${EXTRA_JAVA_OPTS}"
    fi

    # Set Debug options if enabled
    if [ "x${KARAF_DEBUG}" != "x" ]; then
        # Ignore DEBUG in case of stop, client, or status mode
        if [ "x${MODE}" = "xstop" ]; then
            return
        fi
        if [ "x${MODE}" = "xclient" ]; then
            return
        fi
        if [ "x${MODE}" = "xstatus" ]; then
            return
        fi
        # Use the defaults if JAVA_DEBUG_OPTS was not set
        if [ "x${JAVA_DEBUG_OPTS}" = "x" ]; then
            JAVA_DEBUG_OPTS="${DEFAULT_JAVA_DEBUG_OPTS}"
        fi

        JAVA_OPTS="${JAVA_DEBUG_OPTS} ${JAVA_OPTS}"
        warn "Enabling Java debug options: ${JAVA_DEBUG_OPTS}"
    fi
}

setupDefaults() {
    DEFAULT_JAVA_OPTS="-Xms${JAVA_MIN_MEM} -Xmx${JAVA_MAX_MEM} -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass "

    #Set the JVM_VENDOR specific JVM flags
    if [ "${JVM_VENDOR}" = "SUN" ]; then
        # permgen was removed in Java 8
        VERSION=$(${JAVA} -version 2>&1 | egrep '"([0-9].[0-9]\..*[0-9]).*"' | awk '{print substr($3,2,length($3)-2)}' | awk '{print substr($1, 3, 3)}' | sed -e 's;\.;;g')
        if [ "${VERSION}" -lt "80" ]; then
            # Check some easily accessible MIN/MAX params for JVM mem usage
            if [ "x${JAVA_PERM_MEM}" != "x" ]; then
                DEFAULT_JAVA_OPTS="${DEFAULT_JAVA_OPTS} -XX:PermSize=${JAVA_PERM_MEM}"
            fi
            if [ "x${JAVA_MAX_PERM_MEM}" != "x" ]; then
                DEFAULT_JAVA_OPTS="${DEFAULT_JAVA_OPTS} -XX:MaxPermSize=${JAVA_MAX_PERM_MEM}"
            fi
        fi
        DEFAULT_JAVA_OPTS="-server ${DEFAULT_JAVA_OPTS} -Dcom.sun.management.jmxremote"
    elif [ "${JVM_VENDOR}" = "IBM" ]; then
        if ${os400}; then
            DEFAULT_JAVA_OPTS="${DEFAULT_JAVA_OPTS}"
        elif ${aix}; then
            DEFAULT_JAVA_OPTS="-Xverify:none -Xdump:heap -Xlp ${DEFAULT_JAVA_OPTS}"
        else
            DEFAULT_JAVA_OPTS="-Xverify:none ${DEFAULT_JAVA_OPTS}"
        fi
    fi

    # Add the jars in the lib dir
    for file in "${KARAF_HOME}"/lib/boot/*.jar
    do
        if [ -z "${CLASSPATH}" ]; then
            CLASSPATH="${file}"
        else
            CLASSPATH="${CLASSPATH}:${file}"
        fi
    done

    DEFAULT_JAVA_DEBUG_PORT="5005"
    if [ "x${JAVA_DEBUG_PORT}" = "x" ]; then
        JAVA_DEBUG_PORT="${DEFAULT_JAVA_DEBUG_PORT}"
    fi
    DEFAULT_JAVA_DEBUG_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=${JAVA_DEBUG_PORT}"

    ##
    ## TODO: Move to conf/profiler/yourkit.{sh|cmd}
    ##
    # Uncomment to enable YourKit profiling
    #DEFAULT_JAVA_DEBUG_OPTS="-Xrunyjpagent"
}

checkRootInstance() {
   ROOT_INSTANCE_RUNNING=false
   if [ -f "${KARAF_HOME}/instances/instance.properties" ];
   then
      ROOT_INSTANCE_PID=$(sed -n -e '/item.0.pid/ s/.*\= *//p' "${KARAF_HOME}/instances/instance.properties")
      ROOT_INSTANCE_NAME=$(sed -n -e '/item.0.name/ s/.*\= *//p' "${KARAF_HOME}/instances/instance.properties")
      if [ "${ROOT_INSTANCE_PID}" -ne "0" ]; then
          if ps p "${ROOT_INSTANCE_PID}" > /dev/null
          then
              ROOT_INSTANCE_RUNNING=true
          fi
      fi
   fi
}

init() {
    # Determine if there is special OS handling we must perform
    detectOS

    # Unlimit the number of file descriptors if possible
    unlimitFD

    # Locate the Karaf home directory
    locateHome

    # Locate the Karaf base directory
    locateBase

    # Locate the Karaf data directory
    locateData

    # Locate the Karaf etc directory
    locateEtc

    # Setup the native library path
    setupNativePath

    # Locate the Java VM to execute
    locateJava

    # Determine the JVM vendor
    detectJVM

    # Determine the JVM version >= 1.6
    checkJvmVersion

    # Check if a root instance is already running
    checkRootInstance

    # Setup default options
    setupDefaults

    # Install debug options
    setupDebugOptions

}

run() {
    OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=true"
    MAIN=org.apache.karaf.main.Main
    CHECK_ROOT_INSTANCE_RUNNING=true
    while [ "${1}" != "" ]; do
        case ${1} in
            'clean')
                rm -rf "${KARAF_DATA:?}"/{.[^.],.??*,*}
                shift
                ;;
            'debug')
                if [ "x${JAVA_DEBUG_OPTS}" = "x" ]; then
                    JAVA_DEBUG_OPTS="${DEFAULT_JAVA_DEBUG_OPTS}"
                fi
                JAVA_OPTS="${JAVA_DEBUG_OPTS} ${JAVA_OPTS}"
                shift
                ;;
            'status')
                MAIN=org.apache.karaf.main.Status
                CHECK_ROOT_INSTANCE_RUNNING=false
                shift
                ;;
            'stop')
                MAIN=org.apache.karaf.main.Stop
                CHECK_ROOT_INSTANCE_RUNNING=false
                shift
                ;;
            'console')
                CHECK_ROOT_INSTANCE_RUNNING=false
                shift
                ;;
            'server')
                OPTS="-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true"
                shift
                ;;
            'daemon')
                OPTS="-Dkaraf.startLocalConsole=false -Dkaraf.startRemoteShell=true"
                KARAF_DAEMON="true"
                KARAF_EXEC="exec"
                shift
                ;;
            'client')
                OPTS="-Dkaraf.startLocalConsole=true -Dkaraf.startRemoteShell=false"
                CHECK_ROOT_INSTANCE_RUNNING=false
                shift
                ;;
            *)
                break
                ;;
        esac
    done

    JAVA_ENDORSED_DIRS="${JAVA_HOME}/jre/lib/endorsed:${JAVA_HOME}/lib/endorsed:${KARAF_HOME}/lib/endorsed"
    JAVA_EXT_DIRS="${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:${KARAF_HOME}/lib/ext"
    if ${cygwin}; then
        KARAF_HOME=$(cygpath --path --windows "${KARAF_HOME}")
        KARAF_BASE=$(cygpath --path --windows "${KARAF_BASE}")
        KARAF_DATA=$(cygpath --path --windows "${KARAF_DATA}")
        KARAF_ETC=$(cygpath --path --windows "${KARAF_ETC}")
        if [ ! -z "${CLASSPATH}" ]; then
            CLASSPATH=$(cygpath --path --windows "${CLASSPATH}")
        fi
        JAVA_HOME=$(cygpath --path --windows "${JAVA_HOME}")
        JAVA_ENDORSED_DIRS=$(cygpath --path --windows "${JAVA_ENDORSED_DIRS}")
        JAVA_EXT_DIRS=$(cygpath --path --windows "${JAVA_EXT_DIRS}")
    fi
    cd "${KARAF_BASE}"

    if [ -z "${KARAF_EXEC}" ]; then
        KARAF_EXEC=""
    fi

    while true; do
        # When users want to update the lib version of, they just need to create
        # a lib.next directory and on the new restart, it will replace the current lib directory.
        if [ -d "${KARAF_HOME:?}/lib.next" ] ; then
            echo "Updating libs..."
            rm -rf "${KARAF_HOME:?}/lib"
            mv -f "${KARAF_HOME:?}/lib.next" "${KARAF_HOME}/lib"
        fi

        # Ensure the log directory exists
        # We may need to have a place to redirect stdout/stderr
        if [ ! -d "${KARAF_DATA}/log" ]; then
            mkdir -p "${KARAF_DATA}/log"
        fi

        if [ "${ROOT_INSTANCE_RUNNING}" = "false" ] || [ "${CHECK_ROOT_INSTANCE_RUNNING}" = "false" ] ; then
            ${KARAF_EXEC} "${JAVA}" ${JAVA_OPTS} \
                -Djava.endorsed.dirs="${JAVA_ENDORSED_DIRS}" \
                -Djava.ext.dirs="${JAVA_EXT_DIRS}" \
                -Dkaraf.instances="${KARAF_HOME}/instances" \
                -Dkaraf.home="${KARAF_HOME}" \
                -Dkaraf.base="${KARAF_BASE}" \
                -Dkaraf.data="${KARAF_DATA}" \
                -Dkaraf.etc="${KARAF_ETC}" \
                -Dkaraf.restart.jvm.supported=true \
                -Djava.io.tmpdir="${KARAF_DATA}/tmp" \
                -Djava.util.logging.config.file="${KARAF_BASE}/etc/java.util.logging.properties" \
                ${KARAF_SYSTEM_OPTS} \
                ${KARAF_OPTS} \
                ${OPTS} \
                -classpath "${CLASSPATH}" \
                ${MAIN} "$@"
        else
            die "There is a Root instance already running with name ${ROOT_INSTANCE_NAME} and pid ${ROOT_INSTANCE_PID}"
        fi

        KARAF_RC=$?
        if [ ${KARAF_DAEMON} ] ; then
            exit ${KARAF_RC}
        else
            if [ "${KARAF_RC}" -eq 10 ]; then
               echo "Restarting JVM..."
            else
               exit ${KARAF_RC}
            fi
        fi
    done
}

main() {
    init
    run "$@"
}

main "$@"
