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 LOGGER = 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) 066 * afterCompletion(request,response,exception)} 067 * implementation, which is guaranteed to be called for every request, even when the chain processing throws 068 * an Exception. 069 * <p/> 070 * The default implementation does nothing (no-op) and exists as a template method for subclasses. 071 * 072 * @param request the incoming ServletRequest 073 * @param response the outgoing ServletResponse 074 * @throws Exception if an error occurs. 075 */ 076 @SuppressWarnings({"UnusedDeclaration"}) 077 protected void postHandle(ServletRequest request, ServletResponse response) throws Exception { 078 } 079 080 /** 081 * Called in all cases in a {@code finally} block even if {@link #preHandle preHandle} returns 082 * {@code false} or if an exception is thrown during filter chain processing. Can be used for resource 083 * cleanup if so desired. 084 * <p/> 085 * The default implementation does nothing (no-op) and exists as a template method for subclasses. 086 * 087 * @param request the incoming ServletRequest 088 * @param response the outgoing ServletResponse 089 * @param exception any exception thrown during {@link #preHandle preHandle}, {@link #executeChain executeChain}, 090 * or {@link #postHandle postHandle} execution, or {@code null} if no exception was thrown 091 * (i.e. the chain processed successfully). 092 * @throws Exception if an error occurs. 093 */ 094 @SuppressWarnings({"UnusedDeclaration"}) 095 public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception { 096 } 097 098 /** 099 * Actually executes the specified filter chain by calling <code>chain.doFilter(request,response);</code>. 100 * <p/> 101 * Can be overridden by subclasses for custom logic. 102 * 103 * @param request the incoming ServletRequest 104 * @param response the outgoing ServletResponse 105 * @param chain the filter chain to execute 106 * @throws Exception if there is any error executing the chain. 107 */ 108 protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception { 109 chain.doFilter(request, response); 110 } 111 112 /** 113 * Actually implements the chain execution logic, utilizing 114 * {@link #preHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) pre}, 115 * {@link #postHandle(javax.servlet.ServletRequest, javax.servlet.ServletResponse) post}, and 116 * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) after} 117 * advice hooks. 118 * 119 * @param request the incoming ServletRequest 120 * @param response the outgoing ServletResponse 121 * @param chain the filter chain to execute 122 * @throws ServletException if a servlet-related error occurs 123 * @throws IOException if an IO error occurs 124 */ 125 public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) 126 throws ServletException, IOException { 127 128 Exception exception = null; 129 130 try { 131 132 boolean continueChain = preHandle(request, response); 133 if (LOGGER.isTraceEnabled()) { 134 LOGGER.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]"); 135 } 136 137 if (continueChain) { 138 executeChain(request, response, chain); 139 } 140 141 postHandle(request, response); 142 if (LOGGER.isTraceEnabled()) { 143 LOGGER.trace("Successfully invoked postHandle method"); 144 } 145 146 } catch (Exception e) { 147 exception = e; 148 } finally { 149 cleanup(request, response, exception); 150 } 151 } 152 153 /** 154 * Executes cleanup logic in the {@code finally} code block in the 155 * {@link #doFilterInternal(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) 156 * doFilterInternal(request, response, filterChain)} 157 * implementation. 158 * <p/> 159 * This implementation specifically calls 160 * {@link #afterCompletion(javax.servlet.ServletRequest, javax.servlet.ServletResponse, Exception) afterCompletion} 161 * as well as handles any exceptions properly. 162 * 163 * @param request the incoming {@code ServletRequest} 164 * @param response the outgoing {@code ServletResponse} 165 * @param existing any exception that might have occurred while executing the {@code FilterChain} or 166 * pre or post advice, or {@code null} if the pre/chain/post execution did not throw an {@code Exception}. 167 * @throws ServletException if any exception other than an {@code IOException} is thrown. 168 * @throws IOException if the pre/chain/post execution throw an {@code IOException} 169 */ 170 protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) 171 throws ServletException, IOException { 172 Exception exception = existing; 173 try { 174 afterCompletion(request, response, exception); 175 if (LOGGER.isTraceEnabled()) { 176 LOGGER.trace("Successfully invoked afterCompletion method."); 177 } 178 } catch (Exception e) { 179 if (exception == null) { 180 exception = e; 181 } else { 182 LOGGER.debug("afterCompletion implementation threw an exception. This will be ignored to " 183 + "allow the original source exception to be propagated.", e); 184 } 185 } 186 if (exception != null) { 187 if (exception instanceof ServletException) { 188 throw (ServletException) exception; 189 } else if (exception instanceof IOException) { 190 throw (IOException) exception; 191 } else { 192 if (LOGGER.isDebugEnabled()) { 193 String msg = "Filter execution resulted in an unexpected Exception " 194 + "(not IOException or ServletException as the Filter API recommends). " 195 + "Wrapping in ServletException and propagating."; 196 LOGGER.debug(msg); 197 } 198 throw new ServletException(exception); 199 } 200 } 201 } 202}