From 4f3c731d37849b5ae452fbab8756c8b2044a1f38 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Sat, 30 Jan 2016 11:56:40 +0000 Subject: [PATCH] Fix trace filter so async requests are handled properly Previously, if a trace id came into the filter from the caller and the controller was async, the span context was not properly managed resulting in warnings in logs. Fixes gh-137 --- .../springframework/cloud/sleuth/Span.java | 58 +++++--- .../sleuth/instrument/web/TraceFilter.java | 127 ++++++++++-------- .../cloud/sleuth/trace/DefaultTracer.java | 50 +++---- .../cloud/sleuth/util/ExceptionUtils.java | 7 + .../web/TraceFilterIntegrationTests.java | 60 +++++++-- .../common/AbstractMvcIntegrationTest.java | 10 +- 6 files changed, 199 insertions(+), 113 deletions(-) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java index 4cc5eba23..c508c59c6 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java @@ -27,10 +27,8 @@ import java.util.Map; import org.springframework.util.Assert; import lombok.Builder; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Singular; -import lombok.ToString; /** * Class for gathering and reporting statistics about a block of execution. @@ -43,13 +41,11 @@ import lombok.ToString; * @author Marcin Grzejszczak */ /* - OpenTracing spans can affect the trace tree by creating children. In this way, - they are like scoped tracers. Sleuth spans are DTOs, whose sole responsibility - is the current span in the trace tree. -*/ + * OpenTracing spans can affect the trace tree by creating children. In this way, they are + * like scoped tracers. Sleuth spans are DTOs, whose sole responsibility is the current + * span in the trace tree. + */ @Builder(toBuilder = true) -@ToString -@EqualsAndHashCode @Getter public class Span { @@ -59,9 +55,8 @@ public class Span { public static final String TRACE_ID_NAME = "X-Trace-Id"; public static final String SPAN_NAME_NAME = "X-Span-Name"; public static final String SPAN_ID_NAME = "X-Span-Id"; - public static final List HEADERS = Arrays - .asList(SPAN_ID_NAME, TRACE_ID_NAME, SPAN_NAME_NAME, PARENT_ID_NAME, - PROCESS_ID_NAME, NOT_SAMPLED_NAME); + public static final List HEADERS = Arrays.asList(SPAN_ID_NAME, TRACE_ID_NAME, + SPAN_NAME_NAME, PARENT_ID_NAME, PROCESS_ID_NAME, NOT_SAMPLED_NAME); public static final String SPAN_EXPORT_NAME = "X-Span-Export"; private final long begin; @@ -100,13 +95,14 @@ public class Span { public Span(long begin, long end, String name, long traceId, List parents, long spanId, boolean remote, boolean exportable, String processId) { - this(begin, end, name, traceId, parents, spanId, remote, exportable, processId, null); + this(begin, end, name, traceId, parents, spanId, remote, exportable, processId, + null); } public Span(long begin, long end, String name, long traceId, List parents, long spanId, boolean remote, boolean exportable, String processId, Span savedSpan) { - this.begin = begin<=0 ? System.currentTimeMillis() : begin; + this.begin = begin <= 0 ? System.currentTimeMillis() : begin; this.end = end; this.name = name; this.traceId = traceId; @@ -118,7 +114,7 @@ public class Span { this.savedSpan = savedSpan; } - //for serialization + // for serialization private Span() { this.begin = 0; this.name = null; @@ -135,8 +131,8 @@ public class Span { public synchronized void stop() { if (this.end == 0) { if (this.begin == 0) { - throw new IllegalStateException("Span for " + this.name - + " has not been started"); + throw new IllegalStateException( + "Span for " + this.name + " has not been started"); } this.end = System.currentTimeMillis(); } @@ -300,4 +296,34 @@ public class Span { Assert.hasText(hexString, "Can't convert empty hex string to long"); return new BigInteger(hexString, 16).longValue(); } + + @Override + public String toString() { + return "[Trace: " + toHex(this.traceId) + ", Span: " + toHex(this.spanId) + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (this.spanId ^ (this.spanId >>> 32)); + result = prime * result + (int) (this.traceId ^ (this.traceId >>> 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; + Span other = (Span) obj; + if (this.spanId != other.spanId) + return false; + if (this.traceId != other.traceId) + return false; + return true; + } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java index d1dd8c3eb..fc891aaba 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java @@ -15,6 +15,20 @@ */ package org.springframework.cloud.sleuth.instrument.web; +import static org.springframework.util.StringUtils.hasText; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Random; +import java.util.regex.Pattern; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Span.SpanBuilder; import org.springframework.cloud.sleuth.Tracer; @@ -32,27 +46,15 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UrlPathHelper; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Random; -import java.util.regex.Pattern; - -import static org.springframework.util.StringUtils.hasText; - /** * Filter that takes the value of the {@link Span#SPAN_ID_NAME} and * {@link Span#TRACE_ID_NAME} header from either request or response and uses them to * create a new span. * - *

In order to keep the size of spans manageable, this only add tags defined in {@link TraceKeys}. - * If you need to add additional tags, such as headers subtype this and override - * {@link #addRequestTags} or {@link #addResponseTags}. + *

+ * In order to keep the size of spans manageable, this only add tags defined in + * {@link TraceKeys}. If you need to add additional tags, such as headers subtype this and + * override {@link #addRequestTags} or {@link #addResponseTags}. * * @see Tracer * @see TraceKeys @@ -82,12 +84,12 @@ public class TraceFilter extends OncePerRequestFilter private UrlPathHelper urlPathHelper = new UrlPathHelper(); private ApplicationEventPublisher publisher; - public TraceFilter(Tracer tracer, TraceKeys traceKeys) { this(tracer, traceKeys, DEFAULT_SKIP_PATTERN, new Random()); } - public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern, Random random) { + public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern, + Random random) { this.tracer = tracer; this.traceKeys = traceKeys; this.skipPattern = skipPattern; @@ -118,47 +120,54 @@ public class TraceFilter extends OncePerRequestFilter } String name = "http" + uri; - if (hasHeader(request, response, Span.TRACE_ID_NAME)) { - long traceId = Span.fromHex(getHeader(request, response, Span.TRACE_ID_NAME)); - long spanId = hasHeader(request, response, Span.SPAN_ID_NAME) ? - Span.fromHex(getHeader(request, response, Span.SPAN_ID_NAME)) : - this.random.nextLong(); + if (spanFromRequest == null) { + if (hasHeader(request, response, Span.TRACE_ID_NAME)) { + long traceId = Span + .fromHex(getHeader(request, response, Span.TRACE_ID_NAME)); + long spanId = hasHeader(request, response, Span.SPAN_ID_NAME) + ? Span.fromHex(getHeader(request, response, Span.SPAN_ID_NAME)) + : this.random.nextLong(); - SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId); - if (skip) { - span.exportable(false); - } - String processId = getHeader(request, response, Span.PROCESS_ID_NAME); - String parentName = getHeader(request, response, Span.SPAN_NAME_NAME); - if (StringUtils.hasText(parentName)) { - span.name(parentName); - } else { - span.name("parent/" + name); - } - if (StringUtils.hasText(processId)) { - span.processId(processId); - } - if (hasHeader(request, response, Span.PARENT_ID_NAME)) { - span.parent( - Span.fromHex(getHeader(request, response, Span.PARENT_ID_NAME))); - } - span.remote(true); + SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId); + if (skip) { + span.exportable(false); + } + String processId = getHeader(request, response, Span.PROCESS_ID_NAME); + String parentName = getHeader(request, response, Span.SPAN_NAME_NAME); + if (StringUtils.hasText(parentName)) { + span.name(parentName); + } + else { + span.name("parent/" + name); + } + if (StringUtils.hasText(processId)) { + span.processId(processId); + } + if (hasHeader(request, response, Span.PARENT_ID_NAME)) { + span.parent(Span + .fromHex(getHeader(request, response, Span.PARENT_ID_NAME))); + } + span.remote(true); - Span parent = span.build(); - spanFromRequest = this.tracer.joinTrace(name, parent); - publish(new ServerReceivedEvent(this, parent, spanFromRequest)); - request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); + Span parent = span.build(); + spanFromRequest = this.tracer.joinTrace(name, parent); + publish(new ServerReceivedEvent(this, parent, spanFromRequest)); + request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); - } - else { - if (skip) { - spanFromRequest = this.tracer.startTrace(name, IsTracingSampler.INSTANCE - ); } else { - spanFromRequest = this.tracer.startTrace(name); + if (skip) { + spanFromRequest = this.tracer.startTrace(name, + IsTracingSampler.INSTANCE); + } + else { + spanFromRequest = this.tracer.startTrace(name); + } + request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); } - request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); + } + else { + this.tracer.continueSpan(spanFromRequest); } Throwable exception = null; @@ -174,6 +183,7 @@ public class TraceFilter extends OncePerRequestFilter } finally { if (isAsyncStarted(request) || request.isAsyncStarted()) { + this.tracer.detach(spanFromRequest); // TODO: how to deal with response annotations and async? return; } @@ -210,11 +220,9 @@ public class TraceFilter extends OncePerRequestFilter protected void addRequestTags(HttpServletRequest request) { String uri = this.urlPathHelper.getPathWithinApplication(request); this.tracer.addTag(this.traceKeys.getHttp().getUrl(), getFullUrl(request)); - this.tracer.addTag(this.traceKeys.getHttp().getHost(), - request.getServerName()); + this.tracer.addTag(this.traceKeys.getHttp().getHost(), request.getServerName()); this.tracer.addTag(this.traceKeys.getHttp().getPath(), uri); - this.tracer.addTag(this.traceKeys.getHttp().getMethod(), - request.getMethod()); + this.tracer.addTag(this.traceKeys.getHttp().getMethod(), request.getMethod()); for (String name : this.traceKeys.getHttp().getHeaders()) { Enumeration values = request.getHeaders(name); if (values.hasMoreElements()) { @@ -236,7 +244,7 @@ public class TraceFilter extends OncePerRequestFilter this.tracer.addTag(this.traceKeys.getHttp().getStatusCode(), String.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); } - else if ((httpStatus < 200) || (httpStatus > 299)){ + else if ((httpStatus < 200) || (httpStatus > 299)) { this.tracer.addTag(this.traceKeys.getHttp().getStatusCode(), String.valueOf(response.getStatus())); } @@ -272,7 +280,8 @@ public class TraceFilter extends OncePerRequestFilter if (queryString == null) { return requestURI.toString(); - } else { + } + else { return requestURI.append('?').append(queryString).toString(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java index dff8b1462..d549f74f5 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java @@ -16,6 +16,11 @@ package org.springframework.cloud.sleuth.trace; +import static org.springframework.cloud.sleuth.util.ExceptionUtils.warn; + +import java.util.Random; +import java.util.concurrent.Callable; + import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; @@ -27,11 +32,6 @@ import org.springframework.cloud.sleuth.instrument.TraceRunnable; import org.springframework.cloud.sleuth.util.ExceptionUtils; import org.springframework.context.ApplicationEventPublisher; -import java.util.Random; -import java.util.concurrent.Callable; - -import static org.springframework.cloud.sleuth.util.ExceptionUtils.warn; - /** * @author Spencer Gibb */ @@ -43,8 +43,8 @@ public class DefaultTracer implements Tracer { private final Random random; - public DefaultTracer(Sampler defaultSampler, - Random random, ApplicationEventPublisher publisher) { + public DefaultTracer(Sampler defaultSampler, Random random, + ApplicationEventPublisher publisher) { this.defaultSampler = defaultSampler; this.random = random; this.publisher = publisher; @@ -57,8 +57,7 @@ public class DefaultTracer implements Tracer { } Span currentSpan = getCurrentSpan(); if (currentSpan != null && !parent.equals(currentSpan)) { - warn("Warn during joining trace: thread " + Thread.currentThread().getName() - + " tried to start a new Span " + "with parent " + parent.toString() + warn("Tried to start a new Span with parent " + parent + ", but there is already a " + "currentSpan " + currentSpan); } return continueSpan(createChild(parent, name)); @@ -78,8 +77,8 @@ public class DefaultTracer implements Tracer { else { // Non-exportable so we keep the trace but not other data long id = createId(); - span = Span.builder().begin(System.currentTimeMillis()).name(name) - .traceId(id).spanId(id).exportable(false).build(); + span = Span.builder().begin(System.currentTimeMillis()).name(name).traceId(id) + .spanId(id).exportable(false).build(); this.publisher.publishEvent(new SpanAcquiredEvent(this, span)); } return continueSpan(span); @@ -91,11 +90,10 @@ public class DefaultTracer implements Tracer { return null; } Span cur = SpanContextHolder.getCurrentSpan(); - if (cur != span) { + if (!span.equals(cur)) { ExceptionUtils.warn("Tried to detach trace span but " - + "it is not the current span for the '" - + Thread.currentThread().getName() + "' thread: " + span - + ". You have " + "probably forgotten to close or detach " + cur); + + "it is not the current span: " + span + + ". You may have forgotten to close or detach " + cur); } else { if (span.hasSavedSpan()) { @@ -115,11 +113,10 @@ public class DefaultTracer implements Tracer { } Span cur = SpanContextHolder.getCurrentSpan(); Span savedSpan = span.getSavedSpan(); - if (cur != span) { - ExceptionUtils.warn("Tried to close trace span but " - + "it is not the current span for the '" - + Thread.currentThread().getName() + "' thread" + span - + ". You have " + "probably forgotten to close or detach " + cur); + if (!span.equals(cur)) { + ExceptionUtils.warn( + "Tried to close span but " + "it is not the current span: " + span + + ". You may have forgotten to close or detach " + cur); } else { span.stop(); @@ -140,8 +137,8 @@ public class DefaultTracer implements Tracer { protected Span createChild(Span parent, String name) { long id = createId(); if (parent == null) { - Span span = Span.builder().begin(System.currentTimeMillis()) - .name(name).traceId(id).spanId(id).build(); + Span span = Span.builder().begin(System.currentTimeMillis()).name(name) + .traceId(id).spanId(id).build(); this.publisher.publishEvent(new SpanAcquiredEvent(this, span)); return span; } @@ -150,9 +147,9 @@ public class DefaultTracer implements Tracer { Span span = createSpan(null, parent); SpanContextHolder.setCurrentSpan(span); } - Span span = Span.builder().begin(System.currentTimeMillis()) - .name(name).traceId(parent.getTraceId()).parent(parent.getSpanId()) - .spanId(id).processId(parent.getProcessId()).build(); + Span span = Span.builder().begin(System.currentTimeMillis()).name(name) + .traceId(parent.getTraceId()).parent(parent.getSpanId()).spanId(id) + .processId(parent.getProcessId()).build(); this.publisher.publishEvent(new SpanAcquiredEvent(this, parent, span)); return span; } @@ -173,6 +170,9 @@ public class DefaultTracer implements Tracer { } protected Span createSpan(Span saved, Span span) { + if (saved == null && span.getSavedSpan() != null) { + saved = span.getSavedSpan(); + } return new Span(span, saved); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ExceptionUtils.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ExceptionUtils.java index bee833db0..7e44e9776 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ExceptionUtils.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ExceptionUtils.java @@ -23,7 +23,14 @@ import lombok.extern.apachecommons.CommonsLog; */ @CommonsLog public abstract class ExceptionUtils { + private static boolean fail = false; public static void warn(String msg) { + if (fail) { + throw new IllegalStateException(msg); + } log.warn(msg); } + public static void setFail(boolean fail) { + ExceptionUtils.fail = fail; + } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java index 69157d80c..8d0db6b5c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java @@ -1,5 +1,14 @@ package org.springframework.cloud.sleuth.instrument.web; +import static org.assertj.core.api.BDDAssertions.then; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.Random; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -14,21 +23,34 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; - -import java.util.Random; - -import static org.assertj.core.api.BDDAssertions.then; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(TraceFilterIntegrationTests.class) @DefaultTestAutoConfiguration +@RestController public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { + private static Log logger = LogFactory.getLog(TraceFilterIntegrationTests.class); + @Autowired Tracer tracer; @Autowired TraceKeys traceKeys; + @RequestMapping("/ping") + public String ping() { + logger.info("ping"); + return "ping"; + } + + @RequestMapping("/future") + public CompletableFuture future() { + logger.info("future"); + return CompletableFuture.completedFuture("ping"); + } + @Test public void should_create_and_return_trace_in_HTTP_header() throws Exception { MvcResult mvcResult = whenSentPingWithoutTracingData(); @@ -46,6 +68,18 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { then(tracingHeaderFrom(mvcResult)).isEqualTo(expectedTraceId); } + @Test + public void when_correlationId_is_sent_to_async_endpoint_span_is_joined() + throws Exception { + Long expectedTraceId = new Random().nextLong(); + + MvcResult mvcResult = whenSentFutureWithTraceId(expectedTraceId); + mvcResult = this.mockMvc.perform(asyncDispatch(mvcResult)).andExpect(status().isOk()) + .andReturn(); + + then(tracingHeaderFrom(mvcResult)).isEqualTo(expectedTraceId); + } + @Override protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) { mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys)); @@ -57,16 +91,24 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest { .andReturn(); } - private MvcResult whenSentPingWithTraceId(Long passedTraceId) - throws Exception { + private MvcResult whenSentPingWithTraceId(Long passedTraceId) throws Exception { return sendPingWithTraceId(Span.TRACE_ID_NAME, passedTraceId); } - private MvcResult sendPingWithTraceId(String headerName, Long passedCorrelationId) + private MvcResult whenSentFutureWithTraceId(Long passedTraceId) throws Exception { + return sendPingWithTraceId("/future", Span.TRACE_ID_NAME, passedTraceId); + } + + private MvcResult sendPingWithTraceId(String headerName, Long correlationId) throws Exception { + return sendPingWithTraceId("/ping", headerName, correlationId); + } + + private MvcResult sendPingWithTraceId(String path, String headerName, + Long correlationId) throws Exception { return this.mockMvc - .perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN) - .header(headerName, Span.toHex(passedCorrelationId)) + .perform(MockMvcRequestBuilders.get(path).accept(MediaType.TEXT_PLAIN) + .header(headerName, Span.toHex(correlationId)) .header(Span.SPAN_ID_NAME, Span.toHex(new Random().nextLong()))) .andReturn(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java index 4ef085e78..169af540a 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java @@ -2,8 +2,9 @@ package org.springframework.cloud.sleuth.instrument.web.common; import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.WebIntegrationTest; +import org.springframework.cloud.sleuth.util.ExceptionUtils; import org.springframework.context.ApplicationContext; +import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -19,15 +20,16 @@ import org.springframework.web.context.WebApplicationContext; * * @author 4financeIT */ -@WebIntegrationTest(randomPort = true) +@WebAppConfiguration public abstract class AbstractMvcIntegrationTest { - @Autowired protected WebApplicationContext webApplicationContext; - @Autowired protected ApplicationContext applicationContext; + @Autowired + protected WebApplicationContext webApplicationContext; protected MockMvc mockMvc; @Before public void setup() { + ExceptionUtils.setFail(true); DefaultMockMvcBuilder mockMvcBuilder = MockMvcBuilders.webAppContextSetup(this.webApplicationContext); configureMockMvcBuilder(mockMvcBuilder); this.mockMvc = mockMvcBuilder.build();