[bs-85] New name for production-ready features = "actuator"

[#49047535]
This commit is contained in:
Dave Syer
2013-05-08 08:53:48 +01:00
parent eb7bc64e75
commit b897f35cad
71 changed files with 7 additions and 7 deletions

View File

@@ -0,0 +1,62 @@
/*
* 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.autoconfigure.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.service.audit.AuditEventRepository;
import org.springframework.bootstrap.service.audit.InMemoryAuditEventRepository;
import org.springframework.bootstrap.service.audit.listener.AuditListener;
import org.springframework.bootstrap.service.security.AuthenticationAuditListener;
import org.springframework.bootstrap.service.security.AuthorizationAuditListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Dave Syer
*
*/
@Configuration
public class AuditConfiguration {
@Autowired(required = false)
private AuditEventRepository auditEventRepository = new InMemoryAuditEventRepository();
@Bean
@ConditionalOnMissingBean(AuditEventRepository.class)
public AuditEventRepository auditEventRepository() throws Exception {
return this.auditEventRepository;
}
@Bean
public AuditListener auditListener() throws Exception {
return new AuditListener(this.auditEventRepository);
}
@Bean
@ConditionalOnClass(name = "org.springframework.security.authentication.event.AbstractAuthenticationEvent")
public AuthenticationAuditListener authenticationAuditListener() throws Exception {
return new AuthenticationAuditListener();
}
@Bean
@ConditionalOnClass(name = "org.springframework.security.access.event.AbstractAuthorizationEvent")
public AuthorizationAuditListener authorizationAuditListener() throws Exception {
return new AuthorizationAuditListener();
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.autoconfigure.service;
import javax.servlet.Servlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.health.HealthIndicator;
import org.springframework.bootstrap.service.health.HealthzEndpoint;
import org.springframework.bootstrap.service.health.VanillaHealthIndicator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for /healthz endpoint.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnMissingBean({ HealthzEndpoint.class })
public class HealthzConfiguration {
@Autowired(required = false)
private HealthIndicator<? extends Object> healthIndicator = new VanillaHealthIndicator();
@Bean
public HealthzEndpoint<? extends Object> healthzEndpoint() {
return new HealthzEndpoint<Object>(healthIndicator);
}
}

View File

@@ -0,0 +1,143 @@
/*
* 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.autoconfigure.service;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import javax.annotation.Resource;
import javax.servlet.Servlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.bootstrap.bind.PropertiesConfigurationFactory;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.info.InfoEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for /info endpoint.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnMissingBean({ InfoEndpoint.class })
public class InfoConfiguration {
@Resource(name = "infoMap")
private Map<String, Object> infoMap;
@Autowired
@Qualifier("gitInfo")
private GitInfo gitInfo;
@Bean
public Map<String, Object> applicationInfo() {
LinkedHashMap<String, Object> info = new LinkedHashMap<String, Object>();
info.putAll(this.infoMap);
if (this.gitInfo.getBranch() != null) {
info.put("git", this.gitInfo);
}
return info;
}
@Bean
public InfoEndpoint infoEndpoint() {
return new InfoEndpoint(applicationInfo());
}
@Configuration
public static class InfoPropertiesConfiguration {
@Autowired
private ConfigurableEnvironment environment = new StandardEnvironment();
@Bean
public PropertiesConfigurationFactory<GitInfo> gitInfo() throws IOException {
PropertiesConfigurationFactory<GitInfo> factory = new PropertiesConfigurationFactory<GitInfo>(
new GitInfo());
factory.setTargetName("git");
Properties properties = new Properties();
if (new ClassPathResource("git.properties").exists()) {
properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(
"git.properties"));
}
factory.setProperties(properties);
return factory;
}
@Bean
public PropertiesConfigurationFactory<Map<String, Object>> infoMap() {
PropertiesConfigurationFactory<Map<String, Object>> factory = new PropertiesConfigurationFactory<Map<String, Object>>(
new LinkedHashMap<String, Object>());
factory.setTargetName("info");
factory.setPropertySources(this.environment.getPropertySources());
return factory;
}
}
public static class GitInfo {
private String branch;
private Commit commit = new Commit();
public String getBranch() {
return this.branch;
}
public void setBranch(String branch) {
this.branch = branch;
}
public Commit getCommit() {
return this.commit;
}
public static class Commit {
private String id;
private String time;
public String getId() {
return this.id == null ? "" : (this.id.length() > 7 ? this.id.substring(
0, 7) : this.id);
}
public void setId(String id) {
this.id = id;
}
public String getTime() {
return this.time;
}
public void setTime(String time) {
this.time = time;
}
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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.autoconfigure.service;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnExpression;
import org.springframework.bootstrap.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.bootstrap.service.properties.ManagementServerProperties;
import org.springframework.bootstrap.service.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.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextRefreshedEvent;
/**
* @author Dave Syer
*
*/
@Configuration
@ConditionalOnExpression("${management.port:8080}>0")
public class ManagementConfiguration implements ApplicationContextAware, DisposableBean,
ApplicationListener<ContextRefreshedEvent> {
private ApplicationContext parent;
private ConfigurableApplicationContext context;
@Autowired
private ServerProperties configuration = new ServerProperties();
@Autowired
private ManagementServerProperties management = new ManagementServerProperties();
@ConditionalOnExpression("${server.port:8080} == ${management.port:8080}")
@Configuration
@Import({ VarzConfiguration.class, HealthzConfiguration.class,
ShutdownConfiguration.class, TraceConfiguration.class })
public static class ManagementEndpointsConfiguration {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.parent = applicationContext;
}
@Override
public void destroy() throws Exception {
if (this.context != null) {
this.context.close();
}
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getSource() != this.parent) {
return;
}
if (this.configuration.getPort() != this.management.getPort()) {
AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext();
context.setParent(this.parent);
context.register(ManagementServerConfiguration.class,
VarzConfiguration.class, HealthzConfiguration.class,
ShutdownConfiguration.class, TraceConfiguration.class);
context.refresh();
this.context = context;
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* 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.autoconfigure.service;
import java.util.Collections;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
import org.springframework.bootstrap.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.ErrorPage;
import org.springframework.bootstrap.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.bootstrap.service.error.ErrorEndpoint;
import org.springframework.bootstrap.service.properties.ManagementServerProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
/**
* Configuration for creating a new container (e.g. tomcat) for the management endpoints.
*
* @author Dave Syer
*/
@Configuration
@EnableWebMvc
public class ManagementServerConfiguration implements BeanPostProcessor {
@Autowired
private ManagementServerProperties configuration = new ManagementServerProperties();
private boolean initialized = false;
@Value("${endpoints.error.path:/error}")
private String errorPath = "/error";
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(
ApplicationContext context) {
return new PropertySourcesPlaceholderConfigurer();
}
@Bean
public ErrorEndpoint errorEndpoint() {
return new ErrorEndpoint();
}
@Bean
@ConditionalOnBean(TomcatEmbeddedServletContainerFactory.class)
public EmbeddedServletContainerFactory tomcatContainer(
HierarchicalBeanFactory beanFactory) {
TomcatEmbeddedServletContainerFactory factory = beanFactory
.getParentBeanFactory().getBean(
TomcatEmbeddedServletContainerFactory.class);
return factory.getChildContextFactory("Management");
}
@Bean
@ConditionalOnBean(JettyEmbeddedServletContainerFactory.class)
public EmbeddedServletContainerFactory jettyContainer() {
return new JettyEmbeddedServletContainerFactory();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof EmbeddedServletContainerFactory) {
if (bean instanceof AbstractEmbeddedServletContainerFactory
&& !this.initialized) {
AbstractEmbeddedServletContainerFactory factory = (AbstractEmbeddedServletContainerFactory) bean;
factory.setPort(this.configuration.getPort());
factory.setContextPath(this.configuration.getContextPath());
factory.setErrorPages(Collections
.singleton(new ErrorPage(this.errorPath)));
this.initialized = true;
}
}
return bean;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.autoconfigure.service;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.metrics.CounterService;
import org.springframework.bootstrap.service.metrics.DefaultCounterService;
import org.springframework.bootstrap.service.metrics.DefaultGaugeService;
import org.springframework.bootstrap.service.metrics.GaugeService;
import org.springframework.bootstrap.service.metrics.InMemoryMetricRepository;
import org.springframework.bootstrap.service.metrics.MetricRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for metrics services.
*
* @author Dave Syer
*
*/
@Configuration
public class MetricConfiguration {
@Bean
@ConditionalOnMissingBean({ CounterService.class })
public CounterService counterService() {
return new DefaultCounterService(metricRepository());
}
@Bean
@ConditionalOnMissingBean({ GaugeService.class })
public GaugeService gaugeService() {
return new DefaultGaugeService(metricRepository());
}
@Bean
@ConditionalOnMissingBean({ MetricRepository.class })
protected MetricRepository metricRepository() {
return new InMemoryMetricRepository();
}
}

View File

@@ -0,0 +1,123 @@
/*
* 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.autoconfigure.service;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnBean;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.metrics.CounterService;
import org.springframework.bootstrap.service.metrics.GaugeService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.UrlPathHelper;
/**
* {@link EnableAutoConfiguration Auto-configuration} for service apps.
*
* @author Dave Syer
*/
@Configuration
// FIXME: make this conditional
// @ConditionalOnBean({ CounterService.class, GaugeService.class })
@ConditionalOnClass({ Servlet.class })
public class MetricFilterConfiguration {
@Autowired(required = false)
private CounterService counterService;
@Autowired(required = false)
private GaugeService gaugeService;
@Bean
@ConditionalOnBean({ CounterService.class, GaugeService.class })
public Filter metricFilter() {
return new CounterServiceFilter();
}
/**
* Filter that counts requests and measures processing times.
*
* @author Dave Syer
*
*/
@Order(Integer.MIN_VALUE)
// TODO: parameterize the order (ideally it runs before any other filter)
private final class CounterServiceFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletResponse servletResponse = (HttpServletResponse) response;
UrlPathHelper helper = new UrlPathHelper();
String suffix = helper.getPathWithinApplication(servletRequest);
int status = 999;
long t0 = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
try {
status = servletResponse.getStatus();
} catch (Exception e) {
// ignore
}
set("response", suffix, System.currentTimeMillis() - t0);
increment("status." + status, suffix);
}
}
private void increment(String prefix, String suffix) {
if (MetricFilterConfiguration.this.counterService != null) {
String key = getKey(prefix + suffix);
MetricFilterConfiguration.this.counterService.increment(key);
}
}
private void set(String prefix, String suffix, double value) {
if (MetricFilterConfiguration.this.gaugeService != null) {
String key = getKey(prefix + suffix);
MetricFilterConfiguration.this.gaugeService.set(key, value);
}
}
private String getKey(String string) {
String value = string.replace("/", "."); // graphite compatible metric names
value = value.replace("..", ".");
if (value.endsWith(".")) {
value = value + "root";
}
if (value.startsWith("_")) {
value = value.substring(1);
}
return value;
}
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.autoconfigure.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableConfigurationProperties;
import org.springframework.bootstrap.service.properties.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.authentication.AuthenticationBuilder;
import org.springframework.security.config.annotation.web.EnableWebSecurity;
import org.springframework.security.config.annotation.web.ExpressionUrlAuthorizations;
import org.springframework.security.config.annotation.web.HttpConfigurator;
import org.springframework.security.config.annotation.web.SpringSecurityFilterChainBuilder.IgnoredRequestRegistry;
import org.springframework.security.config.annotation.web.WebSecurityConfigurerAdapter;
/**
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ EnableWebSecurity.class })
@EnableWebSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfiguration {
@Bean
@ConditionalOnMissingBean({ AuthenticationEventPublisher.class })
public AuthenticationEventPublisher authenticationEventPublisher() {
return new DefaultAuthenticationEventPublisher();
}
@Bean
public WebSecurityConfigurerAdapter webSecurityConfigurerAdapter() {
return new BoostrapWebSecurityConfigurerAdapter();
}
private static class BoostrapWebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {
@Value("${endpoints.healthz.path:/healthz}")
private String healthzPath = "/healthz";
@Value("${endpoints.info.path:/info}")
private String infoPath = "/info";
@Autowired
private SecurityProperties security;
@Autowired
private AuthenticationEventPublisher authenticationEventPublisher;
@Override
protected void ignoredRequests(IgnoredRequestRegistry ignoredRequests) {
ignoredRequests.antMatchers(this.healthzPath);
ignoredRequests.antMatchers(this.infoPath);
}
@Override
protected void authorizeUrls(ExpressionUrlAuthorizations interceptUrls) {
interceptUrls.antMatchers("/**").hasRole("USER");
}
@Override
protected void configure(HttpConfigurator http) throws Exception {
http.antMatcher("/**").httpBasic().and().anonymous().disable();
if (this.security.isRequireSsl()) {
http.requiresChannel().antMatchers("/**").requiresSecure();
}
}
@Override
protected AuthenticationManager authenticationManager() throws Exception {
AuthenticationManager manager = super.authenticationManager();
if (manager instanceof ProviderManager) {
((ProviderManager) manager)
.setAuthenticationEventPublisher(this.authenticationEventPublisher);
}
return manager;
}
}
@ConditionalOnMissingBean(AuthenticationManager.class)
@Configuration
public static class AuthenticationManagerConfiguration {
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return new AuthenticationBuilder().inMemoryAuthentication().withUser("user")
.password("password").roles("USER").and().and().build();
}
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.autoconfigure.service;
import java.util.Collections;
import javax.servlet.Servlet;
import org.apache.catalina.valves.AccessLogValve;
import org.apache.catalina.valves.RemoteIpValve;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.bootstrap.context.embedded.ErrorPage;
import org.springframework.bootstrap.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.bootstrap.service.error.ErrorEndpoint;
import org.springframework.bootstrap.service.properties.ServerProperties;
import org.springframework.bootstrap.service.properties.ServerProperties.Tomcat;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
/**
* Configuration for injecting externalized properties into the container (e.g. tomcat).
*
* @author Dave Syer
*/
// Slight hack here (BeanPostProcessor), to force the server properties to be bound in
// the right order
@Configuration
@ConditionalOnClass({ Servlet.class })
@Order(Integer.MIN_VALUE)
@Import(InfoConfiguration.class)
public class ServerConfiguration implements BeanPostProcessor, BeanFactoryAware {
private BeanFactory beanFactory;
// Don't do this! We don't get a callback for our own dependencies (lifecycle).
// @Autowired
// private AbstractEmbeddedServletContainerFactory factory;
private boolean initialized = false;
@Value("${endpoints.error.path:/error}")
private String errorPath = "/error";
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Bean
public ErrorEndpoint errorEndpoint() {
return new ErrorEndpoint();
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof EmbeddedServletContainerFactory) {
if (bean instanceof AbstractEmbeddedServletContainerFactory
&& !this.initialized) {
// Cannot use @Autowired because the injection happens too early
ServerProperties server = this.beanFactory
.getBean(ServerProperties.class);
AbstractEmbeddedServletContainerFactory factory = (AbstractEmbeddedServletContainerFactory) bean;
factory.setPort(server.getPort());
factory.setContextPath(server.getContextPath());
if (factory instanceof TomcatEmbeddedServletContainerFactory) {
configureTomcat((TomcatEmbeddedServletContainerFactory) factory,
server);
}
factory.setErrorPages(Collections
.singleton(new ErrorPage(this.errorPath)));
this.initialized = true;
}
}
return bean;
}
private void configureTomcat(TomcatEmbeddedServletContainerFactory tomcatFactory,
ServerProperties configuration) {
Tomcat tomcat = configuration.getTomcat();
if (tomcat.getBasedir() != null) {
tomcatFactory.setBaseDirectory(tomcat.getBasedir());
}
String remoteIpHeader = tomcat.getRemoteIpHeader();
String protocolHeader = tomcat.getProtocolHeader();
if (StringUtils.hasText(remoteIpHeader) || StringUtils.hasText(protocolHeader)) {
RemoteIpValve valve = new RemoteIpValve();
valve.setRemoteIpHeader(remoteIpHeader);
valve.setProtocolHeader(protocolHeader);
tomcatFactory.addContextValves(valve);
}
String pattern = tomcat.getAccessLogPattern();
if (pattern != null) {
AccessLogValve valve = new AccessLogValve();
valve.setPattern(pattern);
valve.setSuffix(".log");
tomcatFactory.addContextValves(valve);
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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.autoconfigure.service;
import java.util.List;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.context.annotation.EnableConfigurationProperties;
import org.springframework.bootstrap.service.properties.EndpointsProperties;
import org.springframework.bootstrap.service.properties.ManagementServerProperties;
import org.springframework.bootstrap.service.properties.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* {@link EnableAutoConfiguration Auto-configuration} for service apps.
*
* @author Dave Syer
*/
@Configuration
@Import({ ManagementConfiguration.class, MetricConfiguration.class,
ServerConfiguration.class, SecurityConfiguration.class,
MetricFilterConfiguration.class, AuditConfiguration.class })
public class ServiceAutoConfiguration extends WebMvcConfigurationSupport {
@Override
protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
addDefaultHttpMessageConverters(converters);
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
jacksonConverter.getObjectMapper().disable(
SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
}
}
}
/*
* ServerProperties has to be declared in a non-conditional bean, so that it gets
* added to the context early enough
*/
@EnableConfigurationProperties({ ServerProperties.class,
ManagementServerProperties.class })
public static class ServerPropertiesConfiguration {
@Bean
@ConditionalOnMissingBean(EndpointsProperties.class)
public EndpointsProperties endpointsProperties() {
return new EndpointsProperties();
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.autoconfigure.service;
import javax.servlet.Servlet;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.shutdown.ShutdownEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for /shutdown endpoint.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnMissingBean({ ShutdownEndpoint.class })
public class ShutdownConfiguration {
@Bean
public ShutdownEndpoint shutdownEndpoint() {
return new ShutdownEndpoint();
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.autoconfigure.service;
import javax.servlet.Servlet;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.bootstrap.service.trace.TraceEndpoint;
import org.springframework.bootstrap.service.trace.TraceRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for /trace endpoint.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnMissingBean({ TraceEndpoint.class })
public class TraceConfiguration {
@Autowired
private TraceRepository traceRepository;
@Configuration
public static class SecurityFilterPostProcessorConfiguration {
@Autowired(required = false)
private TraceRepository traceRepository = new InMemoryTraceRepository();
@Bean
@ConditionalOnMissingBean(TraceRepository.class)
protected TraceRepository traceRepository() {
return this.traceRepository;
}
@Value("${management.dump_requests:false}")
private boolean dumpRequests;
@Bean
@ConditionalOnClass(name = "org.springframework.security.web.SecurityFilterChain")
public SecurityFilterPostProcessor securityFilterPostProcessor(
BeanFactory beanFactory) {
SecurityFilterPostProcessor processor = new SecurityFilterPostProcessor(
this.traceRepository);
processor.setDumpRequests(this.dumpRequests);
return processor;
}
}
@Bean
public TraceEndpoint traceEndpoint() {
return new TraceEndpoint(this.traceRepository);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.autoconfigure.service;
import javax.servlet.Servlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.bootstrap.service.metrics.MetricRepository;
import org.springframework.bootstrap.service.varz.PublicMetrics;
import org.springframework.bootstrap.service.varz.VanillaPublicMetrics;
import org.springframework.bootstrap.service.varz.VarzEndpoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.DispatcherServlet;
/**
* {@link EnableAutoConfiguration Auto-configuration} for /varz endpoint.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class })
@ConditionalOnMissingBean({ VarzEndpoint.class })
public class VarzConfiguration {
@Autowired
private MetricRepository repository;
@Autowired(required = false)
private PublicMetrics metrics;
@Bean
public VarzEndpoint varzEndpoint() {
if (this.metrics == null) {
this.metrics = new VanillaPublicMetrics(this.repository);
}
return new VarzEndpoint(this.metrics);
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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.service.audit;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* A value object representing an audit event: at a particular time, a particular user or
* agent carried out an action of a particular type. This object records the details of
* such an event.
*
* @author Dave Syer
*
*/
public class AuditEvent {
final private Date timestamp;
final private String principal;
final private String type;
final private Map<String, Object> data;
/**
* Create a new audit event for the current time from data provided as name-value
* pairs
*/
public AuditEvent(String principal, String type, String... data) {
this(new Date(), principal, type, convert(data));
}
/**
* Create a new audit event for the current time
*/
public AuditEvent(String principal, String type, Map<String, Object> data) {
this(new Date(), principal, type, data);
}
/**
* Create a new audit event.
*/
public AuditEvent(Date timestamp, String principal, String type,
Map<String, Object> data) {
this.timestamp = timestamp;
this.principal = principal;
this.type = type;
this.data = Collections.unmodifiableMap(data);
}
public Date getTimestamp() {
return this.timestamp;
}
public String getPrincipal() {
return this.principal;
}
public String getType() {
return this.type;
}
public Map<String, Object> getData() {
return this.data;
}
private static Map<String, Object> convert(String[] data) {
Map<String, Object> result = new HashMap<String, Object>();
for (String entry : data) {
if (entry.contains("=")) {
int index = entry.indexOf("=");
result.put(entry.substring(0, index), entry.substring(index + 1));
} else {
result.put(entry, null);
}
}
return result;
}
@Override
public String toString() {
return "AuditEvent [timestamp=" + this.timestamp + ", principal="
+ this.principal + ", type=" + this.type + ", data=" + this.data + "]";
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.service.audit;
import java.util.Date;
import java.util.List;
/**
* @author Dave Syer
*
*/
public interface AuditEventRepository {
/**
* Find audit events relating to the specified principal since the time provided.
*
* @param principal the principal name to search for
* @param after timestamp of earliest result required
* @return audit events relating to the principal
*/
List<AuditEvent> find(String principal, Date after);
/**
* Log an event.
*
* @param event the audit event to log
*/
void add(AuditEvent event);
}

View File

@@ -0,0 +1,67 @@
/*
* 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.service.audit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Dave Syer
*
*/
public class InMemoryAuditEventRepository implements AuditEventRepository {
private int capacity = 100;
private Map<String, List<AuditEvent>> events = new HashMap<String, List<AuditEvent>>();
/**
* @param capacity the capacity to set
*/
public void setCapacity(int capacity) {
this.capacity = capacity;
}
@Override
public List<AuditEvent> find(String principal, Date after) {
synchronized (this.events) {
return Collections.unmodifiableList(getEvents(principal));
}
}
private List<AuditEvent> getEvents(String principal) {
if (!this.events.containsKey(principal)) {
this.events.put(principal, new ArrayList<AuditEvent>());
}
return this.events.get(principal);
}
@Override
public void add(AuditEvent event) {
synchronized (this.events) {
List<AuditEvent> list = getEvents(event.getPrincipal());
while (list.size() >= this.capacity) {
list.remove(0);
}
list.add(event);
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.service.audit.listener;
import org.springframework.bootstrap.service.audit.AuditEvent;
import org.springframework.context.ApplicationEvent;
/**
* @author Dave Syer
*
*/
public class AuditApplicationEvent extends ApplicationEvent {
private AuditEvent auditEvent;
/**
* @param auditEvent the source of this event
*/
public AuditApplicationEvent(AuditEvent auditEvent) {
super(auditEvent);
this.auditEvent = auditEvent;
}
/**
* @return the audit event
*/
public AuditEvent getAuditEvent() {
return this.auditEvent;
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.service.audit.listener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.bootstrap.service.audit.AuditEventRepository;
import org.springframework.context.ApplicationListener;
/**
* @author Dave Syer
*
*/
public class AuditListener implements ApplicationListener<AuditApplicationEvent> {
private static Log logger = LogFactory.getLog(AuditListener.class);
private final AuditEventRepository auditEventRepository;
public AuditListener(AuditEventRepository auditEventRepository) {
this.auditEventRepository = auditEventRepository;
}
@Override
public void onApplicationEvent(AuditApplicationEvent event) {
logger.info(event.getAuditEvent());
this.auditEventRepository.add(event.getAuditEvent());
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.service.error;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.bootstrap.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Basic fallback global error endpoint, rendering servlet container error codes and
* messages where available. More specific errors can be handled either using Spring MVC
* abstractions (e.g. {@code @ExceptionHandler}) or by adding servlet
* {@link AbstractEmbeddedServletContainerFactory#setErrorPages(java.util.Set) container
* error pages}.
*
* @author Dave Syer
*/
@Controller
public class ErrorEndpoint {
private Log logger = LogFactory.getLog(ErrorEndpoint.class);
@RequestMapping("${endpoints.error.path:/error}")
@ResponseBody
public Map<String, Object> error(HttpServletRequest request) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
try {
Throwable error = (Throwable) request
.getAttribute("javax.servlet.error.exception");
Object obj = request.getAttribute("javax.servlet.error.status_code");
int status = 999;
if (obj != null) {
status = (Integer) obj;
map.put("error", HttpStatus.valueOf(status).getReasonPhrase());
} else {
map.put("error", "None");
}
map.put("status", status);
if (error != null) {
while (error instanceof ServletException) {
error = ((ServletException) error).getCause();
}
map.put("exception", error.getClass().getName());
map.put("message", error.getMessage());
String trace = request.getParameter("trace");
if (trace != null && !"false".equals(trace.toLowerCase())) {
StringWriter stackTrace = new StringWriter();
error.printStackTrace(new PrintWriter(stackTrace));
stackTrace.flush();
map.put("trace", stackTrace.toString());
}
this.logger.error(error);
} else {
Object message = request.getAttribute("javax.servlet.error.message");
map.put("message", message == null ? "No message available" : message);
}
return map;
} catch (Exception e) {
map.put("error", e.getClass().getName());
map.put("message", e.getMessage());
this.logger.error(e);
return map;
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.service.health;
/**
* @author Dave Syer
*/
public interface HealthIndicator<T> {
/**
* @return an indication of health
*/
T health();
}

View File

@@ -0,0 +1,45 @@
/*
* 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.service.health;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@Controller
public class HealthzEndpoint<T> {
private HealthIndicator<? extends T> indicator;
/**
* @param indicator
*/
public HealthzEndpoint(HealthIndicator<? extends T> indicator) {
super();
this.indicator = indicator;
}
@RequestMapping("${endpoints.healthz.path:/healthz}")
@ResponseBody
public T healthz() {
return this.indicator.health();
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.service.health;
/**
* @author Dave Syer
*/
public class VanillaHealthIndicator implements HealthIndicator<String> {
@Override
public String health() {
return "ok";
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.service.info;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@Controller
public class InfoEndpoint {
private Map<String, Object> info;
/**
* @param info
*/
public InfoEndpoint(Map<String, Object> info) {
this.info = new LinkedHashMap<String, Object>(info);
this.info.putAll(getAdditionalInfo());
}
@RequestMapping("${endpoints.info.path:/info}")
@ResponseBody
public Map<String, Object> info() {
return this.info;
}
protected Map<String, Object> getAdditionalInfo() {
return Collections.emptyMap();
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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.service.metrics;
public interface CounterService {
void increment(String metricName);
void decrement(String metricName);
void reset(String metricName);
}

View File

@@ -0,0 +1,59 @@
/*
* 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.service.metrics;
import java.util.Date;
/**
* @author Dave Syer
*/
public class DefaultCounterService implements CounterService {
private MetricRepository counterRepository;
/**
* @param counterRepository
*/
public DefaultCounterService(MetricRepository counterRepository) {
super();
this.counterRepository = counterRepository;
}
@Override
public void increment(String metricName) {
this.counterRepository.increment(wrap(metricName), 1, new Date());
}
@Override
public void decrement(String metricName) {
this.counterRepository.increment(wrap(metricName), -1, new Date());
}
@Override
public void reset(String metricName) {
this.counterRepository.set(wrap(metricName), 0, new Date());
}
private String wrap(String metricName) {
if (metricName.startsWith("counter")) {
return metricName;
} else {
return "counter." + metricName;
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.service.metrics;
import java.util.Date;
/**
* @author Dave Syer
*/
public class DefaultGaugeService implements GaugeService {
private MetricRepository metricRepository;
/**
* @param counterRepository
*/
public DefaultGaugeService(MetricRepository counterRepository) {
super();
this.metricRepository = counterRepository;
}
@Override
public void set(String metricName, double value) {
this.metricRepository.set(wrap(metricName), value, new Date());
}
private String wrap(String metricName) {
if (metricName.startsWith("gauge")) {
return metricName;
} else {
return "gauge." + metricName;
}
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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.service.metrics;
public interface GaugeService {
void set(String metricName, double value);
}

View File

@@ -0,0 +1,80 @@
/*
* 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.service.metrics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* @author Dave Syer
*/
public class InMemoryMetricRepository implements MetricRepository {
private ConcurrentMap<String, Measurement> metrics = new ConcurrentHashMap<String, Measurement>();
@Override
public void increment(String metricName, int amount, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
new Measurement(timestamp, metric.increment(amount)));
} else {
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, amount)));
}
}
@Override
public void set(String metricName, double value, Date timestamp) {
Measurement current = this.metrics.get(metricName);
if (current != null) {
Metric metric = current.getMetric();
this.metrics.replace(metricName, current,
new Measurement(timestamp, metric.set(value)));
} else {
this.metrics.putIfAbsent(metricName, new Measurement(timestamp, new Metric(
metricName, value)));
}
}
@Override
public void delete(String metricName) {
this.metrics.remove(metricName);
}
@Override
public Metric findOne(String metricName) {
if (this.metrics.containsKey(metricName)) {
return this.metrics.get(metricName).getMetric();
}
return new Metric(metricName, 0);
}
@Override
public Collection<Metric> findAll() {
ArrayList<Metric> result = new ArrayList<Metric>();
for (Measurement measurement : this.metrics.values()) {
result.add(measurement.getMetric());
}
return result;
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.service.metrics;
import java.util.Date;
/**
* @author Dave Syer
*/
public class Measurement {
private Date timestamp;
private Metric metric;
public Measurement(Date timestamp, Metric metric) {
this.timestamp = timestamp;
this.metric = metric;
}
public Date getTimestamp() {
return this.timestamp;
}
public Metric getMetric() {
return this.metric;
}
@Override
public String toString() {
return "Measurement [dateTime=" + this.timestamp + ", metric=" + this.metric + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((this.timestamp == null) ? 0 : this.timestamp.hashCode());
result = prime * result + ((this.metric == null) ? 0 : this.metric.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Measurement other = (Measurement) obj;
if (this.timestamp == null) {
if (other.timestamp != null)
return false;
} else if (!this.timestamp.equals(other.timestamp))
return false;
if (this.metric == null) {
if (other.metric != null)
return false;
} else if (!this.metric.equals(other.metric))
return false;
return true;
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.service.metrics;
/**
* @author Dave Syer
*/
public class Metric {
private final String name;
private final double value;
public Metric(String name, double value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public double getValue() {
return this.value;
}
public Metric increment(int amount) {
return new Metric(this.name, new Double(((int) this.value) + amount));
}
public Metric set(double value) {
return new Metric(this.name, value);
}
@Override
public String toString() {
return "Metric [name=" + this.name + ", value=" + this.value + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
long temp;
temp = Double.doubleToLongBits(this.value);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Metric other = (Metric) obj;
if (this.name == null) {
if (other.name != null)
return false;
} else if (!this.name.equals(other.name))
return false;
if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(other.value))
return false;
return true;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.service.metrics;
import java.util.Collection;
import java.util.Date;
/**
* @author Dave Syer
*/
public interface MetricRepository {
void increment(String metricName, int amount, Date timestamp);
void set(String metricName, double value, Date timestamp);
void delete(String metricName);
Metric findOne(String metricName);
Collection<Metric> findAll();
}

View File

@@ -0,0 +1,105 @@
/*
* 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.service.properties;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
/**
* Externalized configuration for endpoints (e.g. paths)
*
* @author Dave Syer
*
*/
@ConfigurationProperties(name = "endpoints", ignoreUnknownFields = false)
public class EndpointsProperties {
@Valid
private Endpoint info = new Endpoint("/info");
@Valid
private Endpoint varz = new Endpoint("/varz");
@Valid
private Endpoint healthz = new Endpoint("/healthz");
@Valid
private Endpoint error = new Endpoint("/error");
@Valid
private Endpoint shutdown = new Endpoint("/shutdown");
@Valid
private Endpoint trace = new Endpoint("/trace");
@Valid
private Endpoint dump = new Endpoint("/dump");
public Endpoint getInfo() {
return this.info;
}
public Endpoint getVarz() {
return this.varz;
}
public Endpoint getHealthz() {
return this.healthz;
}
public Endpoint getError() {
return this.error;
}
public Endpoint getShutdown() {
return this.shutdown;
}
public Endpoint getTrace() {
return this.trace;
}
public Endpoint getDump() {
return this.dump;
}
public static class Endpoint {
@NotNull
@Pattern(regexp = "/[^/]*", message = "Path must start with /")
private String path;
public Endpoint() {
}
public Endpoint(String path) {
super();
this.path = path;
}
public String getPath() {
return this.path;
}
public void setPath(String path) {
this.path = path;
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.service.properties;
import javax.validation.constraints.NotNull;
import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
/**
* Properties for the management server (e.g. port and path settings).
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "management", ignoreUnknownFields = false)
public class ManagementServerProperties {
private int port = 8080;
@NotNull
private String contextPath = "";
private boolean allowShutdown = false;
public boolean isAllowShutdown() {
return this.allowShutdown;
}
public void setAllowShutdown(boolean allowShutdown) {
this.allowShutdown = allowShutdown;
}
public int getPort() {
return this.port;
}
public void setPort(int port) {
this.port = port;
}
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.service.properties;
import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
/**
* Properties for the security aspects of the service.
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "security", ignoreUnknownFields = false)
public class SecurityProperties {
private boolean requireSsl;
public boolean isRequireSsl() {
return requireSsl;
}
public void setRequireSsl(boolean requireSsl) {
this.requireSsl = requireSsl;
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.service.properties;
import java.io.File;
import javax.validation.constraints.NotNull;
import org.springframework.bootstrap.context.annotation.ConfigurationProperties;
/**
* Properties for the web server (e.g. port and path settings).
*
* @author Dave Syer
*/
@ConfigurationProperties(name = "server", ignoreUnknownFields = false)
public class ServerProperties {
private int port = 8080;
@NotNull
private String contextPath = "";
private Tomcat tomcat = new Tomcat();
public Tomcat getTomcat() {
return this.tomcat;
}
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public int getPort() {
return this.port;
}
public void setPort(int port) {
this.port = port;
}
public static class Tomcat {
private String accessLogPattern;
private String protocolHeader = "x-forwarded-proto";
private String remoteIpHeader = "x-forwarded-for";
private File basedir;
public File getBasedir() {
return this.basedir;
}
public void setBasedir(File basedir) {
this.basedir = basedir;
}
public String getAccessLogPattern() {
return this.accessLogPattern;
}
public void setAccessLogPattern(String accessLogPattern) {
this.accessLogPattern = accessLogPattern;
}
public String getProtocolHeader() {
return this.protocolHeader;
}
public void setProtocolHeader(String protocolHeader) {
this.protocolHeader = protocolHeader;
}
public String getRemoteIpHeader() {
return this.remoteIpHeader;
}
public void setRemoteIpHeader(String remoteIpHeader) {
this.remoteIpHeader = remoteIpHeader;
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.service.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.bootstrap.service.audit.AuditEvent;
import org.springframework.bootstrap.service.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.security.web.authentication.switchuser.AuthenticationSwitchUserEvent;
/**
* @author Dave Syer
*
*/
public class AuthenticationAuditListener implements
ApplicationListener<AbstractAuthenticationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event instanceof AbstractAuthenticationFailureEvent) {
data.put("type", ((AbstractAuthenticationFailureEvent) event).getException()
.getClass().getName());
data.put("message", ((AbstractAuthenticationFailureEvent) event)
.getException().getMessage());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_FAILURE", data));
} else if (event instanceof AuthenticationSwitchUserEvent) {
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());
}
data.put("target", ((AuthenticationSwitchUserEvent) event).getTargetUser()
.getUsername());
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_SWITCH", data));
} else {
if (event.getAuthentication().getDetails() != null) {
data.put("details", event.getAuthentication().getDetails());
}
publish(new AuditEvent(event.getAuthentication().getName(),
"AUTHENTICATION_SUCCESS", data));
}
}
private void publish(AuditEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(new AuditApplicationEvent(event));
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.service.security;
import java.util.HashMap;
import java.util.Map;
import org.springframework.bootstrap.service.audit.AuditEvent;
import org.springframework.bootstrap.service.audit.listener.AuditApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.security.access.event.AbstractAuthorizationEvent;
import org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;
import org.springframework.security.access.event.AuthorizationFailureEvent;
/**
* @author Dave Syer
*
*/
public class AuthorizationAuditListener implements
ApplicationListener<AbstractAuthorizationEvent>, ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void onApplicationEvent(AbstractAuthorizationEvent event) {
Map<String, Object> data = new HashMap<String, Object>();
if (event instanceof AuthenticationCredentialsNotFoundEvent) {
data.put("type", ((AuthenticationCredentialsNotFoundEvent) event)
.getCredentialsNotFoundException().getClass().getName());
data.put("message", ((AuthenticationCredentialsNotFoundEvent) event)
.getCredentialsNotFoundException().getMessage());
publish(new AuditEvent("<unknown>", "AUTHENTICATION_FAILURE", data));
} else if (event instanceof AuthorizationFailureEvent) {
data.put("type", ((AuthorizationFailureEvent) event)
.getAccessDeniedException().getClass().getName());
data.put("message", ((AuthorizationFailureEvent) event)
.getAccessDeniedException().getMessage());
publish(new AuditEvent(((AuthorizationFailureEvent) event)
.getAuthentication().getName(), "AUTHORIZATION_FAILURE", data));
}
}
private void publish(AuditEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(new AuditApplicationEvent(event));
}
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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.service.security;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.bootstrap.service.trace.TraceRepository;
import org.springframework.core.Ordered;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.Assert;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Bean post processor that adds a filter to Spring Security. The filter (optionally) logs
* request headers at trace level and also sends the headers to a {@link TraceRepository}
* for later analysis.
*
* @author Luke Taylor
* @author Dave Syer
*
*/
public class SecurityFilterPostProcessor implements BeanPostProcessor, Ordered {
private final static Log logger = LogFactory
.getLog(SecurityFilterPostProcessor.class);
private boolean dumpRequests = false;
private List<String> ignore = Collections.emptyList();
private TraceRepository traceRepository = new InMemoryTraceRepository();
private int order = Integer.MAX_VALUE;
/**
* @param order the order to set
*/
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
/**
* @param traceRepository
*/
public SecurityFilterPostProcessor(TraceRepository traceRepository) {
super();
this.traceRepository = traceRepository;
}
/**
* List of filter chains which should be ignored completely.
*/
public void setIgnore(List<String> ignore) {
Assert.notNull(ignore);
this.ignore = ignore;
}
/**
* Debugging feature. If enabled, and trace logging is enabled
*/
public void setDumpRequests(boolean dumpRequests) {
this.dumpRequests = dumpRequests;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (!this.ignore.contains(beanName)) {
if (bean instanceof FilterChainProxy) {
FilterChainProxy proxy = (FilterChainProxy) bean;
for (SecurityFilterChain filterChain : proxy.getFilterChains()) {
processFilterChain(filterChain, beanName);
}
}
if (bean instanceof SecurityFilterChain) {
processFilterChain((SecurityFilterChain) bean, beanName);
}
}
return bean;
}
private void processFilterChain(SecurityFilterChain filterChain, String beanName) {
logger.info("Processing security filter chain " + beanName);
Filter loggingFilter = new WebRequestLoggingFilter(beanName);
filterChain.getFilters().add(0, loggingFilter);
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
class WebRequestLoggingFilter implements Filter {
final Log logger = LogFactory.getLog(WebRequestLoggingFilter.class);
private final String name;
private ObjectMapper objectMapper = new ObjectMapper();
WebRequestLoggingFilter(String name) {
this.name = name;
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
Map<String, Object> trace = getTrace(request);
@SuppressWarnings("unchecked")
Map<String, Object> headers = (Map<String, Object>) trace.get("headers");
SecurityFilterPostProcessor.this.traceRepository.add(trace);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Filter chain '" + this.name + "' processing request "
+ request.getMethod() + " " + request.getRequestURI());
if (SecurityFilterPostProcessor.this.dumpRequests) {
try {
this.logger.trace("Headers: "
+ this.objectMapper.writeValueAsString(headers));
} catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot create JSON", e);
}
}
}
chain.doFilter(request, response);
}
protected Map<String, Object> getTrace(HttpServletRequest request) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
Enumeration<String> names = request.getHeaderNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
List<String> values = Collections.list(request.getHeaders(name));
Object value = values;
if (values.size() == 1) {
value = values.get(0);
} else if (values.isEmpty()) {
value = "";
}
map.put(name, value);
}
Map<String, Object> trace = new LinkedHashMap<String, Object>();
trace.put("chain", this.name);
trace.put("method", request.getMethod());
trace.put("path", request.getRequestURI());
trace.put("headers", map);
return trace;
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.service.shutdown;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.service.properties.ManagementServerProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.support.ServletRequestHandledEvent;
/**
* @author Dave Syer
*
*/
@Controller
public class ShutdownEndpoint implements ApplicationContextAware,
ApplicationListener<ServletRequestHandledEvent> {
private static Log logger = LogFactory.getLog(ShutdownEndpoint.class);
private ConfigurableApplicationContext context;
@Autowired
private ManagementServerProperties configuration = new ManagementServerProperties();
private boolean shuttingDown = false;
@RequestMapping(value = "${endpoints.shutdown.path:/shutdown}", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> shutdown() {
if (this.configuration.isAllowShutdown()) {
this.shuttingDown = true;
return Collections.<String, Object> singletonMap("message",
"Shutting down, bye...");
} else {
return Collections.<String, Object> singletonMap("message",
"Shutdown not enabled, sorry.");
}
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (context instanceof ConfigurableApplicationContext) {
this.context = (ConfigurableApplicationContext) context;
}
}
@Override
public void onApplicationEvent(ServletRequestHandledEvent event) {
if (this.context != null && this.configuration.isAllowShutdown()
&& this.shuttingDown) {
new Thread(new Runnable() {
@Override
public void run() {
logger.info("Shutting down Spring in response to admin request");
ConfigurableApplicationContext context = ShutdownEndpoint.this.context;
ApplicationContext parent = context.getParent();
context.close();
if (parent != null
&& parent instanceof ConfigurableApplicationContext) {
context = (ConfigurableApplicationContext) parent;
context.close();
parent = context.getParent();
}
}
}).start();
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.service.trace;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author Dave Syer
*
*/
public class InMemoryTraceRepository implements TraceRepository {
private int capacity = 100;
private List<Trace> traces = new ArrayList<Trace>();
/**
* @param capacity the capacity to set
*/
public void setCapacity(int capacity) {
this.capacity = capacity;
}
@Override
public List<Trace> traces() {
synchronized (this.traces) {
return Collections.unmodifiableList(this.traces);
}
}
@Override
public void add(Map<String, Object> map) {
Trace trace = new Trace(new Date(), map);
synchronized (this.traces) {
while (this.traces.size() >= this.capacity) {
this.traces.remove(0);
}
this.traces.add(trace);
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.service.trace;
import java.util.Date;
import java.util.Map;
/**
* @author Dave Syer
*
*/
public class Trace {
private Date timestamp;
private Map<String, Object> info;
public Trace(Date timestamp, Map<String, Object> info) {
super();
this.timestamp = timestamp;
this.info = info;
}
public Date getTimestamp() {
return this.timestamp;
}
public Map<String, Object> getInfo() {
return this.info;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.service.trace;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@Controller
public class TraceEndpoint {
private TraceRepository tracer;
/**
* @param tracer
*/
public TraceEndpoint(TraceRepository tracer) {
super();
this.tracer = tracer;
}
@RequestMapping("${endpoints.trace.path:/trace}")
@ResponseBody
public List<Trace> trace() {
return this.tracer.traces();
}
@RequestMapping("${endpoints.dump.path:/dump}")
@ResponseBody
public List<ThreadInfo> dump() {
return Arrays.asList(ManagementFactory.getThreadMXBean().dumpAllThreads(true,
true));
}
}

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.service.trace;
import java.util.List;
import java.util.Map;
/**
* A repository for traces. Traces are simple documents (maps) with a timestamp, and can
* be used for analysing contextual information like HTTP headers.
*
* @author Dave Syer
*
*/
public interface TraceRepository {
List<Trace> traces();
void add(Map<String, Object> trace);
}

View File

@@ -0,0 +1,33 @@
/*
* 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.service.varz;
import java.util.Collection;
import org.springframework.bootstrap.service.metrics.Metric;
/**
* @author Dave Syer
*/
public interface PublicMetrics {
/**
* @return an indication of current state through metrics
*/
Collection<Metric> metrics();
}

View File

@@ -0,0 +1,49 @@
/*
* 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.service.varz;
import java.util.Collection;
import java.util.LinkedHashSet;
import org.springframework.bootstrap.service.metrics.Metric;
import org.springframework.bootstrap.service.metrics.MetricRepository;
import org.springframework.util.Assert;
/**
* @author Dave Syer
*/
public class VanillaPublicMetrics implements PublicMetrics {
private MetricRepository metricRepository;
public VanillaPublicMetrics(MetricRepository metricRepository) {
Assert.notNull(metricRepository, "A MetricRepository must be provided");
this.metricRepository = metricRepository;
}
@Override
public Collection<Metric> metrics() {
Collection<Metric> result = new LinkedHashSet<Metric>(
this.metricRepository.findAll());
result.add(new Metric("mem", new Long(Runtime.getRuntime().totalMemory()) / 1024));
result.add(new Metric("mem.free",
new Long(Runtime.getRuntime().freeMemory()) / 1024));
result.add(new Metric("processors", Runtime.getRuntime().availableProcessors()));
return result;
}
}

View File

@@ -0,0 +1,52 @@
/*
* 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.service.varz;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.bootstrap.service.metrics.Metric;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author Dave Syer
*/
@Controller
public class VarzEndpoint {
private PublicMetrics metrics;
/**
* @param metrics
*/
public VarzEndpoint(PublicMetrics metrics) {
this.metrics = metrics;
}
@RequestMapping("${endpoints.varz.path:/varz}")
@ResponseBody
public Map<String, Object> varz() {
Map<String, Object> result = new LinkedHashMap<String, Object>();
for (Metric metric : this.metrics.metrics()) {
result.put(metric.getName(), metric.getValue());
}
return result;
}
}

View File

@@ -0,0 +1,4 @@
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\
org.springframework.bootstrap.autoconfigure.service.ServiceAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
org.springframework.bootstrap.logging.LoggingInitializer

View File

@@ -0,0 +1,48 @@
/*
* 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.service.audit;
import java.util.Collections;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Dave Syer
*
*/
public class AuditEventTests {
@Test
public void testNowEvent() throws Exception {
AuditEvent event = new AuditEvent("phil", "UNKNOWN", Collections.singletonMap(
"a", (Object) "b"));
assertEquals("b", event.getData().get("a"));
assertEquals("UNKNOWN", event.getType());
assertEquals("phil", event.getPrincipal());
assertNotNull(event.getTimestamp());
}
@Test
public void testConvertStringsToData() throws Exception {
AuditEvent event = new AuditEvent("phil", "UNKNOWN", "a=b", "c=d");
assertEquals("b", event.getData().get("a"));
assertEquals("d", event.getData().get("c"));
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.service.audit;
import java.util.Date;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Dave Syer
*
*/
public class InMemoryAuditEventRepositoryTests {
private InMemoryAuditEventRepository repository = new InMemoryAuditEventRepository();
@Test
public void testAddToCapacity() throws Exception {
this.repository.setCapacity(2);
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
this.repository.add(new AuditEvent("dave", "UNKNOWN"));
this.repository.add(new AuditEvent("dave", "UNKNOWN"));
this.repository.add(new AuditEvent("phil", "UNKNOWN"));
assertEquals(2, this.repository.find("phil", new Date(0L)).size());
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.service.properties;
import org.junit.Test;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Externalized configuration for endpoints (e.g. paths)
*
* @author Dave Syer
*
*/
public class EndpointsPropertiesTests {
private EndpointsProperties properties = new EndpointsProperties();
@Test
public void testDefaultPathValid() throws Exception {
assertEquals("/error", this.properties.getError().getPath());
Errors errors = validate(this.properties);
assertFalse(errors.hasErrors());
}
@Test
public void testQueryPathValid() throws Exception {
Errors errors = validate(new EndpointsProperties.Endpoint("/foo?bar"));
assertFalse(errors.hasErrors());
}
@Test
public void testEmptyPathInvalid() throws Exception {
Errors errors = validate(new EndpointsProperties.Endpoint(""));
assertTrue(errors.hasErrors());
}
@Test
public void testDoubleSlashInvalid() throws Exception {
Errors errors = validate(new EndpointsProperties.Endpoint("//foo"));
assertTrue(errors.hasErrors());
}
@Test
public void testEmptyPathInProperties() throws Exception {
this.properties.getError().setPath("");
Errors errors = validate(this.properties);
assertTrue(errors.hasErrors());
}
/**
* @return
*/
private Errors validate(Object target) {
BindException errors = new BindException(target, "properties");
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
validator.validate(target, errors);
return errors;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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.service.security;
import java.util.Map;
import org.junit.Test;
import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor;
import org.springframework.bootstrap.service.security.SecurityFilterPostProcessor.WebRequestLoggingFilter;
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
import org.springframework.mock.web.MockHttpServletRequest;
import static org.junit.Assert.assertEquals;
/**
* @author Dave Syer
*
*/
public class SecurityFilterPostProcessorTests {
private SecurityFilterPostProcessor processor = new SecurityFilterPostProcessor(
new InMemoryTraceRepository());
@Test
public void filterDumpsRequest() {
WebRequestLoggingFilter filter = this.processor.new WebRequestLoggingFilter("foo");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.addHeader("Accept", "application/json");
Map<String, Object> trace = filter.getTrace(request);
assertEquals("GET", trace.get("method"));
assertEquals("/foo", trace.get("path"));
assertEquals("{Accept=application/json}", trace.get("headers").toString());
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.service.trace;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Dave Syer
*
*/
public class InMemoryTraceRepositoryTests {
private InMemoryTraceRepository repository = new InMemoryTraceRepository();
@Test
public void capacityLimited() {
this.repository.setCapacity(2);
this.repository.add(Collections.<String, Object> singletonMap("foo", "bar"));
this.repository.add(Collections.<String, Object> singletonMap("bar", "foo"));
this.repository.add(Collections.<String, Object> singletonMap("bar", "bar"));
List<Trace> traces = this.repository.traces();
assertEquals(2, traces.size());
assertEquals("bar", traces.get(1).getInfo().get("bar"));
}
}