WingtipsRequestSpanCompletionAsyncListener implements AsyncListener { @SuppressWarnings("deprecation") protected void completeRequestSpan(AsyncEvent event) { if (alreadyCompleted.getAndSet(true)) { return; } ServletRequest request = event.getSuppliedRequest(); ServletResponse response = event.getSuppliedResponse(); final HttpServletRequest httpRequest = (request instanceof HttpServletRequest) ? (HttpServletRequest) request : null; final HttpServletResponse httpResponse = (response instanceof HttpServletResponse) ? (HttpServletResponse) response : null; final Throwable error = event.getThrowable(); runnableWithTracing( new Runnable() { @Override public void run() { Span span = Tracer.getInstance().getCurrentSpan(); try { tagAndNamingStrategy.handleResponseTaggingAndFinalSpanName( span, httpRequest, httpResponse, error, tagAndNamingAdapter ); } finally { Tracer.getInstance().completeRequestSpan(); } } }, originalRequestTracingState ).run(); } WingtipsRequestSpanCompletionAsyncListener(
        TracingState originalRequestTracingState,
        HttpTagAndSpanNamingStrategy<HttpServletRequest, HttpServletResponse> tagAndNamingStrategy,
        HttpTagAndSpanNamingAdapter<HttpServletRequest, HttpServletResponse> tagAndNamingAdapter
    ); @Override void onComplete(AsyncEvent event); @Override void onTimeout(AsyncEvent event); @Override void onError(AsyncEvent event); @Override void onStartAsync(AsyncEvent event);  }
@DataProvider(value = { "ALL_RESOURCES_EXIST", "REQUEST_IS_NULL", "REQUEST_IS_NOT_HTTP_SERVLET_REQUEST", "RESPONSE_IS_NULL", "RESPONSE_IS_NOT_HTTP_SERVLET_REQUEST", "ERROR_IS_NULL" }) @Test public void completeRequestSpan_completes_request_span_as_expected(TaggingResourceScenario scenario) { Tracer.getInstance().startRequestWithRootSpan("someOtherUnrelatedSpan"); TracingState unrelatedThreadTracingState = TracingState.getCurrentThreadTracingState(); SpanRecorder spanRecorder = new SpanRecorder(); Tracer.getInstance().addSpanLifecycleListener(spanRecorder); assertThat(tracingStateSpan.isCompleted()).isFalse(); assertThat(implSpy.alreadyCompleted.get()).isFalse(); doReturn(scenario.requestObj).when(asyncEventMock).getSuppliedRequest(); doReturn(scenario.responseObj).when(asyncEventMock).getSuppliedResponse(); doReturn(scenario.errorObj).when(asyncEventMock).getThrowable(); implSpy.completeRequestSpan(asyncEventMock); assertThat(spanRecorder.completedSpans).hasSize(1); Span completedSpan = spanRecorder.completedSpans.get(0); assertThat(completedSpan).isSameAs(tracingStateSpan); assertThat(tracingStateSpan.isCompleted()).isTrue(); assertThat(implSpy.alreadyCompleted.get()).isTrue(); assertThat(strategyResponseTaggingAndFinalSpanNameMethodCalled.get()).isTrue(); strategyResponseTaggingArgs.get().verifyArgs( completedSpan, scenario.expectedRequestObjForTagging, scenario.expectedResponseObjForTagging, scenario.errorObj, tagAndNamingAdapterMock ); assertThat(TracingState.getCurrentThreadTracingState()).isEqualTo(unrelatedThreadTracingState); }
@Test public void completeRequestSpan_marks_listener_as_completed_even_if_unexpected_exception_occurs() { Tracer.getInstance().startRequestWithRootSpan("someOtherUnrelatedSpan"); TracingState unrelatedThreadTracingState = TracingState.getCurrentThreadTracingState(); final RuntimeException expectedExplosion = new RuntimeException("kaboom"); SpanRecorder explodingSpanRecorder = new SpanRecorder() { @Override public void spanCompleted(Span span) { throw expectedExplosion; } }; Tracer.getInstance().addSpanLifecycleListener(explodingSpanRecorder); assertThat(implSpy.alreadyCompleted.get()).isFalse(); Throwable actualEx = catchThrowable(() -> implSpy.completeRequestSpan(asyncEventMock)); assertThat(actualEx).isSameAs(expectedExplosion); assertThat(explodingSpanRecorder.completedSpans).hasSize(0); assertThat(implSpy.alreadyCompleted.get()).isTrue(); assertThat(TracingState.getCurrentThreadTracingState()).isEqualTo(unrelatedThreadTracingState); }
@Test public void completeRequestSpan_does_nothing_if_listener_is_already_marked_completed() { implSpy.alreadyCompleted.set(true); assertThat(tracingStateSpan.isCompleted()).isFalse(); implSpy.completeRequestSpan(asyncEventMock); assertThat(tracingStateSpan.isCompleted()).isFalse(); assertThat(implSpy.alreadyCompleted.get()).isTrue(); }