Refactor actuator package locations

Restructure actuator packages to improve structure. The following
changes have been made:

 - Separate actuator and actuator auto-configuration into different
   modules.
 - Move endpoint code into `spring-boot-actuator`.
 - Move `Endpoint` implementations from a single package into
   technology specific packages.
 - Move `HealthIndicator` implementations from a single package into
   technology specific packages.
 - As much as possible attempt to mirror the `spring-boot` package
   structure and class naming in `spring-boot-actuator` and
   `spring-boot-actuator-autoconfigure`.
 - Move `DataSourceBuilder` and DataSource meta-data support from
   `spring-boot-actuator` to `spring-boot`.

Fixes gh-10261
This commit is contained in:
Phillip Webb
2017-08-31 22:19:44 -07:00
parent 0f99b29b1a
commit 2e51b48cd9
501 changed files with 8340 additions and 4736 deletions

View File

@@ -14,9 +14,12 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
package org.springframework.boot.actuate.amqp;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.util.Assert;
/**
@@ -31,7 +34,7 @@ public class RabbitHealthIndicator extends AbstractHealthIndicator {
private final RabbitTemplate rabbitTemplate;
public RabbitHealthIndicator(RabbitTemplate rabbitTemplate) {
Assert.notNull(rabbitTemplate, "RabbitTemplate must not be null.");
Assert.notNull(rabbitTemplate, "RabbitTemplate must not be null");
this.rabbitTemplate = rabbitTemplate;
}

View File

@@ -14,15 +14,13 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.actuate.audit;
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.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.util.Assert;
/**

View File

@@ -14,14 +14,13 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint.jmx;
package org.springframework.boot.actuate.audit;
import java.util.Date;
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint;
import org.springframework.boot.actuate.endpoint.AuditEventsEndpoint.AuditEventsDescriptor;
import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.endpoint.jmx.JmxEndpointExtension;
import org.springframework.boot.actuate.audit.AuditEventsEndpoint.AuditEventsDescriptor;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointExtension;
/**
* JMX-specific extension of the {@link AuditEventsEndpoint}.

View File

@@ -1,251 +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;
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.AutoConfigureOrder;
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.Ordered;
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.util.Assert;
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
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class ManagementContextAutoConfiguration {
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
static class ServletManagementContextConfiguration {
@Bean
public ManagementServletContext managementServletContext(
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() {
verifySslConfiguration();
if (this.environment instanceof ConfigurableEnvironment) {
addLocalManagementPortPropertyAlias(
(ConfigurableEnvironment) this.environment);
}
}
private void verifySslConfiguration() {
Boolean enabled = this.environment.getProperty("management.ssl.enabled",
Boolean.class, false);
Assert.state(!enabled,
"Management-specific SSL cannot be configured as the management "
+ "server is not 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));
}
}
}

View File

@@ -1,57 +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;
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.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* Specialized {@link Configuration @Configuration} class that defines configuration
* specific for the management context. Configurations should be registered in
* {@code /META-INF/spring.factories} under the
* {@code org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration}
* key.
* <p>
* {@code ManagementContextConfiguration} classes can be ordered using {@link Order}.
* Ordering by implementing {@link Ordered} is not supported and will have no effect.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 1.3.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@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;
}

View File

@@ -1,152 +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;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
/**
* Selects configuration classes for the management context configuration. Entries are
* loaded from {@code /META-INF/spring.factories} under the
* {@code org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration}
* key.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @see ManagementContextConfiguration
*/
@Order(Ordered.LOWEST_PRECEDENCE)
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) {
if (configuration.getContextType() == ManagementContextType.ANY
|| configuration.getContextType() == contextType) {
names.add(configuration.getClassName());
}
}
return names.toArray(new String[names.size()]);
}
private List<ManagementConfiguration> getConfigurations() {
SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(
this.classLoader);
List<ManagementConfiguration> configurations = new ArrayList<>();
for (String className : loadFactoryNames()) {
getConfiguration(readerFactory, configurations, className);
}
return configurations;
}
private void getConfiguration(SimpleMetadataReaderFactory readerFactory,
List<ManagementConfiguration> configurations, String className) {
try {
MetadataReader metadataReader = readerFactory.getMetadataReader(className);
configurations.add(new ManagementConfiguration(metadataReader));
}
catch (IOException ex) {
throw new RuntimeException(
"Failed to read annotation metadata for '" + className + "'", ex);
}
}
protected List<String> loadFactoryNames() {
return SpringFactoriesLoader
.loadFactoryNames(ManagementContextConfiguration.class, this.classLoader);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* A management configuration class which can be sorted according to {@code @Order}.
*/
private static final class ManagementConfiguration implements Ordered {
private final String className;
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) {
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(Order.class.getName());
Integer order = (attributes == null ? null
: (Integer) attributes.get("value"));
return (order == null ? Ordered.LOWEST_PRECEDENCE : order);
}
public String getClassName() {
return this.className;
}
@Override
public int getOrder() {
return this.order;
}
public ManagementContextType getContextType() {
return this.contextType;
}
}
}

View File

@@ -1,39 +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;
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 {
/**
* Create the management application context.
* @param parent the parent context
* @param configurationClasses the configuration classes
* @return a configured application context
*/
ConfigurableApplicationContext createManagementContext(ApplicationContext parent,
Class<?>... configurationClasses);
}

View File

@@ -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.autoconfigure;
import org.springframework.core.env.Environment;
/**
* Port types that can be used to control how the management server is started.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
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);
}
}

View File

@@ -1,83 +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;
import java.lang.annotation.Annotation;
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.ConditionContext;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Base endpoint element condition. An element can be disabled globally via the
* {@code defaults} name or individually via the name of the element.
*
* @author Stephane Nicoll
* @author Madhura Bhave
* @since 2.0.0
*/
public abstract class OnEnabledEndpointElementCondition extends SpringBootCondition {
private final String prefix;
private final Class<? extends Annotation> annotationType;
protected OnEnabledEndpointElementCondition(String prefix,
Class<? extends Annotation> annotationType) {
this.prefix = prefix;
this.annotationType = annotationType;
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
AnnotationAttributes annotationAttributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(this.annotationType.getName()));
String endpointName = annotationAttributes.getString("value");
ConditionOutcome outcome = getEndpointOutcome(context, endpointName);
if (outcome != null) {
return outcome;
}
return getDefaultEndpointsOutcome(context);
}
protected ConditionOutcome getEndpointOutcome(ConditionContext context,
String endpointName) {
Environment environment = context.getEnvironment();
String enabledProperty = this.prefix + endpointName + ".enabled";
if (environment.containsProperty(enabledProperty)) {
boolean match = environment.getProperty(enabledProperty, Boolean.class, true);
return new ConditionOutcome(match,
ConditionMessage.forCondition(this.annotationType).because(
this.prefix + endpointName + ".enabled is " + match));
}
return null;
}
protected ConditionOutcome getDefaultEndpointsOutcome(ConditionContext context) {
boolean match = Boolean.valueOf(context.getEnvironment()
.getProperty(this.prefix + "defaults.enabled", "true"));
return new ConditionOutcome(match,
ConditionMessage.forCondition(this.annotationType).because(
this.prefix + "defaults.enabled is considered " + match));
}
}

View File

@@ -1,75 +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;
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);
}
}

View File

@@ -1,85 +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;
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();
}
}

View File

@@ -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;
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<>(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();
}
}

View File

@@ -1,82 +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.audit;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.audit.AuditEvent;
import org.springframework.boot.actuate.audit.AuditEventRepository;
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
import org.springframework.boot.actuate.audit.listener.AbstractAuditListener;
import org.springframework.boot.actuate.audit.listener.AuditListener;
import org.springframework.boot.actuate.security.AbstractAuthenticationAuditListener;
import org.springframework.boot.actuate.security.AbstractAuthorizationAuditListener;
import org.springframework.boot.actuate.security.AuthenticationAuditListener;
import org.springframework.boot.actuate.security.AuthorizationAuditListener;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link AuditEvent}s.
*
* @author Dave Syer
* @author Vedran Pavic
* @since 2.0.0
*/
@Configuration
public class AuditAutoConfiguration {
private final AuditEventRepository auditEventRepository;
public AuditAutoConfiguration(
ObjectProvider<AuditEventRepository> auditEventRepository) {
this.auditEventRepository = auditEventRepository.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(AbstractAuditListener.class)
public AuditListener auditListener() throws Exception {
return new AuditListener(this.auditEventRepository);
}
@Bean
@ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent")
@ConditionalOnMissingBean(AbstractAuthenticationAuditListener.class)
public AuthenticationAuditListener authenticationAuditListener() throws Exception {
return new AuthenticationAuditListener();
}
@Bean
@ConditionalOnClass(name = "org.springframework.security.access.event.AbstractAuthorizationEvent")
@ConditionalOnMissingBean(AbstractAuthorizationAuditListener.class)
public AuthorizationAuditListener authorizationAuditListener() throws Exception {
return new AuthorizationAuditListener();
}
@ConditionalOnMissingBean(AuditEventRepository.class)
protected static class AuditEventRepositoryConfiguration {
@Bean
public InMemoryAuditEventRepository auditEventRepository() throws Exception {
return new InMemoryAuditEventRepository();
}
}
}

View File

@@ -1,150 +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.cache;
import javax.cache.Caching;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.hazelcast.core.IMap;
import com.hazelcast.spring.cache.HazelcastCache;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.statistics.StatisticsGateway;
import org.infinispan.spring.provider.SpringCache;
import org.springframework.boot.actuate.cache.CacheStatistics;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider;
import org.springframework.boot.actuate.cache.CaffeineCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.ConcurrentMapCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.DefaultCacheStatistics;
import org.springframework.boot.actuate.cache.EhCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.HazelcastCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.InfinispanCacheStatisticsProvider;
import org.springframework.boot.actuate.cache.JCacheCacheStatisticsProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.ehcache.EhCacheCache;
import org.springframework.cache.jcache.JCacheCache;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link CacheStatisticsProvider}
* beans.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Eddú Meléndez
* @since 2.0.0
*/
@Configuration
@AutoConfigureAfter(CacheAutoConfiguration.class)
@ConditionalOnBean(CacheManager.class)
public class CacheStatisticsAutoConfiguration {
@Configuration
@ConditionalOnClass({ Caching.class, JCacheCache.class })
static class JCacheCacheStatisticsProviderConfiguration {
@Bean
public JCacheCacheStatisticsProvider jCacheCacheStatisticsProvider() {
return new JCacheCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ EhCacheCache.class, Ehcache.class, StatisticsGateway.class })
static class EhCacheCacheStatisticsProviderConfiguration {
@Bean
public EhCacheStatisticsProvider ehCacheCacheStatisticsProvider() {
return new EhCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ IMap.class, HazelcastCache.class })
static class HazelcastCacheStatisticsConfiguration {
@Bean
public HazelcastCacheStatisticsProvider hazelcastCacheStatisticsProvider() {
return new HazelcastCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ SpringCache.class })
static class InfinispanCacheStatisticsProviderConfiguration {
@Bean
public InfinispanCacheStatisticsProvider infinispanCacheStatisticsProvider() {
return new InfinispanCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass({ Caffeine.class, CaffeineCacheManager.class })
static class CaffeineCacheStatisticsProviderConfiguration {
@Bean
public CaffeineCacheStatisticsProvider caffeineCacheStatisticsProvider() {
return new CaffeineCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass(ConcurrentMapCache.class)
static class ConcurrentMapCacheStatisticsConfiguration {
@Bean
public ConcurrentMapCacheStatisticsProvider concurrentMapCacheStatisticsProvider() {
return new ConcurrentMapCacheStatisticsProvider();
}
}
@Configuration
@ConditionalOnClass(NoOpCacheManager.class)
static class NoOpCacheStatisticsConfiguration {
private static final CacheStatistics NO_OP_STATS = new DefaultCacheStatistics();
@Bean
public CacheStatisticsProvider<Cache> noOpCacheStatisticsProvider() {
return (cacheManager, cache) -> {
if (cacheManager instanceof NoOpCacheManager) {
return NO_OP_STATS;
}
return null;
};
}
}
}

View File

@@ -1,76 +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.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.EndpointExposure;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional} that checks whether an endpoint is enabled or not. Matches
* according to the {@code enabledByDefault} flag {@code types} flag that the
* {@link Endpoint} may be restricted to.
* <p>
* If no specific {@code endpoints.<id>.*} or {@code endpoints.default.*} properties are
* defined, the condition matches the {@code enabledByDefault} value regardless of the
* specific {@link EndpointExposure}, if any. If any property are set, they are evaluated
* with a sensible order of precedence.
* <p>
* For instance if {@code endpoints.default.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">
* &#064;Configuration
* public class MyAutoConfiguration {
*
* &#064;ConditionalOnEnabledEndpoint
* &#064;Bean
* public MyEndpoint myEndpoint() {
* ...
* }
*
* &#064;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 {
}

View File

@@ -1,305 +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.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
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.EnvironmentEndpoint;
import org.springframework.boot.actuate.endpoint.FlywayEndpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.InfoEndpoint;
import org.springframework.boot.actuate.endpoint.LiquibaseEndpoint;
import org.springframework.boot.actuate.endpoint.LoggersEndpoint;
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.StatusEndpoint;
import org.springframework.boot.actuate.endpoint.ThreadDumpEndpoint;
import org.springframework.boot.actuate.endpoint.TraceEndpoint;
import org.springframework.boot.actuate.health.CompositeHealthIndicatorFactory;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.Environment;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
/**
* {@link EnableAutoConfiguration Auto-configuration} for common management
* {@link Endpoint}s.
*
* @author Dave Syer
* @author Phillip Webb
* @author Greg Turnquist
* @author Christian Dupuis
* @author Stephane Nicoll
* @author Eddú Meléndez
* @author Meang Akira Tanaka
* @author Ben Hale
* @author Andy Wilkinson
* @since 2.0.0
*/
@Configuration
@AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class })
public class EndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public EnvironmentEndpoint environmentEndpoint(Environment environment) {
return new EnvironmentEndpoint(environment);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public BeansEndpoint beansEndpoint(
ConfigurableApplicationContext applicationContext) {
return new BeansEndpoint(applicationContext);
}
@Bean
@ConditionalOnMissingBean
@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
@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
@ConditionalOnEnabledEndpoint
public TraceEndpoint traceEndpoint(ObjectProvider<TraceRepository> traceRepository) {
return new TraceEndpoint(
traceRepository.getIfAvailable(() -> new InMemoryTraceRepository()));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public ThreadDumpEndpoint dumpEndpoint() {
return new ThreadDumpEndpoint();
}
@Bean
@ConditionalOnBean(ConditionEvaluationReport.class)
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
@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
@Import(HealthIndicatorsSupplierConfiguration.class)
static class HealthEndpointConfiguration {
private final HealthIndicator healthIndicator;
HealthEndpointConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
Supplier<Map<String, HealthIndicator>> healthIndicatorsSupplier) {
this.healthIndicator = new CompositeHealthIndicatorFactory()
.createHealthIndicator(
healthAggregator.getIfAvailable(OrderedHealthAggregator::new),
healthIndicatorsSupplier.get());
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public HealthEndpoint healthEndpoint() {
return new HealthEndpoint(this.healthIndicator);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public StatusEndpoint statusEndpoint() {
return new StatusEndpoint(this.healthIndicator);
}
}
@Configuration
static class HealthIndicatorsSupplierConfiguration {
@Configuration
@ConditionalOnMissingClass("reactor.core.publisher.Flux")
static class SimpleHealthIndicatorsSupplierConfiguration {
@Bean
public Supplier<Map<String, HealthIndicator>> allHealthIndicators(
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
return () -> healthIndicators.getIfAvailable(Collections::emptyMap);
}
}
@Configuration
@ConditionalOnClass(name = "reactor.core.publisher.Flux")
static class ReactiveHealthIndicatorsSupplierConfiguration {
@Bean
public Supplier<Map<String, HealthIndicator>> allHealthIndicators(
ObjectProvider<Map<String, HealthIndicator>> healthIndicators,
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators) {
return () -> merge(healthIndicators.getIfAvailable(Collections::emptyMap),
reactiveHealthIndicators.getIfAvailable(Collections::emptyMap));
}
private Map<String, HealthIndicator> merge(
Map<String, HealthIndicator> healthIndicators,
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators) {
if (ObjectUtils.isEmpty(reactiveHealthIndicators)) {
return healthIndicators;
}
Map<String, HealthIndicator> allIndicators = new LinkedHashMap<>(
healthIndicators);
reactiveHealthIndicators.forEach((beanName, indicator) -> allIndicators
.computeIfAbsent(beanName, n -> adapt(indicator)));
return allIndicators;
}
private HealthIndicator adapt(ReactiveHealthIndicator healthIndicator) {
return () -> healthIndicator.health().block();
}
}
}
@Configuration
@ConditionalOnBean(Flyway.class)
@ConditionalOnClass(Flyway.class)
static class FlywayEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public FlywayEndpoint flywayEndpoint(Map<String, Flyway> flyways) {
return new FlywayEndpoint(flyways);
}
}
@Configuration
@ConditionalOnBean(SpringLiquibase.class)
@ConditionalOnClass(SpringLiquibase.class)
static class LiquibaseEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public LiquibaseEndpoint liquibaseEndpoint(
Map<String, SpringLiquibase> liquibases) {
return new LiquibaseEndpoint(liquibases);
}
}
@Configuration
@ConditionalOnClass(AbstractHandlerMethodMapping.class)
protected static class RequestMappingEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public RequestMappingEndpoint requestMappingEndpoint() {
RequestMappingEndpoint endpoint = new RequestMappingEndpoint();
return endpoint;
}
}
}

View File

@@ -1,42 +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 org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
import org.springframework.boot.endpoint.EndpointPathResolver;
/**
* {@link EndpointPathResolver} implementation for resolving actuator endpoint paths based
* on the endpoint id and management.context-path.
*
* @author Madhura Bhave
* @since 2.0.0
*/
public class ManagementEndpointPathResolver implements EndpointPathResolver {
private final String contextPath;
public ManagementEndpointPathResolver(ManagementServerProperties properties) {
this.contextPath = properties.getContextPath();
}
@Override
public String resolvePath(String endpointId) {
return this.contextPath + "/" + endpointId;
}
}

View File

@@ -1,136 +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 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.EndpointExposure;
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.Assert;
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 attributes = getEndpointAttributes(context, metadata);
EndpointEnablement endpointEnablement = attributes
.getEnablement(new EndpointEnablementProvider(context.getEnvironment()));
return new ConditionOutcome(endpointEnablement.isEnabled(),
ConditionMessage.forCondition(ConditionalOnEnabledEndpoint.class)
.because(endpointEnablement.getReason()));
}
private EndpointAttributes getEndpointAttributes(ConditionContext context,
AnnotatedTypeMetadata metadata) {
Assert.state(
metadata instanceof MethodMetadata
&& metadata.isAnnotated(Bean.class.getName()),
"OnEnabledEndpointCondition may only be used on @Bean methods");
return getEndpointAttributes(context, (MethodMetadata) metadata);
}
private EndpointAttributes getEndpointAttributes(ConditionContext context,
MethodMetadata methodMetadata) {
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);
}
}
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 exposure technologies are exposed
EndpointExposure[] exposures = endpoint.exposure();
return new EndpointAttributes(endpoint.id(), endpoint.enabledByDefault(),
(exposures.length == 1 ? exposures[0] : null));
}
private static class EndpointAttributes {
private final String id;
private final boolean enabled;
private final EndpointExposure exposure;
EndpointAttributes(String id, boolean enabled, EndpointExposure exposure) {
if (!StringUtils.hasText(id)) {
throw new IllegalStateException("Endpoint id could not be determined");
}
this.id = id;
this.enabled = enabled;
this.exposure = exposure;
}
public EndpointEnablement getEnablement(EndpointEnablementProvider provider) {
return provider.getEndpointEnablement(this.id, this.enabled, this.exposure);
}
}
}

View File

@@ -1,48 +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.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);
}
}

View File

@@ -1,114 +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.infrastructure;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for MVC endpoints' CORS support.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "management.endpoints.cors")
public class CorsEndpointProperties {
/**
* Comma-separated list of origins to allow. '*' allows all origins. When not set,
* CORS support is disabled.
*/
private List<String> allowedOrigins = new ArrayList<>();
/**
* Comma-separated list of methods to allow. '*' allows all methods. When not set,
* defaults to GET.
*/
private List<String> allowedMethods = new ArrayList<>();
/**
* Comma-separated list of headers to allow in a request. '*' allows all headers.
*/
private List<String> allowedHeaders = new ArrayList<>();
/**
* Comma-separated list of headers to include in a response.
*/
private List<String> exposedHeaders = new ArrayList<>();
/**
* Set whether credentials are supported. When not set, credentials are not supported.
*/
private Boolean allowCredentials;
/**
* How long, in seconds, the response from a pre-flight request can be cached by
* clients.
*/
private Long maxAge = 1800L;
public List<String> getAllowedOrigins() {
return this.allowedOrigins;
}
public void setAllowedOrigins(List<String> allowedOrigins) {
this.allowedOrigins = allowedOrigins;
}
public List<String> getAllowedMethods() {
return this.allowedMethods;
}
public void setAllowedMethods(List<String> allowedMethods) {
this.allowedMethods = allowedMethods;
}
public List<String> getAllowedHeaders() {
return this.allowedHeaders;
}
public void setAllowedHeaders(List<String> allowedHeaders) {
this.allowedHeaders = allowedHeaders;
}
public List<String> getExposedHeaders() {
return this.exposedHeaders;
}
public void setExposedHeaders(List<String> exposedHeaders) {
this.exposedHeaders = exposedHeaders;
}
public Boolean getAllowCredentials() {
return this.allowCredentials;
}
public void setAllowCredentials(Boolean allowCredentials) {
this.allowCredentials = allowCredentials;
}
public Long getMaxAge() {
return this.maxAge;
}
public void setMaxAge(Long maxAge) {
this.maxAge = maxAge;
}
}

View File

@@ -1,85 +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.infrastructure;
import java.util.Map;
import javax.management.MBeanServer;
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.ObjectUtils;
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 final JmxEndpointExporterProperties properties;
private final MBeanServer mBeanServer;
private final String contextId;
DefaultEndpointObjectNameFactory(JmxEndpointExporterProperties properties,
MBeanServer mBeanServer, String contextId) {
this.properties = properties;
this.mBeanServer = mBeanServer;
this.contextId = contextId;
}
@Override
public ObjectName generate(EndpointMBean mBean) throws MalformedObjectNameException {
String baseObjectName = this.properties.getDomain() + ":type=Endpoint" + ",name="
+ StringUtils.capitalize(mBean.getEndpointId());
StringBuilder builder = new StringBuilder(baseObjectName);
if (this.mBeanServer != null && hasMBean(baseObjectName)) {
builder.append(",context=").append(this.contextId);
}
if (this.properties.isUniqueNames()) {
builder.append(",identity=").append(ObjectUtils.getIdentityHexString(mBean));
}
builder.append(getStaticNames());
return ObjectNameManager.getInstance(builder.toString());
}
private boolean hasMBean(String baseObjectName) throws MalformedObjectNameException {
ObjectName query = new ObjectName(baseObjectName + ",*");
return this.mBeanServer.queryNames(query, null).size() > 0;
}
private String getStaticNames() {
if (this.properties.getStaticNames().isEmpty()) {
return "";
}
StringBuilder builder = new StringBuilder();
for (Map.Entry<Object, Object> name : this.properties.getStaticNames()
.entrySet()) {
builder.append(",").append(name.getKey()).append("=").append(name.getValue());
}
return builder.toString();
}
}

View File

@@ -1,149 +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.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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.endpoint.ConversionServiceOperationParameterMapper;
import org.springframework.boot.endpoint.EndpointExposure;
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.ObjectUtils;
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)
@EnableConfigurationProperties(JmxEndpointExporterProperties.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, this::convertToDate);
return new ConversionServiceOperationParameterMapper(conversionService);
}
private Date convertToDate(String value) {
if (StringUtils.hasLength(value)) {
OffsetDateTime offsetDateTime = OffsetDateTime.parse(value,
DateTimeFormatter.ISO_OFFSET_DATE_TIME);
return new Date(offsetDateTime.toEpochSecond() * 1000);
}
return null;
}
@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(JmxEndpointExporterProperties properties,
MBeanServer mBeanServer, JmxAnnotationEndpointDiscoverer endpointDiscoverer,
ObjectProvider<ObjectMapper> objectMapper) {
EndpointProvider<JmxEndpointOperation> endpointProvider = new EndpointProvider<>(
this.applicationContext.getEnvironment(), endpointDiscoverer,
EndpointExposure.JMX);
EndpointMBeanRegistrar endpointMBeanRegistrar = new EndpointMBeanRegistrar(
mBeanServer, new DefaultEndpointObjectNameFactory(properties, mBeanServer,
ObjectUtils.getIdentityHexString(this.applicationContext)));
return new JmxEndpointExporter(endpointProvider, endpointMBeanRegistrar,
objectMapper.getIfAvailable(ObjectMapper::new));
}
@Configuration
@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),
EndpointExposure.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);
}
}
}

View File

@@ -1,68 +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.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.EndpointExposure;
import org.springframework.boot.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.Operation;
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 Operation> {
private final EndpointDiscoverer<T> discoverer;
private final EndpointEnablementProvider endpointEnablementProvider;
private final EndpointExposure exposure;
/**
* 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 exposure the exposure technology for the endpoint
*/
public EndpointProvider(Environment environment, EndpointDiscoverer<T> discoverer,
EndpointExposure exposure) {
this.discoverer = discoverer;
this.endpointEnablementProvider = new EndpointEnablementProvider(environment);
this.exposure = exposure;
}
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.exposure).isEnabled();
}
}

View File

@@ -1,133 +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.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;
}
}
}

View File

@@ -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.autoconfigure.endpoint.infrastructure;
import java.util.Properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* Configuration properties for JMX export of endpoints.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@ConfigurationProperties("management.endpoints.jmx")
public class JmxEndpointExporterProperties {
/**
* Endpoints JMX domain name. Fallback to 'spring.jmx.default-domain' if set.
*/
private String domain = "org.springframework.boot";
/**
* Ensure that ObjectNames are modified in case of conflict.
*/
private boolean uniqueNames = false;
/**
* Additional static properties to append to all ObjectNames of MBeans representing
* Endpoints.
*/
private final Properties staticNames = new Properties();
public JmxEndpointExporterProperties(Environment environment) {
String defaultDomain = environment.getProperty("spring.jmx.default-domain");
if (StringUtils.hasText(defaultDomain)) {
this.domain = defaultDomain;
}
}
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;
}
}

View File

@@ -1,89 +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.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()));
}
}

View File

@@ -1,63 +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.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);
}
}
}

View File

@@ -1,85 +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.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);
}
}
}

View File

@@ -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.autoconfigure.endpoint.infrastructure;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
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.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.ManagementErrorEndpoint;
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.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.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.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* 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
*/
@Configuration
@ManagementContextConfiguration(ManagementContextType.CHILD)
@ConditionalOnWebApplication(type = Type.SERVLET)
class ServletEndpointChildManagementContextConfiguration {
@Bean
public ManagementServletWebServerFactoryCustomization serverCustomization(
ListableBeanFactory beanFactory) {
return new ManagementServletWebServerFactoryCustomization(beanFactory);
}
@Bean
public UndertowAccessLogCustomizer undertowAccessLogCustomizer() {
return new UndertowAccessLogCustomizer();
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.valves.AccessLogValve")
public TomcatAccessLogCustomizer tomcatAccessLogCustomizer() {
return new TomcatAccessLogCustomizer();
}
@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
@ConditionalOnClass(ResourceConfig.class)
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
static class JerseyEndpointChildContextConfiguration {
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;
}
}
@Configuration
@ConditionalOnClass({ EnableWebSecurity.class, Filter.class })
@ConditionalOnBean(name = "springSecurityFilterChain", search = SearchStrategy.ANCESTORS)
static class EndpointWebMvcChildContextSecurityConfiguration {
@Bean
public Filter springSecurityFilterChain(HierarchicalBeanFactory beanFactory) {
BeanFactory parent = beanFactory.getParentBeanFactory();
return parent.getBean("springSecurityFilterChain", Filter.class);
}
}
static class ManagementServletWebServerFactoryCustomization extends
ManagementWebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
ManagementServletWebServerFactoryCustomization(ListableBeanFactory beanFactory) {
super(beanFactory, DefaultServletWebServerFactoryCustomizer.class);
}
@Override
protected void customize(ConfigurableServletWebServerFactory webServerFactory,
ManagementServerProperties managementServerProperties,
ServerProperties serverProperties) {
super.customize(webServerFactory, managementServerProperties,
serverProperties);
webServerFactory.setContextPath("");
}
}
static class CompositeHandlerMapping implements HandlerMapping {
@Autowired
private ListableBeanFactory beanFactory;
private List<HandlerMapping> mappings;
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request)
throws Exception {
if (this.mappings == null) {
this.mappings = extractMappings();
}
for (HandlerMapping mapping : this.mappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
private List<HandlerMapping> extractMappings() {
List<HandlerMapping> list = new ArrayList<>();
list.addAll(this.beanFactory.getBeansOfType(HandlerMapping.class).values());
list.remove(this);
AnnotationAwareOrderComparator.sort(list);
return list;
}
}
static class CompositeHandlerAdapter implements HandlerAdapter {
@Autowired
private ListableBeanFactory beanFactory;
private List<HandlerAdapter> adapters;
private List<HandlerAdapter> extractAdapters() {
List<HandlerAdapter> list = new ArrayList<>();
list.addAll(this.beanFactory.getBeansOfType(HandlerAdapter.class).values());
list.remove(this);
AnnotationAwareOrderComparator.sort(list);
return list;
}
@Override
public boolean supports(Object handler) {
if (this.adapters == null) {
this.adapters = extractAdapters();
}
for (HandlerAdapter mapping : this.adapters) {
if (mapping.supports(handler)) {
return true;
}
}
return false;
}
@Override
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (this.adapters == null) {
this.adapters = extractAdapters();
}
for (HandlerAdapter mapping : this.adapters) {
if (mapping.supports(handler)) {
return mapping.handle(request, response, handler);
}
}
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (this.adapters == null) {
this.adapters = extractAdapters();
}
for (HandlerAdapter mapping : this.adapters) {
if (mapping.supports(handler)) {
return mapping.getLastModified(request, handler);
}
}
return 0;
}
}
static class CompositeHandlerExceptionResolver implements HandlerExceptionResolver {
@Autowired
private ListableBeanFactory beanFactory;
private List<HandlerExceptionResolver> resolvers;
private List<HandlerExceptionResolver> extractResolvers() {
List<HandlerExceptionResolver> list = new ArrayList<>();
list.addAll(this.beanFactory.getBeansOfType(HandlerExceptionResolver.class)
.values());
list.remove(this);
AnnotationAwareOrderComparator.sort(list);
return list;
}
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
if (this.resolvers == null) {
this.resolvers = extractResolvers();
}
for (HandlerExceptionResolver mapping : this.resolvers) {
ModelAndView mav = mapping.resolveException(request, response, handler,
ex);
if (mav != null) {
return mav;
}
}
return null;
}
}
static abstract class AccessLogCustomizer implements Ordered {
protected String customizePrefix(String prefix) {
return "management_" + prefix;
}
@Override
public int getOrder() {
return 1;
}
}
static class TomcatAccessLogCustomizer extends AccessLogCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory serverFactory) {
AccessLogValve accessLogValve = findAccessLogValve(serverFactory);
if (accessLogValve == null) {
return;
}
accessLogValve.setPrefix(customizePrefix(accessLogValve.getPrefix()));
}
private AccessLogValve findAccessLogValve(
TomcatServletWebServerFactory serverFactory) {
for (Valve engineValve : serverFactory.getEngineValves()) {
if (engineValve instanceof AccessLogValve) {
return (AccessLogValve) engineValve;
}
}
return null;
}
}
static class UndertowAccessLogCustomizer extends AccessLogCustomizer
implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
@Override
public void customize(UndertowServletWebServerFactory serverFactory) {
serverFactory.setAccessLogPrefix(
customizePrefix(serverFactory.getAccessLogPrefix()));
}
}
}

View File

@@ -1,148 +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.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.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.EndpointMapping;
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({ CorsEndpointProperties.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(
new EndpointMapping(
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,
CorsEndpointProperties corsProperties,
ManagementServerProperties managementServerProperties) {
WebEndpointServletHandlerMapping handlerMapping = new WebEndpointServletHandlerMapping(
new EndpointMapping(managementServerProperties.getContextPath()),
provider.getEndpoints(), getCorsConfiguration(corsProperties));
for (WebEndpointHandlerMappingCustomizer customizer : this.mappingCustomizers) {
customizer.customize(handlerMapping);
}
return handlerMapping;
}
private CorsConfiguration getCorsConfiguration(
CorsEndpointProperties 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(
new EndpointMapping(managementServerProperties.getContextPath()),
provider.getEndpoints());
}
}
}

View File

@@ -1,21 +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.
*/
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for the Actuator's endpoint infrastructure.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure;

View File

@@ -1,50 +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.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);
}
}

View File

@@ -1,21 +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.
*/
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for the Actuator's JMX endpoints.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;

View File

@@ -1,57 +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.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;
}
}

View File

@@ -1,175 +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.support;
import org.springframework.boot.endpoint.EndpointExposure;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;
/**
* 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 exposure the requested {@link EndpointExposure}
* @return the {@link EndpointEnablement} of that endpoint for the specified
* {@link EndpointExposure}
*/
public EndpointEnablement getEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointExposure exposure) {
Assert.hasText(endpointId, "Endpoint id must have a value");
Assert.isTrue(!endpointId.equals("default"),
"Endpoint id 'default' is a reserved "
+ "value and cannot be used by an endpoint");
EndpointEnablement result = findEnablement(endpointId, exposure);
if (result != null) {
return result;
}
result = findEnablement(getKey(endpointId, "enabled"));
if (result != null) {
return result;
}
// All endpoints specific attributes have been looked at. Checking default value
// for the endpoint
if (!enabledByDefault) {
return getDefaultEndpointEnablement(endpointId, false, exposure);
}
return getGlobalEndpointEnablement(endpointId, enabledByDefault, exposure);
}
private EndpointEnablement findEnablement(String endpointId,
EndpointExposure exposure) {
if (exposure != null) {
return findEnablement(getKey(endpointId, exposure));
}
return findEnablementForAnyExposureTechnology(endpointId);
}
private EndpointEnablement getGlobalEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointExposure exposure) {
EndpointEnablement result = findGlobalEndpointEnablement(exposure);
if (result != null) {
return result;
}
result = findEnablement(getKey("default", "enabled"));
if (result != null) {
return result;
}
return getDefaultEndpointEnablement(endpointId, enabledByDefault, exposure);
}
private EndpointEnablement findGlobalEndpointEnablement(EndpointExposure exposure) {
if (exposure != null) {
EndpointEnablement result = findEnablement(getKey("default", exposure));
if (result != null) {
return result;
}
if (!exposure.isEnabledByDefault()) {
return getDefaultEndpointEnablement("default", false, exposure);
}
return null;
}
return findEnablementForAnyExposureTechnology("default");
}
private EndpointEnablement findEnablementForAnyExposureTechnology(String endpointId) {
for (EndpointExposure candidate : EndpointExposure.values()) {
EndpointEnablement result = findEnablementForExposureTechnology(endpointId,
candidate);
if (result != null && result.isEnabled()) {
return result;
}
}
return null;
}
private EndpointEnablement findEnablementForExposureTechnology(String endpointId,
EndpointExposure exposure) {
String endpointTypeKey = getKey(endpointId, exposure);
return findEnablement(endpointTypeKey);
}
private EndpointEnablement getDefaultEndpointEnablement(String endpointId,
boolean enabledByDefault, EndpointExposure exposure) {
return new EndpointEnablement(enabledByDefault,
createDefaultEnablementMessage(endpointId, enabledByDefault, exposure));
}
private String createDefaultEnablementMessage(String endpointId,
boolean enabledByDefault, EndpointExposure exposure) {
StringBuilder message = new StringBuilder();
message.append(String.format("endpoint '%s' ", endpointId));
if (exposure != null) {
message.append(String.format("(%s) ", exposure.name().toLowerCase()));
}
message.append(String.format("is %s by default",
(enabledByDefault ? "enabled" : "disabled")));
return message.toString();
}
private String getKey(String endpointId, EndpointExposure exposure) {
return getKey(endpointId, exposure.name().toLowerCase() + ".enabled");
}
private String getKey(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 findEnablement(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;
}
}

View File

@@ -1,20 +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.
*/
/**
* Support classes for the Actuator's endpoint auto-configuration.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint.support;

View File

@@ -1,128 +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.web;
import java.util.Collections;
import java.util.Map;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorProperties;
import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.endpoint.StatusEndpoint;
import org.springframework.boot.actuate.endpoint.web.HealthReactiveWebEndpointExtension;
import org.springframework.boot.actuate.endpoint.web.HealthWebEndpointExtension;
import org.springframework.boot.actuate.endpoint.web.StatusReactiveWebEndpointExtension;
import org.springframework.boot.actuate.endpoint.web.StatusWebEndpointExtension;
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicatorFactory;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.HealthStatusHttpMapper;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration for web-specific health endpoints .
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@EnableConfigurationProperties(HealthIndicatorProperties.class)
public class HealthWebEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
public HealthStatusHttpMapper createHealthStatusHttpMapper(
HealthIndicatorProperties healthIndicatorProperties) {
HealthStatusHttpMapper statusHttpMapper = new HealthStatusHttpMapper();
if (healthIndicatorProperties.getHttpMapping() != null) {
statusHttpMapper.addStatusMapping(healthIndicatorProperties.getHttpMapping());
}
return statusHttpMapper;
}
@Configuration
@ConditionalOnWebApplication(type = Type.REACTIVE)
static class ReactiveWebHealthConfiguration {
private final ReactiveHealthIndicator reactiveHealthIndicator;
ReactiveWebHealthConfiguration(ObjectProvider<HealthAggregator> healthAggregator,
ObjectProvider<Map<String, ReactiveHealthIndicator>> reactiveHealthIndicators,
ObjectProvider<Map<String, HealthIndicator>> healthIndicators) {
this.reactiveHealthIndicator = new CompositeReactiveHealthIndicatorFactory()
.createReactiveHealthIndicator(
healthAggregator.getIfAvailable(OrderedHealthAggregator::new),
reactiveHealthIndicators
.getIfAvailable(Collections::emptyMap),
healthIndicators.getIfAvailable(Collections::emptyMap));
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(HealthEndpoint.class)
public HealthReactiveWebEndpointExtension healthWebEndpointExtension(
HealthStatusHttpMapper healthStatusHttpMapper) {
return new HealthReactiveWebEndpointExtension(this.reactiveHealthIndicator,
healthStatusHttpMapper);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(StatusEndpoint.class)
public StatusReactiveWebEndpointExtension statusWebEndpointExtension(
HealthStatusHttpMapper healthStatusHttpMapper) {
return new StatusReactiveWebEndpointExtension(this.reactiveHealthIndicator,
healthStatusHttpMapper);
}
}
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
static class ServletWebHealthConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(HealthEndpoint.class)
public HealthWebEndpointExtension healthWebEndpointExtension(
HealthEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) {
return new HealthWebEndpointExtension(delegate, healthStatusHttpMapper);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@ConditionalOnBean(StatusEndpoint.class)
public StatusWebEndpointExtension statusWebEndpointExtension(
StatusEndpoint delegate, HealthStatusHttpMapper healthStatusHttpMapper) {
return new StatusWebEndpointExtension(delegate, healthStatusHttpMapper);
}
}
}

View File

@@ -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.autoconfigure.endpoint.web;
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.ConditionalOnEnabledEndpoint;
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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Import;
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
@Import(HealthWebEndpointConfiguration.class)
public class WebEndpointManagementContextConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public HeapDumpWebEndpoint heapDumpWebEndpoint() {
return new HeapDumpWebEndpoint();
}
@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());
}
}
}

View File

@@ -1,21 +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.
*/
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
* Auto-configuration} for the Actuator's web endpoints.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint.web;

View File

@@ -1,70 +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.health;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.core.ResolvableType;
/**
* Base class for configurations that can combine source beans using a
* {@link CompositeHealthIndicator}.
*
* @param <H> the health indicator type
* @param <S> the bean source type
* @author Stephane Nicoll
* @since 2.0.0
*/
public abstract class CompositeHealthIndicatorConfiguration<H extends HealthIndicator, S> {
@Autowired
private HealthAggregator healthAggregator;
protected HealthIndicator createHealthIndicator(Map<String, S> beans) {
if (beans.size() == 1) {
return createHealthIndicator(beans.values().iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, S> entry : beans.entrySet()) {
composite.addHealthIndicator(entry.getKey(),
createHealthIndicator(entry.getValue()));
}
return composite;
}
@SuppressWarnings("unchecked")
protected H createHealthIndicator(S source) {
Class<?>[] generics = ResolvableType
.forClass(CompositeHealthIndicatorConfiguration.class, getClass())
.resolveGenerics();
Class<H> indicatorClass = (Class<H>) generics[0];
Class<S> sourceClass = (Class<S>) generics[1];
try {
return indicatorClass.getConstructor(sourceClass).newInstance(source);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create indicator " + indicatorClass
+ " for source " + sourceClass, ex);
}
}
}

View File

@@ -1,69 +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.health;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.CompositeReactiveHealthIndicator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.core.ResolvableType;
/**
* Reactive variant of {@link CompositeHealthIndicatorConfiguration}.
*
* @param <H> the health indicator type
* @param <S> the bean source type
* @author Stephane Nicoll
* @since 2.0.0
*/
public class CompositeReactiveHealthIndicatorConfiguration<H extends ReactiveHealthIndicator, S> {
@Autowired
private HealthAggregator healthAggregator;
protected ReactiveHealthIndicator createHealthIndicator(Map<String, S> beans) {
if (beans.size() == 1) {
return createHealthIndicator(beans.values().iterator().next());
}
CompositeReactiveHealthIndicator composite = new CompositeReactiveHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, S> entry : beans.entrySet()) {
composite.addHealthIndicator(entry.getKey(),
createHealthIndicator(entry.getValue()));
}
return composite;
}
@SuppressWarnings("unchecked")
protected H createHealthIndicator(S source) {
Class<?>[] generics = ResolvableType
.forClass(CompositeReactiveHealthIndicatorConfiguration.class, getClass())
.resolveGenerics();
Class<H> indicatorClass = (Class<H>) generics[0];
Class<S> sourceClass = (Class<S>) generics[1];
try {
return indicatorClass.getConstructor(sourceClass).newInstance(source);
}
catch (Exception ex) {
throw new IllegalStateException("Unable to create indicator " + indicatorClass
+ " for source " + sourceClass, ex);
}
}
}

View File

@@ -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.autoconfigure.health;
import java.util.Map;
import io.searchbox.client.JestClient;
import org.elasticsearch.client.Client;
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicatorProperties;
import org.springframework.boot.actuate.health.ElasticsearchJestHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Actual Elasticsearch health indicator configurations imported by
* {@link HealthIndicatorAutoConfiguration}.
*
* @author Stephane Nicoll
*/
class ElasticsearchHealthIndicatorConfiguration {
@Configuration
@ConditionalOnBean(Client.class)
@ConditionalOnEnabledHealthIndicator("elasticsearch")
@EnableConfigurationProperties(ElasticsearchHealthIndicatorProperties.class)
static class ElasticsearchClientHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<ElasticsearchHealthIndicator, Client> {
private final Map<String, Client> clients;
private final ElasticsearchHealthIndicatorProperties properties;
ElasticsearchClientHealthIndicatorConfiguration(Map<String, Client> clients,
ElasticsearchHealthIndicatorProperties properties) {
this.clients = clients;
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(name = "elasticsearchHealthIndicator")
public HealthIndicator elasticsearchHealthIndicator() {
return createHealthIndicator(this.clients);
}
@Override
protected ElasticsearchHealthIndicator createHealthIndicator(Client client) {
return new ElasticsearchHealthIndicator(client, this.properties);
}
}
@Configuration
@ConditionalOnBean(JestClient.class)
@ConditionalOnEnabledHealthIndicator("elasticsearch")
static class ElasticsearchJestHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<ElasticsearchJestHealthIndicator, JestClient> {
private final Map<String, JestClient> clients;
ElasticsearchJestHealthIndicatorConfiguration(Map<String, JestClient> clients) {
this.clients = clients;
}
@Bean
@ConditionalOnMissingBean(name = "elasticsearchHealthIndicator")
public HealthIndicator elasticsearchHealthIndicator() {
return createHealthIndicator(this.clients);
}
@Override
protected ElasticsearchJestHealthIndicator createHealthIndicator(
JestClient client) {
return new ElasticsearchJestHealthIndicator(client);
}
}
}

View File

@@ -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.autoconfigure.health;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
import org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration;
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthIndicator}s.
*
* @author Christian Dupuis
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Phillip Webb
* @author Tommy Ludwig
* @author Eddú Meléndez
* @author Eric Spiegelberg
* @since 2.0.0
*/
@Configuration
@AutoConfigureBefore({ EndpointAutoConfiguration.class })
@AutoConfigureAfter({ ActiveMQAutoConfiguration.class, ArtemisAutoConfiguration.class,
CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
CouchbaseDataAutoConfiguration.class, DataSourceAutoConfiguration.class,
ElasticsearchAutoConfiguration.class, JestAutoConfiguration.class,
LdapDataAutoConfiguration.class, MailSenderAutoConfiguration.class,
MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
Neo4jDataAutoConfiguration.class, RabbitAutoConfiguration.class,
RedisAutoConfiguration.class, SolrAutoConfiguration.class })
@EnableConfigurationProperties({ HealthIndicatorProperties.class })
@Import({ ReactiveHealthIndicatorsConfiguration.class,
HealthIndicatorsConfiguration.class })
public class HealthIndicatorAutoConfiguration {
private final HealthIndicatorProperties properties;
public HealthIndicatorAutoConfiguration(HealthIndicatorProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(HealthAggregator.class)
public OrderedHealthAggregator healthAggregator() {
OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
if (this.properties.getOrder() != null) {
healthAggregator.setStatusOrder(this.properties.getOrder());
}
return healthAggregator;
}
}

View File

@@ -1,59 +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.health;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for some health properties.
*
* @author Christian Dupuis
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "management.health.status")
public class HealthIndicatorProperties {
/**
* Comma-separated list of health statuses in order of severity.
*/
private List<String> order = null;
/**
* Mapping of health statuses to HttpStatus codes. By default, registered health
* statuses map to sensible defaults (i.e. UP maps to 200).
*/
private final Map<String, Integer> httpMapping = new HashMap<>();
public List<String> getOrder() {
return this.order;
}
public void setOrder(List<String> statusOrder) {
if (statusOrder != null && !statusOrder.isEmpty()) {
this.order = statusOrder;
}
}
public Map<String, Integer> getHttpMapping() {
return this.httpMapping;
}
}

View File

@@ -1,381 +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.health;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.jms.ConnectionFactory;
import javax.sql.DataSource;
import com.couchbase.client.java.Bucket;
import com.datastax.driver.core.Cluster;
import org.apache.solr.client.solrj.SolrClient;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.actuate.health.CassandraHealthIndicator;
import org.springframework.boot.actuate.health.CouchbaseHealthIndicator;
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.JmsHealthIndicator;
import org.springframework.boot.actuate.health.LdapHealthIndicator;
import org.springframework.boot.actuate.health.MailHealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator;
import org.springframework.boot.actuate.health.Neo4jHealthIndicator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
import org.springframework.boot.actuate.health.RedisHealthIndicator;
import org.springframework.boot.actuate.health.SolrHealthIndicator;
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.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.ldap.core.LdapOperations;
import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
* Configuration for available {@link HealthIndicator health indicators}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@Import({
ElasticsearchHealthIndicatorConfiguration.ElasticsearchClientHealthIndicatorConfiguration.class,
ElasticsearchHealthIndicatorConfiguration.ElasticsearchJestHealthIndicatorConfiguration.class })
public class HealthIndicatorsConfiguration {
@Bean
@ConditionalOnMissingBean(HealthIndicator.class)
public ApplicationHealthIndicator applicationHealthIndicator() {
return new ApplicationHealthIndicator();
}
@Configuration
@ConditionalOnClass({ CassandraOperations.class, Cluster.class })
@ConditionalOnBean(CassandraOperations.class)
@ConditionalOnEnabledHealthIndicator("cassandra")
public static class CassandraHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<CassandraHealthIndicator, CassandraOperations> {
private final Map<String, CassandraOperations> cassandraOperations;
public CassandraHealthIndicatorConfiguration(
Map<String, CassandraOperations> cassandraOperations) {
this.cassandraOperations = cassandraOperations;
}
@Bean
@ConditionalOnMissingBean(name = "cassandraHealthIndicator")
public HealthIndicator cassandraHealthIndicator() {
return createHealthIndicator(this.cassandraOperations);
}
}
@Configuration
@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })
@ConditionalOnBean(CouchbaseOperations.class)
@ConditionalOnEnabledHealthIndicator("couchbase")
public static class CouchbaseHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<CouchbaseHealthIndicator, CouchbaseOperations> {
private final Map<String, CouchbaseOperations> couchbaseOperations;
public CouchbaseHealthIndicatorConfiguration(
Map<String, CouchbaseOperations> couchbaseOperations) {
this.couchbaseOperations = couchbaseOperations;
}
@Bean
@ConditionalOnMissingBean(name = "couchbaseHealthIndicator")
public HealthIndicator couchbaseHealthIndicator() {
return createHealthIndicator(this.couchbaseOperations);
}
}
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnEnabledHealthIndicator("db")
public static class DataSourcesHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<DataSourceHealthIndicator, DataSource>
implements InitializingBean {
private final Map<String, DataSource> dataSources;
private final Collection<DataSourcePoolMetadataProvider> metadataProviders;
private DataSourcePoolMetadataProvider poolMetadataProvider;
public DataSourcesHealthIndicatorConfiguration(
ObjectProvider<Map<String, DataSource>> dataSources,
ObjectProvider<Collection<DataSourcePoolMetadataProvider>> metadataProviders) {
this.dataSources = filterDataSources(dataSources.getIfAvailable());
this.metadataProviders = metadataProviders.getIfAvailable();
}
private Map<String, DataSource> filterDataSources(
Map<String, DataSource> candidates) {
if (candidates == null) {
return null;
}
Map<String, DataSource> dataSources = new LinkedHashMap<>();
for (Map.Entry<String, DataSource> entry : candidates.entrySet()) {
if (!(entry.getValue() instanceof AbstractRoutingDataSource)) {
dataSources.put(entry.getKey(), entry.getValue());
}
}
return dataSources;
}
@Override
public void afterPropertiesSet() throws Exception {
this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
this.metadataProviders);
}
@Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator dbHealthIndicator() {
return createHealthIndicator(this.dataSources);
}
@Override
protected DataSourceHealthIndicator createHealthIndicator(DataSource source) {
return new DataSourceHealthIndicator(source, getValidationQuery(source));
}
private String getValidationQuery(DataSource source) {
DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
.getDataSourcePoolMetadata(source);
return (poolMetadata == null ? null : poolMetadata.getValidationQuery());
}
}
@Configuration
@ConditionalOnClass(LdapOperations.class)
@ConditionalOnBean(LdapOperations.class)
@ConditionalOnEnabledHealthIndicator("ldap")
public static class LdapHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<LdapHealthIndicator, LdapOperations> {
private final Map<String, LdapOperations> ldapOperations;
public LdapHealthIndicatorConfiguration(
Map<String, LdapOperations> ldapOperations) {
this.ldapOperations = ldapOperations;
}
@Bean
@ConditionalOnMissingBean(name = "ldapHealthIndicator")
public HealthIndicator ldapHealthIndicator() {
return createHealthIndicator(this.ldapOperations);
}
}
@Configuration
@ConditionalOnClass(MongoTemplate.class)
@ConditionalOnBean(MongoTemplate.class)
@ConditionalOnEnabledHealthIndicator("mongo")
public static class MongoHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<MongoHealthIndicator, MongoTemplate> {
private final Map<String, MongoTemplate> mongoTemplates;
public MongoHealthIndicatorConfiguration(
Map<String, MongoTemplate> mongoTemplates) {
this.mongoTemplates = mongoTemplates;
}
@Bean
@ConditionalOnMissingBean(name = "mongoHealthIndicator")
public HealthIndicator mongoHealthIndicator() {
return createHealthIndicator(this.mongoTemplates);
}
}
@Configuration
@ConditionalOnClass(SessionFactory.class)
@ConditionalOnBean(SessionFactory.class)
@ConditionalOnEnabledHealthIndicator("neo4j")
public static class Neo4jHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<Neo4jHealthIndicator, SessionFactory> {
private final Map<String, SessionFactory> sessionFactories;
public Neo4jHealthIndicatorConfiguration(
Map<String, SessionFactory> sessionFactories) {
this.sessionFactories = sessionFactories;
}
@Bean
@ConditionalOnMissingBean(name = "neo4jHealthIndicator")
public HealthIndicator neo4jHealthIndicator() {
return createHealthIndicator(this.sessionFactories);
}
}
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnEnabledHealthIndicator("redis")
public static class RedisHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<RedisHealthIndicator, RedisConnectionFactory> {
private final Map<String, RedisConnectionFactory> redisConnectionFactories;
public RedisHealthIndicatorConfiguration(
Map<String, RedisConnectionFactory> redisConnectionFactories) {
this.redisConnectionFactories = redisConnectionFactories;
}
@Bean
@ConditionalOnMissingBean(name = "redisHealthIndicator")
public HealthIndicator redisHealthIndicator() {
return createHealthIndicator(this.redisConnectionFactories);
}
}
@Configuration
@ConditionalOnClass(RabbitTemplate.class)
@ConditionalOnBean(RabbitTemplate.class)
@ConditionalOnEnabledHealthIndicator("rabbit")
public static class RabbitHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<RabbitHealthIndicator, RabbitTemplate> {
private final Map<String, RabbitTemplate> rabbitTemplates;
public RabbitHealthIndicatorConfiguration(
Map<String, RabbitTemplate> rabbitTemplates) {
this.rabbitTemplates = rabbitTemplates;
}
@Bean
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")
public HealthIndicator rabbitHealthIndicator() {
return createHealthIndicator(this.rabbitTemplates);
}
}
@Configuration
@ConditionalOnClass(SolrClient.class)
@ConditionalOnBean(SolrClient.class)
@ConditionalOnEnabledHealthIndicator("solr")
public static class SolrHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<SolrHealthIndicator, SolrClient> {
private final Map<String, SolrClient> solrClients;
public SolrHealthIndicatorConfiguration(Map<String, SolrClient> solrClients) {
this.solrClients = solrClients;
}
@Bean
@ConditionalOnMissingBean(name = "solrHealthIndicator")
public HealthIndicator solrHealthIndicator() {
return createHealthIndicator(this.solrClients);
}
}
@Configuration
@ConditionalOnEnabledHealthIndicator("diskspace")
public static class DiskSpaceHealthIndicatorConfiguration {
@Bean
@ConditionalOnMissingBean(name = "diskSpaceHealthIndicator")
public DiskSpaceHealthIndicator diskSpaceHealthIndicator(
DiskSpaceHealthIndicatorProperties properties) {
return new DiskSpaceHealthIndicator(properties);
}
@Bean
public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
return new DiskSpaceHealthIndicatorProperties();
}
}
@Configuration
@ConditionalOnClass(JavaMailSenderImpl.class)
@ConditionalOnBean(JavaMailSenderImpl.class)
@ConditionalOnEnabledHealthIndicator("mail")
public static class MailHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<MailHealthIndicator, JavaMailSenderImpl> {
private final Map<String, JavaMailSenderImpl> mailSenders;
public MailHealthIndicatorConfiguration(
ObjectProvider<Map<String, JavaMailSenderImpl>> mailSenders) {
this.mailSenders = mailSenders.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(name = "mailHealthIndicator")
public HealthIndicator mailHealthIndicator() {
return createHealthIndicator(this.mailSenders);
}
}
@Configuration
@ConditionalOnClass(ConnectionFactory.class)
@ConditionalOnBean(ConnectionFactory.class)
@ConditionalOnEnabledHealthIndicator("jms")
public static class JmsHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<JmsHealthIndicator, ConnectionFactory> {
private final Map<String, ConnectionFactory> connectionFactories;
public JmsHealthIndicatorConfiguration(
ObjectProvider<Map<String, ConnectionFactory>> connectionFactories) {
this.connectionFactories = connectionFactories.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(name = "jmsHealthIndicator")
public HealthIndicator jmsHealthIndicator() {
return createHealthIndicator(this.connectionFactories);
}
}
}

View File

@@ -1,63 +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.health;
import java.util.Map;
import reactor.core.publisher.Flux;
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
import org.springframework.boot.actuate.health.RedisReactiveHealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
/**
* Configuration for available {@link ReactiveHealthIndicator reactive health indicators}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(Flux.class)
public class ReactiveHealthIndicatorsConfiguration {
@Configuration
@ConditionalOnBean(ReactiveRedisConnectionFactory.class)
@ConditionalOnEnabledHealthIndicator("redis")
static class RedisHealthIndicatorConfiguration extends
CompositeReactiveHealthIndicatorConfiguration<RedisReactiveHealthIndicator, ReactiveRedisConnectionFactory> {
private final Map<String, ReactiveRedisConnectionFactory> redisConnectionFactories;
RedisHealthIndicatorConfiguration(
Map<String, ReactiveRedisConnectionFactory> redisConnectionFactories) {
this.redisConnectionFactories = redisConnectionFactories;
}
@Bean
@ConditionalOnMissingBean(name = "redisHealthIndicator")
public ReactiveHealthIndicator redisHealthIndicator() {
return createHealthIndicator(this.redisConnectionFactories);
}
}
}

View File

@@ -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.autoconfigure.info;
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 a default info contributor is enabled.
* Matches if the value of the {@code management.info.<name>.enabled} property is
* {@code true}. Otherwise, matches if the value of the
* {@code management.info.defaults.enabled} property is {@code true} or if it is not
* configured.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnEnabledInfoContributorCondition.class)
public @interface ConditionalOnEnabledInfoContributor {
/**
* The name of the info contributor.
* @return the name of the info contributor
*/
String value();
}

View File

@@ -1,89 +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.info;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.info.BuildInfoContributor;
import org.springframework.boot.actuate.info.EnvironmentInfoContributor;
import org.springframework.boot.actuate.info.GitInfoContributor;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.info.BuildProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
/**
* {@link EnableAutoConfiguration Auto-configuration} for standard
* {@link InfoContributor}s.
*
* @author Meang Akira Tanaka
* @author Stephane Nicoll
* @since 2.0.0
*/
@Configuration
@AutoConfigureAfter(ProjectInfoAutoConfiguration.class)
@AutoConfigureBefore(EndpointAutoConfiguration.class)
@EnableConfigurationProperties(InfoContributorProperties.class)
public class InfoContributorAutoConfiguration {
/**
* The default order for the core {@link InfoContributor} beans.
*/
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
private final InfoContributorProperties properties;
public InfoContributorAutoConfiguration(InfoContributorProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnEnabledInfoContributor("env")
@Order(DEFAULT_ORDER)
public EnvironmentInfoContributor envInfoContributor(
ConfigurableEnvironment environment) {
return new EnvironmentInfoContributor(environment);
}
@Bean
@ConditionalOnEnabledInfoContributor("git")
@ConditionalOnSingleCandidate(GitProperties.class)
@ConditionalOnMissingBean
@Order(DEFAULT_ORDER)
public GitInfoContributor gitInfoContributor(GitProperties gitProperties) {
return new GitInfoContributor(gitProperties, this.properties.getGit().getMode());
}
@Bean
@ConditionalOnEnabledInfoContributor("build")
@ConditionalOnSingleCandidate(BuildProperties.class)
@Order(DEFAULT_ORDER)
public InfoContributor buildInfoContributor(BuildProperties buildProperties) {
return new BuildInfoContributor(buildProperties);
}
}

View File

@@ -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.autoconfigure.info;
import org.springframework.boot.actuate.info.GitInfoContributor;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for core info contributors.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@ConfigurationProperties("management.info")
public class InfoContributorProperties {
private final Git git = new Git();
public Git getGit() {
return this.git;
}
public static class Git {
/**
* Mode to use to expose git information.
*/
private GitInfoContributor.Mode mode = GitInfoContributor.Mode.SIMPLE;
public GitInfoContributor.Mode getMode() {
return this.mode;
}
public void setMode(GitInfoContributor.Mode mode) {
this.mode = mode;
}
}
}

View File

@@ -1,81 +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.jolokia;
import org.jolokia.http.AgentServlet;
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
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.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.mvc.ServletWrappingController;
/**
* {@link ManagementContextConfiguration} for embedding Jolokia, a JMX-HTTP bridge giving
* an alternative to JSR-160 connectors.
* <p>
* This configuration will get automatically enabled as soon as the Jolokia
* {@link AgentServlet} is on the classpath. To disable it set
* {@code management.jolokia.enabled=false}.
* <p>
* Additional configuration parameters for Jolokia can be provided by specifying
* {@code management.jolokia.config.*} properties. See the
* <a href="http://jolokia.org">http://jolokia.org</a> web site for more information on
* supported configuration parameters.
*
* @author Christian Dupuis
* @author Dave Syer
* @author Andy Wilkinson
* @author Madhura Bhave
* @author Stephane Nicoll
* @since 2.0.0
*/
@ManagementContextConfiguration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ AgentServlet.class, ServletWrappingController.class })
@ConditionalOnProperty(value = "management.jolokia.enabled", havingValue = "true")
@EnableConfigurationProperties(JolokiaProperties.class)
public class JolokiaManagementContextConfiguration {
private final ManagementServletContext managementServletContext;
private final JolokiaProperties properties;
public JolokiaManagementContextConfiguration(
ManagementServletContext managementServletContext,
JolokiaProperties properties) {
this.managementServletContext = managementServletContext;
this.properties = properties;
}
@Bean
public ServletRegistrationBean<AgentServlet> jolokiaServlet() {
String path = this.managementServletContext.getContextPath()
+ this.properties.getPath();
String urlMapping = (path.endsWith("/") ? path + "*" : path + "/*");
ServletRegistrationBean<AgentServlet> registration = new ServletRegistrationBean<>(
new AgentServlet(), urlMapping);
registration.setInitParameters(this.properties.getConfig());
return registration;
}
}

View File

@@ -1,71 +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.jolokia;
import java.util.HashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for Jolokia.
*
* @author Christian Dupuis
* @author Dave Syer
* @author Stephane Nicoll
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "management.jolokia")
public class JolokiaProperties {
/**
* Enable Jolokia.
*/
private boolean enabled;
/**
* Path at which Jolokia will be available.
*/
private String path = "/jolokia";
/**
* Jolokia settings. These are traditionally set using servlet parameters. Refer to
* the documentation of Jolokia for more details.
*/
private final Map<String, String> config = new HashMap<>();
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
public Map<String, String> getConfig() {
return this.config;
}
}

View File

@@ -1,43 +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.metrics;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
/**
* Qualifier annotation for a metric reader that can be exported (to distinguish it from
* others that might be installed by the user for other purposes).
*
* @author Dave Syer
* @since 2.0.0
*/
@Qualifier
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ExportMetricReader {
}

View File

@@ -1,147 +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.metrics;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.endpoint.MetricsEndpointMetricReader;
import org.springframework.boot.actuate.metrics.export.Exporter;
import org.springframework.boot.actuate.metrics.export.MetricExportProperties;
import org.springframework.boot.actuate.metrics.export.MetricExporters;
import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.statsd.StatsdMetricWriter;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.util.CollectionUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics export.
*
* @author Dave Syer
* @author Simon Buettner
* @since 2.0.0
*/
@Configuration
@EnableScheduling
@ConditionalOnProperty(value = "spring.metrics.export.enabled", matchIfMissing = true)
@EnableConfigurationProperties
public class MetricExportAutoConfiguration {
private final MetricsEndpointMetricReader endpointReader;
private final List<MetricReader> readers;
private final Map<String, GaugeWriter> writers;
private final Map<String, Exporter> exporters;
public MetricExportAutoConfiguration(MetricExportProperties properties,
ObjectProvider<MetricsEndpointMetricReader> endpointReader,
@ExportMetricReader ObjectProvider<List<MetricReader>> readers,
@ExportMetricWriter ObjectProvider<Map<String, GaugeWriter>> writers,
ObjectProvider<Map<String, Exporter>> exporters) {
this.endpointReader = endpointReader.getIfAvailable();
this.readers = readers.getIfAvailable();
this.writers = writers.getIfAvailable();
this.exporters = exporters.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean(name = "metricWritersMetricExporter")
public SchedulingConfigurer metricWritersMetricExporter(
MetricExportProperties properties) {
Map<String, GaugeWriter> writers = new HashMap<>();
MetricReader reader = this.endpointReader;
if (reader == null && !CollectionUtils.isEmpty(this.readers)) {
reader = new CompositeMetricReader(
this.readers.toArray(new MetricReader[this.readers.size()]));
}
if (reader == null && CollectionUtils.isEmpty(this.exporters)) {
return new NoOpSchedulingConfigurer();
}
MetricExporters exporters = new MetricExporters(properties);
if (reader != null) {
if (!CollectionUtils.isEmpty(this.writers)) {
writers.putAll(this.writers);
}
exporters.setReader(reader);
exporters.setWriters(writers);
}
exporters.setExporters(this.exporters == null
? Collections.<String, Exporter>emptyMap() : this.exporters);
return exporters;
}
@Configuration
static class StatsdConfiguration {
@Bean
@ExportMetricWriter
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.metrics.export.statsd", name = "host")
public StatsdMetricWriter statsdMetricWriter(MetricExportProperties properties) {
MetricExportProperties.Statsd statsdProperties = properties.getStatsd();
return new StatsdMetricWriter(statsdProperties.getPrefix(),
statsdProperties.getHost(), statsdProperties.getPort());
}
}
@Configuration
protected static class MetricExportPropertiesConfiguration {
@Value("${spring.application.name:application}.${random.value:0000}")
private String prefix = "";
private String aggregateKeyPattern = "k.d";
@Bean(name = "spring.metrics.export-org.springframework.boot.actuate.metrics.export.MetricExportProperties")
@ConditionalOnMissingBean
public MetricExportProperties metricExportProperties() {
MetricExportProperties export = new MetricExportProperties();
export.getRedis().setPrefix("spring.metrics"
+ (this.prefix.length() > 0 ? "." : "") + this.prefix);
export.getAggregate().setPrefix(this.prefix);
export.getAggregate().setKeyPattern(this.aggregateKeyPattern);
return export;
}
}
private static class NoOpSchedulingConfigurer implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
}
}
}

View File

@@ -1,75 +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.metrics;
import javax.servlet.Servlet;
import javax.servlet.ServletRegistration;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilter;
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.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.HandlerMapping;
/**
* {@link EnableAutoConfiguration Auto-configuration} that records Servlet interactions
* with a {@link CounterService} and {@link GaugeService}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Andy Wilkinson
* @author Sebastian Kirsch
* @since 2.0.0
*/
@Configuration
@ConditionalOnBean({ CounterService.class, GaugeService.class })
@ConditionalOnClass({ Servlet.class, ServletRegistration.class,
OncePerRequestFilter.class, HandlerMapping.class })
@AutoConfigureAfter(MetricRepositoryAutoConfiguration.class)
@ConditionalOnProperty(prefix = "management.metrics.filter", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties({ MetricFilterProperties.class })
public class MetricFilterAutoConfiguration {
private final CounterService counterService;
private final GaugeService gaugeService;
private final MetricFilterProperties properties;
public MetricFilterAutoConfiguration(CounterService counterService,
GaugeService gaugeService, MetricFilterProperties properties) {
this.counterService = counterService;
this.gaugeService = gaugeService;
this.properties = properties;
}
@Bean
public MetricsFilter metricsFilter() {
return new MetricsFilter(this.counterService, this.gaugeService,
this.properties.getCounterSubmissions(),
this.properties.getGaugeSubmissions());
}
}

View File

@@ -1,69 +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.metrics;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilter;
import org.springframework.boot.actuate.metrics.web.servlet.MetricsFilterSubmission;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Configuration properties for the {@link MetricsFilter}.
*
* @author Sebastian Kirsch
* @author Phillip Webb
* @since 2.0.0
*/
@ConfigurationProperties(prefix = "management.metrics.filter")
public class MetricFilterProperties {
/**
* Submissions that should be made to the gauge.
*/
private Set<MetricsFilterSubmission> gaugeSubmissions;
/**
* Submissions that should be made to the counter.
*/
private Set<MetricsFilterSubmission> counterSubmissions;
public MetricFilterProperties() {
this.gaugeSubmissions = new HashSet<>(EnumSet.of(MetricsFilterSubmission.MERGED));
this.counterSubmissions = new HashSet<>(
EnumSet.of(MetricsFilterSubmission.MERGED));
}
public Set<MetricsFilterSubmission> getGaugeSubmissions() {
return this.gaugeSubmissions;
}
public void setGaugeSubmissions(Set<MetricsFilterSubmission> gaugeSubmissions) {
this.gaugeSubmissions = gaugeSubmissions;
}
public Set<MetricsFilterSubmission> getCounterSubmissions() {
return this.counterSubmissions;
}
public void setCounterSubmissions(Set<MetricsFilterSubmission> counterSubmissions) {
this.counterSubmissions = counterSubmissions;
}
}

View File

@@ -1,114 +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.metrics;
import com.codahale.metrics.MetricRegistry;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.buffer.BufferCounterService;
import org.springframework.boot.actuate.metrics.buffer.BufferGaugeService;
import org.springframework.boot.actuate.metrics.buffer.BufferMetricReader;
import org.springframework.boot.actuate.metrics.buffer.CounterBuffers;
import org.springframework.boot.actuate.metrics.buffer.GaugeBuffers;
import org.springframework.boot.actuate.metrics.export.Exporter;
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository;
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics services. Creates
* user-facing {@link GaugeService} and {@link CounterService} instances, and also back
* end repositories to catch the data pumped into them.
* <p>
* In general, even if metric data needs to be stored and analysed remotely, it is
* recommended to use in-memory storage to buffer metric updates locally as is done by the
* default {@link CounterBuffers} and {@link GaugeBuffers}. The values can be exported
* (e.g. on a periodic basis) using an {@link Exporter}, most implementations of which
* have optimizations for sending data to remote repositories.
* <p>
* If Spring Messaging is on the classpath and a {@link MessageChannel} called
* "metricsChannel" is also available, all metric update events are published additionally
* as messages on that channel. Additional analysis or actions can be taken by clients
* subscribing to that channel.
* <p>
* In addition if Dropwizard's metrics library is on the classpath a
* {@link MetricRegistry} will be created and the default counter and gauge services will
* switch to using it instead of the default repository. Users can create "special"
* Dropwizard metrics by prefixing their metric names with the appropriate type (e.g.
* "histogram.*", "meter.*". "timer.*") and sending them to the {@code GaugeService} or
* {@code CounterService}.
* <p>
* By default all metric updates go to all {@link MetricWriter} instances in the
* application context via a {@link MetricCopyExporter} firing every 5 seconds (disable
* this by setting {@code spring.metrics.export.enabled=false}).
*
* @see GaugeService
* @see CounterService
* @see MetricWriter
* @see InMemoryMetricRepository
* @see Exporter
* @author Dave Syer
* @since 2.0.0
*/
@Configuration
public class MetricRepositoryAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(GaugeService.class)
static class FastMetricServicesConfiguration {
@Bean
@ConditionalOnMissingBean
public CounterBuffers counterBuffers() {
return new CounterBuffers();
}
@Bean
@ConditionalOnMissingBean
public GaugeBuffers gaugeBuffers() {
return new GaugeBuffers();
}
@Bean
@ExportMetricReader
@ConditionalOnMissingBean
public BufferMetricReader actuatorMetricReader(CounterBuffers counters,
GaugeBuffers gauges) {
return new BufferMetricReader(counters, gauges);
}
@Bean
@ConditionalOnMissingBean(CounterService.class)
public BufferCounterService counterService(CounterBuffers writer) {
return new BufferCounterService(writer);
}
@Bean
@ConditionalOnMissingBean(GaugeService.class)
public BufferGaugeService gaugeService(GaugeBuffers writer) {
return new BufferGaugeService(writer);
}
}
}

View File

@@ -1,51 +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.metrics;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.metrics.writer.MessageChannelMetricWriter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.MessageChannel;
/**
* {@link EnableAutoConfiguration Auto-configuration} for writing metrics to a
* {@link MessageChannel}.
*
* @author Dave Syer
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(MessageChannel.class)
@ConditionalOnBean(name = "metricsChannel")
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsChannelAutoConfiguration {
@Bean
@ExportMetricWriter
@ConditionalOnMissingBean
public MessageChannelMetricWriter messageChannelMetricWriter(
@Qualifier("metricsChannel") MessageChannel channel) {
return new MessageChannelMetricWriter(channel);
}
}

View File

@@ -1,80 +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.metrics;
import com.codahale.metrics.MetricRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
import org.springframework.boot.actuate.metrics.dropwizard.ReservoirFactory;
import org.springframework.boot.actuate.metrics.reader.MetricRegistryMetricReader;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Dropwizard-based metrics.
*
* @author Dave Syer
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass(MetricRegistry.class)
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsDropwizardAutoConfiguration {
private final ReservoirFactory reservoirFactory;
public MetricsDropwizardAutoConfiguration(
ObjectProvider<ReservoirFactory> reservoirFactory) {
this.reservoirFactory = reservoirFactory.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean
public MetricRegistry metricRegistry() {
return new MetricRegistry();
}
@Bean
@ConditionalOnMissingBean({ DropwizardMetricServices.class, CounterService.class,
GaugeService.class })
public DropwizardMetricServices dropwizardMetricServices(
MetricRegistry metricRegistry) {
if (this.reservoirFactory == null) {
return new DropwizardMetricServices(metricRegistry);
}
else {
return new DropwizardMetricServices(metricRegistry, this.reservoirFactory);
}
}
@Bean
public MetricReaderPublicMetrics dropwizardPublicMetrics(
MetricRegistry metricRegistry) {
MetricRegistryMetricReader reader = new MetricRegistryMetricReader(
metricRegistry);
return new MetricReaderPublicMetrics(reader);
}
}

View File

@@ -1,170 +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.metrics;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.servlet.Servlet;
import javax.sql.DataSource;
import org.apache.catalina.startup.Tomcat;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.autoconfigure.cache.CacheStatisticsAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider;
import org.springframework.boot.actuate.endpoint.CachePublicMetrics;
import org.springframework.boot.actuate.endpoint.DataSourcePublicMetrics;
import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics;
import org.springframework.boot.actuate.endpoint.PublicMetrics;
import org.springframework.boot.actuate.endpoint.RichGaugeReaderPublicMetrics;
import org.springframework.boot.actuate.endpoint.SystemPublicMetrics;
import org.springframework.boot.actuate.endpoint.TomcatPublicMetrics;
import org.springframework.boot.actuate.metrics.integration.SpringIntegrationMetricReader;
import org.springframework.boot.actuate.metrics.reader.CompositeMetricReader;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.rich.RichGaugeReader;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
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.SearchStrategy;
import org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.EnableIntegrationManagement;
import org.springframework.integration.support.management.IntegrationManagementConfigurer;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link PublicMetrics}.
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Johannes Edmeier
* @author Artem Bilan
* @since 2.0.0
*/
@Configuration
@AutoConfigureBefore(EndpointAutoConfiguration.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class,
MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class,
IntegrationAutoConfiguration.class })
public class PublicMetricsAutoConfiguration {
private final List<MetricReader> metricReaders;
public PublicMetricsAutoConfiguration(
@ExportMetricReader ObjectProvider<List<MetricReader>> metricReaders) {
this.metricReaders = metricReaders.getIfAvailable();
}
@Bean
public SystemPublicMetrics systemPublicMetrics() {
return new SystemPublicMetrics();
}
@Bean
public MetricReaderPublicMetrics metricReaderPublicMetrics() {
return new MetricReaderPublicMetrics(
new CompositeMetricReader(this.metricReaders == null ? new MetricReader[0]
: this.metricReaders
.toArray(new MetricReader[this.metricReaders.size()])));
}
@Bean
@ConditionalOnBean(RichGaugeReader.class)
public RichGaugeReaderPublicMetrics richGaugePublicMetrics(
RichGaugeReader richGaugeReader) {
return new RichGaugeReaderPublicMetrics(richGaugeReader);
}
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnBean(DataSource.class)
static class DataSourceMetricsConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(DataSourcePoolMetadataProvider.class)
public DataSourcePublicMetrics dataSourcePublicMetrics() {
return new DataSourcePublicMetrics();
}
}
@Configuration
@ConditionalOnClass({ Servlet.class, Tomcat.class })
@ConditionalOnWebApplication
static class TomcatMetricsConfiguration {
@Bean
@ConditionalOnMissingBean
public TomcatPublicMetrics tomcatPublicMetrics() {
return new TomcatPublicMetrics();
}
}
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheManager.class)
static class CacheStatisticsConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(CacheStatisticsProvider.class)
public CachePublicMetrics cachePublicMetrics(
Map<String, CacheManager> cacheManagers,
Collection<CacheStatisticsProvider<?>> statisticsProviders) {
return new CachePublicMetrics(cacheManagers, statisticsProviders);
}
}
@Configuration
@ConditionalOnClass(EnableIntegrationManagement.class)
static class IntegrationMetricsConfiguration {
@Bean(name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME)
@ConditionalOnMissingBean(value = IntegrationManagementConfigurer.class, name = IntegrationManagementConfigurer.MANAGEMENT_CONFIGURER_NAME, search = SearchStrategy.CURRENT)
public IntegrationManagementConfigurer managementConfigurer() {
IntegrationManagementConfigurer configurer = new IntegrationManagementConfigurer();
configurer.setDefaultCountsEnabled(true);
configurer.setDefaultStatsEnabled(true);
return configurer;
}
@Bean
@ConditionalOnMissingBean(name = "springIntegrationPublicMetrics")
public MetricReaderPublicMetrics springIntegrationPublicMetrics(
IntegrationManagementConfigurer managementConfigurer) {
return new MetricReaderPublicMetrics(
new SpringIntegrationMetricReader(managementConfigurer));
}
}
}

View File

@@ -1,20 +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.
*/
/**
* Classes for general Actuator auto-configuration concerns.
*/
package org.springframework.boot.actuate.autoconfigure;

View File

@@ -1,48 +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.security;
import org.springframework.boot.actuate.autoconfigure.endpoint.ManagementEndpointPathResolver;
import org.springframework.boot.actuate.autoconfigure.web.ManagementServerProperties;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
import org.springframework.boot.endpoint.EndpointPathResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
/**
* Security configuration for management endpoints.
*
* @author Madhura Bhave
* @since 2.0.0
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ EnableWebSecurity.class })
@AutoConfigureBefore(SecurityAutoConfiguration.class)
public class ManagementWebSecurityAutoConfiguration {
@Bean
public EndpointPathResolver managementEndpointPathResolver(
ManagementServerProperties properties) {
return new ManagementEndpointPathResolver(properties);
}
}

View File

@@ -1,41 +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.trace;
import org.springframework.boot.actuate.trace.InMemoryTraceRepository;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link TraceRepository tracing}.
*
* @author Dave Syer
* @since 2.0.0
*/
@Configuration
public class TraceRepositoryAutoConfiguration {
@ConditionalOnMissingBean(TraceRepository.class)
@Bean
public InMemoryTraceRepository traceRepository() {
return new InMemoryTraceRepository();
}
}

View File

@@ -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.autoconfigure.trace;
import javax.servlet.Servlet;
import javax.servlet.ServletRegistration;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.actuate.trace.TraceProperties;
import org.springframework.boot.actuate.trace.TraceRepository;
import org.springframework.boot.actuate.trace.WebRequestTraceFilter;
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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorAttributes;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link WebRequestTraceFilter
* tracing}.
*
* @author Dave Syer
* @since 2.0.0
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, ServletRegistration.class })
@AutoConfigureAfter(TraceRepositoryAutoConfiguration.class)
@ConditionalOnProperty(prefix = "management.trace.filter", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(TraceProperties.class)
public class TraceWebFilterAutoConfiguration {
private final TraceRepository traceRepository;
private final TraceProperties traceProperties;
private final ErrorAttributes errorAttributes;
public TraceWebFilterAutoConfiguration(TraceRepository traceRepository,
TraceProperties traceProperties,
ObjectProvider<ErrorAttributes> errorAttributes) {
this.traceRepository = traceRepository;
this.traceProperties = traceProperties;
this.errorAttributes = errorAttributes.getIfAvailable();
}
@Bean
@ConditionalOnMissingBean
public WebRequestTraceFilter webRequestLoggingFilter(BeanFactory beanFactory) {
WebRequestTraceFilter filter = new WebRequestTraceFilter(this.traceRepository,
this.traceProperties);
if (this.errorAttributes != null) {
filter.setErrorAttributes(this.errorAttributes);
}
return filter;
}
}

View File

@@ -1,127 +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.web;
import java.net.InetAddress;
import org.springframework.boot.autoconfigure.security.SecurityPrerequisite;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.web.server.Ssl;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Properties for the management server (e.g. port and path settings).
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Vedran Pavic
* @see ServerProperties
*/
@ConfigurationProperties(prefix = "management", ignoreUnknownFields = true)
public class ManagementServerProperties implements SecurityPrerequisite {
/**
* Management endpoint HTTP port. Use the same port as the application by default.
*/
private Integer port;
@NestedConfigurationProperty
private Ssl ssl;
/**
* Network address that the management endpoints should bind to.
*/
private InetAddress address;
/**
* Management endpoint context-path.
*/
private String contextPath = "/application";
/**
* Add the "X-Application-Context" HTTP header in each response.
*/
private boolean addApplicationContextHeader = false;
/**
* Returns the management port or {@code null} if the
* {@link ServerProperties#getPort() server port} should be used.
* @return the port
* @see #setPort(Integer)
*/
public Integer getPort() {
return this.port;
}
/**
* Sets the port of the management server, use {@code null} if the
* {@link ServerProperties#getPort() server port} should be used. To disable use 0.
* @param port the port
*/
public void setPort(Integer port) {
this.port = port;
}
public Ssl getSsl() {
return this.ssl;
}
public void setSsl(Ssl ssl) {
this.ssl = ssl;
}
public InetAddress getAddress() {
return this.address;
}
public void setAddress(InetAddress address) {
this.address = address;
}
/**
* Return the context path with no trailing slash (i.e. the '/' root context is
* represented as the empty string).
* @return the context path (no trailing slash)
*/
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
Assert.notNull(contextPath, "ContextPath must not be null");
this.contextPath = cleanContextPath(contextPath);
}
private String cleanContextPath(String contextPath) {
if (StringUtils.hasText(contextPath) && contextPath.endsWith("/")) {
return contextPath.substring(0, contextPath.length() - 1);
}
return contextPath;
}
public boolean getAddApplicationContextHeader() {
return this.addApplicationContextHeader;
}
public void setAddApplicationContextHeader(boolean addApplicationContextHeader) {
this.addApplicationContextHeader = addApplicationContextHeader;
}
}

View File

@@ -1,20 +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.
*/
/**
* Auto-configuration for Actuator's web concerns.
*/
package org.springframework.boot.actuate.autoconfigure.web;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.actuate.beans;
import java.util.HashMap;
import java.util.List;
@@ -23,8 +23,8 @@ import java.util.Map;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;
@@ -35,6 +35,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
* @author Andy Wilkinson
* @since 2.0.0
*/
@Endpoint(id = "beans")
public class BeansEndpoint {

View File

@@ -15,7 +15,6 @@
*/
/**
* Actuator web server support.
*
* Actuator support relating to Spring Beans.
*/
package org.springframework.boot.actuate.web.server;
package org.springframework.boot.actuate.beans;

View File

@@ -14,16 +14,15 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.actuate.cache;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.springframework.boot.actuate.cache.CacheStatistics;
import org.springframework.boot.actuate.cache.CacheStatisticsProvider;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.PublicMetrics;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
@@ -36,7 +35,7 @@ import org.springframework.util.MultiValueMap;
* A {@link PublicMetrics} implementation that provides cache statistics.
*
* @author Stephane Nicoll
* @since 1.3.0
* @since 2.0.0
*/
public class CachePublicMetrics implements PublicMetrics {

View File

@@ -15,6 +15,6 @@
*/
/**
* Classes for cache statistics.
* Actuator support for cache statistics.
*/
package org.springframework.boot.actuate.cache;

View File

@@ -14,12 +14,15 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
package org.springframework.boot.actuate.cassandra;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.util.Assert;
@@ -28,7 +31,7 @@ import org.springframework.util.Assert;
* Cassandra data stores.
*
* @author Julien Dubois
* @since 1.3.0
* @since 2.0.0
*/
public class CassandraHealthIndicator extends AbstractHealthIndicator {

View File

@@ -1,67 +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.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
/**
* The specific access level granted to the cloud foundry user that's calling the
* endpoints.
*
* @author Madhura Bhave
*/
enum AccessLevel {
/**
* Restricted access to a limited set of endpoints.
*/
RESTRICTED("", "health", "info"),
/**
* Full access to all endpoints.
*/
FULL;
private static final String REQUEST_ATTRIBUTE = "cloudFoundryAccessLevel";
private final List<String> endpointPaths;
AccessLevel(String... endpointPaths) {
this.endpointPaths = Arrays.asList(endpointPaths);
}
/**
* Returns if the access level should allow access to the specified endpoint path.
* @param endpointPath the endpoint path
* @return {@code true} if access is allowed
*/
public boolean isAccessAllowed(String endpointPath) {
return this.endpointPaths.isEmpty() || this.endpointPaths.contains(endpointPath);
}
public void put(HttpServletRequest request) {
request.setAttribute(REQUEST_ATTRIBUTE, this);
}
public static AccessLevel get(HttpServletRequest request) {
return (AccessLevel) request.getAttribute(REQUEST_ATTRIBUTE);
}
}

View File

@@ -1,136 +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.Arrays;
import org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure.EndpointProvider;
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.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.WebEndpointOperation;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
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.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} to expose actuator endpoints for
* cloud foundry to use.
*
* @author Madhura Bhave
* @since 2.0.0
*/
@Configuration
@ConditionalOnProperty(prefix = "management.cloudfoundry", name = "enabled", matchIfMissing = true)
@AutoConfigureAfter(ServletEndpointAutoConfiguration.class)
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudFoundryActuatorAutoConfiguration {
/**
* Configuration for MVC endpoints on Cloud Foundry.
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@ConditionalOnBean(DispatcherServlet.class)
static class MvcWebEndpointConfiguration {
@Bean
public CloudFoundryWebEndpointServletHandlerMapping cloudFoundryWebEndpointServletHandlerMapping(
EndpointProvider<WebEndpointOperation> provider, Environment environment,
RestTemplateBuilder builder) {
return new CloudFoundryWebEndpointServletHandlerMapping(
new EndpointMapping("/cloudfoundryapplication"),
provider.getEndpoints(), getCorsConfiguration(),
getSecurityInterceptor(builder, environment));
}
private CloudFoundrySecurityInterceptor getSecurityInterceptor(
RestTemplateBuilder restTemplateBuilder, Environment environment) {
CloudFoundrySecurityService cloudfoundrySecurityService = getCloudFoundrySecurityService(
restTemplateBuilder, environment);
TokenValidator tokenValidator = new TokenValidator(
cloudfoundrySecurityService);
return new CloudFoundrySecurityInterceptor(tokenValidator,
cloudfoundrySecurityService,
environment.getProperty("vcap.application.application_id"));
}
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;
}
}
/**
* {@link WebSecurityConfigurer} to tell Spring Security to ignore cloudfoundry
* specific paths. The Cloud foundry endpoints are protected by their own security
* interceptor.
*/
@ConditionalOnClass(WebSecurity.class)
@Order(SecurityProperties.IGNORED_ORDER)
@Configuration
public static class IgnoredPathsWebSecurityConfigurer
implements WebSecurityConfigurer<WebSecurity> {
@Override
public void init(WebSecurity builder) throws Exception {
builder.ignoring().requestMatchers(
new AntPathRequestMatcher("/cloudfoundryapplication/**"));
}
@Override
public void configure(WebSecurity builder) throws Exception {
}
}
}

View File

@@ -1,92 +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 org.springframework.http.HttpStatus;
/**
* Authorization exceptions thrown to limit access to the endpoints.
*
* @author Madhura Bhave
*/
class CloudFoundryAuthorizationException extends RuntimeException {
private final Reason reason;
CloudFoundryAuthorizationException(Reason reason, String message) {
this(reason, message, null);
}
CloudFoundryAuthorizationException(Reason reason, String message, Throwable cause) {
super(message);
this.reason = reason;
}
/**
* Return the status code that should be returned to the client.
* @return the HTTP status code
*/
public HttpStatus getStatusCode() {
return getReason().getStatus();
}
/**
* Return the reason why the authorization exception was thrown.
* @return the reason
*/
public Reason getReason() {
return this.reason;
}
/**
* Reasons why the exception can be thrown.
*/
enum Reason {
ACCESS_DENIED(HttpStatus.FORBIDDEN),
INVALID_AUDIENCE(HttpStatus.UNAUTHORIZED),
INVALID_ISSUER(HttpStatus.UNAUTHORIZED),
INVALID_KEY_ID(HttpStatus.UNAUTHORIZED),
INVALID_SIGNATURE(HttpStatus.UNAUTHORIZED),
INVALID_TOKEN(HttpStatus.UNAUTHORIZED),
MISSING_AUTHORIZATION(HttpStatus.UNAUTHORIZED),
TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED),
UNSUPPORTED_TOKEN_SIGNING_ALGORITHM(HttpStatus.UNAUTHORIZED),
SERVICE_UNAVAILABLE(HttpStatus.SERVICE_UNAVAILABLE);
private final HttpStatus status;
Reason(HttpStatus status) {
this.status = status;
}
public HttpStatus getStatus() {
return this.status;
}
}
}

View File

@@ -1,145 +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 javax.servlet.http.HttpServletRequest;
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.util.StringUtils;
import org.springframework.web.cors.CorsUtils;
/**
* Security interceptor to validate the cloud foundry token.
*
* @author Madhura Bhave
*/
class CloudFoundrySecurityInterceptor {
private static final Log logger = LogFactory
.getLog(CloudFoundrySecurityInterceptor.class);
private final TokenValidator tokenValidator;
private final CloudFoundrySecurityService cloudFoundrySecurityService;
private final String applicationId;
private static SecurityResponse SUCCESS = SecurityResponse.success();
CloudFoundrySecurityInterceptor(TokenValidator tokenValidator,
CloudFoundrySecurityService cloudFoundrySecurityService,
String applicationId) {
this.tokenValidator = tokenValidator;
this.cloudFoundrySecurityService = cloudFoundrySecurityService;
this.applicationId = applicationId;
}
SecurityResponse preHandle(HttpServletRequest request, String endpointId) {
if (CorsUtils.isPreFlightRequest(request)) {
return SecurityResponse.success();
}
try {
if (!StringUtils.hasText(this.applicationId)) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.SERVICE_UNAVAILABLE,
"Application id is not available");
}
if (this.cloudFoundrySecurityService == null) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.SERVICE_UNAVAILABLE,
"Cloud controller URL is not available");
}
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
return SUCCESS;
}
check(request, endpointId);
}
catch (Exception ex) {
logger.error(ex);
if (ex instanceof CloudFoundryAuthorizationException) {
CloudFoundryAuthorizationException cfException = (CloudFoundryAuthorizationException) ex;
return new SecurityResponse(cfException.getStatusCode(),
"{\"security_error\":\"" + cfException.getMessage() + "\"}");
}
return new SecurityResponse(HttpStatus.INTERNAL_SERVER_ERROR,
ex.getMessage());
}
return SecurityResponse.success();
}
private void check(HttpServletRequest request, String path) throws Exception {
Token token = getToken(request);
this.tokenValidator.validate(token);
AccessLevel accessLevel = this.cloudFoundrySecurityService
.getAccessLevel(token.toString(), this.applicationId);
if (!accessLevel.isAccessAllowed(path)) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.ACCESS_DENIED,
"Access denied");
}
accessLevel.put(request);
}
private Token getToken(HttpServletRequest request) {
String authorization = request.getHeader("Authorization");
String bearerPrefix = "bearer ";
if (authorization == null
|| !authorization.toLowerCase().startsWith(bearerPrefix)) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.MISSING_AUTHORIZATION,
"Authorization header is missing or invalid");
}
return new Token(authorization.substring(bearerPrefix.length()));
}
/**
* Response from the security interceptor.
*/
static class SecurityResponse {
private final HttpStatus status;
private final String message;
SecurityResponse(HttpStatus status) {
this(status, null);
}
SecurityResponse(HttpStatus status, String message) {
this.status = status;
this.message = message;
}
public HttpStatus getStatus() {
return this.status;
}
public String getMessage() {
return this.message;
}
static SecurityResponse success() {
return new SecurityResponse(HttpStatus.OK);
}
}
}

View File

@@ -1,146 +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.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.util.Assert;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
/**
* Cloud Foundry security service to handle REST calls to the cloud controller and UAA.
*
* @author Madhura Bhave
*/
class CloudFoundrySecurityService {
private final RestTemplate restTemplate;
private final String cloudControllerUrl;
private String uaaUrl;
CloudFoundrySecurityService(RestTemplateBuilder restTemplateBuilder,
String cloudControllerUrl, boolean skipSslValidation) {
Assert.notNull(restTemplateBuilder, "RestTemplateBuilder must not be null");
Assert.notNull(cloudControllerUrl, "CloudControllerUrl must not be null");
if (skipSslValidation) {
restTemplateBuilder = restTemplateBuilder
.requestFactory(SkipSslVerificationHttpRequestFactory.class);
}
this.restTemplate = restTemplateBuilder.build();
this.cloudControllerUrl = cloudControllerUrl;
}
/**
* Return the access level that should be granted to the given token.
* @param token the token
* @param applicationId the cloud foundry application ID
* @return the access level that should be granted
* @throws CloudFoundryAuthorizationException if the token is not authorized
*/
public AccessLevel getAccessLevel(String token, String applicationId)
throws CloudFoundryAuthorizationException {
try {
URI uri = getPermissionsUri(applicationId);
RequestEntity<?> request = RequestEntity.get(uri)
.header("Authorization", "bearer " + token).build();
Map<?, ?> body = this.restTemplate.exchange(request, Map.class).getBody();
if (Boolean.TRUE.equals(body.get("read_sensitive_data"))) {
return AccessLevel.FULL;
}
return AccessLevel.RESTRICTED;
}
catch (HttpClientErrorException ex) {
if (ex.getStatusCode().equals(HttpStatus.FORBIDDEN)) {
throw new CloudFoundryAuthorizationException(Reason.ACCESS_DENIED,
"Access denied");
}
throw new CloudFoundryAuthorizationException(Reason.INVALID_TOKEN,
"Invalid token", ex);
}
catch (HttpServerErrorException ex) {
throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
"Cloud controller not reachable");
}
}
private URI getPermissionsUri(String applicationId) {
try {
return new URI(this.cloudControllerUrl + "/v2/apps/" + applicationId
+ "/permissions");
}
catch (URISyntaxException ex) {
throw new IllegalStateException(ex);
}
}
/**
* Return all token keys known by the UAA.
* @return a list of token keys
*/
public Map<String, String> fetchTokenKeys() {
try {
return extractTokenKeys(this.restTemplate
.getForObject(getUaaUrl() + "/token_keys", Map.class));
}
catch (HttpStatusCodeException e) {
throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
"UAA not reachable");
}
}
private Map<String, String> extractTokenKeys(Map<?, ?> response) {
Map<String, String> tokenKeys = new HashMap<>();
for (Object key : (List<?>) response.get("keys")) {
Map<?, ?> tokenKey = (Map<?, ?>) key;
tokenKeys.put((String) tokenKey.get("kid"), (String) tokenKey.get("value"));
}
return tokenKeys;
}
/**
* Return the URL of the UAA.
* @return the UAA url
*/
public String getUaaUrl() {
if (this.uaaUrl == null) {
try {
Map<?, ?> response = this.restTemplate
.getForObject(this.cloudControllerUrl + "/info", Map.class);
this.uaaUrl = (String) response.get("token_endpoint");
}
catch (HttpStatusCodeException ex) {
throw new CloudFoundryAuthorizationException(Reason.SERVICE_UNAVAILABLE,
"Unable to fetch token keys from UAA");
}
}
return this.uaaUrl;
}
}

View File

@@ -1,198 +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.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
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.endpoint.EndpointInfo;
import org.springframework.boot.endpoint.OperationInvoker;
import org.springframework.boot.endpoint.ParameterMappingException;
import org.springframework.boot.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.endpoint.web.EndpointMapping;
import org.springframework.boot.endpoint.web.Link;
import org.springframework.boot.endpoint.web.WebEndpointOperation;
import org.springframework.boot.endpoint.web.WebEndpointResponse;
import org.springframework.boot.endpoint.web.mvc.AbstractWebEndpointServletHandlerMapping;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
/**
* A custom {@link RequestMappingInfoHandlerMapping} that makes web endpoints available on
* Cloudfoundry specific URLS over HTTP using Spring MVC.
*
* @author Madhura Bhave
*/
class CloudFoundryWebEndpointServletHandlerMapping
extends AbstractWebEndpointServletHandlerMapping {
private final Method handle = ReflectionUtils.findMethod(OperationHandler.class,
"handle", HttpServletRequest.class, Map.class);
private final Method links = ReflectionUtils.findMethod(
CloudFoundryWebEndpointServletHandlerMapping.class, "links",
HttpServletRequest.class, HttpServletResponse.class);
private static final Log logger = LogFactory
.getLog(CloudFoundryWebEndpointServletHandlerMapping.class);
private final CloudFoundrySecurityInterceptor securityInterceptor;
private final EndpointLinksResolver endpointLinksResolver = new EndpointLinksResolver();
CloudFoundryWebEndpointServletHandlerMapping(EndpointMapping endpointMapping,
Collection<EndpointInfo<WebEndpointOperation>> webEndpoints,
CorsConfiguration corsConfiguration,
CloudFoundrySecurityInterceptor securityInterceptor) {
super(endpointMapping, webEndpoints, corsConfiguration);
this.securityInterceptor = securityInterceptor;
}
@Override
protected Method getLinks() {
return this.links;
}
@ResponseBody
private Map<String, Map<String, Link>> links(HttpServletRequest request,
HttpServletResponse response) {
CloudFoundrySecurityInterceptor.SecurityResponse securityResponse = this.securityInterceptor
.preHandle(request, "");
if (!securityResponse.getStatus().equals(HttpStatus.OK)) {
sendFailureResponse(response, securityResponse);
}
AccessLevel accessLevel = AccessLevel.get(request);
Map<String, Link> links = this.endpointLinksResolver.resolveLinks(getEndpoints(),
request.getRequestURL().toString());
Map<String, Link> filteredLinks = new LinkedHashMap<>();
if (accessLevel == null) {
return Collections.singletonMap("_links", filteredLinks);
}
filteredLinks = links.entrySet().stream()
.filter((e) -> e.getKey().equals("self")
|| accessLevel.isAccessAllowed(e.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return Collections.singletonMap("_links", filteredLinks);
}
private void sendFailureResponse(HttpServletResponse response,
CloudFoundrySecurityInterceptor.SecurityResponse securityResponse) {
try {
response.sendError(securityResponse.getStatus().value(),
securityResponse.getMessage());
}
catch (Exception ex) {
logger.debug("Failed to send error response", ex);
}
}
@Override
protected void registerMappingForOperation(WebEndpointOperation operation) {
registerMapping(createRequestMappingInfo(operation),
new OperationHandler(operation.getInvoker(), operation.getId(),
this.securityInterceptor),
this.handle);
}
/**
* Handler which has the handler method and security interceptor.
*/
final class OperationHandler {
private final OperationInvoker operationInvoker;
private final String endpointId;
private final CloudFoundrySecurityInterceptor securityInterceptor;
OperationHandler(OperationInvoker operationInvoker, String id,
CloudFoundrySecurityInterceptor securityInterceptor) {
this.operationInvoker = operationInvoker;
this.endpointId = id;
this.securityInterceptor = securityInterceptor;
}
@SuppressWarnings("unchecked")
@ResponseBody
public Object handle(HttpServletRequest request,
@RequestBody(required = false) Map<String, String> body) {
CloudFoundrySecurityInterceptor.SecurityResponse securityResponse = this.securityInterceptor
.preHandle(request, this.endpointId);
if (!securityResponse.getStatus().equals(HttpStatus.OK)) {
return failureResponse(securityResponse);
}
Map<String, Object> arguments = new HashMap<>((Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE));
HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod());
if (body != null && HttpMethod.POST == httpMethod) {
arguments.putAll(body);
}
request.getParameterMap().forEach((name, values) -> arguments.put(name,
values.length == 1 ? values[0] : Arrays.asList(values)));
try {
return handleResult(this.operationInvoker.invoke(arguments), httpMethod);
}
catch (ParameterMappingException ex) {
return new ResponseEntity<Void>(HttpStatus.BAD_REQUEST);
}
}
private Object failureResponse(
CloudFoundrySecurityInterceptor.SecurityResponse response) {
return handleResult(new WebEndpointResponse<>(response.getMessage(),
response.getStatus().value()));
}
private Object handleResult(Object result) {
return handleResult(result, null);
}
private Object handleResult(Object result, HttpMethod httpMethod) {
if (result == null) {
return new ResponseEntity<>(httpMethod == HttpMethod.GET
? HttpStatus.NOT_FOUND : HttpStatus.NO_CONTENT);
}
if (!(result instanceof WebEndpointResponse)) {
return result;
}
WebEndpointResponse<?> response = (WebEndpointResponse<?>) result;
return new ResponseEntity<Object>(response.getBody(),
HttpStatus.valueOf(response.getStatus()));
}
}
}

View File

@@ -1,93 +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.io.IOException;
import java.net.HttpURLConnection;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
/**
* {@link SimpleClientHttpRequestFactory} that skips SSL certificate verification.
*
* @author Madhura Bhave
*/
class SkipSslVerificationHttpRequestFactory extends SimpleClientHttpRequestFactory {
@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod)
throws IOException {
if (connection instanceof HttpsURLConnection) {
prepareHttpsConnection((HttpsURLConnection) connection);
}
super.prepareConnection(connection, httpMethod);
}
private void prepareHttpsConnection(HttpsURLConnection connection) {
connection.setHostnameVerifier(new SkipHostnameVerifier());
try {
connection.setSSLSocketFactory(createSslSocketFactory());
}
catch (Exception ex) {
// Ignore
}
}
private SSLSocketFactory createSslSocketFactory() throws Exception {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] { new SkipX509TrustManager() },
new SecureRandom());
return context.getSocketFactory();
}
private class SkipHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
private static class SkipX509TrustManager implements X509TrustManager {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
}
}

View File

@@ -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.cloudfoundry;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import org.springframework.boot.json.JsonParserFactory;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
/**
* The JSON web token provided with each request that originates from Cloud Foundry.
*
* @author Madhura Bhave
*/
class Token {
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final String encoded;
private final String signature;
private final Map<String, Object> header;
private final Map<String, Object> claims;
Token(String encoded) {
this.encoded = encoded;
int firstPeriod = encoded.indexOf('.');
int lastPeriod = encoded.lastIndexOf('.');
if (firstPeriod <= 0 || lastPeriod <= firstPeriod) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.INVALID_TOKEN,
"JWT must have header, body and signature");
}
this.header = parseJson(encoded.substring(0, firstPeriod));
this.claims = parseJson(encoded.substring(firstPeriod + 1, lastPeriod));
this.signature = encoded.substring(lastPeriod + 1);
if (!StringUtils.hasLength(this.signature)) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.INVALID_TOKEN,
"Token must have non-empty crypto segment");
}
}
private Map<String, Object> parseJson(String base64) {
try {
byte[] bytes = Base64Utils.decodeFromUrlSafeString(base64);
return JsonParserFactory.getJsonParser().parseMap(new String(bytes, UTF_8));
}
catch (RuntimeException ex) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.INVALID_TOKEN,
"Token could not be parsed", ex);
}
}
public byte[] getContent() {
return this.encoded.substring(0, this.encoded.lastIndexOf(".")).getBytes();
}
public byte[] getSignature() {
return Base64Utils.decodeFromUrlSafeString(this.signature);
}
public String getSignatureAlgorithm() {
return getRequired(this.header, "alg", String.class);
}
public String getIssuer() {
return getRequired(this.claims, "iss", String.class);
}
public long getExpiry() {
return getRequired(this.claims, "exp", Integer.class).longValue();
}
@SuppressWarnings("unchecked")
public List<String> getScope() {
return getRequired(this.claims, "scope", List.class);
}
public String getKeyId() {
return getRequired(this.header, "kid", String.class);
}
@SuppressWarnings("unchecked")
private <T> T getRequired(Map<String, Object> map, String key, Class<T> type) {
Object value = map.get(key);
if (value == null) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.INVALID_TOKEN,
"Unable to get value from key " + key);
}
if (!type.isInstance(value)) {
throw new CloudFoundryAuthorizationException(
CloudFoundryAuthorizationException.Reason.INVALID_TOKEN,
"Unexpected value type from key " + key + " value " + value);
}
return (T) value;
}
@Override
public String toString() {
return this.encoded;
};
}

View File

@@ -1,141 +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.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.actuate.cloudfoundry.CloudFoundryAuthorizationException.Reason;
import org.springframework.util.Base64Utils;
/**
* Validator used to ensure that a signed {@link Token} has not been tampered with.
*
* @author Madhura Bhave
*/
class TokenValidator {
private final CloudFoundrySecurityService securityService;
private Map<String, String> tokenKeys;
TokenValidator(CloudFoundrySecurityService cloudFoundrySecurityService) {
this.securityService = cloudFoundrySecurityService;
}
public void validate(Token token) {
validateAlgorithm(token);
validateKeyIdAndSignature(token);
validateExpiry(token);
validateIssuer(token);
validateAudience(token);
}
private void validateAlgorithm(Token token) {
String algorithm = token.getSignatureAlgorithm();
if (algorithm == null) {
throw new CloudFoundryAuthorizationException(Reason.INVALID_SIGNATURE,
"Signing algorithm cannot be null");
}
if (!algorithm.equals("RS256")) {
throw new CloudFoundryAuthorizationException(
Reason.UNSUPPORTED_TOKEN_SIGNING_ALGORITHM,
"Signing algorithm " + algorithm + " not supported");
}
}
private void validateKeyIdAndSignature(Token token) {
String keyId = token.getKeyId();
if (this.tokenKeys == null || !hasValidKeyId(keyId)) {
this.tokenKeys = this.securityService.fetchTokenKeys();
if (!hasValidKeyId(keyId)) {
throw new CloudFoundryAuthorizationException(Reason.INVALID_KEY_ID,
"Key Id present in token header does not match");
}
}
if (!hasValidSignature(token, this.tokenKeys.get(keyId))) {
throw new CloudFoundryAuthorizationException(Reason.INVALID_SIGNATURE,
"RSA Signature did not match content");
}
}
private boolean hasValidKeyId(String tokenKey) {
for (String candidate : this.tokenKeys.keySet()) {
if (tokenKey.equals(candidate)) {
return true;
}
}
return false;
}
private boolean hasValidSignature(Token token, String key) {
try {
PublicKey publicKey = getPublicKey(key);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(token.getContent());
return signature.verify(token.getSignature());
}
catch (GeneralSecurityException ex) {
return false;
}
}
private PublicKey getPublicKey(String key)
throws NoSuchAlgorithmException, InvalidKeySpecException {
key = key.replace("-----BEGIN PUBLIC KEY-----\n", "");
key = key.replace("-----END PUBLIC KEY-----", "");
key = key.trim().replace("\n", "");
byte[] bytes = Base64Utils.decodeFromString(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
}
private void validateExpiry(Token token) {
long currentTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
if (currentTime > token.getExpiry()) {
throw new CloudFoundryAuthorizationException(Reason.TOKEN_EXPIRED,
"Token expired");
}
}
private void validateIssuer(Token token) {
String uaaUrl = this.securityService.getUaaUrl();
String issuerUri = String.format("%s/oauth/token", uaaUrl);
if (!issuerUri.equals(token.getIssuer())) {
throw new CloudFoundryAuthorizationException(Reason.INVALID_ISSUER,
"Token issuer does not match " + uaaUrl + "/oauth/token");
}
}
private void validateAudience(Token token) {
if (!token.getScope().contains("actuator.read")) {
throw new CloudFoundryAuthorizationException(Reason.INVALID_AUDIENCE,
"Token does not have audience actuator");
}
}
}

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.actuate.context;
import java.util.Collections;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.boot.endpoint.Endpoint;
import org.springframework.boot.endpoint.WriteOperation;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
@@ -32,6 +32,7 @@ import org.springframework.context.ConfigurableApplicationContext;
* @author Dave Syer
* @author Christian Dupuis
* @author Andy Wilkinson
* @since 2.0.0
*/
@Endpoint(id = "shutdown", enabledByDefault = false)
public class ShutdownEndpoint implements ApplicationContextAware {

View File

@@ -15,6 +15,6 @@
*/
/**
* Auto-configuration for metrics.
* Actuator support relating to Spring Context.
*/
package org.springframework.boot.actuate.autoconfigure.metrics;
package org.springframework.boot.actuate.context;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.endpoint;
package org.springframework.boot.actuate.context.properties;
import java.util.ArrayList;
import java.util.Collection;
@@ -40,10 +40,11 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import org.springframework.beans.BeansException;
import org.springframework.boot.actuate.endpoint.Sanitizer;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
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;
@@ -62,6 +63,7 @@ import org.springframework.util.StringUtils;
* @author Christian Dupuis
* @author Dave Syer
* @author Stephane Nicoll
* @since 2.0.0
*/
@Endpoint(id = "configprops")
@ConfigurationProperties("endpoints.configprops")

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Actuator support relating to external configuration properties.
*/
package org.springframework.boot.actuate.context.properties;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 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.
@@ -14,12 +14,15 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
package org.springframework.boot.actuate.couchbase;
import java.util.List;
import com.couchbase.client.java.util.features.Version;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -28,7 +31,7 @@ import org.springframework.util.StringUtils;
* {@link HealthIndicator} for Couchbase.
*
* @author Eddú Meléndez
* @since 1.4.0
* @since 2.0.0
*/
public class CouchbaseHealthIndicator extends AbstractHealthIndicator {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 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.
@@ -14,43 +14,68 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
package org.springframework.boot.actuate.elasticsearch;
import java.util.List;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.util.ObjectUtils;
/**
* {@link HealthIndicator} for an Elasticsearch cluster.
*
* @author Binwei Yang
* @author Andy Wilkinson
* @since 1.3.0
* @since 2.0.0
*/
public class ElasticsearchHealthIndicator extends AbstractHealthIndicator {
private static final String[] allIndices = { "_all" };
private static final String[] ALL_INDICES = { "_all" };
private final Client client;
private final ElasticsearchHealthIndicatorProperties properties;
private final String[] indices;
public ElasticsearchHealthIndicator(Client client,
ElasticsearchHealthIndicatorProperties properties) {
private final long responseTimeout;
/**
* Create a new {@link ElasticsearchHealthIndicator} instance.
* @param client the Elasticsearch client
* @param responseTimeout the request timeout in milliseconds
* @param indices the indices to check
*/
public ElasticsearchHealthIndicator(Client client, long responseTimeout,
List<String> indices) {
this(client, responseTimeout, (indices == null ? (String[]) null
: indices.toArray(new String[indices.size()])));
}
/**
* Create a new {@link ElasticsearchHealthIndicator} instance.
* @param client the Elasticsearch client
* @param responseTimeout the request timeout in milliseconds
* @param indices the indices to check
*/
public ElasticsearchHealthIndicator(Client client, long responseTimeout,
String... indices) {
this.client = client;
this.properties = properties;
this.responseTimeout = responseTimeout;
this.indices = indices;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
List<String> indices = this.properties.getIndices();
ClusterHealthResponse response = this.client.admin().cluster()
.health(Requests.clusterHealthRequest(indices.isEmpty() ? allIndices
: indices.toArray(new String[indices.size()])))
.actionGet(this.properties.getResponseTimeout());
ClusterHealthRequest request = Requests.clusterHealthRequest(
ObjectUtils.isEmpty(this.indices) ? ALL_INDICES : this.indices);
ClusterHealthResponse response = this.client.admin().cluster().health(request)
.actionGet(this.responseTimeout);
switch (response.getStatus()) {
case GREEN:
case YELLOW:

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.health;
package org.springframework.boot.actuate.elasticsearch;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -23,11 +23,15 @@ import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.indices.Stats;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
/**
* {@link HealthIndicator} for Elasticsearch using a {@link JestClient}.
*
* @author Stephane Nicoll
* @since 1.4.0
* @since 2.0.0
*/
public class ElasticsearchJestHealthIndicator extends AbstractHealthIndicator {

View File

@@ -1,196 +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.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
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.endpoint.Endpoint;
import org.springframework.boot.endpoint.ReadOperation;
import org.springframework.context.annotation.Condition;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
* {@link Endpoint} to expose the {@link ConditionEvaluationReport}.
*
* @author Greg Turnquist
* @author Phillip Webb
* @author Dave Syer
* @author Andy Wilkinson
*/
@Endpoint(id = "autoconfig")
public class AutoConfigurationReportEndpoint {
private final ConditionEvaluationReport conditionEvaluationReport;
public AutoConfigurationReportEndpoint(
ConditionEvaluationReport conditionEvaluationReport) {
this.conditionEvaluationReport = conditionEvaluationReport;
}
@ReadOperation
public Report getEvaluationReport() {
return new Report(this.conditionEvaluationReport);
}
/**
* Adapts {@link ConditionEvaluationReport} to a JSON friendly structure.
*/
@JsonPropertyOrder({ "positiveMatches", "negativeMatches", "exclusions",
"unconditionalClasses" })
@JsonInclude(Include.NON_EMPTY)
public static class Report {
private final MultiValueMap<String, MessageAndCondition> positiveMatches;
private final Map<String, MessageAndConditions> negativeMatches;
private final List<String> exclusions;
private final Set<String> unconditionalClasses;
private final Report parent;
public Report(ConditionEvaluationReport report) {
this.positiveMatches = new LinkedMultiValueMap<>();
this.negativeMatches = new LinkedHashMap<>();
this.exclusions = report.getExclusions();
this.unconditionalClasses = report.getUnconditionalClasses();
for (Map.Entry<String, ConditionAndOutcomes> entry : report
.getConditionAndOutcomesBySource().entrySet()) {
if (entry.getValue().isFullMatch()) {
add(this.positiveMatches, entry.getKey(), entry.getValue());
}
else {
add(this.negativeMatches, entry.getKey(), entry.getValue());
}
}
boolean hasParent = report.getParent() != null;
this.parent = (hasParent ? new Report(report.getParent()) : null);
}
private void add(Map<String, MessageAndConditions> map, String source,
ConditionAndOutcomes conditionAndOutcomes) {
String name = ClassUtils.getShortName(source);
map.put(name, new MessageAndConditions(conditionAndOutcomes));
}
private void add(MultiValueMap<String, MessageAndCondition> map, String source,
ConditionAndOutcomes conditionAndOutcomes) {
String name = ClassUtils.getShortName(source);
for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
map.add(name, new MessageAndCondition(conditionAndOutcome));
}
}
public Map<String, List<MessageAndCondition>> getPositiveMatches() {
return this.positiveMatches;
}
public Map<String, MessageAndConditions> getNegativeMatches() {
return this.negativeMatches;
}
public List<String> getExclusions() {
return this.exclusions;
}
public Set<String> getUnconditionalClasses() {
return this.unconditionalClasses;
}
public Report getParent() {
return this.parent;
}
}
/**
* Adapts {@link ConditionAndOutcomes} to a JSON friendly structure.
*/
@JsonPropertyOrder({ "notMatched", "matched" })
public static class MessageAndConditions {
private final List<MessageAndCondition> notMatched = new ArrayList<>();
private final List<MessageAndCondition> matched = new ArrayList<>();
public MessageAndConditions(ConditionAndOutcomes conditionAndOutcomes) {
for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
List<MessageAndCondition> target = conditionAndOutcome.getOutcome()
.isMatch() ? this.matched : this.notMatched;
target.add(new MessageAndCondition(conditionAndOutcome));
}
}
public List<MessageAndCondition> getNotMatched() {
return this.notMatched;
}
public List<MessageAndCondition> getMatched() {
return this.matched;
}
}
/**
* Adapts {@link ConditionAndOutcome} to a JSON friendly structure.
*/
@JsonPropertyOrder({ "condition", "message" })
public static class MessageAndCondition {
private final String condition;
private final String message;
public MessageAndCondition(ConditionAndOutcome conditionAndOutcome) {
Condition condition = conditionAndOutcome.getCondition();
ConditionOutcome outcome = conditionAndOutcome.getOutcome();
this.condition = ClassUtils.getShortName(condition.getClass());
if (StringUtils.hasLength(outcome.getMessage())) {
this.message = outcome.getMessage();
}
else {
this.message = (outcome.isMatch() ? "matched" : "did not match");
}
}
public String getCondition() {
return this.condition;
}
public String getMessage() {
return this.message;
}
}
}

View File

@@ -14,24 +14,25 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.endpoint.infrastructure;
package org.springframework.boot.actuate.endpoint;
import org.springframework.boot.endpoint.web.mvc.WebEndpointServletHandlerMapping;
import java.util.Collection;
/**
* Callback for customizing the {@link WebEndpointServletHandlerMapping} at configuration
* time.
* Discovers endpoints and provides an {@link EndpointInfo} for each of them.
*
* @param <T> the type of the operation
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 2.0.0
*/
@FunctionalInterface
public interface WebEndpointHandlerMappingCustomizer {
public interface EndpointDiscoverer<T extends Operation> {
/**
* Customize the given {@code mapping}.
* @param mapping the mapping to customize
* Perform endpoint discovery.
* @return the discovered endpoints
*/
void customize(WebEndpointServletHandlerMapping mapping);
Collection<EndpointInfo<T>> discoverEndpoints();
}

View File

@@ -0,0 +1,73 @@
/*
* 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.Collection;
/**
* Information describing an endpoint.
*
* @param <T> the type of the endpoint's operations
* @author Andy Wilkinson
* @since 2.0.0
*/
public class EndpointInfo<T extends Operation> {
private final String id;
private final boolean enabledByDefault;
private final Collection<T> operations;
/**
* Creates a new {@code EndpointInfo} describing an endpoint with the given {@code id}
* and {@code operations}.
* @param id the id of the endpoint
* @param enabledByDefault whether or not the endpoint is enabled by default
* @param operations the operations of the endpoint
*/
public EndpointInfo(String id, boolean enabledByDefault, Collection<T> operations) {
this.id = id;
this.enabledByDefault = enabledByDefault;
this.operations = operations;
}
/**
* Returns the id of the endpoint.
* @return the id
*/
public String getId() {
return this.id;
}
/**
* Returns whether or not this endpoint is enabled by default.
* @return {@code true} if it is enabled by default, otherwise {@code false}
*/
public boolean isEnabledByDefault() {
return this.enabledByDefault;
}
/**
* Returns the operations of the endpoint.
* @return the operations
*/
public Collection<T> getOperations() {
return this.operations;
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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;
/**
* An operation on an endpoint.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
public class Operation {
private final OperationType type;
private final OperationInvoker invoker;
private final boolean blocking;
/**
* Creates a new {@code EndpointOperation} for an operation of the given {@code type}.
* The operation can be performed using the given {@code operationInvoker}.
* @param type the type of the operation
* @param operationInvoker used to perform the operation
* @param blocking whether or not this is a blocking operation
*/
public Operation(OperationType type, OperationInvoker operationInvoker,
boolean blocking) {
this.type = type;
this.invoker = operationInvoker;
this.blocking = blocking;
}
/**
* Returns the {@link OperationType type} of the operation.
* @return the type
*/
public OperationType getType() {
return this.type;
}
/**
* Returns the {@code OperationInvoker} that can be used to invoke this endpoint
* operation.
* @return the operation invoker
*/
public OperationInvoker getInvoker() {
return this.invoker;
}
/**
* Whether or not this is a blocking operation.
*
* @return {@code true} if it is a blocking operation, otherwise {@code false}.
*/
public boolean isBlocking() {
return this.blocking;
}
}

View File

@@ -14,31 +14,24 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
package org.springframework.boot.actuate.endpoint;
import java.util.Map;
/**
* Enumeration of management context types.
* An {@code OperationInvoker} is used to invoke an operation on an endpoint.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
public enum ManagementContextType {
@FunctionalInterface
public interface OperationInvoker {
/**
* The management context is the same as the main application context.
* Invoke the underlying operation using the given {@code arguments}.
* @param arguments the arguments to pass to the operation
* @return the result of the operation, may be {@code null}
*/
SAME,
/**
* 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
Object invoke(Map<String, Object> arguments);
}

View File

@@ -0,0 +1,39 @@
/*
* 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;
/**
* An {@code OperationParameterMapper} is used to map parameters to the required type when
* invoking an endpoint.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@FunctionalInterface
public interface OperationParameterMapper {
/**
* Map the specified {@code input} parameter to the given {@code parameterType}.
* @param input a parameter value
* @param parameterType the required type of the parameter
* @return a value suitable for that parameter
* @param <T> the actual type of the parameter
* @throws ParameterMappingException when a mapping failure occurs
*/
<T> T mapParameter(Object input, Class<T> parameterType);
}

View File

@@ -14,17 +14,29 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
import org.springframework.context.annotation.Configuration;
package org.springframework.boot.actuate.endpoint;
/**
* Configuration class used to enable configuration of a child management context.
* An enumeration of the different types of operation supported by an endpoint.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
@Configuration
@EnableManagementContext(ManagementContextType.CHILD)
class EnableChildManagementContextConfiguration {
public enum OperationType {
/**
* A read operation.
*/
READ,
/**
* A write operation.
*/
WRITE,
/**
* A delete operation.
*/
DELETE
}

View File

@@ -0,0 +1,64 @@
/*
* 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;
/**
* A {@code ParameterMappingException} is thrown when a failure occurs during
* {@link OperationParameterMapper#mapParameter(Object, Class) operation parameter
* mapping}.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
public class ParameterMappingException extends RuntimeException {
private final Object input;
private final Class<?> type;
/**
* Creates a new {@code ParameterMappingException} for a failure that occurred when
* trying to map the given {@code input} to the given {@code type}.
*
* @param input the input that was being mapped
* @param type the type that was being mapped to
* @param cause the cause of the mapping failure
*/
public ParameterMappingException(Object input, Class<?> type, Throwable cause) {
super("Failed to map " + input + " of type " + input.getClass() + " to type "
+ type, cause);
this.input = input;
this.type = type;
}
/**
* Returns the input that was to be mapped.
* @return the input
*/
public Object getInput() {
return this.input;
}
/**
* Returns the type to be mapped to.
* @return the type
*/
public Class<?> getType() {
return this.type;
}
}

View File

@@ -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.endpoint;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.util.ReflectionUtils;
/**
* An {@code OperationInvoker} that invokes an operation using reflection.
*
* @author Andy Wilkinson
* @since 2.0.0
*/
public class ReflectiveOperationInvoker implements OperationInvoker {
private final OperationParameterMapper parameterMapper;
private final Object target;
private final Method method;
/**
* Creates a new {code ReflectiveOperationInvoker} that will invoke the given
* {@code method} on the given {@code target}. The given {@code parameterMapper} will
* be used to map parameters to the required types.
* @param parameterMapper the parameter mapper
* @param target the target of the reflective call
* @param method the method to call
*/
public ReflectiveOperationInvoker(OperationParameterMapper parameterMapper,
Object target, Method method) {
this.parameterMapper = parameterMapper;
this.target = target;
ReflectionUtils.makeAccessible(method);
this.method = method;
}
@Override
public Object invoke(Map<String, Object> arguments) {
return ReflectionUtils.invokeMethod(this.method, this.target,
resolveArguments(arguments));
}
private Object[] resolveArguments(Map<String, Object> arguments) {
return Stream.of(this.method.getParameters())
.map((parameter) -> resolveArgument(parameter, arguments))
.collect(Collectors.collectingAndThen(Collectors.toList(),
(list) -> list.toArray(new Object[list.size()])));
}
private Object resolveArgument(Parameter parameter, Map<String, Object> arguments) {
Object resolved = arguments.get(parameter.getName());
return this.parameterMapper.mapParameter(resolved, parameter.getType());
}
}

View File

@@ -1,151 +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.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
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;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import org.springframework.web.servlet.handler.AbstractUrlHandlerMapping;
/**
* {@link Endpoint} to expose Spring MVC mappings.
*
* @author Dave Syer
* @author Andy Wilkinson
*/
@Endpoint(id = "mappings")
public class RequestMappingEndpoint implements ApplicationContextAware {
private List<AbstractUrlHandlerMapping> handlerMappings = Collections.emptyList();
private List<AbstractHandlerMethodMapping<?>> methodMappings = Collections
.emptyList();
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
/**
* Set the handler mappings.
* @param handlerMappings the handler mappings
*/
public void setHandlerMappings(List<AbstractUrlHandlerMapping> handlerMappings) {
this.handlerMappings = handlerMappings;
}
/**
* Set the method mappings.
* @param methodMappings the method mappings
*/
public void setMethodMappings(List<AbstractHandlerMethodMapping<?>> methodMappings) {
this.methodMappings = methodMappings;
}
@ReadOperation
public Map<String, Object> mappings() {
Map<String, Object> result = new LinkedHashMap<>();
extractHandlerMappings(this.handlerMappings, result);
extractHandlerMappings(this.applicationContext, result);
extractMethodMappings(this.methodMappings, result);
extractMethodMappings(this.applicationContext, result);
return result;
}
@SuppressWarnings("rawtypes")
protected void extractMethodMappings(ApplicationContext applicationContext,
Map<String, Object> result) {
if (applicationContext != null) {
for (Entry<String, AbstractHandlerMethodMapping> bean : applicationContext
.getBeansOfType(AbstractHandlerMethodMapping.class).entrySet()) {
@SuppressWarnings("unchecked")
Map<?, HandlerMethod> methods = bean.getValue().getHandlerMethods();
for (Entry<?, HandlerMethod> method : methods.entrySet()) {
Map<String, String> map = new LinkedHashMap<>();
map.put("bean", bean.getKey());
map.put("method", method.getValue().toString());
result.put(method.getKey().toString(), map);
}
}
}
}
protected void extractHandlerMappings(ApplicationContext applicationContext,
Map<String, Object> result) {
if (applicationContext != null) {
Map<String, AbstractUrlHandlerMapping> mappings = applicationContext
.getBeansOfType(AbstractUrlHandlerMapping.class);
for (Entry<String, AbstractUrlHandlerMapping> mapping : mappings.entrySet()) {
Map<String, Object> handlers = getHandlerMap(mapping.getValue());
for (Entry<String, Object> handler : handlers.entrySet()) {
result.put(handler.getKey(),
Collections.singletonMap("bean", mapping.getKey()));
}
}
}
}
private Map<String, Object> getHandlerMap(AbstractUrlHandlerMapping mapping) {
if (AopUtils.isCglibProxy(mapping)) {
// If the AbstractUrlHandlerMapping is a cglib proxy we can't call
// the final getHandlerMap() method.
return Collections.emptyMap();
}
return mapping.getHandlerMap();
}
protected void extractHandlerMappings(
Collection<AbstractUrlHandlerMapping> handlerMappings,
Map<String, Object> result) {
for (AbstractUrlHandlerMapping mapping : handlerMappings) {
Map<String, Object> handlers = mapping.getHandlerMap();
for (Map.Entry<String, Object> entry : handlers.entrySet()) {
Class<? extends Object> handlerClass = entry.getValue().getClass();
result.put(entry.getKey(),
Collections.singletonMap("type", handlerClass.getName()));
}
}
}
protected void extractMethodMappings(
Collection<AbstractHandlerMethodMapping<?>> methodMappings,
Map<String, Object> result) {
for (AbstractHandlerMethodMapping<?> mapping : methodMappings) {
Map<?, HandlerMethod> methods = mapping.getHandlerMethods();
for (Map.Entry<?, HandlerMethod> entry : methods.entrySet()) {
result.put(String.valueOf(entry.getKey()), Collections
.singletonMap("method", String.valueOf(entry.getValue())));
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 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.
@@ -21,25 +21,27 @@ import java.util.regex.Pattern;
import org.springframework.util.Assert;
/**
* Internal strategy used to sanitize potentially sensitive keys.
* Strategy that be be used by endpoint implementations to sanitize potentially sensitive
* keys.
*
* @author Christian Dupuis
* @author Toshiaki Maki
* @author Phillip Webb
* @author Nicolas Lejeune
* @author Stephane Nicoll
* @since 2.0.0
*/
class Sanitizer {
public class Sanitizer {
private static final String[] REGEX_PARTS = { "*", "$", "^", "+" };
private Pattern[] keysToSanitize;
Sanitizer() {
public Sanitizer() {
this("password", "secret", "key", "token", ".*credentials.*", "vcap_services");
}
Sanitizer(String... keysToSanitize) {
public Sanitizer(String... keysToSanitize) {
setKeysToSanitize(keysToSanitize);
}

View File

@@ -0,0 +1,410 @@
/*
* 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.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
import org.springframework.boot.actuate.endpoint.EndpointInfo;
import org.springframework.boot.actuate.endpoint.Operation;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.boot.actuate.endpoint.cache.CachingConfiguration;
import org.springframework.boot.actuate.endpoint.cache.CachingConfigurationFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.ObjectUtils;
/**
* A base {@link EndpointDiscoverer} implementation that discovers {@link Endpoint} beans
* in an application context.
*
* @param <T> the type of the operation
* @param <K> the type of the operation key
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 2.0.0
*/
public abstract class AnnotationEndpointDiscoverer<T extends Operation, K>
implements EndpointDiscoverer<T> {
private final ApplicationContext applicationContext;
private final EndpointOperationFactory<T> operationFactory;
private final Function<T, K> operationKeyFactory;
private final CachingConfigurationFactory cachingConfigurationFactory;
protected AnnotationEndpointDiscoverer(ApplicationContext applicationContext,
EndpointOperationFactory<T> operationFactory,
Function<T, K> operationKeyFactory,
CachingConfigurationFactory cachingConfigurationFactory) {
this.applicationContext = applicationContext;
this.operationFactory = operationFactory;
this.operationKeyFactory = operationKeyFactory;
this.cachingConfigurationFactory = cachingConfigurationFactory;
}
/**
* Perform endpoint discovery, including discovery and merging of extensions.
* @param extensionType the annotation type of the extension
* @param exposure the {@link EndpointExposure} that should be considered
* @return the list of {@link EndpointInfo EndpointInfos} that describes the
* discovered endpoints matching the specified {@link EndpointExposure}
*/
protected Collection<EndpointInfoDescriptor<T, K>> discoverEndpoints(
Class<? extends Annotation> extensionType, EndpointExposure exposure) {
Map<Class<?>, EndpointInfo<T>> endpoints = discoverEndpoints(exposure);
Map<Class<?>, EndpointExtensionInfo<T>> extensions = discoverExtensions(endpoints,
extensionType, exposure);
Collection<EndpointInfoDescriptor<T, K>> result = new ArrayList<>();
endpoints.forEach((endpointClass, endpointInfo) -> {
EndpointExtensionInfo<T> extension = extensions.remove(endpointClass);
result.add(createDescriptor(endpointClass, endpointInfo, extension));
});
return result;
}
private Map<Class<?>, EndpointInfo<T>> discoverEndpoints(EndpointExposure exposure) {
String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
this.applicationContext, Endpoint.class);
Map<Class<?>, EndpointInfo<T>> endpoints = new LinkedHashMap<>();
Map<String, EndpointInfo<T>> endpointsById = new LinkedHashMap<>();
for (String beanName : beanNames) {
Class<?> beanType = this.applicationContext.getType(beanName);
AnnotationAttributes attributes = AnnotatedElementUtils
.findMergedAnnotationAttributes(beanType, Endpoint.class, true, true);
if (isExposedOver(attributes, exposure)) {
EndpointInfo<T> info = createEndpointInfo(beanName, beanType, attributes);
EndpointInfo<T> previous = endpointsById.putIfAbsent(info.getId(), info);
Assert.state(previous == null, () -> "Found two endpoints with the id '"
+ info.getId() + "': " + info + " and " + previous);
endpoints.put(beanType, info);
}
}
return endpoints;
}
private EndpointInfo<T> createEndpointInfo(String beanName, Class<?> beanType,
AnnotationAttributes attributes) {
String id = attributes.getString("id");
boolean enabledByDefault = attributes.getBoolean("enabledByDefault");
Map<Method, T> operations = discoverOperations(id, beanName, beanType);
return new EndpointInfo<>(id, enabledByDefault, operations.values());
}
private Map<Class<?>, EndpointExtensionInfo<T>> discoverExtensions(
Map<Class<?>, EndpointInfo<T>> endpoints,
Class<? extends Annotation> extensionType, EndpointExposure exposure) {
if (extensionType == null) {
return Collections.emptyMap();
}
String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(
this.applicationContext, extensionType);
Map<Class<?>, EndpointExtensionInfo<T>> extensions = new HashMap<>();
for (String beanName : beanNames) {
Class<?> beanType = this.applicationContext.getType(beanName);
Class<?> endpointType = getEndpointType(extensionType, beanType);
AnnotationAttributes endpointAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(endpointType, Endpoint.class);
Assert.state(isExposedOver(endpointAttributes, exposure),
"Invalid extension " + beanType.getName() + "': endpoint '"
+ endpointType.getName()
+ "' does not support such extension");
EndpointInfo<T> info = getEndpointInfo(endpoints, beanType, endpointType);
Map<Method, T> operations = discoverOperations(info.getId(), beanName,
beanType);
EndpointExtensionInfo<T> extension = new EndpointExtensionInfo<>(beanType,
operations.values());
EndpointExtensionInfo<T> previous = extensions.putIfAbsent(endpointType,
extension);
Assert.state(previous == null,
() -> "Found two extensions for the same endpoint '"
+ endpointType.getName() + "': "
+ extension.getExtensionType().getName() + " and "
+ previous.getExtensionType().getName());
}
return extensions;
}
private EndpointInfo<T> getEndpointInfo(Map<Class<?>, EndpointInfo<T>> endpoints,
Class<?> beanType, Class<?> endpointClass) {
EndpointInfo<T> endpoint = endpoints.get(endpointClass);
Assert.state(endpoint != null, "Invalid extension '" + beanType.getName()
+ "': no endpoint found with type '" + endpointClass.getName() + "'");
return endpoint;
}
private Class<?> getEndpointType(Class<? extends Annotation> extensionType,
Class<?> beanType) {
AnnotationAttributes attributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(beanType, extensionType);
return (Class<?>) attributes.get("endpoint");
}
private EndpointInfoDescriptor<T, K> createDescriptor(Class<?> type,
EndpointInfo<T> info, EndpointExtensionInfo<T> extension) {
Map<OperationKey<K>, List<T>> operations = indexOperations(info.getId(), type,
info.getOperations());
if (extension != null) {
operations.putAll(indexOperations(info.getId(), extension.getExtensionType(),
extension.getOperations()));
return new EndpointInfoDescriptor<>(mergeEndpoint(info, extension),
operations);
}
return new EndpointInfoDescriptor<>(info, operations);
}
private EndpointInfo<T> mergeEndpoint(EndpointInfo<T> endpoint,
EndpointExtensionInfo<T> extension) {
Map<K, T> operations = new HashMap<>();
Consumer<T> consumer = (operation) -> operations
.put(this.operationKeyFactory.apply(operation), operation);
endpoint.getOperations().forEach(consumer);
extension.getOperations().forEach(consumer);
return new EndpointInfo<>(endpoint.getId(), endpoint.isEnabledByDefault(),
operations.values());
}
private Map<OperationKey<K>, List<T>> indexOperations(String endpointId,
Class<?> target, Collection<T> operations) {
LinkedMultiValueMap<OperationKey<K>, T> result = new LinkedMultiValueMap<>();
operations.forEach((operation) -> {
K key = this.operationKeyFactory.apply(operation);
result.add(new OperationKey<>(endpointId, target, key), operation);
});
return result;
}
private boolean isExposedOver(AnnotationAttributes attributes,
EndpointExposure exposure) {
if (exposure == null) {
return true;
}
EndpointExposure[] supported = (EndpointExposure[]) attributes.get("exposure");
return ObjectUtils.isEmpty(supported)
|| ObjectUtils.containsElement(supported, exposure);
}
private Map<Method, T> discoverOperations(String id, String name, Class<?> type) {
return MethodIntrospector.selectMethods(type,
(MethodIntrospector.MetadataLookup<T>) (
method) -> createOperationIfPossible(id, name, method));
}
private T createOperationIfPossible(String endpointId, String beanName,
Method method) {
T operation = createReadOperationIfPossible(endpointId, beanName, method);
if (operation != null) {
return operation;
}
operation = createWriteOperationIfPossible(endpointId, beanName, method);
if (operation != null) {
return operation;
}
return createDeleteOperationIfPossible(endpointId, beanName, method);
}
private T createReadOperationIfPossible(String endpointId, String beanName,
Method method) {
return createOperationIfPossible(endpointId, beanName, method,
ReadOperation.class, OperationType.READ);
}
private T createWriteOperationIfPossible(String endpointId, String beanName,
Method method) {
return createOperationIfPossible(endpointId, beanName, method,
WriteOperation.class, OperationType.WRITE);
}
private T createDeleteOperationIfPossible(String endpointId, String beanName,
Method method) {
return createOperationIfPossible(endpointId, beanName, method,
DeleteOperation.class, OperationType.DELETE);
}
private T createOperationIfPossible(String endpointId, String beanName, Method method,
Class<? extends Annotation> operationAnnotation,
OperationType operationType) {
AnnotationAttributes operationAttributes = AnnotatedElementUtils
.getMergedAnnotationAttributes(method, operationAnnotation);
if (operationAttributes == null) {
return null;
}
CachingConfiguration cachingConfiguration = this.cachingConfigurationFactory
.getCachingConfiguration(endpointId);
return this.operationFactory.createOperation(endpointId, operationAttributes,
this.applicationContext.getBean(beanName), method, operationType,
determineTimeToLive(cachingConfiguration, operationType, method));
}
private long determineTimeToLive(CachingConfiguration cachingConfiguration,
OperationType operationType, Method method) {
if (cachingConfiguration != null && cachingConfiguration.getTimeToLive() > 0
&& operationType == OperationType.READ
&& method.getParameters().length == 0) {
return cachingConfiguration.getTimeToLive();
}
return 0;
}
/**
* An {@code EndpointOperationFactory} creates an {@link Operation} for an operation
* on an endpoint.
*
* @param <T> the {@link Operation} type
*/
@FunctionalInterface
protected interface EndpointOperationFactory<T extends Operation> {
/**
* Creates an {@code EndpointOperation} for an operation on an endpoint.
* @param endpointId the id of the endpoint
* @param operationAttributes the annotation attributes for the operation
* @param target the target that implements the operation
* @param operationMethod the method on the bean that implements the operation
* @param operationType the type of the operation
* @param timeToLive the caching period in milliseconds
* @return the operation info that describes the operation
*/
T createOperation(String endpointId, AnnotationAttributes operationAttributes,
Object target, Method operationMethod, OperationType operationType,
long timeToLive);
}
/**
* Describes a tech-specific extension of an endpoint.
* @param <T> the type of the operation
*/
private static final class EndpointExtensionInfo<T extends Operation> {
private final Class<?> extensionType;
private final Collection<T> operations;
private EndpointExtensionInfo(Class<?> extensionType, Collection<T> operations) {
this.extensionType = extensionType;
this.operations = operations;
}
private Class<?> getExtensionType() {
return this.extensionType;
}
private Collection<T> getOperations() {
return this.operations;
}
}
/**
* Describes an {@link EndpointInfo endpoint} and whether or not it is valid.
*
* @param <T> the type of the operation
* @param <K> the type of the operation key
*/
protected static class EndpointInfoDescriptor<T extends Operation, K> {
private final EndpointInfo<T> endpointInfo;
private final Map<OperationKey<K>, List<T>> operations;
protected EndpointInfoDescriptor(EndpointInfo<T> endpointInfo,
Map<OperationKey<K>, List<T>> operations) {
this.endpointInfo = endpointInfo;
this.operations = operations;
}
public EndpointInfo<T> getEndpointInfo() {
return this.endpointInfo;
}
public Map<OperationKey<K>, List<T>> findDuplicateOperations() {
Map<OperationKey<K>, List<T>> duplicateOperations = new HashMap<>();
this.operations.forEach((k, list) -> {
if (list.size() > 1) {
duplicateOperations.put(k, list);
}
});
return duplicateOperations;
}
}
/**
* Define the key of an operation in the context of an operation's implementation.
*
* @param <K> the type of the key
*/
protected static final class OperationKey<K> {
private final String endpointId;
private final Class<?> endpointType;
private final K key;
public OperationKey(String endpointId, Class<?> endpointType, K key) {
this.endpointId = endpointId;
this.endpointType = endpointType;
this.key = key;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
OperationKey<?> other = (OperationKey<?>) o;
Boolean result = true;
result = result && this.endpointId.equals(other.endpointId);
result = result && this.endpointType.equals(other.endpointType);
result = result && this.key.equals(other.key);
return result;
}
@Override
public int hashCode() {
int result = this.endpointId.hashCode();
result = 31 * result + this.endpointType.hashCode();
result = 31 * result + this.key.hashCode();
return result;
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure;
package org.springframework.boot.actuate.endpoint.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -22,23 +22,23 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
/**
* Enables the management context.
* Identifies a method on an {@link Endpoint} as being a delete operation.
*
* @author Stephane Nicoll
* @author Andy Wilkinson
* @since 2.0.0
*/
@Target(ElementType.TYPE)
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ManagementContextConfigurationImportSelector.class)
@interface EnableManagementContext {
public @interface DeleteOperation {
/**
* The management context type that should be enabled.
* @return the management context type
* The media type of the result of the operation.
*
* @return the media type
*/
ManagementContextType value();
String[] produces() default {};
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.health;
package org.springframework.boot.actuate.endpoint.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -22,28 +22,37 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
import org.springframework.boot.actuate.endpoint.EndpointDiscoverer;
/**
* {@link Conditional} that checks whether or not a default health indicator is enabled.
* Matches if the value of the {@code management.health.<name>.enabled} property is
* {@code true}. Otherwise, matches if the value of the
* {@code management.health.defaults.enabled} property is {@code true} or if it is not
* configured.
* Identifies a type as being an endpoint.
*
* @author Stephane Nicoll
* @author Andy Wilkinson
* @since 2.0.0
* @see EndpointDiscoverer
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnEnabledHealthIndicatorCondition.class)
public @interface ConditionalOnEnabledHealthIndicator {
public @interface Endpoint {
/**
* The name of the health indicator.
* @return the name of the health indicator
* The id of the endpoint.
* @return the id
*/
String value();
String id();
/**
* Defines the {@link EndpointExposure technologies} over which the endpoint should be
* exposed. By default, all technologies are supported.
* @return the supported endpoint exposure technologies
*/
EndpointExposure[] exposure() default {};
/**
* 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;
}

Some files were not shown because too many files have changed in this diff Show More