|
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
|