Skip to content

Commit b8e7349

Browse files
authored
Merge pull request #4685 from evolvedbinary/hotfix/windows-service-manager-mem
Windows Service installer - prunsrv.exe expects memory to be supplied as an integer
2 parents 2a7085c + 2b2a4b9 commit b8e7349

File tree

2 files changed

+158
-4
lines changed

2 files changed

+158
-4
lines changed

exist-core/src/main/java/org/exist/launcher/WindowsServiceManager.java

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.apache.logging.log4j.Logger;
3131
import org.exist.util.ConfigurationHelper;
3232

33+
import javax.annotation.Nullable;
3334
import java.io.BufferedReader;
3435
import java.io.IOException;
3536
import java.io.InputStreamReader;
@@ -38,6 +39,8 @@
3839
import java.nio.file.Paths;
3940
import java.util.*;
4041
import java.util.List;
42+
import java.util.regex.Matcher;
43+
import java.util.regex.Pattern;
4144

4245
import static com.evolvedbinary.j8fu.Either.Left;
4346
import static com.evolvedbinary.j8fu.Either.Right;
@@ -49,6 +52,11 @@
4952
@NotThreadSafe
5053
class WindowsServiceManager implements ServiceManager {
5154

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+
5260
private static final Logger LOG = LogManager.getLogger(WindowsServiceManager.class);
5361
private static final String PROCRUN_SRV_EXE = "prunsrv-x86_64.exe";
5462
private static final String SC_EXE = "sc.exe";
@@ -86,8 +94,8 @@ public void install() throws ServiceManagerException {
8694
.orElse(existHome.resolve("etc").resolve("conf.xml"));
8795

8896
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"));
9199

92100
final StringBuilder jvmOptions = new StringBuilder();
93101
jvmOptions.append("-Dfile.encoding=UTF-8");
@@ -117,7 +125,6 @@ public void install() throws ServiceManagerException {
117125
"--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!
118126
"--Jvm=" + findJvm().orElse("auto"),
119127
"--Classpath=\"" + existHome.resolve("lib").toAbsolutePath().toString().replace('\\', '/') + "/*\"",
120-
"--JvmMs=" + minMemory,
121128
"--StartMode=jvm",
122129
"--StartClass=org.exist.service.ExistDbDaemon",
123130
"--StartMethod=start",
@@ -127,7 +134,8 @@ public void install() throws ServiceManagerException {
127134
"--JvmOptions=\"" + jvmOptions + "\"",
128135
"--StartParams=\"" + configFile.toAbsolutePath().toString() + "\""
129136
);
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));
131139

132140
try {
133141
final Tuple2<Integer, String> execResult = run(args, true);
@@ -367,4 +375,75 @@ private Tuple2<Integer, String> run(List<String> args, final boolean elevated) t
367375
final int exitValue = process.waitFor();
368376
return Tuple(exitValue, output.toString());
369377
}
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+
}
370449
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* eXist-db Open Source Native XML Database
3+
* Copyright (C) 2001 The eXist-db Authors
4+
*
5+
6+
* http://www.exist-db.org
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; either
11+
* version 2.1 of the License, or (at your option) any later version.
12+
*
13+
* This library is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
* Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public
19+
* License along with this library; if not, write to the Free Software
20+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
*/
22+
package org.exist.launcher;
23+
24+
import org.junit.jupiter.api.Test;
25+
26+
import java.util.Optional;
27+
28+
import static org.junit.jupiter.api.Assertions.assertEquals;
29+
30+
public class WindowsServiceManagerTest {
31+
32+
@Test
33+
public void asJavaCmdlineMemoryString() {
34+
assertEquals(Optional.of("1024k"), WindowsServiceManager.asJavaCmdlineMemoryString("1024k"));
35+
assertEquals(Optional.of("1024K"), WindowsServiceManager.asJavaCmdlineMemoryString("1024K"));
36+
assertEquals(Optional.of("1024m"), WindowsServiceManager.asJavaCmdlineMemoryString("1024m"));
37+
assertEquals(Optional.of("1024M"), WindowsServiceManager.asJavaCmdlineMemoryString("1024M"));
38+
assertEquals(Optional.of("1024g"), WindowsServiceManager.asJavaCmdlineMemoryString("1024g"));
39+
assertEquals(Optional.of("1024G"), WindowsServiceManager.asJavaCmdlineMemoryString("1024G"));
40+
41+
// default to MB
42+
assertEquals(Optional.of("128m"), WindowsServiceManager.asJavaCmdlineMemoryString("128"));
43+
44+
// ignore junk
45+
assertEquals(Optional.empty(), WindowsServiceManager.asJavaCmdlineMemoryString("One"));
46+
47+
// if the unit is unknown, fallback to MB
48+
assertEquals(Optional.of("1024m"), WindowsServiceManager.asJavaCmdlineMemoryString("1024t"));
49+
assertEquals(Optional.of("1024m"), WindowsServiceManager.asJavaCmdlineMemoryString("1024T"));
50+
assertEquals(Optional.of("1024m"), WindowsServiceManager.asJavaCmdlineMemoryString("1024z"));
51+
assertEquals(Optional.of("1024m"), WindowsServiceManager.asJavaCmdlineMemoryString("1024Z"));
52+
}
53+
54+
@Test
55+
public void asPrunSrvMemoryString() {
56+
assertEquals(Optional.of("1"), WindowsServiceManager.asPrunSrvMemoryString("1024k"));
57+
assertEquals(Optional.of("1"), WindowsServiceManager.asPrunSrvMemoryString("1024K"));
58+
assertEquals(Optional.of("1024"), WindowsServiceManager.asPrunSrvMemoryString("1024m"));
59+
assertEquals(Optional.of("1024"), WindowsServiceManager.asPrunSrvMemoryString("1024M"));
60+
assertEquals(Optional.of("1048576"), WindowsServiceManager.asPrunSrvMemoryString("1024g"));
61+
assertEquals(Optional.of("1048576"), WindowsServiceManager.asPrunSrvMemoryString("1024G"));
62+
63+
// default to MB
64+
assertEquals(Optional.of("128"), WindowsServiceManager.asPrunSrvMemoryString("128"));
65+
66+
// ignore junk
67+
assertEquals(Optional.empty(), WindowsServiceManager.asPrunSrvMemoryString("One"));
68+
69+
// if the unit is unknown, fallback to MB
70+
assertEquals(Optional.of("1024"), WindowsServiceManager.asPrunSrvMemoryString("1024t"));
71+
assertEquals(Optional.of("1024"), WindowsServiceManager.asPrunSrvMemoryString("1024T"));
72+
assertEquals(Optional.of("1024"), WindowsServiceManager.asPrunSrvMemoryString("1024z"));
73+
assertEquals(Optional.of("1024"), WindowsServiceManager.asPrunSrvMemoryString("1024Z"));
74+
}
75+
}

0 commit comments

Comments
 (0)