Skip to content

Commit f0d96ab

Browse files
authored
Improve filtering: ignore subtrees with no matches, exclude forbidden… (#240)
1 parent 8c24c4e commit f0d96ab

File tree

10 files changed

+311
-38
lines changed

10 files changed

+311
-38
lines changed

build-helper-mojo/src/main/java/com/oracle/wls/buildhelper/GitTagMojo.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
// Copyright (c) 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.buildhelper;
55

6-
import org.apache.maven.plugin.AbstractMojo;
7-
import org.apache.maven.plugin.MojoExecutionException;
8-
import org.apache.maven.plugins.annotations.LifecyclePhase;
9-
import org.apache.maven.plugins.annotations.Mojo;
10-
import org.apache.maven.plugins.annotations.Parameter;
11-
126
import java.io.File;
137
import java.io.IOException;
148
import java.nio.file.Files;
@@ -17,6 +11,12 @@
1711
import java.util.Collections;
1812
import java.util.List;
1913

14+
import org.apache.maven.plugin.AbstractMojo;
15+
import org.apache.maven.plugin.MojoExecutionException;
16+
import org.apache.maven.plugins.annotations.LifecyclePhase;
17+
import org.apache.maven.plugins.annotations.Mojo;
18+
import org.apache.maven.plugins.annotations.Parameter;
19+
2020
@Mojo(name = "gitVersion", defaultPhase = LifecyclePhase.PREPARE_PACKAGE)
2121
public class GitTagMojo extends AbstractMojo {
2222

@@ -61,7 +61,7 @@ private String toVersionString(String gitResponse) {
6161
}
6262

6363
private String formatVersionString(String[] segments) {
64-
final String commit = segments[segments.length - 1];
64+
final String commit = segments[segments.length - 1].substring(1);
6565
final String numCommits = segments[segments.length - 2];
6666
final String versionParts = String.join("-", Arrays.copyOfRange(segments, 0, segments.length - 2));
6767
return formatVersionString(commit, numCommits, versionParts);

build-helper-mojo/src/test/java/com/oracle/wls/buildhelper/GitTagMojoTest.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
// Copyright (c) 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2022, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.buildhelper;
55

6+
import java.io.File;
7+
import java.lang.reflect.Field;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
611
import org.apache.maven.plugin.AbstractMojo;
712
import org.apache.maven.plugin.MojoExecutionException;
813
import org.apache.maven.plugins.annotations.LifecyclePhase;
914
import org.junit.jupiter.api.BeforeEach;
1015
import org.junit.jupiter.api.Test;
1116

12-
import java.io.File;
13-
import java.lang.reflect.Field;
14-
import java.nio.file.Files;
15-
import java.nio.file.Path;
16-
1717
import static org.hamcrest.MatcherAssert.assertThat;
18-
import static org.hamcrest.Matchers.*;
18+
import static org.hamcrest.Matchers.containsString;
19+
import static org.hamcrest.Matchers.equalTo;
20+
import static org.hamcrest.Matchers.instanceOf;
21+
import static org.hamcrest.Matchers.is;
22+
import static org.hamcrest.Matchers.notNullValue;
1923
import static org.junit.jupiter.api.Assertions.assertThrows;
2024

2125
class GitTagMojoTest {
@@ -88,7 +92,7 @@ void whenGitResponseIsVersionPlusHistory_createVersionProperty() throws Exceptio
8892
mojo.execute();
8993

9094
assertThat(inMemoryFileSystem.getContents(outputFile.getAbsolutePath()),
91-
containsString("version=gcb4385f3aa (946 commits since v3.3.5-3)"));
95+
containsString("version=cb4385f3aa (946 commits since v3.3.5-3)"));
9296
}
9397

9498
@Test

wls-exporter-core/src/main/java/com/oracle/wls/exporter/ExporterCall.java

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2021, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2021, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.exporter;
@@ -52,9 +52,7 @@ private void displayMetrics(WebClient webClient, MetricsStream metricsStream, MB
5252
if (!metrics.isEmpty())
5353
sort(metrics).forEach(metricsStream::printMetric);
5454
} catch (RestQueryException e) {
55-
metricsStream.println(
56-
withCommentMarkers("REST service was unable to handle this query and returned a " + HTTP_BAD_REQUEST + "\n"
57-
+ selector.getPrintableRequest()));
55+
reportProblem(metricsStream, selector);
5856
} catch (AuthenticationChallengeException e) { // don't add a message for this case
5957
throw e;
6058
} catch (IOException | RuntimeException e) {
@@ -63,6 +61,19 @@ private void displayMetrics(WebClient webClient, MetricsStream metricsStream, MB
6361
}
6462
}
6563

64+
private void reportProblem(MetricsStream metricsStream, MBeanSelector selector) {
65+
metricsStream.println(withCommentMarkers(getProblem(selector) + "\n" + selector.getPrintableRequest()));
66+
}
67+
68+
private String getProblem(MBeanSelector selector) {
69+
if (selector.isRequestForPrivilegedProperty())
70+
return "You seem to have encountered a bug in the WebLogic REST API.\n" +
71+
" The JDBCServiceRuntime.JDBCDataSourceRuntimeMBeans.properties property " +
72+
" may only be accessed by a user with administrator privileges.";
73+
else
74+
return "REST service was unable to handle this query and returned a " + HTTP_BAD_REQUEST;
75+
}
76+
6677
private static String withCommentMarkers(String string) {
6778
StringBuilder sb = new StringBuilder();
6879
for (String s : string.split("\\r?\\n"))

wls-exporter-core/src/main/java/com/oracle/wls/exporter/WebClientCommon.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
2626
import static java.net.HttpURLConnection.HTTP_FORBIDDEN;
2727
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
28-
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
2928

3029
/**
3130
* A client for sending http requests. Note that it does not do any authentication by itself.
@@ -227,7 +226,7 @@ private void processStatusCode() {
227226
case HTTP_FORBIDDEN:
228227
throw new ForbiddenException();
229228
default:
230-
if (response.getResponseCode() > SC_BAD_REQUEST)
229+
if (response.getResponseCode() > HTTP_BAD_REQUEST)
231230
throw createServerErrorException();
232231
}
233232
}

wls-exporter-core/src/main/java/com/oracle/wls/exporter/domain/ExporterConfig.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.exporter.domain;
@@ -24,6 +24,10 @@
2424
* @author Russell Gold
2525
*/
2626
public class ExporterConfig implements MetricsProcessor {
27+
// Due to a bug in the WLS REST API, a query that includes this field will fail if not run with admin privileges.
28+
// The code thus ensures that it is excluded from all queries.
29+
private static final String[] FORBIDDEN_FIELDS = {"JDBCServiceRuntime:JDBCDataSourceRuntimeMBeans:properties"};
30+
2731
public static final String DOMAIN_NAME_PROPERTY = "DOMAIN";
2832

2933
private static final String QUERY_SYNC = "query_sync";
@@ -173,7 +177,7 @@ private QuerySyncConfiguration loadQuerySync(Object o) {
173177

174178
private void appendQueries(Object queriesYaml) {
175179
for (Map<String,Object> selectorSpec : getAsListOfMaps(queriesYaml)) {
176-
appendQuery(MBeanSelector.create(selectorSpec));
180+
appendQuery(MBeanSelector.create(selectorSpec).withForbiddenFields(FORBIDDEN_FIELDS));
177181
}
178182
}
179183

wls-exporter-core/src/main/java/com/oracle/wls/exporter/domain/JsonQuerySpec.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017, 2020, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.exporter.domain;
@@ -8,6 +8,7 @@
88
import java.util.HashMap;
99
import java.util.List;
1010
import java.util.Map;
11+
import java.util.Objects;
1112
import java.util.Set;
1213

1314
import com.google.gson.Gson;
@@ -27,14 +28,15 @@ class JsonQuerySpec {
2728
private Map<String, JsonQuerySpec> children = null;
2829
private String keyName = null;
2930
private List<String> selectedKeys = null;
31+
private List<String> excludeFields = null;
3032

3133
/**
3234
* Specifies the name of any mbean values which should be retrieved.
3335
* @param newFields the field names to add to any previous defined
3436
*/
3537
void addFields(String... newFields) {
3638
if (fields == null) fields = new ArrayList<>();
37-
fields.addAll(Arrays.asList(newFields));
39+
Arrays.stream(newFields).filter(Objects::nonNull).forEach(fields::add);
3840
}
3941

4042
/**
@@ -62,6 +64,7 @@ JsonObject toJsonObject() {
6264

6365
result.add("links", new JsonArray());
6466
if (fields != null) result.add("fields", asStringArray(fields));
67+
if (excludeFields != null) result.add("excludeFields", asStringArray(excludeFields));
6568
if (keyName != null) result.add(keyName, asStringArray(selectedKeys));
6669
if (children != null) asChildObject(result);
6770

@@ -81,4 +84,9 @@ private void asChildObject(JsonObject result) {
8184
nesting.add(entry.getKey(), entry.getValue().toJsonObject());
8285
}
8386
}
87+
88+
public void excludeField(String fieldName) {
89+
if (excludeFields == null) excludeFields = new ArrayList<>();
90+
excludeFields.add(fieldName);
91+
}
8492
}

wls-exporter-core/src/main/java/com/oracle/wls/exporter/domain/MBeanSelector.java

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.exporter.domain;
55

66
import java.time.Clock;
77
import java.util.ArrayList;
88
import java.util.Arrays;
9+
import java.util.Collections;
910
import java.util.HashMap;
1011
import java.util.HashSet;
1112
import java.util.LinkedHashMap;
@@ -15,6 +16,7 @@
1516
import java.util.Optional;
1617
import java.util.Set;
1718
import java.util.regex.Pattern;
19+
import java.util.stream.Collectors;
1820
import java.util.stream.Stream;
1921
import java.util.stream.StreamSupport;
2022

@@ -61,6 +63,7 @@ public class MBeanSelector {
6163
private Map<String, MBeanSelector> nestedSelectors = new LinkedHashMap<>();
6264
private QueryType queryType = QueryType.RUNTIME;
6365
private long lastKeyTime = 0;
66+
private String[] forbiddenFields;
6467

6568
private static MBeanSelector createDomainNameSelector() {
6669
Map<String,Object> yaml = new HashMap<>();
@@ -228,6 +231,39 @@ public static MBeanSelector create(Map<String, Object> map) {
228231
return new MBeanSelector(map);
229232
}
230233

234+
MBeanSelector withForbiddenFields(String... forbiddenFields) {
235+
setForbiddenFields(forbiddenFields);
236+
return this;
237+
}
238+
239+
private void setForbiddenFields(String[] forbiddenFields) {
240+
this.forbiddenFields = forbiddenFields;
241+
nestedSelectors.entrySet().forEach(this::defineNestedForbiddenFields);
242+
}
243+
244+
private void defineNestedForbiddenFields(Map.Entry<String, MBeanSelector> entry) {
245+
entry.getValue().setForbiddenFields(getNestedForbiddenFields(entry.getKey()));
246+
}
247+
248+
private String[] getNestedForbiddenFields(String key) {
249+
return Arrays.stream(forbiddenFields)
250+
.map(f -> withoutMatchingTopLevel(f, key))
251+
.filter(Objects::nonNull)
252+
.toArray(String[]::new);
253+
}
254+
255+
private String withoutMatchingTopLevel(String forbiddenField, String key) {
256+
final int index = forbiddenField.indexOf(':');
257+
if (index < 0)
258+
return null;
259+
else if (!key.equalsIgnoreCase(forbiddenField.substring(0, index))) {
260+
return null;
261+
} else {
262+
return forbiddenField.substring(index+1);
263+
}
264+
}
265+
266+
231267
/**
232268
* Returns the type of mbean to process, from among those captured by this selector. If empty or null,
233269
* processes all captured mbeans.
@@ -284,6 +320,7 @@ String getExcludedKeys() {
284320
String[] getQueryValues() {
285321
final List<String> result = new ArrayList<>(values);
286322
if (stringValues != null) result.addAll(stringValues.keySet());
323+
getActiveForbiddenFields().forEach(result::remove);
287324

288325
return result.toArray(new String[0]);
289326
}
@@ -318,8 +355,11 @@ public String getRequest() {
318355

319356
JsonQuerySpec toQuerySpec() {
320357
JsonQuerySpec spec = new JsonQuerySpec();
321-
if (!useAllValues())
358+
if (useAllValues()) {
359+
getActiveForbiddenFields().forEach(spec::excludeField);
360+
} else {
322361
selectQueryFields(spec, getQueryValues());
362+
}
323363
if (currentSelectorHasFilter() && !filter.isEmpty())
324364
spec.setFilter(key, filter);
325365

@@ -330,6 +370,17 @@ JsonQuerySpec toQuerySpec() {
330370
return spec;
331371
}
332372

373+
private List<String> getActiveForbiddenFields() {
374+
if (forbiddenFields == null)
375+
return Collections.emptyList();
376+
else
377+
return Arrays.stream(forbiddenFields).filter(this::isLeafField).collect(Collectors.toList());
378+
}
379+
380+
private boolean isLeafField(String forbiddenField) {
381+
return !forbiddenField.contains(":");
382+
}
383+
333384
private boolean isEnabled() {
334385
return !filter.isEmpty() || !currentSelectorHasFilter();
335386
}
@@ -354,10 +405,14 @@ public String getKeyRequest() {
354405

355406
JsonQuerySpec toKeyQuerySpec() {
356407
JsonQuerySpec spec = new JsonQuerySpec();
357-
if (currentSelectorHasFilter()) spec.addFields(key);
408+
if (currentSelectorHasFilter())
409+
spec.addFields(key);
410+
else
411+
spec.addFields();
358412

359413
for (Map.Entry<String, MBeanSelector> selector : nestedSelectors.entrySet())
360-
spec.addChild(selector.getKey(), selector.getValue().toKeyQuerySpec());
414+
if (selector.getValue().hasFilter())
415+
spec.addChild(selector.getKey(), selector.getValue().toKeyQuerySpec());
361416

362417
return spec;
363418
}
@@ -552,4 +607,19 @@ private int getIndex(String value, List<String> fieldValues) {
552607
void postProcessMetrics(Map<String, Object> metrics, MetricsProcessor processor) {
553608
queryType.postProcessMetrics(metrics, processor);
554609
}
610+
611+
public boolean isRequestForPrivilegedProperty() {
612+
if (queryType != QueryType.RUNTIME) return false;
613+
614+
return Optional.ofNullable(nestedSelectors.get("JDBCServiceRuntime"))
615+
.map(MBeanSelector::isJDBCDataSourceRuntimeMBeansPropertiesRequest)
616+
.orElse(false);
617+
}
618+
619+
private boolean isJDBCDataSourceRuntimeMBeansPropertiesRequest() {
620+
final MBeanSelector jdbcSourceRuntimeSelector = nestedSelectors.get("JDBCDataSourceRuntimeMBeans");
621+
if (jdbcSourceRuntimeSelector == null) return false;
622+
return jdbcSourceRuntimeSelector.values.isEmpty() || jdbcSourceRuntimeSelector.values.contains("properties");
623+
}
624+
555625
}

wls-exporter-core/src/main/java/com/oracle/wls/exporter/DemoInputs.java renamed to wls-exporter-core/src/test/java/com/oracle/wls/exporter/DemoInputs.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017, 2022, Oracle and/or its affiliates.
1+
// Copyright (c) 2017, 2023, Oracle and/or its affiliates.
22
// Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
33

44
package com.oracle.wls.exporter;

wls-exporter-core/src/test/java/com/oracle/wls/exporter/ExporterCallTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class ExporterCallTest {
2525
private static final String ONE_VALUE_CONFIG = "queries:\n- groups:\n key: name\n values: testSample1";
2626
private static final String CONFIG_WITH_FILTER = "queries:" +
2727
"\n- groups:\n key: name\n includedKeyValues: abc.*\n values: testSample1";
28+
private static final String REQUEST_FOR_PRIVILEGED_PROPERTY = "queries:" +
29+
"\n- JDBCServiceRuntime:\n JDBCDataSourceRuntimeMBeans:\n key: name\n values: properties";
30+
private static final String REQUEST_INCLUDES_PRIVILEGED_PROPERTY = "queries:" +
31+
"\n- JDBCServiceRuntime:\n JDBCDataSourceRuntimeMBeans:\n prefix: ds_\n key: name\n";
2832
private static final String DUAL_QUERY_CONFIG = ONE_VALUE_CONFIG +
2933
"\n- clubs:\n key: name\n values: testSample2";
3034

@@ -117,6 +121,26 @@ void whenQuerySentWithFilter_twoRequestsAreMade() throws IOException {
117121
assertThat(factory.getNumQueriesSent(), equalTo(2));
118122
}
119123

124+
@Test
125+
void whenBadQueryReceivedAndConfigurationSelectsPrivilegedPropertiesProperty_explainProblem() throws IOException {
126+
factory.reportBadQuery();
127+
LiveConfiguration.loadFromString(REQUEST_FOR_PRIVILEGED_PROPERTY);
128+
129+
handleMetricsCall(context.withHttps());
130+
131+
assertThat(context.getResponse(), containsString("bug in the WebLogic REST API"));
132+
}
133+
134+
@Test
135+
void whenBadQueryReceivedAndConfigurationSelectsPropertiesIncludingPrivilegedProperty_explainProblem() throws IOException {
136+
factory.reportBadQuery();
137+
LiveConfiguration.loadFromString(REQUEST_INCLUDES_PRIVILEGED_PROPERTY);
138+
139+
handleMetricsCall(context.withHttps());
140+
141+
assertThat(context.getResponse(), containsString("bug in the WebLogic REST API"));
142+
}
143+
120144
@Test
121145
void whenHaveMultipleQueries_sendServerDefinedCookieOnSecondQuery() throws IOException {
122146
factory.forJson(QUERY_RESPONSE1_JSON).withResponseHeader(SET_COOKIE_HEADER, "cookieName=newValue").addResponse();

0 commit comments

Comments
 (0)