@Test public void sendDuplicateBeforeHandlerInitialized() throws Exception { this.emitter.send("foo", MediaType.TEXT_PLAIN); this.emitter.send("foo", MediaType.TEXT_PLAIN); this.emitter.complete(); verifyNoMoreInteractions(this.handler); this.emitter.initialize(this.handler); verify(this.handler, times(2)).send("foo", MediaType.TEXT_PLAIN); verify(this.handler).complete(); verifyNoMoreInteractions(this.handler); }
@Test public void sendBeforeHandlerInitializedWithError() throws Exception { IllegalStateException ex = new IllegalStateException(); this.emitter.send("foo", MediaType.TEXT_PLAIN); this.emitter.send("bar", MediaType.TEXT_PLAIN); this.emitter.completeWithError(ex); verifyNoMoreInteractions(this.handler); this.emitter.initialize(this.handler); verify(this.handler).send("foo", MediaType.TEXT_PLAIN); verify(this.handler).send("bar", MediaType.TEXT_PLAIN); verify(this.handler).completeWithError(ex); verifyNoMoreInteractions(this.handler); }
@Override public final void onSubscribe(Subscription subscription) { this.subscription = subscription; this.emitter.onTimeout(() -> { if (logger.isTraceEnabled()) { logger.trace("Connection timeout for " + this.emitter); } terminate(); this.emitter.complete(); }); this.emitter.onError(this.emitter::completeWithError); subscription.request(1); }
/** * Write the given object to the response. * <p>If any exception occurs a dispatch is made back to the app server where * Spring MVC will pass the exception through its exception handling mechanism. * <p><strong>Note:</strong> if the send fails with an IOException, you do * not need to call {@link #completeWithError(Throwable)} in order to clean * up. Instead the Servlet container creates a notification that results in a * dispatch where Spring MVC invokes exception resolvers and completes * processing. * @param object the object to write * @throws IOException raised when an I/O error occurs * @throws java.lang.IllegalStateException wraps any other errors */ public void send(Object object) throws IOException { send(object, null); }
logger.trace("Publisher for " + this.emitter + " failed: " + ex); this.emitter.completeWithError(ex); logger.trace("Publisher for " + this.emitter + " completed"); this.emitter.complete();
emitter.extendResponse(outputMessage); outputMessage = new StreamingServletServerHttpResponse(outputMessage); DeferredResult<?> deferredResult = new DeferredResult<>(emitter.getTimeout()); WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer); emitter.initialize(handler);
@Test public void responseBodyEmitter() throws Exception { MethodParameter type = on(TestController.class).resolveReturnType(ResponseBodyEmitter.class); ResponseBodyEmitter emitter = new ResponseBodyEmitter(); this.handler.handleReturnValue(emitter, type, this.mavContainer, this.webRequest); assertTrue(this.request.isAsyncStarted()); assertEquals("", this.response.getContentAsString()); SimpleBean bean = new SimpleBean(); bean.setId(1L); bean.setName("Joe"); emitter.send(bean); emitter.send("\n"); bean.setId(2L); bean.setName("John"); emitter.send(bean); emitter.send("\n"); bean.setId(3L); bean.setName("Jason"); emitter.send(bean); assertEquals("{\"id\":1,\"name\":\"Joe\"}\n" + "{\"id\":2,\"name\":\"John\"}\n" + "{\"id\":3,\"name\":\"Jason\"}", this.response.getContentAsString()); MockAsyncContext asyncContext = (MockAsyncContext) this.request.getAsyncContext(); assertNull(asyncContext.getDispatchedPath()); emitter.complete(); assertNotNull(asyncContext.getDispatchedPath()); }
@Test public void responseBodyEmitterWithTimeoutValue() throws Exception { AsyncWebRequest asyncWebRequest = mock(AsyncWebRequest.class); WebAsyncUtils.getAsyncManager(this.request).setAsyncWebRequest(asyncWebRequest); ResponseBodyEmitter emitter = new ResponseBodyEmitter(19000L); emitter.onTimeout(mock(Runnable.class)); emitter.onCompletion(mock(Runnable.class)); MethodParameter type = on(TestController.class).resolveReturnType(ResponseBodyEmitter.class); this.handler.handleReturnValue(emitter, type, this.mavContainer, this.webRequest); verify(asyncWebRequest).setTimeout(19000L); verify(asyncWebRequest).addTimeoutHandler(any(Runnable.class)); verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class)); verify(asyncWebRequest).startAsync(); }
@Test public void onTimeoutAfterHandlerInitialized() throws Exception { this.emitter.initialize(this.handler); ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); verify(this.handler).onTimeout(captor.capture()); verify(this.handler).onCompletion(any()); Runnable runnable = mock(Runnable.class); this.emitter.onTimeout(runnable); assertNotNull(captor.getValue()); captor.getValue().run(); verify(runnable).run(); }
@SuppressWarnings("unchecked") @Test public void responseBodyEmitterWithErrorValue() throws Exception { AsyncWebRequest asyncWebRequest = mock(AsyncWebRequest.class); WebAsyncUtils.getAsyncManager(this.request).setAsyncWebRequest(asyncWebRequest); ResponseBodyEmitter emitter = new ResponseBodyEmitter(19000L); emitter.onError(mock(Consumer.class)); emitter.onCompletion(mock(Runnable.class)); MethodParameter type = on(TestController.class).resolveReturnType(ResponseBodyEmitter.class); this.handler.handleReturnValue(emitter, type, this.mavContainer, this.webRequest); verify(asyncWebRequest).addErrorHandler(any(Consumer.class)); verify(asyncWebRequest, times(2)).addCompletionHandler(any(Runnable.class)); verify(asyncWebRequest).startAsync(); }
@Test public void sendWithError() throws Exception { this.emitter.initialize(this.handler); verify(this.handler).onTimeout(any()); verify(this.handler).onError(any()); verify(this.handler).onCompletion(any()); verifyNoMoreInteractions(this.handler); IOException failure = new IOException(); doThrow(failure).when(this.handler).send("foo", MediaType.TEXT_PLAIN); try { this.emitter.send("foo", MediaType.TEXT_PLAIN); fail("Expected exception"); } catch (IOException ex) { // expected } verify(this.handler).send("foo", MediaType.TEXT_PLAIN); verifyNoMoreInteractions(this.handler); }
@RequestMapping(value = "/{job:.*}/logStream", method = GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseBodyEmitter getJobLogStream(@PathVariable("job") String job) { JobInstance ji = jobsManager.getJob(job); ExtendedAssert.notFound(ji, "Job was not found by id: " + job); ResponseBodyEmitter emitter = new ResponseBodyEmitter(TimeUnit.MINUTES.toMillis(10L)); JobEventConsumer consumer = new JobEventConsumer(this.jobsManager, emitter, ji); ji.atEnd().addListener(() -> { // it need for job which finish before request emitter.complete(); }, ExecutorUtils.DIRECT); // TODO we may want to consume history, also. Subscription subs = jobsManager.getSubscriptions().openSubscriptionOnKey(consumer, ji.getInfo()); emitter.onCompletion(() -> { // Emitter not invoke this at client disconnect, // may be it will be fix in future versions subs.close(); }); return emitter; }
@Test public void onCompletionBeforeHandlerInitialized() throws Exception { Runnable runnable = mock(Runnable.class); this.emitter.onCompletion(runnable); this.emitter.initialize(this.handler); ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); verify(this.handler).onTimeout(any()); verify(this.handler).onCompletion(captor.capture()); assertNotNull(captor.getValue()); captor.getValue().run(); verify(runnable).run(); }
@Test public void writeStreamJson() throws Exception { this.servletRequest.addHeader("Accept", "application/stream+json"); EmitterProcessor<Bar> processor = EmitterProcessor.create(); ResponseBodyEmitter emitter = handleValue(processor, Flux.class, forClass(Bar.class)); EmitterHandler emitterHandler = new EmitterHandler(); emitter.initialize(emitterHandler); ServletServerHttpResponse message = new ServletServerHttpResponse(this.servletResponse); emitter.extendResponse(message); Bar bar1 = new Bar("foo"); Bar bar2 = new Bar("bar"); processor.onNext(bar1); processor.onNext(bar2); processor.onComplete(); assertEquals("application/stream+json", message.getHeaders().getContentType().toString()); assertEquals(Arrays.asList(bar1, "\n", bar2, "\n"), emitterHandler.getValues()); }
@Override public void onComplete() { if(!completed) { completed = true; responseBodyEmitter.complete(); } }
public ResponseBodyEmitterObserver(MediaType mediaType, Observable<T> observable, ResponseBodyEmitter responseBodyEmitter) { this.mediaType = mediaType; this.responseBodyEmitter = responseBodyEmitter; this.responseBodyEmitter.onTimeout(this); this.responseBodyEmitter.onCompletion(this); observable.subscribe(this); }
@Override protected void extendResponse(ServerHttpResponse outputMessage) { super.extendResponse(outputMessage); HttpHeaders headers = outputMessage.getHeaders(); if (headers.getContentType() == null) { headers.setContentType(UTF8_TEXT_EVENTSTREAM); } }
@Before public void setup() { MockitoAnnotations.initMocks(this); this.emitter = new ResponseBodyEmitter(); }
@Override public void onError(Throwable e) { responseBodyEmitter.completeWithError(e); }