/** * Helper method for determining (and then caching) the {@link ServletRuntime} implementation appropriate for * the current Servlet runtime environment. If the current Servlet runtime environment supports the Servlet 3 API * (i.e. async requests) then a Servlet-3-async-request-capable implementation will be returned, otherwise a * Servlet-2-blocking-requests-only implementation will be returned. The first time this method is called the * result will be cached, and the cached value returned for subsequent calls. * * @param request The concrete {@link ServletRequest} implementation use to determine the Servlet runtime * environment. * * @return The {@link ServletRuntime} implementation appropriate for the current Servlet runtime environment. */ protected ServletRuntime getServletRuntime(ServletRequest request) { // It's ok that this isn't synchronized - this logic is idempotent and if it's executed a few extra times // due to concurrent requests when the service first starts up it won't hurt anything. if (servletRuntime == null) { servletRuntime = ServletRuntime.determineServletRuntime(request.getClass(), ASYNC_LISTENER_CLASSNAME); } return servletRuntime; }
/** * The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced in Servlet 3.0 means a filter can be * invoked in more than one thread over the course of a single request. This method should return {@code true} if * the filter is currently executing within an asynchronous dispatch. * * @param request the current request * * @deprecated This method is no longer used to determine whether this filter should execute, and will be removed * in a future update. It remains here only to prevent breaking impls that overrode the method. */ @Deprecated protected boolean isAsyncDispatch(HttpServletRequest request) { return getServletRuntime(request).isAsyncDispatch(request); }
/** * Returns the value of calling {@link ServletRuntime#isAsyncRequest(HttpServletRequest)} on the {@link * ServletRuntime} returned by {@link #getServletRuntime(ServletRequest)}. This method is here to allow * easy overriding by subclasses if needed, where {@link ServletRuntime} is not in scope. * * @param request The request to inspect to see if it's part of an async servlet request or not. * * @return the value of calling {@link ServletRuntime#isAsyncRequest(HttpServletRequest)} on the {@link * ServletRuntime} returned by {@link #getServletRuntime(ServletRequest)}. */ protected boolean isAsyncRequest(HttpServletRequest request) { return getServletRuntime(request).isAsyncRequest(request); }
/** * Delegates to {@link * ServletRuntime#setupTracingCompletionWhenAsyncRequestCompletes(HttpServletRequest, HttpServletResponse, * TracingState, HttpTagAndSpanNamingStrategy, HttpTagAndSpanNamingAdapter)}, with the {@link ServletRuntime} * retrieved via {@link #getServletRuntime(ServletRequest)}. This method is here to allow easy overriding by * subclasses if needed, where {@link ServletRuntime} is not in scope. * * @param asyncRequest The async servlet request (guaranteed to be async since this method will only be called when * {@link #isAsyncRequest(HttpServletRequest)} returns true). * @param asyncResponse The servlet response object - needed for span tagging. * @param originalRequestTracingState The {@link TracingState} that was generated when this request started, and * which should be completed when the given async servlet request finishes. * @param tagAndNamingStrategy The {@link HttpTagAndSpanNamingStrategy} that should be used for final span name * and tagging. * @param tagAndNamingAdapter The {@link HttpTagAndSpanNamingAdapter} that should be used by * {@code tagAndNamingStrategy} for final span name and tagging. */ protected void setupTracingCompletionWhenAsyncRequestCompletes( HttpServletRequest asyncRequest, HttpServletResponse asyncResponse, TracingState originalRequestTracingState, HttpTagAndSpanNamingStrategy<HttpServletRequest, HttpServletResponse> tagAndNamingStrategy, HttpTagAndSpanNamingAdapter<HttpServletRequest,HttpServletResponse> tagAndNamingAdapter ) { getServletRuntime(asyncRequest).setupTracingCompletionWhenAsyncRequestCompletes( asyncRequest, asyncResponse, originalRequestTracingState, tagAndNamingStrategy, tagAndNamingAdapter ); }
@Test public void setupTracingCompletionWhenAsyncRequestCompletes_delegates_to_ServletRuntime() { // given RequestTracingFilter filterSpy = spy(getBasicFilter()); doReturn(servletRuntimeMock).when(filterSpy).getServletRuntime(any(HttpServletRequest.class)); TracingState tracingStateMock = mock(TracingState.class); // when filterSpy.setupTracingCompletionWhenAsyncRequestCompletes( requestMock, responseMock, tracingStateMock, tagAndNamingStrategy, tagAndNamingAdapterMock ); // then verify(filterSpy).setupTracingCompletionWhenAsyncRequestCompletes( requestMock, responseMock, tracingStateMock, tagAndNamingStrategy, tagAndNamingAdapterMock ); verify(filterSpy).getServletRuntime(requestMock); verify(servletRuntimeMock).setupTracingCompletionWhenAsyncRequestCompletes( requestMock, responseMock, tracingStateMock, tagAndNamingStrategy, tagAndNamingAdapterMock ); verifyNoMoreInteractions(filterSpy, servletRuntimeMock, requestMock, tracingStateMock); }
@Test public void getServletRuntime_returns_value_of_ServletRuntime_determineServletRuntime_method_and_caches_result() { // given Class<? extends ServletRuntime> expectedServletRuntimeClass = ServletRuntime.determineServletRuntime(requestMock.getClass(), ASYNC_LISTENER_CLASSNAME).getClass(); RequestTracingFilter filter = getBasicFilter(); assertThat(filter.servletRuntime).isNull(); // when ServletRuntime result = filter.getServletRuntime(requestMock); // then assertThat(result.getClass()).isEqualTo(expectedServletRuntimeClass); assertThat(filter.servletRuntime).isSameAs(result); }
@DataProvider(value = { "true", "false" }) @Test @SuppressWarnings("deprecation") public void isAsyncDispatch_delegates_to_ServletRuntime(boolean servletRuntimeResult) { // given RequestTracingFilter filterSpy = spy(getBasicFilter()); doReturn(servletRuntimeMock).when(filterSpy).getServletRuntime(any(HttpServletRequest.class)); doReturn(servletRuntimeResult).when(servletRuntimeMock).isAsyncDispatch(any(HttpServletRequest.class)); // when boolean result = filterSpy.isAsyncDispatch(requestMock); // then assertThat(result).isEqualTo(servletRuntimeResult); verify(filterSpy).getServletRuntime(requestMock); verify(servletRuntimeMock).isAsyncDispatch(requestMock); }
@DataProvider(value = { "true", "false" }, splitBy = "\\|") @Test public void isAsyncRequest_delegates_to_ServletRuntime(boolean servletRuntimeResult) { // given RequestTracingFilter filterSpy = spy(getBasicFilter()); doReturn(servletRuntimeMock).when(filterSpy).getServletRuntime(any(HttpServletRequest.class)); doReturn(servletRuntimeResult).when(servletRuntimeMock).isAsyncRequest(any(HttpServletRequest.class)); // when boolean result = filterSpy.isAsyncRequest(requestMock); // then assertThat(result).isEqualTo(servletRuntimeResult); verify(filterSpy).getServletRuntime(requestMock); verify(servletRuntimeMock).isAsyncRequest(requestMock); }
@DataProvider(value = { "true | true | true", "true | false | false", "false | true | false", "false | false | false" }, splitBy = "\\|") @Test public void determineServletRuntime_returns_ServletRuntime_based_on_arguments( boolean classHasServlet3Method, boolean useAsyncListenerClassThatExists, boolean expectServlet3Runtime ) { // given Class<?> servletRequestClass = (classHasServlet3Method) ? GoodFakeServletRequest.class : BadFakeServletRequest.class; String asyncListenerClassname = (useAsyncListenerClassThatExists) ? AsyncListener.class.getName() : "does.not.exist.AsyncListener" + UUID.randomUUID().toString(); // when ServletRuntime result = ServletRuntime.determineServletRuntime(servletRequestClass, asyncListenerClassname); // then if (expectServlet3Runtime) { assertThat(result).isInstanceOf(Servlet3Runtime.class); } else { assertThat(result).isInstanceOf(Servlet2Runtime.class); } }