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