From 601fba66987e00aa1716dc6eeca11bf99fe81a52 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Sat, 14 Jun 2025 01:44:18 -0400 Subject: [PATCH 1/9] Add oieserver launcher script This script is the main launcher for the Open Integration Engine (OIE) server. It prepares the Java environment and executes the server launcher JAR file. The script automatically finds a compatible Java runtime (version 17+ by default) by searching for a valid executable in the following priority order: 1. The OIE_JAVA_PATH environment variable. 2. The -java-cmd directive in the oieserver.vmoptions file or included .vmoptions files. Must specify the path to the 'java' executable. This is the preferred way to declare the desired version for running the server and can be overridden by OIE_JAVA_PATH. Can be a relative path from the location of this script. 3. The JAVA_HOME environment variable. 4. The 'java' command available in the system's PATH. It also parses the 'oieserver.vmoptions' file to configure JVM options, system properties (-D...), and classpath modifications. Signed-off-by: Tony Germano Co-authored-by: Mitch Gaffigan Issue: https://github.com/OpenIntegrationEngine/engine/issues/2 --- server/basedir-includes/oieserver | 229 ++++++++++++++++++++++++++++++ server/build.xml | 3 + server/conf/custom.vmoptions | 3 +- 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100755 server/basedir-includes/oieserver diff --git a/server/basedir-includes/oieserver b/server/basedir-includes/oieserver new file mode 100755 index 000000000..78099790e --- /dev/null +++ b/server/basedir-includes/oieserver @@ -0,0 +1,229 @@ +#!/usr/bin/env bash +# +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2025 Tony Germano and Mitch Gaffigan +# + +# ============================================================================= +# Open Integration Engine Server Launcher Script +# +# Description: +# This script is the main launcher for the Open Integration Engine (OIE) +# server. It prepares the Java environment and executes the server launcher +# JAR file. +# +# The script automatically finds a compatible Java runtime (version 17+ by +# default) by searching for a valid executable in the following priority order: +# 1. The OIE_JAVA_PATH environment variable. +# 2. The -java-cmd directive in the oieserver.vmoptions file or included +# .vmoptions files. Must specify the path to the 'java' executable. +# This is the preferred way to declare the desired version for running +# the server and can be overridden by OIE_JAVA_PATH. Can be a relative +# path from the location of this script. +# 3. The JAVA_HOME environment variable. +# 4. The 'java' command available in the system's PATH. +# +# It also parses the 'oieserver.vmoptions' file to configure JVM options, +# system properties (-D...), and classpath modifications. +# +# Usage: +# ./oieserver.sh [app-arguments] +# +# All [app-arguments] are passed directly to the underlying Java application +# (com.mirth.connect.server.launcher.MirthLauncher). +# +# Configuration via Environment Variables: +# OIE_JAVA_PATH - (Highest priority) Set the full path to the 'java' +# executable to be used. Can be a relative path from the +# location of this script or a tilde path +# (e.g., ~/path/to/java). +# JAVA_HOME - Set the path to the root of a Java installation. The +# script will look for 'bin/java' within this path. +# ============================================================================= + +APP_ARGS=("$@") +MIN_JAVA_VERSION=17 + +# Set OIE_HOME to the script directory +OIE_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +CLASSPATH="$OIE_HOME/mirth-server-launcher.jar" +VMOPTIONS=() +# This will hold the validated path to the Java executable. It is intentionally left empty for now. +FINAL_JAVA_CMD="" +# This will temporarily hold the result from parsing the vmoptions file. +VMOPTIONS_JAVA_CMD="" + + +# --- Function to resolve a path to a canonical absolute path --- +# Resolves a given path, handling tilde, relative, and '..' components. +# @param $1: The path to resolve. +# @echo: The resolved, canonical absolute path. +resolve_absolute_path() { + local path_to_resolve="$1" + + # Explicitly handle tilde expansion first + if [[ "$path_to_resolve" =~ ^~ ]]; then + # Use eval to expand the tilde to the user's home directory. + # This correctly handles "~", "~/path", and "~username/path". + path_to_resolve=$(eval echo "$path_to_resolve") + fi + + # If the path is not absolute, assume it's relative to OIE_HOME + if [[ ! "$path_to_resolve" =~ ^/ ]]; then + path_to_resolve="$OIE_HOME/$path_to_resolve" + fi + + # Use cd and pwd to resolve '..' and '.' components for a canonical path. + if [[ -d "$(dirname "$path_to_resolve")" ]]; then + echo "$(cd "$(dirname "$path_to_resolve")" && pwd)/$(basename "$path_to_resolve")" + else + # If the directory doesn't exist, we can't resolve '..'. Return the concatenated path. + # Subsequent checks in is_valid_java_version will then fail it gracefully. + echo "$path_to_resolve" + fi +} + +# --- Function to validate Java version --- +# Checks if a given command points to a Java executable of the required minimum version. +# @param $1: The java command or path to check (e.g., "java", "/opt/jdk-17/bin/java") +# @return: 0 on success (is valid), 1 on failure. +is_valid_java_version() { + local java_cmd="$1" + 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." + + # Check if the command is found and is executable + if ! command -v "$java_cmd" &> /dev/null || ! [[ -x "$(command -v "$java_cmd")" ]]; then + echo "$error_message" >&2 + return 1 + fi + + # Execute 'java -version' and capture the output from stderr + # Example output: openjdk version "17.0.2" 2022-07-19 + local version_output + version_output=$("$java_cmd" -version 2>&1) + + # Check if the version command succeeded + if [[ $? -ne 0 ]]; then + echo "$error_message" >&2 + return 1 + fi + + # Extract the major version number. This works for formats like "1.8.0" and "17.0.2". + local major_version + major_version=$(echo "$version_output" | head -n 1 | cut -d '"' -f 2 | cut -d '.' -f 1) + + # Check if the extracted version is a number and meets the minimum requirement + if [[ "$major_version" =~ ^[0-9]+$ ]] && [[ "$major_version" -ge "$MIN_JAVA_VERSION" ]]; then + echo "Info: Found suitable java version specified by $2" + return 0 # Success + else + echo "$error_message" >&2 + return 1 # Failure + fi +} + +# Set Java options by parsing the vmoptions file +parse_vmoptions() { + local file="$1" + + if [[ ! -f "$file" ]]; then + echo "Warning: VM options file not found: $file" >&2 + return 1 + fi + + # Read the file line by line + while IFS= read -r line; do + # Trim leading/trailing whitespace + line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + + # Skip empty lines and comments + if [[ -z "$line" || "$line" =~ ^# ]]; then + continue + fi + + # Evaluate environment variables to be their actual values + # NOTE: This eval also handles tilde expansion for vmoptions lines. + line=$(eval echo "$line") + + # Check for -include-options directive + if [[ "$line" =~ ^-include-options[[:space:]]+(.+) ]]; then + local included_file="${BASH_REMATCH[1]}" + # Resolve relative paths + if [[ ! "$included_file" =~ ^/ ]]; then # Not an absolute path + included_file="$(dirname "$file")/$included_file" + fi + # Recursively call parse_vmoptions for the included file + parse_vmoptions "$included_file" + elif [[ "$line" =~ ^-classpath[[:space:]]+(.+) ]]; then + # Handle -classpath directive + CLASSPATH="${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^-classpath/a[[:space:]]+(.+) ]]; then + # Handle -classpath/a directive (append to existing classpath) + CLASSPATH="${CLASSPATH}:${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^-classpath/p[[:space:]]+(.+) ]]; then + # Handle -classpath/p directive (prepend to existing classpath) + CLASSPATH="${BASH_REMATCH[1]}:${CLASSPATH}" + elif [[ "$line" =~ ^-java-cmd[[:space:]]+(.+) ]]; then + # Handle -java-cmd directive + local candidate_java_cmd="${BASH_REMATCH[1]}" + # Resolve path to an absolute one before validation + local resolved_candidate_cmd + resolved_candidate_cmd=$(resolve_absolute_path "$candidate_java_cmd") + + if [[ -z "$FINAL_JAVA_CMD" ]] && is_valid_java_version "$resolved_candidate_cmd" "the -java-cmd directive in '$file'"; then + VMOPTIONS_JAVA_CMD="$resolved_candidate_cmd" + fi + else + # Add the option to the accumulated string + VMOPTIONS+=("$line") + fi + done < "$file" + return 0 +} + +# The OIE_JAVA_PATH has highest priority for specifying the Java executable. +# If a valid version is found, the '-java-cmd' directive will be ignored when parsing the VM options file. +if [[ -n "$OIE_JAVA_PATH" ]]; then + # Resolve path to an absolute one before validation + resolved_oie_java_path=$(resolve_absolute_path "$OIE_JAVA_PATH") + + if is_valid_java_version "$resolved_oie_java_path" "the OIE_JAVA_PATH environment variable"; then + FINAL_JAVA_CMD="$resolved_oie_java_path" + fi +fi + +# Recursively parse the VM options file +parse_vmoptions "$OIE_HOME/oieserver.vmoptions" + +# Find the Java executable if not already set by '$OIE_JAVA_PATH' +if [[ -z "$FINAL_JAVA_CMD" ]]; then + # Check for the result from parsing the vmoptions file. + # Validation is done during parsing. + if [[ -n "$VMOPTIONS_JAVA_CMD" ]]; then + FINAL_JAVA_CMD="$VMOPTIONS_JAVA_CMD" + # If not found, check JAVA_HOME + elif [[ -n "$JAVA_HOME" ]] && [[ -f "$JAVA_HOME/bin/java" ]] && is_valid_java_version "$JAVA_HOME/bin/java" "the JAVA_HOME environment variable"; then + FINAL_JAVA_CMD="$JAVA_HOME/bin/java" + # If still not found, check for 'java' in the system PATH + elif is_valid_java_version "java" "the system PATH"; then + FINAL_JAVA_CMD="java" + fi +fi + +# Final check for a valid Java path before execution. +if [[ -z "$FINAL_JAVA_CMD" ]]; then + echo "Error: Could not find a Java ${MIN_JAVA_VERSION}+ installation." >&2 + 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 + exit 1 +fi + +JAVA_OPTS=("${VMOPTIONS[@]}" + "-cp" "$CLASSPATH" + "com.mirth.connect.server.launcher.MirthLauncher" + "${APP_ARGS[@]}") + +# Launch Open Integration Engine (as this PID with exec) +echo "Starting Open Integration Engine..." +echo "Using Java from: $FINAL_JAVA_CMD" +echo "$FINAL_JAVA_CMD ${JAVA_OPTS[*]}" +exec "$FINAL_JAVA_CMD" "${JAVA_OPTS[@]}" diff --git a/server/build.xml b/server/build.xml index 9aa6606c1..153790891 100644 --- a/server/build.xml +++ b/server/build.xml @@ -1007,6 +1007,9 @@ + + + diff --git a/server/conf/custom.vmoptions b/server/conf/custom.vmoptions index 1acaf2e42..d14573ac2 100644 --- a/server/conf/custom.vmoptions +++ b/server/conf/custom.vmoptions @@ -13,7 +13,8 @@ # : append to classpath. # # -java-cmd -# : use the java binary specified to launch the engine. +# : use the java binary specified to launch the engine. Relative paths are relative to +# : the engine home directory. # # Additionally, the form ${ENV_VAR} can be used anywhere in the file to substitute the value # from an environment variable. From 56d8e6e5b85f01b01a99961e44f53d1ae5e36856 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Sun, 15 Jun 2025 00:43:40 -0400 Subject: [PATCH 2/9] Changes to oieserver script from PR suggestions Issue: https://github.com/OpenIntegrationEngine/engine/issues/2 Pull-request: https://github.com/OpenIntegrationEngine/engine/pull/119 Signed-off-by: Tony Germano --- server/basedir-includes/oieserver | 102 ++++++++++++++++++------------ 1 file changed, 62 insertions(+), 40 deletions(-) diff --git a/server/basedir-includes/oieserver b/server/basedir-includes/oieserver index 78099790e..c664fae01 100755 --- a/server/basedir-includes/oieserver +++ b/server/basedir-includes/oieserver @@ -46,25 +46,26 @@ MIN_JAVA_VERSION=17 # Set OIE_HOME to the script directory OIE_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -CLASSPATH="$OIE_HOME/mirth-server-launcher.jar" +LAUNCHER_JAR="$OIE_HOME/mirth-server-launcher.jar" +CLASSPATH="$LAUNCHER_JAR" VMOPTIONS=() # This will hold the validated path to the Java executable. It is intentionally left empty for now. FINAL_JAVA_CMD="" # This will temporarily hold the result from parsing the vmoptions file. VMOPTIONS_JAVA_CMD="" +VMOPTIONS_JAVA_CMD_FILE="" # --- Function to resolve a path to a canonical absolute path --- # Resolves a given path, handling tilde, relative, and '..' components. # @param $1: The path to resolve. # @echo: The resolved, canonical absolute path. -resolve_absolute_path() { +resolve_canonical_path() { local path_to_resolve="$1" # Explicitly handle tilde expansion first if [[ "$path_to_resolve" =~ ^~ ]]; then # Use eval to expand the tilde to the user's home directory. - # This correctly handles "~", "~/path", and "~username/path". path_to_resolve=$(eval echo "$path_to_resolve") fi @@ -77,23 +78,19 @@ resolve_absolute_path() { if [[ -d "$(dirname "$path_to_resolve")" ]]; then echo "$(cd "$(dirname "$path_to_resolve")" && pwd)/$(basename "$path_to_resolve")" else - # If the directory doesn't exist, we can't resolve '..'. Return the concatenated path. - # Subsequent checks in is_valid_java_version will then fail it gracefully. echo "$path_to_resolve" fi } # --- Function to validate Java version --- # Checks if a given command points to a Java executable of the required minimum version. -# @param $1: The java command or path to check (e.g., "java", "/opt/jdk-17/bin/java") +# @param $1: The java command or path to check # @return: 0 on success (is valid), 1 on failure. is_valid_java_version() { local java_cmd="$1" - 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." # Check if the command is found and is executable if ! command -v "$java_cmd" &> /dev/null || ! [[ -x "$(command -v "$java_cmd")" ]]; then - echo "$error_message" >&2 return 1 fi @@ -104,7 +101,6 @@ is_valid_java_version() { # Check if the version command succeeded if [[ $? -ne 0 ]]; then - echo "$error_message" >&2 return 1 fi @@ -114,10 +110,8 @@ is_valid_java_version() { # Check if the extracted version is a number and meets the minimum requirement if [[ "$major_version" =~ ^[0-9]+$ ]] && [[ "$major_version" -ge "$MIN_JAVA_VERSION" ]]; then - echo "Info: Found suitable java version specified by $2" return 0 # Success else - echo "$error_message" >&2 return 1 # Failure fi } @@ -165,14 +159,9 @@ parse_vmoptions() { CLASSPATH="${BASH_REMATCH[1]}:${CLASSPATH}" elif [[ "$line" =~ ^-java-cmd[[:space:]]+(.+) ]]; then # Handle -java-cmd directive - local candidate_java_cmd="${BASH_REMATCH[1]}" - # Resolve path to an absolute one before validation - local resolved_candidate_cmd - resolved_candidate_cmd=$(resolve_absolute_path "$candidate_java_cmd") - - if [[ -z "$FINAL_JAVA_CMD" ]] && is_valid_java_version "$resolved_candidate_cmd" "the -java-cmd directive in '$file'"; then - VMOPTIONS_JAVA_CMD="$resolved_candidate_cmd" - fi + # Store the path and the file it was found in. Validation is deferred. + VMOPTIONS_JAVA_CMD=$(resolve_canonical_path "${BASH_REMATCH[1]}") + VMOPTIONS_JAVA_CMD_FILE="$file" else # Add the option to the accumulated string VMOPTIONS+=("$line") @@ -181,39 +170,73 @@ parse_vmoptions() { return 0 } -# The OIE_JAVA_PATH has highest priority for specifying the Java executable. -# If a valid version is found, the '-java-cmd' directive will be ignored when parsing the VM options file. +# --- Main Logic --- + +# 1. Recursively parse the VM options file to populate vmoptions variables. +parse_vmoptions "$OIE_HOME/oieserver.vmoptions" + +# 2. Ensure the launcher JAR is always in the classpath. +case "$CLASSPATH" in + *"$LAUNCHER_JAR"*) + # It's already there, do nothing. + ;; + *) + # Prepend the launcher JAR if a custom classpath was provided. + echo "Info: Prepending mirth-server-launcher.jar to the classpath." + CLASSPATH="$LAUNCHER_JAR:$CLASSPATH" + ;; +esac + +# 3. Discover the Java executable using the documented priority order. + +# Check OIE_JAVA_PATH (fail-fast on invalid). if [[ -n "$OIE_JAVA_PATH" ]]; then - # Resolve path to an absolute one before validation - resolved_oie_java_path=$(resolve_absolute_path "$OIE_JAVA_PATH") + resolved_path=$(resolve_canonical_path "$OIE_JAVA_PATH") + if is_valid_java_version "$resolved_path"; then + echo "Info: Found suitable java version specified by the OIE_JAVA_PATH environment variable" + FINAL_JAVA_CMD="$resolved_path" + else + echo "Error: '$resolved_path' is specified by the OIE_JAVA_PATH environment variable, which is not a valid Java executable of at least version $MIN_JAVA_VERSION. Exiting." >&2 + exit 1 + fi +fi - if is_valid_java_version "$resolved_oie_java_path" "the OIE_JAVA_PATH environment variable"; then - FINAL_JAVA_CMD="$resolved_oie_java_path" +# Check -java-cmd from vmoptions (fail-fast on invalid, only if not already found). +if [[ -z "$FINAL_JAVA_CMD" ]] && [[ -n "$VMOPTIONS_JAVA_CMD" ]]; then + if is_valid_java_version "$VMOPTIONS_JAVA_CMD"; then + echo "Info: Found suitable java version specified by the -java-cmd directive in '$VMOPTIONS_JAVA_CMD_FILE'" + FINAL_JAVA_CMD="$VMOPTIONS_JAVA_CMD" + else + echo "Error: '$VMOPTIONS_JAVA_CMD' is specified by the -java-cmd directive in '$VMOPTIONS_JAVA_CMD_FILE', which is not a valid Java executable of at least version $MIN_JAVA_VERSION. Exiting." >&2 + exit 1 fi fi -# Recursively parse the VM options file -parse_vmoptions "$OIE_HOME/oieserver.vmoptions" +# Check JAVA_HOME (no fail-fast). +if [[ -z "$FINAL_JAVA_CMD" ]] && [[ -n "$JAVA_HOME" ]]; then + java_home_path="$JAVA_HOME/bin/java" + if is_valid_java_version "$java_home_path"; then + echo "Info: Found suitable java version specified by the JAVA_HOME environment variable" + FINAL_JAVA_CMD="$java_home_path" + else + echo "Warning: '$java_home_path' is specified by the JAVA_HOME environment variable, which is not a valid Java executable of at least version $MIN_JAVA_VERSION. Ignoring." >&2 + fi +fi -# Find the Java executable if not already set by '$OIE_JAVA_PATH' +# Check system PATH (no fail-fast). if [[ -z "$FINAL_JAVA_CMD" ]]; then - # Check for the result from parsing the vmoptions file. - # Validation is done during parsing. - if [[ -n "$VMOPTIONS_JAVA_CMD" ]]; then - FINAL_JAVA_CMD="$VMOPTIONS_JAVA_CMD" - # If not found, check JAVA_HOME - elif [[ -n "$JAVA_HOME" ]] && [[ -f "$JAVA_HOME/bin/java" ]] && is_valid_java_version "$JAVA_HOME/bin/java" "the JAVA_HOME environment variable"; then - FINAL_JAVA_CMD="$JAVA_HOME/bin/java" - # If still not found, check for 'java' in the system PATH - elif is_valid_java_version "java" "the system PATH"; then + if is_valid_java_version "java"; then + echo "Info: Found suitable java version in the PATH" FINAL_JAVA_CMD="java" + else + echo "Warning: 'java' does not exist in your PATH or is not a valid Java executable of at least version $MIN_JAVA_VERSION." >&2 fi fi -# Final check for a valid Java path before execution. +# 4. Final check for a valid Java path before execution. if [[ -z "$FINAL_JAVA_CMD" ]]; then echo "Error: Could not find a Java ${MIN_JAVA_VERSION}+ installation." >&2 - 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 + echo "Please configure -java-cmd in conf/custom.vmoptions, set OIE_JAVA_PATH, set JAVA_HOME, or ensure 'java' in your PATH is version ${MIN_JAVA_VERSION} or higher." >&2 exit 1 fi @@ -224,6 +247,5 @@ JAVA_OPTS=("${VMOPTIONS[@]}" # Launch Open Integration Engine (as this PID with exec) echo "Starting Open Integration Engine..." -echo "Using Java from: $FINAL_JAVA_CMD" echo "$FINAL_JAVA_CMD ${JAVA_OPTS[*]}" exec "$FINAL_JAVA_CMD" "${JAVA_OPTS[@]}" From 228334a7fba4d87e784f04cc70c2adb8cae04b84 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Thu, 19 Jun 2025 00:01:11 -0400 Subject: [PATCH 3/9] replace eval usage with sed Signed-off-by: Tony Germano --- server/basedir-includes/oieserver | 28 ++++++++++++++++++++-------- server/conf/custom.vmoptions | 3 ++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/server/basedir-includes/oieserver b/server/basedir-includes/oieserver index c664fae01..1cb322176 100755 --- a/server/basedir-includes/oieserver +++ b/server/basedir-includes/oieserver @@ -64,10 +64,7 @@ resolve_canonical_path() { local path_to_resolve="$1" # Explicitly handle tilde expansion first - if [[ "$path_to_resolve" =~ ^~ ]]; then - # Use eval to expand the tilde to the user's home directory. - path_to_resolve=$(eval echo "$path_to_resolve") - fi + path_to_resolve=$(sed -E "s,^~(/|$),${HOME}\1," <<< "$path_to_resolve") # If the path is not absolute, assume it's relative to OIE_HOME if [[ ! "$path_to_resolve" =~ ^/ ]]; then @@ -82,6 +79,23 @@ resolve_canonical_path() { fi } +# --- Function to expand environment variable in a string --- +# @param $1: The line to expand +# @echo: The expanded line +expand_line_variables() { + local line="$1" + # Safely handle `${VAR}` style environment variables + while [[ "$line" =~ (\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}) ]]; do + local full_match="${BASH_REMATCH[1]}" + local var_name="${BASH_REMATCH[2]}" + # Use indirect expansion to get the variable's value. Default to empty string if not set. + local var_value="${!var_name:-}" + # Replace the first found match with its value + line="${line/"$full_match"/"$var_value"}" + done + echo "$line" +} + # --- Function to validate Java version --- # Checks if a given command points to a Java executable of the required minimum version. # @param $1: The java command or path to check @@ -135,9 +149,7 @@ parse_vmoptions() { continue fi - # Evaluate environment variables to be their actual values - # NOTE: This eval also handles tilde expansion for vmoptions lines. - line=$(eval echo "$line") + line=$(expand_line_variables "$line") # Check for -include-options directive if [[ "$line" =~ ^-include-options[[:space:]]+(.+) ]]; then @@ -213,7 +225,7 @@ if [[ -z "$FINAL_JAVA_CMD" ]] && [[ -n "$VMOPTIONS_JAVA_CMD" ]]; then fi # Check JAVA_HOME (no fail-fast). -if [[ -z "$FINAL_JAVA_CMD" ]] && [[ -n "$JAVA_HOME" ]]; then +if [[ -z "$FINAL_JAVA_CMD" ]] && [[ -d "$JAVA_HOME" ]]; then java_home_path="$JAVA_HOME/bin/java" if is_valid_java_version "$java_home_path"; then echo "Info: Found suitable java version specified by the JAVA_HOME environment variable" diff --git a/server/conf/custom.vmoptions b/server/conf/custom.vmoptions index d14573ac2..ab1f51fc0 100644 --- a/server/conf/custom.vmoptions +++ b/server/conf/custom.vmoptions @@ -1,7 +1,8 @@ # Add any custom vmoptions to this file. Special directives are: # # -include-options -# : include options from another .vmoptions file. Applied recursively. +# : include options from another .vmoptions file. Applied recursively. Relative paths +# : are relative to the current vmoptions file. # # -classpath # : replace the classpath built so far with From 63f60d2e2ec7286ac286d7ca8f51cb805fc1f4c6 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 20 Jun 2025 00:35:58 -0400 Subject: [PATCH 4/9] change oieserver working directory; avoid recursive variable replacement Signed-off-by: Tony Germano --- server/basedir-includes/oieserver | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/server/basedir-includes/oieserver b/server/basedir-includes/oieserver index 1cb322176..86b3c3c06 100755 --- a/server/basedir-includes/oieserver +++ b/server/basedir-includes/oieserver @@ -46,6 +46,11 @@ MIN_JAVA_VERSION=17 # Set OIE_HOME to the script directory OIE_HOME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +# The engine expects it's working directory to be OIE_HOME +if ! cd "$OIE_HOME"; then + echo "Error: Could not change to the OIE_HOME directory: $OIE_HOME" >&2 + exit 1 +fi LAUNCHER_JAR="$OIE_HOME/mirth-server-launcher.jar" CLASSPATH="$LAUNCHER_JAR" VMOPTIONS=() @@ -84,16 +89,25 @@ resolve_canonical_path() { # @echo: The expanded line expand_line_variables() { local line="$1" - # Safely handle `${VAR}` style environment variables + local result_line="" + + # This loop consumes the line from left to right, preventing recursive expansion. while [[ "$line" =~ (\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}) ]]; do - local full_match="${BASH_REMATCH[1]}" + # Append the text before the match to our result. + result_line+="${line%%"${BASH_REMATCH[0]}"*}" + + # Get the variable name and its value. local var_name="${BASH_REMATCH[2]}" # Use indirect expansion to get the variable's value. Default to empty string if not set. local var_value="${!var_name:-}" - # Replace the first found match with its value - line="${line/"$full_match"/"$var_value"}" + result_line+="$var_value" + + # Update the line to be only the portion *after* the match. + line="${line#*"${BASH_REMATCH[0]}"}" done - echo "$line" + + # Append any remaining part of the line after the last match and return. + echo "$result_line$line" } # --- Function to validate Java version --- From 9e3d27768544943a79902f32bed7351bff209892 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 20 Jun 2025 00:36:45 -0400 Subject: [PATCH 5/9] add powershell version Signed-off-by: Tony Germano --- server/basedir-includes/oieserver.ps1 | 290 ++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 server/basedir-includes/oieserver.ps1 diff --git a/server/basedir-includes/oieserver.ps1 b/server/basedir-includes/oieserver.ps1 new file mode 100644 index 000000000..690d3aee5 --- /dev/null +++ b/server/basedir-includes/oieserver.ps1 @@ -0,0 +1,290 @@ +# +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2025 Tony Germano and Mitch Gaffigan +# + +# ============================================================================= +# Open Integration Engine Server Launcher Script (PowerShell Version) +# +# Description: +# This script is the main launcher for the Open Integration Engine (OIE) +# server. It prepares the Java environment and executes the server launcher +# JAR file. +# +# The script automatically finds a compatible Java runtime (version 17+ by +# default) by searching for a valid executable in the following priority order: +# 1. The OIE_JAVA_PATH environment variable. +# 2. The -java-cmd directive in the oieserver.vmoptions file or included +# .vmoptions files. Must specify the path to the 'java' executable. +# This is the preferred way to declare the desired version for running +# the server and can be overridden by OIE_JAVA_PATH. Can be a relative +# path from the location of this script. +# 3. The JAVA_HOME environment variable. +# 4. The 'java' command available in the system's PATH. +# +# It also parses the 'oieserver.vmoptions' file to configure JVM options, +# system properties (-D...), and classpath modifications. +# +# Usage: +# ./oieserver.ps1 [app-arguments] +# +# All [app-arguments] are passed directly to the underlying Java application +# (com.mirth.connect.server.launcher.MirthLauncher). +# +# Configuration via Environment Variables: +# OIE_JAVA_PATH - (Highest priority) Set the full path to the 'java' +# executable to be used. Can be a relative path from the +# location of this script or a tilde path +# (e.g., ~/path/to/java). +# JAVA_HOME - Set the path to the root of a Java installation. The +# script will look for 'bin/java' within this path. +# ============================================================================= + +# --- Script Parameters and Initial Variables --- +param( + [string[]]$AppArgs = $args +) + +$MinJavaVersion = 17 + +# Set OieHome to the script directory using PowerShell's built-in variable +$OieHome = $PSScriptRoot +# The engine expects it's working directory to be OieHome +try { + Set-Location -Path $OieHome -ErrorAction Stop +} +catch { + Write-Error "Could not change to the OieHome directory: $OieHome" + exit 1 +} +$LauncherJar = Join-Path -Path $OieHome -ChildPath "mirth-server-launcher.jar" +$script:Classpath = $LauncherJar # Use script scope to be modifiable by functions +$script:VmOptions = [System.Collections.Generic.List[string]]::new() + +# This will hold the validated path to the Java executable. +$FinalJavaCmd = $null +# This will temporarily hold the result from parsing the vmoptions file. +$script:VmOptionsJavaCmd = $null +$script:VmOptionsJavaCmdFile = $null + +# --- Function to resolve a path to a canonical absolute path --- +function Resolve-CanonicalPath { + param( + [string]$PathToResolve + ) + + # Explicitly handle simple tilde expansion first (`~/` or `~`) + if ($PathToResolve -match '^~(/|$)') { + $homePath = $env:HOME + if ([string]::IsNullOrWhiteSpace($homePath)) { + $homePath = $env:USERPROFILE + } + $PathToResolve = ($PathToResolve -replace '^~/', "$($homePath)/") -replace '^~$', $homePath + } + + # If the path is not absolute, assume it's relative to OIE_HOME + if (-not [System.IO.Path]::IsPathRooted($PathToResolve)) { + $PathToResolve = Join-Path -Path $OieHome -ChildPath $PathToResolve + } + + try { + return (Resolve-Path -LiteralPath $PathToResolve).Path + } + catch { + return $PathToResolve + } +} + +# --- Function to safely expand specific variable formats in a string --- +function Expand-LineVariables { + param( + [string]$Line + ) + $resultBuilder = [System.Text.StringBuilder]::new() + $remainingLine = $Line + + # This loop consumes the line from left to right, preventing recursive expansion. + while ($remainingLine -match '\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}') { + # Append the text before the match to our result. + $prefix = $remainingLine.Substring(0, $matches[0].Index) + $resultBuilder.Append($prefix) | Out-Null + + # Get the variable name and its value. + $varName = $matches[1] + $varValue = (Get-Variable -Name $varName -Scope "global" -ErrorAction SilentlyContinue).Value + if ($null -eq $varValue) { + $varValue = (Get-Variable -Name "env:$varName" -ErrorAction SilentlyContinue).Value + } + $resultBuilder.Append($varValue) | Out-Null + + # Update the line to be only the portion *after* the match. + $remainingLine = $remainingLine.Substring($matches[0].Index + $matches[0].Length) + } + + # Append any remaining part of the line after the last match and return. + $resultBuilder.Append($remainingLine) | Out-Null + return $resultBuilder.ToString() +} + +# --- Function to validate Java version --- +function Test-IsValidJavaVersion { + param( + [string]$JavaCmd + ) + + # Check if the command is found and is executable + if (-not (Get-Command $JavaCmd -ErrorAction SilentlyContinue)) { + return $false + } + + # Execute 'java -version' and capture the output from stderr + # Example output: openjdk version "17.0.2" 2022-07-19 + try { + $versionOutput = & $JavaCmd -version 2>&1 + } + catch { + return $false + } + + if ($LASTEXITCODE -ne 0) { + return $false + } + + # Extract the major version number. This works for formats like "1.8.0" and "17.0.2". + $match = $versionOutput | Select-String -Pattern '"(\d+)\.' + if ($match -and ($match.Matches[0].Groups[1].Value -as [int]) -ge $MinJavaVersion) { + return $true + } else { + return $false + } +} + +# --- Function to parse vmoptions file --- +function Parse-VmOptions { + param( + [string]$File + ) + + if (-not (Test-Path -LiteralPath $File -PathType Leaf)) { + Write-Warning "VM options file not found: $File" + return + } + + # Read the file line by line + Get-Content -Path $File | ForEach-Object { + $line = $_.Trim() + + # Skip empty lines and comments + if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith('#')) { + return + } + + $line = Expand-LineVariables -Line $line + + if ($line -match '^-include-options\s+(.+)') { + $includedFile = $matches[1].Trim() + # Resolve relative paths to the current file's directory + if (-not [System.IO.Path]::IsPathRooted($includedFile)) { + $includedFile = Join-Path -Path (Split-Path -Path $File -Parent) -ChildPath $includedFile + } + Parse-VmOptions -File $includedFile + } + elseif ($line -match '^-classpath\s+(.+)') { + $script:Classpath = $matches[1].Trim() + } + elseif ($line -match '^-classpath/a\s+(.+)') { + $script:Classpath = "$($script:Classpath)$([System.IO.Path]::PathSeparator)$($matches[1].Trim())" + } + elseif ($line -match '^-classpath/p\s+(.+)') { + $script:Classpath = "$($matches[1].Trim())$([System.IO.Path]::PathSeparator)$($script:Classpath)" + } + elseif ($line -match '^-java-cmd\s+(.+)') { + # Store the path and the file it was found in. Validation is deferred. + $script:VmOptionsJavaCmd = Resolve-CanonicalPath -PathToResolve $matches[1].Trim() + $script:VmOptionsJavaCmdFile = $File + } + else { + $script:VmOptions.Add($line) | Out-Null + } + } +} + +# --- Main Logic --- + +# 1. Recursively parse the VM options file to populate vmoptions variables. +Parse-VmOptions -File (Join-Path -Path $OieHome -ChildPath "oieserver.vmoptions") + +# 2. Ensure the launcher JAR is always in the classpath. +if ($script:Classpath -notmatch [regex]::Escape($LauncherJar)) { + Write-Host "Info: Prepending mirth-server-launcher.jar to the classpath." -ForegroundColor Green + $script:Classpath = "$($LauncherJar)$([System.IO.Path]::PathSeparator)$($script:Classpath)" +} + +# 3. Discover the Java executable using the documented priority order. + +# Check OIE_JAVA_PATH (fail-fast on invalid). +if (-not [string]::IsNullOrWhiteSpace($env:OIE_JAVA_PATH)) { + $resolvedPath = Resolve-CanonicalPath -PathToResolve $env:OIE_JAVA_PATH + if (Test-IsValidJavaVersion -JavaCmd $resolvedPath) { + Write-Host "Info: Found suitable java version specified by the OIE_JAVA_PATH environment variable" -ForegroundColor Green + $FinalJavaCmd = $resolvedPath + } else { + Write-Error "'$resolvedPath' is specified by the OIE_JAVA_PATH environment variable, which is not a valid Java executable of at least version $MinJavaVersion. Exiting." + exit 1 + } +} + +# Check -java-cmd from vmoptions (fail-fast on invalid, only if not already found). +if (-not $FinalJavaCmd -and -not [string]::IsNullOrWhiteSpace($script:VmOptionsJavaCmd)) { + if (Test-IsValidJavaVersion -JavaCmd $script:VmOptionsJavaCmd) { + Write-Host "Info: Found suitable java version specified by the -java-cmd directive in '$($script:VmOptionsJavaCmdFile)'" -ForegroundColor Green + $FinalJavaCmd = $script:VmOptionsJavaCmd + } else { + Write-Error "'$($script:VmOptionsJavaCmd)' is specified by the -java-cmd directive in '$($script:VmOptionsJavaCmdFile)', which is not a valid Java executable of at least version $MinJavaVersion. Exiting." + exit 1 + } +} + +# Check JAVA_HOME (no fail-fast). +if (-not $FinalJavaCmd -and (Test-Path -Path $env:JAVA_HOME -PathType Container)) { + $javaHomePath = Join-Path -Path $env:JAVA_HOME -ChildPath "bin\java" + if (Test-IsValidJavaVersion -JavaCmd $javaHomePath) { + Write-Host "Info: Found suitable java version specified by the JAVA_HOME environment variable" -ForegroundColor Green + $FinalJavaCmd = $javaHomePath + } else { + Write-Warning "'$javaHomePath' is specified by the JAVA_HOME environment variable, which is not a valid Java executable of at least version $MinJavaVersion. Ignoring." + } +} + +# Check system PATH (no fail-fast). +if (-not $FinalJavaCmd) { + if (Get-Command "java" -ErrorAction SilentlyContinue) { + if (Test-IsValidJavaVersion -JavaCmd "java") { + Write-Host "Info: Found suitable java version in the PATH" -ForegroundColor Green + $FinalJavaCmd = "java" + } else { + Write-Warning "'java' does not exist in your PATH or is not a valid Java executable of at least version $MinJavaVersion." + } + } +} + +# 4. Final check for a valid Java path before execution. +if (-not $FinalJavaCmd) { + Write-Error "Could not find a Java $($MinJavaVersion)+ installation. Please configure -java-cmd in conf/custom.vmoptions, set OIE_JAVA_PATH, set JAVA_HOME, or ensure 'java' in your PATH is version $($MinJavaVersion) or higher." + exit 1 +} + +# 5. Assemble final arguments and launch the process. +$javaOpts = [System.Collections.Generic.List[string]]::new() +$javaOpts.AddRange($script:VmOptions) +$javaOpts.Add("-cp") +$javaOpts.Add(('"{0}"' -f ($script:Classpath -split "[:;]" -join [System.IO.Path]::PathSeparator))) +$javaOpts.Add("com.mirth.connect.server.launcher.MirthLauncher") +$javaOpts.AddRange($AppArgs) + +# Launch Open Integration Engine +Write-Host "Starting Open Integration Engine..." -ForegroundColor Green +Write-Host "$FinalJavaCmd $($javaOpts -join ' ')" + +& $FinalJavaCmd $javaOpts +exit $LASTEXITCODE From f86f60136afe6241bafcfd4b250a065797f2bb07 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 20 Jun 2025 23:45:29 -0400 Subject: [PATCH 6/9] fix powershell Expand-LineVariables Signed-off-by: Tony Germano --- server/basedir-includes/oieserver.ps1 | 35 +++++++++++++++------------ 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/server/basedir-includes/oieserver.ps1 b/server/basedir-includes/oieserver.ps1 index 690d3aee5..f03f52edb 100644 --- a/server/basedir-includes/oieserver.ps1 +++ b/server/basedir-includes/oieserver.ps1 @@ -100,30 +100,33 @@ function Expand-LineVariables { param( [string]$Line ) - $resultBuilder = [System.Text.StringBuilder]::new() - $remainingLine = $Line - - # This loop consumes the line from left to right, preventing recursive expansion. - while ($remainingLine -match '\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}') { - # Append the text before the match to our result. - $prefix = $remainingLine.Substring(0, $matches[0].Index) - $resultBuilder.Append($prefix) | Out-Null + + # Define a "match evaluator" script block. This block will be called + # for every match the regex finds. + $evaluator = { + param($match) + + # The variable name is in the first capture group. + $varName = $match.Groups[1].Value - # Get the variable name and its value. - $varName = $matches[1] + # Look for a PowerShell variable first. $varValue = (Get-Variable -Name $varName -Scope "global" -ErrorAction SilentlyContinue).Value + # If not found, look for an environment variable. if ($null -eq $varValue) { $varValue = (Get-Variable -Name "env:$varName" -ErrorAction SilentlyContinue).Value } - $resultBuilder.Append($varValue) | Out-Null - # Update the line to be only the portion *after* the match. - $remainingLine = $remainingLine.Substring($matches[0].Index + $matches[0].Length) + # Return the found value. This will be the replacement for the match. + return $varValue } - # Append any remaining part of the line after the last match and return. - $resultBuilder.Append($remainingLine) | Out-Null - return $resultBuilder.ToString() + # Define the regex pattern to find ${...} variables. + $regex = '\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}' + + # Use the static Replace method, passing the input line, the regex, and our evaluator. + $expandedLine = [regex]::Replace($Line, $regex, $evaluator) + + return $expandedLine } # --- Function to validate Java version --- From a8b2ed5ac04588aab7e5c404a8fc1340ee2c7ce2 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Fri, 20 Jun 2025 23:58:58 -0400 Subject: [PATCH 7/9] simplify logic and prevent breaking absolute paths Signed-off-by: Tony Germano --- server/basedir-includes/oieserver.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/basedir-includes/oieserver.ps1 b/server/basedir-includes/oieserver.ps1 index f03f52edb..a30a7c33a 100644 --- a/server/basedir-includes/oieserver.ps1 +++ b/server/basedir-includes/oieserver.ps1 @@ -281,7 +281,7 @@ if (-not $FinalJavaCmd) { $javaOpts = [System.Collections.Generic.List[string]]::new() $javaOpts.AddRange($script:VmOptions) $javaOpts.Add("-cp") -$javaOpts.Add(('"{0}"' -f ($script:Classpath -split "[:;]" -join [System.IO.Path]::PathSeparator))) +$javaOpts.Add('"{0}"' -f $script:Classpath) $javaOpts.Add("com.mirth.connect.server.launcher.MirthLauncher") $javaOpts.AddRange($AppArgs) From 65510653026484fbbc7a0cf673535178b509fe7c Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Sat, 21 Jun 2025 23:07:48 -0400 Subject: [PATCH 8/9] Resolve-CanonicalPath only checks directory, and not leaf This matches what the bash script does, and allows the function to work when a path to a java command omits the `.exe`. Signed-off-by: Tony Germano --- server/basedir-includes/oieserver.ps1 | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/server/basedir-includes/oieserver.ps1 b/server/basedir-includes/oieserver.ps1 index a30a7c33a..8b6483ada 100644 --- a/server/basedir-includes/oieserver.ps1 +++ b/server/basedir-includes/oieserver.ps1 @@ -75,22 +75,23 @@ function Resolve-CanonicalPath { # Explicitly handle simple tilde expansion first (`~/` or `~`) if ($PathToResolve -match '^~(/|$)') { - $homePath = $env:HOME - if ([string]::IsNullOrWhiteSpace($homePath)) { - $homePath = $env:USERPROFILE - } - $PathToResolve = ($PathToResolve -replace '^~/', "$($homePath)/") -replace '^~$', $homePath + $PathToResolve = ($PathToResolve -replace '^~/', "$env:USERPROFILE/") -replace '^~$', $env:USERPROFILE } - # If the path is not absolute, assume it's relative to OIE_HOME + # If the path is not absolute, assume it's relative to OieHome if (-not [System.IO.Path]::IsPathRooted($PathToResolve)) { $PathToResolve = Join-Path -Path $OieHome -ChildPath $PathToResolve } - try { - return (Resolve-Path -LiteralPath $PathToResolve).Path + $parentDir = [System.IO.Path]::GetDirectoryName($PathToResolve) + $leaf = [System.IO.Path]::GetFileName($PathToResolve) + + # Only resolve the path if the parent directory actually exists. + if (Test-Path -LiteralPath $parentDir -PathType Container) { + $resolvedParentDir = (Resolve-Path -LiteralPath $parentDir).Path + return Join-Path -Path $resolvedParentDir -ChildPath $leaf } - catch { + else { return $PathToResolve } } From 309d0604887d44013ad4ee0711b8afb853ba8ce0 Mon Sep 17 00:00:00 2001 From: Tony Germano Date: Tue, 24 Jun 2025 20:18:09 -0400 Subject: [PATCH 9/9] additional fixes and some of mitch's suggestions In env expansion both versions will leave ${UNSET_VAR} untouched to match the behavior of install4j. Previously unset variables were converted to the empty string. incorporated several suggestions to make the powershell script more idiomatic.. Signed-off-by: Tony Germano --- server/basedir-includes/oieserver | 10 +- server/basedir-includes/oieserver.ps1 | 176 +++++++++++++------------- 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/server/basedir-includes/oieserver b/server/basedir-includes/oieserver index 86b3c3c06..b1180e5db 100755 --- a/server/basedir-includes/oieserver +++ b/server/basedir-includes/oieserver @@ -98,9 +98,13 @@ expand_line_variables() { # Get the variable name and its value. local var_name="${BASH_REMATCH[2]}" - # Use indirect expansion to get the variable's value. Default to empty string if not set. - local var_value="${!var_name:-}" - result_line+="$var_value" + # Use indirect expansion to get the variable's value. Do not replace if not set. + if [[ -v "$var_name" ]]; then + local var_value="${!var_name}" + result_line+="$var_value" + else + result_line+="${BASH_REMATCH[0]}" + fi # Update the line to be only the portion *after* the match. line="${line#*"${BASH_REMATCH[0]}"}" diff --git a/server/basedir-includes/oieserver.ps1 b/server/basedir-includes/oieserver.ps1 index 8b6483ada..79ff35350 100644 --- a/server/basedir-includes/oieserver.ps1 +++ b/server/basedir-includes/oieserver.ps1 @@ -3,62 +3,64 @@ # SPDX-FileCopyrightText: 2025 Tony Germano and Mitch Gaffigan # -# ============================================================================= -# Open Integration Engine Server Launcher Script (PowerShell Version) -# -# Description: -# This script is the main launcher for the Open Integration Engine (OIE) -# server. It prepares the Java environment and executes the server launcher -# JAR file. -# -# The script automatically finds a compatible Java runtime (version 17+ by -# default) by searching for a valid executable in the following priority order: -# 1. The OIE_JAVA_PATH environment variable. -# 2. The -java-cmd directive in the oieserver.vmoptions file or included -# .vmoptions files. Must specify the path to the 'java' executable. -# This is the preferred way to declare the desired version for running -# the server and can be overridden by OIE_JAVA_PATH. Can be a relative -# path from the location of this script. -# 3. The JAVA_HOME environment variable. -# 4. The 'java' command available in the system's PATH. -# -# It also parses the 'oieserver.vmoptions' file to configure JVM options, -# system properties (-D...), and classpath modifications. -# -# Usage: -# ./oieserver.ps1 [app-arguments] -# -# All [app-arguments] are passed directly to the underlying Java application -# (com.mirth.connect.server.launcher.MirthLauncher). -# -# Configuration via Environment Variables: -# OIE_JAVA_PATH - (Highest priority) Set the full path to the 'java' -# executable to be used. Can be a relative path from the -# location of this script or a tilde path -# (e.g., ~/path/to/java). -# JAVA_HOME - Set the path to the root of a Java installation. The -# script will look for 'bin/java' within this path. -# ============================================================================= - -# --- Script Parameters and Initial Variables --- +<# +.SYNOPSIS + Open Integration Engine Server Launcher Script (PowerShell Version) + +.DESCRIPTION + This script is the main launcher for the Open Integration Engine (OIE) + server. It prepares the Java environment and executes the server launcher + JAR file. + + The script automatically finds a compatible Java runtime (version 17+ by + default) by searching for a valid executable in the following priority order: + + 1. The OIE_JAVA_PATH environment variable. + 2. The -java-cmd directive in the oieserver.vmoptions file or included .vmoptions + files. Must specify the path to the 'java' executable. This is the preferred + way to declare the desired version for running the server and can be overridden + by OIE_JAVA_PATH. Can be a relative path from the location of this script. + 3. The JAVA_HOME environment variable. + 4. The 'java' command available in the system's PATH. + + It also parses the 'oieserver.vmoptions' file to configure JVM options, + system properties (-D...), and classpath modifications. + +.NOTES + Configuration via Environment Variables: + OIE_JAVA_PATH - (Highest priority) Set the full path to the 'java' + executable to be used. Can be a relative path from the + location of this script or a tilde path + (e.g., ~/path/to/java). + JAVA_HOME - Set the path to the root of a Java installation. The + script will look for 'bin/java' within this path. + +.PARAMETER AppArgs + Arguments passed directly to the underlying Java application + (com.mirth.connect.server.launcher.MirthLauncher). + +.EXAMPLE + ./oieserver.ps1 + +.EXAMPLE + $env:OIE_JAVA_PATH = 'C:\path\to\java.exe'; + ./oieserver.ps1 -Dproperty=value --some-arg value +#> + param( - [string[]]$AppArgs = $args + [parameter(ValueFromRemainingArguments = $true)][string[]] $AppArgs ) +# Stop on any error and exit non-zero +$ErrorActionPreference = "Stop" $MinJavaVersion = 17 # Set OieHome to the script directory using PowerShell's built-in variable $OieHome = $PSScriptRoot -# The engine expects it's working directory to be OieHome -try { - Set-Location -Path $OieHome -ErrorAction Stop -} -catch { - Write-Error "Could not change to the OieHome directory: $OieHome" - exit 1 -} $LauncherJar = Join-Path -Path $OieHome -ChildPath "mirth-server-launcher.jar" -$script:Classpath = $LauncherJar # Use script scope to be modifiable by functions + # Use script scope to be modifiable by functions +$script:Classpath = [System.Collections.Generic.List[string]]::new() +$script:Classpath.Add($LauncherJar) $script:VmOptions = [System.Collections.Generic.List[string]]::new() # This will hold the validated path to the Java executable. @@ -68,14 +70,14 @@ $script:VmOptionsJavaCmd = $null $script:VmOptionsJavaCmdFile = $null # --- Function to resolve a path to a canonical absolute path --- -function Resolve-CanonicalPath { - param( - [string]$PathToResolve - ) - +function Resolve-CanonicalPath([string]$PathToResolve) { # Explicitly handle simple tilde expansion first (`~/` or `~`) if ($PathToResolve -match '^~(/|$)') { - $PathToResolve = ($PathToResolve -replace '^~/', "$env:USERPROFILE/") -replace '^~$', $env:USERPROFILE + $homePath = $env:HOME + if ([string]::IsNullOrWhiteSpace($homePath)) { + $homePath = $env:USERPROFILE + } + $PathToResolve = ($PathToResolve -replace '^~/', "$($homePath)/") -replace '^~$', $homePath } # If the path is not absolute, assume it's relative to OieHome @@ -97,11 +99,7 @@ function Resolve-CanonicalPath { } # --- Function to safely expand specific variable formats in a string --- -function Expand-LineVariables { - param( - [string]$Line - ) - +function Expand-LineVariables([string]$Line) { # Define a "match evaluator" script block. This block will be called # for every match the regex finds. $evaluator = { @@ -117,8 +115,13 @@ function Expand-LineVariables { $varValue = (Get-Variable -Name "env:$varName" -ErrorAction SilentlyContinue).Value } - # Return the found value. This will be the replacement for the match. - return $varValue + # If a value was found (it's not null), return it. + # Otherwise, return the original text that was matched (e.g., "${UNDEFINED_VAR}"). + if ($null -ne $varValue) { + return $varValue + } else { + return $match.Value + } } # Define the regex pattern to find ${...} variables. @@ -131,11 +134,7 @@ function Expand-LineVariables { } # --- Function to validate Java version --- -function Test-IsValidJavaVersion { - param( - [string]$JavaCmd - ) - +function Test-IsValidJavaVersion([string] $JavaCmd) { # Check if the command is found and is executable if (-not (Get-Command $JavaCmd -ErrorAction SilentlyContinue)) { return $false @@ -156,18 +155,11 @@ function Test-IsValidJavaVersion { # Extract the major version number. This works for formats like "1.8.0" and "17.0.2". $match = $versionOutput | Select-String -Pattern '"(\d+)\.' - if ($match -and ($match.Matches[0].Groups[1].Value -as [int]) -ge $MinJavaVersion) { - return $true - } else { - return $false - } + return ($match -and ($match.Matches[0].Groups[1].Value -as [int]) -ge $MinJavaVersion) } # --- Function to parse vmoptions file --- -function Parse-VmOptions { - param( - [string]$File - ) +function Parse-VmOptions([string] $File) { if (-not (Test-Path -LiteralPath $File -PathType Leaf)) { Write-Warning "VM options file not found: $File" @@ -194,13 +186,14 @@ function Parse-VmOptions { Parse-VmOptions -File $includedFile } elseif ($line -match '^-classpath\s+(.+)') { - $script:Classpath = $matches[1].Trim() + $script:Classpath.Clear() + $script:Classpath.Add($matches[1].Trim()) } elseif ($line -match '^-classpath/a\s+(.+)') { - $script:Classpath = "$($script:Classpath)$([System.IO.Path]::PathSeparator)$($matches[1].Trim())" + $script:Classpath.Add($($matches[1].Trim()) } elseif ($line -match '^-classpath/p\s+(.+)') { - $script:Classpath = "$($matches[1].Trim())$([System.IO.Path]::PathSeparator)$($script:Classpath)" + $script:Classpath.Insert(0, $($matches[1].Trim()) } elseif ($line -match '^-java-cmd\s+(.+)') { # Store the path and the file it was found in. Validation is deferred. @@ -208,7 +201,7 @@ function Parse-VmOptions { $script:VmOptionsJavaCmdFile = $File } else { - $script:VmOptions.Add($line) | Out-Null + $script:VmOptions.Add($line) } } } @@ -219,9 +212,9 @@ function Parse-VmOptions { Parse-VmOptions -File (Join-Path -Path $OieHome -ChildPath "oieserver.vmoptions") # 2. Ensure the launcher JAR is always in the classpath. -if ($script:Classpath -notmatch [regex]::Escape($LauncherJar)) { +if (-not $script:Classpath.Contains($LauncherJar)) { Write-Host "Info: Prepending mirth-server-launcher.jar to the classpath." -ForegroundColor Green - $script:Classpath = "$($LauncherJar)$([System.IO.Path]::PathSeparator)$($script:Classpath)" + $script:Classpath.Insert(0, $LauncherJar) } # 3. Discover the Java executable using the documented priority order. @@ -234,7 +227,6 @@ if (-not [string]::IsNullOrWhiteSpace($env:OIE_JAVA_PATH)) { $FinalJavaCmd = $resolvedPath } else { Write-Error "'$resolvedPath' is specified by the OIE_JAVA_PATH environment variable, which is not a valid Java executable of at least version $MinJavaVersion. Exiting." - exit 1 } } @@ -251,7 +243,7 @@ if (-not $FinalJavaCmd -and -not [string]::IsNullOrWhiteSpace($script:VmOptionsJ # Check JAVA_HOME (no fail-fast). if (-not $FinalJavaCmd -and (Test-Path -Path $env:JAVA_HOME -PathType Container)) { - $javaHomePath = Join-Path -Path $env:JAVA_HOME -ChildPath "bin\java" + $javaHomePath = Join-Path -Path (Join-Path -Path $env:JAVA_HOME -ChildPath "bin") -ChildPath "java" if (Test-IsValidJavaVersion -JavaCmd $javaHomePath) { Write-Host "Info: Found suitable java version specified by the JAVA_HOME environment variable" -ForegroundColor Green $FinalJavaCmd = $javaHomePath @@ -275,20 +267,26 @@ if (-not $FinalJavaCmd) { # 4. Final check for a valid Java path before execution. if (-not $FinalJavaCmd) { Write-Error "Could not find a Java $($MinJavaVersion)+ installation. Please configure -java-cmd in conf/custom.vmoptions, set OIE_JAVA_PATH, set JAVA_HOME, or ensure 'java' in your PATH is version $($MinJavaVersion) or higher." - exit 1 } # 5. Assemble final arguments and launch the process. $javaOpts = [System.Collections.Generic.List[string]]::new() $javaOpts.AddRange($script:VmOptions) $javaOpts.Add("-cp") -$javaOpts.Add('"{0}"' -f $script:Classpath) +$javaOpts.Add($script:Classpath -join [System.IO.Path]::PathSeparator) $javaOpts.Add("com.mirth.connect.server.launcher.MirthLauncher") -$javaOpts.AddRange($AppArgs) +if ($AppArgs) { $javaOpts.AddRange($AppArgs) } # Launch Open Integration Engine Write-Host "Starting Open Integration Engine..." -ForegroundColor Green -Write-Host "$FinalJavaCmd $($javaOpts -join ' ')" +Write-Host ("$FinalJavaCmd " + (($javaOpts | %{ "`"$_`"" }) -join ' ')); -& $FinalJavaCmd $javaOpts -exit $LASTEXITCODE +# The engine expects it's working directory to be OieHome +Push-Location -Path $OieHome +try { + & $FinalJavaCmd @javaOpts + exit $LASTEXITCODE +} +finally { + Pop-Location +}