Project

General

Profile

server.sh

Sergey Ivanovskiy, 07/27/2026 04:49 PM

Download (13.1 KB)

 
1
#!/bin/bash
2

    
3
# standard FWD server startup script
4

    
5
# defaults
6
whole_path="$(cd "$(dirname "$0")"; pwd)/$(basename "$0")"
7
echo $whole_path
8
srcname=$(basename "$whole_path")
9
srcpath=$(dirname "$whole_path")
10
echo $srcname $srcpath
11
[ ! -f ${srcpath}/appname ] && echo "FATAL: No appname to source" && exit
12
[ -z "$appname" ] && . ${srcpath}/appname
13
suspend="n"
14
hprof=""
15
prog="java"
16
dump="-XX:+HeapDumpOnOutOfMemoryError"
17
srvr="-server"
18
agent=""
19
heap=8192
20
cfg="server.xml"
21
port=""
22
portbase=3333
23
portstep=100
24
mode=""
25
statcode="n"
26
instance=0
27
batch=""
28
test=0
29
appjar=$appname
30
applib=""
31
open_api=""
32
entrypoint="com.goldencode.p2j.main.FwdLauncher"
33
appcds=0
34
max_classes_default=100000
35
cdsparams=""
36
jmx_port=22100
37
gc_log="server_garbage_collection_stats.log"
38
archive_retention=3
39
# Default log directories
40
default_log_dirs=".:./logs:../logs"
41

    
42
# Determine the configured JDK
43
#jver is 18 for java 1.8, 15 for java 1.5, 110 for java 11, 170 for java 17 etc.
44
jver=$(${prog} -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F '.' '{sub("^$", "0", $2); print $1$2}')
45

    
46
# Function to clear *.log* in each directory passed (':' separated list of directories)
47
clear_logs()
48
{
49
   local paths
50
   IFS=':' read -r -a paths <<< "${@}"
51
   for path in "${paths[@]}"; do
52
      if [ -d "$path" ]; then
53
         log_files=("${path}"/*.log)
54
         [ ${#log_files[@]} -gt 0 ] && [ -f "${log_files[0]}" ] && rm -f ${path}/*.log*
55
      fi
56
   done
57
}
58

    
59
# Ensure UTF-8 is set
60
ensure_utf8_lang()
61
{
62
   # Get the current LANG setting and encoding portion (after the dot '.')
63
   current_lang="${LANG}"
64
   encoding="${current_lang##*.}"
65

    
66
   shopt -s nocasematch
67
   # Check if the encoding is not UTF-8
68
   if [[ ! "$encoding" =~ ^utf-?8$ ]]; then
69
      # Warn the user
70
      echo "Warning: The current LANG setting (${current_lang}) is not using UTF-8 encoding."
71

    
72
      # Set LANG to LANGUAGE.UTF-8, or fallback to en_US.UTF-8 if LANGUAGE is not set
73
      [ -z "$LANGUAGE" ] && new_lang="en_US.UTF-8" || new_lang="${LANGUAGE}.UTF-8"
74

    
75
      # Apply the new LANG setting
76
      export LANG="$new_lang"
77

    
78
      # Inform the user
79
      echo "LANG has been updated to: ${LANG}"
80
   fi
81
   shopt -u nocasematch
82
}
83

    
84
# Setup rolling generation for the archive
85
roll_cds_archive()
86
{
87
   local archive_file=$1
88
   local retention=$2
89

    
90
   if [ -f "${archive_file}.${retention}" ]; then
91
      rm -f "${archive_file}.${retention}"
92
   fi
93
   for ((i=retention-1; i>=0; i--)); do
94
      if [ -f "${archive_file}.${i}" ]; then
95
         mv "${archive_file}.${i}" "${archive_file}.$((i+1))"
96
      fi
97
   done
98
   if [ -f "${archive_file}" ]; then
99
      mv "${archive_file}" "${archive_file}.0"
100
   fi
101
}
102

    
103
# Setup Application Class Data Sharing
104
setup_appcds()
105
{
106
   local appcds=$1
107
   local max_classes=$2
108

    
109
   unfiltered_cds="cds-class-list-unedited.lst" 
110
   [ -f "cds-blacklist.lst" ] && cds_filter=$(cat "cds-blacklist.lst") || cds_filter=""
111
   filtered_cds="cds-class-list-filtered.lst" 
112
   cds_archive="cds-archive.jsa" 
113
   xlog=""
114
   if [ $appcds -eq 1 ]; then
115
      # Full recording
116
      xshare="-Xshare:off" 
117
      xxdumploaded="-XX:DumpLoadedClassList=${unfiltered_cds}" 
118

    
119
      # Perform rolling archive
120
      roll_cds_archive $cds_archive $archive_retention
121
   elif [ $appcds -eq 2 ]; then
122
      # Archive previous full recording
123
      if [ ! -f $unfiltered_cds ]; then
124
         echo "File $unfiltered_cds is missing. Make sure you started the server with parameter \"-x1\" at least once before." 
125
         exit 1
126
      else
127
         echo "Using class list created at $(stat -c "%w" $unfiltered_cds | cut -d '.' -f 1)".
128
      fi
129
      awk 'length($0) <= 4096' $unfiltered_cds | head -n $max_classes > $filtered_cds
130
      for p in $cds_filter; do
131
         sed -i "/$p/d" $filtered_cds
132
      done
133
      xshare="-Xshare:dump" 
134
      xxsharedclass="-XX:SharedClassListFile=${filtered_cds}" 
135
      xxsharedarchive="-XX:SharedArchiveFile=$cds_archive" 
136
      xlog="-Xlog:cds:file=cds.log -Xlog:class+load:file=cds-class-load.log"
137
      output_redir="> /dev/null 2>&1"
138
   elif [ $appcds -eq 3 ]; then
139
      if [ ! -f $cds_archive ]; then
140
         echo "Archive $cds_archive is missing. Make sure you have previously started the server with parameter \"-x2\"." 
141
         exit 1
142
      else
143
         echo "Using archive created at $(stat -c "%w" $cds_archive | cut -d '.' -f 1)".
144
      fi
145
      # Start with previous archived list
146
      xshare="-Xshare:on" 
147
      xxsharedarchive="-XX:SharedArchiveFile=$cds_archive" 
148
   fi
149
   # Setup the cds parameters from above
150
   cdsparams="${xshare} ${xxsharedarchive} ${xxdumploaded} ${xxsharedclass} ${xlog}" 
151
}
152

    
153
# Handler to forward the kill signal to the FWD server
154
shutdown_handler()
155
{
156
    kill -SIGTERM "$pid"
157
    
158
    wait "$pid"
159
}
160

    
161
show_usage()
162
{
163
   i=0;        usage[$i]="\nUsage: $0 [-dyptksw1a] [-o<path>] [-m{1|0}] [-b<process>] [-z<cp>] [-h<heap>] [-c<cfg>] [-i<instance>] [-r<profile>] [-j<jar>]
164
[--gc] [--jmx_port=<port>]"
165
   [[ $jver -ge 110 ]] && usage[$i]="${usage[$i]} [-x{1|2 [max_classes]|3}]"
166
   i=$((i+1)); usage[$i]="\nStart the FWD server."
167
   i=$((i+1)); usage[$i]="\nWhere:"
168
   i=$((i+1)); usage[$i]="\t-d = enable JVM debug mode (debugger port will be set as 2080 + instance #)"
169
   i=$((i+1)); usage[$i]="\t-y = suspend the JVM on startup when debug mode is enabled (does not suspend by default)"
170
   i=$((i+1)); usage[$i]="\t-p = enable JVM hprof (profiling output to gc.log)"
171
   i=$((i+1)); usage[$i]="\t-t = test mode (displays the command but doesn't execute)"
172
   i=$((i+1)); usage[$i]="\t-k = kill mode (shuts down the specified/default instance)"
173
   i=$((i+1)); usage[$i]="\t-s = status mode (displays the status of the specified/default instance)"
174
   i=$((i+1)); usage[$i]="\t-w = wait mode (does not return until the specified/default instance is STATUS_RUNNING)"
175
   i=$((i+1)); usage[$i]="\t-1 = enable C1 HotSpot compiler (client compiler for the JVM)"
176
   i=$((i+1)); usage[$i]="\t-a = enable agent for method profiling (not for production use!)"
177
   i=$((i+1)); usage[$i]="\t-o = Enable heap dump on OOME to given path"
178
   i=$((i+1)); usage[$i]="\t-m1 = start collecting method profile data (must have started server with -a)"
179
   i=$((i+1)); usage[$i]="\t-m0 = stop collecting method profile data (must have started server with -a)"
180
   i=$((i+1)); usage[$i]="\t-b = launch the batch program <process>";
181
   i=$((i+1)); usage[$i]="\t-z = override the classpath with <cp>. See cpath.txt for the default contents. $(echo $cpath > cpath.txt)"
182
   i=$((i+1)); usage[$i]="\t-h = set JVM max heap size to <heap> (must be an integer number of MB)"
183
   i=$((i+1)); usage[$i]="\t-c = use FWD bootstrap config file <cfg> (defaults to server.xml)"
184
   i=$((i+1)); usage[$i]="\t-i = server instance number (0..9, assigns the FWD server port, defaults to 0)"
185
   i=$((i+1)); usage[$i]="\t-r = pass <profile> to ServerDriver via \"-profile\""
186
   i=$((i+1)); usage[$i]="\t-j = main application jar name (default is ${appjar})"
187
   if [[ $jver -ge 110 ]]; then
188
      i=$((i+1)); usage[$i]="\t-x1 = appcds (Application Class Data Sharing): Record the name of every class loaded by the VM until the server is stopped (Java 11 or higher)"
189
      i=$((i+1)); usage[$i]="\t-x2 = appcds: Create an archive of the classes recorded via -x1; specify the number of classes included, default to $max_classes_default"
190
      i=$((i+1)); usage[$i]="\t-x3 = appcds: Loads at startup the archive of classes created via -x2"
191
   fi
192
   i=$((i+1)); usage[$i]="\t--gc monitor garbage collection statistics via Java Management Extensions (JMX). Data logged to file \"${gc_log}\""
193
   i=$((i+1)); usage[$i]="\t--jmx_port=<port> Set JMX port to <port> (default is ${jmx_port})"
194
   i=$((i+1)); usage[$i]="\t--clear_logs[=<dirlist>] Clear '*.log*' files in directories listed, each seperated by a colon (default is ${default_log_dirs})"
195
   i=$((i+1)); usage[$i]="\n -? or --help = show usage."
196

    
197
   for i in "${usage[@]}"; do
198
      echo -e $i
199
   done
200
}
201

    
202
# process options
203
while getopts "?dyptksw1a:z:h:c:i:b:m:o:j:r:x:-:" opt; do
204
   case $opt in
205
      d  ) debug=true ;;
206
      y  ) suspend="y" ;;
207
      p  ) hprof="-Xrunhprof:heap=dump,doe=y,format=b,file=gc.log" ;;
208
      t  ) test=1 ;;
209
      k  ) mode="-k"; statcode="y" ;;
210
      s  ) mode="-s"; statcode="y" ;;
211
      w  ) mode="-w -1"; statcode="y" ;;
212
      b  ) batch="-b $OPTARG" ;;
213
      a  ) agent="-javaagent:${OPTARG}" ;;
214
      m  ) mode="-m $OPTARG"; statcode="y" ;;
215
      o  ) dump+=" -XX:HeapDumpPath=${OPTARG}" ;;
216
      1  ) srvr="" ;;
217
      z  ) cpath=$OPTARG ;;
218
      h  ) heap=$OPTARG ;;
219
      c  ) cfg=$OPTARG ;; 
220
      i  ) instance=$OPTARG ;;
221
      j  ) appjar=$OPTARG ;;
222
      r  ) profile="-profile $OPTARG" ;;
223
      x  ) appcds=$OPTARG
224
           if [[ ${!OPTIND} =~ ^[0-9]+$ ]]; then
225
              max_classes=${!OPTIND}
226
              OPTIND=$((OPTIND + 1))
227
           else
228
              max_classes=$max_classes_default
229
           fi
230
           ;;
231
      -  ) case ${OPTARG} in
232
              "gc"         ) enable_gc_stats=true ;;
233
              "jmx_port="* ) jmx_port=$(echo $OPTARG | cut -d "=" -f2) ;;
234
              "clear_logs"*) clear_logs_flag=true
235
                             if [[ "${OPTARG}" == "clear_logs="* ]]; then
236
                                log_dirs=$(echo $OPTARG | cut -d "=" -f2)
237
                             else
238
                                log_dirs=$default_log_dirs
239
                             fi ;;
240
              "help"       ) show_usage
241
                             exit 1 ;;
242
           esac ;;
243
      \? ) show_usage
244
           exit 1 ;;
245
   esac
246
done
247
shift $(($OPTIND - 1))
248

    
249
# Clear logs, if requested
250
if [ "$clear_logs_flag" = true ]; then
251
   clear_logs "$log_dirs"
252
fi
253

    
254
# getcp.sh is a helper for setting up the classpath for the given application. 
255
# This allows more flexibility in where the FWD configuration is placed. It can be one directory up,
256
# and then in lib, or in /opt/fwd/lib, or specified by FWD_LIB exported or up to the build directory,
257
# then down to lib. If the helper isn't found, we can fall back to the original method.
258
helper=$(cd $srcpath/..; echo $(pwd))
259
export PATH=$helper:$PATH
260

    
261
if [ $jver -lt 170 ]; then
262
   echo "Using invalid java (must be 17 or greater). Exiting." 
263
   exit 1
264
fi
265

    
266
spi="-Djava.locale.providers=SPI,CLDR,COMPAT" 
267
if command -v getcp.sh >/dev/null 2>&1; then
268
   cpath=$(getcp.sh "$appjar" "$srcpath")
269
else
270
   cpath="" 
271
   shopt -s nullglob
272
   for f in ../lib/*.jar
273
   do
274
      if [[ "$f" != "../lib/${appjar}.jar" ]]; then
275
         cpath="${cpath}:$f" 
276
      fi
277
   done
278
fi
279

    
280
# Add extra jars, if needed
281
if [ -n "$applib" ] && [ -d "../${applib}" ]; then
282
   for f in ../${applib}/*.jar
283
   do
284
       cpath=$f:$cpath
285
   done
286
fi
287

    
288
# Additional parameters for Java 17
289
if [ $jver -ge 170 ]; then
290
   open_api="--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.naming/javax.naming=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED"
291
fi
292

    
293
# Compatibility with Java 25
294
if [ $jver -gt 240 ]; then
295
   open_api+=" --enable-native-access=ALL-UNNAMED" 
296
fi
297

    
298
# final arg prep
299
cpath="-classpath $cpath"
300
dport=$((2080 + $instance))
301
sport=$(($portbase + $instance))
302
iport=$(($portbase + $instance + $portstep))
303
port="security:provider:name=conscrypt net:connection:secure=true net:server:secure_port=$sport net:server:insecure_port=$iport"
304

    
305
if [ "$debug" == true ]; then
306
   [ -f "/.dockerenv" ] && daddress="0.0.0.0:${dport}" || daddress="${dport}"
307
   dtxt="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=${daddress},server=y,suspend=${suspend}" 
308
fi
309

    
310
perf="-XX:GCTimeRatio=19 -XX:MaxTenuringThreshold=7 -XX:StringTableSize=1000003"
311
maxheap="-Xmx"$heap"m"
312
if [ "$enable_gc_stats" = "true" ]; then
313
   jmxMonitoringD="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
314
   jmxMonitoringPort="-Dcom.sun.management.jmxremote.port=${jmx_port} -Dcom.sun.management.jmxremote.rmi.port=${jmx_port}"
315
   gcMonitoringXX="-XX:+PrintGCDetails"
316
   [ $jver -ge 170 ] && Xlog="-Xlog:gc*:file=${gc_log}:time" || Xlog="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:${gc_log}"
317
   jmx="${jmxMonitoringD} ${jmxMonitoringPort} ${gcMonitoringXX} -verbose:gc ${Xlog}"
318
fi
319

    
320
# Determine if AppCDS (Application Class Data Sharing) will change startup
321
if [[ $jver -ge 110 ]] && [[ $appcds -ge 1 && $appcds -le 3 ]]; then
322
   setup_appcds $appcds $max_classes
323
fi
324

    
325
# Ensure UTF-8 encoding
326
original_lang="${LANG}"
327
ensure_utf8_lang
328

    
329
# run the FWD server
330
JAVA_CMD="$prog $dump $perf $hprof $maxheap $srvr $jmx $dtxt $agent $spi $open_api $cpath $cdsparams $entrypoint $mode $batch $cfg $port $profile"
331
if [ $test -eq 1 ] ; then
332
   echo "$JAVA_CMD" "$@"
333
elif [ $statcode = "y" ]; then
334
   $JAVA_CMD "$@" 2> /dev/null
335
else
336
   trap 'shutdown_handler' SIGTERM SIGINT
337
   eval $JAVA_CMD "$@" $output_redir \&
338
   pid=$!
339

    
340
   wait "$pid"
341
fi
342

    
343
rc=$?
344

    
345
# Restore the original LANG setting, if sourced and set
346
[ "${BASH_SOURCE[0]}" != "${0}" ] && [ ."$original_lang". != ".." ] && export LANG="$original_lang"
347

    
348
if [ $statcode = "y" ] && [ $test -ne 1 ]; then
349
   case $rc in
350
      0  ) status="STATUS_RUNNING" ;;
351
      1  ) status="STATUS_TIMEOUT" ;;
352
      2  ) status="STATUS_UNKNOWN" ;;
353
      3  ) status="STATUS_STOPPED" ;;
354
   esac
355
   echo "Result =" $status
356
fi
357

    
358
# Check if the archive was created successfully when appcds=2
359
if [ $appcds -eq 2 ] && [ ! -f $cds_archive ]; then
360
   echo "Warning: Archive $cds_archive was not created successfully."
361
fi