Browse Source
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]pull/2/merge
21 changed files with 756 additions and 133 deletions
@ -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 { |
||||||
|
|
||||||
|
} |
||||||
@ -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()]); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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; |
|
||||||
} |
|
||||||
} |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -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 { |
||||||
|
|
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,2 +1,4 @@ |
|||||||
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\ |
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 |
||||||
|
|||||||
@ -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> |
||||||
@ -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
@ -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> |
||||||
@ -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; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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 |
||||||
Loading…
Reference in new issue