EndpointInvocationHandler implements EndpointRunnableFactory { void handle(OngoingRequest ongoingRequest, RequestContext requestContext, Endpoint endpoint) { try { endpoint.invoke(requestContext) .whenComplete((message, throwable) -> { try { if (message != null) { ongoingRequest.reply(message); } else if (throwable != null) { if (throwable instanceof CompletionException) { throwable = throwable.getCause(); } handleException(throwable, ongoingRequest); } else { LOG.error( "Both message and throwable null in EndpointInvocationHandler for request " + ongoingRequest + " - this shouldn't happen!"); handleException(new IllegalStateException("Both message and throwable null"), ongoingRequest); } } catch (Throwable t) { LOG.error("Exception caught when replying", t); } }); } catch (Exception e) { handleException(e, ongoingRequest); } }  @Override Runnable create(
      OngoingRequest ongoingRequest,
      RequestContext requestContext,
      Endpoint endpoint);  }
@Test public void shouldRespondWithResponseMessageForOk() throws Exception { when(endpoint.invoke(any(RequestContext.class))) .thenReturn(completedFuture(response)); handler.handle(ongoingRequest, requestContext, endpoint); verify(ongoingRequest).reply(response); }
@Test public void shouldRespondWith500ForGeneralException() throws Exception { RuntimeException exception = new RuntimeException("expected"); future.completeExceptionally(exception); when(endpoint.invoke(any(RequestContext.class))) .thenReturn(future); handler.handle(ongoingRequest, requestContext, endpoint); verify(ongoingRequest).reply(messageArgumentCaptor.capture()); assertThat(messageArgumentCaptor.getValue().status().code(), equalTo(INTERNAL_SERVER_ERROR.code())); }
@Test public void shouldRespondWithDetailMessageForExceptionsToNonClientCallers() throws Exception { RuntimeException exception = new RuntimeException("expected"); when(endpoint.invoke(any(RequestContext.class))) .thenReturn(future); future.completeExceptionally(exception); handler.handle(ongoingRequest, requestContext, endpoint); verify(ongoingRequest).reply(messageArgumentCaptor.capture()); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), containsString(exception.getMessage())); }
@Test public void shouldNotBlockOnFutures() throws Exception { when(endpoint.invoke(any(RequestContext.class))) .thenReturn(future); handler.handle(ongoingRequest, requestContext, endpoint); Thread.sleep(50); verifyZeroInteractions(ongoingRequest); future.complete(response); verify(ongoingRequest).reply(response); }
@Test public void shouldUnwrapMultiLineExceptions() throws Exception { RuntimeException exception = new RuntimeException("expected\nwith multiple\rlines"); future.completeExceptionally(exception); when(endpoint.invoke(any(RequestContext.class))) .thenReturn(future); handler.handle(ongoingRequest, requestContext, endpoint); verify(ongoingRequest).reply(messageArgumentCaptor.capture()); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), containsString("expected")); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), containsString("with multiple")); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), containsString("lines")); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), not(containsString("\r"))); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), not(containsString("\n"))); }
@Test public void shouldRespondWithDetailMessageForSyncExceptionsToNonClientCallers() throws Exception { RuntimeException exception = new RuntimeException("expected"); when(endpoint.invoke(any(RequestContext.class))) .thenThrow(exception); handler.handle(ongoingRequest, requestContext, endpoint); verify(ongoingRequest).reply(messageArgumentCaptor.capture()); assertThat(messageArgumentCaptor.getValue().status().reasonPhrase(), containsString(exception.getMessage())); }
@Test public void shouldLogExceptionsWhenReplying() throws Exception { when(endpoint.invoke(any(RequestContext.class))) .thenReturn(completedFuture(response)); doThrow(new RuntimeException("log this exception!")).when(ongoingRequest).reply(any(Response.class)); handler.handle(ongoingRequest, requestContext, endpoint); List<String> eventMessages = testLogger.getLoggingEvents().stream() .filter(loggingEvent -> loggingEvent.getLevel() == Level.ERROR) .map(loggingEvent -> MessageFormatter.arrayFormat(loggingEvent.getMessage(), loggingEvent.getArguments().toArray()).getMessage()) .collect(Collectors.toList()); assertThat(eventMessages, hasSize(1)); assertThat(eventMessages, hasItem(containsString("Exception caught when replying"))); }