13
13
import java .nio .file .Paths ;
14
14
import java .util .ArrayList ;
15
15
import java .util .Arrays ;
16
+ import java .util .Collection ;
16
17
import java .util .Collections ;
18
+ import java .util .LinkedHashSet ;
17
19
import java .util .List ;
18
20
import java .util .Locale ;
19
21
import java .util .Map ;
20
22
import java .util .Optional ;
23
+ import java .util .Set ;
21
24
import java .util .SortedMap ;
22
25
import java .util .TreeMap ;
23
26
import java .util .regex .Matcher ;
24
27
import java .util .regex .Pattern ;
28
+ import java .util .stream .Collectors ;
25
29
26
30
27
31
/**
28
32
* Parse jvm.options file applying version conditional logic. Heavily inspired by same functionality in Elasticsearch.
29
33
* */
30
34
public class JvmOptionsParser {
31
35
36
+ private static final String [] MANDATORY_JVM_OPTIONS = new String []{
37
+ "-Djruby.regexp.interruptible=true" ,
38
+ "16-:--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED" ,
39
+ "16-:--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED" ,
40
+ "16-:--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED" ,
41
+ "16-:--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED" ,
42
+ "16-:--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" ,
43
+ "11-:--add-opens=java.base/java.security=ALL-UNNAMED" ,
44
+ "11-:--add-opens=java.base/java.io=ALL-UNNAMED" ,
45
+ "11-:--add-opens=java.base/java.nio.channels=ALL-UNNAMED" ,
46
+ "11-:--add-opens=java.base/sun.nio.ch=ALL-UNNAMED" ,
47
+ "11-:--add-opens=java.management/sun.management=ALL-UNNAMED"
48
+ };
49
+
32
50
static class JvmOptionsFileParserException extends Exception {
33
51
34
52
private static final long serialVersionUID = 2446165130736962758L ;
@@ -71,8 +89,7 @@ public static void main(final String[] args) throws InterruptedException, IOExce
71
89
);
72
90
}
73
91
bailOnOldJava ();
74
- final String lsJavaOpts = System .getenv ("LS_JAVA_OPTS" );
75
- handleJvmOptions (args , lsJavaOpts );
92
+ handleJvmOptions (args , System .getenv ("LS_JAVA_OPTS" ));
76
93
}
77
94
78
95
static void bailOnOldJava (){
@@ -93,7 +110,7 @@ static void handleJvmOptions(String[] args, String lsJavaOpts) {
93
110
final String jvmOpts = args .length == 2 ? args [1 ] : null ;
94
111
try {
95
112
Optional <Path > jvmOptions = parser .lookupJvmOptionsFile (jvmOpts );
96
- parser .parseAndInjectEnvironment (jvmOptions , lsJavaOpts );
113
+ parser .handleJvmOptions (jvmOptions , lsJavaOpts );
97
114
} catch (JvmOptionsFileParserException pex ) {
98
115
System .err .printf (Locale .ROOT ,
99
116
"encountered [%d] error%s parsing [%s]" ,
@@ -128,20 +145,38 @@ private Optional<Path> lookupJvmOptionsFile(String jvmOpts) {
128
145
.findFirst ();
129
146
}
130
147
131
- private void parseAndInjectEnvironment (Optional <Path > jvmOptionsFile , String lsJavaOpts ) throws IOException , JvmOptionsFileParserException {
132
- final List <String > jvmOptionsContent = new ArrayList <>(parseJvmOptions (jvmOptionsFile ));
148
+ private void handleJvmOptions (Optional <Path > jvmOptionsFile , String lsJavaOpts ) throws IOException , JvmOptionsFileParserException {
149
+ int javaMajorVersion = javaMajorVersion ();
150
+
151
+ // Add JVM Options from config/jvm.options
152
+ final Set <String > jvmOptionsContent = new LinkedHashSet <>(getJvmOptionsFromFile (jvmOptionsFile , javaMajorVersion ));
133
153
154
+ // Add JVM Options from LS_JAVA_OPTS
134
155
if (lsJavaOpts != null && !lsJavaOpts .isEmpty ()) {
135
156
if (isDebugEnabled ()) {
136
157
System .err .println ("Appending jvm options from environment LS_JAVA_OPTS" );
137
158
}
138
159
jvmOptionsContent .add (lsJavaOpts );
139
160
}
161
+ // Set mandatory JVM options
162
+ jvmOptionsContent .addAll (getMandatoryJvmOptions (javaMajorVersion ));
140
163
141
164
System .out .println (String .join (" " , jvmOptionsContent ));
142
165
}
143
166
144
- private List <String > parseJvmOptions (Optional <Path > jvmOptionsFile ) throws IOException , JvmOptionsFileParserException {
167
+ /**
168
+ * Returns the list of mandatory JVM options for the given version of Java.
169
+ * @param javaMajorVersion
170
+ * @return Collection of mandatory options
171
+ */
172
+ static Collection <String > getMandatoryJvmOptions (int javaMajorVersion ){
173
+ return Arrays .stream (MANDATORY_JVM_OPTIONS )
174
+ .map (option -> jvmOptionFromLine (javaMajorVersion , option ))
175
+ .flatMap (Optional ::stream )
176
+ .collect (Collectors .toUnmodifiableList ());
177
+ }
178
+
179
+ private List <String > getJvmOptionsFromFile (final Optional <Path > jvmOptionsFile , final int javaMajorVersion ) throws IOException , JvmOptionsFileParserException {
145
180
if (!jvmOptionsFile .isPresent ()) {
146
181
System .err .println ("Warning: no jvm.options file found." );
147
182
return Collections .emptyList ();
@@ -155,13 +190,11 @@ private List<String> parseJvmOptions(Optional<Path> jvmOptionsFile) throws IOExc
155
190
if (isDebugEnabled ()) {
156
191
System .err .format ("Processing jvm.options file at `%s`\n " , optionsFilePath );
157
192
}
158
- final int majorJavaVersion = javaMajorVersion ();
159
-
160
193
try (InputStream is = Files .newInputStream (optionsFilePath );
161
194
Reader reader = new InputStreamReader (is , StandardCharsets .UTF_8 );
162
195
BufferedReader br = new BufferedReader (reader )
163
196
) {
164
- final ParseResult parseResults = parse (majorJavaVersion , br );
197
+ final ParseResult parseResults = parse (javaMajorVersion , br );
165
198
if (parseResults .hasErrors ()) {
166
199
throw new JvmOptionsFileParserException (optionsFilePath , parseResults .getInvalidLines ());
167
200
}
@@ -209,7 +242,36 @@ public List<String> getJvmOptions() {
209
242
private static final Pattern OPTION_DEFINITION = Pattern .compile ("((?<start>\\ d+)(?<range>-)?(?<end>\\ d+)?:)?(?<option>-.*)$" );
210
243
211
244
/**
212
- * Parse the line-delimited JVM options from the specified buffered reader for the specified Java major version.
245
+ *
246
+ * If the version syntax specified on a line matches the specified JVM options, the JVM option callback will be invoked with the JVM
247
+ * option. If the line does not match the specified syntax for the JVM options, the invalid line callback will be invoked with the
248
+ * contents of the entire line.
249
+ *
250
+ * @param javaMajorVersion the Java major version to match JVM options against
251
+ * @param br the buffered reader to read line-delimited JVM options from
252
+ * @return the admitted options lines respecting the javaMajorVersion and the error lines
253
+ * @throws IOException if an I/O exception occurs reading from the buffered reader
254
+ */
255
+ static ParseResult parse (final int javaMajorVersion , final BufferedReader br ) throws IOException {
256
+ final ParseResult result = new ParseResult ();
257
+ int lineNumber = 0 ;
258
+ while (true ) {
259
+ final String line = br .readLine ();
260
+ lineNumber ++;
261
+ if (line == null ) {
262
+ break ;
263
+ }
264
+ try {
265
+ jvmOptionFromLine (javaMajorVersion , line ).ifPresent (result ::appendOption );
266
+ } catch (IllegalArgumentException e ){
267
+ result .appendError (lineNumber , line );
268
+ };
269
+ }
270
+ return result ;
271
+ }
272
+
273
+ /**
274
+ * Parse the line-delimited JVM options from the specified string for the specified Java major version.
213
275
* Valid JVM options are:
214
276
* <ul>
215
277
* <li>
@@ -256,82 +318,52 @@ public List<String> getJvmOptions() {
256
318
* {@code 9-10:-Xlog:age*=trace,gc*,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m}
257
319
* </li>
258
320
* </ul>
259
- *
260
- * If the version syntax specified on a line matches the specified JVM options, the JVM option callback will be invoked with the JVM
261
- * option. If the line does not match the specified syntax for the JVM options, the invalid line callback will be invoked with the
262
- * contents of the entire line.
263
- *
264
- * @param javaMajorVersion the Java major version to match JVM options against
265
- * @param br the buffered reader to read line-delimited JVM options from
266
- * @return the admitted options lines respecting the javaMajorVersion and the error lines
267
- * @throws IOException if an I/O exception occurs reading from the buffered reader
321
+
322
+ * @param javaMajorVersion
323
+ * @param line
324
+ * @return Returns an Optional containing a string if the line contains a valid option for the specified Java
325
+ * version and empty otherwise
268
326
*/
269
- static ParseResult parse (final int javaMajorVersion , final BufferedReader br ) throws IOException {
270
- final ParseResult result = new ParseResult ();
271
- int lineNumber = 0 ;
272
- while (true ) {
273
- final String line = br .readLine ();
274
- lineNumber ++;
275
- if (line == null ) {
276
- break ;
277
- }
278
- if (line .startsWith ("#" )) {
279
- // lines beginning with "#" are treated as comments
280
- continue ;
281
- }
282
- if (line .matches ("\\ s*" )) {
283
- // skip blank lines
284
- continue ;
285
- }
286
- final Matcher matcher = OPTION_DEFINITION .matcher (line );
287
- if (matcher .matches ()) {
288
- final String start = matcher .group ("start" );
289
- final String end = matcher .group ("end" );
290
- if (start == null ) {
291
- // no range present, unconditionally apply the JVM option
292
- result .appendOption (line );
327
+ private static Optional <String > jvmOptionFromLine (final int javaMajorVersion , final String line ){
328
+ if (line .startsWith ("#" ) || line .matches ("\\ s*" )) {
329
+ // Skip comments and blank lines
330
+ return Optional .empty ();
331
+ }
332
+ final Matcher matcher = OPTION_DEFINITION .matcher (line );
333
+ if (matcher .matches ()) {
334
+ final String start = matcher .group ("start" );
335
+ final String end = matcher .group ("end" );
336
+ if (start == null ) {
337
+ // no range present, unconditionally apply the JVM option
338
+ return Optional .of (line );
339
+ } else {
340
+ final int lower = Integer .parseInt (start );
341
+ final int upper ;
342
+ if (matcher .group ("range" ) == null ) {
343
+ // no range is present, apply the JVM option to the specified major version only
344
+ upper = lower ;
345
+ } else if (end == null ) {
346
+ // a range of the form \\d+- is present, apply the JVM option to all major versions larger than the specified one
347
+ upper = Integer .MAX_VALUE ;
293
348
} else {
294
- final int lower ;
295
- try {
296
- lower = Integer .parseInt (start );
297
- } catch (final NumberFormatException e ) {
298
- result .appendError (lineNumber , line );
299
- continue ;
300
- }
301
- final int upper ;
302
- if (matcher .group ("range" ) == null ) {
303
- // no range is present, apply the JVM option to the specified major version only
304
- upper = lower ;
305
- } else if (end == null ) {
306
- // a range of the form \\d+- is present, apply the JVM option to all major versions larger than the specified one
307
- upper = Integer .MAX_VALUE ;
308
- } else {
309
- // a range of the form \\d+-\\d+ is present, apply the JVM option to the specified range of major versions
310
- try {
311
- upper = Integer .parseInt (end );
312
- } catch (final NumberFormatException e ) {
313
- result .appendError (lineNumber , line );
314
- continue ;
315
- }
316
- if (upper < lower ) {
317
- result .appendError (lineNumber , line );
318
- continue ;
319
- }
320
- }
321
- if (lower <= javaMajorVersion && javaMajorVersion <= upper ) {
322
- result .appendOption (matcher .group ("option" ));
349
+ upper = Integer .parseInt (end );
350
+ if (upper < lower ) {
351
+ throw new IllegalArgumentException ("Upper bound must be greater than lower bound" );
323
352
}
324
353
}
325
- } else {
326
- result .appendError (lineNumber , line );
354
+ if (lower <= javaMajorVersion && javaMajorVersion <= upper ) {
355
+ return Optional .of (matcher .group ("option" ));
356
+ }
327
357
}
358
+ } else {
359
+ throw new IllegalArgumentException ("Illegal JVM Option" );
328
360
}
329
- return result ;
361
+ return Optional . empty () ;
330
362
}
331
363
332
364
private static final Pattern JAVA_VERSION = Pattern .compile ("^(?:1\\ .)?(?<javaMajorVersion>\\ d+)(?:\\ .\\ d+)?$" );
333
365
334
- private int javaMajorVersion () {
366
+ private static int javaMajorVersion () {
335
367
final String specVersion = System .getProperty ("java.specification.version" );
336
368
final Matcher specVersionMatcher = JAVA_VERSION .matcher (specVersion );
337
369
if (!specVersionMatcher .matches ()) {
0 commit comments