#!/bin/sh -e

# Licensed to Crate.io GmbH ("Crate") under one or more contributor
# license agreements.  See the NOTICE file distributed with this work for
# additional information regarding copyright ownership.  Crate 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.
#
# However, if you have executed another commercial license agreement
# with Crate these terms will supersede the license and you may use the
# software solely pursuant to the terms of the relevant commercial agreement.


# OPTIONS:
#   -v: print crate version to stdout
#   -h: print usage information
#   -D: set a java system property value
#   -C: set a Crate property value
#   -X: set a nonstandard java option

# CONTROLLING STARTUP:
#
# This script relies on few environment variables to determine startup
# behavior, those variables are:
#
#   CRATE_JAVA_OPTS -- External Java Opts on top of the defaults set
#
# Optionally, exact memory values can be set using the following values, note,
# they can still be set using the `CRATE_JAVA_OPTS`. Sample format include "512m", and "10g".
#
#   CRATE_HEAP_SIZE -- Sets both the minimum and maximum memory to allocate (recommended)
#

CDPATH=""
SCRIPT="$0"

# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path.
while [ -h "$SCRIPT" ] ; do
  ls=$(ls -ld "$SCRIPT")
  # Drop everything prior to ->
  link=$(expr "$ls" : '.*-> \(.*\)$')
  if expr "$link" : '/.*' > /dev/null; then
    SCRIPT="$link"
  else
    SCRIPT=$(dirname "$SCRIPT")/"$link"
  fi
done

# determine crate home
CRATE_HOME=$(dirname "$SCRIPT")/..

# make CRATE_HOME absolute
CRATE_HOME=$(cd "$CRATE_HOME"; pwd)


if [ "x$CRATE_CLASSPATH" != "x" ]; then
    cat >&2 << EOF
Error: Don't modify the classpath with CRATE_CLASSPATH.
Add plugins and their dependencies into the plugins/ folder instead.
EOF
    exit 1
fi

for libname in "$CRATE_HOME"/lib/*.jar; do
    if [ "x$CRATE_CLASSPATH" != "x" ]; then
        CRATE_CLASSPATH="$CRATE_CLASSPATH:$libname"
    else
        CRATE_CLASSPATH="$libname"
    fi
done

if [ "$CRATE_MIN_MEM" = "" ]; then
    CRATE_MIN_MEM=256m
fi
if [ "x$CRATE_HEAP_SIZE" != "x" ]; then
    CRATE_MIN_MEM=$CRATE_HEAP_SIZE
    CRATE_MAX_MEM=$CRATE_HEAP_SIZE
fi

# min and max heap sizes should be set to the same value to avoid
# stop-the-world GC pauses during resize, and so that we can lock the
# heap in memory on startup to prevent any of it from being swapped
# out.
JAVA_OPTS="$JAVA_OPTS -Xms${CRATE_MIN_MEM}"
if [ "x$CRATE_MAX_MEM" != "x" ]; then
    JAVA_OPTS="$JAVA_OPTS -Xmx${CRATE_MAX_MEM}"
fi

# new generation
if [ "x$CRATE_HEAP_NEWSIZE" != "x" ]; then
    JAVA_OPTS="$JAVA_OPTS -Xmn${CRATE_HEAP_NEWSIZE}"
fi

# max direct memory
if [ "x$CRATE_DIRECT_SIZE" != "x" ]; then
    JAVA_OPTS="$JAVA_OPTS -XX:MaxDirectMemorySize=${CRATE_DIRECT_SIZE}"
fi

# set to headless, just in case
JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true"

# Force the JVM to use IPv4 stack
if [ "x$CRATE_USE_IPV4" != "x" ]; then
  JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true"
fi

## GC configuration
JAVA_OPTS="$JAVA_OPTS -XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30"

# GC logging options
# Set CRATE_DISABLE_GC_LOGGING=1 to disable GC logging
if [ "$CRATE_DISABLE_GC_LOGGING" = "" ]; then
  # GC log directory needs to be set explicitly by packages
  # GC logging requires 16x64mb = 1g of free disk space
  GC_LOG_DIR=${CRATE_GC_LOG_DIR:-"$CRATE_HOME/logs"};
  GC_LOG_SIZE=${CRATE_GC_LOG_SIZE:-"64m"}
  GC_LOG_FILES=${CRATE_GC_LOG_FILES:-"16"}

  # Ensure that the directory for the log file exists: the JVM will not create it.
  if ! test -d "$GC_LOG_DIR" || ! test -x "$GC_LOG_DIR"; then
    cat >&2 << EOF
ERROR: Garbage collection log directory '$GC_LOG_DIR' does not exist or is not accessible.
EOF
    exit 1
  fi
  LOGGC="$GC_LOG_DIR/gc.log"

  if [ -x "$JAVA_HOME/bin/java" ]; then
      JAVA="$JAVA_HOME/bin/java"
  else
      JAVA=java
  fi
  export JAVA
  JAVA_OPTS="$JAVA_OPTS -Xlog:gc*,gc+age=trace,safepoint:file=\"${LOGGC}\":utctime,pid,tags:filecount=${GC_LOG_FILES},filesize=${GC_LOG_SIZE}"
fi

# Disables explicit GC
JAVA_OPTS="$JAVA_OPTS -XX:+DisableExplicitGC"

# Prevent denial of service attacks via SSL renegotiation
JAVA_OPTS="$JAVA_OPTS -Djdk.tls.rejectClientInitiatedRenegotiation=true"

# Ensure UTF-8 encoding by default (e.g. filenames)
JAVA_OPTS="$JAVA_OPTS -Dfile.encoding=UTF-8"

# Use our provided JNA always versus the system one
JAVA_OPTS="$JAVA_OPTS -Djna.nosys=true"

# log4j options
JAVA_OPTS="$JAVA_OPTS -Dlog4j.shutdownHookEnabled=false -Dlog4j2.disable.jmx=true -Dlog4j.skipJansi=true"

# Disable netty recycler
JAVA_OPTS="$JAVA_OPTS -Dio.netty.recycler.maxCapacityPerThread=0"

# Lucene uses native access and vector module
JAVA_OPTS="$JAVA_OPTS --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.vector"

# Dump heap on OOM
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError"
if [ "x$CRATE_HEAP_DUMP_PATH" != "x" ]; then
    JAVA_OPTS="$JAVA_OPTS -XX:HeapDumpPath=$CRATE_HEAP_DUMP_PATH"
fi

JAVA="$CRATE_HOME/jdk/bin/java"

if [ -z "$CRATE_CLASSPATH" ]; then
    echo "ERROR: CRATE_CLASSPATH is not defined or empty" >&2
    exit 1
fi

# Special-case path variables.
case $(uname) in
    CYGWIN*)
      CRATE_CLASSPATH=$(cygpath -p -w "$CRATE_CLASSPATH")
      CRATE_HOME=$(cygpath -p -w "$CRATE_HOME")
    ;;
esac

launch_service()
{
    javaProperties=$1

    case "$2" in
        *"-Cpath.home="*)
            props="$2" ;;
        *)
            props="\"-Cpath.home=$CRATE_HOME\" $2";;
    esac

    eval exec "\"$JAVA\"" $JAVA_OPTS $CRATE_JAVA_OPTS $javaProperties -cp "\"$CRATE_CLASSPATH\"" \
          io.crate.bootstrap.CrateDB $props
    execval=$?

    return $execval
}

# Parse any command line options.
while getopts "vhD:X:C:" opt; do
    case $opt in
        v)
            "$JAVA" $JAVA_OPTS $CRATE_JAVA_OPTS -cp "$CRATE_CLASSPATH" $props \
                    org.elasticsearch.Version
            exit 0
        ;;
        h)
            echo "CrateDB is a shared nothing, fully searchable document oriented cluster datastore."
            echo ""
            echo "  Usage: $(basename "$0") [OPTION]..."
            echo "         starts a new CrateDB instance"
            echo ""
            echo "General options:"
            echo "  -h            print usage information"
            echo "  -v            print version information"
            echo "  -C            set a CrateDB setting"
            echo "  -D            set a java system property value"
            echo "  -X            set a nonstandard java option"
            exit 0
        ;;
        D)
            javaProperties="$javaProperties \"-D$OPTARG\""
        ;;
        C)
            properties="$properties \"-C$OPTARG\""
        ;;
        X)
            properties="$properties \"-X$OPTARG\""
        ;;
        *)
            echo "Error parsing arguments!" >&2
            exit 1
        ;;
    esac
done

# Start up the service
launch_service "$javaProperties" "$properties"

exit $?
