001/*
002 * Copyright 2002-2017 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.core.task.AsyncTaskExecutor;
022import org.springframework.web.context.request.NativeWebRequest;
023
024/**
025 * Intercepts concurrent request handling, where the concurrent result is
026 * obtained by executing a {@link Callable} on behalf of the application with
027 * an {@link AsyncTaskExecutor}.
028 *
029 * <p>A {@code CallableProcessingInterceptor} is invoked before and after the
030 * invocation of the {@code Callable} task in the asynchronous thread, as well
031 * as on timeout/error from a container thread, or after completing for any reason
032 * including a timeout or network error.
033 *
034 * <p>As a general rule exceptions raised by interceptor methods will cause
035 * async processing to resume by dispatching back to the container and using
036 * the Exception instance as the concurrent result. Such exceptions will then
037 * be processed through the {@code HandlerExceptionResolver} mechanism.
038 *
039 * <p>The {@link #handleTimeout(NativeWebRequest, Callable) handleTimeout} method
040 * can select a value to be used to resume processing.
041 *
042 * @author Rossen Stoyanchev
043 * @author Rob Winch
044 * @since 3.2
045 */
046public interface CallableProcessingInterceptor {
047
048        /**
049         * Constant indicating that no result has been determined by this
050         * interceptor, giving subsequent interceptors a chance.
051         * @see #handleTimeout
052         * @see #handleError
053         */
054        Object RESULT_NONE = new Object();
055
056        /**
057         * Constant indicating that the response has been handled by this interceptor
058         * without a result and that no further interceptors are to be invoked.
059         * @see #handleTimeout
060         * @see #handleError
061         */
062        Object RESPONSE_HANDLED = new Object();
063
064
065        /**
066         * Invoked <em>before</em> the start of concurrent handling in the original
067         * thread in which the {@code Callable} is submitted for concurrent handling.
068         * <p>This is useful for capturing the state of the current thread just prior to
069         * invoking the {@link Callable}. Once the state is captured, it can then be
070         * transferred to the new {@link Thread} in
071         * {@link #preProcess(NativeWebRequest, Callable)}. Capturing the state of
072         * Spring Security's SecurityContextHolder and migrating it to the new Thread
073         * is a concrete example of where this is useful.
074         * <p>The default implementation is empty.
075         * @param request the current request
076         * @param task the task for the current async request
077         * @throws Exception in case of errors
078         */
079        default <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) throws Exception {
080        }
081
082        /**
083         * Invoked <em>after</em> the start of concurrent handling in the async
084         * thread in which the {@code Callable} is executed and <em>before</em> the
085         * actual invocation of the {@code Callable}.
086         * <p>The default implementation is empty.
087         * @param request the current request
088         * @param task the task for the current async request
089         * @throws Exception in case of errors
090         */
091        default <T> void preProcess(NativeWebRequest request, Callable<T> task) throws Exception {
092        }
093
094        /**
095         * Invoked <em>after</em> the {@code Callable} has produced a result in the
096         * async thread in which the {@code Callable} is executed. This method may
097         * be invoked later than {@code afterTimeout} or {@code afterCompletion}
098         * depending on when the {@code Callable} finishes processing.
099         * <p>The default implementation is empty.
100         * @param request the current request
101         * @param task the task for the current async request
102         * @param concurrentResult the result of concurrent processing, which could
103         * be a {@link Throwable} if the {@code Callable} raised an exception
104         * @throws Exception in case of errors
105         */
106        default <T> void postProcess(NativeWebRequest request, Callable<T> task,
107                        Object concurrentResult) throws Exception {
108        }
109
110        /**
111         * Invoked from a container thread when the async request times out before
112         * the {@code Callable} task completes. Implementations may return a value,
113         * including an {@link Exception}, to use instead of the value the
114         * {@link Callable} did not return in time.
115         * <p>The default implementation always returns {@link #RESULT_NONE}.
116         * @param request the current request
117         * @param task the task for the current async request
118         * @return a concurrent result value; if the value is anything other than
119         * {@link #RESULT_NONE} or {@link #RESPONSE_HANDLED}, concurrent processing
120         * is resumed and subsequent interceptors are not invoked
121         * @throws Exception in case of errors
122         */
123        default <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception {
124                return RESULT_NONE;
125        }
126
127        /**
128         * Invoked from a container thread when an error occurred while processing
129         * the async request before the {@code Callable} task completes.
130         * Implementations may return a value, including an {@link Exception}, to
131         * use instead of the value the {@link Callable} did not return in time.
132         * <p>The default implementation always returns {@link #RESULT_NONE}.
133         * @param request the current request
134         * @param task the task for the current async request
135         * @param t the error that occurred while request processing
136         * @return a concurrent result value; if the value is anything other than
137         * {@link #RESULT_NONE} or {@link #RESPONSE_HANDLED}, concurrent processing
138         * is resumed and subsequent interceptors are not invoked
139         * @throws Exception in case of errors
140         * @since 5.0
141         */
142        default <T> Object handleError(NativeWebRequest request, Callable<T> task, Throwable t) throws Exception {
143                return RESULT_NONE;
144        }
145
146        /**
147         * Invoked from a container thread when async processing completes for any
148         * reason including timeout or network error.
149         * <p>The default implementation is empty.
150         * @param request the current request
151         * @param task the task for the current async request
152         * @throws Exception in case of errors
153         */
154        default <T> void afterCompletion(NativeWebRequest request, Callable<T> task) throws Exception {
155        }
156
157}