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}