Project

General

Profile

server.sh

Roger Borrello, 12/11/2025 04:51 PM

Download (12.7 KB)

 
1
#!/bin/bash
2

    
3
# standard FWD server startup script
4

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

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

    
53
# Ensure UTF-8 is set
54
ensure_utf8_lang()
55
{
56
   # Get the current LANG setting and encoding portion (after the dot '.')
57
   current_lang="${LANG}"
58
   encoding="${current_lang##*.}"
59

    
60
   shopt -s nocasematch
61
   # Check if the encoding is not UTF-8
62
   if [[ ! "$encoding" =~ ^utf-?8$ ]]; then
63
      # Warn the user
64
      echo "Warning: The current LANG setting (${current_lang}) is not using UTF-8 encoding."
65

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

    
69
      # Apply the new LANG setting
70
      export LANG="$new_lang"
71

    
72
      # Inform the user
73
      echo "LANG has been updated to: ${LANG}"
74
   fi
75
   shopt -u nocasematch
76
}
77

    
78
# Setup rolling generation for the archive
79
roll_cds_archive()
80
{
81
   local archive_file=$1
82
   local retention=$2
83

    
84
   if [ -f "${archive_file}.${retention}" ]; then
85
      rm -f "${archive_file}.${retention}"
86
   fi
87
   for ((i=retention-1; i>=0; i--)); do
88
      if [ -f "${archive_file}.${i}" ]; then
89
         mv "${archive_file}.${i}" "${archive_file}.$((i+1))"
90
      fi
91
   done
92
   if [ -f "${archive_file}" ]; then
93
      mv "${archive_file}" "${archive_file}.0"
94
   fi
95
}
96

    
97
# Setup Application Class Data Sharing
98
setup_appcds()
99
{
100
   local appcds=$1
101
   local max_classes=$2
102

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

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

    
147
show_usage()
148
{
149
   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>]
150
[--gc] [--jmx_port=<port>] [-x{1|2 [max_classes]|3}]"
151
   i=$((i+1)); usage[$i]="\nStart the FWD server."
152
   i=$((i+1)); usage[$i]="\nWhere:"
153
   i=$((i+1)); usage[$i]="\t-d = enable JVM debug mode (debugger port will be set as 2080 + instance #)"
154
   i=$((i+1)); usage[$i]="\t-y = suspend the JVM on startup when debug mode is enabled (does not suspend by default)"
155
   i=$((i+1)); usage[$i]="\t-p = enable JVM hprof (profiling output to gc.log)"
156
   i=$((i+1)); usage[$i]="\t-t = test mode (displays the command but doesn't execute)"
157
   i=$((i+1)); usage[$i]="\t-k = kill mode (shuts down the specified/default instance)"
158
   i=$((i+1)); usage[$i]="\t-s = status mode (displays the status of the specified/default instance)"
159
   i=$((i+1)); usage[$i]="\t-w = wait mode (does not return until the specified/default instance is STATUS_RUNNING)"
160
   i=$((i+1)); usage[$i]="\t-1 = enable C1 HotSpot compiler (client compiler for the JVM)"
161
   i=$((i+1)); usage[$i]="\t-a = enable agent for method profiling (not for production use!)"
162
   i=$((i+1)); usage[$i]="\t-o = Enable heap dump on OOME to given path"
163
   i=$((i+1)); usage[$i]="\t-m1 = start collecting method profile data (must have started server with -a)"
164
   i=$((i+1)); usage[$i]="\t-m0 = stop collecting method profile data (must have started server with -a)"
165
   i=$((i+1)); usage[$i]="\t-b = launch the batch program <process>";
166
   i=$((i+1)); usage[$i]="\t-z = override the classpath with <cp>. See cpath.txt for the default contents. $(echo $cpath > cpath.txt)"
167
   i=$((i+1)); usage[$i]="\t-h = set JVM max heap size to <heap> (must be an integer number of MB)"
168
   i=$((i+1)); usage[$i]="\t-c = use FWD bootstrap config file <cfg> (defaults to server.xml)"
169
   i=$((i+1)); usage[$i]="\t-i = server instance number (0..9, assigns the FWD server port, defaults to 0)"
170
   i=$((i+1)); usage[$i]="\t-r = pass <profile> to ServerDriver via \"-profile\""
171
   i=$((i+1)); usage[$i]="\t-j = main application jar name (default is ${appjar})"
172
   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)"
173
   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"
174
   i=$((i+1)); usage[$i]="\t-x3 = appcds: Loads at startup the archive of classes created via -x2"
175
   i=$((i+1)); usage[$i]="\t--gc monitor garbage collection statistics via Java Management Extensions (JMX). Data logged to file \"${gc_log}\""
176
   i=$((i+1)); usage[$i]="\t--jmx_port=<port> Set JMX port to <port> (default is ${jmx_port})"
177
   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})"
178
   i=$((i+1)); usage[$i]="\n -? or --help = show usage."
179

    
180
   for i in "${usage[@]}"; do
181
      echo -e $i
182
   done
183
}
184

    
185
# process options
186
while getopts "?dyptksw1a:z:h:c:i:b:m:o:j:r:x:-:" opt; do
187
   case $opt in
188
      d  ) debug=true ;;
189
      y  ) suspend="y" ;;
190
      p  ) hprof="-Xrunhprof:heap=dump,doe=y,format=b,file=gc.log" ;;
191
      t  ) test=1 ;;
192
      k  ) mode="-k"; statcode="y" ;;
193
      s  ) mode="-s"; statcode="y" ;;
194
      w  ) mode="-w -1"; statcode="y" ;;
195
      b  ) batch="-b $OPTARG" ;;
196
      a  ) agent="-javaagent:${OPTARG}" ;;
197
      m  ) mode="-m $OPTARG"; statcode="y" ;;
198
      o  ) dump+=" -XX:HeapDumpPath=${OPTARG}" ;;
199
      1  ) srvr="" ;;
200
      z  ) cpath=$OPTARG ;;
201
      h  ) heap=$OPTARG ;;
202
      c  ) cfg=$OPTARG ;; 
203
      i  ) instance=$OPTARG ;;
204
      j  ) appjar=$OPTARG ;;
205
      r  ) profile="-profile $OPTARG" ;;
206
      x  ) appcds=$OPTARG
207
           if [[ ${!OPTIND} =~ ^[0-9]+$ ]]; then
208
              max_classes=${!OPTIND}
209
              OPTIND=$((OPTIND + 1))
210
           else
211
              max_classes=$max_classes_default
212
           fi
213
           ;;
214
      -  ) case ${OPTARG} in
215
              "gc"         ) enable_gc_stats=true ;;
216
              "jmx_port="* ) jmx_port=$(echo $OPTARG | cut -d "=" -f2) ;;
217
              "clear_logs"*) clear_logs_flag=true
218
                             if [[ "${OPTARG}" == "clear_logs="* ]]; then
219
                                log_dirs=$(echo $OPTARG | cut -d "=" -f2)
220
                             else
221
                                log_dirs=$default_log_dirs
222
                             fi ;;
223
              "help"       ) show_usage
224
                             exit 1 ;;
225
           esac ;;
226
      \? ) show_usage
227
           exit 1 ;;
228
   esac
229
done
230
shift $(($OPTIND - 1))
231

    
232
# Clear logs, if requested
233
if [ "$clear_logs_flag" = true ]; then
234
   clear_logs "$log_dirs"
235
fi
236

    
237
# getcp.sh and getspi.sh are helpers for setting up the classpath and spi for the given application. 
238
# This allows more flexibility in where the FWD configuration is placed. It can be one directory up,
239
# and then in lib, or in /opt/fwd/lib, or specified by FWD_LIB exported or up to the build directory,
240
# then down to lib. If the helper isn't found, we can only fall back to the original method.
241
helper=$(cd $srcpath/..; echo $(pwd))
242
export PATH=$helper:$PATH
243
if [ ."$(which getcp.sh)". == ".." ]; then
244
   cpath=""
245
   for f in ../lib/*.jar
246
   do
247
      if [ $f != "../lib/${appjar}.jar" ] ; then
248
         cpath=$cpath:$f
249
      fi
250
   done
251
   spi="-Djava.locale.providers=SPI,CLDR,COMPAT"
252
   # in case we are running Java 8 build on Java 11+ VM
253
   [[ "$cpath" != *fwdspi.jar* ]] && cpath="${cpath}:../lib/spi/fwdspi.jar"
254
else
255
   cpath=$(getcp.sh "$appjar" $srcpath )
256
   spi=$(getspi.sh $cpath)
257
fi
258

    
259
# Add extra jars, if needed
260
if [ -n "$applib" ] && [ -d "../${applib}" ]; then
261
   for f in ../${applib}/*.jar
262
   do
263
       cpath=$f:$cpath
264
   done
265
fi
266

    
267
# Additional parameters for Java 17
268
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"
269

    
270
# final arg prep
271
cp="-classpath $cpath"
272
dport=$((2080 + $instance))
273
sport=$(($portbase + $instance))
274
iport=$(($portbase + $instance + $portstep))
275
port="net:connection:secure=true net:server:secure_port=$sport net:server:insecure_port=$iport"
276

    
277
if [ "$debug" == true ]; then
278
   [ -f "/.dockerenv" ] && daddress="0.0.0.0:${dport}" || daddress="${dport}"
279
   dtxt="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=${daddress},server=y,suspend=${suspend}" 
280
fi
281

    
282
perf="-XX:GCTimeRatio=19 -XX:MaxTenuringThreshold=7 -XX:StringTableSize=1000003"
283
maxheap="-Xmx"$heap"m"
284
if [ "$enable_gc_stats" = "true" ]; then
285
   jmxMonitoringD="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false"
286
   jmxMonitoringPort="-Dcom.sun.management.jmxremote.port=${jmx_port} -Dcom.sun.management.jmxremote.rmi.port=${jmx_port}"
287
   gcMonitoringXX="-XX:+PrintGCDetails"
288
   Xlog="-Xlog:gc*:file=${gc_log}:time" || Xlog="-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:${gc_log}"
289
   jmx="${jmxMonitoringD} ${jmxMonitoringPort} ${gcMonitoringXX} -verbose:gc ${Xlog}"
290
fi
291

    
292
# Determine if AppCDS (Application Class Data Sharing) will change startup
293
if [[ $appcds -ge 1 && $appcds -le 3 ]]; then
294
   setup_appcds $appcds $max_classes
295
fi
296

    
297
# Ensure UTF-8 encoding
298
original_lang="${LANG}"
299
ensure_utf8_lang
300

    
301
# run the FWD server
302
if [ $test -eq 1 ] ; then
303
   echo $prog $dump $perf $hprof $maxheap $srvr $jmx $dtxt $agent $spi $open_api $cp $cdsparams $entrypoint $mode $batch $cfg $port $profile "$@"
304
elif [ $statcode = "y" ]; then
305
   $prog $dump $perf $hprof $maxheap $srvr $jmx $dtxt $agent $spi $open_api $cp $cdsparams $entrypoint $mode $batch $cfg $port $profile "$@" 2> /dev/null
306
else
307
   [ -f "/usr/local/bin/set_spawner.sh" ] && sudo set_spawner.sh --classpath=${cpath}
308
   eval $prog $dump $perf $hprof $maxheap $srvr $jmx $dtxt $agent $spi $open_api $cp $cdsparams $entrypoint $mode $batch $cfg $port $profile "$@" $output_redir
309
fi
310

    
311
rc=$?
312

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

    
316
if [ $statcode = "y" ] && [ $test -ne 1 ]; then
317
   case $rc in
318
      0  ) status="STATUS_RUNNING" ;;
319
      1  ) status="STATUS_TIMEOUT" ;;
320
      2  ) status="STATUS_UNKNOWN" ;;
321
      3  ) status="STATUS_STOPPED" ;;
322
   esac
323
   echo "Result =" $status
324
fi
325

    
326
# Check if the archive was created successfully when appcds=2
327
if [ $appcds -eq 2 ] && [ ! -f $cds_archive ]; then
328
   echo "Warning: Archive $cds_archive was not created successfully."
329
fi