Update to new Boot 2 actuator @Endpoint.

Some tests are still broken.

Fixes gh-238
This commit is contained in:
Spencer Gibb
2017-09-07 21:21:37 -06:00
parent 752148fea5
commit f8d9e18fb9
20 changed files with 219 additions and 399 deletions

View File

@@ -15,21 +15,15 @@
*/
package org.springframework.cloud.autoconfigure;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.cloud.context.environment.EnvironmentManager;
import org.springframework.cloud.context.environment.EnvironmentManagerMvcEndpoint;
import org.springframework.cloud.context.restart.RestartEndpoint;
import org.springframework.cloud.context.restart.RestartMvcEndpoint;
import org.springframework.cloud.endpoint.GenericPostableMvcEndpoint;
import org.springframework.cloud.endpoint.RefreshEndpoint;
import org.springframework.cloud.context.environment.EnvironmentWebEndpointExtension;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -44,41 +38,16 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(EnvironmentEndpoint.class)
@ConditionalOnWebApplication
//TODO: support reactive
@AutoConfigureAfter({ WebMvcAutoConfiguration.class,
RefreshEndpointAutoConfiguration.class })
public class LifecycleMvcEndpointAutoConfiguration {
@Bean
@ConditionalOnBean(EnvironmentEndpoint.class)
@ConditionalOnEnabledEndpoint(value = "env.post")
public EnvironmentManagerMvcEndpoint environmentManagerEndpoint(
EnvironmentEndpoint delegate, EnvironmentManager environment) {
return new EnvironmentManagerMvcEndpoint(delegate, environment);
}
@Bean
@ConditionalOnBean(RefreshEndpoint.class)
public MvcEndpoint refreshMvcEndpoint(RefreshEndpoint endpoint) {
return new GenericPostableMvcEndpoint(endpoint);
}
@Bean
@ConditionalOnBean(RestartEndpoint.class)
public RestartMvcEndpoint restartMvcEndpoint(RestartEndpoint restartEndpoint) {
return new RestartMvcEndpoint(restartEndpoint);
}
@Bean
@ConditionalOnBean(RestartEndpoint.PauseEndpoint.class)
public MvcEndpoint pauseMvcEndpoint(RestartEndpoint.PauseEndpoint pauseEndpoint) {
return new GenericPostableMvcEndpoint(pauseEndpoint);
}
@Bean
@ConditionalOnBean(RestartEndpoint.ResumeEndpoint.class)
public MvcEndpoint resumeMvcEndpoint(RestartEndpoint.ResumeEndpoint resumeEndpoint) {
return new GenericPostableMvcEndpoint(resumeEndpoint);
@ConditionalOnEnabledEndpoint
public EnvironmentWebEndpointExtension environmentWebEndpointExtension(
EnvironmentManager environment) {
return new EnvironmentWebEndpointExtension(environment);
}
}

View File

@@ -17,16 +17,15 @@
package org.springframework.cloud.autoconfigure;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration;
import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder;
import org.springframework.cloud.context.refresh.ContextRefresher;
@@ -36,6 +35,7 @@ import org.springframework.cloud.endpoint.RefreshEndpoint;
import org.springframework.cloud.health.RefreshScopeHealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
/**
@@ -46,6 +46,9 @@ import org.springframework.integration.monitor.IntegrationMBeanExporter;
@Configuration
@ConditionalOnClass(Endpoint.class)
@AutoConfigureAfter(EndpointAutoConfiguration.class)
@Import({ RestartEndpointWithIntegrationConfiguration.class,
RestartEndpointWithoutIntegrationConfiguration.class,
PauseResumeEndpointsConfiguration.class })
public class RefreshEndpointAutoConfiguration {
@ConditionalOnMissingBean
@@ -56,67 +59,71 @@ public class RefreshEndpointAutoConfiguration {
return new RefreshScopeHealthIndicator(scope, rebinder);
}
@ConditionalOnClass(IntegrationMBeanExporter.class)
@ConditionalOnEnabledEndpoint(value = "restart", enabledByDefault = false)
protected static class RestartEndpointWithIntegration {
@Autowired(required = false)
private IntegrationMBeanExporter exporter;
@Bean
@ConditionalOnMissingBean
public RestartEndpoint restartEndpoint() {
RestartEndpoint endpoint = new RestartEndpoint();
if (this.exporter != null) {
endpoint.setIntegrationMBeanExporter(this.exporter);
}
return endpoint;
}
}
@ConditionalOnMissingClass("org.springframework.integration.monitor.IntegrationMBeanExporter")
@ConditionalOnEnabledEndpoint(value = "restart", enabledByDefault = false)
protected static class RestartEndpointWithoutIntegration {
@Bean
@ConditionalOnMissingBean
public RestartEndpoint restartEndpointWithoutIntegration() {
return new RestartEndpoint();
}
}
@ConditionalOnEnabledEndpoint(value = "restart", enabledByDefault = false)
protected static class PauseResumeEndpoints {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint("pause")
public RestartEndpoint.PauseEndpoint pauseEndpoint(RestartEndpoint restartEndpoint) {
return restartEndpoint.getPauseEndpoint();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint("resume")
public RestartEndpoint.ResumeEndpoint resumeEndpoint(RestartEndpoint restartEndpoint) {
return restartEndpoint.getResumeEndpoint();
}
}
@Configuration
@ConditionalOnEnabledEndpoint("refresh")
@ConditionalOnBean(PropertySourceBootstrapConfiguration.class)
protected static class RefreshEndpointConfiguration {
@Bean
@ConditionalOnEnabledEndpoint
@ConditionalOnMissingBean
public RefreshEndpoint refreshEndpoint(ContextRefresher contextRefresher) {
RefreshEndpoint endpoint = new RefreshEndpoint(contextRefresher);
return endpoint;
return new RefreshEndpoint(contextRefresher);
}
}
}
@Configuration
@ConditionalOnClass(IntegrationMBeanExporter.class)
class RestartEndpointWithIntegrationConfiguration {
@Autowired(required = false)
private IntegrationMBeanExporter exporter;
@Bean
@ConditionalOnEnabledEndpoint
@ConditionalOnMissingBean
public RestartEndpoint restartEndpoint() {
RestartEndpoint endpoint = new RestartEndpoint();
if (this.exporter != null) {
endpoint.setIntegrationMBeanExporter(this.exporter);
}
return endpoint;
}
}
@Configuration
@ConditionalOnMissingClass("org.springframework.integration.monitor.IntegrationMBeanExporter")
class RestartEndpointWithoutIntegrationConfiguration {
@Bean
@ConditionalOnEnabledEndpoint
@ConditionalOnMissingBean
public RestartEndpoint restartEndpointWithoutIntegration() {
return new RestartEndpoint();
}
}
@Configuration
class PauseResumeEndpointsConfiguration {
@Bean
@ConditionalOnBean(RestartEndpoint.class)
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public RestartEndpoint.PauseEndpoint pauseEndpoint(RestartEndpoint restartEndpoint) {
return restartEndpoint.getPauseEndpoint();
}
@Bean
@ConditionalOnBean(RestartEndpoint.class)
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public RestartEndpoint.ResumeEndpoint resumeEndpoint(
RestartEndpoint restartEndpoint) {
return restartEndpoint.getResumeEndpoint();
}
}

View File

@@ -17,13 +17,10 @@ package org.springframework.cloud.context.environment;
import java.util.Map;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.boot.endpoint.DeleteOperation;
import org.springframework.boot.endpoint.WriteOperation;
import org.springframework.boot.endpoint.web.WebEndpointExtension;
/**
* MVC endpoint for the {@link EnvironmentManager} providing a POST to /env as a simple
@@ -32,28 +29,24 @@ import org.springframework.web.bind.annotation.ResponseBody;
* @author Dave Syer
*
*/
public class EnvironmentManagerMvcEndpoint implements MvcEndpoint {
@WebEndpointExtension(endpoint = EnvironmentEndpoint.class)
public class EnvironmentWebEndpointExtension {
private EnvironmentManager environment;
private EnvironmentEndpoint delegate;
public EnvironmentManagerMvcEndpoint(EnvironmentEndpoint delegate,
EnvironmentManager enviroment) {
this.delegate = delegate;
public EnvironmentWebEndpointExtension(EnvironmentManager enviroment) {
environment = enviroment;
}
@RequestMapping(value = "", method = RequestMethod.POST)
@ResponseBody
public Object value(@RequestParam Map<String, String> params) {
@WriteOperation
public Object write(Map<String, String> params) {
for (String name : params.keySet()) {
environment.setProperty(name, params.get(name));
}
return params;
}
@RequestMapping(value = "reset", method = RequestMethod.POST)
@ResponseBody
@DeleteOperation
public Map<String, Object> reset() {
return environment.reset();
}
@@ -62,19 +55,4 @@ public class EnvironmentManagerMvcEndpoint implements MvcEndpoint {
this.environment = environment;
}
@Override
public String getPath() {
return "/" + this.delegate.getId();
}
@Override
public boolean isSensitive() {
return this.delegate.isSensitive();
}
@Override
@SuppressWarnings("rawtypes")
public Class<? extends Endpoint> getEndpointType() {
return this.delegate.getClass();
}
}

View File

@@ -19,38 +19,31 @@ package org.springframework.cloud.context.restart;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.WriteOperation;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.util.ClassUtils;
/**
* An endpoint that restarts the application context. Install as a bean and also register
* a {@link RestartListener} with the {@link SpringApplication} that starts the context.
* Those two components communicate via an {@link ApplicationEvent} and set up the state
* needed to restart the context.
* needed to doRestart the context.
*
* @author Dave Syer
*
*/
@ConfigurationProperties("endpoints.restart")
@ManagedResource
public class RestartEndpoint extends AbstractEndpoint<Boolean>
@Endpoint(id = "restart", enabledByDefault = false)
public class RestartEndpoint
implements ApplicationListener<ApplicationPreparedEvent> {
private static Log logger = LogFactory.getLog(RestartEndpoint.class);
public RestartEndpoint() {
super("restart", true, false);
}
private ConfigurableApplicationContext context;
private SpringApplication application;
@@ -63,7 +56,7 @@ public class RestartEndpoint extends AbstractEndpoint<Boolean>
private long timeout;
@ManagedAttribute
// @ManagedAttribute
public long getTimeout() {
return this.timeout;
}
@@ -88,19 +81,20 @@ public class RestartEndpoint extends AbstractEndpoint<Boolean>
}
}
@Override
public Boolean invoke() {
@WriteOperation
//FIXME: map with "message: Restarting" or couldn't restart
public Boolean restart() {
try {
restart();
doRestart();
logger.info("Restarted");
return true;
}
catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.info("Could not restart", e);
logger.info("Could not doRestart", e);
}
else {
logger.info("Could not restart: " + e.getMessage());
logger.info("Could not doRestart: " + e.getMessage());
}
return false;
}
@@ -114,42 +108,35 @@ public class RestartEndpoint extends AbstractEndpoint<Boolean>
return new ResumeEndpoint();
}
@ConfigurationProperties("endpoints.pause")
public class PauseEndpoint extends AbstractEndpoint<Boolean> {
@Endpoint(id = "pause")
public class PauseEndpoint {
public PauseEndpoint() {
super("pause", true);
}
@Override
public Boolean invoke() {
@WriteOperation
public Boolean pause() {
if (isRunning()) {
pause();
doPause();
return true;
}
return false;
}
}
@Endpoint(id = "resume")
@ConfigurationProperties("endpoints.resume")
public class ResumeEndpoint extends AbstractEndpoint<Boolean> {
public class ResumeEndpoint {
public ResumeEndpoint() {
super("resume", true);
}
@Override
public Boolean invoke() {
@WriteOperation
public Boolean resume() {
if (!isRunning()) {
resume();
doResume();
return true;
}
return false;
}
}
@ManagedOperation
public synchronized ConfigurableApplicationContext restart() {
// @ManagedOperation
public synchronized ConfigurableApplicationContext doRestart() {
if (this.context != null) {
if (this.integrationShutdown != null) {
this.integrationShutdown.stop(this.timeout);
@@ -164,7 +151,7 @@ public class RestartEndpoint extends AbstractEndpoint<Boolean>
return this.context;
}
@ManagedAttribute
// @ManagedAttribute
public boolean isRunning() {
if (this.context != null) {
return this.context.isRunning();
@@ -172,15 +159,15 @@ public class RestartEndpoint extends AbstractEndpoint<Boolean>
return false;
}
@ManagedOperation
public synchronized void pause() {
// @ManagedOperation
public synchronized void doPause() {
if (this.context != null) {
this.context.stop();
}
}
@ManagedOperation
public synchronized void resume() {
// @ManagedOperation
public synchronized void doResume() {
if (this.context != null) {
this.context.start();
}

View File

@@ -1,59 +0,0 @@
/*
* Copyright 2013-2014 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.cloud.context.restart;
import java.util.Collections;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* MVC endpoint to allow an application to be restarted on a POST (to /restart by
* default).
*
* @author Dave Syer
*
*/
public class RestartMvcEndpoint extends EndpointMvcAdapter {
public RestartMvcEndpoint(RestartEndpoint delegate) {
super(delegate);
}
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@Override
public Object invoke() {
if (!getDelegate().isEnabled()) {
return new ResponseEntity<>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
RestartMvcEndpoint.super.invoke();
}
});
thread.setDaemon(false);
thread.start();
return Collections.singletonMap("message", "Restarting");
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2013-2014 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.cloud.endpoint;
import java.util.Collections;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* A convenient base class for MVC endpoints that accept a POST (instead of the default
* GET).
*
* @author Dave Syer
*
*/
public class GenericPostableMvcEndpoint extends EndpointMvcAdapter {
public GenericPostableMvcEndpoint(Endpoint<?> delegate) {
super(delegate);
}
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
@Override
public Object invoke() {
if (!getDelegate().isEnabled()) {
return new ResponseEntity<>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
}
return super.invoke();
}
}

View File

@@ -16,40 +16,30 @@
package org.springframework.cloud.endpoint;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
/**
* @author Dave Syer
* @author Venil Noronha
*/
@ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false)
@ManagedResource
public class RefreshEndpoint extends AbstractEndpoint<Collection<String>> {
@Endpoint(id = "refresh")
public class RefreshEndpoint {
private ContextRefresher contextRefresher;
public RefreshEndpoint(ContextRefresher contextRefresher) {
super("refresh");
this.contextRefresher = contextRefresher;
}
@ManagedOperation
public String[] refresh() {
@ReadOperation
public Collection<String> refresh() {
Set<String> keys = contextRefresher.refresh();
return keys.toArray(new String[keys.size()]);
}
@Override
public Collection<String> invoke() {
return Arrays.asList(refresh());
return keys;
}
}