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}