#!/bin/sh -
#
# Wrapper script for starting a JRE within OpenNMS.
# DJ Gregor
# Copyright (c) Daniel J. Gregor, Jr.
#    parts Copyright (c) The OpenNMS Group
# Tuesday, August 31, 2004
#
# Parts based on parseMib.sh, a Java MIB parser by David Hustace
# Copyright (c) The OpenNMS Group
# Friday, February 13, 2004
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# For more information contact:
#      OpenNMS Licensing       <license@opennms.org>
#      http://www.opennms.org/
#      http://www.opennms.com/
#
# put an '@' here so we don't break maven substition

opennms_home='${install.dir}'
if [ ! -d "${opennms_home}" ]; then
    opennms_home="$OPENNMS_HOME"
fi

searchdirs="/usr/lib/jvm /usr/java /System/Library/Java/JavaVirtualMachines /Library/Java/JavaVirtualMachines /Library/Java/Home /usr /opt"

dryrun=0

java_conf="$opennms_home/etc/java.conf"
basename="`basename $0`"

die(){
    echo "${basename}: $*" >&2
    exit 1
}
warn(){
    echo "${basename}: $*" >&2
}
tell(){
    echo "${basename}: $*" >&3
}


#
# Function: printHelp()
# Give the user some basic help.
#
printHelp() {
    echo "usage: $basename [-f] [-n] [-q]"
    echo "       {-s | -S <JRE path> | -c | -r <java options> | -h}"
    echo ""
    echo "Exactly one of the following options is required:"
    echo ""
    echo "  -r <java options>"
    echo "      Exec the configured Java Runtime Environment (JRE) with"
    echo "      with <java options>."
    echo ""
    echo "  -s  Search for a suitable JRE and configure it, if found."
    echo ""
    echo "  -S <JRE path>"
    echo "      Store the JRE at <JRE path> as the configured JRE."
    echo ""
    echo "  -c  Check that the configured JRE is appropriate."
    echo ""
    echo "  -h  Print this help information."
    echo ""
    echo "Optional options:"
    echo ""
    echo "  -f  Force mode.  Ignores the JRE version check.  This option"
    echo "      only makes sense with the -r and -S options."
    echo ""
    echo "  -n  Dry run mode.  Don't change the configured JRE (-s or -S"
    echo "      options) or execute a JRE (-r option)."
    echo ""
    echo "  -q  Quiet mode.  Be fairly quiet."
    echo ""
    echo "The \"-s\" option chooses a JRE based on these factors:"
    echo ""
    echo "  1) If the JAVA_HOME environment variable is set and points at"
    echo "     an appropriate JRE, it \$JAVA_HOME/bin/java will be used."
    echo ""
    echo "  2) If \"which java\" returns an appropriate JRE, it will be used."
    echo ""
    echo "  3) Lastly, a few directories are searched to find an"
    echo "     appropriate JRE: \"$searchdirs\"."
    echo ""
    echo "If none of the above options work or if they don't find the JRE"
    echo "you want OpenNMS to use, use \"-S <JRE>\" to specify the JRE you"
    echo "want OpenNMS to use."
}

#
# Function: javaCheck()
# Requires: varible $JP (java path)
# This function verifies the version of the java
# command found in the javaPath() and the
# javaFind() functions.
#
javaCheck() {
        if [ -z "$OPENJDK_MINIMUM_BUILD" ]; then
                OPENJDK_MINIMUM_BUILD=14
        fi

    ret="`$JP -version 2>&1 | grep ' version \"' | sed 's/^.* version \"//;s/\".*$//'`"
    if echo "$ret" | egrep '^1\.[8-9]\.' > /dev/null; then
        # Check for Sun Java
        if $JP -version 2>&1 | egrep '^(Diablo )?Java\(TM\)' > /dev/null; then
            return 0
        fi
        if $JP -version 2>&1 | egrep 'OpenJDK .* VM' >/dev/null; then
                build="`$JP -version 2>&1 | egrep '^OpenJDK .* VM' | sed -e 's,^.*build ,,' -e 's,\..*$,,'`"
                if [ $build -ge $OPENJDK_MINIMUM_BUILD ]; then
                        return 0
                else
                        tell "OpenNMS is only known to work with OpenJDK build $OPENJDK_MINIMUM_BUILD and later."
                        return 1
                fi
        fi
        
        if [ $run -eq 0 ] && [ $force -eq 0 ]; then
            tell "$JP is not a Sun JVM and only Sun JVMs are supported."
            tell "You can use the '-f' option to ignore this check, but you will be on your own for support."
            return 1
        else
            warn "$JP is not a supported JVM.  Using it anyways."
            return 0
        fi
    else
        tell "$JP is not Java 1.8 or newer."
        return 1
    fi
}

#
# Function: javaPaths()
# This function attempts to find a path
# to a java executable.  It searchs for
# files (hopefully directories) that match
# the regex "^j2*" or "^java$" and appends "/bin/java".
# It then tests to see if this there is an
# executable file by this name.  This is not
# foolproof but should get us close.
#
javaPaths() {
        if [ -x "${opennms_home}/bin/find-java.sh" ]; then
                found="$("${opennms_home}/bin/find-java.sh" 1.8.0 1.8.9999)"
                if [ -d "${found}" ] && [ -x "${found}/bin/java" ]; then
                        JP="${found}/bin/java"
                        return 0
                fi
        fi

        warn "${opennms_home}/bin/find-java.sh failed to find a valid JDK. Falling back to the old algorithm."
        for searchdir in $searchdirs; do
                if [ ! -d "$searchdir" ]; then
                        continue;
                fi

                # We search "j2*" for the Sun-supplied Java packages ("j2sdk"),
                # "java" for the Java installation shipped with SuSE, and "Home"
                # to catch /Library/Java/Home on Mac OS X.
                jdirs="`find \"$searchdir\" -maxdepth 3 \\( -name \"j2*\" -o -name \"jdk*\" -o -name \"java\" -o -name \"Home\" \\) -print 2>/dev/null`"
                for jdir in $searchdir/java/default $searchdir/java/latest $jdirs; do
                        if [ -x $jdir/bin/java ]; then
                                JP=$jdir/bin/java
                                javaCheck && return 0
                        fi
                done
        done
        return 1
}

#
# Function: checkJavaHome()
# See if $JAVA_HOME/bin/java is a good JRE.
#
checkJavaHome() {
    tell "Checking for an appropriate JRE in JAVA_HOME..."
    if [ x"$JAVA_HOME" = x"" ]; then
        tell "skipping... JAVA_HOME not set"
        return 1
    fi

    if [ ! -d "$JAVA_HOME" ]; then
        tell "skipping... JAVA_HOME (\"$JAVA_HOME\") is not a directory"
        return 1
    fi
    
    if [ ! -x "$JAVA_HOME/bin/java" ]; then
        tell "skipping... \"$JAVA_HOME/bin/java\" does not exist or is not executable"
        return 1
    fi

    JP=$JAVA_HOME/bin/java
    if javaCheck; then
        tell "found: \"$JAVA_HOME/bin/java\" is an appropriate JRE"
        return 0
    else
        tell "\"$JAVA_HOME/bin/java\" is not an appropriate JRE"
        return 1
    fi
}

#
# Function: checkPath()
# Checks for a JRE in our path, using which(1).
#
checkPath() {
    JP="`which java`"
    if [ $? -eq 1 ]; then
        tell "did not find a JRE in user's path"
        return 1
    fi

    tell "Checking JRE in user's path: \"$JP\"..."
    if javaCheck; then
        tell "found an appropriate JRE in user's path: \"$JP\""
        return 0
    else
        tell "did not find an appropriate JRE in user's path: \"$JP\""
        return 1
    fi
}

#
# Function: findJava()
# A wrapper around javaPaths to give the user a clue about what is going on.
# 
findJava() {
    tell "searching for a good JRE..."
    if javaPaths; then
        tell "found a good JRE in \"$JP\""
        return 0
    else
        tell "did not find an appropriate JRE while searching for one"
        return 1
    fi
}


#
# Function: javaFind()
# This function first checks JAVA_HOME, then
# the current path for a java executable and then
# searches for one by calling javaPaths().
#
javaFind() {
    tell "Looking for an appropriate JRE..."

    checkJavaHome && return 0
    checkPath && return 0
    findJava && return 0

    warn "Could not find an appropriate JRE."
    warn "You can set a particular JRE by using"
    warn "\"$basename -S <JRE>\"."

    return 1
}

#
# Function: checkConfiguredJava()
# Verifies that the java configured in $java_conf looks good.
# Complains and returns 1 if it doesn't look good.
# Is quiet and returns 0 if things look good.
#
checkConfiguredJava() {
    local verbose="$1"
    mantra="run \"$opennms_home/bin/runjava -s\" to setup java.conf"

    if [ ! -f $java_conf ]; then
        warn "error: $java_conf file not found"
        warn "$mantra"

        return 1
    fi

    JP="`cat $java_conf`"

    if [ ! -x $JP ]; then
        warn "error: configured Java runtime environment not found"
        warn "\"$JP\" does not exist or is not executable"
        warn "$mantra"

        return 1
    fi

    javaCheck

    if [ $? -ne 0 ]; then
        warn "error: bad version or vendor for configured Java runtime environment"
        warn "\"$JP -version\" does not report that is version 1.8+ and a compatible JDK."
        warn "$mantra"

        return 1
    fi

    if [ $verbose ]; then
        echo "$JP"
    fi

    return 0
}

#
# Function: runJava()
# Verifies that we have an appropriate JRE configured in $java_conf and
# execs it with the command-line arguments passed to us.
#
runJava() {
    checkConfiguredJava
    if [ $? -ne 0 ]; then
        if [ $force -gt 0 ]; then
            warn "Ignoring Java warning due to '-f' option being used"
        else
            return 1
        fi
    fi

    if [ $dryrun -eq 0 ]; then
        add_ld_path
        exec "$JP" "$@"
    else
        tell "Dry run mode is set.  Would have executed: $JP $*"
    fi
}

#
# Function: add_ld_path()
# Adds $opennms_home/lib to the linker path.
#
add_ld_path () {
    case "`uname`" in
        Darwin)
            if echo "$DYLD_LIBRARY_PATH" | \
                grep -v "$opennms_home/lib" >/dev/null; then
                DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:$opennms_home/lib:$opennms_home/lib/mac"
                export DYLD_LIBRARY_PATH
            fi
            return
            ;;

        Linux)
            if echo "$LD_LIBRARY_PATH" | \
                grep -v "$opennms_home/lib" >/dev/null; then
                LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$opennms_home/lib"
                export LD_LIBRARY_PATH
            fi

            case "`uname -p`" in
                x86_64)
                    if echo "$LD_LIBRARY_PATH" | \
                        grep -v "$opennms_home/lib/linux64" >/dev/null; then
                        LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$opennms_home/lib/linux64"
                        export LD_LIBRARY_PATH
                    fi
                    ;;

                    *)
                    if echo "$LD_LIBRARY_PATH" | \
                        grep -v "$opennms_home/lib/linux32" >/dev/null; then
                        LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$opennms_home/lib/linux32"
                        export LD_LIBRARY_PATH
                    fi
                    ;;
            esac

            return
            ;;

        *)
            if echo "$LD_LIBRARY_PATH" | \
                grep -v "$opennms_home/lib" >/dev/null; then
                LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$opennms_home/lib"
                export LD_LIBRARY_PATH
            fi
            return
            ;;
    esac
    
    return 1
}


#
# Function: setupJava()
# Calls javaFind and if successful, stores the location of the JRE in
# $java_conf.
#
setupJava() {
    if javaFind; then
        saveJava
        return $?
    else
        return 1
    fi
}

#
# Function: setJava()
# Calls javaFind and if successful, stores the location of the JRE in
# $java_conf.
#
setJava() {
    tell "checking specified JRE: \"$JP\"..."
    if [ ! -x "$JP" ]; then
        die "specified JRE \"$JP\" does not exist or is not executable"
    fi
    javaCheck
    if [ $? -ne 0 ]; then
        if [ $force -gt 0 ]; then
            warn "JRE is not an appropriate version and/or vendor"
            warn "Ignoring Java warning due to '-f' option being used"
        else
            die "specified JRE \"$JP\" is not an appropriate JRE"
        fi
    else
        tell "specified JRE is good."
    fi

    saveJava
    return $?
}

saveJava() {
    if [ $dryrun -eq 0 ]; then
        echo "$JP" > $java_conf
        if [ $? -ne 0 ]; then
            warn "error saving JRE to configuration file"
            warn "configuration file: $java_conf"
            die "exiting..."
        fi
        tell "value of \"$JP\" stored in configuration file"
    else
        tell "value of \"$JP\" would have been stored if not in dryrun mode"
    fi

    return 0
}

parseArgsAndDoStuff() {
    exclusive=0
    
    verbose=0
    check=0
    force=0
    quiet=0
    run=0
    search=0
    location=""
    
    while getopts vcfhnqrsS: c
    do
      case $c in
          c)
              check=1
              exclusive=`expr $exclusive + 1`
              ;;
    
          f)
              force=1
          ;;
    
          h)
              printHelp
              exit 0
              ;;
    
          n)
              dryrun=1
              ;;
    
          q)
              quiet=1
              ;;
    
          r)
              run=1
              exclusive=`expr $exclusive + 1`
              ;;
    
          s)
              search=1
              exclusive=`expr $exclusive + 1`
              ;;

          S)
              location="$OPTARG"
              exclusive=`expr $exclusive + 1`
              ;;
    
          v)
              verbose=1
              ;;
    
          \?)
              die "Invalid option.  Use \"-h\" for help."
              ;;
      esac
    done
    shift `expr $OPTIND - 1`
    
    if [ $exclusive -eq 0 ]; then
        warn "You must choose one of the command-line options."
        die "Use \"-h\" for help."
    fi
    
    if [ $exclusive -gt 1 ]; then
        warn "You must choose only one of the command-line options."
        die "Use \"-h\" for help."
    fi
    
    if [ $quiet -gt 0 ]; then
        exec 3>/dev/null
    else
        exec 3>&1
    fi
    
    if [ $check -gt 0 ]; then
        checkConfiguredJava $verbose
        exit $?
    fi
    
    if [ x"$location" != x"" ]; then
        JP="$location"
        setJava
        exit $?
    fi
    
    if [ $run -gt 0 ]; then
        runJava "$@"
        exit $?
    fi
    
    if [ $search -gt 0 ]; then
        setupJava
        exit $?
    fi
    
    die "internal error... got to end of script and shouldn't have"
}

parseArgsAndDoStuff "$@"
