View Javadoc

1   /**
2    * This file Copyright (c) 2003-2013 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.filters;
35  
36  import info.magnolia.cms.util.CustomServletConfig;
37  import info.magnolia.objectfactory.Classes;
38  
39  import java.io.IOException;
40  import java.util.Map;
41  import java.util.regex.Matcher;
42  
43  import javax.inject.Inject;
44  import javax.servlet.FilterChain;
45  import javax.servlet.FilterConfig;
46  import javax.servlet.Servlet;
47  import javax.servlet.ServletException;
48  import javax.servlet.http.HttpServletRequest;
49  import javax.servlet.http.HttpServletRequestWrapper;
50  import javax.servlet.http.HttpServletResponse;
51  
52  import info.magnolia.objectfactory.ComponentProvider;
53  import org.apache.commons.lang.StringUtils;
54  import org.slf4j.Logger;
55  import org.slf4j.LoggerFactory;
56  
57  
58  /**
59   * A filter that dispatches requests to a wrapped servlet.
60   *
61   * TODO : cache matching URIs ?
62   *
63   * @author vsteller
64   * @version $Id$
65   */
66  public class ServletDispatchingFilter extends AbstractMgnlFilter {
67  
68      static final Logger log = LoggerFactory.getLogger(ServletDispatchingFilter.class);
69  
70      private final ComponentProvider componentProvider;
71  
72      private String servletName;
73  
74      private String servletClass;
75  
76      private Map parameters;
77  
78      private String comment;
79  
80      private Servlet servlet;
81  
82      @Inject
83      public ServletDispatchingFilter(ComponentProvider componentProvider) {
84          this.componentProvider = componentProvider;
85      }
86  
87      @Override
88      public String getName() {
89          return "Wrapper for " + servletName + " servlet";
90      }
91  
92      /**
93       * Initializes the servlet and its mappings. ServletConfig is wrapped to take init parameters into account.
94       */
95      @Override
96      public void init(final FilterConfig filterConfig) throws ServletException {
97          super.init(filterConfig);
98  
99          if (servletClass != null) {
100             try {
101                 servlet = componentProvider.newInstance(Classes.getClassFactory().<Servlet>forName(servletClass));
102                 servlet.init(new CustomServletConfig(servletName, filterConfig.getServletContext(), parameters));
103             }
104             catch (Throwable e) {
105                 log.error("Unable to load servlet " + servletClass + " : " + e.getMessage(), e);
106             }
107         }
108     }
109 
110     /**
111      * Delegates the destroy() call to the wrapper servlet, then to this filter itself.
112      */
113     @Override
114     public void destroy() {
115         if (servlet != null) {
116             servlet.destroy();
117         }
118         super.destroy();
119     }
120 
121     /**
122      * Dispatches the request to the servlet if not already bypassed. The request is wrapped for properly setting the
123      * pathInfo.
124      */
125     @Override
126     public void doFilter(final HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
127         log.debug("Dispatching to servlet {}", getServletClass());
128         final Matcher matcher = getMapping().match(request).getMatcher();
129         servlet.service(new WrappedRequest(request, matcher), response);
130     }
131 
132     public String getServletName() {
133         return servletName;
134     }
135 
136     public void setServletName(String servletName) {
137         this.servletName = servletName;
138     }
139 
140     public String getServletClass() {
141         return servletClass;
142     }
143 
144     public void setServletClass(String servletClass) {
145         this.servletClass = servletClass;
146     }
147 
148     public Map getParameters() {
149         return parameters;
150     }
151 
152     public void setParameters(Map parameters) {
153         this.parameters = parameters;
154     }
155 
156     public String getComment() {
157         return comment;
158     }
159 
160     public void setComment(String comment) {
161         this.comment = comment;
162     }
163 
164     /**
165      * Request wrapper that overrides servletPath and pathInfo with new values. If any of the path elements changes in
166      * a wrapper behind it then it returns them instead of the overridden values. This happens on forwards. It's
167      * necessary to check all four since they act as a group, if any of them changes we cannot override any of them.
168      */
169     private static class WrappedRequest extends HttpServletRequestWrapper {
170 
171         private String originalRequestUri;
172         private String originalServletPath;
173         private String originalPathInfo;
174         private String originalQueryString;
175 
176         private String newServletPath;
177         private String newPathInfo;
178 
179         /**
180          * The given Matcher should be built from a Pattern containing two groups:
181          * (1) servletPath (2) ignored (3) pathInfo.
182          */
183         public WrappedRequest(HttpServletRequest request, Matcher matcher) {
184             super(request);
185 
186             this.originalRequestUri = request.getRequestURI();
187             this.originalServletPath = request.getServletPath();
188             this.originalPathInfo = request.getPathInfo();
189             this.originalQueryString = request.getQueryString();
190 
191             this.newServletPath = matcher.group(1);
192             if (matcher.groupCount() > 2) {
193                 String pathInfo = matcher.group(3);
194                 // pathInfo should be null when empty
195                 if (!pathInfo.equals("")) {
196                     // according to the servlet spec the pathInfo should contain a leading slash
197                     this.newPathInfo = (pathInfo.startsWith("/") ? pathInfo : "/" + pathInfo);
198                 }
199             }
200         }
201 
202         @Override
203         public String getPathInfo() {
204             String current = super.getPathInfo();
205             if (!StringUtils.equals(super.getRequestURI(), originalRequestUri)) {
206                 return current;
207             }
208             if (!StringUtils.equals(super.getServletPath(), originalServletPath)) {
209                 return current;
210             }
211             if (!StringUtils.equals(current, originalPathInfo)) {
212                 return current;
213             }
214             if (!StringUtils.equals(super.getQueryString(), originalQueryString)) {
215                 return current;
216             }
217             return newPathInfo;
218         }
219 
220         @Override
221         public String getServletPath() {
222             String current = super.getServletPath();
223             if (!StringUtils.equals(super.getRequestURI(), originalRequestUri)) {
224                 return current;
225             }
226             if (!StringUtils.equals(current, originalServletPath)) {
227                 return current;
228             }
229             if (!StringUtils.equals(super.getPathInfo(), originalPathInfo)) {
230                 return current;
231             }
232             if (!StringUtils.equals(super.getQueryString(), originalQueryString)) {
233                 return current;
234             }
235             return newServletPath;
236         }
237     }
238 }