#!/usr/bin/env bash set -euo pipefail # TMS runtime script for jar deployment. # Commands: # ./scripts/tms.sh start # ./scripts/tms.sh stop # ./scripts/tms.sh restart # ./scripts/tms.sh status # # External config: # ${APP_HOME}/config/application.yml # ${APP_HOME}/config/application-${SPRING_PROFILES_ACTIVE}.yml DEFAULT_APP_HOME="/home/tms" APP_HOME="${APP_HOME:-${DEFAULT_APP_HOME}}" APP_NAME="${APP_NAME:-tms-framework}" RUN_DIR="${RUN_DIR:-${APP_HOME}/run}" LOG_DIR="${LOG_DIR:-${APP_HOME}/logs}" CONF_DIR="${CONF_DIR:-${APP_HOME}/config}" PID_FILE="${PID_FILE:-${RUN_DIR}/${APP_NAME}.pid}" OUT_LOG="${OUT_LOG:-${LOG_DIR}/${APP_NAME}.out.log}" ERR_LOG="${ERR_LOG:-${LOG_DIR}/${APP_NAME}.err.log}" SPRING_PROFILES_ACTIVE="${SPRING_PROFILES_ACTIVE:-prod}" JAVA_BIN="${JAVA_BIN:-java}" JAVA_OPTS="${JAVA_OPTS:- -Xms512m -Xmx1024m}" ALLOW_MISSING_EXTERNAL_CONFIG="${ALLOW_MISSING_EXTERNAL_CONFIG:-false}" JAR_PATH="${JAR_PATH:-${APP_HOME}/${APP_NAME}.jar}" ensure_dirs() { mkdir -p "${RUN_DIR}" "${LOG_DIR}" "${CONF_DIR}" } resolve_jar_path() { if [[ -f "${JAR_PATH}" ]]; then return 0 fi local found found="$(ls -1 "${APP_HOME}/target/${APP_NAME}"-*.jar 2>/dev/null | grep -vE '(sources|javadoc|original)' | head -n1 || true)" if [[ -n "${found}" ]]; then JAR_PATH="${found}" return 0 fi echo "[FATAL] jar not found. set JAR_PATH or place jar at ${APP_HOME}/${APP_NAME}.jar" exit 1 } check_external_config() { local base_cfg="${CONF_DIR}/application.yml" if [[ "${ALLOW_MISSING_EXTERNAL_CONFIG}" != "true" && ! -f "${base_cfg}" ]]; then echo "[FATAL] external config missing: ${base_cfg}" echo "Copy template: cp ${APP_HOME}/config/application.yml.example ${base_cfg}" exit 1 fi } is_running() { if [[ ! -f "${PID_FILE}" ]]; then return 1 fi local pid pid="$(cat "${PID_FILE}")" if [[ -z "${pid}" ]]; then return 1 fi if kill -0 "${pid}" >/dev/null 2>&1; then return 0 fi return 1 } start_app() { ensure_dirs resolve_jar_path check_external_config if is_running; then echo "[INFO] ${APP_NAME} already running, pid=$(cat "${PID_FILE}")" return 0 fi local cfg_location cfg_location="optional:file:${CONF_DIR}/" echo "[INFO] starting ${APP_NAME}" echo "[INFO] app home: ${APP_HOME}" echo "[INFO] jar path: ${JAR_PATH}" echo "[INFO] profile: ${SPRING_PROFILES_ACTIVE}" echo "[INFO] external config dir: ${CONF_DIR}" # shellcheck disable=SC2086 nohup "${JAVA_BIN}" ${JAVA_OPTS} \ -jar "${JAR_PATH}" \ --spring.profiles.active="${SPRING_PROFILES_ACTIVE}" \ --spring.config.additional-location="${cfg_location}" \ >"${OUT_LOG}" 2>"${ERR_LOG}" & local pid="$!" echo "${pid}" > "${PID_FILE}" sleep 1 if kill -0 "${pid}" >/dev/null 2>&1; then echo "[INFO] started, pid=${pid}" echo "[INFO] out log: ${OUT_LOG}" echo "[INFO] err log: ${ERR_LOG}" else echo "[ERROR] start failed, check logs" rm -f "${PID_FILE}" exit 1 fi } stop_app() { if ! is_running; then echo "[INFO] ${APP_NAME} not running" rm -f "${PID_FILE}" || true return 0 fi local pid pid="$(cat "${PID_FILE}")" echo "[INFO] stopping ${APP_NAME}, pid=${pid}" kill "${pid}" >/dev/null 2>&1 || true local i for i in $(seq 1 20); do if ! kill -0 "${pid}" >/dev/null 2>&1; then rm -f "${PID_FILE}" || true echo "[INFO] stopped" return 0 fi sleep 1 done echo "[WARN] graceful stop timeout, force kill pid=${pid}" kill -9 "${pid}" >/dev/null 2>&1 || true rm -f "${PID_FILE}" || true echo "[INFO] stopped(force)" } status_app() { if is_running; then echo "[INFO] ${APP_NAME} is running, pid=$(cat "${PID_FILE}")" return 0 fi echo "[INFO] ${APP_NAME} is not running" return 1 } usage() { cat </tms-framework.jar CONF_DIR default: /config RUN_DIR default: /run LOG_DIR default: /logs SPRING_PROFILES_ACTIVE default: prod JAVA_BIN default: java JAVA_OPTS default: -Xms512m -Xmx1024m ALLOW_MISSING_EXTERNAL_CONFIG default: false EOF } cmd="${1:-}" case "${cmd}" in start) start_app ;; stop) stop_app ;; restart) stop_app start_app ;; status) status_app ;; *) usage exit 1 ;; esac