diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ActuatorAutoConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ActuatorAutoConfiguration.java index b2cd5a677f..3bf53b2c15 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ActuatorAutoConfiguration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ActuatorAutoConfiguration.java @@ -40,7 +40,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; */ @Configuration @Import({ ManagementConfiguration.class, MetricConfiguration.class, - ServerConfiguration.class, SecurityConfiguration.class, + ServerConfiguration.class, SecurityConfiguration.class, TraceConfiguration.class, MetricFilterConfiguration.class, AuditConfiguration.class }) public class ActuatorAutoConfiguration extends WebMvcConfigurationSupport { diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementConfiguration.java index 534080917d..9f54e83091 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementConfiguration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/ManagementConfiguration.java @@ -51,7 +51,7 @@ public class ManagementConfiguration implements ApplicationContextAware, Disposa @ConditionalOnExpression("${server.port:8080} == ${management.port:${server.port:8080}}") @Configuration @Import({ VarzConfiguration.class, HealthzConfiguration.class, - ShutdownConfiguration.class, TraceConfiguration.class }) + ShutdownConfiguration.class }) public static class ManagementEndpointsConfiguration { } @@ -78,7 +78,7 @@ public class ManagementConfiguration implements ApplicationContextAware, Disposa context.setParent(this.parent); context.register(ManagementServerConfiguration.class, VarzConfiguration.class, HealthzConfiguration.class, - ShutdownConfiguration.class, TraceConfiguration.class); + ShutdownConfiguration.class); context.refresh(); this.context = context; } diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/TraceConfiguration.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/TraceConfiguration.java index 0cb62c15bc..72c0af444a 100644 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/TraceConfiguration.java +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/autoconfigure/TraceConfiguration.java @@ -21,10 +21,10 @@ 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.actuate.security.SecurityFilterPostProcessor; import org.springframework.bootstrap.actuate.trace.InMemoryTraceRepository; import org.springframework.bootstrap.actuate.trace.TraceEndpoint; import org.springframework.bootstrap.actuate.trace.TraceRepository; +import org.springframework.bootstrap.actuate.trace.WebRequestLoggingFilter; import org.springframework.bootstrap.context.annotation.ConditionalOnClass; import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean; import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration; @@ -38,43 +38,33 @@ import org.springframework.web.servlet.DispatcherServlet; * @author Dave Syer */ @Configuration -@ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) -@ConditionalOnMissingBean({ TraceEndpoint.class }) public class TraceConfiguration { - @Autowired - private TraceRepository traceRepository; + @Autowired(required = false) + private TraceRepository traceRepository = new InMemoryTraceRepository(); - @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; - } + @Value("${management.dump_requests:false}") + private boolean dumpRequests; + @Bean + @ConditionalOnMissingBean(TraceRepository.class) + protected TraceRepository traceRepository() { + return this.traceRepository; } @Bean + @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) + @ConditionalOnMissingBean({ TraceEndpoint.class }) public TraceEndpoint traceEndpoint() { return new TraceEndpoint(this.traceRepository); } + @Bean + @ConditionalOnClass({ Servlet.class, DispatcherServlet.class }) + public WebRequestLoggingFilter securityFilterPostProcessor(BeanFactory beanFactory) { + WebRequestLoggingFilter filter = new WebRequestLoggingFilter(this.traceRepository); + filter.setDumpRequests(this.dumpRequests); + return filter; + } + } diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/security/SecurityFilterPostProcessor.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/security/SecurityFilterPostProcessor.java deleted file mode 100644 index a899465ea8..0000000000 --- a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/security/SecurityFilterPostProcessor.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2012-2013 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.bootstrap.actuate.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.actuate.trace.InMemoryTraceRepository; -import org.springframework.bootstrap.actuate.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 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 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 trace = getTrace(request); - @SuppressWarnings("unchecked") - Map headers = (Map) 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 getTrace(HttpServletRequest request) { - - Map map = new LinkedHashMap(); - Enumeration names = request.getHeaderNames(); - - while (names.hasMoreElements()) { - String name = names.nextElement(); - List 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 trace = new LinkedHashMap(); - 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() { - } - } - -} diff --git a/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/trace/WebRequestLoggingFilter.java b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/trace/WebRequestLoggingFilter.java new file mode 100644 index 0000000000..444bc23445 --- /dev/null +++ b/spring-bootstrap-actuator/src/main/java/org/springframework/bootstrap/actuate/trace/WebRequestLoggingFilter.java @@ -0,0 +1,139 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.actuate.trace; + +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.core.Ordered; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Dave Syer + * + */ +public class WebRequestLoggingFilter implements Filter, Ordered { + + final Log logger = LogFactory.getLog(WebRequestLoggingFilter.class); + + private boolean dumpRequests = false; + + private final TraceRepository traceRepository; + + private int order = Integer.MAX_VALUE; + + private ObjectMapper objectMapper = new ObjectMapper(); + + /** + * @param traceRepository + */ + public WebRequestLoggingFilter(TraceRepository traceRepository) { + this.traceRepository = traceRepository; + } + + /** + * @param order the order to set + */ + public void setOrder(int order) { + this.order = order; + } + + @Override + public int getOrder() { + return this.order; + } + + /** + * Debugging feature. If enabled, and trace logging is enabled then web request + * headers will be logged. + */ + public void setDumpRequests(boolean dumpRequests) { + this.dumpRequests = dumpRequests; + } + + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) req; + HttpServletResponse response = (HttpServletResponse) res; + + Map trace = getTrace(request); + @SuppressWarnings("unchecked") + Map headers = (Map) trace.get("headers"); + this.traceRepository.add(trace); + if (this.logger.isTraceEnabled()) { + this.logger.trace("Processing request " + request.getMethod() + " " + + request.getRequestURI()); + if (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 getTrace(HttpServletRequest request) { + + Map map = new LinkedHashMap(); + Enumeration names = request.getHeaderNames(); + + while (names.hasMoreElements()) { + String name = names.nextElement(); + List 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 trace = new LinkedHashMap(); + 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() { + } + +} diff --git a/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/security/SecurityFilterPostProcessorTests.java b/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/trace/WebRequestLoggingFilterTests.java similarity index 70% rename from spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/security/SecurityFilterPostProcessorTests.java rename to spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/trace/WebRequestLoggingFilterTests.java index 0acb692f7e..bb692701a6 100644 --- a/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/security/SecurityFilterPostProcessorTests.java +++ b/spring-bootstrap-actuator/src/test/java/org/springframework/bootstrap/actuate/trace/WebRequestLoggingFilterTests.java @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.bootstrap.actuate.security; +package org.springframework.bootstrap.actuate.trace; import java.util.Map; import org.junit.Test; -import org.springframework.bootstrap.actuate.security.SecurityFilterPostProcessor; -import org.springframework.bootstrap.actuate.security.SecurityFilterPostProcessor.WebRequestLoggingFilter; import org.springframework.bootstrap.actuate.trace.InMemoryTraceRepository; +import org.springframework.bootstrap.actuate.trace.WebRequestLoggingFilter; import org.springframework.mock.web.MockHttpServletRequest; import static org.junit.Assert.assertEquals; @@ -29,17 +28,16 @@ import static org.junit.Assert.assertEquals; * @author Dave Syer * */ -public class SecurityFilterPostProcessorTests { +public class WebRequestLoggingFilterTests { - private SecurityFilterPostProcessor processor = new SecurityFilterPostProcessor( + private WebRequestLoggingFilter filter = new WebRequestLoggingFilter( 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 trace = filter.getTrace(request); + Map trace = this.filter.getTrace(request); assertEquals("GET", trace.get("method")); assertEquals("/foo", trace.get("path")); assertEquals("{Accept=application/json}", trace.get("headers").toString()); diff --git a/spring-bootstrap-starters/spring-bootstrap-web-starter/pom.xml b/spring-bootstrap-starters/spring-bootstrap-web-starter/pom.xml index 09fd6e8a9f..468ecba13b 100644 --- a/spring-bootstrap-starters/spring-bootstrap-web-starter/pom.xml +++ b/spring-bootstrap-starters/spring-bootstrap-web-starter/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.springframework.bootstrap @@ -15,12 +16,12 @@ ${project.version} - org.eclipse.jetty - jetty-webapp + org.apache.tomcat.embed + tomcat-embed-core - org.eclipse.jetty - jetty-util + org.apache.tomcat.embed + tomcat-embed-logging-juli org.springframework diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index 5080dadff2..333ec45e83 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -182,7 +182,8 @@ public class TomcatEmbeddedServletContainerFactory extends context.addServletMapping("*.jspx", "jsp"); } - private void customizeConnector(Connector connector) { + // Needs to be protected so it can be used by subclasses + protected void customizeConnector(Connector connector) { connector.setPort(getPort()); if (connector.getProtocolHandler() instanceof AbstractProtocol && getAddress() != null) {