30
30
import org .apache .logging .log4j .Logger ;
31
31
import org .exist .util .ConfigurationHelper ;
32
32
33
+ import javax .annotation .Nullable ;
33
34
import java .io .BufferedReader ;
34
35
import java .io .IOException ;
35
36
import java .io .InputStreamReader ;
38
39
import java .nio .file .Paths ;
39
40
import java .util .*;
40
41
import java .util .List ;
42
+ import java .util .regex .Matcher ;
43
+ import java .util .regex .Pattern ;
41
44
42
45
import static com .evolvedbinary .j8fu .Either .Left ;
43
46
import static com .evolvedbinary .j8fu .Either .Right ;
49
52
@ NotThreadSafe
50
53
class WindowsServiceManager implements ServiceManager {
51
54
55
+ /**
56
+ * See <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html#BABHDABI">Java - Non-Standard Options</a>.
57
+ */
58
+ private static final Pattern JAVA_CMDLINE_MEMORY_STRING = Pattern .compile ("([0-9]+)(g|G|m|M|k|K)?.*" );
59
+
52
60
private static final Logger LOG = LogManager .getLogger (WindowsServiceManager .class );
53
61
private static final String PROCRUN_SRV_EXE = "prunsrv-x86_64.exe" ;
54
62
private static final String SC_EXE = "sc.exe" ;
@@ -86,8 +94,8 @@ public void install() throws ServiceManagerException {
86
94
.orElse (existHome .resolve ("etc" ).resolve ("conf.xml" ));
87
95
88
96
final Properties launcherProperties = ConfigurationUtility .loadProperties ();
89
- final Optional <String > maxMemory = Optional .ofNullable (launcherProperties .getProperty (LAUNCHER_PROPERTY_MAX_MEM )).map ( s -> s + "m" );
90
- final String minMemory = launcherProperties .getProperty (LAUNCHER_PROPERTY_MIN_MEM , "128" ) + "m" ;
97
+ final Optional <String > maxMemory = Optional .ofNullable (launcherProperties .getProperty (LAUNCHER_PROPERTY_MAX_MEM )).flatMap ( WindowsServiceManager :: asJavaCmdlineMemoryString );
98
+ final Optional < String > minMemory = asJavaCmdlineMemoryString ( launcherProperties .getProperty (LAUNCHER_PROPERTY_MIN_MEM , "128" )) ;
91
99
92
100
final StringBuilder jvmOptions = new StringBuilder ();
93
101
jvmOptions .append ("-Dfile.encoding=UTF-8" );
@@ -117,7 +125,6 @@ public void install() throws ServiceManagerException {
117
125
"--ServiceUser=LocalSystem" , // TODO(AR) this changed from `LocalSystem` to `NT Authority\LocalService` in procrun 1.2.0, however our service won't seem to start under that account... we need to investigate!
118
126
"--Jvm=" + findJvm ().orElse ("auto" ),
119
127
"--Classpath=\" " + existHome .resolve ("lib" ).toAbsolutePath ().toString ().replace ('\\' , '/' ) + "/*\" " ,
120
- "--JvmMs=" + minMemory ,
121
128
"--StartMode=jvm" ,
122
129
"--StartClass=org.exist.service.ExistDbDaemon" ,
123
130
"--StartMethod=start" ,
@@ -127,7 +134,8 @@ public void install() throws ServiceManagerException {
127
134
"--JvmOptions=\" " + jvmOptions + "\" " ,
128
135
"--StartParams=\" " + configFile .toAbsolutePath ().toString () + "\" "
129
136
);
130
- maxMemory .ifPresent (xmx -> args .add ("--JvmMx=" + xmx ));
137
+ minMemory .flatMap (WindowsServiceManager ::asPrunSrvMemoryString ).ifPresent (xms -> args .add ("--JvmMs=" + xms ));
138
+ maxMemory .flatMap (WindowsServiceManager ::asPrunSrvMemoryString ).ifPresent (xmx -> args .add ("--JvmMx=" + xmx ));
131
139
132
140
try {
133
141
final Tuple2 <Integer , String > execResult = run (args , true );
@@ -367,4 +375,75 @@ private Tuple2<Integer, String> run(List<String> args, final boolean elevated) t
367
375
final int exitValue = process .waitFor ();
368
376
return Tuple (exitValue , output .toString ());
369
377
}
378
+
379
+ /**
380
+ * Transform the supplied memory string into a string
381
+ * that is compatible with the Java command line arguments for -Xms and -Xmx.
382
+ *
383
+ * See <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html#BABHDABI">Java - Non-Standard Options</a>.
384
+ *
385
+ * @param memoryString the memory string.
386
+ *
387
+ * @return a memory string compatible with java.exe.
388
+ */
389
+ static Optional <String > asJavaCmdlineMemoryString (final String memoryString ) {
390
+ // should optionally end in g|G|m|M|k|K
391
+ final Matcher mtcJavaCmdlineMemoryString = JAVA_CMDLINE_MEMORY_STRING .matcher (memoryString );
392
+ if (!mtcJavaCmdlineMemoryString .matches ()) {
393
+ // invalid java cmdline memory string
394
+ return Optional .empty ();
395
+ }
396
+
397
+ final String value = mtcJavaCmdlineMemoryString .group (1 );
398
+ @ Nullable final String mnemonic = mtcJavaCmdlineMemoryString .group (2 );
399
+
400
+ if (mnemonic == null ) {
401
+ // no mnemonic supplied, assume `m` for megabytes
402
+ return Optional .of (value + "m" );
403
+ }
404
+
405
+ // valid mnemonic supplied, so return as is (excluding any additional cruft)
406
+ return Optional .of (value + mnemonic );
407
+ }
408
+
409
+ /**
410
+ * Converts a memory string for the Java command line arguments -Xms or -Xmx, into
411
+ * a memory string that is understood by prunsrv.exe.
412
+ * prunsrv.exe expects an integer in megabytes.
413
+ *
414
+ * @param javaCmdlineMemoryString the memory strig as would be given to the Java command line.
415
+ *
416
+ * @return a memory string suitable for use with prunsrv.exe.
417
+ */
418
+ static Optional <String > asPrunSrvMemoryString (final String javaCmdlineMemoryString ) {
419
+ // should optionally end in g|G|m|M|k|K
420
+ final Matcher mtcJavaCmdlineMemoryString = JAVA_CMDLINE_MEMORY_STRING .matcher (javaCmdlineMemoryString );
421
+ if (!mtcJavaCmdlineMemoryString .matches ()) {
422
+ // invalid java cmdline memory string
423
+ return Optional .empty ();
424
+ }
425
+
426
+ long value = Integer .valueOf (mtcJavaCmdlineMemoryString .group (1 )).longValue ();
427
+ @ Nullable String mnemonic = mtcJavaCmdlineMemoryString .group (2 );
428
+ if (mnemonic == null ) {
429
+ mnemonic = "m" ;
430
+ }
431
+
432
+ switch (mnemonic .toLowerCase ()) {
433
+ case "k" :
434
+ value = value / 1024 ;
435
+ break ;
436
+
437
+ case "g" :
438
+ value = value * 1024 ;
439
+ break ;
440
+
441
+ case "m" :
442
+ default :
443
+ // do nothing, megabytes is the default!
444
+ break ;
445
+ }
446
+
447
+ return Optional .of (Long .toString (value ));
448
+ }
370
449
}
0 commit comments