001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.web.servlet; 020 021import org.slf4j.Logger; 022import org.slf4j.LoggerFactory; 023 024import javax.servlet.FilterChain; 025import javax.servlet.ServletException; 026import javax.servlet.ServletRequest; 027import javax.servlet.ServletResponse; 028import java.io.IOException; 029 030/** 031 * A Servlet Filter that enables AOP-style "around" advice for a ServletRequest via 032 * {@link #preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) preHandle}, 033 * {@link #postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) postHandle}, 034 * and {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion} 035 * hooks. 036 * 037 * @since 0.9 038 */ 039public abstract class AdviceFilter extends OncePerRequestFilter { 040 041 /** 042 * The static logger available to this class only 043 */ 044 private static final Logger log = LoggerFactory.getLogger(AdviceFilter.class); 045 046 /** 047 * Returns {@code true} if the filter chain should be allowed to continue, {@code false} otherwise. 048 * It is called before the chain is actually consulted/executed. 049 * <p/> 050 * The default implementation returns {@code true} always and exists as a template method for subclasses. 051 * 052 * @param request the incoming ServletRequest 053 * @param response the outgoing ServletResponse 054 * @return {@code true} if the filter chain should be allowed to continue, {@code false} otherwise. 055 * @throws Exception if there is any error. 056 */ 057 protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { 058 return true; 059 } 060 061 /** 062 * Allows 'post' advice logic to be called, but only if no exception occurs during filter chain execution. That 063 * is, if {@link #executeChain executeChain} throws an exception, this method will never be called. Be aware of 064 * this when implementing logic. Most resource 'cleanup' behavior is often done in the 065 * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion(request,response,exception)} 066 * implementation, which is guaranteed to be called for every request, even when the chain processing throws 067 * an Exception. 068 * <p/> 069 * The default implementation does nothing (no-op) and exists as a template method for subclasses. 070 * 071 * @param request the incoming ServletRequest 072 * @param response the outgoing ServletResponse 073 * @throws Exception if an error occurs. 074 */ 075 @SuppressWarnings({"UnusedDeclaration"}) 076 protected void postHandle(ServletRequest request, ServletResponse response) throws Exception { 077 } 078 079 /** 080 * Called in all cases in a {@code finally} block even if {@link #preHandle preHandle} returns 081 * {@code false} or if an exception is thrown during filter chain processing. Can be used for resource 082 * cleanup if so desired. 083 * <p/> 084 * The default implementation does nothing (no-op) and exists as a template method for subclasses. 085 * 086 * @param request the incoming ServletRequest 087 * @param response the outgoing ServletResponse 088 * @param exception any exception thrown during {@link #preHandle preHandle}, {@link #executeChain executeChain}, 089 * or {@link #postHandle postHandle} execution, or {@code null} if no exception was thrown 090 * (i.e. the chain processed successfully). 091 * @throws Exception if an error occurs. 092 */ 093 @SuppressWarnings({"UnusedDeclaration"}) 094 public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception { 095 } 096 097 /** 098 * Actually executes the specified filter chain by calling <code>chain.doFilter(request,response);</code>. 099 * <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}