View Javadoc
1   /*******************************************************************************
2    *   Gisgraphy Project 
3    * 
4    *   This library is free software; you can redistribute it and/or
5    *   modify it under the terms of the GNU Lesser General Public
6    *   License as published by the Free Software Foundation; either
7    *   version 2.1 of the License, or (at your option) any later version.
8    * 
9    *   This library is distributed in the hope that it will be useful,
10   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12   *   Lesser General Public License for more details.
13   * 
14   *   You should have received a copy of the GNU Lesser General Public
15   *   License along with this library; if not, write to the Free Software
16   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
17   * 
18   *  Copyright 2008  Gisgraphy project 
19   *  David Masclet <davidmasclet@gisgraphy.com>
20   *  
21   *  
22   *******************************************************************************/
23  /* This software is published under the terms of the OpenSymphony Software
24   * License version 1.1, of which a copy has been included with this
25   * distribution in the LICENSE.txt file. */
26  package com.opensymphony.module.sitemesh.filter;
27  
28  import java.io.IOException;
29  import java.io.PrintWriter;
30  
31  import javax.servlet.Filter;
32  import javax.servlet.FilterChain;
33  import javax.servlet.FilterConfig;
34  import javax.servlet.RequestDispatcher;
35  import javax.servlet.ServletContext;
36  import javax.servlet.ServletException;
37  import javax.servlet.ServletRequest;
38  import javax.servlet.ServletResponse;
39  import javax.servlet.http.HttpServletRequest;
40  import javax.servlet.http.HttpServletResponse;
41  
42  import com.opensymphony.module.sitemesh.Config;
43  import com.opensymphony.module.sitemesh.Decorator;
44  import com.opensymphony.module.sitemesh.Factory;
45  import com.opensymphony.module.sitemesh.Page;
46  import com.opensymphony.module.sitemesh.RequestConstants;
47  import com.opensymphony.module.sitemesh.util.Container;
48  
49  /**
50   * Main SiteMesh filter for applying Decorators to entire Pages without creating a session.
51   * 
52   * @author <a HREF="mailto:joe@truemesh.com">Joe Walnes</a>
53   * @author <a HREF="mailto:scott@atlassian.com">Scott Farquhar</a>
54   * @version $Revision: 1.12 $
55   */
56  public class PageFilterWithoutSession implements Filter, RequestConstants {
57      private FilterConfig filterConfig = null;
58  
59      private Factory factory = null;
60  
61      /**
62       * Main method of the Filter.
63       * 
64       * <p>
65       * Checks if the Filter has been applied this request. If not, parses the
66       * page and applies {@link com.opensymphony.module.sitemesh.Decorator} (if
67       * found).
68       */
69      public void doFilter(ServletRequest rq, ServletResponse rs,
70  	    FilterChain chain) throws IOException, ServletException {
71  
72  	HttpServletRequest request = (HttpServletRequest) rq;
73  
74  	if (rq.getAttribute(FILTER_APPLIED) != null
75  		|| factory.isPathExcluded(extractRequestPath(request))) {
76  	    // ensure that filter is only applied once per request
77  	    chain.doFilter(rq, rs);
78  	} else {
79  	    request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
80  
81  	    // force creation of the session now because Tomcat 4 had problems
82  	    // with
83  	    // creating sessions after the response had been committed
84  	    // hack
85  	    if (Container.get() == Container.TOMCAT) {
86  		request.getSession(false);
87  	    }
88  	    HttpServletResponse response = (HttpServletResponse) rs;
89  
90  	    // parse data into Page object (or continue as normal if Page not
91  	    // parseable)
92  	    Page page = parsePage(request, response, chain);
93  
94  	    if (page != null) {
95  		page.setRequest(request);
96  
97  		Decorator decorator = factory.getDecoratorMapper()
98  			.getDecorator(request, page);
99  		if (decorator != null && decorator.getPage() != null) {
100 		    applyDecorator(page, decorator, request, response);
101 		    page = null;
102 		    return;
103 		}
104 
105 		// if we got here, an exception occured or the decorator was
106 		// null,
107 		// what we don't want is an exception printed to the user, so
108 		// we write the original page
109 		writeOriginal(request, response, page);
110 		page = null;
111 	    }
112 	}
113     }
114 
115     private String extractRequestPath(HttpServletRequest request) {
116 	String servletPath = request.getServletPath();
117 	String pathInfo = request.getPathInfo();
118 	String query = request.getQueryString();
119 	return (servletPath == null ? "" : servletPath)
120 		+ (pathInfo == null ? "" : pathInfo)
121 		+ (query == null ? "" : ("?" + query));
122     }
123 
124     /**
125      * Set FilterConfig, and get instance of
126      * {@link com.opensymphony.module.sitemesh.DecoratorMapper}.
127      */
128     public void init(FilterConfig filterConfig) {
129 	if (filterConfig != null) {
130 	    this.filterConfig = filterConfig;
131 	    factory = Factory.getInstance(new Config(filterConfig));
132 	} else {
133 	    destroy();
134 	}
135     }
136 
137     /**
138      * @deprecated Not needed in final version of Servlet 2.3 API - replaced by
139      *             init().
140      */
141     // NOTE: SiteMesh doesn't work with Orion 1.5.2 without this method
142     public FilterConfig getFilterConfig() {
143 	return filterConfig;
144     }
145 
146     /**
147      * @deprecated Not needed in final version of Servlet 2.3 API - replaced by
148      *             init().
149      */
150     // NOTE: SiteMesh doesn't work with Orion 1.5.2 without this method
151     public void setFilterConfig(FilterConfig filterConfig) {
152 	init(filterConfig);
153     }
154 
155     /** Shutdown filter. */
156     public void destroy() {
157 	factory = null;
158     }
159 
160     /**
161      * Continue in filter-chain, writing all content to buffer and parsing into
162      * returned {@link com.opensymphony.module.sitemesh.Page} object. If
163      * {@link com.opensymphony.module.sitemesh.Page} is not parseable, null is
164      * returned.
165      */
166     protected Page parsePage(HttpServletRequest request,
167 	    HttpServletResponse response, FilterChain chain)
168 	    throws IOException, ServletException {
169 	try {
170 	    PageResponseWrapper pageResponse = new PageResponseWrapper(
171 		    response, factory);
172 	    chain.doFilter(request, pageResponse);
173 	    // check if another servlet or filter put a page object to the
174 	    // request
175 	    Page result = (Page) request.getAttribute(PAGE);
176 	    if (result == null) {
177 		// parse the page
178 		result = pageResponse.getPage();
179 	    }
180 	    request.setAttribute(USING_STREAM, new Boolean(pageResponse
181 		    .isUsingStream()));
182 	    return result;
183 	} catch (IllegalStateException e) {
184 	    // weblogic throws an IllegalStateException when an error page is
185 	    // served.
186 	    // it's ok to ignore this, however for all other containers it
187 	    // should be thrown
188 	    // properly.
189 	    if (Container.get() != Container.WEBLOGIC)
190 		throw e;
191 	    return null;
192 	}
193     }
194 
195     /**
196      * Apply {@link com.opensymphony.module.sitemesh.Decorator} to
197      * {@link com.opensymphony.module.sitemesh.Page} and write to the response.
198      */
199     protected void applyDecorator(Page page, Decorator decorator,
200 	    HttpServletRequest request, HttpServletResponse response)
201 	    throws ServletException, IOException {
202 	try {
203 	    request.setAttribute(PAGE, page);
204 	    ServletContext context = filterConfig.getServletContext();
205 	    // see if the URI path (webapp) is set
206 	    if (decorator.getURIPath() != null) {
207 		// in a security conscious environment, the servlet container
208 		// may return null for a given URL
209 		if (context.getContext(decorator.getURIPath()) != null) {
210 		    context = context.getContext(decorator.getURIPath());
211 		}
212 	    }
213 	    // get the dispatcher for the decorator
214 	    RequestDispatcher dispatcher = context
215 		    .getRequestDispatcher(decorator.getPage());
216 	    // create a wrapper around the response
217 	    dispatcher.include(request, response);
218 
219 	    // set the headers specified as decorator init params
220 	    while (decorator.getInitParameterNames().hasNext()) {
221 		String initParam = (String) decorator.getInitParameterNames()
222 			.next();
223 		if (initParam.startsWith("header.")) {
224 		    response.setHeader(initParam.substring(initParam
225 			    .indexOf('.')), decorator
226 			    .getInitParameter(initParam));
227 		}
228 	    }
229 
230 	    request.removeAttribute(PAGE);
231 	} catch (RuntimeException e) {
232 	    // added a print message here because otherwise Tomcat swallows
233 	    // the error and you never see it = bad!
234 	    if (Container.get() == Container.TOMCAT)
235 	    throw e;
236 	}
237     }
238 
239     /** Write the original page data to the response. */
240     private void writeOriginal(HttpServletRequest request,
241 	    HttpServletResponse response, Page page) throws IOException {
242 	response.setContentLength(page.getContentLength());
243 	if (request.getAttribute(USING_STREAM).equals(Boolean.TRUE)) {
244 	    PrintWriter writer = new PrintWriter(response.getOutputStream());
245 	    page.writePage(writer);
246 	    // flush writer to underlying outputStream
247 	    writer.flush();
248 	    response.getOutputStream().flush();
249 	} else {
250 	    page.writePage(response.getWriter());
251 	    response.getWriter().flush();
252 	}
253     }
254 }