Skip to content

Commit 30094f5

Browse files
committed
post-review de-groovify + static factory
1 parent ffc05c1 commit 30094f5

File tree

6 files changed

+267
-284
lines changed

6 files changed

+267
-284
lines changed

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/TargetSystemIntegrationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import com.linecorp.armeria.server.grpc.GrpcService;
1212
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
1313
import io.grpc.stub.StreamObserver;
14-
import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer;
1514
import io.opentelemetry.contrib.jmxscraper.JmxConnectorBuilder;
15+
import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer;
1616
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
1717
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
1818
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;

jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/JmxScraper.java

+11-12
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import io.opentelemetry.contrib.jmxscraper.config.ConfigurationException;
99
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfig;
10-
import io.opentelemetry.contrib.jmxscraper.config.JmxScraperConfigFactory;
1110
import java.io.DataInputStream;
1211
import java.io.IOException;
1312
import java.io.InputStream;
@@ -37,9 +36,13 @@ public class JmxScraper {
3736
@SuppressWarnings({"SystemOut", "SystemExitOutsideMain"})
3837
public static void main(String[] args) {
3938
try {
40-
JmxScraperConfig config = JmxScraper.createConfigFromArgs(Arrays.asList(args));
39+
JmxScraperConfig config =
40+
JmxScraperConfig.fromProperties(parseArgs(Arrays.asList(args)), System.getProperties());
41+
// propagate effective user-provided configuration to JVM system properties
42+
config.propagateSystemProperties();
4143
// TODO: depend on instrumentation 2.9.0 snapshot
42-
// service = JmxMetricInsight.createService(GlobalOpenTelemetry.get(), config.getIntervalMilliseconds());
44+
// service = JmxMetricInsight.createService(GlobalOpenTelemetry.get(),
45+
// config.getIntervalMilliseconds());
4346
JmxScraper jmxScraper = new JmxScraper(JmxConnectorBuilder.createNew(config.getServiceUrl()));
4447
jmxScraper.start();
4548

@@ -56,15 +59,14 @@ public static void main(String[] args) {
5659
System.err.println("Unable to connect " + e.getMessage());
5760
System.exit(2);
5861
}
59-
6062
}
6163

6264
/**
63-
* Create {@link JmxScraperConfig} object basing on command line options
65+
* Create {@link Properties} from command line options
6466
*
6567
* @param args application commandline arguments
6668
*/
67-
static JmxScraperConfig createConfigFromArgs(List<String> args)
69+
static Properties parseArgs(List<String> args)
6870
throws ArgumentsParsingException, ConfigurationException {
6971

7072
if (args.isEmpty()) {
@@ -77,14 +79,12 @@ static JmxScraperConfig createConfigFromArgs(List<String> args)
7779
throw new ArgumentsParsingException("unexpected first argument must be '" + CONFIG_ARG + "'");
7880
}
7981

80-
Properties properties;
8182
String path = args.get(1);
8283
if (path.trim().equals("-")) {
83-
properties = loadPropertiesFromStdin();
84+
return loadPropertiesFromStdin();
8485
} else {
85-
properties = loadPropertiesFromPath(path);
86+
return loadPropertiesFromPath(path);
8687
}
87-
return new JmxScraperConfigFactory().createConfig(properties);
8888
}
8989

9090
private static Properties loadPropertiesFromStdin() throws ConfigurationException {
@@ -97,8 +97,7 @@ private static Properties loadPropertiesFromStdin() throws ConfigurationExceptio
9797
}
9898
}
9999

100-
private static Properties loadPropertiesFromPath(String path)
101-
throws ConfigurationException {
100+
private static Properties loadPropertiesFromPath(String path) throws ConfigurationException {
102101
Properties properties = new Properties();
103102
try (InputStream is = Files.newInputStream(Paths.get(path))) {
104103
properties.load(is);

jmx-scraper/src/main/java/io/opentelemetry/contrib/jmxscraper/config/JmxScraperConfig.java

+186-14
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,68 @@
55

66
package io.opentelemetry.contrib.jmxscraper.config;
77

8+
import static io.opentelemetry.contrib.jmxscraper.util.StringUtils.isBlank;
9+
10+
import java.util.Arrays;
811
import java.util.Collections;
12+
import java.util.List;
13+
import java.util.Locale;
14+
import java.util.Map;
15+
import java.util.Properties;
916
import java.util.Set;
17+
import java.util.stream.Collectors;
1018

1119
/** This class keeps application settings */
1220
public class JmxScraperConfig {
13-
String serviceUrl = "";
14-
String customJmxScrapingConfigPath = "";
15-
Set<String> targetSystems = Collections.emptySet();
16-
int intervalMilliseconds;
17-
String metricsExporterType = "";
18-
19-
String otlpExporterEndpoint = "";
2021

21-
String username = "";
22-
String password = "";
23-
String realm = "";
24-
String remoteProfile = "";
25-
boolean registrySsl;
26-
27-
JmxScraperConfig() {}
22+
static final String SERVICE_URL = "otel.jmx.service.url";
23+
static final String CUSTOM_JMX_SCRAPING_CONFIG = "otel.jmx.custom.scraping.config";
24+
static final String TARGET_SYSTEM = "otel.jmx.target.system";
25+
static final String INTERVAL_MILLISECONDS = "otel.jmx.interval.milliseconds";
26+
static final String METRICS_EXPORTER_TYPE = "otel.metrics.exporter";
27+
static final String EXPORTER_INTERVAL = "otel.metric.export.interval";
28+
static final String REGISTRY_SSL = "otel.jmx.remote.registry.ssl";
29+
30+
static final String OTLP_ENDPOINT = "otel.exporter.otlp.endpoint";
31+
32+
static final String JMX_USERNAME = "otel.jmx.username";
33+
static final String JMX_PASSWORD = "otel.jmx.password";
34+
static final String JMX_REMOTE_PROFILE = "otel.jmx.remote.profile";
35+
static final String JMX_REALM = "otel.jmx.realm";
36+
37+
static final List<String> AVAILABLE_TARGET_SYSTEMS =
38+
Arrays.asList(
39+
"activemq",
40+
"cassandra",
41+
"hbase",
42+
"hadoop",
43+
"jetty",
44+
"jvm",
45+
"kafka",
46+
"kafka-consumer",
47+
"kafka-producer",
48+
"solr",
49+
"tomcat",
50+
"wildfly");
51+
52+
private String serviceUrl = "";
53+
private String customJmxScrapingConfigPath = "";
54+
private Set<String> targetSystems = Collections.emptySet();
55+
private int intervalMilliseconds;
56+
private String metricsExporterType = "";
57+
private String otlpExporterEndpoint = "";
58+
private String username = "";
59+
private String password = "";
60+
private String realm = "";
61+
private String remoteProfile = "";
62+
private boolean registrySsl;
63+
64+
/** Combined properties kept for initializing system properties */
65+
private final Properties properties;
66+
67+
private JmxScraperConfig(Properties properties) {
68+
this.properties = properties;
69+
}
2870

2971
public String getServiceUrl() {
3072
return serviceUrl;
@@ -69,4 +111,134 @@ public String getRemoteProfile() {
69111
public boolean isRegistrySsl() {
70112
return registrySsl;
71113
}
114+
115+
/**
116+
* Builds scraper configuration from user and system properties
117+
*
118+
* @param userProperties user-provided configuration
119+
* @param systemProperties system properties through '-Dxxx' JVM arguments
120+
* @return JMX scraper configuration
121+
* @throws ConfigurationException if there is any configuration error
122+
*/
123+
public static JmxScraperConfig fromProperties(
124+
Properties userProperties, Properties systemProperties) throws ConfigurationException {
125+
126+
Properties properties = new Properties();
127+
properties.putAll(userProperties);
128+
129+
// command line takes precedence so replace any that were specified via config file properties
130+
properties.putAll(systemProperties);
131+
132+
JmxScraperConfig config = new JmxScraperConfig(properties);
133+
134+
config.serviceUrl = properties.getProperty(SERVICE_URL);
135+
config.customJmxScrapingConfigPath = properties.getProperty(CUSTOM_JMX_SCRAPING_CONFIG);
136+
String targetSystem =
137+
properties.getProperty(TARGET_SYSTEM, "").toLowerCase(Locale.ENGLISH).trim();
138+
139+
List<String> targets =
140+
Arrays.asList(isBlank(targetSystem) ? new String[0] : targetSystem.split(","));
141+
config.targetSystems = targets.stream().map(String::trim).collect(Collectors.toSet());
142+
143+
int interval = getProperty(properties, INTERVAL_MILLISECONDS, 0);
144+
config.intervalMilliseconds = (interval == 0 ? 10000 : interval);
145+
// configure SDK metric exporter interval from jmx metric interval
146+
getAndSetPropertyIfUndefined(properties, EXPORTER_INTERVAL, config.intervalMilliseconds);
147+
148+
config.metricsExporterType =
149+
getAndSetPropertyIfUndefined(properties, METRICS_EXPORTER_TYPE, "logging");
150+
config.otlpExporterEndpoint = properties.getProperty(OTLP_ENDPOINT);
151+
152+
config.username = properties.getProperty(JMX_USERNAME);
153+
config.password = properties.getProperty(JMX_PASSWORD);
154+
155+
config.remoteProfile = properties.getProperty(JMX_REMOTE_PROFILE);
156+
config.realm = properties.getProperty(JMX_REALM);
157+
158+
config.registrySsl = Boolean.parseBoolean(properties.getProperty(REGISTRY_SSL));
159+
160+
validateConfig(config);
161+
return config;
162+
}
163+
164+
/**
165+
* Sets system properties from effective configuration, must be called once and early before any
166+
* OTel SDK or SSL/TLS stack initialization. This allows to override JVM system properties using
167+
* user-providded configuration and also to set standard OTel SDK configuration.
168+
*/
169+
public void propagateSystemProperties() {
170+
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
171+
172+
String key = (String) entry.getKey();
173+
String value = (String) entry.getValue();
174+
if (key.startsWith("otel.")
175+
|| key.startsWith("javax.net.ssl.keyStore")
176+
|| key.startsWith("javax.net.ssl.trustStore")) {
177+
System.setProperty(key, value);
178+
}
179+
}
180+
}
181+
182+
private static int getProperty(Properties properties, String key, int defaultValue)
183+
throws ConfigurationException {
184+
String propVal = properties.getProperty(key);
185+
if (propVal == null) {
186+
return defaultValue;
187+
}
188+
try {
189+
return Integer.parseInt(propVal);
190+
} catch (NumberFormatException e) {
191+
throw new ConfigurationException("Failed to parse " + key, e);
192+
}
193+
}
194+
195+
/**
196+
* Similar to getProperty(key, defaultValue) but sets the property to default if not in object.
197+
*/
198+
private static String getAndSetPropertyIfUndefined(
199+
Properties properties, String key, String defaultValue) {
200+
String propVal = properties.getProperty(key, defaultValue);
201+
if (propVal.equals(defaultValue)) {
202+
properties.setProperty(key, defaultValue);
203+
}
204+
return propVal;
205+
}
206+
207+
private static int getAndSetPropertyIfUndefined(
208+
Properties properties, String key, int defaultValue) throws ConfigurationException {
209+
int propVal = getProperty(properties, key, defaultValue);
210+
if (propVal == defaultValue) {
211+
properties.setProperty(key, String.valueOf(defaultValue));
212+
}
213+
return propVal;
214+
}
215+
216+
/** Will determine if parsed config is complete, setting any applicable values and defaults. */
217+
private static void validateConfig(JmxScraperConfig config) throws ConfigurationException {
218+
if (isBlank(config.serviceUrl)) {
219+
throw new ConfigurationException(SERVICE_URL + " must be specified.");
220+
}
221+
222+
if (isBlank(config.customJmxScrapingConfigPath) && config.targetSystems.isEmpty()) {
223+
throw new ConfigurationException(
224+
CUSTOM_JMX_SCRAPING_CONFIG + " or " + TARGET_SYSTEM + " must be specified.");
225+
}
226+
227+
if (!config.targetSystems.isEmpty()
228+
&& !AVAILABLE_TARGET_SYSTEMS.containsAll(config.targetSystems)) {
229+
throw new ConfigurationException(
230+
String.format(
231+
"%s must specify targets from %s", config.targetSystems, AVAILABLE_TARGET_SYSTEMS));
232+
}
233+
234+
if (isBlank(config.otlpExporterEndpoint)
235+
&& (!isBlank(config.metricsExporterType)
236+
&& config.metricsExporterType.equalsIgnoreCase("otlp"))) {
237+
throw new ConfigurationException(OTLP_ENDPOINT + " must be specified for otlp format.");
238+
}
239+
240+
if (config.intervalMilliseconds < 0) {
241+
throw new ConfigurationException(INTERVAL_MILLISECONDS + " must be positive.");
242+
}
243+
}
72244
}

0 commit comments

Comments
 (0)