[bs-138] Add @OnManagementContext for management beans

User adds @OnManagementContext to a bean or @Configuration and
it should be included in the management context (if there is one)
whether it is the main context or a child.

Makes it easy to secure the management endpoints and keep
the rest open (see actuator-ui-sample).

[#50721675]
This commit is contained in:
Dave Syer 2013-05-29 15:59:59 +01:00
parent 729cfe813b
commit 3ba5f56808
21 changed files with 756 additions and 133 deletions

View File

@ -451,6 +451,11 @@
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.171</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@ -31,9 +31,8 @@ import org.springframework.context.annotation.Import;
* @author Dave Syer
*/
@Configuration
@Import({ ActuatorWebConfiguration.class, ManagementConfiguration.class,
MetricRepositoryConfiguration.class, ErrorConfiguration.class,
SecurityConfiguration.class, TraceFilterConfiguration.class,
@Import({ ActuatorWebConfiguration.class, MetricRepositoryConfiguration.class,
ErrorConfiguration.class, TraceFilterConfiguration.class,
MetricFilterConfiguration.class, AuditConfiguration.class })
public class ActuatorAutoConfiguration {

View File

@ -0,0 +1,41 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.actuate.autoconfigure;
import 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;
/**
* A bean with this annotation will only be instantiated in the management context,
* whether that is the current context or a child context. Using this feature makes it
* easy to have a single set of configuration beans for both contexts and be able to
* switch the management features to a child context externally. Very useful if (for
* instance) you want management endpoints but open, or differently secured public facing
* endpoints.
*
* @author Dave Syer
*
*/
@Conditional(OnManagementContextCondition.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface ConditionalOnManagementContext {
}

View File

@ -0,0 +1,163 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.actuate.autoconfigure;
import java.util.ArrayList;
import java.util.Arrays;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.actuate.autoconfigure.ManagementAutoConfiguration.RememberManagementConfiguration;
import org.springframework.bootstrap.actuate.properties.ManagementServerProperties;
import org.springframework.bootstrap.autoconfigure.web.EmbeddedContainerCustomizerConfiguration;
import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.bootstrap.properties.ServerProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
/**
* @author Dave Syer
*
*/
@Configuration
@Conditional(RememberManagementConfiguration.class)
@Import(ManagementEndpointsRegistration.class)
public class ManagementAutoConfiguration implements ApplicationContextAware {
public static final String MEMO_BEAN_NAME = ManagementAutoConfiguration.class
.getName() + ".MEMO";
private ApplicationContext parent;
private ConfigurableApplicationContext context;
@Autowired
private ServerProperties configuration = new ServerProperties();
@Autowired
private ManagementServerProperties management = new ManagementServerProperties();
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.parent = applicationContext;
}
@Bean
public ApplicationListener<ContextClosedEvent> managementContextClosedListener() {
return new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (event.getSource() != ManagementAutoConfiguration.this.parent) {
return;
}
if (ManagementAutoConfiguration.this.context != null) {
ManagementAutoConfiguration.this.context.close();
}
}
};
}
@Bean
public ApplicationListener<ContextRefreshedEvent> managementContextRefeshedListener() {
return new ApplicationListener<ContextRefreshedEvent>() {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getSource() != ManagementAutoConfiguration.this.parent) {
return;
}
if (ManagementAutoConfiguration.this.configuration.getPort() != ManagementAutoConfiguration.this.management
.getPort()) {
AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext();
context.setParent(ManagementAutoConfiguration.this.parent);
context.setEnvironment((ConfigurableEnvironment) ManagementAutoConfiguration.this.parent
.getEnvironment());
context.register(assembleConfigClasses(ManagementAutoConfiguration.this.parent));
context.refresh();
ManagementAutoConfiguration.this.context = context;
}
}
};
}
protected static class RememberManagementConfiguration implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
int serverPort = environment.getProperty("server.port", Integer.class, 8080);
int managementPort = environment.getProperty("management.port",
Integer.class, serverPort);
if (!context.getBeanFactory().containsSingleton(MEMO_BEAN_NAME)) {
context.getBeanFactory().registerSingleton(MEMO_BEAN_NAME,
managementPort > 0);
}
return managementPort > 0;
}
}
protected Class<?>[] assembleConfigClasses(BeanFactory parent) {
// Some basic context configuration that all child context need
ArrayList<Class<?>> configs = new ArrayList<Class<?>>(Arrays.<Class<?>> asList(
EmbeddedContainerCustomizerConfiguration.class,
ManagementServerConfiguration.class, ErrorConfiguration.class));
String managementContextBeanName = OnManagementContextCondition.class.getName();
// Management context only beans pulled in from the deferred list in the parent
// context
if (parent.containsBean(managementContextBeanName)) {
String[] names = parent.getBean(managementContextBeanName, String[].class);
for (String name : names) {
try {
configs.add(ClassUtils.forName(name,
ManagementAutoConfiguration.this.parent.getClassLoader()));
} catch (ClassNotFoundException e) {
throw new BeanCreationException(managementContextBeanName,
"Class not found: " + name);
}
}
}
return configs.toArray(new Class<?>[configs.size()]);
}
}

View File

@ -1,103 +0,0 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.actuate.autoconfigure;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.actuate.properties.ManagementServerProperties;
import org.springframework.bootstrap.autoconfigure.web.EmbeddedContainerCustomizerConfiguration;
import org.springframework.bootstrap.context.annotation.ConditionalOnExpression;
import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.bootstrap.properties.ServerProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
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.annotation.Import;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* @author Dave Syer
*
*/
@Configuration
@ConditionalOnExpression("${management.port:${server.port:8080}}>0")
public class ManagementConfiguration implements ApplicationContextAware {
private ApplicationContext parent;
private ConfigurableApplicationContext context;
@Autowired
private ServerProperties configuration = new ServerProperties();
@Autowired
private ManagementServerProperties management = new ManagementServerProperties();
@ConditionalOnExpression("${server.port:8080} == ${management.port:${server.port:8080}}")
@Configuration
@Import({ MetricsConfiguration.class, HealthConfiguration.class,
ShutdownConfiguration.class, TraceConfiguration.class })
public static class ManagementEndpointsConfiguration {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.parent = applicationContext;
}
@Bean
public ApplicationListener<ContextClosedEvent> managementContextClosedListener() {
return new ApplicationListener<ContextClosedEvent>() {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
if (event.getSource() != ManagementConfiguration.this.parent) {
return;
}
if (ManagementConfiguration.this.context != null) {
ManagementConfiguration.this.context.close();
}
}
};
}
@Bean
public ApplicationListener<ContextRefreshedEvent> managementContextRefeshedListener() {
return new ApplicationListener<ContextRefreshedEvent>() {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getSource() != ManagementConfiguration.this.parent) {
return;
}
if (ManagementConfiguration.this.configuration.getPort() != ManagementConfiguration.this.management
.getPort()) {
AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext();
context.setParent(ManagementConfiguration.this.parent);
context.register(EmbeddedContainerCustomizerConfiguration.class,
ManagementServerConfiguration.class,
MetricsConfiguration.class, HealthConfiguration.class,
ShutdownConfiguration.class, TraceConfiguration.class);
context.refresh();
ManagementConfiguration.this.context = context;
}
}
};
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.actuate.autoconfigure;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Convenient collector for all the management endpoints (stuff that goes in the
* management context whether it is a child context or not).
*
* @author Dave Syer
*
*/
@Configuration
@ConditionalOnManagementContext
@Import({ MetricsConfiguration.class, HealthConfiguration.class,
ShutdownConfiguration.class, TraceConfiguration.class })
public class ManagementEndpointsRegistration {
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.actuate.autoconfigure;
import java.util.Collection;
import java.util.HashSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.bootstrap.context.annotation.ConditionLogUtils;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
/**
* A condition that can determine if the bean it applies to is in the management context
* (the application context with the management endpoints).
*
* @author Dave Syer
*
* @see ConditionalOnManagementContext
*
*/
public class OnManagementContextCondition implements Condition {
private static Log logger = LogFactory.getLog(OnManagementContextCondition.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(logger, metadata);
Environment environment = context.getEnvironment();
int serverPort = environment.getProperty("server.port", Integer.class, 8080);
int managementPort = environment.getProperty("management.port", Integer.class,
serverPort);
// If there is no management context, the decision is easy (match=false)
boolean managementEnabled = managementPort > 0;
// The management context is the same as the parent context
boolean managementContextInParent = serverPort == managementPort;
// The current context is a child context with a management server
boolean managementChildContext = context.getBeanFactory().getBeanNamesForType(
ManagementServerConfiguration.class).length > 0;
// The management auto configuration either hasn't been added yet or has been
// added to the context and it is enabled
boolean containsManagementBeans = !context.getBeanFactory().containsSingleton(
ManagementAutoConfiguration.MEMO_BEAN_NAME)
|| (Boolean) context.getBeanFactory().getSingleton(
ManagementAutoConfiguration.MEMO_BEAN_NAME);
boolean result = managementEnabled
&& ((managementContextInParent && containsManagementBeans) || managementChildContext);
if (logger.isDebugEnabled()) {
if (!managementEnabled) {
logger.debug(checking + "Management context is disabled");
} else {
logger.debug(checking + "Management context is in parent: "
+ managementContextInParent + " (management.port="
+ managementPort + ", server.port=" + serverPort + ")");
logger.debug(checking + "In management child context: "
+ managementChildContext);
logger.debug(checking + "In management parent context: "
+ containsManagementBeans);
logger.debug(checking + "Finished matching and result is matches="
+ result);
}
}
if (!result && metadata instanceof AnnotationMetadata) {
Collection<String> beanClasses = getManagementContextClasses(context
.getBeanFactory());
beanClasses.add(((AnnotationMetadata) metadata).getClassName());
}
return result;
}
private Collection<String> getManagementContextClasses(
ConfigurableListableBeanFactory beanFactory) {
String name = OnManagementContextCondition.class.getName();
if (!beanFactory.containsSingleton(name)) {
beanFactory.registerSingleton(name, new HashSet<String>());
}
@SuppressWarnings("unchecked")
Collection<String> result = (Collection<String>) beanFactory.getSingleton(name);
return result;
}
}

View File

@ -31,7 +31,7 @@ import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.EnableWebSecurity;
import org.springframework.security.config.annotation.web.HttpConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.WebSecurityBuilder;
import org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
@ -43,7 +43,7 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationEn
@ConditionalOnClass({ EnableWebSecurity.class })
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfiguration {
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean({ AuthenticationEventPublisher.class })
@ -92,7 +92,7 @@ public class SecurityConfiguration {
}
@Override
public void configure(WebSecurityConfiguration builder) throws Exception {
public void configure(WebSecurityBuilder builder) throws Exception {
builder.ignoring().antMatchers(this.endpoints.getHealth().getPath(),
this.endpoints.getInfo().getPath(),
this.endpoints.getError().getPath());

View File

@ -1,2 +1,4 @@
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\
org.springframework.bootstrap.actuate.autoconfigure.ActuatorAutoConfiguration
org.springframework.bootstrap.actuate.autoconfigure.ActuatorAutoConfiguration,\
org.springframework.bootstrap.actuate.autoconfigure.SecurityAutoConfiguration,\
org.springframework.bootstrap.actuate.autoconfigure.ManagementAutoConfiguration

View File

@ -36,6 +36,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockServletContext;
import org.springframework.stereotype.Controller;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -57,30 +58,64 @@ public class ManagementConfigurationTests {
@Test
public void testManagementConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context
.register(MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class,
ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.register(MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class, ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(HealthEndpoint.class));
}
@Test
public void testSuppressManagementConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestUtils.addEnviroment(this.context, "management.port:0");
this.context.register(MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class, ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertEquals(0, this.context.getBeanNamesForType(HealthEndpoint.class).length);
}
@Test
public void testManagementConfigurationExtensions() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class, ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, NewEndpoint.class);
this.context.refresh();
assertNotNull(this.context.getBean(NewEndpoint.class));
}
@Test
public void testManagementConfigurationExtensionsOrderDependence() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(NewEndpoint.class, MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class, ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(NewEndpoint.class));
}
@Test
public void testChildContextCreated() throws Exception {
this.context = new AnnotationConfigApplicationContext();
TestUtils.addEnviroment(this.context, "server.port:7000", "management.port:7001");
this.context
.register(ParentContext.class, MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class,
ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.register(ParentContext.class, MetricRepositoryConfiguration.class,
TraceFilterConfiguration.class, ServerPropertiesConfiguration.class,
ActuatorAutoConfiguration.ServerPropertiesConfiguration.class,
ManagementAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, NewEndpoint.class);
this.context.refresh();
assertEquals(0, this.context.getBeanNamesForType(HealthEndpoint.class).length);
assertEquals(0, this.context.getBeanNamesForType(NewEndpoint.class).length);
}
@Configuration
@ -116,4 +151,10 @@ public class ManagementConfigurationTests {
}
}
@Controller
@ConditionalOnManagementContext
protected static class NewEndpoint {
}
}

View File

@ -42,7 +42,7 @@ public class SecurityConfigurationTests {
public void testWebConfiguration() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(SecurityConfiguration.class, EndpointsProperties.class,
this.context.register(SecurityAutoConfiguration.class, EndpointsProperties.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(AuthenticationManager.class));
@ -52,7 +52,7 @@ public class SecurityConfigurationTests {
public void testOverrideAuthenticationManager() throws Exception {
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(TestConfiguration.class, SecurityConfiguration.class,
this.context.register(TestConfiguration.class, SecurityAutoConfiguration.class,
EndpointsProperties.class, PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertEquals(this.context.getBean(TestConfiguration.class).authenticationManager,

View File

@ -11,13 +11,14 @@
<modules>
<module>spring-bootstrap-sample</module>
<module>spring-bootstrap-actuator-sample</module>
<module>spring-bootstrap-actuator-ui-sample</module>
<module>spring-bootstrap-batch-sample</module>
<module>spring-bootstrap-data-sample</module>
<module>spring-bootstrap-integration-sample</module>
<module>spring-bootstrap-jetty-sample</module>
<module>spring-bootstrap-profile-sample</module>
<module>spring-bootstrap-simple-sample</module>
<module>spring-bootstrap-tomcat-sample</module>
<module>spring-bootstrap-profile-sample</module>
<module>spring-bootstrap-trad-sample</module>
<module>spring-bootstrap-ui-sample</module>
<module>spring-bootstrap-xml-sample</module>

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-samples</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-actuator-ui-sample</artifactId>
<properties>
<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
<start-class>org.springframework.bootstrap.sample.ui.UiBootstrapApplication</start-class>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-actuator</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-javaconfig</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-exec-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,51 @@
package org.springframework.bootstrap.sample.ui;
import java.util.Date;
import java.util.Map;
import org.springframework.bootstrap.SpringApplication;
import org.springframework.bootstrap.actuate.autoconfigure.ConditionalOnManagementContext;
import org.springframework.bootstrap.actuate.autoconfigure.ManagementAutoConfiguration;
import org.springframework.bootstrap.actuate.autoconfigure.SecurityAutoConfiguration;
import org.springframework.bootstrap.context.annotation.ConditionalOnExpression;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class,
ManagementAutoConfiguration.class })
@ComponentScan
@Controller
public class ActuatorUiBootstrapApplication {
@RequestMapping("/")
public String home(Map<String, Object> model) {
model.put("message", "Hello World");
model.put("title", "Hello Home");
model.put("date", new Date());
return "home";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(ActuatorUiBootstrapApplication.class, args);
}
@Configuration
@ConditionalOnExpression("${server.port:8080} != ${management.port:${server.port:8080}}")
@Import(ManagementAutoConfiguration.class)
protected static class ManagementConfiguration {
}
@Configuration
@ConditionalOnExpression("${server.port:8080} != ${management.port:${server.port:8080}}")
@ConditionalOnManagementContext
@Import(SecurityAutoConfiguration.class)
protected static class ManagementSecurityConfiguration {
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${title}">Title</title>
<link rel="stylesheet" th:href="@{/resources/css/bootstrap.min.css}"
href="../../resources/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<a class="brand" href="http://www.thymeleaf.org"> Thymeleaf -
Plain </a>
<ul class="nav">
<li><a th:href="@{/}" href="home.html"> Home </a></li>
</ul>
</div>
</div>
<h1 th:text="${title}">Title</h1>
<div th:text="${message}">Fake content</div>
<div id="created" th:text="${#dates.format(date)}">July 11,
2012 2:17:16 PM CDT</div>
</div>
</body>
</html>

View File

@ -0,0 +1,82 @@
package org.springframework.bootstrap.sample.ui;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.bootstrap.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Basic integration tests for demo application.
*
* @author Dave Syer
*
*/
public class ActuatorUiBootstrapApplicationTests {
private static ConfigurableApplicationContext context;
@BeforeClass
public static void start() throws Exception {
Future<ConfigurableApplicationContext> future = Executors
.newSingleThreadExecutor().submit(
new Callable<ConfigurableApplicationContext>() {
@Override
public ConfigurableApplicationContext call() throws Exception {
return (ConfigurableApplicationContext) SpringApplication
.run(ActuatorUiBootstrapApplication.class);
}
});
context = future.get(30, TimeUnit.SECONDS);
}
@AfterClass
public static void stop() {
if (context != null) {
context.close();
}
}
@Test
public void testHome() throws Exception {
ResponseEntity<String> entity = getRestTemplate().getForEntity(
"http://localhost:8080", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body (title doesn't match):\n" + entity.getBody(), entity
.getBody().contains("<title>Hello"));
}
@Test
public void testCss() throws Exception {
ResponseEntity<String> entity = getRestTemplate().getForEntity(
"http://localhost:8080/css/bootstrap.min.css", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body"));
}
private RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return restTemplate;
}
}

View File

@ -0,0 +1,94 @@
package org.springframework.bootstrap.sample.ui;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.bootstrap.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals;
/**
* Integration tests for separate management and main service ports.
*
* @author Dave Syer
*
*/
public class ManagementServiceBootstrapApplicationTests {
private static ConfigurableApplicationContext context;
private static int port = 9000;
private static int managementPort = 9001;
@BeforeClass
public static void start() throws Exception {
final String[] args = new String[] { "--server.port=" + port,
"--management.port=" + managementPort, "--management.address=127.0.0.1" };
Future<ConfigurableApplicationContext> future = Executors
.newSingleThreadExecutor().submit(
new Callable<ConfigurableApplicationContext>() {
@Override
public ConfigurableApplicationContext call() throws Exception {
return (ConfigurableApplicationContext) SpringApplication
.run(ActuatorUiBootstrapApplication.class, args);
}
});
context = future.get(10, TimeUnit.SECONDS);
}
@AfterClass
public static void stop() {
if (context != null) {
context.close();
}
}
@Test
public void testHome() throws Exception {
ResponseEntity<String> entity = getRestTemplate().getForEntity(
"http://localhost:" + port, String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
}
@Test
public void testMetrics() throws Exception {
@SuppressWarnings("rawtypes")
ResponseEntity<Map> entity = getRestTemplate().getForEntity(
"http://localhost:" + managementPort + "/metrics", Map.class);
assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode());
}
@Test
public void testHealth() throws Exception {
ResponseEntity<String> entity = getRestTemplate().getForEntity(
"http://localhost:" + managementPort + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals("ok", entity.getBody());
}
private RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return restTemplate;
}
}

View File

@ -0,0 +1,7 @@
handlers = java.util.logging.ConsoleHandler
.level = INFO
java.util.logging.ConsoleHandler.level = FINE
sun.net.www.protocol.http.HttpURLConnection.level = ALL
org.springframework.bootstrap.context.annotation.level = ALL
org.springframework.bootstrap.actuate.autoconfigure.level = ALL

View File

@ -18,11 +18,6 @@
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap-actuator</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>

View File

@ -65,7 +65,7 @@ public class OnExpressionCondition implements Condition {
if (resolver == null) {
resolver = new StandardBeanExpressionResolver();
}
Boolean result = (Boolean) resolver.evaluate(value, expressionContext);
boolean result = (Boolean) resolver.evaluate(value, expressionContext);
if (logger.isDebugEnabled()) {
logger.debug(checking + "Finished matching and result is matches=" + result);
}