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