View Javadoc

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 }