|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# SPDX-License-Identifier: MPL-2.0 |
| 4 | +# SPDX-FileCopyrightText: 2025 Tony Germano and Mitch Gaffigan |
| 5 | +# |
| 6 | + |
| 7 | +# ============================================================================= |
| 8 | +# Open Integration Engine Server Launcher Script |
| 9 | +# |
| 10 | +# Description: |
| 11 | +# This script is the main launcher for the Open Integration Engine (OIE) |
| 12 | +# server. It prepares the Java environment and executes the server launcher |
| 13 | +# JAR file. |
| 14 | +# |
| 15 | +# The script automatically finds a compatible Java runtime (version 17+ by |
| 16 | +# default) by searching for a valid executable in the following priority order: |
| 17 | +# 1. The OIE_JAVA_PATH environment variable. |
| 18 | +# 2. The -java-cmd directive in the oieserver.vmoptions file or included |
| 19 | +# .vmoptions files. Must specify the path to the 'java' executable. |
| 20 | +# This is the preferred way to declare the desired version for running |
| 21 | +# the server and can be overridden by OIE_JAVA_PATH. Can be a relative |
| 22 | +# path from the location of this script. |
| 23 | +# 3. The JAVA_HOME environment variable. |
| 24 | +# 4. The 'java' command available in the system's PATH. |
| 25 | +# |
| 26 | +# It also parses the 'oieserver.vmoptions' file to configure JVM options, |
| 27 | +# system properties (-D...), and classpath modifications. |
| 28 | +# |
| 29 | +# Usage: |
| 30 | +# ./oieserver.sh [app-arguments] |
| 31 | +# |
| 32 | +# All [app-arguments] are passed directly to the underlying Java application |
| 33 | +# (com.mirth.connect.server.launcher.MirthLauncher). |
| 34 | +# |
| 35 | +# Configuration via Environment Variables: |
| 36 | +# OIE_JAVA_PATH - (Highest priority) Set the full path to the 'java' |
| 37 | +# executable to be used. Can be a relative path from the |
| 38 | +# location of this script or a tilde path |
| 39 | +# (e.g., ~/path/to/java). |
| 40 | +# JAVA_HOME - Set the path to the root of a Java installation. The |
| 41 | +# script will look for 'bin/java' within this path. |
| 42 | +# ============================================================================= |
| 43 | + |
| 44 | +APP_ARGS=("$@") |
| 45 | +MIN_JAVA_VERSION=17 |
| 46 | + |
| 47 | +# Set OIE_HOME to the script directory |
| 48 | +OIE_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" |
| 49 | +CLASSPATH="$OIE_HOME/mirth-server-launcher.jar" |
| 50 | +VMOPTIONS=() |
| 51 | +# This will hold the validated path to the Java executable. It is intentionally left empty for now. |
| 52 | +FINAL_JAVA_CMD="" |
| 53 | +# This will temporarily hold the result from parsing the vmoptions file. |
| 54 | +VMOPTIONS_JAVA_CMD="" |
| 55 | + |
| 56 | + |
| 57 | +# --- Function to resolve a path to a canonical absolute path --- |
| 58 | +# Resolves a given path, handling tilde, relative, and '..' components. |
| 59 | +# @param $1: The path to resolve. |
| 60 | +# @echo: The resolved, canonical absolute path. |
| 61 | +resolve_absolute_path() { |
| 62 | + local path_to_resolve="$1" |
| 63 | + |
| 64 | + # Explicitly handle tilde expansion first |
| 65 | + if [[ "$path_to_resolve" =~ ^~ ]]; then |
| 66 | + # Use eval to expand the tilde to the user's home directory. |
| 67 | + # This correctly handles "~", "~/path", and "~username/path". |
| 68 | + path_to_resolve=$(eval echo "$path_to_resolve") |
| 69 | + fi |
| 70 | + |
| 71 | + # If the path is not absolute, assume it's relative to OIE_HOME |
| 72 | + if [[ ! "$path_to_resolve" =~ ^/ ]]; then |
| 73 | + path_to_resolve="$OIE_HOME/$path_to_resolve" |
| 74 | + fi |
| 75 | + |
| 76 | + # Use cd and pwd to resolve '..' and '.' components for a canonical path. |
| 77 | + if [[ -d "$(dirname "$path_to_resolve")" ]]; then |
| 78 | + echo "$(cd "$(dirname "$path_to_resolve")" && pwd)/$(basename "$path_to_resolve")" |
| 79 | + else |
| 80 | + # If the directory doesn't exist, we can't resolve '..'. Return the concatenated path. |
| 81 | + # Subsequent checks in is_valid_java_version will then fail it gracefully. |
| 82 | + echo "$path_to_resolve" |
| 83 | + fi |
| 84 | +} |
| 85 | + |
| 86 | +# --- Function to validate Java version --- |
| 87 | +# Checks if a given command points to a Java executable of the required minimum version. |
| 88 | +# @param $1: The java command or path to check (e.g., "java", "/opt/jdk-17/bin/java") |
| 89 | +# @return: 0 on success (is valid), 1 on failure. |
| 90 | +is_valid_java_version() { |
| 91 | + local java_cmd="$1" |
| 92 | + local error_message="Warning: '$java_cmd' is specified by $2, which is not a valid Java executable of at least version $MIN_JAVA_VERSION. Ignoring." |
| 93 | + |
| 94 | + # Check if the command is found and is executable |
| 95 | + if ! command -v "$java_cmd" &> /dev/null || ! [[ -x "$(command -v "$java_cmd")" ]]; then |
| 96 | + echo "$error_message" >&2 |
| 97 | + return 1 |
| 98 | + fi |
| 99 | + |
| 100 | + # Execute 'java -version' and capture the output from stderr |
| 101 | + # Example output: openjdk version "17.0.2" 2022-07-19 |
| 102 | + local version_output |
| 103 | + version_output=$("$java_cmd" -version 2>&1) |
| 104 | + |
| 105 | + # Check if the version command succeeded |
| 106 | + if [[ $? -ne 0 ]]; then |
| 107 | + echo "$error_message" >&2 |
| 108 | + return 1 |
| 109 | + fi |
| 110 | + |
| 111 | + # Extract the major version number. This works for formats like "1.8.0" and "17.0.2". |
| 112 | + local major_version |
| 113 | + major_version=$(echo "$version_output" | head -n 1 | cut -d '"' -f 2 | cut -d '.' -f 1) |
| 114 | + |
| 115 | + # Check if the extracted version is a number and meets the minimum requirement |
| 116 | + if [[ "$major_version" =~ ^[0-9]+$ ]] && [[ "$major_version" -ge "$MIN_JAVA_VERSION" ]]; then |
| 117 | + echo "Info: Found suitable java version specified by $2" |
| 118 | + return 0 # Success |
| 119 | + else |
| 120 | + echo "$error_message" >&2 |
| 121 | + return 1 # Failure |
| 122 | + fi |
| 123 | +} |
| 124 | + |
| 125 | +# Set Java options by parsing the vmoptions file |
| 126 | +parse_vmoptions() { |
| 127 | + local file="$1" |
| 128 | + |
| 129 | + if [[ ! -f "$file" ]]; then |
| 130 | + echo "Warning: VM options file not found: $file" >&2 |
| 131 | + return 1 |
| 132 | + fi |
| 133 | + |
| 134 | + # Read the file line by line |
| 135 | + while IFS= read -r line; do |
| 136 | + # Trim leading/trailing whitespace |
| 137 | + line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') |
| 138 | + |
| 139 | + # Skip empty lines and comments |
| 140 | + if [[ -z "$line" || "$line" =~ ^# ]]; then |
| 141 | + continue |
| 142 | + fi |
| 143 | + |
| 144 | + # Evaluate environment variables to be their actual values |
| 145 | + # NOTE: This eval also handles tilde expansion for vmoptions lines. |
| 146 | + line=$(eval echo "$line") |
| 147 | + |
| 148 | + # Check for -include-options directive |
| 149 | + if [[ "$line" =~ ^-include-options[[:space:]]+(.+) ]]; then |
| 150 | + local included_file="${BASH_REMATCH[1]}" |
| 151 | + # Resolve relative paths |
| 152 | + if [[ ! "$included_file" =~ ^/ ]]; then # Not an absolute path |
| 153 | + included_file="$(dirname "$file")/$included_file" |
| 154 | + fi |
| 155 | + # Recursively call parse_vmoptions for the included file |
| 156 | + parse_vmoptions "$included_file" |
| 157 | + elif [[ "$line" =~ ^-classpath[[:space:]]+(.+) ]]; then |
| 158 | + # Handle -classpath directive |
| 159 | + CLASSPATH="${BASH_REMATCH[1]}" |
| 160 | + elif [[ "$line" =~ ^-classpath/a[[:space:]]+(.+) ]]; then |
| 161 | + # Handle -classpath/a directive (append to existing classpath) |
| 162 | + CLASSPATH="${CLASSPATH}:${BASH_REMATCH[1]}" |
| 163 | + elif [[ "$line" =~ ^-classpath/p[[:space:]]+(.+) ]]; then |
| 164 | + # Handle -classpath/p directive (prepend to existing classpath) |
| 165 | + CLASSPATH="${BASH_REMATCH[1]}:${CLASSPATH}" |
| 166 | + elif [[ "$line" =~ ^-java-cmd[[:space:]]+(.+) ]]; then |
| 167 | + # Handle -java-cmd directive |
| 168 | + local candidate_java_cmd="${BASH_REMATCH[1]}" |
| 169 | + # Resolve path to an absolute one before validation |
| 170 | + local resolved_candidate_cmd |
| 171 | + resolved_candidate_cmd=$(resolve_absolute_path "$candidate_java_cmd") |
| 172 | + |
| 173 | + if [[ -z "$FINAL_JAVA_CMD" ]] && is_valid_java_version "$resolved_candidate_cmd" "the -java-cmd directive in '$file'"; then |
| 174 | + VMOPTIONS_JAVA_CMD="$resolved_candidate_cmd" |
| 175 | + fi |
| 176 | + else |
| 177 | + # Add the option to the accumulated string |
| 178 | + VMOPTIONS+=("$line") |
| 179 | + fi |
| 180 | + done < "$file" |
| 181 | + return 0 |
| 182 | +} |
| 183 | + |
| 184 | +# The OIE_JAVA_PATH has highest priority for specifying the Java executable. |
| 185 | +# If a valid version is found, the '-java-cmd' directive will be ignored when parsing the VM options file. |
| 186 | +if [[ -n "$OIE_JAVA_PATH" ]]; then |
| 187 | + # Resolve path to an absolute one before validation |
| 188 | + resolved_oie_java_path=$(resolve_absolute_path "$OIE_JAVA_PATH") |
| 189 | + |
| 190 | + if is_valid_java_version "$resolved_oie_java_path" "the OIE_JAVA_PATH environment variable"; then |
| 191 | + FINAL_JAVA_CMD="$resolved_oie_java_path" |
| 192 | + fi |
| 193 | +fi |
| 194 | + |
| 195 | +# Recursively parse the VM options file |
| 196 | +parse_vmoptions "$OIE_HOME/oieserver.vmoptions" |
| 197 | + |
| 198 | +# Find the Java executable if not already set by '$OIE_JAVA_PATH' |
| 199 | +if [[ -z "$FINAL_JAVA_CMD" ]]; then |
| 200 | + # Check for the result from parsing the vmoptions file. |
| 201 | + # Validation is done during parsing. |
| 202 | + if [[ -n "$VMOPTIONS_JAVA_CMD" ]]; then |
| 203 | + FINAL_JAVA_CMD="$VMOPTIONS_JAVA_CMD" |
| 204 | + # If not found, check JAVA_HOME |
| 205 | + elif [[ -n "$JAVA_HOME" ]] && [[ -f "$JAVA_HOME/bin/java" ]] && is_valid_java_version "$JAVA_HOME/bin/java" "the JAVA_HOME environment variable"; then |
| 206 | + FINAL_JAVA_CMD="$JAVA_HOME/bin/java" |
| 207 | + # If still not found, check for 'java' in the system PATH |
| 208 | + elif is_valid_java_version "java" "the system PATH"; then |
| 209 | + FINAL_JAVA_CMD="java" |
| 210 | + fi |
| 211 | +fi |
| 212 | + |
| 213 | +# Final check for a valid Java path before execution. |
| 214 | +if [[ -z "$FINAL_JAVA_CMD" ]]; then |
| 215 | + echo "Error: Could not find a Java ${MIN_JAVA_VERSION}+ installation." >&2 |
| 216 | + echo "Please configure -java-cmd in conf/custom.vmoptions, set JAVA_HOME, set OIE_JAVA_PATH, or ensure 'java' in your PATH is version ${MIN_JAVA_VERSION} or higher." >&2 |
| 217 | + exit 1 |
| 218 | +fi |
| 219 | + |
| 220 | +JAVA_OPTS=("${VMOPTIONS[@]}" |
| 221 | + "-cp" "$CLASSPATH" |
| 222 | + "com.mirth.connect.server.launcher.MirthLauncher" |
| 223 | + "${APP_ARGS[@]}") |
| 224 | + |
| 225 | +# Launch Open Integration Engine (as this PID with exec) |
| 226 | +echo "Starting Open Integration Engine..." |
| 227 | +echo "Using Java from: $FINAL_JAVA_CMD" |
| 228 | +echo "$FINAL_JAVA_CMD ${JAVA_OPTS[*]}" |
| 229 | +exec "$FINAL_JAVA_CMD" "${JAVA_OPTS[@]}" |
0 commit comments