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}