1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.shiro.web.servlet; 20 21 import org.slf4j.Logger; 22 import org.slf4j.LoggerFactory; 23 24 import javax.servlet.FilterChain; 25 import javax.servlet.ServletException; 26 import javax.servlet.ServletRequest; 27 import javax.servlet.ServletResponse; 28 import java.io.IOException; 29 30 /** 31 * A Servlet Filter that enables AOP-style "around" advice for a ServletRequest via 32 * {@link #preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) preHandle}, 33 * {@link #postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) postHandle}, 34 * and {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion} 35 * hooks. 36 * 37 * @since 0.9 38 */ 39 public abstract class AdviceFilter extends OncePerRequestFilter { 40 41 /** 42 * The static logger available to this class only 43 */ 44 private static final Logger log = LoggerFactory.getLogger(AdviceFilter.class); 45 46 /** 47 * Returns {@code true} if the filter chain should be allowed to continue, {@code false} otherwise. 48 * It is called before the chain is actually consulted/executed. 49 * <p/> 50 * The default implementation returns {@code true} always and exists as a template method for subclasses. 51 * 52 * @param request the incoming ServletRequest 53 * @param response the outgoing ServletResponse 54 * @return {@code true} if the filter chain should be allowed to continue, {@code false} otherwise. 55 * @throws Exception if there is any error. 56 */ 57 protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 58 return true; 59 } 60 61 /** 62 * Allows 'post' advice logic to be called, but only if no exception occurs during filter chain execution. That 63 * is, if {@link #executeChain executeChain} throws an exception, this method will never be called. Be aware of 64 * this when implementing logic. Most resource 'cleanup' behavior is often done in the 65 * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion(request,response,exception)} 66 * implementation, which is guaranteed to be called for every request, even when the chain processing throws 67 * an Exception. 68 * <p/> 69 * The default implementation does nothing (no-op) and exists as a template method for subclasses. 70 * 71 * @param request the incoming ServletRequest 72 * @param response the outgoing ServletResponse 73 * @throws Exception if an error occurs. 74 */ 75 @SuppressWarnings({"UnusedDeclaration"}) 76 protected void postHandle(ServletRequest request, ServletResponse response) throws Exception { 77 } 78 79 /** 80 * Called in all cases in a {@code finally} block even if {@link #preHandle preHandle} returns 81 * {@code false} or if an exception is thrown during filter chain processing. Can be used for resource 82 * cleanup if so desired. 83 * <p/> 84 * The default implementation does nothing (no-op) and exists as a template method for subclasses. 85 * 86 * @param request the incoming ServletRequest 87 * @param response the outgoing ServletResponse 88 * @param exception any exception thrown during {@link #preHandle preHandle}, {@link #executeChain executeChain}, 89 * or {@link #postHandle postHandle} execution, or {@code null} if no exception was thrown 90 * (i.e. the chain processed successfully). 91 * @throws Exception if an error occurs. 92 */ 93 @SuppressWarnings({"UnusedDeclaration"}) 94 public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception { 95 } 96 97 /** 98 * Actually executes the specified filter chain by calling <code>chain.doFilter(request,response);</code>. 99 * <p/> 100 * Can be overridden by subclasses for custom logic. 101 * 102 * @param request the incoming ServletRequest 103 * @param response the outgoing ServletResponse 104 * @param chain the filter chain to execute 105 * @throws Exception if there is any error executing the chain. 106 */ 107 protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception { 108 chain.doFilter(request, response); 109 } 110 111 /** 112 * Actually implements the chain execution logic, utilizing 113 * {@link #preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) pre}, 114 * {@link #postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) post}, and 115 * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) after} 116 * advice hooks. 117 * 118 * @param request the incoming ServletRequest 119 * @param response the outgoing ServletResponse 120 * @param chain the filter chain to execute 121 * @throws ServletException if a servlet-related error occurs 122 * @throws IOException if an IO error occurs 123 */ 124 public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) 125 throws ServletException, IOException { 126 127 Exception exception = null; 128 129 try { 130 131 boolean continueChain = preHandle(request, response); 132 if (log.isTraceEnabled()) { 133 log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]"); 134 } 135 136 if (continueChain) { 137 executeChain(request, response, chain); 138 } 139 140 postHandle(request, response); 141 if (log.isTraceEnabled()) { 142 log.trace("Successfully invoked postHandle method"); 143 } 144 145 } catch (Exception e) { 146 exception = e; 147 } finally { 148 cleanup(request, response, exception); 149 } 150 } 151 152 /** 153 * Executes cleanup logic in the {@code finally} code block in the 154 * {@link #doFilterInternal(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) doFilterInternal} 155 * implementation. 156 * <p/> 157 * This implementation specifically calls 158 * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion} 159 * as well as handles any exceptions properly. 160 * 161 * @param request the incoming {@code ServletRequest} 162 * @param response the outgoing {@code ServletResponse} 163 * @param existing any exception that might have occurred while executing the {@code FilterChain} or 164 * pre or post advice, or {@code null} if the pre/chain/post execution did not throw an {@code Exception}. 165 * @throws ServletException if any exception other than an {@code IOException} is thrown. 166 * @throws IOException if the pre/chain/post execution throw an {@code IOException} 167 */ 168 protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) 169 throws ServletException, IOException { 170 Exception exception = existing; 171 try { 172 afterCompletion(request, response, exception); 173 if (log.isTraceEnabled()) { 174 log.trace("Successfully invoked afterCompletion method."); 175 } 176 } catch (Exception e) { 177 if (exception == null) { 178 exception = e; 179 } else { 180 log.debug("afterCompletion implementation threw an exception. This will be ignored to " + 181 "allow the original source exception to be propagated.", e); 182 } 183 } 184 if (exception != null) { 185 if (exception instanceof ServletException) { 186 throw (ServletException) exception; 187 } else if (exception instanceof IOException) { 188 throw (IOException) exception; 189 } else { 190 if (log.isDebugEnabled()) { 191 String msg = "Filter execution resulted in an unexpected Exception " + 192 "(not IOException or ServletException as the Filter API recommends). " + 193 "Wrapping in ServletException and propagating."; 194 log.debug(msg); 195 } 196 throw new ServletException(exception); 197 } 198 } 199 } 200 }