public void closeChannelDueToUnrecoverableError(Throwable cause) { try { // Ignore subsequent calls to this method, and only try to do something if the call is still active. // If the call is *not* active, then everything has already been cleaned up and we shouldn't // do anything because the channel might have already been handed out for a different call. if (!channelClosedDueToUnrecoverableError && callActiveHolder.heldObject) { // Schedule the close on the channel's event loop. channel.eventLoop().execute(() -> doCloseChannelDueToUnrecoverableError(cause)); return; } if (!alreadyLoggedMessageAboutIgnoringCloseDueToError && logger.isDebugEnabled()) { runnableWithTracingAndMdc( () -> logger.debug( "Ignoring calls to StreamingChannel.closeChannelDueToUnrecoverableError() because it " + "has already been called, or the call is no longer active. " + "previously_called={}, call_is_active={}", channelClosedDueToUnrecoverableError, callActiveHolder.heldObject ), distributedTracingSpanStack, distributedTracingMdcInfo ).run(); } alreadyLoggedMessageAboutIgnoringCloseDueToError = true; } finally { channelClosedDueToUnrecoverableError = true; } }
public void closeChannelDueToUnrecoverableError(Throwable cause) { try { // Ignore subsequent calls to this method, and only try to do something if the call is still active. // If the call is *not* active, then everything has already been cleaned up and we shouldn't // do anything because the channel might have already been handed out for a different call. if (!channelClosedDueToUnrecoverableError && callActiveHolder.heldObject) { // Schedule the close on the channel's event loop. channel.eventLoop().execute(() -> doCloseChannelDueToUnrecoverableError(cause)); return; } if (!alreadyLoggedMessageAboutIgnoringCloseDueToError && logger.isDebugEnabled()) { runnableWithTracingAndMdc( () -> logger.debug( "Ignoring calls to StreamingChannel.closeChannelDueToUnrecoverableError() because it " + "has already been called, or the call is no longer active. " + "previously_called={}, call_is_active={}", channelClosedDueToUnrecoverableError, callActiveHolder.heldObject ), distributedTracingSpanStack, distributedTracingMdcInfo ).run(); } alreadyLoggedMessageAboutIgnoringCloseDueToError = true; } finally { channelClosedDueToUnrecoverableError = true; } }
@Test public void StreamingChannel_closeChannelDueToUnrecoverableError_calls_the_do_method_and_sets_field_to_true_when_not_closed_and_call_active() { // given Throwable unrecoverableError = new RuntimeException("kaboom"); streamingChannelSpy.channelClosedDueToUnrecoverableError = false; streamingChannelSpy.callActiveHolder.heldObject = true; // when streamingChannelSpy.closeChannelDueToUnrecoverableError(unrecoverableError); // then assertThat(streamingChannelSpy.channelClosedDueToUnrecoverableError).isTrue(); ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class); verify(eventLoopMock).execute(taskCaptor.capture()); Runnable task = taskCaptor.getValue(); assertThat(task).isNotNull(); // and given verify(streamingChannelSpy, never()).doCloseChannelDueToUnrecoverableError(any(Throwable.class)); // when task.run(); // then verify(streamingChannelSpy).doCloseChannelDueToUnrecoverableError(unrecoverableError); }
@DataProvider(value = { "true", "false" }) @Test public void StreamingChannel_doCloseChannelDueToUnrecoverableError_works_as_expected(boolean callActive) { // given streamingChannelSpy.callActiveHolder.heldObject = callActive; Throwable unrecoverableError = new RuntimeException("kaboom"); // when streamingChannelSpy.doCloseChannelDueToUnrecoverableError(unrecoverableError); // then if (callActive) { verify(channelIsBrokenAttrMock).set(true); verifyChannelReleasedBackToPool(streamingChannelSpy.callActiveHolder, channelPoolMock, channelMock); verify(channelMock).close(); } else { verify(channelIsBrokenAttrMock, never()).set(anyBoolean()); verify(channelMock, never()).close(); } }