001/* 002 * Copyright 2002-2018 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.context.request.async; 018 019import java.util.concurrent.Callable; 020 021import org.springframework.beans.factory.BeanFactory; 022import org.springframework.beans.factory.BeanFactoryAware; 023import org.springframework.core.task.AsyncTaskExecutor; 024import org.springframework.lang.Nullable; 025import org.springframework.util.Assert; 026import org.springframework.web.context.request.NativeWebRequest; 027 028/** 029 * Holder for a {@link Callable}, a timeout value, and a task executor. 030 * 031 * @author Rossen Stoyanchev 032 * @author Juergen Hoeller 033 * @since 3.2 034 * @param <V> the value type 035 */ 036public class WebAsyncTask<V> implements BeanFactoryAware { 037 038 private final Callable<V> callable; 039 040 private Long timeout; 041 042 private AsyncTaskExecutor executor; 043 044 private String executorName; 045 046 private BeanFactory beanFactory; 047 048 private Callable<V> timeoutCallback; 049 050 private Callable<V> errorCallback; 051 052 private Runnable completionCallback; 053 054 055 /** 056 * Create a {@code WebAsyncTask} wrapping the given {@link Callable}. 057 * @param callable the callable for concurrent handling 058 */ 059 public WebAsyncTask(Callable<V> callable) { 060 Assert.notNull(callable, "Callable must not be null"); 061 this.callable = callable; 062 } 063 064 /** 065 * Create a {@code WebAsyncTask} with a timeout value and a {@link Callable}. 066 * @param timeout a timeout value in milliseconds 067 * @param callable the callable for concurrent handling 068 */ 069 public WebAsyncTask(long timeout, Callable<V> callable) { 070 this(callable); 071 this.timeout = timeout; 072 } 073 074 /** 075 * Create a {@code WebAsyncTask} with a timeout value, an executor name, and a {@link Callable}. 076 * @param timeout the timeout value in milliseconds; ignored if {@code null} 077 * @param executorName the name of an executor bean to use 078 * @param callable the callable for concurrent handling 079 */ 080 public WebAsyncTask(@Nullable Long timeout, String executorName, Callable<V> callable) { 081 this(callable); 082 Assert.notNull(executorName, "Executor name must not be null"); 083 this.executorName = executorName; 084 this.timeout = timeout; 085 } 086 087 /** 088 * Create a {@code WebAsyncTask} with a timeout value, an executor instance, and a Callable. 089 * @param timeout the timeout value in milliseconds; ignored if {@code null} 090 * @param executor the executor to use 091 * @param callable the callable for concurrent handling 092 */ 093 public WebAsyncTask(@Nullable Long timeout, AsyncTaskExecutor executor, Callable<V> callable) { 094 this(callable); 095 Assert.notNull(executor, "Executor must not be null"); 096 this.executor = executor; 097 this.timeout = timeout; 098 } 099 100 101 /** 102 * Return the {@link Callable} to use for concurrent handling (never {@code null}). 103 */ 104 public Callable<?> getCallable() { 105 return this.callable; 106 } 107 108 /** 109 * Return the timeout value in milliseconds, or {@code null} if no timeout is set. 110 */ 111 @Nullable 112 public Long getTimeout() { 113 return this.timeout; 114 } 115 116 /** 117 * A {@link BeanFactory} to use for resolving an executor name. 118 * <p>This factory reference will automatically be set when 119 * {@code WebAsyncTask} is used within a Spring MVC controller. 120 */ 121 @Override 122 public void setBeanFactory(BeanFactory beanFactory) { 123 this.beanFactory = beanFactory; 124 } 125 126 /** 127 * Return the AsyncTaskExecutor to use for concurrent handling, 128 * or {@code null} if none specified. 129 */ 130 @Nullable 131 public AsyncTaskExecutor getExecutor() { 132 if (this.executor != null) { 133 return this.executor; 134 } 135 else if (this.executorName != null) { 136 Assert.state(this.beanFactory != null, "BeanFactory is required to look up an executor bean by name"); 137 return this.beanFactory.getBean(this.executorName, AsyncTaskExecutor.class); 138 } 139 else { 140 return null; 141 } 142 } 143 144 145 /** 146 * Register code to invoke when the async request times out. 147 * <p>This method is called from a container thread when an async request times 148 * out before the {@code Callable} has completed. The callback is executed in 149 * the same thread and therefore should return without blocking. It may return 150 * an alternative value to use, including an {@link Exception} or return 151 * {@link CallableProcessingInterceptor#RESULT_NONE RESULT_NONE}. 152 */ 153 public void onTimeout(Callable<V> callback) { 154 this.timeoutCallback = callback; 155 } 156 157 /** 158 * Register code to invoke for an error during async request processing. 159 * <p>This method is called from a container thread when an error occurred 160 * while processing an async request before the {@code Callable} has 161 * completed. The callback is executed in the same thread and therefore 162 * should return without blocking. It may return an alternative value to 163 * use, including an {@link Exception} or return 164 * {@link CallableProcessingInterceptor#RESULT_NONE RESULT_NONE}. 165 * @since 5.0 166 */ 167 public void onError(Callable<V> callback) { 168 this.errorCallback = callback; 169 } 170 171 /** 172 * Register code to invoke when the async request completes. 173 * <p>This method is called from a container thread when an async request 174 * completed for any reason, including timeout and network error. 175 */ 176 public void onCompletion(Runnable callback) { 177 this.completionCallback = callback; 178 } 179 180 CallableProcessingInterceptor getInterceptor() { 181 return new CallableProcessingInterceptor() { 182 @Override 183 public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception { 184 return (timeoutCallback != null ? timeoutCallback.call() : CallableProcessingInterceptor.RESULT_NONE); 185 } 186 @Override 187 public <T> Object handleError(NativeWebRequest request, Callable<T> task, Throwable t) throws Exception { 188 return (errorCallback != null ? errorCallback.call() : CallableProcessingInterceptor.RESULT_NONE); 189 } 190 @Override 191 public <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception { 192 if (completionCallback != null) { 193 completionCallback.run(); 194 } 195 } 196 }; 197 } 198 199}