From 4ee6a90edd9fe579c9e2ddf2b68d436031aeba92 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 7 Jun 2013 13:29:35 +0100 Subject: [PATCH] [bs-148] Add /env endpoint for Spring Environment Any enumerable property source is enumerated. Plus all properties are available through /env/{name}. [Fixes #51141441] --- .../autoconfigure/EnvConfiguration.java | 49 ++++++++++++ .../ManagementEndpointsRegistration.java | 3 +- .../actuate/endpoint/env/EnvEndpoint.java | 75 +++++++++++++++++++ .../properties/EndpointsProperties.java | 10 ++- .../ServiceBootstrapApplicationTests.java | 22 ++++++ 5 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/EnvConfiguration.java create mode 100644 spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/endpoint/env/EnvEndpoint.java diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/EnvConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/EnvConfiguration.java new file mode 100644 index 00000000000..3161c5b5cc4 --- /dev/null +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/EnvConfiguration.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.bootstrap.actuate.autoconfigure; + +import javax.servlet.Servlet; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.bootstrap.actuate.endpoint.env.EnvEndpoint; +import org.springframework.bootstrap.context.annotation.ConditionalOnClass; +import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean; +import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.web.servlet.DispatcherServlet; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for /metrics endpoint. + * + * @author Dave Syer + */ +@Configuration +@ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) +@ConditionalOnMissingBean({ EnvEndpoint.class }) +public class EnvConfiguration { + + @Autowired + private ConfigurableEnvironment environment; + + @Bean + public EnvEndpoint envEndpoint() { + return new EnvEndpoint(this.environment); + } + +} diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementEndpointsRegistration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementEndpointsRegistration.java index f7e23e391ca..fdc6b5243b6 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementEndpointsRegistration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementEndpointsRegistration.java @@ -28,7 +28,8 @@ import org.springframework.context.annotation.Import; @Configuration @ConditionalOnManagementContext @Import({ MetricsConfiguration.class, HealthConfiguration.class, - ShutdownConfiguration.class, TraceConfiguration.class, BeansConfiguration.class }) + ShutdownConfiguration.class, TraceConfiguration.class, BeansConfiguration.class, + EnvConfiguration.class }) public class ManagementEndpointsRegistration { } diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/endpoint/env/EnvEndpoint.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/endpoint/env/EnvEndpoint.java new file mode 100644 index 00000000000..d189f277dd2 --- /dev/null +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/endpoint/env/EnvEndpoint.java @@ -0,0 +1,75 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.bootstrap.actuate.endpoint.env; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.EnumerablePropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author Dave Syer + */ +@Controller +public class EnvEndpoint { + + private final ConfigurableEnvironment environment; + + public EnvEndpoint(ConfigurableEnvironment environment) { + this.environment = environment; + } + + @RequestMapping("${endpoints.metrics.path:/env}") + @ResponseBody + public Map env() { + Map result = new LinkedHashMap(); + for (PropertySource source : this.environment.getPropertySources()) { + if (source instanceof EnumerablePropertySource) { + EnumerablePropertySource enumerable = (EnumerablePropertySource) source; + Map map = new LinkedHashMap(); + for (String name : enumerable.getPropertyNames()) { + map.put(name, sanitize(name, enumerable.getProperty(name))); + } + result.put(source.getName(), map); + } + } + return result; + } + + @RequestMapping("${endpoints.metrics.path:/env}/{name:[a-zA-Z0-9._-]+}") + @ResponseBody + public Map env(@PathVariable String name) { + Map result = new LinkedHashMap(); + result.put(name, sanitize(name, this.environment.getProperty(name))); + return result; + } + + private Object sanitize(String name, Object object) { + if (name.toLowerCase().endsWith("password") + || name.toLowerCase().endsWith("secret")) { + return object == null ? null : "******"; + } + return object; + } + +} diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/properties/EndpointsProperties.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/properties/EndpointsProperties.java index fd83566faeb..9993305e3db 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/properties/EndpointsProperties.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/properties/EndpointsProperties.java @@ -54,6 +54,9 @@ public class EndpointsProperties { @Valid private Endpoint beans = new Endpoint("/beans"); + @Valid + private Endpoint env = new Endpoint("/env"); + public Endpoint getInfo() { return this.info; } @@ -86,6 +89,10 @@ public class EndpointsProperties { return this.beans; } + public Endpoint getEnv() { + return this.env; + } + public static class Endpoint { @NotNull @@ -111,7 +118,8 @@ public class EndpointsProperties { public String[] getSecurePaths() { return new String[] { getMetrics().getPath(), getBeans().getPath(), - getDump().getPath(), getShutdown().getPath(), getTrace().getPath() }; + getDump().getPath(), getShutdown().getPath(), getTrace().getPath(), + getEnv().getPath() }; } public String[] getOpenPaths() { diff --git a/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java b/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java index 9170072861a..2a3b5249443 100644 --- a/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java +++ b/spring-bootstrap-samples/spring-bootstrap-actuator-sample/src/test/java/org/springframework/bootstrap/sample/test/ServiceBootstrapApplicationTests.java @@ -97,6 +97,28 @@ public class ServiceBootstrapApplicationTests { assertTrue("Wrong body: " + body, body.containsKey("counter.status.200.root")); } + @Test + public void testEnv() throws Exception { + @SuppressWarnings("rawtypes") + ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + "http://localhost:8080/env", Map.class); + assertEquals(HttpStatus.OK, entity.getStatusCode()); + @SuppressWarnings("unchecked") + Map body = entity.getBody(); + assertTrue("Wrong body: " + body, body.containsKey("systemProperties")); + } + + @Test + public void testEnvProperty() throws Exception { + @SuppressWarnings("rawtypes") + ResponseEntity entity = getRestTemplate("user", "password").getForEntity( + "http://localhost:8080/env/logging.file", Map.class); + assertEquals(HttpStatus.OK, entity.getStatusCode()); + @SuppressWarnings("unchecked") + Map body = entity.getBody(); + assertEquals("{logging.file=/tmp/logs/app.log}", body.toString()); + } + @Test public void testHealth() throws Exception { ResponseEntity entity = getRestTemplate().getForEntity(