@Test public void testSetNullResponseSuccess() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setResponse(null); // fetch value assertEquals(null, v.get()); }
@Override public void setComplete() { if (!isTerminated()) { subject.onCompleted(); } }
/** * Set an ISE if a response is not yet received otherwise skip it * * @param e A pre-generated exception. If this is null an ISE will be created and returned * @param exceptionMessage The message for the ISE */ public Exception setExceptionIfResponseNotReceived(Exception e, String exceptionMessage) { Exception exception = e; if (!valueSet.get() && !isTerminated()) { if (e == null) { exception = new IllegalStateException(exceptionMessage); } setExceptionIfResponseNotReceived(exception); } // return any exception that was generated return exception; }
} else { CollapsedRequestSubject<ResponseType, RequestArgumentType> collapsedRequest = new CollapsedRequestSubject<ResponseType, RequestArgumentType>(arg, this); final CollapsedRequestSubject<ResponseType, RequestArgumentType> existing = (CollapsedRequestSubject<ResponseType, RequestArgumentType>) argumentMap.putIfAbsent(arg, collapsedRequest); boolean requestCachingEnabled = properties.requestCacheEnabled().get(); if (requestCachingEnabled) { return existing.toObservable(); } else { return Observable.error(new IllegalArgumentException("Duplicate argument in collapser batch : [" + arg + "] This is not supported. Please turn request-caching on for HystrixCollapser:" + commandCollapser.getCollapserKey().name() + " or prevent duplicates from making it into the batch!")); return collapsedRequest.toObservable();
@Test public void testSetExceptionAfterResponse() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setResponse("theResponse"); try { cr.setException(new RuntimeException("anException")); fail("expected IllegalState"); } catch (IllegalStateException e) { } assertEquals("theResponse", v.get()); }
@Test(expected = CancellationException.class) public void testSetExceptionAfterUnsubscribe() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> f = o.toBlocking().toFuture(); // cancel/unsubscribe f.cancel(true); try { cr.setException(new RuntimeException("anException")); } catch (IllegalStateException e) { fail("this should have done nothing as it was unsubscribed already"); } // expect CancellationException after cancelling f.get(); }
/** * This handles failed completions */ @Override public void call(Throwable e) { // handle Throwable in case anything is thrown so we don't block Observers waiting for onError/onCompleted Exception ee; if (e instanceof Exception) { ee = (Exception) e; } else { ee = new RuntimeException("Throwable caught while executing batch and mapping responses.", e); } logger.debug("Exception mapping responses to requests.", e); // if a failure occurs we want to pass that exception to all of the Futures that we've returned for (CollapsedRequest<ResponseType, RequestArgumentType> request : argumentMap.values()) { try { ((CollapsedRequestSubject<ResponseType, RequestArgumentType>) request).setExceptionIfResponseNotReceived(ee); } catch (IllegalStateException e2) { // if we have partial responses set in mapResponseToRequests // then we may get IllegalStateException as we loop over them // so we'll log but continue to the rest logger.error("Partial success of 'mapResponseToRequests' resulted in IllegalStateException while setting Exception. Continuing ... ", e2); } } }
@Test public void testSetResponseAfterException() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setException(new RuntimeException("anException")); try { cr.setResponse("theResponse"); fail("expected IllegalState"); } catch (IllegalStateException e) { } try { v.get(); fail("expected exception"); } catch (ExecutionException e) { assertEquals("anException", e.getCause().getMessage()); } }
@Test public void testSetException() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setException(new RuntimeException("anException")); // fetch value try { v.get(); fail("expected exception"); } catch (ExecutionException e) { assertEquals("anException", e.getCause().getMessage()); } }
} else { CollapsedRequestSubject<ResponseType, RequestArgumentType> collapsedRequest = new CollapsedRequestSubject<ResponseType, RequestArgumentType>(arg, this); final CollapsedRequestSubject<ResponseType, RequestArgumentType> existing = (CollapsedRequestSubject<ResponseType, RequestArgumentType>) argumentMap.putIfAbsent(arg, collapsedRequest); boolean requestCachingEnabled = properties.requestCacheEnabled().get(); if (requestCachingEnabled) { return existing.toObservable(); } else { return Observable.error(new IllegalArgumentException("Duplicate argument in collapser batch : [" + arg + "] This is not supported. Please turn request-caching on for HystrixCollapser:" + commandCollapser.getCollapserKey().name() + " or prevent duplicates from making it into the batch!")); return collapsedRequest.toObservable();
((CollapsedRequestSubject<ResponseType, RequestArgumentType>) request).setExceptionIfResponseNotReceived(new IllegalStateException("Requests not executed before shutdown.")); } catch (Exception e) { logger.debug("Failed to setException on CollapsedRequestFutureImpl instances.", e);
@Test public void testUnsubscribeAfterSetResponse() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setResponse("theResponse"); // unsubscribe after the value is sent v.cancel(true); // still get value as it was set before canceling assertEquals("theResponse", v.get()); } }
/** * Set an exception if a response is not yet received otherwise skip it * * @param e synthetic error to set on initial command when no actual response is available */ public void setExceptionIfResponseNotReceived(Exception e) { if (!valueSet.get() && !isTerminated()) { subject.onError(e); } }
/** * Set an ISE if a response is not yet received otherwise skip it * * @param e A pre-generated exception. If this is null an ISE will be created and returned * @param exceptionMessage The message for the ISE */ public Exception setExceptionIfResponseNotReceived(Exception e, String exceptionMessage) { Exception exception = e; if (!valueSet.get() && !isTerminated()) { if (e == null) { exception = new IllegalStateException(exceptionMessage); } setExceptionIfResponseNotReceived(exception); } // return any exception that was generated return exception; }
for (CollapsedRequest<ResponseType, RequestArgumentType> request : shardRequests) { try { e = ((CollapsedRequestSubject<ResponseType, RequestArgumentType>) request).setExceptionIfResponseNotReceived(e,"No response set by " + commandCollapser.getCollapserKey().name() + " 'mapResponseToRequests' implementation."); } catch (IllegalStateException e2) { logger.debug("Partial success of 'mapResponseToRequests' resulted in IllegalStateException while setting 'No response set' Exception. Continuing ... ", e2);
@Test public void testSetResponseSuccess() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setResponse("theResponse"); // fetch value assertEquals("theResponse", v.get()); }
/** * Emit a response that should be OnNexted to an Observer * @param response response to emit to initial command */ @Override public void emitResponse(T response) { if (!isTerminated()) { subject.onNext(response); valueSet.set(true); } else { throw new IllegalStateException("Response has already terminated so response can not be set : " + response); } }
/** * This handles failed completions */ @Override public void call(Throwable e) { // handle Throwable in case anything is thrown so we don't block Observers waiting for onError/onCompleted Exception ee; if (e instanceof Exception) { ee = (Exception) e; } else { ee = new RuntimeException("Throwable caught while executing batch and mapping responses.", e); } logger.debug("Exception mapping responses to requests.", e); // if a failure occurs we want to pass that exception to all of the Futures that we've returned for (CollapsedRequest<ResponseType, RequestArgumentType> request : argumentMap.values()) { try { ((CollapsedRequestSubject<ResponseType, RequestArgumentType>) request).setExceptionIfResponseNotReceived(ee); } catch (IllegalStateException e2) { // if we have partial responses set in mapResponseToRequests // then we may get IllegalStateException as we loop over them // so we'll log but continue to the rest logger.error("Partial success of 'mapResponseToRequests' resulted in IllegalStateException while setting Exception. Continuing ... ", e2); } } }
@Test public void testSetResponseDuplicate() throws InterruptedException, ExecutionException { CollapsedRequestSubject<String, String> cr = new CollapsedRequestSubject<String, String>("hello"); Observable<String> o = cr.toObservable(); Future<String> v = o.toBlocking().toFuture(); cr.setResponse("theResponse"); try { cr.setResponse("theResponse2"); fail("expected IllegalState"); } catch (IllegalStateException e) { } assertEquals("theResponse", v.get()); }
/** * When set any client thread blocking on get() will immediately be unblocked and receive the exception. * * @throws IllegalStateException * if called more than once or after setResponse. * @param e received exception that gets set on the initial command */ @Override public void setException(Exception e) { if (!isTerminated()) { subject.onError(e); } else { throw new IllegalStateException("Response has already terminated so exception can not be set", e); } }