001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.mvc.method.annotation; 018 019import java.util.concurrent.CompletionException; 020import java.util.concurrent.CompletionStage; 021import java.util.function.BiFunction; 022 023import org.springframework.core.MethodParameter; 024import org.springframework.lang.Nullable; 025import org.springframework.util.concurrent.ListenableFuture; 026import org.springframework.util.concurrent.ListenableFutureCallback; 027import org.springframework.web.context.request.NativeWebRequest; 028import org.springframework.web.context.request.async.DeferredResult; 029import org.springframework.web.context.request.async.WebAsyncUtils; 030import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 031import org.springframework.web.method.support.ModelAndViewContainer; 032 033/** 034 * Handler for return values of type {@link DeferredResult}, 035 * {@link ListenableFuture}, and {@link CompletionStage}. 036 * 037 * @author Rossen Stoyanchev 038 * @since 3.2 039 */ 040public class DeferredResultMethodReturnValueHandler implements HandlerMethodReturnValueHandler { 041 042 @Override 043 public boolean supportsReturnType(MethodParameter returnType) { 044 Class<?> type = returnType.getParameterType(); 045 return (DeferredResult.class.isAssignableFrom(type) || 046 ListenableFuture.class.isAssignableFrom(type) || 047 CompletionStage.class.isAssignableFrom(type)); 048 } 049 050 @Override 051 public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, 052 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { 053 054 if (returnValue == null) { 055 mavContainer.setRequestHandled(true); 056 return; 057 } 058 059 DeferredResult<?> result; 060 061 if (returnValue instanceof DeferredResult) { 062 result = (DeferredResult<?>) returnValue; 063 } 064 else if (returnValue instanceof ListenableFuture) { 065 result = adaptListenableFuture((ListenableFuture<?>) returnValue); 066 } 067 else if (returnValue instanceof CompletionStage) { 068 result = adaptCompletionStage((CompletionStage<?>) returnValue); 069 } 070 else { 071 // Should not happen... 072 throw new IllegalStateException("Unexpected return value type: " + returnValue); 073 } 074 075 WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer); 076 } 077 078 private DeferredResult<Object> adaptListenableFuture(ListenableFuture<?> future) { 079 DeferredResult<Object> result = new DeferredResult<>(); 080 future.addCallback(new ListenableFutureCallback<Object>() { 081 @Override 082 public void onSuccess(@Nullable Object value) { 083 result.setResult(value); 084 } 085 @Override 086 public void onFailure(Throwable ex) { 087 result.setErrorResult(ex); 088 } 089 }); 090 return result; 091 } 092 093 private DeferredResult<Object> adaptCompletionStage(CompletionStage<?> future) { 094 DeferredResult<Object> result = new DeferredResult<>(); 095 future.handle((BiFunction<Object, Throwable, Object>) (value, ex) -> { 096 if (ex != null) { 097 if (ex instanceof CompletionException && ex.getCause() != null) { 098 ex = ex.getCause(); 099 } 100 result.setErrorResult(ex); 101 } 102 else { 103 result.setResult(value); 104 } 105 return null; 106 }); 107 return result; 108 } 109 110}