Update Actuator to use the new endpoint infrastructure
This commit migrates the Actuator onto the new endpoint infrastruture. In addition to the existing support for accessing the endpoints via JMX and HTTP using Spring MVC, support for access via HTTP using Jersey and WebFlux has been added. This includes using a separate management port where we now spin up an additional, appropriately configured servlet or reactive web server to expose the management context on a different HTTP port to the main application. Closes gh-2921 Closes gh-5389 Closes gh-9796
This commit is contained in:
parent
e92cb115e3
commit
ee16332745
|
|
@ -153,6 +153,16 @@
|
|||
<artifactId>flyway-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.containers</groupId>
|
||||
<artifactId>jersey-container-servlet-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
|
|
@ -183,6 +193,11 @@
|
|||
<artifactId>spring-jdbc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
|
|
@ -241,16 +256,6 @@
|
|||
<artifactId>spring-integration-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.hateoas</groupId>
|
||||
<artifactId>spring-hateoas</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
|
|
@ -261,11 +266,6 @@
|
|||
<artifactId>spring-security-config</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>hal-browser</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Annotation processing -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
|
@ -298,6 +298,10 @@
|
|||
<artifactId>json-path</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.ipc</groupId>
|
||||
<artifactId>reactor-netty</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-core</artifactId>
|
||||
|
|
@ -324,6 +328,16 @@
|
|||
<artifactId>hsqldb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.media</groupId>
|
||||
<artifactId>jersey-media-json-jackson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.ext</groupId>
|
||||
<artifactId>jersey-spring3</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.skyscreamer</groupId>
|
||||
<artifactId>jsonassert</artifactId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that matches based on the configuration of the management port.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Documented
|
||||
@Conditional(OnManagementPortCondition.class)
|
||||
public @interface ConditionalOnManagementPort {
|
||||
|
||||
/**
|
||||
* The {@link ManagementPortType} to match.
|
||||
* @return the port type
|
||||
*/
|
||||
ManagementPortType value();
|
||||
|
||||
}
|
||||
|
|
@ -14,21 +14,17 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.mvc;
|
||||
package org.springframework.boot.actuate.autoconfigure;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Callback for customizing the {@link EndpointHandlerMapping} at configuration time.
|
||||
* Configurtaion class used to enable configuration of a child management context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.2.0
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EndpointHandlerMappingCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the specified {@link EndpointHandlerMapping}.
|
||||
* @param mapping the {@link EndpointHandlerMapping} to customize
|
||||
*/
|
||||
void customize(EndpointHandlerMapping mapping);
|
||||
@Configuration
|
||||
@EnableManagementContext(ManagementContextType.CHILD)
|
||||
class EnableChildManagementContextConfiguration {
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.mvc;
|
||||
package org.springframework.boot.actuate.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
|
|
@ -22,31 +22,19 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Specialized {@link RequestMapping} for {@link RequestMethod#GET GET} requests that
|
||||
* produce {@code application/json} or
|
||||
* {@code application/vnd.spring-boot.actuator.v1+json} responses.
|
||||
* Enables the management context.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@RequestMapping(method = RequestMethod.GET, produces = {
|
||||
ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
@interface ActuatorGetMapping {
|
||||
@Import(ManagementContextConfigurationImportSelector.class)
|
||||
@interface EnableManagementContext {
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#value}.
|
||||
* @return the value
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] value() default {};
|
||||
ManagementContextType value();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for the management context. If the
|
||||
* {@code management.port} is the same as the {@code server.port} the management context
|
||||
* will be the same as the main application context. If the {@code management.port} is
|
||||
* different to the {@code server.port} the management context will be a separate context
|
||||
* that has the main application context as its parent.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
public class ManagementContextAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
static class ServletManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public ManagementServletContext managementServletContext(
|
||||
final ManagementServerProperties properties) {
|
||||
return () -> properties.getContextPath();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnManagementPort(ManagementPortType.SAME)
|
||||
static class SameManagementContextConfiguration
|
||||
implements SmartInitializingSingleton {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
SameManagementContextConfiguration(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
veriifySslConfiguration();
|
||||
verifyContextPathConfiguration();
|
||||
if (this.environment instanceof ConfigurableEnvironment) {
|
||||
addLocalManagementPortPropertyAlias(
|
||||
(ConfigurableEnvironment) this.environment);
|
||||
}
|
||||
}
|
||||
|
||||
private void veriifySslConfiguration() {
|
||||
if (this.environment.getProperty("management.ssl.enabled", Boolean.class,
|
||||
false)) {
|
||||
throw new IllegalStateException(
|
||||
"Management-specific SSL cannot be configured as the management "
|
||||
+ "server is not listening on a separate port");
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyContextPathConfiguration() {
|
||||
String contextPath = this.environment.getProperty("management.context-path");
|
||||
if ("".equals(contextPath) || "/".equals(contextPath)) {
|
||||
throw new IllegalStateException("A management context path of '"
|
||||
+ contextPath + "' requires the management server to be "
|
||||
+ "listening on a separate port");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias for 'local.management.port' that actually resolves using
|
||||
* 'local.server.port'.
|
||||
* @param environment the environment
|
||||
*/
|
||||
private void addLocalManagementPortPropertyAlias(
|
||||
ConfigurableEnvironment environment) {
|
||||
environment.getPropertySources()
|
||||
.addLast(new PropertySource<Object>("Management Server") {
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
if ("local.management.port".equals(name)) {
|
||||
return environment.getProperty("local.server.port");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EnableManagementContext(ManagementContextType.SAME)
|
||||
static class EnableSameManagementContextConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
|
||||
static class SeparateManagementContextConfiguration
|
||||
implements SmartInitializingSingleton {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final ManagementContextFactory managementContextFactory;
|
||||
|
||||
SeparateManagementContextConfiguration(ApplicationContext applicationContext,
|
||||
ManagementContextFactory managementContextFactory) {
|
||||
this.applicationContext = applicationContext;
|
||||
this.managementContextFactory = managementContextFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
ConfigurableApplicationContext managementContext = this.managementContextFactory
|
||||
.createManagementContext(this.applicationContext,
|
||||
EnableChildManagementContextConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
setNamespaceIfPossible(managementContext);
|
||||
managementContext.setId(this.applicationContext.getId() + ":management");
|
||||
setClassLoaderIfPossible(managementContext);
|
||||
CloseManagementContextListener.addIfPossible(this.applicationContext,
|
||||
managementContext);
|
||||
managementContext.refresh();
|
||||
}
|
||||
|
||||
private void setClassLoaderIfPossible(ConfigurableApplicationContext child) {
|
||||
if (child instanceof DefaultResourceLoader) {
|
||||
((AbstractApplicationContext) child)
|
||||
.setClassLoader(this.applicationContext.getClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
private void setNamespaceIfPossible(ConfigurableApplicationContext child) {
|
||||
if (child instanceof ConfigurableReactiveWebApplicationContext) {
|
||||
((ConfigurableReactiveWebApplicationContext) child)
|
||||
.setNamespace("management");
|
||||
}
|
||||
else if (child instanceof ConfigurableWebApplicationContext) {
|
||||
((ConfigurableWebApplicationContext) child).setNamespace("management");
|
||||
}
|
||||
}
|
||||
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
static class ServletChildContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServletWebManagementContextFactory servletWebChildContextFactory() {
|
||||
return new ServletWebManagementContextFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
static class ReactiveChildContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public ReactiveWebManagementContextFactory reactiveWebChildContextFactory() {
|
||||
return new ReactiveWebManagementContextFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ApplicationListener} to propagate the {@link ContextClosedEvent} and
|
||||
* {@link ApplicationFailedEvent} from a parent to a child.
|
||||
*/
|
||||
private static class CloseManagementContextListener
|
||||
implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private final ApplicationContext parentContext;
|
||||
|
||||
private final ConfigurableApplicationContext childContext;
|
||||
|
||||
CloseManagementContextListener(ApplicationContext parentContext,
|
||||
ConfigurableApplicationContext childContext) {
|
||||
this.parentContext = parentContext;
|
||||
this.childContext = childContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextClosedEvent) {
|
||||
onContextClosedEvent((ContextClosedEvent) event);
|
||||
}
|
||||
if (event instanceof ApplicationFailedEvent) {
|
||||
onApplicationFailedEvent((ApplicationFailedEvent) event);
|
||||
}
|
||||
};
|
||||
|
||||
private void onContextClosedEvent(ContextClosedEvent event) {
|
||||
propagateCloseIfNecessary(event.getApplicationContext());
|
||||
}
|
||||
|
||||
private void onApplicationFailedEvent(ApplicationFailedEvent event) {
|
||||
propagateCloseIfNecessary(event.getApplicationContext());
|
||||
}
|
||||
|
||||
private void propagateCloseIfNecessary(ApplicationContext applicationContext) {
|
||||
if (applicationContext == this.parentContext) {
|
||||
this.childContext.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void addIfPossible(ApplicationContext parentContext,
|
||||
ConfigurableApplicationContext childContext) {
|
||||
if (parentContext instanceof ConfigurableApplicationContext) {
|
||||
add((ConfigurableApplicationContext) parentContext, childContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static void add(ConfigurableApplicationContext parentContext,
|
||||
ConfigurableApplicationContext childContext) {
|
||||
parentContext.addApplicationListener(
|
||||
new CloseManagementContextListener(parentContext, childContext));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -46,4 +46,12 @@ import org.springframework.core.annotation.Order;
|
|||
@Configuration
|
||||
public @interface ManagementContextConfiguration {
|
||||
|
||||
/**
|
||||
* Specifies the type of management context that is required for this configuration to
|
||||
* be applied.
|
||||
* @return the required management context type
|
||||
* @since 2.0.0
|
||||
*/
|
||||
ManagementContextType value() default ManagementContextType.ANY;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint;
|
||||
package org.springframework.boot.actuate.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -22,7 +22,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.context.annotation.DeferredImportSelector;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
|
|
@ -44,19 +43,25 @@ import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
|||
* @see ManagementContextConfiguration
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
class ManagementContextConfigurationsImportSelector
|
||||
class ManagementContextConfigurationImportSelector
|
||||
implements DeferredImportSelector, BeanClassLoaderAware {
|
||||
|
||||
private ClassLoader classLoader;
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata metadata) {
|
||||
ManagementContextType contextType = (ManagementContextType) metadata
|
||||
.getAnnotationAttributes(EnableManagementContext.class.getName())
|
||||
.get("value");
|
||||
// Find all management context configuration classes, filtering duplicates
|
||||
List<ManagementConfiguration> configurations = getConfigurations();
|
||||
OrderComparator.sort(configurations);
|
||||
List<String> names = new ArrayList<>();
|
||||
for (ManagementConfiguration configuration : configurations) {
|
||||
names.add(configuration.getClassName());
|
||||
if (configuration.getContextType() == ManagementContextType.ANY
|
||||
|| configuration.getContextType() == contextType) {
|
||||
names.add(configuration.getClassName());
|
||||
}
|
||||
}
|
||||
return names.toArray(new String[names.size()]);
|
||||
}
|
||||
|
|
@ -102,11 +107,23 @@ class ManagementContextConfigurationsImportSelector
|
|||
|
||||
private final int order;
|
||||
|
||||
private final ManagementContextType contextType;
|
||||
|
||||
ManagementConfiguration(MetadataReader metadataReader) {
|
||||
AnnotationMetadata annotationMetadata = metadataReader
|
||||
.getAnnotationMetadata();
|
||||
this.order = readOrder(annotationMetadata);
|
||||
this.className = metadataReader.getClassMetadata().getClassName();
|
||||
this.contextType = readContextType(annotationMetadata);
|
||||
}
|
||||
|
||||
private ManagementContextType readContextType(
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Map<String, Object> annotationAttributes = annotationMetadata
|
||||
.getAnnotationAttributes(
|
||||
ManagementContextConfiguration.class.getName());
|
||||
return annotationAttributes == null ? ManagementContextType.ANY
|
||||
: (ManagementContextType) annotationAttributes.get("value");
|
||||
}
|
||||
|
||||
private int readOrder(AnnotationMetadata annotationMetadata) {
|
||||
|
|
@ -126,6 +143,10 @@ class ManagementContextConfigurationsImportSelector
|
|||
return this.order;
|
||||
}
|
||||
|
||||
public ManagementContextType getContextType() {
|
||||
return this.contextType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* A factory for creating a separate management context when the management web server is
|
||||
* running on a different port to the main application.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
interface ManagementContextFactory {
|
||||
|
||||
ConfigurableApplicationContext createManagementContext(ApplicationContext parent,
|
||||
Class<?>... configClasses);
|
||||
|
||||
}
|
||||
|
|
@ -14,32 +14,31 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.mvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
package org.springframework.boot.actuate.autoconfigure;
|
||||
|
||||
/**
|
||||
* Adapter class to expose {@link Endpoint}s as {@link MvcEndpoint}s.
|
||||
* Enumeration of management context types.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class EndpointMvcAdapter extends AbstractEndpointMvcAdapter<Endpoint<?>> {
|
||||
public enum ManagementContextType {
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointMvcAdapter}.
|
||||
* @param delegate the underlying {@link Endpoint} to adapt.
|
||||
* The management context is the same as the main application context.
|
||||
*/
|
||||
public EndpointMvcAdapter(Endpoint<?> delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
SAME,
|
||||
|
||||
@Override
|
||||
@ActuatorGetMapping
|
||||
@ResponseBody
|
||||
public Object invoke() {
|
||||
return super.invoke();
|
||||
}
|
||||
/**
|
||||
* The management context is a separate context that is a child of the main
|
||||
* application context.
|
||||
*/
|
||||
CHILD,
|
||||
|
||||
/**
|
||||
* The management context can be either the same as the main application context or a
|
||||
* child of the main application context.
|
||||
*/
|
||||
ANY
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
public enum ManagementPortType {
|
||||
|
||||
/**
|
||||
* The management port has been disabled.
|
||||
*/
|
||||
DISABLED,
|
||||
|
||||
/**
|
||||
* The management port is the same as the server port.
|
||||
*/
|
||||
SAME,
|
||||
|
||||
/**
|
||||
* The management port and server port are different.
|
||||
*/
|
||||
DIFFERENT;
|
||||
|
||||
static ManagementPortType get(Environment environment) {
|
||||
Integer serverPort = getPortProperty(environment, "server.");
|
||||
Integer managementPort = getPortProperty(environment, "management.");
|
||||
if (managementPort != null && managementPort < 0) {
|
||||
return DISABLED;
|
||||
}
|
||||
return ((managementPort == null)
|
||||
|| (serverPort == null && managementPort.equals(8080))
|
||||
|| (managementPort != 0 && managementPort.equals(serverPort)) ? SAME
|
||||
: DIFFERENT);
|
||||
}
|
||||
|
||||
private static Integer getPortProperty(Environment environment, String prefix) {
|
||||
return environment.getProperty(prefix + "port", Integer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebApplicationContext;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
/**
|
||||
* {@link SpringBootCondition} that matches when the management server is running on a
|
||||
* different port.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class OnManagementPortCondition extends SpringBootCondition {
|
||||
|
||||
private static final String CLASS_NAME_WEB_APPLICATION_CONTEXT = "org.springframework.web.context.WebApplicationContext";
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("Management Port");
|
||||
if (!isWebApplicationContext(context)) {
|
||||
return ConditionOutcome
|
||||
.noMatch(message.because("non web application context"));
|
||||
}
|
||||
Map<String, Object> annotationAttributes = metadata
|
||||
.getAnnotationAttributes(ConditionalOnManagementPort.class.getName());
|
||||
ManagementPortType requiredType = (ManagementPortType) annotationAttributes
|
||||
.get("value");
|
||||
ManagementPortType actualType = ManagementPortType.get(context.getEnvironment());
|
||||
if (actualType == requiredType) {
|
||||
return ConditionOutcome.match(message.because(
|
||||
"actual port type (" + actualType + ") matched required type"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.because("actual port type (" + actualType
|
||||
+ ") did not match required type (" + requiredType + ")"));
|
||||
}
|
||||
|
||||
private boolean isWebApplicationContext(ConditionContext context) {
|
||||
ResourceLoader resourceLoader = context.getResourceLoader();
|
||||
if (resourceLoader instanceof ConfigurableReactiveWebApplicationContext) {
|
||||
return true;
|
||||
}
|
||||
if (!ClassUtils.isPresent(CLASS_NAME_WEB_APPLICATION_CONTEXT,
|
||||
context.getClassLoader())) {
|
||||
return false;
|
||||
}
|
||||
return WebApplicationContext.class.isInstance(resourceLoader);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerAutoConfiguration;
|
||||
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
|
||||
import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
|
||||
import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link ManagementContextFactory} for WebFlux-based web applications.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ReactiveWebManagementContextFactory implements ManagementContextFactory {
|
||||
|
||||
@Override
|
||||
public ConfigurableApplicationContext createManagementContext(
|
||||
ApplicationContext parent, Class<?>... configClasses) {
|
||||
ReactiveWebServerApplicationContext child = new ReactiveWebServerApplicationContext();
|
||||
child.setParent(parent);
|
||||
child.register(configClasses);
|
||||
child.register(ReactiveWebServerAutoConfiguration.class);
|
||||
registerReactiveWebServerFactory(parent, child);
|
||||
return child;
|
||||
}
|
||||
|
||||
private void registerReactiveWebServerFactory(ApplicationContext parent,
|
||||
GenericReactiveWebApplicationContext childContext) {
|
||||
try {
|
||||
ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
registry.registerBeanDefinition("ReactiveWebServerFactory",
|
||||
new RootBeanDefinition(
|
||||
determineReactiveWebServerFactoryClass(parent)));
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Ignore and assume auto-configuration
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> determineReactiveWebServerFactoryClass(ApplicationContext parent)
|
||||
throws NoSuchBeanDefinitionException {
|
||||
Class<?> factoryClass = parent.getBean(ReactiveWebServerFactory.class).getClass();
|
||||
if (cannotBeInstantiated(factoryClass)) {
|
||||
throw new FatalBeanException("ReactiveWebServerFactory implementation "
|
||||
+ factoryClass.getName() + " cannot be instantiated. "
|
||||
+ "To allow a separate management port to be used, a top-level class "
|
||||
+ "or static inner class should be used instead");
|
||||
}
|
||||
return factoryClass;
|
||||
}
|
||||
|
||||
private boolean cannotBeInstantiated(Class<?> clazz) {
|
||||
return clazz.isLocalClass()
|
||||
|| (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers()))
|
||||
|| clazz.isAnonymousClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* A {@link ManagementContextFactory} for servlet-based web applications.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ServletWebManagementContextFactory implements ManagementContextFactory {
|
||||
|
||||
@Override
|
||||
public ConfigurableApplicationContext createManagementContext(
|
||||
ApplicationContext parent, Class<?>... configClasses) {
|
||||
AnnotationConfigServletWebServerApplicationContext child = new AnnotationConfigServletWebServerApplicationContext();
|
||||
child.setParent(parent);
|
||||
List<Class<?>> combinedClasses = new ArrayList<Class<?>>(
|
||||
Arrays.asList(configClasses));
|
||||
combinedClasses.add(ServletWebServerFactoryAutoConfiguration.class);
|
||||
child.register(combinedClasses.toArray(new Class<?>[combinedClasses.size()]));
|
||||
registerServletWebServerFactory(parent, child);
|
||||
return child;
|
||||
}
|
||||
|
||||
private void registerServletWebServerFactory(ApplicationContext parent,
|
||||
AnnotationConfigServletWebServerApplicationContext childContext) {
|
||||
try {
|
||||
ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
registry.registerBeanDefinition("ServletWebServerFactory",
|
||||
new RootBeanDefinition(
|
||||
determineServletWebServerFactoryClass(parent)));
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Ignore and assume auto-configuration
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> determineServletWebServerFactoryClass(ApplicationContext parent)
|
||||
throws NoSuchBeanDefinitionException {
|
||||
Class<?> factoryClass = parent.getBean(ServletWebServerFactory.class).getClass();
|
||||
if (cannotBeInstantiated(factoryClass)) {
|
||||
throw new FatalBeanException("ServletWebServerFactory implementation "
|
||||
+ factoryClass.getName() + " cannot be instantiated. "
|
||||
+ "To allow a separate management port to be used, a top-level class "
|
||||
+ "or static inner class should be used instead");
|
||||
}
|
||||
return factoryClass;
|
||||
}
|
||||
|
||||
private boolean cannotBeInstantiated(Class<?> clazz) {
|
||||
return clazz.isLocalClass()
|
||||
|| (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers()))
|
||||
|| clazz.isAnonymousClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,34 +16,18 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.cloudfoundry;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointWebMvcAutoConfiguration;
|
||||
import org.springframework.boot.actuate.cloudfoundry.CloudFoundryEndpointHandlerMapping;
|
||||
import org.springframework.boot.actuate.cloudfoundry.CloudFoundrySecurityInterceptor;
|
||||
import org.springframework.boot.actuate.cloudfoundry.CloudFoundrySecurityService;
|
||||
import org.springframework.boot.actuate.cloudfoundry.TokenValidator;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure.ServletEndpointAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.IgnoredRequestCustomizer;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} to expose actuator endpoints for
|
||||
|
|
@ -54,57 +38,10 @@ import org.springframework.web.servlet.HandlerInterceptor;
|
|||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "management.cloudfoundry", name = "enabled", matchIfMissing = true)
|
||||
@ConditionalOnBean(MvcEndpoints.class)
|
||||
@AutoConfigureAfter(EndpointWebMvcAutoConfiguration.class)
|
||||
@AutoConfigureAfter(ServletEndpointAutoConfiguration.class)
|
||||
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
|
||||
public class CloudFoundryActuatorAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public CloudFoundryEndpointHandlerMapping cloudFoundryEndpointHandlerMapping(
|
||||
MvcEndpoints mvcEndpoints, RestTemplateBuilder restTemplateBuilder,
|
||||
Environment environment) {
|
||||
Set<NamedMvcEndpoint> endpoints = new LinkedHashSet<>(
|
||||
mvcEndpoints.getEndpoints(NamedMvcEndpoint.class));
|
||||
HandlerInterceptor securityInterceptor = getSecurityInterceptor(
|
||||
restTemplateBuilder, environment);
|
||||
CorsConfiguration corsConfiguration = getCorsConfiguration();
|
||||
CloudFoundryEndpointHandlerMapping mapping = new CloudFoundryEndpointHandlerMapping(
|
||||
endpoints, corsConfiguration, securityInterceptor);
|
||||
mapping.setPrefix("/cloudfoundryapplication");
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private HandlerInterceptor getSecurityInterceptor(
|
||||
RestTemplateBuilder restTemplateBuilder, Environment environment) {
|
||||
CloudFoundrySecurityService cloudfoundrySecurityService = getCloudFoundrySecurityService(
|
||||
restTemplateBuilder, environment);
|
||||
TokenValidator tokenValidator = new TokenValidator(cloudfoundrySecurityService);
|
||||
HandlerInterceptor securityInterceptor = new CloudFoundrySecurityInterceptor(
|
||||
tokenValidator, cloudfoundrySecurityService,
|
||||
environment.getProperty("vcap.application.application_id"));
|
||||
return securityInterceptor;
|
||||
}
|
||||
|
||||
private CloudFoundrySecurityService getCloudFoundrySecurityService(
|
||||
RestTemplateBuilder restTemplateBuilder, Environment environment) {
|
||||
String cloudControllerUrl = environment.getProperty("vcap.application.cf_api");
|
||||
boolean skipSslValidation = environment.getProperty(
|
||||
"management.cloudfoundry.skip-ssl-validation", Boolean.class, false);
|
||||
return cloudControllerUrl == null ? null
|
||||
: new CloudFoundrySecurityService(restTemplateBuilder, cloudControllerUrl,
|
||||
skipSslValidation);
|
||||
}
|
||||
|
||||
private CorsConfiguration getCorsConfiguration() {
|
||||
CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
corsConfiguration.addAllowedOrigin(CorsConfiguration.ALL);
|
||||
corsConfiguration.setAllowedMethods(
|
||||
Arrays.asList(HttpMethod.GET.name(), HttpMethod.POST.name()));
|
||||
corsConfiguration.setAllowedHeaders(
|
||||
Arrays.asList("Authorization", "X-Cf-App-Instance", "Content-Type"));
|
||||
return corsConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nested configuration for ignored requests if Spring Security is present.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.EndpointType;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that checks whether an endpoint is enabled or not. Matches
|
||||
* according to the {@link Endpoint#enabledByDefault() enabledByDefault flag} and the
|
||||
* specific {@link Endpoint#types() tech} that the endpoint may be restricted to.
|
||||
* <p>
|
||||
* If no specific {@code endpoints.<id>.*} or {@code endpoints.all.*} properties are
|
||||
* defined, the condition matches the {@code enabledByDefault} value regardless of the
|
||||
* specific {@link EndpointType}, if any. If any property are set, they are evaluated
|
||||
* with a sensible order of precedence.
|
||||
* <p>
|
||||
* For instance if {@code endpoints.all.enabled} is {@code false} but
|
||||
* {@code endpoints.<id>.enabled} is {@code true}, the condition will match.
|
||||
* <p>
|
||||
* This condition must be placed on a {@code @Bean} method producing an endpoint as its
|
||||
* id and other attributes are inferred from the {@link Endpoint} annotation set on the
|
||||
* return type of the factory method. Consider the following valid example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyAutoConfiguration {
|
||||
*
|
||||
* @ConditionalOnEnabledEndpoint
|
||||
* @Bean
|
||||
* public MyEndpoint myEndpoint() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* @Endpoint(id = "my", enabledByDefault = false)
|
||||
* static class MyEndpoint { ... }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
*
|
||||
* In the sample above the condition will be evaluated with the attributes specified on
|
||||
* {@code MyEndpoint}. In particular, in the absence of any property in the environment,
|
||||
* the condition will not match as this endpoint is disabled by default.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
* @see Endpoint
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Documented
|
||||
@Conditional(OnEnabledEndpointCondition.class)
|
||||
public @interface ConditionalOnEnabledEndpoint {
|
||||
|
||||
}
|
||||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
|
@ -26,11 +24,11 @@ import liquibase.integration.spring.SpringLiquibase;
|
|||
import org.flywaydb.core.Flyway;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.BeansEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.DumpEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointProperties;
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.FlywayEndpoint;
|
||||
|
|
@ -42,6 +40,7 @@ import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
|
|||
import org.springframework.boot.actuate.endpoint.PublicMetrics;
|
||||
import org.springframework.boot.actuate.endpoint.RequestMappingEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ThreadDumpEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
|
|
@ -59,10 +58,12 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
|||
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
|
||||
|
||||
/**
|
||||
|
|
@ -78,114 +79,112 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
|
|||
* @author Meang Akira Tanaka
|
||||
* @author Ben Hale
|
||||
* @since 2.0.0
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(EndpointProperties.class)
|
||||
public class EndpointAutoConfiguration {
|
||||
|
||||
private final HealthAggregator healthAggregator;
|
||||
|
||||
private final Map<String, HealthIndicator> healthIndicators;
|
||||
|
||||
private final List<InfoContributor> infoContributors;
|
||||
|
||||
private final Collection<PublicMetrics> publicMetrics;
|
||||
|
||||
private final TraceRepository traceRepository;
|
||||
|
||||
public EndpointAutoConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
|
||||
ObjectProvider<Map<String, HealthIndicator>> healthIndicators,
|
||||
ObjectProvider<List<InfoContributor>> infoContributors,
|
||||
ObjectProvider<Collection<PublicMetrics>> publicMetrics,
|
||||
ObjectProvider<TraceRepository> traceRepository) {
|
||||
this.healthAggregator = healthAggregator.getIfAvailable();
|
||||
this.healthIndicators = healthIndicators.getIfAvailable();
|
||||
this.infoContributors = infoContributors.getIfAvailable();
|
||||
this.publicMetrics = publicMetrics.getIfAvailable();
|
||||
this.traceRepository = traceRepository.getIfAvailable();
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public EnvironmentEndpoint environmentEndpoint(Environment environment) {
|
||||
return new EnvironmentEndpoint(environment);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EnvironmentEndpoint environmentEndpoint() {
|
||||
return new EnvironmentEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HealthEndpoint healthEndpoint() {
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public HealthEndpoint healthEndpoint(
|
||||
ObjectProvider<HealthAggregator> healthAggregator,
|
||||
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
|
||||
return new HealthEndpoint(
|
||||
this.healthAggregator == null ? new OrderedHealthAggregator()
|
||||
: this.healthAggregator,
|
||||
this.healthIndicators == null
|
||||
? Collections.<String, HealthIndicator>emptyMap()
|
||||
: this.healthIndicators);
|
||||
healthAggregator.getIfAvailable(() -> new OrderedHealthAggregator()),
|
||||
healthIndicators.getIfAvailable(Collections::emptyMap));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public BeansEndpoint beansEndpoint() {
|
||||
return new BeansEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public InfoEndpoint infoEndpoint() throws Exception {
|
||||
return new InfoEndpoint(this.infoContributors == null
|
||||
? Collections.<InfoContributor>emptyList() : this.infoContributors);
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public InfoEndpoint infoEndpoint(
|
||||
ObjectProvider<List<InfoContributor>> infoContributors) {
|
||||
return new InfoEndpoint(infoContributors.getIfAvailable(Collections::emptyList));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(LoggingSystem.class)
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public LoggersEndpoint loggersEndpoint(LoggingSystem loggingSystem) {
|
||||
return new LoggersEndpoint(loggingSystem);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MetricsEndpoint metricsEndpoint() {
|
||||
List<PublicMetrics> publicMetrics = new ArrayList<>();
|
||||
if (this.publicMetrics != null) {
|
||||
publicMetrics.addAll(this.publicMetrics);
|
||||
}
|
||||
publicMetrics.sort(AnnotationAwareOrderComparator.INSTANCE);
|
||||
return new MetricsEndpoint(publicMetrics);
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public MetricsEndpoint metricsEndpoint(
|
||||
ObjectProvider<List<PublicMetrics>> publicMetrics) {
|
||||
List<PublicMetrics> sortedPublicMetrics = publicMetrics
|
||||
.getIfAvailable(Collections::emptyList);
|
||||
Collections.sort(sortedPublicMetrics, AnnotationAwareOrderComparator.INSTANCE);
|
||||
return new MetricsEndpoint(sortedPublicMetrics);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TraceEndpoint traceEndpoint() {
|
||||
return new TraceEndpoint(this.traceRepository == null
|
||||
? new InMemoryTraceRepository() : this.traceRepository);
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public TraceEndpoint traceEndpoint(ObjectProvider<TraceRepository> traceRepository) {
|
||||
return new TraceEndpoint(
|
||||
traceRepository.getIfAvailable(() -> new InMemoryTraceRepository()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DumpEndpoint dumpEndpoint() {
|
||||
return new DumpEndpoint();
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public ThreadDumpEndpoint dumpEndpoint() {
|
||||
return new ThreadDumpEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ConditionEvaluationReport.class)
|
||||
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
|
||||
public AutoConfigurationReportEndpoint autoConfigurationReportEndpoint() {
|
||||
return new AutoConfigurationReportEndpoint();
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public AutoConfigurationReportEndpoint autoConfigurationReportEndpoint(
|
||||
ConditionEvaluationReport conditionEvaluationReport) {
|
||||
return new AutoConfigurationReportEndpoint(conditionEvaluationReport);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public ShutdownEndpoint shutdownEndpoint() {
|
||||
return new ShutdownEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public ConfigurationPropertiesReportEndpoint configurationPropertiesReportEndpoint() {
|
||||
return new ConfigurationPropertiesReportEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(AuditEventRepository.class)
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public AuditEventsEndpoint auditEventsEndpoint(
|
||||
AuditEventRepository auditEventRepository) {
|
||||
return new AuditEventsEndpoint(auditEventRepository);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(Flyway.class)
|
||||
@ConditionalOnClass(Flyway.class)
|
||||
|
|
@ -193,6 +192,7 @@ public class EndpointAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public FlywayEndpoint flywayEndpoint(Map<String, Flyway> flyways) {
|
||||
return new FlywayEndpoint(flyways);
|
||||
}
|
||||
|
|
@ -206,6 +206,7 @@ public class EndpointAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public LiquibaseEndpoint liquibaseEndpoint(
|
||||
Map<String, SpringLiquibase> liquibases) {
|
||||
return new LiquibaseEndpoint(liquibases);
|
||||
|
|
@ -219,6 +220,7 @@ public class EndpointAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public RequestMappingEndpoint requestMappingEndpoint() {
|
||||
RequestMappingEndpoint endpoint = new RequestMappingEndpoint();
|
||||
return endpoint;
|
||||
|
|
|
|||
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointMBeanExportAutoConfiguration.JmxEnabledCondition;
|
||||
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.AuditEventsJmxEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} to enable JMX export for
|
||||
* {@link Endpoint}s.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@Conditional(JmxEnabledCondition.class)
|
||||
@AutoConfigureAfter({ EndpointAutoConfiguration.class, JmxAutoConfiguration.class })
|
||||
@EnableConfigurationProperties(EndpointMBeanExportProperties.class)
|
||||
public class EndpointMBeanExportAutoConfiguration {
|
||||
|
||||
private final EndpointMBeanExportProperties properties;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
public EndpointMBeanExportAutoConfiguration(EndpointMBeanExportProperties properties,
|
||||
ObjectProvider<ObjectMapper> objectMapper) {
|
||||
this.properties = properties;
|
||||
this.objectMapper = objectMapper.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EndpointMBeanExporter endpointMBeanExporter(MBeanServer server) {
|
||||
EndpointMBeanExporter mbeanExporter = new EndpointMBeanExporter(
|
||||
this.objectMapper);
|
||||
String domain = this.properties.getDomain();
|
||||
if (StringUtils.hasText(domain)) {
|
||||
mbeanExporter.setDomain(domain);
|
||||
}
|
||||
mbeanExporter.setServer(server);
|
||||
mbeanExporter.setEnsureUniqueRuntimeObjectNames(this.properties.isUniqueNames());
|
||||
mbeanExporter.setObjectNameStaticProperties(this.properties.getStaticNames());
|
||||
return mbeanExporter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(MBeanServer.class)
|
||||
public MBeanServer mbeanServer() {
|
||||
return new JmxAutoConfiguration().mbeanServer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(AuditEventRepository.class)
|
||||
@ConditionalOnEnabledEndpoint("auditevents")
|
||||
public AuditEventsJmxEndpoint auditEventsEndpoint(
|
||||
AuditEventRepository auditEventRepository) {
|
||||
return new AuditEventsJmxEndpoint(this.objectMapper, auditEventRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Condition to check that spring.jmx and endpoints.jmx are enabled.
|
||||
*/
|
||||
static class JmxEnabledCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
boolean jmxEnabled = context.getEnvironment()
|
||||
.getProperty("spring.jmx.enabled", Boolean.class, true);
|
||||
boolean jmxEndpointsEnabled = context.getEnvironment()
|
||||
.getProperty("endpoints.jmx.enabled", Boolean.class, true);
|
||||
if (jmxEnabled && jmxEndpointsEnabled) {
|
||||
return ConditionOutcome.match(
|
||||
ConditionMessage.forCondition("JMX Enabled").found("properties")
|
||||
.items("spring.jmx.enabled", "endpoints.jmx.enabled"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(ConditionMessage.forCondition("JMX Enabled")
|
||||
.because("spring.jmx.enabled or endpoints.jmx.enabled is not set"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration properties for JMX endpoints.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.jmx")
|
||||
public class EndpointMBeanExportProperties {
|
||||
|
||||
/**
|
||||
* JMX domain name. Initialized with the value of 'spring.jmx.default-domain' if set.
|
||||
*/
|
||||
@Value("${spring.jmx.default-domain:}")
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* Ensure that ObjectNames are modified in case of conflict.
|
||||
*/
|
||||
private boolean uniqueNames = false;
|
||||
|
||||
/**
|
||||
* Enable the JMX endpoints.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* Additional static properties to append to all ObjectNames of MBeans representing
|
||||
* Endpoints.
|
||||
*/
|
||||
private Properties staticNames = new Properties();
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return this.domain;
|
||||
}
|
||||
|
||||
public void setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
public boolean isUniqueNames() {
|
||||
return this.uniqueNames;
|
||||
}
|
||||
|
||||
public void setUniqueNames(boolean uniqueNames) {
|
||||
this.uniqueNames = uniqueNames;
|
||||
}
|
||||
|
||||
public Properties getStaticNames() {
|
||||
return this.staticNames;
|
||||
}
|
||||
|
||||
public void setStaticNames(String[] staticNames) {
|
||||
this.staticNames = StringUtils.splitArrayElementsIntoProperties(staticNames, "=");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,363 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
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.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
|
||||
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
|
||||
import org.springframework.boot.web.servlet.filter.ApplicationContextHeaderFilter;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} to enable Spring MVC to handle
|
||||
* {@link Endpoint} requests. If the {@link ManagementServerProperties} specifies a
|
||||
* different port to {@link ServerProperties} a new child context is created, otherwise it
|
||||
* is assumed that endpoint requests will be mapped and handled via an already registered
|
||||
* {@link DispatcherServlet}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Johannes Edmeier
|
||||
* @author Eddú Meléndez
|
||||
* @author Venil Noronha
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(ManagementServerProperties.class)
|
||||
@AutoConfigureAfter({ EndpointAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
ServletWebServerFactoryAutoConfiguration.class, WebMvcAutoConfiguration.class,
|
||||
RepositoryRestMvcAutoConfiguration.class, HypermediaAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class })
|
||||
public class EndpointWebMvcAutoConfiguration
|
||||
implements ApplicationContextAware, SmartInitializingSingleton {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(EndpointWebMvcAutoConfiguration.class);
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ManagementContextResolver managementContextResolver() {
|
||||
return new ManagementContextResolver(this.applicationContext);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ManagementServletContext managementServletContext(
|
||||
final ManagementServerProperties properties) {
|
||||
return properties::getContextPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
ManagementServerPort managementPort = ManagementServerPort.DIFFERENT;
|
||||
Environment environment = this.applicationContext.getEnvironment();
|
||||
if (this.applicationContext instanceof WebApplicationContext) {
|
||||
managementPort = ManagementServerPort.get(environment);
|
||||
}
|
||||
if (managementPort == ManagementServerPort.DIFFERENT) {
|
||||
if (this.applicationContext instanceof ServletWebServerApplicationContext
|
||||
&& ((ServletWebServerApplicationContext) this.applicationContext)
|
||||
.getWebServer() != null) {
|
||||
createChildManagementContext();
|
||||
}
|
||||
else {
|
||||
logger.warn("Could not start management web server on "
|
||||
+ "different port (management endpoints are still available "
|
||||
+ "through JMX)");
|
||||
}
|
||||
}
|
||||
if (managementPort == ManagementServerPort.SAME) {
|
||||
String contextPath = environment.getProperty("management.context-path");
|
||||
if ("".equals(contextPath) || "/".equals(contextPath)) {
|
||||
throw new IllegalStateException("A management context path of '"
|
||||
+ contextPath + "' requires the management server to be "
|
||||
+ "listening on a separate port");
|
||||
}
|
||||
if (environment.getProperty("management.ssl.enabled", Boolean.class, false)) {
|
||||
throw new IllegalStateException(
|
||||
"Management-specific SSL cannot be configured as the management "
|
||||
+ "server is not listening on a separate port");
|
||||
}
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
addLocalManagementPortPropertyAlias(
|
||||
(ConfigurableEnvironment) environment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createChildManagementContext() {
|
||||
AnnotationConfigServletWebServerApplicationContext childContext = new AnnotationConfigServletWebServerApplicationContext();
|
||||
childContext.setParent(this.applicationContext);
|
||||
childContext.setNamespace("management");
|
||||
childContext.setId(this.applicationContext.getId() + ":management");
|
||||
childContext.setClassLoader(this.applicationContext.getClassLoader());
|
||||
childContext.register(EndpointWebMvcChildContextConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class,
|
||||
ServletWebServerFactoryAutoConfiguration.class,
|
||||
DispatcherServletAutoConfiguration.class);
|
||||
registerServletWebServerFactory(childContext);
|
||||
CloseManagementContextListener.addIfPossible(this.applicationContext,
|
||||
childContext);
|
||||
childContext.refresh();
|
||||
managementContextResolver().setApplicationContext(childContext);
|
||||
}
|
||||
|
||||
private void registerServletWebServerFactory(
|
||||
AnnotationConfigServletWebServerApplicationContext childContext) {
|
||||
try {
|
||||
ConfigurableListableBeanFactory beanFactory = childContext.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) {
|
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
|
||||
registry.registerBeanDefinition("ServletWebServerFactory",
|
||||
new RootBeanDefinition(determineServletWebServerFactoryClass()));
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// Ignore and assume auto-configuration
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> determineServletWebServerFactoryClass()
|
||||
throws NoSuchBeanDefinitionException {
|
||||
Class<?> factoryClass = this.applicationContext
|
||||
.getBean(ServletWebServerFactory.class).getClass();
|
||||
if (cannotBeInstantiated(factoryClass)) {
|
||||
throw new FatalBeanException("ServletWebServerFactory implementation "
|
||||
+ factoryClass.getName() + " cannot be instantiated. "
|
||||
+ "To allow a separate management port to be used, a top-level class "
|
||||
+ "or static inner class should be used instead");
|
||||
}
|
||||
return factoryClass;
|
||||
}
|
||||
|
||||
private boolean cannotBeInstantiated(Class<?> clazz) {
|
||||
return clazz.isLocalClass()
|
||||
|| (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers()))
|
||||
|| clazz.isAnonymousClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias for 'local.management.port' that actually resolves using
|
||||
* 'local.server.port'.
|
||||
* @param environment the environment
|
||||
*/
|
||||
private void addLocalManagementPortPropertyAlias(
|
||||
final ConfigurableEnvironment environment) {
|
||||
environment.getPropertySources()
|
||||
.addLast(new PropertySource<Object>("Management Server") {
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
if ("local.management.port".equals(name)) {
|
||||
return environment.getProperty("local.server.port");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Put Servlets and Filters in their own nested class so they don't force early
|
||||
// instantiation of ManagementServerProperties.
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "management", name = "add-application-context-header", havingValue = "true")
|
||||
protected static class ApplicationContextFilterConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApplicationContextHeaderFilter applicationContextIdFilter(
|
||||
ApplicationContext context) {
|
||||
return new ApplicationContextHeaderFilter(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Conditional(OnManagementMvcCondition.class)
|
||||
@Import(ManagementContextConfigurationsImportSelector.class)
|
||||
protected static class EndpointWebMvcConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ApplicationListener} to propagate the {@link ContextClosedEvent} and
|
||||
* {@link ApplicationFailedEvent} from a parent to a child.
|
||||
*/
|
||||
private static class CloseManagementContextListener
|
||||
implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private final ApplicationContext parentContext;
|
||||
|
||||
private final ConfigurableApplicationContext childContext;
|
||||
|
||||
CloseManagementContextListener(ApplicationContext parentContext,
|
||||
ConfigurableApplicationContext childContext) {
|
||||
this.parentContext = parentContext;
|
||||
this.childContext = childContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof ContextClosedEvent) {
|
||||
onContextClosedEvent((ContextClosedEvent) event);
|
||||
}
|
||||
if (event instanceof ApplicationFailedEvent) {
|
||||
onApplicationFailedEvent((ApplicationFailedEvent) event);
|
||||
}
|
||||
};
|
||||
|
||||
private void onContextClosedEvent(ContextClosedEvent event) {
|
||||
propagateCloseIfNecessary(event.getApplicationContext());
|
||||
}
|
||||
|
||||
private void onApplicationFailedEvent(ApplicationFailedEvent event) {
|
||||
propagateCloseIfNecessary(event.getApplicationContext());
|
||||
}
|
||||
|
||||
private void propagateCloseIfNecessary(ApplicationContext applicationContext) {
|
||||
if (applicationContext == this.parentContext) {
|
||||
this.childContext.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void addIfPossible(ApplicationContext parentContext,
|
||||
ConfigurableApplicationContext childContext) {
|
||||
if (parentContext instanceof ConfigurableApplicationContext) {
|
||||
add((ConfigurableApplicationContext) parentContext, childContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static void add(ConfigurableApplicationContext parentContext,
|
||||
ConfigurableApplicationContext childContext) {
|
||||
parentContext.addApplicationListener(
|
||||
new CloseManagementContextListener(parentContext, childContext));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class OnManagementMvcCondition extends SpringBootCondition
|
||||
implements ConfigurationCondition {
|
||||
|
||||
@Override
|
||||
public ConfigurationPhase getConfigurationPhase() {
|
||||
return ConfigurationPhase.REGISTER_BEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("Management Server MVC");
|
||||
if (!(context.getResourceLoader() instanceof WebApplicationContext)) {
|
||||
return ConditionOutcome
|
||||
.noMatch(message.because("non WebApplicationContext"));
|
||||
}
|
||||
ManagementServerPort port = ManagementServerPort
|
||||
.get(context.getEnvironment());
|
||||
if (port == ManagementServerPort.SAME) {
|
||||
return ConditionOutcome.match(message.because("port is same"));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.because("port is not same"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected enum ManagementServerPort {
|
||||
|
||||
DISABLE, SAME, DIFFERENT;
|
||||
|
||||
public static ManagementServerPort get(Environment environment) {
|
||||
Integer serverPort = getPortProperty(environment, "server.");
|
||||
Integer managementPort = getPortProperty(environment, "management.");
|
||||
if (managementPort != null && managementPort < 0) {
|
||||
return DISABLE;
|
||||
}
|
||||
return ((managementPort == null)
|
||||
|| (serverPort == null && managementPort.equals(8080))
|
||||
|| (managementPort != 0 && managementPort.equals(serverPort)) ? SAME
|
||||
: DIFFERENT);
|
||||
}
|
||||
|
||||
private static Integer getPortProperty(Environment environment, String prefix) {
|
||||
return environment.getProperty(prefix + "port", Integer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.health.HealthMvcEndpointProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCustomizer;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.LogFileMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpointSecurityInterceptor;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
/**
|
||||
* Configuration to expose {@link Endpoint} instances over Spring MVC.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Ben Hale
|
||||
* @author Vedran Pavic
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ManagementContextConfiguration
|
||||
@EnableConfigurationProperties({ HealthMvcEndpointProperties.class,
|
||||
EndpointCorsProperties.class })
|
||||
public class EndpointWebMvcManagementContextConfiguration {
|
||||
|
||||
private final HealthMvcEndpointProperties healthMvcEndpointProperties;
|
||||
|
||||
private final ManagementServerProperties managementServerProperties;
|
||||
|
||||
private final EndpointCorsProperties corsProperties;
|
||||
|
||||
private final List<EndpointHandlerMappingCustomizer> mappingCustomizers;
|
||||
|
||||
public EndpointWebMvcManagementContextConfiguration(
|
||||
HealthMvcEndpointProperties healthMvcEndpointProperties,
|
||||
ManagementServerProperties managementServerProperties,
|
||||
EndpointCorsProperties corsProperties,
|
||||
ObjectProvider<List<EndpointHandlerMappingCustomizer>> mappingCustomizers) {
|
||||
this.healthMvcEndpointProperties = healthMvcEndpointProperties;
|
||||
this.managementServerProperties = managementServerProperties;
|
||||
this.corsProperties = corsProperties;
|
||||
List<EndpointHandlerMappingCustomizer> providedCustomizers = mappingCustomizers
|
||||
.getIfAvailable();
|
||||
this.mappingCustomizers = providedCustomizers == null
|
||||
? Collections.<EndpointHandlerMappingCustomizer>emptyList()
|
||||
: providedCustomizers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EndpointHandlerMapping endpointHandlerMapping() {
|
||||
Set<MvcEndpoint> endpoints = mvcEndpoints().getEndpoints();
|
||||
CorsConfiguration corsConfiguration = getCorsConfiguration(this.corsProperties);
|
||||
EndpointHandlerMapping mapping = new EndpointHandlerMapping(endpoints,
|
||||
corsConfiguration);
|
||||
mapping.setPrefix(this.managementServerProperties.getContextPath());
|
||||
MvcEndpointSecurityInterceptor securityInterceptor = new MvcEndpointSecurityInterceptor(
|
||||
this.managementServerProperties.getSecurity().isEnabled(),
|
||||
this.managementServerProperties.getSecurity().getRoles());
|
||||
mapping.setSecurityInterceptor(securityInterceptor);
|
||||
for (EndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
|
||||
customizer.customize(mapping);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private CorsConfiguration getCorsConfiguration(EndpointCorsProperties properties) {
|
||||
if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
|
||||
return null;
|
||||
}
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(properties.getAllowedOrigins());
|
||||
if (!CollectionUtils.isEmpty(properties.getAllowedHeaders())) {
|
||||
configuration.setAllowedHeaders(properties.getAllowedHeaders());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(properties.getAllowedMethods())) {
|
||||
configuration.setAllowedMethods(properties.getAllowedMethods());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(properties.getExposedHeaders())) {
|
||||
configuration.setExposedHeaders(properties.getExposedHeaders());
|
||||
}
|
||||
if (properties.getMaxAge() != null) {
|
||||
configuration.setMaxAge(properties.getMaxAge());
|
||||
}
|
||||
if (properties.getAllowCredentials() != null) {
|
||||
configuration.setAllowCredentials(properties.getAllowCredentials());
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public MvcEndpoints mvcEndpoints() {
|
||||
return new MvcEndpoints();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(EnvironmentEndpoint.class)
|
||||
@ConditionalOnEnabledEndpoint("env")
|
||||
public EnvironmentMvcEndpoint environmentMvcEndpoint(EnvironmentEndpoint delegate) {
|
||||
return new EnvironmentMvcEndpoint(delegate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint("heapdump")
|
||||
public HeapdumpMvcEndpoint heapdumpMvcEndpoint() {
|
||||
return new HeapdumpMvcEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(HealthEndpoint.class)
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint("health")
|
||||
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
|
||||
this.managementServerProperties.getSecurity().isEnabled(),
|
||||
managementServerProperties.getSecurity().getRoles());
|
||||
if (this.healthMvcEndpointProperties.getMapping() != null) {
|
||||
healthMvcEndpoint
|
||||
.addStatusMapping(this.healthMvcEndpointProperties.getMapping());
|
||||
}
|
||||
return healthMvcEndpoint;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(LoggersEndpoint.class)
|
||||
@ConditionalOnEnabledEndpoint("loggers")
|
||||
public LoggersMvcEndpoint loggersMvcEndpoint(LoggersEndpoint delegate) {
|
||||
return new LoggersMvcEndpoint(delegate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MetricsEndpoint.class)
|
||||
@ConditionalOnEnabledEndpoint("metrics")
|
||||
public MetricsMvcEndpoint metricsMvcEndpoint(MetricsEndpoint delegate) {
|
||||
return new MetricsMvcEndpoint(delegate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint("logfile")
|
||||
@Conditional(LogFileCondition.class)
|
||||
public LogFileMvcEndpoint logfileMvcEndpoint() {
|
||||
return new LogFileMvcEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(ShutdownEndpoint.class)
|
||||
@ConditionalOnEnabledEndpoint(value = "shutdown", enabledByDefault = false)
|
||||
public ShutdownMvcEndpoint shutdownMvcEndpoint(ShutdownEndpoint delegate) {
|
||||
return new ShutdownMvcEndpoint(delegate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(AuditEventRepository.class)
|
||||
@ConditionalOnEnabledEndpoint("auditevents")
|
||||
public AuditEventsMvcEndpoint auditEventMvcEndpoint(
|
||||
AuditEventRepository auditEventRepository) {
|
||||
return new AuditEventsMvcEndpoint(auditEventRepository);
|
||||
}
|
||||
|
||||
private static class LogFileCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
Environment environment = context.getEnvironment();
|
||||
String config = environment.resolvePlaceholders("${logging.file:}");
|
||||
ConditionMessage.Builder message = ConditionMessage.forCondition("Log File");
|
||||
if (StringUtils.hasText(config)) {
|
||||
return ConditionOutcome
|
||||
.match(message.found("logging.file").items(config));
|
||||
}
|
||||
config = environment.resolvePlaceholders("${logging.path:}");
|
||||
if (StringUtils.hasText(config)) {
|
||||
return ConditionOutcome
|
||||
.match(message.found("logging.path").items(config));
|
||||
}
|
||||
config = environment.getProperty("endpoints.logfile.external-file");
|
||||
if (StringUtils.hasText(config)) {
|
||||
return ConditionOutcome.match(
|
||||
message.found("endpoints.logfile.external-file").items(config));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.didNotFind("logging file").atAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
|
|
@ -34,23 +33,6 @@ public class ManagementContextResolver {
|
|||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all {@link MvcEndpoints} from the management context.
|
||||
* @return {@link MvcEndpoints} from the management context
|
||||
*/
|
||||
public MvcEndpoints getMvcEndpoints() {
|
||||
try {
|
||||
return getApplicationContext().getBean(MvcEndpoints.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the management {@link ApplicationContext}.
|
||||
* @return the management {@link ApplicationContext}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.support.EndpointEnablement;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.support.EndpointEnablementProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.EndpointType;
|
||||
import org.springframework.boot.endpoint.jmx.JmxEndpointExtension;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointExtension;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A condition that checks if an endpoint is enabled.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class OnEnabledEndpointCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
EndpointAttributes endpoint = getEndpointAttributes(context, metadata);
|
||||
if (!StringUtils.hasText(endpoint.id)) {
|
||||
throw new IllegalStateException("Endpoint id could not be determined");
|
||||
}
|
||||
EndpointEnablementProvider enablementProvider = new EndpointEnablementProvider(
|
||||
context.getEnvironment());
|
||||
EndpointEnablement endpointEnablement = enablementProvider.getEndpointEnablement(
|
||||
endpoint.id, endpoint.enabled, endpoint.endpointType);
|
||||
return new ConditionOutcome(endpointEnablement.isEnabled(),
|
||||
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
|
||||
.because(endpointEnablement.getReason()));
|
||||
}
|
||||
|
||||
private EndpointAttributes getEndpointAttributes(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
if (metadata instanceof MethodMetadata
|
||||
&& metadata.isAnnotated(Bean.class.getName())) {
|
||||
MethodMetadata methodMetadata = (MethodMetadata) metadata;
|
||||
try {
|
||||
// We should be safe to load at this point since we are in the
|
||||
// REGISTER_BEAN phase
|
||||
Class<?> returnType = ClassUtils.forName(
|
||||
methodMetadata.getReturnTypeName(), context.getClassLoader());
|
||||
return extractEndpointAttributes(returnType);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException("Failed to extract endpoint id for "
|
||||
+ methodMetadata.getDeclaringClassName() + "."
|
||||
+ methodMetadata.getMethodName(), ex);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"OnEnabledEndpointCondition may only be used on @Bean methods");
|
||||
}
|
||||
|
||||
protected EndpointAttributes extractEndpointAttributes(Class<?> type) {
|
||||
EndpointAttributes attributes = extractEndpointAttributesFromEndpoint(type);
|
||||
if (attributes != null) {
|
||||
return attributes;
|
||||
}
|
||||
JmxEndpointExtension jmxExtension = AnnotationUtils.findAnnotation(type,
|
||||
JmxEndpointExtension.class);
|
||||
if (jmxExtension != null) {
|
||||
return extractEndpointAttributes(jmxExtension.endpoint());
|
||||
}
|
||||
WebEndpointExtension webExtension = AnnotationUtils.findAnnotation(type,
|
||||
WebEndpointExtension.class);
|
||||
if (webExtension != null) {
|
||||
return extractEndpointAttributes(webExtension.endpoint());
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"OnEnabledEndpointCondition may only be used on @Bean methods that return"
|
||||
+ " @Endpoint, @JmxEndpointExtension, or @WebEndpointExtension");
|
||||
}
|
||||
|
||||
private EndpointAttributes extractEndpointAttributesFromEndpoint(
|
||||
Class<?> endpointClass) {
|
||||
Endpoint endpoint = AnnotationUtils.findAnnotation(endpointClass, Endpoint.class);
|
||||
if (endpoint == null) {
|
||||
return null;
|
||||
}
|
||||
// If both types are set, all techs are exposed
|
||||
EndpointType endpointType = (endpoint.types().length == 1 ? endpoint.types()[0]
|
||||
: null);
|
||||
return new EndpointAttributes(endpoint.id(), endpoint.enabledByDefault(),
|
||||
endpointType);
|
||||
}
|
||||
|
||||
private static class EndpointAttributes {
|
||||
|
||||
private final String id;
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
private final EndpointType endpointType;
|
||||
|
||||
EndpointAttributes(String id, boolean enabled, EndpointType endpointType) {
|
||||
this.id = id;
|
||||
this.enabled = enabled;
|
||||
this.endpointType = endpointType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.mvc;
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ import org.springframework.http.MediaType;
|
|||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
* @since 1.5.0
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class ActuatorMediaTypes {
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.endpoint.CachingConfiguration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A {@link CachingConfiguration} factory that use the {@link Environment} to extract
|
||||
* the caching settings of each endpoint.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class CachingConfigurationFactory
|
||||
implements Function<String, CachingConfiguration> {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* Create a new instance with the {@link Environment} to use.
|
||||
* @param environment the environment
|
||||
*/
|
||||
CachingConfigurationFactory(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachingConfiguration apply(String endpointId) {
|
||||
String key = String.format("endpoints.%s.cache.time-to-live", endpointId);
|
||||
Long ttl = this.environment.getProperty(key, Long.class, 0L);
|
||||
return new CachingConfiguration(ttl);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.springframework.boot.endpoint.jmx.EndpointMBean;
|
||||
import org.springframework.boot.endpoint.jmx.EndpointObjectNameFactory;
|
||||
import org.springframework.jmx.support.ObjectNameManager;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link EndpointObjectNameFactory} that generates standard {@link ObjectName} for
|
||||
* Actuator's endpoints.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class DefaultEndpointObjectNameFactory implements EndpointObjectNameFactory {
|
||||
|
||||
private String domain = "org.springframework.boot";
|
||||
|
||||
@Override
|
||||
public ObjectName generate(EndpointMBean mBean) throws MalformedObjectNameException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(this.domain);
|
||||
builder.append(":type=Endpoint");
|
||||
builder.append(",name=" + StringUtils.capitalize(mBean.getEndpointId()));
|
||||
return ObjectNameManager.getInstance(builder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
|
||||
import org.springframework.boot.endpoint.EndpointType;
|
||||
import org.springframework.boot.endpoint.OperationParameterMapper;
|
||||
import org.springframework.boot.endpoint.jmx.EndpointMBeanRegistrar;
|
||||
import org.springframework.boot.endpoint.jmx.JmxAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.jmx.JmxEndpointOperation;
|
||||
import org.springframework.boot.endpoint.web.WebAnnotationEndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for the endpoint infrastructure used
|
||||
* by the Actuator.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(JmxAutoConfiguration.class)
|
||||
public class EndpointInfrastructureAutoConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
public EndpointInfrastructureAutoConfiguration(
|
||||
ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OperationParameterMapper operationParameterMapper() {
|
||||
DefaultConversionService conversionService = new DefaultConversionService();
|
||||
conversionService.addConverter(String.class, Date.class, (string) -> {
|
||||
if (StringUtils.hasLength(string)) {
|
||||
OffsetDateTime offsetDateTime = OffsetDateTime.parse(string,
|
||||
DateTimeFormatter.ISO_OFFSET_DATE_TIME);
|
||||
return new Date(offsetDateTime.toEpochSecond() * 1000);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return new ConversionServiceOperationParameterMapper(conversionService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CachingConfigurationFactory cacheConfigurationFactory() {
|
||||
return new CachingConfigurationFactory(this.applicationContext.getEnvironment());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JmxAnnotationEndpointDiscoverer jmxEndpointDiscoverer(
|
||||
OperationParameterMapper operationParameterMapper,
|
||||
CachingConfigurationFactory cachingConfigurationFactory) {
|
||||
return new JmxAnnotationEndpointDiscoverer(this.applicationContext,
|
||||
operationParameterMapper, cachingConfigurationFactory);
|
||||
}
|
||||
|
||||
@ConditionalOnSingleCandidate(MBeanServer.class)
|
||||
@Bean
|
||||
public JmxEndpointExporter jmxMBeanExporter(MBeanServer mBeanServer,
|
||||
JmxAnnotationEndpointDiscoverer endpointDiscoverer,
|
||||
ObjectProvider<ObjectMapper> objectMapper) {
|
||||
EndpointProvider<JmxEndpointOperation> endpointProvider = new EndpointProvider<>(
|
||||
this.applicationContext.getEnvironment(), endpointDiscoverer,
|
||||
EndpointType.JMX);
|
||||
EndpointMBeanRegistrar endpointMBeanRegistrar = new EndpointMBeanRegistrar(
|
||||
mBeanServer, new DefaultEndpointObjectNameFactory());
|
||||
return new JmxEndpointExporter(endpointProvider, endpointMBeanRegistrar,
|
||||
objectMapper.getIfAvailable(ObjectMapper::new));
|
||||
}
|
||||
|
||||
@ConditionalOnWebApplication
|
||||
static class WebInfrastructureConfiguration {
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
WebInfrastructureConfiguration(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EndpointProvider<WebEndpointOperation> webEndpointProvider(
|
||||
OperationParameterMapper operationParameterMapper,
|
||||
CachingConfigurationFactory cachingConfigurationFactory) {
|
||||
return new EndpointProvider<>(this.applicationContext.getEnvironment(),
|
||||
webEndpointDiscoverer(operationParameterMapper,
|
||||
cachingConfigurationFactory),
|
||||
EndpointType.WEB);
|
||||
}
|
||||
|
||||
private WebAnnotationEndpointDiscoverer webEndpointDiscoverer(
|
||||
OperationParameterMapper operationParameterMapper,
|
||||
CachingConfigurationFactory cachingConfigurationFactory) {
|
||||
List<String> mediaTypes = Arrays.asList(
|
||||
ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON_VALUE,
|
||||
"application/json");
|
||||
return new WebAnnotationEndpointDiscoverer(this.applicationContext,
|
||||
operationParameterMapper, cachingConfigurationFactory, mediaTypes,
|
||||
mediaTypes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.support.EndpointEnablementProvider;
|
||||
import org.springframework.boot.endpoint.EndpointDiscoverer;
|
||||
import org.springframework.boot.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.endpoint.EndpointOperation;
|
||||
import org.springframework.boot.endpoint.EndpointType;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Provides the endpoints that are enabled according to an {@link EndpointDiscoverer} and
|
||||
* the current {@link Environment}.
|
||||
*
|
||||
* @param <T> the endpoint operation type
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class EndpointProvider<T extends EndpointOperation> {
|
||||
|
||||
private final EndpointDiscoverer<T> discoverer;
|
||||
|
||||
private final EndpointEnablementProvider endpointEnablementProvider;
|
||||
|
||||
private final EndpointType endpointType;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param environment the environment to use to check the endpoints that are enabled
|
||||
* @param discoverer the discoverer to get the initial set of endpoints
|
||||
* @param endpointType the type of endpoint to handle
|
||||
*/
|
||||
public EndpointProvider(Environment environment, EndpointDiscoverer<T> discoverer,
|
||||
EndpointType endpointType) {
|
||||
this.discoverer = discoverer;
|
||||
this.endpointEnablementProvider = new EndpointEnablementProvider(environment);
|
||||
this.endpointType = endpointType;
|
||||
}
|
||||
|
||||
public Collection<EndpointInfo<T>> getEndpoints() {
|
||||
return this.discoverer.discoverEndpoints().stream().filter(this::isEnabled)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private boolean isEnabled(EndpointInfo<?> endpoint) {
|
||||
return this.endpointEnablementProvider.getEndpointEnablement(endpoint.getId(),
|
||||
endpoint.isEnabledByDefault(), this.endpointType).isEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.jmx.EndpointMBean;
|
||||
import org.springframework.boot.endpoint.jmx.EndpointMBeanRegistrar;
|
||||
import org.springframework.boot.endpoint.jmx.JmxEndpointMBeanFactory;
|
||||
import org.springframework.boot.endpoint.jmx.JmxEndpointOperation;
|
||||
import org.springframework.boot.endpoint.jmx.JmxOperationResponseMapper;
|
||||
|
||||
/**
|
||||
* Exports all available {@link Endpoint} to a configurable {@link MBeanServer}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
class JmxEndpointExporter implements InitializingBean, DisposableBean {
|
||||
|
||||
private final EndpointProvider<JmxEndpointOperation> endpointProvider;
|
||||
|
||||
private final EndpointMBeanRegistrar endpointMBeanRegistrar;
|
||||
|
||||
private final JmxEndpointMBeanFactory mBeanFactory;
|
||||
|
||||
private Collection<ObjectName> registeredObjectNames;
|
||||
|
||||
JmxEndpointExporter(EndpointProvider<JmxEndpointOperation> endpointProvider,
|
||||
EndpointMBeanRegistrar endpointMBeanRegistrar,
|
||||
ObjectMapper objectMapper) {
|
||||
this.endpointProvider = endpointProvider;
|
||||
this.endpointMBeanRegistrar = endpointMBeanRegistrar;
|
||||
DataConverter dataConverter = new DataConverter(objectMapper);
|
||||
this.mBeanFactory = new JmxEndpointMBeanFactory(dataConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
this.registeredObjectNames = registerEndpointMBeans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
unregisterEndpointMBeans(this.registeredObjectNames);
|
||||
}
|
||||
|
||||
private Collection<ObjectName> registerEndpointMBeans() {
|
||||
List<ObjectName> objectNames = new ArrayList<>();
|
||||
Collection<EndpointMBean> mBeans = this.mBeanFactory.createMBeans(
|
||||
this.endpointProvider.getEndpoints());
|
||||
for (EndpointMBean mBean : mBeans) {
|
||||
objectNames.add(this.endpointMBeanRegistrar.registerEndpointMBean(mBean));
|
||||
}
|
||||
return objectNames;
|
||||
}
|
||||
|
||||
private void unregisterEndpointMBeans(Collection<ObjectName> objectNames) {
|
||||
objectNames.forEach(this.endpointMBeanRegistrar::unregisterEndpointMbean);
|
||||
|
||||
}
|
||||
|
||||
static class DataConverter implements JmxOperationResponseMapper {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final JavaType listObject;
|
||||
|
||||
private final JavaType mapStringObject;
|
||||
|
||||
DataConverter(ObjectMapper objectMapper) {
|
||||
this.objectMapper = (objectMapper == null ? new ObjectMapper()
|
||||
: objectMapper);
|
||||
this.listObject = this.objectMapper.getTypeFactory()
|
||||
.constructParametricType(List.class, Object.class);
|
||||
this.mapStringObject = this.objectMapper.getTypeFactory()
|
||||
.constructParametricType(Map.class, String.class, Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object mapResponse(Object response) {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
if (response instanceof String) {
|
||||
return response;
|
||||
}
|
||||
if (response.getClass().isArray() || response instanceof Collection) {
|
||||
return this.objectMapper.convertValue(response, this.listObject);
|
||||
}
|
||||
return this.objectMapper.convertValue(response, this.mapStringObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> mapResponseType(Class<?> responseType) {
|
||||
if (responseType.equals(String.class)) {
|
||||
return String.class;
|
||||
}
|
||||
if (responseType.isArray() || Collection.class.isAssignableFrom(responseType)) {
|
||||
return List.class;
|
||||
}
|
||||
return Map.class;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.web.server.ConfigurableWebServerFactory;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.Ssl;
|
||||
import org.springframework.boot.web.server.WebServerFactory;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* {@link WebServerFactoryCustomizer} that customizes the {@link WebServerFactory} used to
|
||||
* create the management context's web server.
|
||||
*
|
||||
* @param <T> the type of web server factory to customize
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class ManagementWebServerFactoryCustomizer<T extends ConfigurableWebServerFactory>
|
||||
implements WebServerFactoryCustomizer<T>, Ordered {
|
||||
|
||||
private final ListableBeanFactory beanFactory;
|
||||
|
||||
private final Class<? extends WebServerFactoryCustomizer<T>> customizerClass;
|
||||
|
||||
protected ManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory,
|
||||
Class<? extends WebServerFactoryCustomizer<T>> customizerClass) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.customizerClass = customizerClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void customize(T webServerFactory) {
|
||||
ManagementServerProperties managementServerProperties = BeanFactoryUtils
|
||||
.beanOfTypeIncludingAncestors(this.beanFactory,
|
||||
ManagementServerProperties.class);
|
||||
ServerProperties serverProperties = BeanFactoryUtils
|
||||
.beanOfTypeIncludingAncestors(this.beanFactory, ServerProperties.class);
|
||||
WebServerFactoryCustomizer<T> webServerFactoryCustomizer = BeanFactoryUtils
|
||||
.beanOfTypeIncludingAncestors(this.beanFactory, this.customizerClass);
|
||||
// Customize as per the parent context first (so e.g. the access logs go to
|
||||
// the same place)
|
||||
webServerFactoryCustomizer.customize(webServerFactory);
|
||||
// Then reset the error pages
|
||||
webServerFactory.setErrorPages(Collections.<ErrorPage>emptySet());
|
||||
// and add the management-specific bits
|
||||
customize(webServerFactory, managementServerProperties, serverProperties);
|
||||
}
|
||||
|
||||
protected void customize(T webServerFactory,
|
||||
ManagementServerProperties managementServerProperties,
|
||||
ServerProperties serverProperties) {
|
||||
webServerFactory.setPort(managementServerProperties.getPort());
|
||||
Ssl ssl = managementServerProperties.getSsl();
|
||||
if (ssl != null) {
|
||||
webServerFactory.setSsl(ssl);
|
||||
}
|
||||
webServerFactory.setServerHeader(serverProperties.getServerHeader());
|
||||
webServerFactory.setAddress(managementServerProperties.getAddress());
|
||||
webServerFactory
|
||||
.addErrorPages(new ErrorPage(serverProperties.getError().getPath()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextType;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.DefaultReactiveWebServerCustomizer;
|
||||
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
/**
|
||||
* Configuration for reactive web endpoint infrastructure when a separate management
|
||||
* context with a web server running on a different port is required.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@EnableWebFlux
|
||||
@ManagementContextConfiguration(ManagementContextType.CHILD)
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
class ReactiveEndpointChildManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public HttpHandler httpHandler(ApplicationContext applicationContext) {
|
||||
return WebHttpHandlerBuilder.applicationContext(applicationContext).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ManagementReactiveWebServerFactoryCustomizer webServerFactoryCustomizer(
|
||||
ListableBeanFactory beanFactory) {
|
||||
return new ManagementReactiveWebServerFactoryCustomizer(beanFactory);
|
||||
}
|
||||
|
||||
static class ManagementReactiveWebServerFactoryCustomizer extends
|
||||
ManagementWebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {
|
||||
|
||||
ManagementReactiveWebServerFactoryCustomizer(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory, DefaultReactiveWebServerCustomizer.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ManagementContextResolver;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
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.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.filter.ApplicationContextHeaderFilter;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Servlet-specific endpoint
|
||||
* concerns.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Johannes Edmeier
|
||||
* @author Eddú Meléndez
|
||||
* @author Venil Noronha
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(Servlet.class)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(ManagementServerProperties.class)
|
||||
@AutoConfigureAfter({ PropertyPlaceholderAutoConfiguration.class,
|
||||
ServletWebServerFactoryAutoConfiguration.class, WebMvcAutoConfiguration.class,
|
||||
RepositoryRestMvcAutoConfiguration.class, HypermediaAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class,
|
||||
EndpointInfrastructureAutoConfiguration.class })
|
||||
public class ServletEndpointAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public ManagementContextResolver managementContextResolver(
|
||||
ApplicationContext applicationContext) {
|
||||
return new ManagementContextResolver(applicationContext);
|
||||
}
|
||||
|
||||
// Put Servlets and Filters in their own nested class so they don't force early
|
||||
// instantiation of ManagementServerProperties.
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "management", name = "add-application-context-header", havingValue = "true")
|
||||
protected static class ApplicationContextFilterConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApplicationContextHeaderFilter applicationContextIdFilter(
|
||||
ApplicationContext context) {
|
||||
return new ApplicationContextHeaderFilter(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,10 +14,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint;
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
|
@ -26,38 +25,37 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.glassfish.jersey.servlet.ServletContainer;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextType;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.ManagementErrorEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.hateoas.HypermediaHttpMessageConverterConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DefaultServletWebServerFactoryCustomizer;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorAttributes;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.ErrorPage;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.hateoas.LinkDiscoverer;
|
||||
import org.springframework.hateoas.config.EnableHypermediaSupport;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
|
|
@ -68,50 +66,23 @@ import org.springframework.web.servlet.ModelAndView;
|
|||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* Configuration triggered from {@link EndpointWebMvcAutoConfiguration} when a new
|
||||
* {@link WebServer} running on a different port is required.
|
||||
* Configuration for Servlet web endpoint infrastructure when a separate management
|
||||
* context with a web server running on a different port is required.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @see EndpointWebMvcAutoConfiguration
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@Import(ManagementContextConfigurationsImportSelector.class)
|
||||
public class EndpointWebMvcChildContextConfiguration {
|
||||
|
||||
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||||
public DispatcherServlet dispatcherServlet() {
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
||||
// Ensure the parent configuration does not leak down to us
|
||||
dispatcherServlet.setDetectAllHandlerAdapters(false);
|
||||
dispatcherServlet.setDetectAllHandlerExceptionResolvers(false);
|
||||
dispatcherServlet.setDetectAllHandlerMappings(false);
|
||||
dispatcherServlet.setDetectAllViewResolvers(false);
|
||||
return dispatcherServlet;
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME)
|
||||
public CompositeHandlerMapping compositeHandlerMapping() {
|
||||
return new CompositeHandlerMapping();
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServlet.HANDLER_ADAPTER_BEAN_NAME)
|
||||
public CompositeHandlerAdapter compositeHandlerAdapter() {
|
||||
return new CompositeHandlerAdapter();
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME)
|
||||
public CompositeHandlerExceptionResolver compositeHandlerExceptionResolver() {
|
||||
return new CompositeHandlerExceptionResolver();
|
||||
}
|
||||
@ManagementContextConfiguration(ManagementContextType.CHILD)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
class ServletEndpointChildManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServerFactoryCustomization serverCustomization() {
|
||||
return new ServerFactoryCustomization();
|
||||
public ManagementServletWebServerFactoryCustomization serverCustomization(
|
||||
ListableBeanFactory beanFactory) {
|
||||
return new ManagementServletWebServerFactoryCustomization(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -125,28 +96,75 @@ public class EndpointWebMvcChildContextConfiguration {
|
|||
return new TomcatAccessLogCustomizer();
|
||||
}
|
||||
|
||||
/*
|
||||
* The error controller is present but not mapped as an endpoint in this context
|
||||
* because of the DispatcherServlet having had its HandlerMapping explicitly disabled.
|
||||
* So we expose the same feature but only for machine endpoints.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(ErrorAttributes.class)
|
||||
public ManagementErrorEndpoint errorEndpoint(ErrorAttributes errorAttributes) {
|
||||
return new ManagementErrorEndpoint(errorAttributes);
|
||||
@EnableWebMvc
|
||||
@ConditionalOnClass(DispatcherServlet.class)
|
||||
static class MvcEndpointChildContextConfiguration {
|
||||
|
||||
/*
|
||||
* The error controller is present but not mapped as an endpoint in this context
|
||||
* because of the DispatcherServlet having had its HandlerMapping explicitly
|
||||
* disabled. So we expose the same feature but only for machine endpoints.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(ErrorAttributes.class)
|
||||
public ManagementErrorEndpoint errorEndpoint(ErrorAttributes errorAttributes) {
|
||||
return new ManagementErrorEndpoint(errorAttributes);
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
|
||||
public DispatcherServlet dispatcherServlet() {
|
||||
DispatcherServlet dispatcherServlet = new DispatcherServlet();
|
||||
// Ensure the parent configuration does not leak down to us
|
||||
dispatcherServlet.setDetectAllHandlerAdapters(false);
|
||||
dispatcherServlet.setDetectAllHandlerExceptionResolvers(false);
|
||||
dispatcherServlet.setDetectAllHandlerMappings(false);
|
||||
dispatcherServlet.setDetectAllViewResolvers(false);
|
||||
return dispatcherServlet;
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServlet.HANDLER_MAPPING_BEAN_NAME)
|
||||
public CompositeHandlerMapping compositeHandlerMapping() {
|
||||
return new CompositeHandlerMapping();
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServlet.HANDLER_ADAPTER_BEAN_NAME)
|
||||
public CompositeHandlerAdapter compositeHandlerAdapter() {
|
||||
return new CompositeHandlerAdapter();
|
||||
}
|
||||
|
||||
@Bean(name = DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME)
|
||||
public CompositeHandlerExceptionResolver compositeHandlerExceptionResolver() {
|
||||
return new CompositeHandlerExceptionResolver();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration to add {@link HandlerMapping} for {@link MvcEndpoint}s.
|
||||
*/
|
||||
@Configuration
|
||||
protected static class EndpointHandlerMappingConfiguration {
|
||||
@ConditionalOnClass(ResourceConfig.class)
|
||||
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
|
||||
static class JerseyEndpointChildContextConfiguration {
|
||||
|
||||
@Autowired
|
||||
public void handlerMapping(MvcEndpoints endpoints,
|
||||
ListableBeanFactory beanFactory, EndpointHandlerMapping mapping) {
|
||||
// In a child context we definitely want to see the parent endpoints
|
||||
mapping.setDetectHandlerMethodsInAncestorContexts(true);
|
||||
private final List<ResourceConfigCustomizer> resourceConfigCustomizers;
|
||||
|
||||
JerseyEndpointChildContextConfiguration(
|
||||
List<ResourceConfigCustomizer> resourceConfigCustomizers) {
|
||||
this.resourceConfigCustomizers = resourceConfigCustomizers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<ServletContainer> jerseyServletRegistration() {
|
||||
ServletRegistrationBean<ServletContainer> registration = new ServletRegistrationBean<>(
|
||||
new ServletContainer(endpointResourceConfig()), "/*");
|
||||
return registration;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ResourceConfig endpointResourceConfig() {
|
||||
ResourceConfig resourceConfig = new ResourceConfig();
|
||||
for (ResourceConfigCustomizer customizer : this.resourceConfigCustomizers) {
|
||||
customizer.customize(resourceConfig);
|
||||
}
|
||||
return resourceConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -154,7 +172,7 @@ public class EndpointWebMvcChildContextConfiguration {
|
|||
@Configuration
|
||||
@ConditionalOnClass({ EnableWebSecurity.class, Filter.class })
|
||||
@ConditionalOnBean(name = "springSecurityFilterChain", search = SearchStrategy.ANCESTORS)
|
||||
public static class EndpointWebMvcChildContextSecurityConfiguration {
|
||||
static class EndpointWebMvcChildContextSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
public Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) {
|
||||
|
|
@ -164,60 +182,20 @@ public class EndpointWebMvcChildContextConfiguration {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass({ LinkDiscoverer.class })
|
||||
@Import(HypermediaHttpMessageConverterConfiguration.class)
|
||||
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
|
||||
static class HypermediaConfiguration {
|
||||
static class ManagementServletWebServerFactoryCustomization extends
|
||||
ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
|
||||
|
||||
}
|
||||
|
||||
static class ServerFactoryCustomization implements
|
||||
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
|
||||
|
||||
@Autowired
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
// This needs to be lazily initialized because web server customizer
|
||||
// instances get their callback very early in the context lifecycle.
|
||||
private ManagementServerProperties managementServerProperties;
|
||||
|
||||
private ServerProperties server;
|
||||
|
||||
private DefaultServletWebServerFactoryCustomizer serverCustomizer;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
ManagementServletWebServerFactoryCustomization(ListableBeanFactory beanFactory) {
|
||||
super(beanFactory, DefaultServletWebServerFactoryCustomizer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableServletWebServerFactory webServerFactory) {
|
||||
if (this.managementServerProperties == null) {
|
||||
this.managementServerProperties = BeanFactoryUtils
|
||||
.beanOfTypeIncludingAncestors(this.beanFactory,
|
||||
ManagementServerProperties.class);
|
||||
this.server = BeanFactoryUtils.beanOfTypeIncludingAncestors(
|
||||
this.beanFactory, ServerProperties.class);
|
||||
this.serverCustomizer = BeanFactoryUtils.beanOfTypeIncludingAncestors(
|
||||
this.beanFactory, DefaultServletWebServerFactoryCustomizer.class);
|
||||
}
|
||||
// Customize as per the parent context first (so e.g. the access logs go to
|
||||
// the same place)
|
||||
this.serverCustomizer.customize(webServerFactory);
|
||||
// Then reset the error pages
|
||||
webServerFactory.setErrorPages(Collections.<ErrorPage>emptySet());
|
||||
// and the context path
|
||||
protected void customize(ConfigurableServletWebServerFactory webServerFactory,
|
||||
ManagementServerProperties managementServerProperties,
|
||||
ServerProperties serverProperties) {
|
||||
super.customize(webServerFactory, managementServerProperties,
|
||||
serverProperties);
|
||||
webServerFactory.setContextPath("");
|
||||
// and add the management-specific bits
|
||||
webServerFactory.setPort(this.managementServerProperties.getPort());
|
||||
if (this.managementServerProperties.getSsl() != null) {
|
||||
webServerFactory.setSsl(this.managementServerProperties.getSsl());
|
||||
}
|
||||
webServerFactory.setServerHeader(this.server.getServerHeader());
|
||||
webServerFactory.setAddress(this.managementServerProperties.getAddress());
|
||||
webServerFactory
|
||||
.addErrorPages(new ErrorPage(this.server.getError().getPath()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import org.springframework.boot.endpoint.web.mvc.WebEndpointServletHandlerMapping;
|
||||
|
||||
/**
|
||||
* Callback for customizing the {@link WebEndpointServletHandlerMapping} at configuration
|
||||
* time.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface WebEndpointHandlerMappingCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the given {@code mapping}.
|
||||
* @param mapping the mapping to customize
|
||||
*/
|
||||
void customize(WebEndpointServletHandlerMapping mapping);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointCorsProperties;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
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.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointOperation;
|
||||
import org.springframework.boot.endpoint.web.jersey.JerseyEndpointResourceFactory;
|
||||
import org.springframework.boot.endpoint.web.mvc.WebEndpointServletHandlerMapping;
|
||||
import org.springframework.boot.endpoint.web.reactive.WebEndpointReactiveHandlerMapping;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
/**
|
||||
* Management context configuration for the infrastructure for web endpoints.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConditionalOnWebApplication
|
||||
@ManagementContextConfiguration
|
||||
@EnableConfigurationProperties({ EndpointCorsProperties.class,
|
||||
ManagementServerProperties.class })
|
||||
class WebEndpointInfrastructureManagementContextConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@ConditionalOnClass(ResourceConfig.class)
|
||||
@ConditionalOnBean(ResourceConfig.class)
|
||||
@ConditionalOnMissingBean(type = "org.springframework.web.servlet.DispatcherServlet")
|
||||
static class JerseyWebEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
public ResourceConfigCustomizer webEndpointRegistrar(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
return (resourceConfig) -> resourceConfig.registerResources(new HashSet<>(
|
||||
new JerseyEndpointResourceFactory().createEndpointResources(
|
||||
managementServerProperties.getContextPath(),
|
||||
provider.getEndpoints())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@ConditionalOnClass(DispatcherServlet.class)
|
||||
@ConditionalOnBean(DispatcherServlet.class)
|
||||
static class MvcWebEndpointConfiguration {
|
||||
|
||||
private final List<WebEndpointHandlerMappingCustomizer> mappingCustomizers;
|
||||
|
||||
MvcWebEndpointConfiguration(
|
||||
ObjectProvider<List<WebEndpointHandlerMappingCustomizer>> mappingCustomizers) {
|
||||
this.mappingCustomizers = mappingCustomizers
|
||||
.getIfUnique(Collections::emptyList);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebEndpointServletHandlerMapping webEndpointServletHandlerMapping(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
EndpointCorsProperties corsProperties,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
WebEndpointServletHandlerMapping handlerMapping = new WebEndpointServletHandlerMapping(
|
||||
managementServerProperties.getContextPath(), provider.getEndpoints(),
|
||||
getCorsConfiguration(corsProperties));
|
||||
for (WebEndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
|
||||
customizer.customize(handlerMapping);
|
||||
}
|
||||
return handlerMapping;
|
||||
}
|
||||
|
||||
private CorsConfiguration getCorsConfiguration(
|
||||
EndpointCorsProperties properties) {
|
||||
if (CollectionUtils.isEmpty(properties.getAllowedOrigins())) {
|
||||
return null;
|
||||
}
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowedOrigins(properties.getAllowedOrigins());
|
||||
if (!CollectionUtils.isEmpty(properties.getAllowedHeaders())) {
|
||||
configuration.setAllowedHeaders(properties.getAllowedHeaders());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(properties.getAllowedMethods())) {
|
||||
configuration.setAllowedMethods(properties.getAllowedMethods());
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(properties.getExposedHeaders())) {
|
||||
configuration.setExposedHeaders(properties.getExposedHeaders());
|
||||
}
|
||||
if (properties.getMaxAge() != null) {
|
||||
configuration.setMaxAge(properties.getMaxAge());
|
||||
}
|
||||
if (properties.getAllowCredentials() != null) {
|
||||
configuration.setAllowCredentials(properties.getAllowCredentials());
|
||||
}
|
||||
return configuration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
static class ReactiveWebEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebEndpointReactiveHandlerMapping webEndpointReactiveHandlerMapping(
|
||||
EndpointProvider<WebEndpointOperation> provider,
|
||||
ManagementServerProperties managementServerProperties) {
|
||||
return new WebEndpointReactiveHandlerMapping(
|
||||
managementServerProperties.getContextPath(), provider.getEndpoints());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for the Actuator's endpoint infrastructure.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure;
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.jmx;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.jmx.AuditEventsJmxEndpointExtension;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.endpoint.jmx.JmxEndpointExtension;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Auto-configuration for JMX {@link org.springframework.boot.endpoint.Endpoint Endpoints}
|
||||
* and JMX-specific {@link JmxEndpointExtension endpoint extensions}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@AutoConfigureAfter(EndpointAutoConfiguration.class)
|
||||
@Configuration
|
||||
public class JmxEndpointAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(AuditEventsEndpoint.class)
|
||||
public AuditEventsJmxEndpointExtension auditEventsJmxEndpointExtension(
|
||||
AuditEventsEndpoint auditEventsEndpoint) {
|
||||
return new AuditEventsJmxEndpointExtension(auditEventsEndpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
* Copyright 2012-2015 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.
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Jolokia.
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for the Actuator's JMX endpoints.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.jolokia;
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.support;
|
||||
|
||||
/**
|
||||
* Determines if an endpoint is enabled or not.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class EndpointEnablement {
|
||||
|
||||
private final boolean enabled;
|
||||
private final String reason;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
* @param enabled whether or not the endpoint is enabled
|
||||
* @param reason a human readable reason of the decision
|
||||
*/
|
||||
EndpointEnablement(boolean enabled, String reason) {
|
||||
this.enabled = enabled;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not the endpoint is enabled.
|
||||
* @return {@code true} if the endpoint is enabled, {@code false} otherwise
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human readable reason of the decision.
|
||||
* @return the reason of the endpoint's enablement
|
||||
*/
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.support;
|
||||
|
||||
import org.springframework.boot.endpoint.EndpointType;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Determines an endpoint's enablement based on the current {@link Environment}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class EndpointEnablementProvider {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the {@link Environment} to use.
|
||||
* @param environment the environment
|
||||
*/
|
||||
public EndpointEnablementProvider(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link EndpointEnablement} of an endpoint with no specific tech
|
||||
* exposure.
|
||||
* @param endpointId the id of the endpoint
|
||||
* @param enabledByDefault whether the endpoint is enabled by default or not
|
||||
* @return the {@link EndpointEnablement} of that endpoint
|
||||
*/
|
||||
public EndpointEnablement getEndpointEnablement(String endpointId,
|
||||
boolean enabledByDefault) {
|
||||
return getEndpointEnablement(endpointId, enabledByDefault, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link EndpointEnablement} of an endpoint for a specific tech exposure.
|
||||
* @param endpointId the id of the endpoint
|
||||
* @param enabledByDefault whether the endpoint is enabled by default or not
|
||||
* @param endpointType the requested {@link EndpointType}
|
||||
* @return the {@link EndpointEnablement} of that endpoint for the specified {@link EndpointType}
|
||||
*/
|
||||
public EndpointEnablement getEndpointEnablement(String endpointId,
|
||||
boolean enabledByDefault, EndpointType endpointType) {
|
||||
if (!StringUtils.hasText(endpointId)) {
|
||||
throw new IllegalArgumentException("Endpoint id must have a value");
|
||||
}
|
||||
if (endpointId.equals("all")) {
|
||||
throw new IllegalArgumentException("Endpoint id 'all' is a reserved value "
|
||||
+ "and cannot be used by an endpoint");
|
||||
}
|
||||
|
||||
if (endpointType != null) {
|
||||
String endpointTypeKey = createTechSpecificKey(endpointId, endpointType);
|
||||
EndpointEnablement endpointTypeSpecificOutcome = getEnablementFor(
|
||||
endpointTypeKey);
|
||||
if (endpointTypeSpecificOutcome != null) {
|
||||
return endpointTypeSpecificOutcome;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If any tech specific is on at this point we should enable the endpoint
|
||||
EndpointEnablement anyTechSpecificOutcome = getAnyTechSpecificOutcomeFor(
|
||||
endpointId);
|
||||
if (anyTechSpecificOutcome != null) {
|
||||
return anyTechSpecificOutcome;
|
||||
}
|
||||
}
|
||||
String endpointKey = createKey(endpointId, "enabled");
|
||||
EndpointEnablement endpointSpecificOutcome = getEnablementFor(endpointKey);
|
||||
if (endpointSpecificOutcome != null) {
|
||||
return endpointSpecificOutcome;
|
||||
}
|
||||
|
||||
// All endpoints specific attributes have been looked at. Checking default value
|
||||
// for the endpoint
|
||||
if (!enabledByDefault) {
|
||||
return new EndpointEnablement(false,
|
||||
createDefaultEnablementMessage(endpointId, enabledByDefault,
|
||||
endpointType));
|
||||
}
|
||||
|
||||
if (endpointType != null) {
|
||||
String globalTypeKey = createTechSpecificKey("all", endpointType);
|
||||
EndpointEnablement globalTypeOutcome = getEnablementFor(globalTypeKey);
|
||||
if (globalTypeOutcome != null) {
|
||||
return globalTypeOutcome;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check if there is a global tech required
|
||||
EndpointEnablement anyTechGeneralOutcome = getAnyTechSpecificOutcomeFor("all");
|
||||
if (anyTechGeneralOutcome != null) {
|
||||
return anyTechGeneralOutcome;
|
||||
}
|
||||
}
|
||||
|
||||
String globalKey = createKey("all", "enabled");
|
||||
EndpointEnablement globalOutCome = getEnablementFor(globalKey);
|
||||
if (globalOutCome != null) {
|
||||
return globalOutCome;
|
||||
}
|
||||
return new EndpointEnablement(enabledByDefault, createDefaultEnablementMessage(
|
||||
endpointId, enabledByDefault, endpointType));
|
||||
}
|
||||
|
||||
private String createDefaultEnablementMessage(String endpointId,
|
||||
boolean enabledByDefault, EndpointType endpointType) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("endpoint '%s' ", endpointId));
|
||||
if (endpointType != null) {
|
||||
sb.append(String.format("(%s) ", endpointType.name().toLowerCase()));
|
||||
}
|
||||
sb.append(String.format("is %s by default",
|
||||
(enabledByDefault ? "enabled" : "disabled")));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private EndpointEnablement getAnyTechSpecificOutcomeFor(String endpointId) {
|
||||
for (EndpointType endpointType : EndpointType.values()) {
|
||||
String key = createTechSpecificKey(endpointId, endpointType);
|
||||
EndpointEnablement outcome = getEnablementFor(key);
|
||||
if (outcome != null && outcome.isEnabled()) {
|
||||
return outcome;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String createTechSpecificKey(String endpointId, EndpointType endpointType) {
|
||||
return createKey(endpointId, endpointType.name().toLowerCase() + ".enabled");
|
||||
}
|
||||
|
||||
private String createKey(String endpointId, String suffix) {
|
||||
return "endpoints." + endpointId + "." + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link EndpointEnablement} for the specified key if it is set or
|
||||
* {@code null} if the key is not present in the environment.
|
||||
* @param key the key to check
|
||||
* @return the outcome or {@code null} if the key is no set
|
||||
*/
|
||||
private EndpointEnablement getEnablementFor(String key) {
|
||||
if (this.environment.containsProperty(key)) {
|
||||
boolean match = this.environment.getProperty(key, Boolean.class, true);
|
||||
return new EndpointEnablement(match, String.format("found property %s", key));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -15,6 +15,6 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* {@code @Condition} annotations and supporting classes.
|
||||
* Support classes for the Actuator's endpoint auto-configuration.
|
||||
*/
|
||||
package org.springframework.boot.actuate.condition;
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.support;
|
||||
|
|
@ -14,35 +14,35 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure.health;
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
/**
|
||||
* Configuration properties for the {@link HealthMvcEndpoint}.
|
||||
* Configuration properties for the {@link HealthWebEndpointExtension}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.health")
|
||||
public class HealthMvcEndpointProperties {
|
||||
public class HealthWebEndpointExtensionProperties {
|
||||
|
||||
/**
|
||||
* Mapping of health statuses to HttpStatus codes. By default, registered health
|
||||
* statuses map to sensible defaults (i.e. UP maps to 200).
|
||||
*/
|
||||
private Map<String, HttpStatus> mapping = new HashMap<>();
|
||||
private Map<String, Integer> mapping = new HashMap<>();
|
||||
|
||||
public Map<String, HttpStatus> getMapping() {
|
||||
public Map<String, Integer> getMapping() {
|
||||
return this.mapping;
|
||||
}
|
||||
|
||||
public void setMapping(Map<String, HttpStatus> mapping) {
|
||||
public void setMapping(Map<String, Integer> mapping) {
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.autoconfigure.endpoint.web;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.AuditEventsWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
|
||||
import org.springframework.boot.actuate.endpoint.web.HeapDumpWebEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.LogFileWebEndpoint;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration for web-specific endpoint functionality.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ManagementContextConfiguration
|
||||
@EnableConfigurationProperties(HealthWebEndpointExtensionProperties.class)
|
||||
public class WebEndpointManagementContextConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
public HeapDumpWebEndpoint heapDumpWebEndpoint() {
|
||||
return new HeapDumpWebEndpoint();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = HealthEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public HealthWebEndpointExtension healthWebEndpointExtension(HealthEndpoint delegate,
|
||||
HealthWebEndpointExtensionProperties extensionProperties) {
|
||||
HealthWebEndpointExtension webExtension = new HealthWebEndpointExtension(
|
||||
delegate);
|
||||
if (extensionProperties.getMapping() != null) {
|
||||
webExtension.addStatusMapping(extensionProperties.getMapping());
|
||||
}
|
||||
return webExtension;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnBean(value = AuditEventsEndpoint.class, search = SearchStrategy.CURRENT)
|
||||
public AuditEventsWebEndpointExtension auditEventsWebEndpointExtension(
|
||||
AuditEventsEndpoint delegate) {
|
||||
return new AuditEventsWebEndpointExtension(delegate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Conditional(LogFileCondition.class)
|
||||
public LogFileWebEndpoint logfileWebEndpoint(Environment environment) {
|
||||
return new LogFileWebEndpoint(environment);
|
||||
}
|
||||
|
||||
private static class LogFileCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
Environment environment = context.getEnvironment();
|
||||
String config = environment.resolvePlaceholders("${logging.file:}");
|
||||
ConditionMessage.Builder message = ConditionMessage.forCondition("Log File");
|
||||
if (StringUtils.hasText(config)) {
|
||||
return ConditionOutcome
|
||||
.match(message.found("logging.file").items(config));
|
||||
}
|
||||
config = environment.resolvePlaceholders("${logging.path:}");
|
||||
if (StringUtils.hasText(config)) {
|
||||
return ConditionOutcome
|
||||
.match(message.found("logging.path").items(config));
|
||||
}
|
||||
config = environment.getProperty("endpoints.logfile.external-file");
|
||||
if (StringUtils.hasText(config)) {
|
||||
return ConditionOutcome.match(
|
||||
message.found("endpoints.logfile.external-file").items(config));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.didNotFind("logging file").atAll());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for the Actuator's web endpoints.
|
||||
*/
|
||||
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.actuate.autoconfigure.security;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -28,9 +29,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.ManagementContextResolver;
|
||||
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
|
|
@ -51,6 +49,9 @@ import org.springframework.boot.autoconfigure.security.SecurityProperties;
|
|||
import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.EndpointInfo;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointOperation;
|
||||
import org.springframework.boot.endpoint.web.mvc.WebEndpointServletHandlerMapping;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
|
|
@ -218,7 +219,8 @@ public class ManagementWebSecurityAutoConfiguration {
|
|||
AuthenticationEntryPoint entryPoint = entryPoint();
|
||||
http.exceptionHandling().authenticationEntryPoint(entryPoint);
|
||||
// Match all the requests for actuator endpoints ...
|
||||
http.requestMatcher(matcher);
|
||||
http.requestMatcher(matcher).authorizeRequests().anyRequest()
|
||||
.authenticated();
|
||||
http.httpBasic().authenticationEntryPoint(entryPoint).and().cors();
|
||||
// No cookies for management endpoints by default
|
||||
http.csrf().disable();
|
||||
|
|
@ -256,20 +258,21 @@ public class ManagementWebSecurityAutoConfiguration {
|
|||
|
||||
private static class EndpointPaths {
|
||||
|
||||
public String[] getPaths(EndpointHandlerMapping endpointHandlerMapping) {
|
||||
public String[] getPaths(
|
||||
WebEndpointServletHandlerMapping endpointHandlerMapping) {
|
||||
if (endpointHandlerMapping == null) {
|
||||
return NO_PATHS;
|
||||
}
|
||||
Set<? extends MvcEndpoint> endpoints = endpointHandlerMapping.getEndpoints();
|
||||
Collection<EndpointInfo<WebEndpointOperation>> endpoints = endpointHandlerMapping
|
||||
.getEndpoints();
|
||||
Set<String> paths = new LinkedHashSet<>(endpoints.size());
|
||||
for (MvcEndpoint endpoint : endpoints) {
|
||||
String path = endpointHandlerMapping.getPath(endpoint.getPath());
|
||||
for (EndpointInfo<WebEndpointOperation> endpoint : endpoints) {
|
||||
String path = endpointHandlerMapping.getEndpointPath() + "/"
|
||||
+ endpoint.getId();
|
||||
paths.add(path);
|
||||
if (!path.equals("")) {
|
||||
paths.add(path + "/**");
|
||||
// Add Spring MVC-generated additional paths
|
||||
paths.add(path + ".*");
|
||||
}
|
||||
paths.add(path + "/**");
|
||||
// Add Spring MVC-generated additional paths
|
||||
paths.add(path + ".*");
|
||||
paths.add(path + "/");
|
||||
}
|
||||
return paths.toArray(new String[paths.size()]);
|
||||
|
|
@ -300,7 +303,7 @@ public class ManagementWebSecurityAutoConfiguration {
|
|||
server.getServlet().getPath(path) + "/**");
|
||||
return matcher;
|
||||
}
|
||||
// Match everything, including the sensitive and non-sensitive paths
|
||||
// Match all endpoint paths
|
||||
return new LazyEndpointPathRequestMatcher(contextResolver,
|
||||
new EndpointPaths());
|
||||
}
|
||||
|
|
@ -323,7 +326,7 @@ public class ManagementWebSecurityAutoConfiguration {
|
|||
ServerProperties server = this.contextResolver.getApplicationContext()
|
||||
.getBean(ServerProperties.class);
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
EndpointHandlerMapping endpointHandlerMapping = getRequiredEndpointHandlerMapping();
|
||||
WebEndpointServletHandlerMapping endpointHandlerMapping = getRequiredEndpointHandlerMapping();
|
||||
for (String path : this.endpointPaths.getPaths(endpointHandlerMapping)) {
|
||||
matchers.add(
|
||||
new AntPathRequestMatcher(server.getServlet().getPath(path)));
|
||||
|
|
@ -331,16 +334,18 @@ public class ManagementWebSecurityAutoConfiguration {
|
|||
return (matchers.isEmpty() ? MATCH_NONE : new OrRequestMatcher(matchers));
|
||||
}
|
||||
|
||||
private EndpointHandlerMapping getRequiredEndpointHandlerMapping() {
|
||||
EndpointHandlerMapping endpointHandlerMapping = null;
|
||||
private WebEndpointServletHandlerMapping getRequiredEndpointHandlerMapping() {
|
||||
WebEndpointServletHandlerMapping endpointHandlerMapping = null;
|
||||
ApplicationContext context = this.contextResolver.getApplicationContext();
|
||||
if (context.getBeanNamesForType(EndpointHandlerMapping.class).length > 0) {
|
||||
endpointHandlerMapping = context.getBean(EndpointHandlerMapping.class);
|
||||
if (context.getBeanNamesForType(
|
||||
WebEndpointServletHandlerMapping.class).length > 0) {
|
||||
endpointHandlerMapping = context
|
||||
.getBean(WebEndpointServletHandlerMapping.class);
|
||||
}
|
||||
if (endpointHandlerMapping == null) {
|
||||
// Maybe there are actually no endpoints (e.g. management.port=-1)
|
||||
endpointHandlerMapping = new EndpointHandlerMapping(
|
||||
Collections.<NamedMvcEndpoint>emptySet());
|
||||
endpointHandlerMapping = new WebEndpointServletHandlerMapping("",
|
||||
Collections.emptySet());
|
||||
}
|
||||
return endpointHandlerMapping;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,75 +16,62 @@
|
|||
|
||||
package org.springframework.boot.actuate.cloudfoundry;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.mvc.AbstractMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* {@link MvcEndpoint} to expose HAL-formatted JSON for Cloud Foundry specific actuator
|
||||
* endpoints.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class CloudFoundryDiscoveryMvcEndpoint extends AbstractMvcEndpoint {
|
||||
class CloudFoundryDiscoveryMvcEndpoint {
|
||||
|
||||
private final Set<NamedMvcEndpoint> endpoints;
|
||||
// TODO Port to new infrastructure
|
||||
|
||||
CloudFoundryDiscoveryMvcEndpoint(Set<NamedMvcEndpoint> endpoints) {
|
||||
super("");
|
||||
this.endpoints = endpoints;
|
||||
}
|
||||
|
||||
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ResponseBody
|
||||
public Map<String, Map<String, Link>> links(HttpServletRequest request) {
|
||||
Map<String, Link> links = new LinkedHashMap<>();
|
||||
String url = request.getRequestURL().toString();
|
||||
if (url.endsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
links.put("self", Link.withHref(url));
|
||||
AccessLevel accessLevel = AccessLevel.get(request);
|
||||
for (NamedMvcEndpoint endpoint : this.endpoints) {
|
||||
if (accessLevel != null && accessLevel.isAccessAllowed(endpoint.getPath())) {
|
||||
links.put(endpoint.getName(),
|
||||
Link.withHref(url + "/" + endpoint.getName()));
|
||||
}
|
||||
}
|
||||
return Collections.singletonMap("_links", links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Details for a link in the HAL response.
|
||||
*/
|
||||
static class Link {
|
||||
|
||||
private String href;
|
||||
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
public void setHref(String href) {
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
static Link withHref(Object href) {
|
||||
Link link = new Link();
|
||||
link.setHref(href.toString());
|
||||
return link;
|
||||
}
|
||||
|
||||
}
|
||||
// private final Set<NamedMvcEndpoint> endpoints;
|
||||
//
|
||||
// CloudFoundryDiscoveryMvcEndpoint(Set<NamedMvcEndpoint> endpoints) {
|
||||
// this.endpoints = endpoints;
|
||||
// }
|
||||
//
|
||||
// @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
// @ResponseBody
|
||||
// public Map<String, Map<String, Link>> links(HttpServletRequest request) {
|
||||
// Map<String, Link> links = new LinkedHashMap<>();
|
||||
// String url = request.getRequestURL().toString();
|
||||
// if (url.endsWith("/")) {
|
||||
// url = url.substring(0, url.length() - 1);
|
||||
// }
|
||||
// links.put("self", Link.withHref(url));
|
||||
// AccessLevel accessLevel = AccessLevel.get(request);
|
||||
// for (NamedMvcEndpoint endpoint : this.endpoints) {
|
||||
// if (accessLevel != null && accessLevel.isAccessAllowed(endpoint.getPath())) {
|
||||
// links.put(endpoint.getName(),
|
||||
// Link.withHref(url + "/" + endpoint.getName()));
|
||||
// }
|
||||
// }
|
||||
// return Collections.singletonMap("_links", links);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Details for a link in the HAL response.
|
||||
// */
|
||||
// static class Link {
|
||||
//
|
||||
// private String href;
|
||||
//
|
||||
// public String getHref() {
|
||||
// return this.href;
|
||||
// }
|
||||
//
|
||||
// public void setHref(String href) {
|
||||
// this.href = href;
|
||||
// }
|
||||
//
|
||||
// static Link withHref(Object href) {
|
||||
// Link link = new Link();
|
||||
// link.setHref(href.toString());
|
||||
// return link;
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.cloudfoundry;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.AbstractEndpointHandlerMapping;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.NamedMvcEndpoint;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* {@link HandlerMapping} to map {@link Endpoint}s to Cloud Foundry specific URLs.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class CloudFoundryEndpointHandlerMapping
|
||||
extends AbstractEndpointHandlerMapping<NamedMvcEndpoint> {
|
||||
|
||||
public CloudFoundryEndpointHandlerMapping(Set<? extends NamedMvcEndpoint> endpoints,
|
||||
CorsConfiguration corsConfiguration, HandlerInterceptor securityInterceptor) {
|
||||
super(endpoints, corsConfiguration);
|
||||
setSecurityInterceptor(securityInterceptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessEndpoints(Set<NamedMvcEndpoint> endpoints) {
|
||||
super.postProcessEndpoints(endpoints);
|
||||
Iterator<NamedMvcEndpoint> iterator = endpoints.iterator();
|
||||
HealthMvcEndpoint healthMvcEndpoint = null;
|
||||
while (iterator.hasNext()) {
|
||||
NamedMvcEndpoint endpoint = iterator.next();
|
||||
if (endpoint instanceof HealthMvcEndpoint) {
|
||||
iterator.remove();
|
||||
healthMvcEndpoint = (HealthMvcEndpoint) endpoint;
|
||||
}
|
||||
}
|
||||
if (healthMvcEndpoint != null) {
|
||||
endpoints.add(
|
||||
new CloudFoundryHealthMvcEndpoint(healthMvcEndpoint.getDelegate()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
detectHandlerMethods(new CloudFoundryDiscoveryMvcEndpoint(getEndpoints()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPath(MvcEndpoint endpoint) {
|
||||
if (endpoint instanceof NamedMvcEndpoint) {
|
||||
return "/" + ((NamedMvcEndpoint) endpoint).getName();
|
||||
}
|
||||
return super.getPath(endpoint);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,31 +16,29 @@
|
|||
|
||||
package org.springframework.boot.actuate.cloudfoundry;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
|
||||
|
||||
/**
|
||||
* Extension of {@link HealthMvcEndpoint} for Cloud Foundry. Since security for Cloud
|
||||
* Foundry actuators is already handled by the {@link CloudFoundrySecurityInterceptor},
|
||||
* this endpoint skips the additional security checks done by the regular
|
||||
* {@link HealthMvcEndpoint}.
|
||||
* Extension of {@link HealthWebEndpointExtension} for Cloud Foundry. Since security for
|
||||
* Cloud Foundry actuators is already handled by the
|
||||
* {@link CloudFoundrySecurityInterceptor}, this endpoint skips the additional security
|
||||
* checks done by the regular {@link HealthWebEndpointExtension}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class CloudFoundryHealthMvcEndpoint extends HealthMvcEndpoint {
|
||||
class CloudFoundryHealthMvcEndpoint extends HealthWebEndpointExtension {
|
||||
|
||||
// TODO Port to new infrastructure
|
||||
|
||||
CloudFoundryHealthMvcEndpoint(HealthEndpoint delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean exposeHealthDetails(HttpServletRequest request,
|
||||
Principal principal) {
|
||||
return true;
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// protected boolean exposeHealthDetails(HttpServletRequest request,
|
||||
// Principal principal) {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,6 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
|
|
@ -57,52 +51,54 @@ public class CloudFoundrySecurityInterceptor extends HandlerInterceptorAdapter {
|
|||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
// TODO Port to new infrastructure
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
if (CorsUtils.isPreFlightRequest(request)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (!StringUtils.hasText(this.applicationId)) {
|
||||
throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
|
||||
"Application id is not available");
|
||||
}
|
||||
if (this.cloudFoundrySecurityService == null) {
|
||||
throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
|
||||
"Cloud controller URL is not available");
|
||||
}
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())
|
||||
&& !(handlerMethod.getBean() instanceof MvcEndpoint)) {
|
||||
return true;
|
||||
}
|
||||
MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean();
|
||||
check(request, mvcEndpoint);
|
||||
}
|
||||
catch (CloudFoundryAuthorizationException ex) {
|
||||
logger.error(ex);
|
||||
response.setContentType(MediaType.APPLICATION_JSON.toString());
|
||||
response.getWriter()
|
||||
.write("{\"security_error\":\"" + ex.getMessage() + "\"}");
|
||||
response.setStatus(ex.getStatusCode().value());
|
||||
return false;
|
||||
}
|
||||
// if (CorsUtils.isPreFlightRequest(request)) {
|
||||
// return true;
|
||||
// }
|
||||
// try {
|
||||
// if (!StringUtils.hasText(this.applicationId)) {
|
||||
// throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
|
||||
// "Application id is not available");
|
||||
// }
|
||||
// if (this.cloudFoundrySecurityService == null) {
|
||||
// throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
|
||||
// "Cloud controller URL is not available");
|
||||
// }
|
||||
// HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
// if (HttpMethod.OPTIONS.matches(request.getMethod())
|
||||
// && !(handlerMethod.getBean() instanceof MvcEndpoint)) {
|
||||
// return true;
|
||||
// }
|
||||
// MvcEndpoint mvcEndpoint = (MvcEndpoint) handlerMethod.getBean();
|
||||
// check(request, mvcEndpoint);
|
||||
// }
|
||||
// catch (CloudFoundryAuthorizationException ex) {
|
||||
// logger.error(ex);
|
||||
// response.setContentType(MediaType.APPLICATION_JSON.toString());
|
||||
// response.getWriter()
|
||||
// .write("{\"security_error\":\"" + ex.getMessage() + "\"}");
|
||||
// response.setStatus(ex.getStatusCode().value());
|
||||
// return false;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
private void check(HttpServletRequest request, MvcEndpoint mvcEndpoint)
|
||||
throws Exception {
|
||||
Token token = getToken(request);
|
||||
this.tokenValidator.validate(token);
|
||||
AccessLevel accessLevel = this.cloudFoundrySecurityService
|
||||
.getAccessLevel(token.toString(), this.applicationId);
|
||||
if (!accessLevel.isAccessAllowed(mvcEndpoint.getPath())) {
|
||||
throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED,
|
||||
"Access denied");
|
||||
}
|
||||
accessLevel.put(request);
|
||||
}
|
||||
// private void check(HttpServletRequest request, MvcEndpoint mvcEndpoint)
|
||||
// throws Exception {
|
||||
// Token token = getToken(request);
|
||||
// this.tokenValidator.validate(token);
|
||||
// AccessLevel accessLevel = this.cloudFoundrySecurityService
|
||||
// .getAccessLevel(token.toString(), this.applicationId);
|
||||
// if (!accessLevel.isAccessAllowed(mvcEndpoint.getPath())) {
|
||||
// throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED,
|
||||
// "Access denied");
|
||||
// }
|
||||
// accessLevel.put(request);
|
||||
// }
|
||||
|
||||
private Token getToken(HttpServletRequest request) {
|
||||
String authorization = request.getHeader("Authorization");
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.boot.actuate.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that checks whether or not an endpoint is enabled. Matches if the
|
||||
* value of the {@code endpoints.<name>.enabled} property is {@code true}. Does not match
|
||||
* if the property's value or {@code enabledByDefault} is {@code false}. Otherwise,
|
||||
* matches if the value of the {@code endpoints.enabled} property is {@code true} or if
|
||||
* the property is not configured.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.4
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Conditional(OnEnabledEndpointCondition.class)
|
||||
@Documented
|
||||
public @interface ConditionalOnEnabledEndpoint {
|
||||
|
||||
/**
|
||||
* The name of the endpoint.
|
||||
* @return The name of the endpoint
|
||||
*/
|
||||
String value();
|
||||
|
||||
/**
|
||||
* Returns whether or not the endpoint is enabled by default.
|
||||
* @return {@code true} if the endpoint is enabled by default, otherwise {@code false}
|
||||
*/
|
||||
boolean enabledByDefault() default true;
|
||||
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.condition;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks whether or not an endpoint is enabled.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
class OnEnabledEndpointCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata
|
||||
.getAnnotationAttributes(ConditionalOnEnabledEndpoint.class.getName()));
|
||||
String endpointName = annotationAttributes.getString("value");
|
||||
boolean enabledByDefault = annotationAttributes.getBoolean("enabledByDefault");
|
||||
ConditionOutcome outcome = determineEndpointOutcome(endpointName,
|
||||
enabledByDefault, context);
|
||||
if (outcome != null) {
|
||||
return outcome;
|
||||
}
|
||||
return determineAllEndpointsOutcome(context);
|
||||
}
|
||||
|
||||
private ConditionOutcome determineEndpointOutcome(String endpointName,
|
||||
boolean enabledByDefault, ConditionContext context) {
|
||||
Environment environment = context.getEnvironment();
|
||||
String enabledProperty = "endpoints." + endpointName + ".enabled";
|
||||
if (environment.containsProperty(enabledProperty) || !enabledByDefault) {
|
||||
boolean match = environment.getProperty(enabledProperty, Boolean.class,
|
||||
enabledByDefault);
|
||||
ConditionMessage message = ConditionMessage
|
||||
.forCondition(ConditionalOnEnabledEndpoint.class,
|
||||
"(" + endpointName + ")")
|
||||
.because(match ? "enabled" : "disabled");
|
||||
return new ConditionOutcome(match, message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ConditionOutcome determineAllEndpointsOutcome(ConditionContext context) {
|
||||
boolean match = Boolean.valueOf(
|
||||
context.getEnvironment().getProperty("endpoints.enabled", "true"));
|
||||
ConditionMessage message = ConditionMessage
|
||||
.forCondition(ConditionalOnEnabledEndpoint.class)
|
||||
.because("All endpoints are " + (match ? "enabled" : "disabled")
|
||||
+ " by default");
|
||||
return new ConditionOutcome(match, message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base for {@link Endpoint} implementations.
|
||||
*
|
||||
* @param <T> the endpoint data type
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
*/
|
||||
public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAware {
|
||||
|
||||
private static final Pattern ID_PATTERN = Pattern.compile("\\w+");
|
||||
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
|
||||
* to a URL (e.g. 'foo' is mapped to '/foo').
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Enable the endpoint.
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* Create a new endpoint instance. The endpoint will enabled flag will be based on the
|
||||
* spring {@link Environment} unless explicitly set.
|
||||
* @param id the endpoint ID
|
||||
*/
|
||||
public AbstractEndpoint(String id) {
|
||||
setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new endpoint instance.
|
||||
* @param id the endpoint ID
|
||||
* @param enabled if the endpoint is enabled or not.
|
||||
*/
|
||||
public AbstractEndpoint(String id, boolean enabled) {
|
||||
setId(id);
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
protected final Environment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
Assert.notNull(id, "Id must not be null");
|
||||
Assert.isTrue(ID_PATTERN.matcher(id).matches(),
|
||||
"Id must only contains letters, numbers and '_'");
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return EndpointProperties.isEnabled(this.environment, this.enabled);
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Endpoint} to expose audit events.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Endpoint(id = "auditevents")
|
||||
public class AuditEventsEndpoint {
|
||||
|
||||
private final AuditEventRepository auditEventRepository;
|
||||
|
||||
public AuditEventsEndpoint(AuditEventRepository auditEventRepository) {
|
||||
Assert.notNull(auditEventRepository, "AuditEventRepository must not be null");
|
||||
this.auditEventRepository = auditEventRepository;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public List<AuditEvent> eventsWithPrincipalDateAfterAndType(String principal,
|
||||
Date after, String type) {
|
||||
return this.auditEventRepository.find(principal, after, type);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -26,13 +26,12 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
|||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.endpoint.AutoConfigurationReportEndpoint.Report;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
|
@ -47,19 +46,19 @@ import org.springframework.util.StringUtils;
|
|||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.autoconfig")
|
||||
public class AutoConfigurationReportEndpoint extends AbstractEndpoint<Report> {
|
||||
@Endpoint(id = "autoconfig")
|
||||
public class AutoConfigurationReportEndpoint {
|
||||
|
||||
@Autowired
|
||||
private ConditionEvaluationReport autoConfigurationReport;
|
||||
private final ConditionEvaluationReport conditionEvaluationReport;
|
||||
|
||||
public AutoConfigurationReportEndpoint() {
|
||||
super("autoconfig");
|
||||
public AutoConfigurationReportEndpoint(
|
||||
ConditionEvaluationReport conditionEvaluationReport) {
|
||||
this.conditionEvaluationReport = conditionEvaluationReport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Report invoke() {
|
||||
return new Report(this.autoConfigurationReport);
|
||||
@ReadOperation
|
||||
public Report getEvaluationReport() {
|
||||
return new Report(this.conditionEvaluationReport);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.json.JsonParser;
|
||||
import org.springframework.boot.json.JsonParserFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
|
@ -40,18 +41,13 @@ import org.springframework.util.Assert;
|
|||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.beans")
|
||||
public class BeansEndpoint extends AbstractEndpoint<List<Object>>
|
||||
implements ApplicationContextAware {
|
||||
@Endpoint(id = "beans")
|
||||
public class BeansEndpoint implements ApplicationContextAware {
|
||||
|
||||
private final HierarchyAwareLiveBeansView liveBeansView = new HierarchyAwareLiveBeansView();
|
||||
|
||||
private final JsonParser parser = JsonParserFactory.getJsonParser();
|
||||
|
||||
public BeansEndpoint() {
|
||||
super("beans");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
if (context.getEnvironment()
|
||||
|
|
@ -60,8 +56,8 @@ public class BeansEndpoint extends AbstractEndpoint<List<Object>>
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> invoke() {
|
||||
@ReadOperation
|
||||
public List<Object> beans() {
|
||||
return this.parser.parseList(this.liveBeansView.getSnapshotAsJson());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
|||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
|
@ -61,9 +63,9 @@ import org.springframework.util.StringUtils;
|
|||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.configprops")
|
||||
public class ConfigurationPropertiesReportEndpoint
|
||||
extends AbstractEndpoint<Map<String, Object>> implements ApplicationContextAware {
|
||||
@Endpoint(id = "configprops")
|
||||
@ConfigurationProperties("endpoints.configprops")
|
||||
public class ConfigurationPropertiesReportEndpoint implements ApplicationContextAware {
|
||||
|
||||
private static final String CGLIB_FILTER_ID = "cglibFilter";
|
||||
|
||||
|
|
@ -71,10 +73,6 @@ public class ConfigurationPropertiesReportEndpoint
|
|||
|
||||
private ApplicationContext context;
|
||||
|
||||
public ConfigurationPropertiesReportEndpoint() {
|
||||
super("configprops");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
|
|
@ -84,8 +82,8 @@ public class ConfigurationPropertiesReportEndpoint
|
|||
this.sanitizer.setKeysToSanitize(keysToSanitize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, Object> configurationProperties() {
|
||||
return extract(this.context);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2015 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.boot.actuate.endpoint;
|
||||
|
||||
/**
|
||||
* An endpoint that can be used to expose useful information to operations. Usually
|
||||
* exposed via Spring MVC but could also be exposed using some other technique. Consider
|
||||
* extending {@link AbstractEndpoint} if you are developing your own endpoint.
|
||||
*
|
||||
* @param <T> the endpoint data type
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Christian Dupuis
|
||||
* @see AbstractEndpoint
|
||||
*/
|
||||
public interface Endpoint<T> {
|
||||
|
||||
/**
|
||||
* The logical ID of the endpoint. Must only contain simple letters, numbers and '_'
|
||||
* characters (i.e. a {@literal "\w"} regex).
|
||||
* @return the endpoint ID
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Return if the endpoint is enabled.
|
||||
* @return if the endpoint is enabled
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Called to invoke the endpoint.
|
||||
* @return the results of the invocation
|
||||
*/
|
||||
T invoke();
|
||||
|
||||
}
|
||||
|
|
@ -21,11 +21,15 @@ import java.util.Arrays;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint.EnvironmentDescriptor;
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint.EnvironmentDescriptor.PropertySourceDescriptor;
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint.EnvironmentDescriptor.PropertySourceDescriptor.PropertyValueDescriptor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.Selector;
|
||||
import org.springframework.boot.origin.OriginLookup;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
|
@ -37,6 +41,9 @@ import org.springframework.core.env.PropertySource;
|
|||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.core.env.PropertySourcesPropertyResolver;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* {@link Endpoint} to expose {@link ConfigurableEnvironment environment} information.
|
||||
|
|
@ -46,42 +53,54 @@ import org.springframework.core.env.StandardEnvironment;
|
|||
* @author Christian Dupuis
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.env")
|
||||
public class EnvironmentEndpoint extends AbstractEndpoint<EnvironmentDescriptor> {
|
||||
@Endpoint(id = "env")
|
||||
public class EnvironmentEndpoint {
|
||||
|
||||
private final Sanitizer sanitizer = new Sanitizer();
|
||||
|
||||
/**
|
||||
* Create a new {@link EnvironmentEndpoint} instance.
|
||||
*/
|
||||
public EnvironmentEndpoint() {
|
||||
super("env");
|
||||
private final Environment environment;
|
||||
|
||||
public EnvironmentEndpoint(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public void setKeysToSanitize(String... keysToSanitize) {
|
||||
this.sanitizer.setKeysToSanitize(keysToSanitize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnvironmentDescriptor invoke() {
|
||||
@ReadOperation
|
||||
public EnvironmentDescriptor environment(String pattern) {
|
||||
if (StringUtils.hasText(pattern)) {
|
||||
return environment(Pattern.compile(pattern).asPredicate());
|
||||
}
|
||||
return environment((name) -> true);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Object getEnvironmentEntry(@Selector String toMatch) {
|
||||
return environment((name) -> toMatch.equals(name));
|
||||
}
|
||||
|
||||
private EnvironmentDescriptor environment(Predicate<String> propertyNamePredicate) {
|
||||
PropertyResolver resolver = getResolver();
|
||||
List<PropertySourceDescriptor> propertySources = new ArrayList<>();
|
||||
getPropertySourcesAsMap().forEach((sourceName, source) -> {
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
propertySources.add(describeSource(sourceName,
|
||||
(EnumerablePropertySource<?>) source, resolver));
|
||||
propertySources.add(
|
||||
describeSource(sourceName, (EnumerablePropertySource<?>) source,
|
||||
resolver, propertyNamePredicate));
|
||||
}
|
||||
});
|
||||
return new EnvironmentDescriptor(
|
||||
Arrays.asList(getEnvironment().getActiveProfiles()), propertySources);
|
||||
Arrays.asList(this.environment.getActiveProfiles()), propertySources);
|
||||
}
|
||||
|
||||
private PropertySourceDescriptor describeSource(String sourceName,
|
||||
EnumerablePropertySource<?> source, PropertyResolver resolver) {
|
||||
EnumerablePropertySource<?> source, PropertyResolver resolver,
|
||||
Predicate<String> namePredicate) {
|
||||
Map<String, PropertyValueDescriptor> properties = new LinkedHashMap<>();
|
||||
for (String name : source.getPropertyNames()) {
|
||||
properties.put(name, describeValueOf(name, source, resolver));
|
||||
}
|
||||
Stream.of(source.getPropertyNames()).filter(namePredicate).forEach(
|
||||
(name) -> properties.put(name, describeValueOf(name, source, resolver)));
|
||||
return new PropertySourceDescriptor(sourceName, properties);
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +113,7 @@ public class EnvironmentEndpoint extends AbstractEndpoint<EnvironmentDescriptor>
|
|||
return new PropertyValueDescriptor(sanitize(name, resolved), origin);
|
||||
}
|
||||
|
||||
public PropertyResolver getResolver() {
|
||||
private PropertyResolver getResolver() {
|
||||
PlaceholderSanitizingPropertyResolver resolver = new PlaceholderSanitizingPropertyResolver(
|
||||
getPropertySources(), this.sanitizer);
|
||||
resolver.setIgnoreUnresolvableNestedPlaceholders(true);
|
||||
|
|
@ -111,9 +130,8 @@ public class EnvironmentEndpoint extends AbstractEndpoint<EnvironmentDescriptor>
|
|||
|
||||
private MutablePropertySources getPropertySources() {
|
||||
MutablePropertySources sources;
|
||||
Environment environment = getEnvironment();
|
||||
if (environment != null && environment instanceof ConfigurableEnvironment) {
|
||||
sources = ((ConfigurableEnvironment) environment).getPropertySources();
|
||||
if (this.environment instanceof ConfigurableEnvironment) {
|
||||
sources = ((ConfigurableEnvironment) this.environment).getPropertySources();
|
||||
}
|
||||
else {
|
||||
sources = new StandardEnvironment().getPropertySources();
|
||||
|
|
@ -239,4 +257,17 @@ public class EnvironmentEndpoint extends AbstractEndpoint<EnvironmentDescriptor>
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when the specified property cannot be found.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such property")
|
||||
public static class NoSuchPropertyException extends RuntimeException {
|
||||
|
||||
public NoSuchPropertyException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ import org.flywaydb.core.api.MigrationInfo;
|
|||
import org.flywaydb.core.api.MigrationState;
|
||||
import org.flywaydb.core.api.MigrationType;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.FlywayEndpoint.FlywayReport;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -40,19 +40,18 @@ import org.springframework.util.Assert;
|
|||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.flyway")
|
||||
public class FlywayEndpoint extends AbstractEndpoint<Map<String, FlywayReport>> {
|
||||
@Endpoint(id = "flyway")
|
||||
public class FlywayEndpoint {
|
||||
|
||||
private final Map<String, Flyway> flyways;
|
||||
|
||||
public FlywayEndpoint(Map<String, Flyway> flyways) {
|
||||
super("flyway");
|
||||
Assert.notEmpty(flyways, "Flyways must be specified");
|
||||
this.flyways = flyways;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, FlywayReport> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, FlywayReport> flywayReports() {
|
||||
Map<String, FlywayReport> reports = new HashMap<>();
|
||||
for (Map.Entry<String, Flyway> entry : this.flyways.entrySet()) {
|
||||
reports.put(entry.getKey(),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
|||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -32,16 +33,11 @@ import org.springframework.util.Assert;
|
|||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.health")
|
||||
public class HealthEndpoint extends AbstractEndpoint<Health> {
|
||||
@Endpoint(id = "health")
|
||||
public class HealthEndpoint {
|
||||
|
||||
private final HealthIndicator healthIndicator;
|
||||
|
||||
/**
|
||||
* Time to live for cached result, in milliseconds.
|
||||
*/
|
||||
private long timeToLive = 1000;
|
||||
|
||||
/**
|
||||
* Create a new {@link HealthEndpoint} instance.
|
||||
* @param healthAggregator the health aggregator
|
||||
|
|
@ -49,7 +45,6 @@ public class HealthEndpoint extends AbstractEndpoint<Health> {
|
|||
*/
|
||||
public HealthEndpoint(HealthAggregator healthAggregator,
|
||||
Map<String, HealthIndicator> healthIndicators) {
|
||||
super("health");
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
|
||||
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
|
||||
|
|
@ -60,28 +55,8 @@ public class HealthEndpoint extends AbstractEndpoint<Health> {
|
|||
this.healthIndicator = healthIndicator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time to live for cached result. This is particularly useful to cache the result of
|
||||
* this endpoint to prevent a DOS attack if it is accessed anonymously.
|
||||
* @return time to live in milliseconds (default 1000)
|
||||
*/
|
||||
public long getTimeToLive() {
|
||||
return this.timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time to live for cached results.
|
||||
* @param timeToLive the time to live in milliseconds
|
||||
*/
|
||||
public void setTimeToLive(long timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke all {@link HealthIndicator} delegates and collect their health information.
|
||||
*/
|
||||
@Override
|
||||
public Health invoke() {
|
||||
@ReadOperation
|
||||
public Health health() {
|
||||
return this.healthIndicator.health();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import java.util.Map;
|
|||
|
||||
import org.springframework.boot.actuate.info.Info;
|
||||
import org.springframework.boot.actuate.info.InfoContributor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -31,8 +32,8 @@ import org.springframework.util.Assert;
|
|||
* @author Meang Akira Tanaka
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.info")
|
||||
public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
||||
@Endpoint(id = "info")
|
||||
public class InfoEndpoint {
|
||||
|
||||
private final List<InfoContributor> infoContributors;
|
||||
|
||||
|
|
@ -41,13 +42,12 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
|||
* @param infoContributors the info contributors to use
|
||||
*/
|
||||
public InfoEndpoint(List<InfoContributor> infoContributors) {
|
||||
super("info");
|
||||
Assert.notNull(infoContributors, "Info contributors must not be null");
|
||||
this.infoContributors = infoContributors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, Object> info() {
|
||||
Info.Builder builder = new Info.Builder();
|
||||
for (InfoContributor contributor : this.infoContributors) {
|
||||
contributor.contribute(builder);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -35,8 +34,8 @@ import liquibase.database.DatabaseFactory;
|
|||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.integration.spring.SpringLiquibase;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint.LiquibaseReport;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -46,23 +45,18 @@ import org.springframework.util.StringUtils;
|
|||
* @author Eddú Meléndez
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.liquibase")
|
||||
public class LiquibaseEndpoint extends AbstractEndpoint<Map<String, LiquibaseReport>> {
|
||||
@Endpoint(id = "liquibase")
|
||||
public class LiquibaseEndpoint {
|
||||
|
||||
private final Map<String, SpringLiquibase> liquibases;
|
||||
|
||||
public LiquibaseEndpoint(SpringLiquibase liquibase) {
|
||||
this(Collections.singletonMap("default", liquibase));
|
||||
}
|
||||
|
||||
public LiquibaseEndpoint(Map<String, SpringLiquibase> liquibases) {
|
||||
super("liquibase");
|
||||
Assert.notEmpty(liquibases, "Liquibases must be specified");
|
||||
this.liquibases = liquibases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, LiquibaseReport> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, LiquibaseReport> liquibaseReports() {
|
||||
Map<String, LiquibaseReport> reports = new HashMap<>();
|
||||
DatabaseFactory factory = DatabaseFactory.getInstance();
|
||||
StandardChangeLogHistoryService service = new StandardChangeLogHistoryService();
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@ import java.util.NavigableSet;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.Selector;
|
||||
import org.springframework.boot.endpoint.WriteOperation;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.boot.logging.LoggerConfiguration;
|
||||
import org.springframework.boot.logging.LoggingSystem;
|
||||
|
|
@ -37,8 +40,8 @@ import org.springframework.util.Assert;
|
|||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.loggers")
|
||||
public class LoggersEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
||||
@Endpoint(id = "loggers")
|
||||
public class LoggersEndpoint {
|
||||
|
||||
private final LoggingSystem loggingSystem;
|
||||
|
||||
|
|
@ -47,13 +50,12 @@ public class LoggersEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
|||
* @param loggingSystem the logging system to expose
|
||||
*/
|
||||
public LoggersEndpoint(LoggingSystem loggingSystem) {
|
||||
super("loggers");
|
||||
Assert.notNull(loggingSystem, "LoggingSystem must not be null");
|
||||
this.loggingSystem = loggingSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, Object> loggers() {
|
||||
Collection<LoggerConfiguration> configurations = this.loggingSystem
|
||||
.getLoggerConfigurations();
|
||||
if (configurations == null) {
|
||||
|
|
@ -65,6 +67,20 @@ public class LoggersEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
|||
return result;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public LoggerLevels loggerLevels(@Selector String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
LoggerConfiguration configuration = this.loggingSystem
|
||||
.getLoggerConfiguration(name);
|
||||
return (configuration == null ? null : new LoggerLevels(configuration));
|
||||
}
|
||||
|
||||
@WriteOperation
|
||||
public void configureLogLevel(@Selector String name, LogLevel configuredLevel) {
|
||||
Assert.notNull(name, "Name must not be empty");
|
||||
this.loggingSystem.setLogLevel(name, configuredLevel);
|
||||
}
|
||||
|
||||
private NavigableSet<LogLevel> getLevels() {
|
||||
Set<LogLevel> levels = this.loggingSystem.getSupportedLogLevels();
|
||||
return new TreeSet<>(levels).descendingSet();
|
||||
|
|
@ -79,18 +95,6 @@ public class LoggersEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
|||
return loggers;
|
||||
}
|
||||
|
||||
public LoggerLevels invoke(String name) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
LoggerConfiguration configuration = this.loggingSystem
|
||||
.getLoggerConfiguration(name);
|
||||
return (configuration == null ? null : new LoggerLevels(configuration));
|
||||
}
|
||||
|
||||
public void setLogLevel(String name, LogLevel level) {
|
||||
Assert.notNull(name, "Name must not be empty");
|
||||
this.loggingSystem.setLogLevel(name, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Levels configured for a given logger exposed in a JSON friendly way.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,19 +22,24 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.Metric;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.Selector;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Endpoint} to expose a collection of {@link PublicMetrics}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.metrics")
|
||||
public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
||||
@Endpoint(id = "metrics")
|
||||
public class MetricsEndpoint {
|
||||
|
||||
private final List<PublicMetrics> publicMetrics;
|
||||
|
||||
|
|
@ -52,7 +57,6 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
|||
* {@link AnnotationAwareOrderComparator}.
|
||||
*/
|
||||
public MetricsEndpoint(Collection<PublicMetrics> publicMetrics) {
|
||||
super("metrics");
|
||||
Assert.notNull(publicMetrics, "PublicMetrics must not be null");
|
||||
this.publicMetrics = new ArrayList<>(publicMetrics);
|
||||
AnnotationAwareOrderComparator.sort(this.publicMetrics);
|
||||
|
|
@ -67,14 +71,31 @@ public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {
|
|||
this.publicMetrics.remove(metrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, Object> metrics(String pattern) {
|
||||
return metrics(StringUtils.hasText(pattern)
|
||||
? Pattern.compile(pattern).asPredicate() : (name) -> true);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, Object> metricNamed(@Selector String requiredName) {
|
||||
Map<String, Object> metrics = metrics((name) -> name.equals(requiredName));
|
||||
if (metrics.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private Map<String, Object> metrics(Predicate<String> namePredicate) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
List<PublicMetrics> metrics = new ArrayList<>(this.publicMetrics);
|
||||
for (PublicMetrics publicMetric : metrics) {
|
||||
try {
|
||||
for (Metric<?> metric : publicMetric.metrics()) {
|
||||
result.put(metric.getName(), metric.getValue());
|
||||
if (namePredicate.test(metric.getName())
|
||||
&& metric.getValue() != null) {
|
||||
result.put(metric.getName(), metric.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class MetricsEndpointMetricReader implements MetricReader {
|
|||
@Override
|
||||
public Metric<?> findOne(String metricName) {
|
||||
Metric<Number> metric = null;
|
||||
Object value = this.endpoint.invoke().get(metricName);
|
||||
Object value = this.endpoint.metrics(null).get(metricName);
|
||||
if (value != null) {
|
||||
metric = new Metric<>(metricName, (Number) value);
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ public class MetricsEndpointMetricReader implements MetricReader {
|
|||
@Override
|
||||
public Iterable<Metric<?>> findAll() {
|
||||
List<Metric<?>> metrics = new ArrayList<>();
|
||||
Map<String, Object> values = this.endpoint.invoke();
|
||||
Map<String, Object> values = this.endpoint.metrics(null);
|
||||
Date timestamp = new Date();
|
||||
for (Entry<String, Object> entry : values.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
|
|
@ -66,7 +66,7 @@ public class MetricsEndpointMetricReader implements MetricReader {
|
|||
|
||||
@Override
|
||||
public long count() {
|
||||
return this.endpoint.invoke().size();
|
||||
return this.endpoint.metrics(null).size();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.endpoint.mvc;
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
|
|
@ -32,8 +32,7 @@ import java.util.regex.PatternSyntaxException;
|
|||
* @author Phillip Webb
|
||||
* @author Sergei Egorov
|
||||
* @author Andy Wilkinson
|
||||
* @author Dylian Bego
|
||||
* @since 1.3.0
|
||||
* @since 2.0.0
|
||||
*/
|
||||
abstract class NamePatternFilter<T> {
|
||||
|
||||
|
|
@ -25,7 +25,8 @@ import java.util.Map.Entry;
|
|||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
|
@ -38,9 +39,8 @@ import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
|
|||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.mappings")
|
||||
public class RequestMappingEndpoint extends AbstractEndpoint<Map<String, Object>>
|
||||
implements ApplicationContextAware {
|
||||
@Endpoint(id = "mappings")
|
||||
public class RequestMappingEndpoint implements ApplicationContextAware {
|
||||
|
||||
private List<AbstractUrlHandlerMapping> handlerMappings = Collections.emptyList();
|
||||
|
||||
|
|
@ -49,10 +49,6 @@ public class RequestMappingEndpoint extends AbstractEndpoint<Map<String, Object>
|
|||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public RequestMappingEndpoint() {
|
||||
super("mappings");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
|
|
@ -75,8 +71,8 @@ public class RequestMappingEndpoint extends AbstractEndpoint<Map<String, Object>
|
|||
this.methodMappings = methodMappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
@ReadOperation
|
||||
public Map<String, Object> mappings() {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
extractHandlerMappings(this.handlerMappings, result);
|
||||
extractHandlerMappings(this.applicationContext, result);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import java.util.Collections;
|
|||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.WriteOperation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
|
@ -32,9 +33,8 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.shutdown")
|
||||
public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
|
||||
implements ApplicationContextAware {
|
||||
@Endpoint(id = "shutdown", enabledByDefault = false)
|
||||
public class ShutdownEndpoint implements ApplicationContextAware {
|
||||
|
||||
private static final Map<String, Object> NO_CONTEXT_MESSAGE = Collections
|
||||
.unmodifiableMap(Collections.<String, Object>singletonMap("message",
|
||||
|
|
@ -46,15 +46,8 @@ public class ShutdownEndpoint extends AbstractEndpoint<Map<String, Object>>
|
|||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
/**
|
||||
* Create a new {@link ShutdownEndpoint} instance.
|
||||
*/
|
||||
public ShutdownEndpoint() {
|
||||
super("shutdown", false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invoke() {
|
||||
@WriteOperation
|
||||
public Map<String, Object> shutdown() {
|
||||
if (this.context == null) {
|
||||
return NO_CONTEXT_MESSAGE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,25 +21,19 @@ import java.lang.management.ThreadInfo;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
|
||||
/**
|
||||
* {@link Endpoint} to expose thread info.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.dump")
|
||||
public class DumpEndpoint extends AbstractEndpoint<List<ThreadInfo>> {
|
||||
@Endpoint(id = "threaddump")
|
||||
public class ThreadDumpEndpoint {
|
||||
|
||||
/**
|
||||
* Create a new {@link DumpEndpoint} instance.
|
||||
*/
|
||||
public DumpEndpoint() {
|
||||
super("dump");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThreadInfo> invoke() {
|
||||
@ReadOperation
|
||||
public List<ThreadInfo> threadDump() {
|
||||
return Arrays
|
||||
.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true, true));
|
||||
}
|
||||
|
|
@ -20,7 +20,8 @@ import java.util.List;
|
|||
|
||||
import org.springframework.boot.actuate.trace.Trace;
|
||||
import org.springframework.boot.actuate.trace.TraceRepository;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.endpoint.Endpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -28,8 +29,8 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.trace")
|
||||
public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
|
||||
@Endpoint(id = "trace")
|
||||
public class TraceEndpoint {
|
||||
|
||||
private final TraceRepository repository;
|
||||
|
||||
|
|
@ -38,13 +39,12 @@ public class TraceEndpoint extends AbstractEndpoint<List<Trace>> {
|
|||
* @param repository the trace repository
|
||||
*/
|
||||
public TraceEndpoint(TraceRepository repository) {
|
||||
super("trace");
|
||||
Assert.notNull(repository, "Repository must not be null");
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Trace> invoke() {
|
||||
@ReadOperation
|
||||
public List<Trace> traces() {
|
||||
return this.repository.findAll();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointProperties;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link JmxEndpoint} implementations without a backing
|
||||
* {@link Endpoint}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public abstract class AbstractJmxEndpoint implements JmxEndpoint, EnvironmentAware {
|
||||
|
||||
private final DataConverter dataConverter;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Enable the endpoint.
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
public AbstractJmxEndpoint(ObjectMapper objectMapper) {
|
||||
this.dataConverter = new DataConverter(objectMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
protected final Environment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return EndpointProperties.isEnabled(this.environment, this.enabled);
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentity() {
|
||||
return ObjectUtils.getIdentityHexString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Class<? extends Endpoint> getEndpointType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given data into JSON.
|
||||
* @param data the source data
|
||||
* @return the JSON representation
|
||||
*/
|
||||
protected Object convert(Object data) {
|
||||
return this.dataConverter.convert(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link JmxEndpoint} for {@link AuditEventRepository}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.auditevents")
|
||||
public class AuditEventsJmxEndpoint extends AbstractJmxEndpoint {
|
||||
|
||||
private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||
|
||||
private final AuditEventRepository auditEventRepository;
|
||||
|
||||
public AuditEventsJmxEndpoint(ObjectMapper objectMapper,
|
||||
AuditEventRepository auditEventRepository) {
|
||||
super(objectMapper);
|
||||
Assert.notNull(auditEventRepository, "AuditEventRepository must not be null");
|
||||
this.auditEventRepository = auditEventRepository;
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Retrieves a list of audit events meeting the given criteria")
|
||||
public Object getData(String dateAfter) {
|
||||
List<AuditEvent> auditEvents = this.auditEventRepository
|
||||
.find(parseDate(dateAfter));
|
||||
return convert(auditEvents);
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Retrieves a list of audit events meeting the given criteria")
|
||||
public Object getData(String dateAfter, String principal) {
|
||||
List<AuditEvent> auditEvents = this.auditEventRepository.find(principal,
|
||||
parseDate(dateAfter));
|
||||
return convert(auditEvents);
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Retrieves a list of audit events meeting the given criteria")
|
||||
public Object getData(String principal, String dateAfter, String type) {
|
||||
List<AuditEvent> auditEvents = this.auditEventRepository.find(principal,
|
||||
parseDate(dateAfter), type);
|
||||
return convert(auditEvents);
|
||||
}
|
||||
|
||||
private Date parseDate(String date) {
|
||||
try {
|
||||
if (StringUtils.hasLength(date)) {
|
||||
return new SimpleDateFormat(DATE_FORMAT).parse(date);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.jmx.JmxEndpointExtension;
|
||||
|
||||
/**
|
||||
* JMX-specific extension of the {@link AuditEventsEndpoint}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@JmxEndpointExtension(endpoint = AuditEventsEndpoint.class)
|
||||
public class AuditEventsJmxEndpointExtension {
|
||||
|
||||
private final AuditEventsEndpoint delegate;
|
||||
|
||||
public AuditEventsJmxEndpointExtension(AuditEventsEndpoint delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public List<AuditEvent> eventsWithDateAfter(Date dateAfter) {
|
||||
return this.delegate.eventsWithPrincipalDateAfterAndType(null, dateAfter, null);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public List<AuditEvent> eventsWithPrincipalAndDateAfter(String principal,
|
||||
Date dateAfter) {
|
||||
return this.delegate.eventsWithPrincipalDateAfterAndType(principal, dateAfter,
|
||||
null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Internal converter that uses an {@link ObjectMapper} to convert to JSON.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DataConverter {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final JavaType listObject;
|
||||
|
||||
private final JavaType mapStringObject;
|
||||
|
||||
DataConverter(ObjectMapper objectMapper) {
|
||||
this.objectMapper = (objectMapper == null ? new ObjectMapper() : objectMapper);
|
||||
this.listObject = this.objectMapper.getTypeFactory()
|
||||
.constructParametricType(List.class, Object.class);
|
||||
this.mapStringObject = this.objectMapper.getTypeFactory()
|
||||
.constructParametricType(Map.class, String.class, Object.class);
|
||||
|
||||
}
|
||||
|
||||
public Object convert(Object data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
if (data instanceof String) {
|
||||
return data;
|
||||
}
|
||||
if (data.getClass().isArray() || data instanceof List) {
|
||||
return this.objectMapper.convertValue(data, this.listObject);
|
||||
}
|
||||
return this.objectMapper.convertValue(data, this.mapStringObject);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
|
||||
/**
|
||||
* Simple wrapper around {@link Endpoint} implementations that provide actuator data of
|
||||
* some sort.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class DataEndpointMBean extends EndpointMBean {
|
||||
|
||||
/**
|
||||
* Create a new {@link DataEndpointMBean} instance.
|
||||
* @param beanName the bean name
|
||||
* @param endpoint the endpoint to wrap
|
||||
* @param objectMapper the {@link ObjectMapper} used to convert the payload
|
||||
*/
|
||||
public DataEndpointMBean(String beanName, Endpoint<?> endpoint,
|
||||
ObjectMapper objectMapper) {
|
||||
super(beanName, endpoint, objectMapper);
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Invoke the underlying endpoint")
|
||||
public Object getData() {
|
||||
return convert(getEndpoint().invoke());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Base for adapters that convert {@link Endpoint} implementations to {@link JmxEndpoint}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
* @author Phillip Webb
|
||||
* @see JmxEndpoint
|
||||
* @see DataEndpointMBean
|
||||
*/
|
||||
public abstract class EndpointMBean implements JmxEndpoint {
|
||||
|
||||
private final DataConverter dataConverter;
|
||||
|
||||
private final Endpoint<?> endpoint;
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointMBean} instance.
|
||||
* @param beanName the bean name
|
||||
* @param endpoint the endpoint to wrap
|
||||
* @param objectMapper the {@link ObjectMapper} used to convert the payload
|
||||
*/
|
||||
public EndpointMBean(String beanName, Endpoint<?> endpoint,
|
||||
ObjectMapper objectMapper) {
|
||||
this.dataConverter = new DataConverter(objectMapper);
|
||||
Assert.notNull(beanName, "BeanName must not be null");
|
||||
Assert.notNull(endpoint, "Endpoint must not be null");
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Returns the class of the underlying endpoint")
|
||||
public String getEndpointClass() {
|
||||
return ClassUtils.getQualifiedName(getEndpointType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.endpoint.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentity() {
|
||||
return ObjectUtils.getIdentityHexString(getEndpoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Class<? extends Endpoint> getEndpointType() {
|
||||
return getEndpoint().getClass();
|
||||
}
|
||||
|
||||
public Endpoint<?> getEndpoint() {
|
||||
return this.endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given data into JSON.
|
||||
* @param data the source data
|
||||
* @return the JSON representation
|
||||
*/
|
||||
protected Object convert(Object data) {
|
||||
return this.dataConverter.convert(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.jmx.export.MBeanExportException;
|
||||
import org.springframework.jmx.export.MBeanExporter;
|
||||
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
|
||||
import org.springframework.jmx.export.metadata.InvalidMetadataException;
|
||||
import org.springframework.jmx.export.metadata.JmxAttributeSource;
|
||||
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
|
||||
import org.springframework.jmx.export.naming.SelfNaming;
|
||||
import org.springframework.jmx.support.ObjectNameManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link SmartLifecycle} bean that registers all known {@link Endpoint}s with an
|
||||
* {@link MBeanServer} using the {@link MBeanExporter} located from the application
|
||||
* context.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class EndpointMBeanExporter extends MBeanExporter
|
||||
implements SmartLifecycle, ApplicationContextAware {
|
||||
|
||||
/**
|
||||
* The default JMX domain.
|
||||
*/
|
||||
public static final String DEFAULT_DOMAIN = "org.springframework.boot";
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EndpointMBeanExporter.class);
|
||||
|
||||
private final AnnotationJmxAttributeSource attributeSource = new EndpointJmxAttributeSource();
|
||||
|
||||
private final MetadataMBeanInfoAssembler assembler = new MetadataMBeanInfoAssembler(
|
||||
this.attributeSource);
|
||||
|
||||
private final MetadataNamingStrategy defaultNamingStrategy = new MetadataNamingStrategy(
|
||||
this.attributeSource);
|
||||
|
||||
private final Set<Class<?>> registeredEndpoints = new HashSet<>();
|
||||
|
||||
private volatile boolean autoStartup = true;
|
||||
|
||||
private volatile int phase = 0;
|
||||
|
||||
private volatile boolean running = false;
|
||||
|
||||
private final ReentrantLock lifecycleLock = new ReentrantLock();
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
private String domain = DEFAULT_DOMAIN;
|
||||
|
||||
private boolean ensureUniqueRuntimeObjectNames = false;
|
||||
|
||||
private Properties objectNameStaticProperties = new Properties();
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointMBeanExporter} instance.
|
||||
*/
|
||||
public EndpointMBeanExporter() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointMBeanExporter} instance.
|
||||
* @param objectMapper the object mapper
|
||||
*/
|
||||
public EndpointMBeanExporter(ObjectMapper objectMapper) {
|
||||
this.objectMapper = (objectMapper == null ? new ObjectMapper() : objectMapper);
|
||||
setAutodetect(false);
|
||||
setNamingStrategy(this.defaultNamingStrategy);
|
||||
setAssembler(this.assembler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
super.setBeanFactory(beanFactory);
|
||||
if (beanFactory instanceof ListableBeanFactory) {
|
||||
this.beanFactory = (ListableBeanFactory) beanFactory;
|
||||
}
|
||||
else {
|
||||
logger.warn("EndpointMBeanExporter not running in a ListableBeanFactory: "
|
||||
+ "autodetection of Endpoints not available.");
|
||||
}
|
||||
}
|
||||
|
||||
public void setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnsureUniqueRuntimeObjectNames(
|
||||
boolean ensureUniqueRuntimeObjectNames) {
|
||||
super.setEnsureUniqueRuntimeObjectNames(ensureUniqueRuntimeObjectNames);
|
||||
this.ensureUniqueRuntimeObjectNames = ensureUniqueRuntimeObjectNames;
|
||||
}
|
||||
|
||||
public void setObjectNameStaticProperties(Properties objectNameStaticProperties) {
|
||||
this.objectNameStaticProperties = objectNameStaticProperties;
|
||||
}
|
||||
|
||||
protected void doStart() {
|
||||
locateAndRegisterEndpoints();
|
||||
}
|
||||
|
||||
protected void locateAndRegisterEndpoints() {
|
||||
registerJmxEndpoints(this.beanFactory.getBeansOfType(JmxEndpoint.class));
|
||||
registerEndpoints(this.beanFactory.getBeansOfType(Endpoint.class));
|
||||
}
|
||||
|
||||
private void registerJmxEndpoints(Map<String, JmxEndpoint> endpoints) {
|
||||
for (Map.Entry<String, JmxEndpoint> entry : endpoints.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
JmxEndpoint endpoint = entry.getValue();
|
||||
Class<?> type = (endpoint.getEndpointType() != null
|
||||
? endpoint.getEndpointType() : endpoint.getClass());
|
||||
if (!this.registeredEndpoints.contains(type) && endpoint.isEnabled()) {
|
||||
try {
|
||||
registerBeanNameOrInstance(endpoint, name);
|
||||
}
|
||||
catch (MBeanExportException ex) {
|
||||
logger.error("Could not register JmxEndpoint [" + name + "]", ex);
|
||||
}
|
||||
this.registeredEndpoints.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void registerEndpoints(Map<String, Endpoint> endpoints) {
|
||||
for (Map.Entry<String, Endpoint> entry : endpoints.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Endpoint endpoint = entry.getValue();
|
||||
Class<?> type = endpoint.getClass();
|
||||
if (!this.registeredEndpoints.contains(type) && endpoint.isEnabled()) {
|
||||
registerEndpoint(name, endpoint);
|
||||
this.registeredEndpoints.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a regular {@link Endpoint} with the {@link MBeanServer}.
|
||||
* @param beanName the bean name
|
||||
* @param endpoint the endpoint to register
|
||||
* @deprecated as of 1.5 in favor of direct {@link JmxEndpoint} registration or
|
||||
* {@link #adaptEndpoint(String, Endpoint)}
|
||||
*/
|
||||
@Deprecated
|
||||
protected void registerEndpoint(String beanName, Endpoint<?> endpoint) {
|
||||
Class<?> type = endpoint.getClass();
|
||||
if (isAnnotatedWithManagedResource(type) || (type.isMemberClass()
|
||||
&& isAnnotatedWithManagedResource(type.getEnclosingClass()))) {
|
||||
// Endpoint is directly managed
|
||||
return;
|
||||
}
|
||||
JmxEndpoint jmxEndpoint = adaptEndpoint(beanName, endpoint);
|
||||
try {
|
||||
registerBeanNameOrInstance(jmxEndpoint, beanName);
|
||||
}
|
||||
catch (MBeanExportException ex) {
|
||||
logger.error("Could not register MBean for endpoint [" + beanName + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAnnotatedWithManagedResource(Class<?> type) {
|
||||
return AnnotationUtils.findAnnotation(type, ManagedResource.class) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapt the given {@link Endpoint} to a {@link JmxEndpoint}.
|
||||
* @param beanName the bean name
|
||||
* @param endpoint the endpoint to adapt
|
||||
* @return an adapted endpoint
|
||||
*/
|
||||
protected JmxEndpoint adaptEndpoint(String beanName, Endpoint<?> endpoint) {
|
||||
if (endpoint instanceof ShutdownEndpoint) {
|
||||
return new ShutdownEndpointMBean(beanName, endpoint, this.objectMapper);
|
||||
}
|
||||
if (endpoint instanceof LoggersEndpoint) {
|
||||
return new LoggersEndpointMBean(beanName, endpoint, this.objectMapper);
|
||||
}
|
||||
return new DataEndpointMBean(beanName, endpoint, this.objectMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ObjectName getObjectName(Object bean, String beanKey)
|
||||
throws MalformedObjectNameException {
|
||||
if (bean instanceof SelfNaming) {
|
||||
return ((SelfNaming) bean).getObjectName();
|
||||
}
|
||||
if (bean instanceof JmxEndpoint) {
|
||||
return getObjectName((JmxEndpoint) bean, beanKey);
|
||||
}
|
||||
return this.defaultNamingStrategy.getObjectName(bean, beanKey);
|
||||
}
|
||||
|
||||
private ObjectName getObjectName(JmxEndpoint jmxEndpoint, String beanKey)
|
||||
throws MalformedObjectNameException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(this.domain);
|
||||
builder.append(":type=Endpoint");
|
||||
builder.append(",name=" + beanKey);
|
||||
if (parentContextContainsSameBean(this.applicationContext, beanKey)) {
|
||||
builder.append(",context="
|
||||
+ ObjectUtils.getIdentityHexString(this.applicationContext));
|
||||
}
|
||||
if (this.ensureUniqueRuntimeObjectNames) {
|
||||
builder.append(",identity=" + jmxEndpoint.getIdentity());
|
||||
}
|
||||
builder.append(getStaticNames());
|
||||
return ObjectNameManager.getInstance(builder.toString());
|
||||
}
|
||||
|
||||
private boolean parentContextContainsSameBean(ApplicationContext applicationContext,
|
||||
String beanKey) {
|
||||
if (applicationContext.getParent() != null) {
|
||||
try {
|
||||
Object bean = this.applicationContext.getParent().getBean(beanKey);
|
||||
if (bean instanceof Endpoint || bean instanceof JmxEndpoint) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
// Ignore and continue
|
||||
}
|
||||
return parentContextContainsSameBean(applicationContext.getParent(), beanKey);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getStaticNames() {
|
||||
if (this.objectNameStaticProperties.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (Entry<Object, Object> name : this.objectNameStaticProperties.entrySet()) {
|
||||
builder.append("," + name.getKey() + "=" + name.getValue());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getPhase() {
|
||||
return this.phase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAutoStartup() {
|
||||
return this.autoStartup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isRunning() {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
return this.running;
|
||||
}
|
||||
finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void start() {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
if (!this.running) {
|
||||
this.doStart();
|
||||
this.running = true;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void stop() {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
if (this.running) {
|
||||
this.running = false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void stop(Runnable callback) {
|
||||
this.lifecycleLock.lock();
|
||||
try {
|
||||
this.stop();
|
||||
callback.run();
|
||||
}
|
||||
finally {
|
||||
this.lifecycleLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JmxAttributeSource} for {@link JmxEndpoint JmxEndpoints}.
|
||||
*/
|
||||
private static class EndpointJmxAttributeSource extends AnnotationJmxAttributeSource {
|
||||
|
||||
@Override
|
||||
public org.springframework.jmx.export.metadata.ManagedResource getManagedResource(
|
||||
Class<?> beanClass) throws InvalidMetadataException {
|
||||
Assert.state(super.getManagedResource(beanClass) == null,
|
||||
"@ManagedResource annotation found on JmxEndpoint " + beanClass);
|
||||
return new org.springframework.jmx.export.metadata.ManagedResource();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.jmx.export.annotation.ManagedResource;
|
||||
|
||||
/**
|
||||
* A strategy for the JMX layer on top of an {@link Endpoint}. Implementations are allowed
|
||||
* to use {@code @ManagedAttribute} and the full Spring JMX machinery but should not use
|
||||
* the {@link ManagedResource @ManagedResource} annotation. Implementations may be backed
|
||||
* by an actual {@link Endpoint} or may be specifically designed for JMX only.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
* @see EndpointMBean
|
||||
* @see AbstractJmxEndpoint
|
||||
*/
|
||||
public interface JmxEndpoint {
|
||||
|
||||
/**
|
||||
* Return if the JMX endpoint is enabled.
|
||||
* @return if the endpoint is enabled
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Return the MBean identity for this endpoint.
|
||||
* @return the MBean identity.
|
||||
*/
|
||||
String getIdentity();
|
||||
|
||||
/**
|
||||
* Return the type of {@link Endpoint} exposed, or {@code null} if this
|
||||
* {@link JmxEndpoint} exposes information that cannot be represented as a traditional
|
||||
* {@link Endpoint}.
|
||||
* @return the endpoint type
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends Endpoint> getEndpointType();
|
||||
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.jmx.export.annotation.ManagedAttribute;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperationParameter;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperationParameters;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Adapter to expose {@link LoggersEndpoint} as an {@link MvcEndpoint}.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class LoggersEndpointMBean extends EndpointMBean {
|
||||
|
||||
public LoggersEndpointMBean(String beanName, Endpoint<?> endpoint,
|
||||
ObjectMapper objectMapper) {
|
||||
super(beanName, endpoint, objectMapper);
|
||||
}
|
||||
|
||||
@ManagedAttribute(description = "Get log levels for all known loggers")
|
||||
public Object getLoggers() {
|
||||
return convert(getEndpoint().invoke());
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Get log level for a given logger")
|
||||
@ManagedOperationParameters({
|
||||
@ManagedOperationParameter(name = "loggerName", description = "Name of the logger") })
|
||||
public Object getLogger(String loggerName) {
|
||||
return convert(getEndpoint().invoke(loggerName));
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Set log level for a given logger")
|
||||
@ManagedOperationParameters({
|
||||
@ManagedOperationParameter(name = "loggerName", description = "Name of the logger"),
|
||||
@ManagedOperationParameter(name = "logLevel", description = "New log level (can be null or empty String to remove the custom level)") })
|
||||
public void setLogLevel(String loggerName, String logLevel) {
|
||||
getEndpoint().setLogLevel(loggerName, determineLogLevel(logLevel));
|
||||
}
|
||||
|
||||
private LogLevel determineLogLevel(String logLevel) {
|
||||
if (StringUtils.hasText(logLevel)) {
|
||||
return LogLevel.valueOf(logLevel.toUpperCase());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoggersEndpoint getEndpoint() {
|
||||
return (LoggersEndpoint) super.getEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.jmx;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
||||
import org.springframework.jmx.export.annotation.ManagedOperation;
|
||||
|
||||
/**
|
||||
* Special endpoint wrapper for {@link ShutdownEndpoint}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class ShutdownEndpointMBean extends EndpointMBean {
|
||||
|
||||
/**
|
||||
* Create a new {@link ShutdownEndpointMBean} instance.
|
||||
* @param beanName the bean name
|
||||
* @param endpoint the endpoint to wrap
|
||||
* @param objectMapper the {@link ObjectMapper} used to convert the payload
|
||||
*/
|
||||
public ShutdownEndpointMBean(String beanName, Endpoint<?> endpoint,
|
||||
ObjectMapper objectMapper) {
|
||||
super(beanName, endpoint, objectMapper);
|
||||
}
|
||||
|
||||
@ManagedOperation(description = "Shutdown the ApplicationContext")
|
||||
public Object shutdown() {
|
||||
return convert(getEndpoint().invoke());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,309 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
|
||||
/**
|
||||
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
|
||||
* The semantics of {@code @RequestMapping} should be identical to a normal
|
||||
* {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
|
||||
* (otherwise they will be mapped by the normal MVC mechanisms).
|
||||
* <p>
|
||||
* One of the aims of the mapping is to support endpoints that work as HTTP endpoints but
|
||||
* can still provide useful service interfaces when there is no HTTP server (and no Spring
|
||||
* MVC on the classpath). Note that any endpoints having method signatures will break in a
|
||||
* non-servlet environment.
|
||||
*
|
||||
* @param <E> The endpoint type
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public abstract class AbstractEndpointHandlerMapping<E extends MvcEndpoint>
|
||||
extends RequestMappingHandlerMapping {
|
||||
|
||||
private final Set<E> endpoints;
|
||||
|
||||
private HandlerInterceptor securityInterceptor;
|
||||
|
||||
private final CorsConfiguration corsConfiguration;
|
||||
|
||||
private String prefix = "";
|
||||
|
||||
private boolean disabled = false;
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractEndpointHandlerMapping} instance. All {@link Endpoint}s
|
||||
* will be detected from the {@link ApplicationContext}. The endpoints will not accept
|
||||
* CORS requests.
|
||||
* @param endpoints the endpoints
|
||||
*/
|
||||
public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints) {
|
||||
this(endpoints, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractEndpointHandlerMapping} instance. All {@link Endpoint}s
|
||||
* will be detected from the {@link ApplicationContext}. The endpoints will accepts
|
||||
* CORS requests based on the given {@code corsConfiguration}.
|
||||
* @param endpoints the endpoints
|
||||
* @param corsConfiguration the CORS configuration for the endpoints
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public AbstractEndpointHandlerMapping(Collection<? extends E> endpoints,
|
||||
CorsConfiguration corsConfiguration) {
|
||||
this.endpoints = new HashSet<>(endpoints);
|
||||
postProcessEndpoints(this.endpoints);
|
||||
this.corsConfiguration = corsConfiguration;
|
||||
// By default the static resource handler mapping is LOWEST_PRECEDENCE - 1
|
||||
// and the RequestMappingHandlerMapping is 0 (we ideally want to be before both)
|
||||
setOrder(-100);
|
||||
setUseSuffixPatternMatch(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process the endpoint setting before they are used. Subclasses can add or
|
||||
* modify the endpoints as necessary.
|
||||
* @param endpoints the endpoints to post process
|
||||
*/
|
||||
protected void postProcessEndpoints(Set<E> endpoints) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
if (!this.disabled) {
|
||||
for (MvcEndpoint endpoint : this.endpoints) {
|
||||
detectHandlerMethods(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Since all handler beans are passed into the constructor there is no need to detect
|
||||
* anything here.
|
||||
*/
|
||||
@Override
|
||||
protected boolean isHandler(Class<?> beanType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
protected void registerHandlerMethod(Object handler, Method method,
|
||||
RequestMappingInfo mapping) {
|
||||
if (mapping == null) {
|
||||
return;
|
||||
}
|
||||
String[] patterns = getPatterns(handler, mapping);
|
||||
if (!ObjectUtils.isEmpty(patterns)) {
|
||||
super.registerHandlerMethod(handler, method,
|
||||
withNewPatterns(mapping, patterns));
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getPatterns(Object handler, RequestMappingInfo mapping) {
|
||||
if (handler instanceof String) {
|
||||
handler = getApplicationContext().getBean((String) handler);
|
||||
}
|
||||
Assert.state(handler instanceof MvcEndpoint, "Only MvcEndpoints are supported");
|
||||
String path = getPath((MvcEndpoint) handler);
|
||||
return (path == null ? null : getEndpointPatterns(path, mapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path that should be used to map the given {@link MvcEndpoint}.
|
||||
* @param endpoint the endpoint to map
|
||||
* @return the path to use for the endpoint or {@code null} if no mapping is required
|
||||
*/
|
||||
protected String getPath(MvcEndpoint endpoint) {
|
||||
return endpoint.getPath();
|
||||
}
|
||||
|
||||
private String[] getEndpointPatterns(String path, RequestMappingInfo mapping) {
|
||||
String patternPrefix = StringUtils.hasText(this.prefix) ? this.prefix + path
|
||||
: path;
|
||||
Set<String> defaultPatterns = mapping.getPatternsCondition().getPatterns();
|
||||
if (defaultPatterns.isEmpty()) {
|
||||
return new String[] { patternPrefix, patternPrefix + ".json" };
|
||||
}
|
||||
List<String> patterns = new ArrayList<>(defaultPatterns);
|
||||
for (int i = 0; i < patterns.size(); i++) {
|
||||
patterns.set(i, patternPrefix + patterns.get(i));
|
||||
}
|
||||
return patterns.toArray(new String[patterns.size()]);
|
||||
}
|
||||
|
||||
private RequestMappingInfo withNewPatterns(RequestMappingInfo mapping,
|
||||
String[] patternStrings) {
|
||||
PatternsRequestCondition patterns = new PatternsRequestCondition(patternStrings,
|
||||
null, null, useSuffixPatternMatch(), useTrailingSlashMatch(), null);
|
||||
return new RequestMappingInfo(patterns, mapping.getMethodsCondition(),
|
||||
mapping.getParamsCondition(), mapping.getHeadersCondition(),
|
||||
mapping.getConsumesCondition(), mapping.getProducesCondition(),
|
||||
mapping.getCustomCondition());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerExecutionChain getHandlerExecutionChain(Object handler,
|
||||
HttpServletRequest request) {
|
||||
HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
|
||||
if (this.securityInterceptor == null || CorsUtils.isCorsRequest(request)) {
|
||||
return chain;
|
||||
}
|
||||
return addSecurityInterceptor(chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerExecutionChain getCorsHandlerExecutionChain(
|
||||
HttpServletRequest request, HandlerExecutionChain chain,
|
||||
CorsConfiguration config) {
|
||||
chain = super.getCorsHandlerExecutionChain(request, chain, config);
|
||||
if (this.securityInterceptor == null) {
|
||||
return chain;
|
||||
}
|
||||
return addSecurityInterceptor(chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extendInterceptors(List<Object> interceptors) {
|
||||
interceptors.add(new SkipPathExtensionContentNegotiation());
|
||||
}
|
||||
|
||||
private HandlerExecutionChain addSecurityInterceptor(HandlerExecutionChain chain) {
|
||||
List<HandlerInterceptor> interceptors = new ArrayList<>();
|
||||
if (chain.getInterceptors() != null) {
|
||||
interceptors.addAll(Arrays.asList(chain.getInterceptors()));
|
||||
}
|
||||
interceptors.add(this.securityInterceptor);
|
||||
return new HandlerExecutionChain(chain.getHandler(),
|
||||
interceptors.toArray(new HandlerInterceptor[interceptors.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the handler interceptor that will be used for security.
|
||||
* @param securityInterceptor the security handler interceptor
|
||||
*/
|
||||
public void setSecurityInterceptor(HandlerInterceptor securityInterceptor) {
|
||||
this.securityInterceptor = securityInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prefix used in mappings.
|
||||
* @param prefix the prefix
|
||||
*/
|
||||
public void setPrefix(String prefix) {
|
||||
Assert.isTrue("".equals(prefix) || StringUtils.startsWithIgnoreCase(prefix, "/"),
|
||||
"prefix must start with '/'");
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prefix used in mappings.
|
||||
* @return the prefix
|
||||
*/
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the endpoint.
|
||||
* @param endpoint the endpoint
|
||||
* @return the path used in mappings
|
||||
*/
|
||||
public String getPath(String endpoint) {
|
||||
return this.prefix + endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this mapping is disabled.
|
||||
* @param disabled if the mapping is disabled
|
||||
*/
|
||||
public void setDisabled(boolean disabled) {
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if this mapping is disabled.
|
||||
* @return {@code true} if the mapping is disabled
|
||||
*/
|
||||
public boolean isDisabled() {
|
||||
return this.disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the endpoints.
|
||||
* @return the endpoints
|
||||
*/
|
||||
public Set<E> getEndpoints() {
|
||||
return Collections.unmodifiableSet(this.endpoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CorsConfiguration initCorsConfiguration(Object handler, Method method,
|
||||
RequestMappingInfo mappingInfo) {
|
||||
return this.corsConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link HandlerInterceptorAdapter} to ensure that
|
||||
* {@link PathExtensionContentNegotiationStrategy} is skipped for actuator endpoints.
|
||||
*/
|
||||
private static final class SkipPathExtensionContentNegotiation
|
||||
extends HandlerInterceptorAdapter {
|
||||
|
||||
private static final String SKIP_ATTRIBUTE = PathExtensionContentNegotiationStrategy.class
|
||||
.getName() + ".SKIP";
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
request.setAttribute(SKIP_ATTRIBUTE, Boolean.TRUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link MvcEndpoint} implementations.
|
||||
*
|
||||
* @param <E> The delegate endpoint
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public abstract class AbstractEndpointMvcAdapter<E extends Endpoint<?>>
|
||||
implements NamedMvcEndpoint {
|
||||
|
||||
private final E delegate;
|
||||
|
||||
/**
|
||||
* Endpoint URL path.
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointMvcAdapter}.
|
||||
* @param delegate the underlying {@link Endpoint} to adapt.
|
||||
*/
|
||||
public AbstractEndpointMvcAdapter(E delegate) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
protected Object invoke() {
|
||||
if (!this.delegate.isEnabled()) {
|
||||
// Shouldn't happen - shouldn't be registered when delegate's disabled
|
||||
return getDisabledResponse();
|
||||
}
|
||||
return this.delegate.invoke();
|
||||
}
|
||||
|
||||
public E getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.delegate.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return (this.path != null ? this.path : "/" + this.delegate.getId());
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
while (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Class<? extends Endpoint> getEndpointType() {
|
||||
return this.delegate.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response that should be returned when the endpoint is disabled.
|
||||
* @return The response to be returned when the endpoint is disabled
|
||||
* @since 1.2.4
|
||||
* @see Endpoint#isEnabled()
|
||||
*/
|
||||
protected ResponseEntity<?> getDisabledResponse() {
|
||||
return MvcEndpoint.DISABLED_RESPONSE;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointProperties;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link MvcEndpoint} implementations without a backing
|
||||
* {@link Endpoint}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Lari Hotari
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public abstract class AbstractMvcEndpoint
|
||||
implements MvcEndpoint, WebMvcConfigurer, EnvironmentAware {
|
||||
|
||||
private Environment environment;
|
||||
|
||||
/**
|
||||
* Endpoint URL path.
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Enable the endpoint.
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
public AbstractMvcEndpoint(String path) {
|
||||
setPath(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
protected final Environment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
Assert.notNull(path, "Path must not be null");
|
||||
Assert.isTrue(path.isEmpty() || path.startsWith("/"),
|
||||
"Path must start with / or be empty");
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return EndpointProperties.isEnabled(this.environment, this.enabled);
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Class<? extends Endpoint> getEndpointType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link NamedMvcEndpoint} implementations without a backing
|
||||
* {@link Endpoint}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public abstract class AbstractNamedMvcEndpoint extends AbstractMvcEndpoint
|
||||
implements NamedMvcEndpoint {
|
||||
|
||||
private final String name;
|
||||
|
||||
public AbstractNamedMvcEndpoint(String name, String path) {
|
||||
super(path);
|
||||
Assert.hasLength(name, "Name must not be empty");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
/**
|
||||
* Specialized {@link RequestMapping} for {@link RequestMethod#POST POST} requests that
|
||||
* consume {@code application/json} or
|
||||
* {@code application/vnd.spring-boot.actuator.v1+json} requests and produce
|
||||
* {@code application/json} or {@code application/vnd.spring-boot.actuator.v1+json}
|
||||
* responses.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@RequestMapping(method = RequestMethod.POST, consumes = {
|
||||
ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = {
|
||||
ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
@interface ActuatorPostMapping {
|
||||
|
||||
/**
|
||||
* Alias for {@link RequestMapping#value}.
|
||||
* @return the value
|
||||
*/
|
||||
@AliasFor(annotation = RequestMapping.class)
|
||||
String[] value() default {};
|
||||
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.audit.AuditEventRepository;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* {@link MvcEndpoint} to expose {@link AuditEvent}s.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.auditevents")
|
||||
public class AuditEventsMvcEndpoint extends AbstractNamedMvcEndpoint {
|
||||
|
||||
private final AuditEventRepository auditEventRepository;
|
||||
|
||||
public AuditEventsMvcEndpoint(AuditEventRepository auditEventRepository) {
|
||||
super("auditevents", "/auditevents");
|
||||
Assert.notNull(auditEventRepository, "AuditEventRepository must not be null");
|
||||
this.auditEventRepository = auditEventRepository;
|
||||
}
|
||||
|
||||
@ActuatorGetMapping
|
||||
@ResponseBody
|
||||
public ResponseEntity<?> findByPrincipalAndAfterAndType(
|
||||
@RequestParam(required = false) String principal,
|
||||
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssZ") Date after,
|
||||
@RequestParam(required = false) String type) {
|
||||
if (!isEnabled()) {
|
||||
return DISABLED_RESPONSE;
|
||||
}
|
||||
Map<Object, Object> result = new LinkedHashMap<>();
|
||||
result.put("events", this.auditEventRepository.find(principal, after, type));
|
||||
return ResponseEntity.ok(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
/**
|
||||
* {@link HandlerMapping} to map {@link Endpoint}s to URLs via {@link Endpoint#getId()}.
|
||||
* The semantics of {@code @RequestMapping} should be identical to a normal
|
||||
* {@code @Controller}, but the endpoints should not be annotated as {@code @Controller}
|
||||
* (otherwise they will be mapped by the normal MVC mechanisms).
|
||||
* <p>
|
||||
* One of the aims of the mapping is to support endpoints that work as HTTP endpoints but
|
||||
* can still provide useful service interfaces when there is no HTTP server (and no Spring
|
||||
* MVC on the classpath). Note that any endpoints having method signatures will break in a
|
||||
* non-servlet environment.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class EndpointHandlerMapping extends AbstractEndpointHandlerMapping<MvcEndpoint> {
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
|
||||
* detected from the {@link ApplicationContext}. The endpoints will not accept CORS
|
||||
* requests.
|
||||
* @param endpoints the endpoints
|
||||
*/
|
||||
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
|
||||
super(endpoints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link EndpointHandlerMapping} instance. All {@link Endpoint}s will be
|
||||
* detected from the {@link ApplicationContext}. The endpoints will accepts CORS
|
||||
* requests based on the given {@code corsConfiguration}.
|
||||
* @param endpoints the endpoints
|
||||
* @param corsConfiguration the CORS configuration for the endpoints
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints,
|
||||
CorsConfiguration corsConfiguration) {
|
||||
super(endpoints, corsConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
detectHandlerMethods(new EndpointLinksMvcEndpoint(
|
||||
getEndpoints().stream().filter(NamedMvcEndpoint.class::isInstance)
|
||||
.map(NamedMvcEndpoint.class::cast).collect(Collectors.toSet())));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MvcEndpoint} to provide HAL-formatted links to all the
|
||||
* {@link NamedMvcEndpoint named endpoints}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
private static final class EndpointLinksMvcEndpoint extends AbstractMvcEndpoint {
|
||||
|
||||
private final Set<NamedMvcEndpoint> endpoints;
|
||||
|
||||
private EndpointLinksMvcEndpoint(Set<NamedMvcEndpoint> endpoints) {
|
||||
super("");
|
||||
this.endpoints = endpoints;
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@ActuatorGetMapping
|
||||
public Map<String, Map<String, Link>> links(HttpServletRequest request) {
|
||||
Map<String, Link> links = new LinkedHashMap<>();
|
||||
String url = request.getRequestURL().toString();
|
||||
if (url.endsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
links.put("self", Link.withHref(url));
|
||||
for (NamedMvcEndpoint endpoint : this.endpoints) {
|
||||
links.put(endpoint.getName(),
|
||||
Link.withHref(url + "/" + endpoint.getName()));
|
||||
}
|
||||
return Collections.singletonMap("_links", links);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Details for a link in the HAL response.
|
||||
*/
|
||||
static final class Link {
|
||||
|
||||
private final String href;
|
||||
|
||||
private Link(String href) {
|
||||
this.href = href;
|
||||
}
|
||||
|
||||
public String getHref() {
|
||||
return this.href;
|
||||
}
|
||||
|
||||
static Link withHref(Object href) {
|
||||
return new Link(href.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Adapter to expose {@link EnvironmentEndpoint} as an {@link MvcEndpoint}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Christian Dupuis
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.env")
|
||||
public class EnvironmentMvcEndpoint extends EndpointMvcAdapter
|
||||
implements EnvironmentAware {
|
||||
|
||||
private Environment environment;
|
||||
|
||||
public EnvironmentMvcEndpoint(EnvironmentEndpoint delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@ActuatorGetMapping("/{name:.*}")
|
||||
@ResponseBody
|
||||
public Object value(@PathVariable String name) {
|
||||
if (!getDelegate().isEnabled()) {
|
||||
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
|
||||
// disabled
|
||||
return getDisabledResponse();
|
||||
}
|
||||
return new NamePatternEnvironmentFilter(this.environment).getResults(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link NamePatternFilter} for the Environment source.
|
||||
*/
|
||||
private class NamePatternEnvironmentFilter extends NamePatternFilter<Environment> {
|
||||
|
||||
NamePatternEnvironmentFilter(Environment source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getNames(Environment source, NameCallback callback) {
|
||||
if (source instanceof ConfigurableEnvironment) {
|
||||
getNames(((ConfigurableEnvironment) source).getPropertySources(),
|
||||
callback);
|
||||
}
|
||||
}
|
||||
|
||||
private void getNames(PropertySources propertySources, NameCallback callback) {
|
||||
for (PropertySource<?> propertySource : propertySources) {
|
||||
if (propertySource instanceof EnumerablePropertySource) {
|
||||
EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) propertySource;
|
||||
for (String name : source.getPropertyNames()) {
|
||||
callback.addName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getOptionalValue(Environment source, String name) {
|
||||
Object result = getValue(name);
|
||||
if (result != null) {
|
||||
result = ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getValue(Environment source, String name) {
|
||||
Object result = getValue(name);
|
||||
if (result == null) {
|
||||
throw new NoSuchPropertyException("No such property: " + name);
|
||||
}
|
||||
return ((EnvironmentEndpoint) getDelegate()).sanitize(name, result);
|
||||
}
|
||||
|
||||
private Object getValue(String name) {
|
||||
return ((EnvironmentEndpoint) getDelegate()).getResolver().getProperty(name,
|
||||
Object.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when the specified property cannot be found.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such property")
|
||||
public static class NoSuchPropertyException extends RuntimeException {
|
||||
|
||||
public NoSuchPropertyException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,240 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Adapter to expose {@link HealthEndpoint} as an {@link MvcEndpoint}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Eddú Meléndez
|
||||
* @author Madhura Bhave
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.health")
|
||||
public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint> {
|
||||
|
||||
private static final List<String> DEFAULT_ROLES = Arrays.asList("ROLE_ACTUATOR");
|
||||
|
||||
private final boolean secure;
|
||||
|
||||
private final List<String> roles;
|
||||
|
||||
private Map<String, HttpStatus> statusMapping = new HashMap<>();
|
||||
|
||||
private volatile CachedHealth cachedHealth;
|
||||
|
||||
public HealthMvcEndpoint(HealthEndpoint delegate) {
|
||||
this(delegate, true);
|
||||
}
|
||||
|
||||
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure) {
|
||||
this(delegate, secure, new ArrayList<>(DEFAULT_ROLES));
|
||||
}
|
||||
|
||||
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure,
|
||||
List<String> roles) {
|
||||
super(delegate);
|
||||
Assert.notNull(roles, "Roles must not be null");
|
||||
this.secure = secure;
|
||||
this.roles = roles;
|
||||
setupDefaultStatusMapping();
|
||||
}
|
||||
|
||||
private void setupDefaultStatusMapping() {
|
||||
addStatusMapping(Status.DOWN, HttpStatus.SERVICE_UNAVAILABLE);
|
||||
addStatusMapping(Status.OUT_OF_SERVICE, HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set specific status mappings.
|
||||
* @param statusMapping a map of status code to {@link HttpStatus}
|
||||
*/
|
||||
public void setStatusMapping(Map<String, HttpStatus> statusMapping) {
|
||||
Assert.notNull(statusMapping, "StatusMapping must not be null");
|
||||
this.statusMapping = new HashMap<>(statusMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add specific status mappings to the existing set.
|
||||
* @param statusMapping a map of status code to {@link HttpStatus}
|
||||
*/
|
||||
public void addStatusMapping(Map<String, HttpStatus> statusMapping) {
|
||||
Assert.notNull(statusMapping, "StatusMapping must not be null");
|
||||
this.statusMapping.putAll(statusMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a status mapping to the existing set.
|
||||
* @param status the status to map
|
||||
* @param httpStatus the http status
|
||||
*/
|
||||
public void addStatusMapping(Status status, HttpStatus httpStatus) {
|
||||
Assert.notNull(status, "Status must not be null");
|
||||
Assert.notNull(httpStatus, "HttpStatus must not be null");
|
||||
addStatusMapping(status.getCode(), httpStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a status mapping to the existing set.
|
||||
* @param statusCode the status code to map
|
||||
* @param httpStatus the http status
|
||||
*/
|
||||
public void addStatusMapping(String statusCode, HttpStatus httpStatus) {
|
||||
Assert.notNull(statusCode, "StatusCode must not be null");
|
||||
Assert.notNull(httpStatus, "HttpStatus must not be null");
|
||||
this.statusMapping.put(statusCode, httpStatus);
|
||||
}
|
||||
|
||||
@ActuatorGetMapping
|
||||
@ResponseBody
|
||||
public Object invoke(HttpServletRequest request, Principal principal) {
|
||||
if (!getDelegate().isEnabled()) {
|
||||
// Shouldn't happen because the request mapping should not be registered
|
||||
return getDisabledResponse();
|
||||
}
|
||||
Health health = getHealth(request, principal);
|
||||
HttpStatus status = getStatus(health);
|
||||
if (status != null) {
|
||||
return new ResponseEntity<>(health, status);
|
||||
}
|
||||
return health;
|
||||
}
|
||||
|
||||
private HttpStatus getStatus(Health health) {
|
||||
String code = getUniformValue(health.getStatus().getCode());
|
||||
if (code != null) {
|
||||
return this.statusMapping.keySet().stream()
|
||||
.filter((key) -> code.equals(getUniformValue(key)))
|
||||
.map(this.statusMapping::get).findFirst().orElse(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getUniformValue(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (char ch : code.toCharArray()) {
|
||||
if (Character.isAlphabetic(ch) || Character.isDigit(ch)) {
|
||||
builder.append(Character.toLowerCase(ch));
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private Health getHealth(HttpServletRequest request, Principal principal) {
|
||||
Health currentHealth = getCurrentHealth();
|
||||
if (exposeHealthDetails(request, principal)) {
|
||||
return currentHealth;
|
||||
}
|
||||
return Health.status(currentHealth.getStatus()).build();
|
||||
}
|
||||
|
||||
private Health getCurrentHealth() {
|
||||
long accessTime = System.currentTimeMillis();
|
||||
CachedHealth cached = this.cachedHealth;
|
||||
if (cached == null || cached.isStale(accessTime, getDelegate().getTimeToLive())) {
|
||||
Health health = getDelegate().invoke();
|
||||
this.cachedHealth = new CachedHealth(health, accessTime);
|
||||
return health;
|
||||
}
|
||||
return cached.getHealth();
|
||||
}
|
||||
|
||||
protected boolean exposeHealthDetails(HttpServletRequest request,
|
||||
Principal principal) {
|
||||
if (!this.secure) {
|
||||
return true;
|
||||
}
|
||||
List<String> roles = getRoles();
|
||||
for (String role : roles) {
|
||||
if (request.isUserInRole(role)) {
|
||||
return true;
|
||||
}
|
||||
if (isSpringSecurityAuthentication(principal)) {
|
||||
Authentication authentication = (Authentication) principal;
|
||||
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||
String name = authority.getAuthority();
|
||||
if (role.equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<String> getRoles() {
|
||||
return this.roles;
|
||||
}
|
||||
|
||||
private boolean isSpringSecurityAuthentication(Principal principal) {
|
||||
return ClassUtils.isPresent("org.springframework.security.core.Authentication",
|
||||
null) && principal instanceof Authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* A cached {@link Health} that encapsulates the {@code Health} itself and the time at
|
||||
* which it was created.
|
||||
*/
|
||||
static class CachedHealth {
|
||||
|
||||
private final Health health;
|
||||
|
||||
private final long creationTime;
|
||||
|
||||
CachedHealth(Health health, long creationTime) {
|
||||
this.health = health;
|
||||
this.creationTime = creationTime;
|
||||
}
|
||||
|
||||
public boolean isStale(long accessTime, long timeToLive) {
|
||||
return (accessTime - this.creationTime) >= timeToLive;
|
||||
}
|
||||
|
||||
public Health getHealth() {
|
||||
return this.health;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.logging.LogFile;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
|
||||
|
||||
/**
|
||||
* Controller that provides an API for logfiles, i.e. downloading the main logfile
|
||||
* configured in environment property 'logging.file' that is standard, but optional
|
||||
* property for spring-boot applications.
|
||||
*
|
||||
* @author Johannes Edmeier
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.logfile")
|
||||
public class LogFileMvcEndpoint extends AbstractNamedMvcEndpoint {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(LogFileMvcEndpoint.class);
|
||||
|
||||
/**
|
||||
* External Logfile to be accessed. Can be used if the logfile is written by output
|
||||
* redirect and not by the logging-system itself.
|
||||
*/
|
||||
private File externalFile;
|
||||
|
||||
public LogFileMvcEndpoint() {
|
||||
super("logfile", "/logfile");
|
||||
}
|
||||
|
||||
public File getExternalFile() {
|
||||
return this.externalFile;
|
||||
}
|
||||
|
||||
public void setExternalFile(File externalFile) {
|
||||
this.externalFile = externalFile;
|
||||
}
|
||||
|
||||
@RequestMapping(method = { RequestMethod.GET, RequestMethod.HEAD })
|
||||
public void invoke(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
if (!isEnabled()) {
|
||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||
return;
|
||||
}
|
||||
Resource resource = getLogFileResource();
|
||||
if (resource != null && !resource.exists()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Log file '" + resource + "' does not exist");
|
||||
}
|
||||
resource = null;
|
||||
}
|
||||
Handler handler = new Handler(resource, request.getServletContext());
|
||||
handler.handleRequest(request, response);
|
||||
}
|
||||
|
||||
private Resource getLogFileResource() {
|
||||
if (this.externalFile != null) {
|
||||
return new FileSystemResource(this.externalFile);
|
||||
}
|
||||
LogFile logFile = LogFile.get(getEnvironment());
|
||||
if (logFile == null) {
|
||||
logger.debug("Missing 'logging.file' or 'logging.path' properties");
|
||||
return null;
|
||||
}
|
||||
return new FileSystemResource(logFile.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ResourceHttpRequestHandler} to send the log file.
|
||||
*/
|
||||
private static class Handler extends ResourceHttpRequestHandler {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
Handler(Resource resource, ServletContext servletContext) {
|
||||
this.resource = resource;
|
||||
getLocations().add(resource);
|
||||
try {
|
||||
setServletContext(servletContext);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initAllowedLocations() {
|
||||
this.getLocations().clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resource getResource(HttpServletRequest request) throws IOException {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MediaType getMediaType(HttpServletRequest request, Resource resource) {
|
||||
return MediaType.TEXT_PLAIN;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
|
||||
import org.springframework.boot.actuate.endpoint.LoggersEndpoint.LoggerLevels;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.logging.LogLevel;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Adapter to expose {@link LoggersEndpoint} as an {@link MvcEndpoint}.
|
||||
*
|
||||
* @author Ben Hale
|
||||
* @author Kazuki Shimizu
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.loggers")
|
||||
public class LoggersMvcEndpoint extends EndpointMvcAdapter {
|
||||
|
||||
private final LoggersEndpoint delegate;
|
||||
|
||||
public LoggersMvcEndpoint(LoggersEndpoint delegate) {
|
||||
super(delegate);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@ActuatorGetMapping("/{name:.*}")
|
||||
@ResponseBody
|
||||
public Object get(@PathVariable String name) {
|
||||
if (!this.delegate.isEnabled()) {
|
||||
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
|
||||
// disabled
|
||||
return getDisabledResponse();
|
||||
}
|
||||
LoggerLevels levels = this.delegate.invoke(name);
|
||||
return (levels == null ? ResponseEntity.notFound().build() : levels);
|
||||
}
|
||||
|
||||
@ActuatorPostMapping("/{name:.*}")
|
||||
@ResponseBody
|
||||
public Object set(@PathVariable String name,
|
||||
@RequestBody Map<String, String> configuration) {
|
||||
if (!this.delegate.isEnabled()) {
|
||||
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
|
||||
// disabled
|
||||
return getDisabledResponse();
|
||||
}
|
||||
try {
|
||||
LogLevel logLevel = getLogLevel(configuration);
|
||||
this.delegate.setLogLevel(name, logLevel);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return ResponseEntity.badRequest().build();
|
||||
}
|
||||
}
|
||||
|
||||
private LogLevel getLogLevel(Map<String, String> configuration) {
|
||||
String level = configuration.get("configuredLevel");
|
||||
return (level == null ? null : LogLevel.valueOf(level.toUpperCase()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,9 +27,9 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
|
||||
/**
|
||||
* Special {@link MvcEndpoint} for handling "/error" path when the management servlet is
|
||||
* in a child context. The regular {@link ErrorController} should be available there but
|
||||
* because of the way the handler mappings are set up it will not be detected.
|
||||
* {@link Controller} for handling "/error" path when the management servlet is in a child
|
||||
* context. The regular {@link ErrorController} should be available there but because of
|
||||
* the way the handler mappings are set up it will not be detected.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.MetricsEndpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
/**
|
||||
* Adapter to expose {@link MetricsEndpoint} as an {@link MvcEndpoint}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Sergei Egorov
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.metrics")
|
||||
public class MetricsMvcEndpoint extends EndpointMvcAdapter {
|
||||
|
||||
private final MetricsEndpoint delegate;
|
||||
|
||||
public MetricsMvcEndpoint(MetricsEndpoint delegate) {
|
||||
super(delegate);
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@ActuatorGetMapping("/{name:.*}")
|
||||
@ResponseBody
|
||||
public Object value(@PathVariable String name) {
|
||||
if (!this.delegate.isEnabled()) {
|
||||
// Shouldn't happen - MVC endpoint shouldn't be registered when delegate's
|
||||
// disabled
|
||||
return getDisabledResponse();
|
||||
}
|
||||
return new NamePatternMapFilter(this.delegate.invoke()).getResults(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link NamePatternFilter} for the Map source.
|
||||
*/
|
||||
private class NamePatternMapFilter extends NamePatternFilter<Map<String, ?>> {
|
||||
|
||||
NamePatternMapFilter(Map<String, ?> source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getNames(Map<String, ?> source, NameCallback callback) {
|
||||
for (String name : source.keySet()) {
|
||||
try {
|
||||
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
|
||||
protected Object getValue(Map<String, ?> source, String name) {
|
||||
Object value = getOptionalValue(source, name);
|
||||
if (value == null) {
|
||||
throw new NoSuchMetricException("No such metric: " + name);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when the specified metric cannot be found.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric")
|
||||
public static class NoSuchMetricException extends RuntimeException {
|
||||
|
||||
public NoSuchMetricException(String string) {
|
||||
super(string);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* A strategy for the MVC layer on top of an {@link Endpoint}. Implementations are allowed
|
||||
* to use {@code @RequestMapping} and the full Spring MVC machinery, but should not use
|
||||
* {@code @Controller} or {@code @RequestMapping} at the type level (since that would lead
|
||||
* to a double mapping of paths, once by the regular MVC handler mappings and once by the
|
||||
* {@link EndpointHandlerMapping}).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see NamedMvcEndpoint
|
||||
*/
|
||||
public interface MvcEndpoint {
|
||||
|
||||
/**
|
||||
* A {@link ResponseEntity} returned for disabled endpoints.
|
||||
*/
|
||||
ResponseEntity<Map<String, String>> DISABLED_RESPONSE = new ResponseEntity<>(
|
||||
Collections.singletonMap("message", "This endpoint is disabled"),
|
||||
HttpStatus.NOT_FOUND);
|
||||
|
||||
/**
|
||||
* Return the MVC path of the endpoint.
|
||||
* @return the endpoint path
|
||||
*/
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* Return the type of {@link Endpoint} exposed, or {@code null} if this
|
||||
* {@link MvcEndpoint} exposes information that cannot be represented as a traditional
|
||||
* {@link Endpoint}.
|
||||
* @return the endpoint type
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
Class<? extends Endpoint> getEndpointType();
|
||||
|
||||
}
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.cors.CorsUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
/**
|
||||
* Security interceptor for MvcEndpoints.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(MvcEndpointSecurityInterceptor.class);
|
||||
|
||||
private final boolean secure;
|
||||
|
||||
private final List<String> roles;
|
||||
|
||||
private AtomicBoolean loggedUnauthorizedAttempt = new AtomicBoolean();
|
||||
|
||||
public MvcEndpointSecurityInterceptor(boolean secure, List<String> roles) {
|
||||
this.secure = secure;
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler) throws Exception {
|
||||
if (CorsUtils.isPreFlightRequest(request) || !this.secure) {
|
||||
return true;
|
||||
}
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
if (HttpMethod.OPTIONS.matches(request.getMethod())
|
||||
&& !(handlerMethod.getBean() instanceof MvcEndpoint)) {
|
||||
return true;
|
||||
}
|
||||
if (isUserAllowedAccess(request)) {
|
||||
return true;
|
||||
}
|
||||
sendFailureResponse(request, response);
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isUserAllowedAccess(HttpServletRequest request) {
|
||||
AuthoritiesValidator authoritiesValidator = null;
|
||||
if (isSpringSecurityAvailable()) {
|
||||
authoritiesValidator = new AuthoritiesValidator();
|
||||
}
|
||||
for (String role : this.roles) {
|
||||
if (request.isUserInRole(role)) {
|
||||
return true;
|
||||
}
|
||||
if (authoritiesValidator != null && authoritiesValidator.hasAuthority(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSpringSecurityAvailable() {
|
||||
return ClassUtils.isPresent(
|
||||
"org.springframework.security.config.annotation.web.WebSecurityConfigurer",
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
|
||||
private void sendFailureResponse(HttpServletRequest request,
|
||||
HttpServletResponse response) throws Exception {
|
||||
if (request.getUserPrincipal() != null) {
|
||||
String roles = StringUtils.collectionToDelimitedString(this.roles, " ");
|
||||
response.sendError(HttpStatus.FORBIDDEN.value(),
|
||||
"Access is denied. User must have one of the these roles: " + roles);
|
||||
}
|
||||
else {
|
||||
logUnauthorizedAttempt();
|
||||
response.sendError(HttpStatus.UNAUTHORIZED.value(),
|
||||
"Full authentication is required to access this resource.");
|
||||
}
|
||||
}
|
||||
|
||||
private void logUnauthorizedAttempt() {
|
||||
if (this.loggedUnauthorizedAttempt.compareAndSet(false, true)
|
||||
&& logger.isInfoEnabled()) {
|
||||
logger.info("Full authentication is required to access "
|
||||
+ "actuator endpoints. Consider adding Spring Security "
|
||||
+ "or set 'management.security.enabled' to false.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to check authorities using Spring Security (when available).
|
||||
*/
|
||||
private static class AuthoritiesValidator {
|
||||
|
||||
private boolean hasAuthority(String role) {
|
||||
Authentication authentication = SecurityContextHolder.getContext()
|
||||
.getAuthentication();
|
||||
if (authentication != null) {
|
||||
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||
if (authority.getAuthority().equals(role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.actuate.endpoint.Endpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* A registry for all {@link MvcEndpoint} beans, and a factory for a set of generic ones
|
||||
* wrapping existing {@link Endpoint} instances that are not already exposed as MVC
|
||||
* endpoints.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class MvcEndpoints implements ApplicationContextAware, InitializingBean {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private final Set<MvcEndpoint> endpoints = new HashSet<>();
|
||||
|
||||
private Set<Class<?>> customTypes;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Collection<MvcEndpoint> existing = BeanFactoryUtils
|
||||
.beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class)
|
||||
.values();
|
||||
this.endpoints.addAll(existing);
|
||||
this.customTypes = findEndpointClasses(existing);
|
||||
@SuppressWarnings("rawtypes")
|
||||
Collection<Endpoint> delegates = BeanFactoryUtils
|
||||
.beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class)
|
||||
.values();
|
||||
for (Endpoint<?> endpoint : delegates) {
|
||||
if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {
|
||||
EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);
|
||||
String path = determinePath(endpoint,
|
||||
this.applicationContext.getEnvironment());
|
||||
if (path != null) {
|
||||
adapter.setPath(path);
|
||||
}
|
||||
this.endpoints.add(adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Class<?>> findEndpointClasses(Collection<MvcEndpoint> existing) {
|
||||
Set<Class<?>> types = new HashSet<>();
|
||||
for (MvcEndpoint endpoint : existing) {
|
||||
Class<?> type = endpoint.getEndpointType();
|
||||
if (type != null) {
|
||||
types.add(type);
|
||||
}
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
public Set<MvcEndpoint> getEndpoints() {
|
||||
return this.endpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the endpoints of the specified type.
|
||||
* @param <E> the Class type of the endpoints to be returned
|
||||
* @param type the endpoint type
|
||||
* @return the endpoints
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E extends MvcEndpoint> Set<E> getEndpoints(Class<E> type) {
|
||||
Set<E> result = new HashSet<>(this.endpoints.size());
|
||||
for (MvcEndpoint candidate : this.endpoints) {
|
||||
if (type.isInstance(candidate)) {
|
||||
result.add((E) candidate);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
private boolean isGenericEndpoint(Class<?> type) {
|
||||
return !this.customTypes.contains(type)
|
||||
&& !MvcEndpoint.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
private String determinePath(Endpoint<?> endpoint, Environment environment) {
|
||||
ConfigurationProperties configurationProperties = AnnotationUtils
|
||||
.findAnnotation(endpoint.getClass(), ConfigurationProperties.class);
|
||||
if (configurationProperties != null) {
|
||||
return environment.getProperty(configurationProperties.prefix() + ".path");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
/**
|
||||
* An {@link MvcEndpoint} that also includes a logical name. Unlike {@link #getPath()
|
||||
* endpoints paths}, it should not be possible for a user to change the endpoint name.
|
||||
* Names provide a consistent way to reference an endpoint, for example they may be used
|
||||
* as the {@literal 'rel'} attribute in a HAL response.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public interface NamedMvcEndpoint extends MvcEndpoint {
|
||||
|
||||
/**
|
||||
* Return the logical name of the endpoint. Names should be non-null, non-empty,
|
||||
* alpha-numeric values.
|
||||
* @return the logical name of the endpoint
|
||||
*/
|
||||
String getName();
|
||||
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.mvc;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.ShutdownEndpoint;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* Adapter to expose {@link ShutdownEndpoint} as an {@link MvcEndpoint}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "endpoints.shutdown")
|
||||
public class ShutdownMvcEndpoint extends EndpointMvcAdapter {
|
||||
|
||||
public ShutdownMvcEndpoint(ShutdownEndpoint delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@PostMapping(produces = { ActuatorMediaTypes.APPLICATION_ACTUATOR_V2_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
@ResponseBody
|
||||
@Override
|
||||
public Object invoke() {
|
||||
if (!getDelegate().isEnabled()) {
|
||||
return new ResponseEntity<>(
|
||||
Collections.singletonMap("message", "This endpoint is disabled"),
|
||||
HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return super.invoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2017 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.
|
||||
|
|
@ -17,6 +17,5 @@
|
|||
/**
|
||||
* Endpoints used to expose actuator information.
|
||||
*
|
||||
* @see org.springframework.boot.actuate.endpoint.Endpoint
|
||||
*/
|
||||
package org.springframework.boot.actuate.endpoint;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.web;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.audit.AuditEvent;
|
||||
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointExtension;
|
||||
|
||||
/**
|
||||
* Web-specific extension of the {@link AuditEventsEndpoint}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@WebEndpointExtension(endpoint = AuditEventsEndpoint.class)
|
||||
public class AuditEventsWebEndpointExtension {
|
||||
|
||||
private final AuditEventsEndpoint delegate;
|
||||
|
||||
public AuditEventsWebEndpointExtension(AuditEventsEndpoint delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public Map<String, List<AuditEvent>> eventsWithPrincipalDateAfterAndType(
|
||||
String principal, Date after, String type) {
|
||||
return Collections.singletonMap("events", this.delegate
|
||||
.eventsWithPrincipalDateAfterAndType(principal, after, type));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright 2012-2017 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.boot.actuate.endpoint.web;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.endpoint.ReadOperation;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointExtension;
|
||||
import org.springframework.boot.endpoint.web.WebEndpointResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link WebEndpointExtension} for the {@link HealthEndpoint}.
|
||||
*
|
||||
* @author Christian Dupuis
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Phillip Webb
|
||||
* @author Eddú Meléndez
|
||||
* @author Madhura Bhave
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@WebEndpointExtension(endpoint = HealthEndpoint.class)
|
||||
public class HealthWebEndpointExtension {
|
||||
|
||||
private Map<String, Integer> statusMapping = new HashMap<>();
|
||||
|
||||
private final HealthEndpoint delegate;
|
||||
|
||||
public HealthWebEndpointExtension(HealthEndpoint delegate) {
|
||||
this.delegate = delegate;
|
||||
setupDefaultStatusMapping();
|
||||
}
|
||||
|
||||
private void setupDefaultStatusMapping() {
|
||||
addStatusMapping(Status.DOWN, 503);
|
||||
addStatusMapping(Status.OUT_OF_SERVICE, 503);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set specific status mappings.
|
||||
* @param statusMapping a map of status code to {@link HttpStatus}
|
||||
*/
|
||||
public void setStatusMapping(Map<String, Integer> statusMapping) {
|
||||
Assert.notNull(statusMapping, "StatusMapping must not be null");
|
||||
this.statusMapping = new HashMap<>(statusMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add specific status mappings to the existing set.
|
||||
* @param statusMapping a map of status code to {@link HttpStatus}
|
||||
*/
|
||||
public void addStatusMapping(Map<String, Integer> statusMapping) {
|
||||
Assert.notNull(statusMapping, "StatusMapping must not be null");
|
||||
this.statusMapping.putAll(statusMapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a status mapping to the existing set.
|
||||
* @param status the status to map
|
||||
* @param httpStatus the http status
|
||||
*/
|
||||
public void addStatusMapping(Status status, Integer httpStatus) {
|
||||
Assert.notNull(status, "Status must not be null");
|
||||
Assert.notNull(httpStatus, "HttpStatus must not be null");
|
||||
addStatusMapping(status.getCode(), httpStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a status mapping to the existing set.
|
||||
* @param statusCode the status code to map
|
||||
* @param httpStatus the http status
|
||||
*/
|
||||
public void addStatusMapping(String statusCode, Integer httpStatus) {
|
||||
Assert.notNull(statusCode, "StatusCode must not be null");
|
||||
Assert.notNull(httpStatus, "HttpStatus must not be null");
|
||||
this.statusMapping.put(statusCode, httpStatus);
|
||||
}
|
||||
|
||||
@ReadOperation
|
||||
public WebEndpointResponse<Health> getHealth() {
|
||||
Health health = this.delegate.health();
|
||||
Integer status = getStatus(health);
|
||||
return new WebEndpointResponse<Health>(health, status);
|
||||
}
|
||||
|
||||
private int getStatus(Health health) {
|
||||
String code = getUniformValue(health.getStatus().getCode());
|
||||
if (code != null) {
|
||||
return this.statusMapping.keySet().stream()
|
||||
.filter((key) -> code.equals(getUniformValue(key)))
|
||||
.map(this.statusMapping::get).findFirst().orElse(200);
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
|
||||
private String getUniformValue(String code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (char ch : code.toCharArray()) {
|
||||
if (Character.isAlphabetic(ch) || Character.isDigit(ch)) {
|
||||
builder.append(Character.toLowerCase(ch));
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue