Skip to content

Commit 6710182

Browse files
committed
If REST port not accessible, try failing over to local port.
1 parent 04634d0 commit 6710182

20 files changed

+608
-367
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Note that there are two parts to the configuration. The optional top portion def
5757
| `query_sync.interval` | The interval, in seconds, at which the service will be queried. Defaults to 10. |
5858
| `metricsNameSnakeCase` | If true, metrics names will be converted to snake case. Defaults to false. |
5959
| `domainQualifier` | If true, the domain name will be included as a qualifier for all metrics. Defaults to false. |
60-
| `restPort` | Optional. Overrides the port on which the exporter should contact the REST API. Needed when behind a load balancer. |
60+
| `restPort` | Optional. Overrides the port on which the exporter should contact the REST API. Needed if the exporter cannot find the REST API. |
6161

6262
The `query` field is more complex. Each query consists of a hierarchy of the [MBeans](https://docs.oracle.com/middleware/1221/wls/WLMBR/core/index.html), starting relative to `ServerRuntimes`.
6363
Within each section, there are a number of options:
@@ -134,4 +134,4 @@ include the `restPort` configuration to tell the exporter which port to use.
134134

135135
## Copyright
136136

137-
Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
137+
Copyright © 2017, 2020, Oracle Corporation and/or its affiliates.

src/main/java/io/prometheus/wls/rest/ConfigurationServlet.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,15 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
4848
}
4949

5050
private void updateConfiguration(WebClient webClient, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
51-
authenticate(webClient.withUrl(LiveConfiguration.getAuthenticationUrl(getProtocol())));
5251
try {
52+
authenticate(webClient.withUrl(getAuthenticationUrl()));
5353
if (!ServletFileUpload.isMultipartContent(req)) throw new ServletException("Must be a multi-part request");
5454

5555
createPostAction(req).perform();
5656
reportUpdatedConfiguration(resp);
57+
} catch (RestPortConnectionException e) {
58+
reportFailure(e);
59+
webClient.setRetryNeeded(true);
5760
} catch (ConfigurationException e) {
5861
reportUnableToUpdateConfiguration(req, resp.getOutputStream(), e);
5962
}

src/main/java/io/prometheus/wls/rest/ExporterServlet.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77

88
import java.io.IOException;
9+
import java.io.PrintWriter;
10+
import java.io.StringWriter;
911
import java.util.Map;
1012
import java.util.TreeMap;
1113
import javax.servlet.ServletConfig;
@@ -48,19 +50,23 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
4850
@SuppressWarnings("unused") // The req parameter is not used, but is required by 'doWithAuthentication'
4951
private void displayMetrics(WebClient webClient, HttpServletRequest req, HttpServletResponse resp) throws IOException {
5052
LiveConfiguration.updateConfiguration();
51-
try (MetricsStream metricsStream = new MetricsStream(resp.getOutputStream())) {
53+
try (MetricsStream metricsStream = new MetricsStream(req, resp.getOutputStream())) {
5254
if (!LiveConfiguration.hasQueries())
5355
metricsStream.println("# No configuration defined.");
5456
else
5557
printMetrics(webClient, metricsStream);
56-
5758
}
5859
}
5960

6061
private void printMetrics(WebClient webClient, MetricsStream metricsStream) throws IOException {
61-
for (MBeanSelector selector : LiveConfiguration.getQueries())
62-
displayMetrics(webClient, metricsStream, selector);
63-
metricsStream.printPerformanceMetrics();
62+
try {
63+
for (MBeanSelector selector : LiveConfiguration.getQueries())
64+
displayMetrics(webClient, metricsStream, selector);
65+
metricsStream.printPerformanceMetrics();
66+
} catch (RestPortConnectionException e) {
67+
reportFailure(e);
68+
webClient.setRetryNeeded(true);
69+
}
6470
}
6571

6672
private void displayMetrics(WebClient webClient, MetricsStream metricsStream, MBeanSelector selector) throws IOException {
@@ -73,9 +79,20 @@ private void displayMetrics(WebClient webClient, MetricsStream metricsStream, MB
7379
withCommentMarkers("REST service was unable to handle this query\n"
7480
+ selector.getPrintableRequest() + '\n'
7581
+ "exception: " + e.getMessage()));
82+
} catch (IOException | RuntimeException | Error e) {
83+
MessagesServlet.addExchange(getQueryUrl(selector), selector.getRequest(), toStackTrace(e));
84+
throw e;
7685
}
7786
}
7887

88+
private String toStackTrace(Throwable e) {
89+
StringWriter sw = new StringWriter();
90+
PrintWriter pw = new PrintWriter(sw);
91+
e.printStackTrace(pw);
92+
pw.close();
93+
return sw.toString();
94+
}
95+
7996
private String withCommentMarkers(String string) {
8097
StringBuilder sb = new StringBuilder();
8198
for (String s : string.split("\\r?\\n"))
@@ -95,7 +112,7 @@ private Map<String, Object> getMetrics(WebClient webClient, MBeanSelector select
95112
}
96113

97114
private String requestMetrics(WebClient webClient, MBeanSelector selector) throws IOException {
98-
String url = LiveConfiguration.getUrl(getProtocol(), selector);
115+
String url = getQueryUrl(selector);
99116
String jsonResponse = webClient.withUrl(url).doPostRequest(selector.getRequest());
100117
MessagesServlet.addExchange(url, selector.getRequest(), jsonResponse);
101118
return jsonResponse;

src/main/java/io/prometheus/wls/rest/LiveConfiguration.java

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@
1313
import java.util.Optional;
1414
import javax.servlet.ServletConfig;
1515
import javax.servlet.ServletException;
16+
import javax.servlet.http.HttpServletRequest;
1617

1718
import com.google.gson.JsonObject;
1819
import com.google.gson.JsonParser;
1920
import io.prometheus.wls.rest.domain.ExporterConfig;
2021
import io.prometheus.wls.rest.domain.MBeanSelector;
21-
import io.prometheus.wls.rest.domain.Protocol;
2222
import io.prometheus.wls.rest.domain.QuerySyncConfiguration;
23-
import io.prometheus.wls.rest.domain.QueryType;
2423
import org.yaml.snakeyaml.Yaml;
2524

2625
/**
@@ -79,35 +78,25 @@ static void setServer(String serverName, int serverPort) {
7978
}
8079

8180
/**
82-
* Returns the URL used to query the management services
83-
* @return a url built for the configured server
84-
* @param protocol the protocol used to authenticate a configuration change
81+
* Specifies the server on which to contact the Management RESTful services.
82+
*
83+
* @param req the incoming request
8584
*/
86-
static String getAuthenticationUrl(Protocol protocol) {
87-
return protocol.format(QueryType.RUNTIME_URL_PATTERN, WLS_HOST, getRestPort());
85+
static void setServer(HttpServletRequest req) {
86+
LiveConfiguration.setServer(req.getServerName(), req.getServerPort());
8887
}
8988

9089
/**
91-
* Returns the URL used to query the management services
92-
*
93-
* @param protocol the protocol to use
94-
* @param selector the selector which will define the query
95-
* @return a url built for the configured server
90+
* Creates a builder for URLs that can handle retries to alternative ports.
91+
* @param request the active servlet request
92+
* @return the new builder
9693
*/
97-
static String getUrl(Protocol protocol, MBeanSelector selector) {
98-
return selector.getUrl(protocol, WLS_HOST, getRestPort());
94+
static UrlBuilder createUrlBuilder(HttpServletRequest request) {
95+
return new UrlBuilder(request, getConfiguredRestPort());
9996
}
10097

101-
private static int getRestPort() {
102-
return Optional.ofNullable(config.getRestPort()).orElse(serverPort);
103-
}
104-
105-
/**
106-
* Returns the qualifiers to add to the performance metrics, specifying the configured server
107-
* @return a metrics qualifier string
108-
*/
109-
static String getPerformanceQualifier() {
110-
return String.format("{instance=\"%s:%d\"}", serverName, serverPort);
98+
static Integer getConfiguredRestPort() {
99+
return Optional.ofNullable(config).map(ExporterConfig::getRestPort).orElse(null);
111100
}
112101

113102
/**

src/main/java/io/prometheus/wls/rest/MainServlet.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
package io.prometheus.wls.rest;
22
/*
3-
* Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates.
44
*
55
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
66
*/
7+
import java.io.IOException;
8+
import java.io.PrintStream;
79
import javax.servlet.ServletConfig;
810
import javax.servlet.ServletOutputStream;
911
import javax.servlet.annotation.WebServlet;
1012
import javax.servlet.http.HttpServlet;
1113
import javax.servlet.http.HttpServletRequest;
1214
import javax.servlet.http.HttpServletResponse;
13-
import java.io.IOException;
14-
import java.io.PrintStream;
1515

16-
import static io.prometheus.wls.rest.ServletConstants.*;
16+
import static io.prometheus.wls.rest.ServletConstants.APPEND_ACTION;
17+
import static io.prometheus.wls.rest.ServletConstants.CONFIGURATION_PAGE;
18+
import static io.prometheus.wls.rest.ServletConstants.EFFECT_OPTION;
19+
import static io.prometheus.wls.rest.ServletConstants.REPLACE_ACTION;
1720

1821
/**
1922
* This servlet represents the 'landing page' for the exporter.
@@ -30,8 +33,8 @@ public void init(ServletConfig servletConfig) {
3033

3134
@Override
3235
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
33-
LiveConfiguration.setServer(req.getServerName(), req.getServerPort());
3436
LiveConfiguration.updateConfiguration();
37+
LiveConfiguration.setServer(req);
3538
resp.getOutputStream().println(ServletConstants.PAGE_HEADER);
3639
displayMetricsLink(req, resp.getOutputStream());
3740
displayForm(req, resp.getOutputStream());

src/main/java/io/prometheus/wls/rest/MessagesServlet.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
import static io.prometheus.wls.rest.ServletConstants.MESSAGES_PAGE;
1919

2020
/**
21-
* A collector of REST requests and replies, that can be viewed to diagnose problems.List<String
21+
* A collector of REST requests and replies, that can be viewed to diagnose problems.
2222
*/
2323
@WebServlet("/" + MESSAGES_PAGE)
2424
public class MessagesServlet extends HttpServlet {
2525
static final int MAX_EXCHANGES = 5;
2626
private static final String TEMPLATE = "REQUEST to %s:%n%s%nREPLY:%n%s%n";
2727

28-
private static Queue<String> messages = new ConcurrentLinkedDeque<>();
28+
private static final Queue<String> messages = new ConcurrentLinkedDeque<>();
2929

3030
@Override
3131
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

src/main/java/io/prometheus/wls/rest/MetricsStream.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package io.prometheus.wls.rest;
22
/*
3-
* Copyright (c) 2017, 2019, Oracle Corporation and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2017, 2020, Oracle Corporation and/or its affiliates.
44
*
55
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
66
*/
7-
import com.sun.management.OperatingSystemMXBean;
8-
9-
import javax.management.MBeanServerConnection;
107
import java.io.IOException;
118
import java.io.OutputStream;
129
import java.io.PrintStream;
1310
import java.lang.management.ManagementFactory;
1411
import java.util.Locale;
12+
import javax.management.MBeanServerConnection;
13+
import javax.servlet.http.HttpServletRequest;
14+
15+
import com.sun.management.OperatingSystemMXBean;
1516

1617
/**
1718
* A PrintStream that computes metrics for the performance of the exporter itself. It does so by tracking the
@@ -22,6 +23,7 @@
2223
class MetricsStream extends PrintStream {
2324
private static final double NANOSEC_PER_SECONDS = 1000000000;
2425

26+
private final HttpServletRequest request;
2527
private final PerformanceProbe performanceProbe;
2628
private final long startTime;
2729
private final long startCpu;
@@ -30,20 +32,23 @@ class MetricsStream extends PrintStream {
3032

3133
/**
3234
* Constructs a metrics stream object, installer a performance probe that access system data.
35+
* @param request the request for the metrics
3336
* @param outputStream the parent output stream
3437
* @throws IOException if some error occurs while creating the performance probe
3538
*/
36-
MetricsStream(OutputStream outputStream) throws IOException {
37-
this(outputStream, new PlatformPeformanceProbe());
39+
MetricsStream(HttpServletRequest request, OutputStream outputStream) throws IOException {
40+
this(request, outputStream, new PlatformPeformanceProbe());
3841
}
3942

4043
/**
4144
* A constructor for unit testing, allowing the specification of a test version of the performance probe.
45+
* @param request the request for the metrics
4246
* @param outputStream the parent output stream
4347
* @param performanceProbe an object which can return performance data
4448
*/
45-
MetricsStream(OutputStream outputStream, PerformanceProbe performanceProbe) {
49+
MetricsStream(HttpServletRequest request, OutputStream outputStream, PerformanceProbe performanceProbe) {
4650
super(outputStream);
51+
this.request = request;
4752
this.performanceProbe = performanceProbe;
4853
startTime = performanceProbe.getCurrentTime();
4954
startCpu = performanceProbe.getCurrentCpu();
@@ -69,15 +74,23 @@ void printPerformanceMetrics() {
6974
}
7075

7176
private String getDurationName() {
72-
return "wls_scrape_duration_seconds" + LiveConfiguration.getPerformanceQualifier();
77+
return "wls_scrape_duration_seconds" + getPerformanceQualifier();
78+
}
79+
80+
/**
81+
* Returns the qualifiers to add to the performance metrics, specifying the configured server
82+
* @return a metrics qualifier string
83+
*/
84+
private String getPerformanceQualifier() {
85+
return String.format("{instance=\"%s:%d\"}", request.getServerName(), request.getServerPort());
7386
}
7487

7588
private String getCpuUsageName() {
76-
return "wls_scrape_cpu_seconds" + LiveConfiguration.getPerformanceQualifier();
89+
return "wls_scrape_cpu_seconds" + getPerformanceQualifier();
7790
}
7891

7992
private String getCountName() {
80-
return "wls_scrape_mbeans_count_total" + LiveConfiguration.getPerformanceQualifier();
93+
return "wls_scrape_mbeans_count_total" + getPerformanceQualifier();
8194
}
8295

8396
private long getElapsedTime() {

src/main/java/io/prometheus/wls/rest/PassThroughAuthenticationServlet.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
import javax.servlet.http.HttpServletResponse;
1515
import javax.servlet.http.HttpSession;
1616

17-
import io.prometheus.wls.rest.domain.Protocol;
17+
import io.prometheus.wls.rest.domain.MBeanSelector;
18+
import io.prometheus.wls.rest.domain.QueryType;
1819

1920
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
2021
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
@@ -26,19 +27,31 @@
2627
*/
2728
abstract public class PassThroughAuthenticationServlet extends HttpServlet {
2829
private final WebClientFactory webClientFactory;
29-
private Protocol protocol;
30+
private UrlBuilder urlBuilder;
3031

3132
PassThroughAuthenticationServlet(WebClientFactory webClientFactory) {
3233
this.webClientFactory = webClientFactory;
3334
}
3435

35-
private WebClient createWebClient(HttpServletRequest req) {
36-
LiveConfiguration.setServer(req.getServerName(), req.getServerPort());
36+
String getAuthenticationUrl() {
37+
return urlBuilder.createUrl(QueryType.RUNTIME_URL_PATTERN);
38+
}
39+
40+
String getQueryUrl(MBeanSelector selector) {
41+
return urlBuilder.createUrl(selector.getQueryType().getUrlPattern());
42+
}
43+
44+
void reportFailure(RestPortConnectionException e) {
45+
urlBuilder.reportFailure(e);
46+
}
47+
48+
WebClient createWebClient(HttpServletRequest req) {
49+
LiveConfiguration.setServer(req);
50+
urlBuilder = LiveConfiguration.createUrlBuilder(req);
3751
final WebClient webClient = webClientFactory.createClient();
3852
webClient.addHeader("X-Requested-By", "rest-exporter");
3953

4054
forwardRequestHeaders(req, webClient);
41-
protocol = Protocol.getProtocol(req);
4255
return webClient;
4356
}
4457

@@ -71,8 +84,8 @@ private String getSessionCookie(HttpServletRequest req) {
7184
void doWithAuthentication(HttpServletRequest req, HttpServletResponse resp, AuthenticatedService authenticatedService) throws IOException, ServletException {
7285
try {
7386
WebClient webClient = createWebClient(req);
74-
authenticatedService.execute(webClient, req, resp);
75-
webClient.forwardResponseHeaders(resp);
87+
performRequest(webClient, req, resp, authenticatedService);
88+
urlBuilder.reportSuccess();
7689
} catch (ForbiddenException e) {
7790
resp.sendError(SC_FORBIDDEN, "Not authorized");
7891
} catch (AuthenticationChallengeException e) {
@@ -89,8 +102,11 @@ void doWithAuthentication(HttpServletRequest req, HttpServletResponse resp, Auth
89102
}
90103
}
91104

92-
protected Protocol getProtocol() {
93-
return protocol;
105+
private void performRequest(WebClient webClient, HttpServletRequest req, HttpServletResponse resp, AuthenticatedService authenticatedService) throws IOException, ServletException {
106+
do {
107+
authenticatedService.execute(webClient, req, resp);
108+
webClient.forwardResponseHeaders(resp);
109+
} while (webClient.isRetryNeeded());
94110
}
95111

96112
private void reportUnableToContactRestApi(HttpServletResponse resp, String uri) throws IOException {

0 commit comments

Comments
 (0)