Skip to content

Commit 1d156ba

Browse files
committed
feat(environment): Add support for JAVA_TOOL_OPTIONS and arg files
1 parent 56eed08 commit 1d156ba

File tree

2 files changed

+106
-12
lines changed

2 files changed

+106
-12
lines changed

components/environment/src/main/java/datadog/environment/JvmOptions.java

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.ArrayList;
1616
import java.util.Arrays;
1717
import java.util.List;
18+
import java.util.ListIterator;
1819
import java.util.StringTokenizer;
1920

2021
/** Fetches and captures the JVM options. */
@@ -55,18 +56,27 @@ private List<String> findVmOptions() {
5556
break;
5657
}
5758
}
58-
String[] vmOptions = new String[index - 1];
59-
System.arraycopy(PROCFS_CMDLINE, 1, vmOptions, 0, vmOptions.length);
60-
61-
// TODO ARGFILES Need to be substituted
62-
63-
// TODO JAVA_TOOLS_OPTIONS Need to be inserted before.
64-
// String javaToolOptions = EnvironmentVariables.getOrDefault("JAVA_TOOL_OPTIONS", null);
65-
// if (javaToolOptions != null) {
66-
// parts.addAll(0, Arrays.asList(javaToolOptions.split(" ")));
67-
// }
68-
69-
return Arrays.asList(vmOptions);
59+
// Create list of VM options
60+
String[] vmOptionsArray = new String[index - 1];
61+
System.arraycopy(PROCFS_CMDLINE, 1, vmOptionsArray, 0, vmOptionsArray.length);
62+
List<String> vmOptions = Arrays.asList(vmOptionsArray);
63+
// Substitute @argfile by their content
64+
ListIterator<String> iterator = vmOptions.listIterator();
65+
while (iterator.hasNext()) {
66+
String vmOption = iterator.next();
67+
if (vmOption.startsWith("@")) {
68+
iterator.remove();
69+
for (String argument : getArgumentsFromFile(vmOption)) {
70+
iterator.add(argument);
71+
}
72+
}
73+
}
74+
// Insert JAVA_TOOL_OPTIONS at the start if present
75+
List<String> toolOptions = getToolOptions();
76+
if (!toolOptions.isEmpty()) {
77+
vmOptions.addAll(0, toolOptions);
78+
}
79+
return vmOptions;
7080
}
7181

7282
// Try Oracle-based
@@ -144,6 +154,52 @@ private static List<String> getArgumentsFromFile(String argFile) {
144154
}
145155
}
146156

157+
private static List<String> getToolOptions() {
158+
String javaToolOptions = EnvironmentVariables.getOrDefault("JAVA_TOOL_OPTIONS", "");
159+
return javaToolOptions.isEmpty() ? emptyList() : parseToolOptions(javaToolOptions);
160+
}
161+
162+
/**
163+
* Parse the JAVA_TOOL_OPTIONS environment variable according the JVMTI specifications
164+
*
165+
* @param javaToolOptions The JAVA_TOOL_OPTIONS environment variable.
166+
* @return The parsed JVM options.
167+
* @see <a
168+
* href="https://docs.oracle.com/en/java/javase/21/docs/specs/jvmti.html#tooloptions">JVMTI
169+
* specifications</a>
170+
*/
171+
static List<String> parseToolOptions(String javaToolOptions) {
172+
List<String> options = new ArrayList<>();
173+
StringBuilder option = new StringBuilder();
174+
boolean inQuotes = false;
175+
char quoteChar = 0;
176+
177+
for (int i = 0; i < javaToolOptions.length(); i++) {
178+
char c = javaToolOptions.charAt(i);
179+
if (inQuotes) {
180+
if (quoteChar == c) {
181+
inQuotes = false;
182+
} else {
183+
option.append(c);
184+
}
185+
} else if (c == '"' || c == '\'') {
186+
inQuotes = true;
187+
quoteChar = c;
188+
} else if (Character.isWhitespace(c)) {
189+
if (option.length() > 0) {
190+
options.add(option.toString());
191+
option.setLength(0);
192+
}
193+
} else {
194+
option.append(c);
195+
}
196+
}
197+
if (option.length() > 0) {
198+
options.add(option.toString());
199+
}
200+
return options;
201+
}
202+
147203
private static List<String> split(String str, String delimiter) {
148204
List<String> parts = new ArrayList<>();
149205
StringTokenizer tokenizer = new StringTokenizer(str, delimiter);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package datadog.environment;
2+
3+
import static java.util.Arrays.asList;
4+
import static java.util.Collections.emptyList;
5+
import static java.util.Collections.singletonList;
6+
import static org.junit.jupiter.api.Assertions.assertEquals;
7+
import static org.junit.jupiter.params.provider.Arguments.arguments;
8+
9+
import java.util.List;
10+
import java.util.stream.Stream;
11+
import org.junit.jupiter.params.ParameterizedTest;
12+
import org.junit.jupiter.params.provider.Arguments;
13+
import org.junit.jupiter.params.provider.MethodSource;
14+
15+
class JvmOptionsTest {
16+
static Stream<Arguments> data() {
17+
return Stream.of(
18+
arguments("", emptyList()),
19+
arguments("-Xmx512m", singletonList("-Xmx512m")),
20+
arguments("-Xms256m -Xmx512m", asList("-Xms256m", "-Xmx512m")),
21+
arguments(" -Xms256m -Xmx512m ", asList("-Xms256m", "-Xmx512m")),
22+
arguments("-Xms256m\t-Xmx512m", asList("-Xms256m", "-Xmx512m")),
23+
arguments("\t -Xms256m \t -Xmx512m \t", asList("-Xms256m", "-Xmx512m")),
24+
arguments(
25+
"-Xmx512m -Dprop=\"value with space\"", asList("-Xmx512m", "-Dprop=value with space")),
26+
arguments(
27+
"-Xmx512m -Dprop='value with space'", asList("-Xmx512m", "-Dprop=value with space")),
28+
arguments("-Xmx512m -Dprop='mixing\"quotes'", asList("-Xmx512m", "-Dprop=mixing\"quotes")),
29+
arguments("-Xmx512m -Dprop=\"mixing'quotes\"", asList("-Xmx512m", "-Dprop=mixing'quotes")));
30+
}
31+
32+
@ParameterizedTest
33+
@MethodSource("data")
34+
void testParseToolOptions(String javaToolOptions, List<String> expectedVmOptions) {
35+
List<String> vmOptions = JvmOptions.parseToolOptions(javaToolOptions);
36+
assertEquals(expectedVmOptions, vmOptions);
37+
}
38+
}

0 commit comments

Comments
 (0)