001/*
002 * Copyright 2002-2020 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;
018
019import java.util.ArrayList;
020import java.util.List;
021import javax.servlet.http.HttpServletRequest;
022import javax.servlet.http.HttpServletResponse;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026
027import org.springframework.util.CollectionUtils;
028import org.springframework.util.ObjectUtils;
029
030/**
031 * Handler execution chain, consisting of handler object and any handler interceptors.
032 * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
033 *
034 * @author Juergen Hoeller
035 * @since 20.06.2003
036 * @see HandlerInterceptor
037 */
038public class HandlerExecutionChain {
039
040        private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
041
042        private final Object handler;
043
044        private HandlerInterceptor[] interceptors;
045
046        private List<HandlerInterceptor> interceptorList;
047
048        private int interceptorIndex = -1;
049
050
051        /**
052         * Create a new HandlerExecutionChain.
053         * @param handler the handler object to execute
054         */
055        public HandlerExecutionChain(Object handler) {
056                this(handler, (HandlerInterceptor[]) null);
057        }
058
059        /**
060         * Create a new HandlerExecutionChain.
061         * @param handler the handler object to execute
062         * @param interceptors the array of interceptors to apply
063         * (in the given order) before the handler itself executes
064         */
065        public HandlerExecutionChain(Object handler, HandlerInterceptor... interceptors) {
066                if (handler instanceof HandlerExecutionChain) {
067                        HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
068                        this.handler = originalChain.getHandler();
069                        this.interceptorList = new ArrayList<HandlerInterceptor>();
070                        CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
071                        CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
072                }
073                else {
074                        this.handler = handler;
075                        this.interceptors = interceptors;
076                }
077        }
078
079
080        /**
081         * Return the handler object to execute.
082         * @return the handler object (may be {@code null})
083         */
084        public Object getHandler() {
085                return this.handler;
086        }
087
088        /**
089         * Add the given interceptor to the end of this chain.
090         */
091        public void addInterceptor(HandlerInterceptor interceptor) {
092                initInterceptorList().add(interceptor);
093        }
094
095        /**
096         * Add the given interceptors to the end of this chain.
097         */
098        public void addInterceptors(HandlerInterceptor... interceptors) {
099                if (!ObjectUtils.isEmpty(interceptors)) {
100                        CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
101                }
102        }
103
104        private List<HandlerInterceptor> initInterceptorList() {
105                if (this.interceptorList == null) {
106                        this.interceptorList = new ArrayList<HandlerInterceptor>();
107                        if (this.interceptors != null) {
108                                // An interceptor array specified through the constructor
109                                CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
110                        }
111                }
112                this.interceptors = null;
113                return this.interceptorList;
114        }
115
116        /**
117         * Return the array of interceptors to apply (in the given order).
118         * @return the array of HandlerInterceptors instances (may be {@code null})
119         */
120        public HandlerInterceptor[] getInterceptors() {
121                if (this.interceptors == null && this.interceptorList != null) {
122                        this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[this.interceptorList.size()]);
123                }
124                return this.interceptors;
125        }
126
127
128        /**
129         * Apply preHandle methods of registered interceptors.
130         * @return {@code true} if the execution chain should proceed with the
131         * next interceptor or the handler itself. Else, DispatcherServlet assumes
132         * that this interceptor has already dealt with the response itself.
133         */
134        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
135                HandlerInterceptor[] interceptors = getInterceptors();
136                if (!ObjectUtils.isEmpty(interceptors)) {
137                        for (int i = 0; i < interceptors.length; i++) {
138                                HandlerInterceptor interceptor = interceptors[i];
139                                if (!interceptor.preHandle(request, response, this.handler)) {
140                                        triggerAfterCompletion(request, response, null);
141                                        return false;
142                                }
143                                this.interceptorIndex = i;
144                        }
145                }
146                return true;
147        }
148
149        /**
150         * Apply postHandle methods of registered interceptors.
151         */
152        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
153                HandlerInterceptor[] interceptors = getInterceptors();
154                if (!ObjectUtils.isEmpty(interceptors)) {
155                        for (int i = interceptors.length - 1; i >= 0; i--) {
156                                HandlerInterceptor interceptor = interceptors[i];
157                                interceptor.postHandle(request, response, this.handler, mv);
158                        }
159                }
160        }
161
162        /**
163         * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
164         * Will just invoke afterCompletion for all interceptors whose preHandle invocation
165         * has successfully completed and returned true.
166         */
167        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
168                        throws Exception {
169
170                HandlerInterceptor[] interceptors = getInterceptors();
171                if (!ObjectUtils.isEmpty(interceptors)) {
172                        for (int i = this.interceptorIndex; i >= 0; i--) {
173                                HandlerInterceptor interceptor = interceptors[i];
174                                try {
175                                        interceptor.afterCompletion(request, response, this.handler, ex);
176                                }
177                                catch (Throwable ex2) {
178                                        logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
179                                }
180                        }
181                }
182        }
183
184        /**
185         * Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
186         */
187        void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
188                HandlerInterceptor[] interceptors = getInterceptors();
189                if (!ObjectUtils.isEmpty(interceptors)) {
190                        for (int i = interceptors.length - 1; i >= 0; i--) {
191                                HandlerInterceptor interceptor = interceptors[i];
192                                if (interceptor instanceof AsyncHandlerInterceptor) {
193                                        try {
194                                                AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptor;
195                                                asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
196                                        }
197                                        catch (Throwable ex) {
198                                                if (logger.isErrorEnabled()) {
199                                                        logger.error("Interceptor [" + interceptor + "] failed in afterConcurrentHandlingStarted", ex);
200                                                }
201                                        }
202                                }
203                        }
204                }
205        }
206
207
208        /**
209         * Delegates to the handler's {@code toString()} implementation.
210         */
211        @Override
212        public String toString() {
213                Object handler = getHandler();
214                if (handler == null) {
215                        return "HandlerExecutionChain with no handler";
216                }
217                StringBuilder sb = new StringBuilder();
218                sb.append("HandlerExecutionChain with handler [").append(handler).append("]");
219                HandlerInterceptor[] interceptors = getInterceptors();
220                if (!ObjectUtils.isEmpty(interceptors)) {
221                        sb.append(" and ").append(interceptors.length).append(" interceptor");
222                        if (interceptors.length > 1) {
223                                sb.append("s");
224                        }
225                }
226                return sb.toString();
227        }
228
229}