Don’t return 404 when metric or env regex matches entry with null value

Previously, if a regular expression was used when calling the metrics or
environment endpoints, a metric or property with a null value would
result in a 404 response.

This commit updates the two affected endpoints so that any metric or
property whose name matches the regular expression but has a null value
is ignored. This allows all of the matching metrics or properties with
non-null values to be returned in a 200 OK response.

Closes gh-4552
pull/4511/merge
Andy Wilkinson 9 years ago
parent f25a5e3b77
commit 8e0d3ed0eb

@ -92,6 +92,15 @@ public class EnvironmentMvcEndpoint extends EndpointMvcAdapter
} }
} }
@Override
protected Object getOptionalValue(Environment source, String name) {
Object result = source.getProperty(name);
if (result != null) {
result = ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
}
return result;
}
@Override @Override
protected Object getValue(Environment source, String name) { protected Object getValue(Environment source, String name) {
String result = source.getProperty(name); String result = source.getProperty(name);

@ -67,13 +67,23 @@ public class MetricsMvcEndpoint extends EndpointMvcAdapter {
@Override @Override
protected void getNames(Map<String, ?> source, NameCallback callback) { protected void getNames(Map<String, ?> source, NameCallback callback) {
for (String name : source.keySet()) { for (String name : source.keySet()) {
try {
callback.addName(name); callback.addName(name);
} }
catch (NoSuchMetricException ex) {
// Metric with null value. Continue.
}
}
}
@Override
protected Object getOptionalValue(Map<String, ?> source, String name) {
return source.get(name);
} }
@Override @Override
protected Object getValue(Map<String, ?> source, String name) { protected Object getValue(Map<String, ?> source, String name) {
Object value = source.get(name); Object value = getOptionalValue(source, name);
if (value == null) { if (value == null) {
throw new NoSuchMetricException("No such metric: " + name); throw new NoSuchMetricException("No such metric: " + name);
} }

@ -71,6 +71,8 @@ abstract class NamePatternFilter<T> {
protected abstract Object getValue(T source, String name); protected abstract Object getValue(T source, String name);
protected abstract Object getOptionalValue(T source, String name);
/** /**
* Callback used to add a name. * Callback used to add a name.
*/ */
@ -96,7 +98,10 @@ abstract class NamePatternFilter<T> {
@Override @Override
public void addName(String name) { public void addName(String name) {
if (this.pattern.matcher(name).matches()) { if (this.pattern.matcher(name).matches()) {
this.results.put(name, getValue(NamePatternFilter.this.source, name)); Object value = getOptionalValue(NamePatternFilter.this.source, name);
if (value != null) {
this.results.put(name, value);
}
} }
} }

@ -16,6 +16,9 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -34,6 +37,8 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
@ -89,6 +94,10 @@ public class EnvironmentMvcEndpointTests {
@Test @Test
public void regex() throws Exception { public void regex() throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("food", null);
((ConfigurableEnvironment) this.context.getEnvironment()).getPropertySources()
.addFirst(new MapPropertySource("null-value", map));
this.mvc.perform(get("/env/foo.*")).andExpect(status().isOk()) this.mvc.perform(get("/env/foo.*")).andExpect(status().isOk())
.andExpect(content().string(containsString("\"foo\":\"bar\""))) .andExpect(content().string(containsString("\"foo\":\"bar\"")))
.andExpect(content().string(containsString("\"fool\":\"baz\""))); .andExpect(content().string(containsString("\"fool\":\"baz\"")));

@ -126,7 +126,6 @@ public class MetricsMvcEndpointTests {
public void specificMetricWithDot() throws Exception { public void specificMetricWithDot() throws Exception {
this.mvc.perform(get("/metrics/group2.a")).andExpect(status().isOk()) this.mvc.perform(get("/metrics/group2.a")).andExpect(status().isOk())
.andExpect(content().string(containsString("1"))); .andExpect(content().string(containsString("1")));
} }
@Import({ JacksonAutoConfiguration.class, @Import({ JacksonAutoConfiguration.class,
@ -148,6 +147,7 @@ public class MetricsMvcEndpointTests {
metrics.add(new Metric<Integer>("group1.b", 1)); metrics.add(new Metric<Integer>("group1.b", 1));
metrics.add(new Metric<Integer>("group2.a", 1)); metrics.add(new Metric<Integer>("group2.a", 1));
metrics.add(new Metric<Integer>("group2_a", 1)); metrics.add(new Metric<Integer>("group2_a", 1));
metrics.add(new Metric<Integer>("baz", null));
return Collections.unmodifiableList(metrics); return Collections.unmodifiableList(metrics);
} }

@ -95,6 +95,11 @@ public class NamePatternFilterTests {
private boolean getNamesCalled; private boolean getNamesCalled;
@Override
protected Object getOptionalValue(Object source, String name) {
return name;
}
@Override @Override
protected Object getValue(Object source, String name) { protected Object getValue(Object source, String name) {
return name; return name;

Loading…
Cancel
Save