1 /* This software is published under the terms of the OpenSymphony Software
2 * License version 1.1, of which a copy has been included with this
3 * distribution in the LICENSE.txt file. */
4 package com.opensymphony.module.sitemesh.filter;
5
6 import com.opensymphony.module.sitemesh;
7 import com.opensymphony.module.sitemesh.util.Container;
8
9 import javax.servlet;
10 import javax.servlet.http.HttpServletRequest;
11 import javax.servlet.http.HttpServletResponse;
12 import java.io.IOException;
13 import java.io.PrintWriter;
14
15 /**
16 * Main SiteMesh filter for applying Decorators to entire Pages.
17 *
18 * @author <a href="joe@truemesh.com">Joe Walnes</a>
19 * @author <a href="scott@atlassian.com">Scott Farquhar</a>
20 * @version $Revision: 1.12 $
21 */
22 public class PageFilter implements Filter, RequestConstants {
23 private FilterConfig filterConfig = null;
24 private Factory factory = null;
25
26 /**
27 * Main method of the Filter.
28 *
29 * <p>Checks if the Filter has been applied this request. If not, parses the page
30 * and applies {@link com.opensymphony.module.sitemesh.Decorator} (if found).
31 */
32 public void doFilter(ServletRequest rq, ServletResponse rs, FilterChain chain)
33 throws IOException, ServletException {
34
35 HttpServletRequest request = (HttpServletRequest) rq;
36
37 if (rq.getAttribute(FILTER_APPLIED) != null || factory.isPathExcluded(extractRequestPath(request))) {
38 // ensure that filter is only applied once per request
39 chain.doFilter(rq, rs);
40 }
41 else {
42 request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
43
44 // force creation of the session now because Tomcat 4 had problems with
45 // creating sessions after the response had been committed
46 if (Container.get() == Container.TOMCAT) {
47 request.getSession(true);
48 }
49 HttpServletResponse response = (HttpServletResponse) rs;
50
51 // parse data into Page object (or continue as normal if Page not parseable)
52 Page page = parsePage(request, response, chain);
53
54 if (page != null) {
55 page.setRequest(request);
56
57 Decorator decorator = factory.getDecoratorMapper().getDecorator(request, page);
58 if (decorator != null && decorator.getPage() != null) {
59 applyDecorator(page, decorator, request, response);
60 page = null;
61 return;
62 }
63
64 // if we got here, an exception occured or the decorator was null,
65 // what we don't want is an exception printed to the user, so
66 // we write the original page
67 writeOriginal(request, response, page);
68 page = null;
69 }
70 }
71 }
72
73 private String extractRequestPath(HttpServletRequest request) {
74 String servletPath = request.getServletPath();
75 String pathInfo = request.getPathInfo();
76 String query = request.getQueryString();
77 return (servletPath == null ? "" : servletPath)
78 + (pathInfo == null ? "" : pathInfo)
79 + (query == null ? "" : ("?" + query));
80 }
81
82 /** Set FilterConfig, and get instance of {@link com.opensymphony.module.sitemesh.DecoratorMapper}. */
83 public void init(FilterConfig filterConfig) {
84 if (filterConfig != null) {
85 this.filterConfig = filterConfig;
86 factory = Factory.getInstance(new Config(filterConfig));
87 }
88 else {
89 destroy();
90 }
91 }
92
93 /** @deprecated Not needed in final version of Servlet 2.3 API - replaced by init(). */
94 // NOTE: SiteMesh doesn't work with Orion 1.5.2 without this method
95 public FilterConfig getFilterConfig() {
96 return filterConfig;
97 }
98
99 /** @deprecated Not needed in final version of Servlet 2.3 API - replaced by init(). */
100 // NOTE: SiteMesh doesn't work with Orion 1.5.2 without this method
101 public void setFilterConfig(FilterConfig filterConfig) {
102 init(filterConfig);
103 }
104
105 /** Shutdown filter. */
106 public void destroy() {
107 factory = null;
108 }
109
110 /**
111 * Continue in filter-chain, writing all content to buffer and parsing
112 * into returned {@link com.opensymphony.module.sitemesh.Page} object. If
113 * {@link com.opensymphony.module.sitemesh.Page} is not parseable, null is returned.
114 */
115 protected Page parsePage(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
116 try {
117 PageResponseWrapper pageResponse = new PageResponseWrapper(response, factory);
118 chain.doFilter(request, pageResponse);
119 // check if another servlet or filter put a page object to the request
120 Page result = (Page)request.getAttribute(PAGE);
121 if (result == null) {
122 // parse the page
123 result = pageResponse.getPage();
124 }
125 request.setAttribute(USING_STREAM, new Boolean(pageResponse.isUsingStream()));
126 return result;
127 }
128 catch (IllegalStateException e) {
129 // weblogic throws an IllegalStateException when an error page is served.
130 // it's ok to ignore this, however for all other containers it should be thrown
131 // properly.
132 if (Container.get() != Container.WEBLOGIC) throw e;
133 return null;
134 }
135 }
136
137 /**
138 * Apply {@link com.opensymphony.module.sitemesh.Decorator} to
139 * {@link com.opensymphony.module.sitemesh.Page} and write to the response.
140 */
141 protected void applyDecorator(Page page, Decorator decorator, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
142 try {
143 request.setAttribute(PAGE, page);
144 ServletContext context = filterConfig.getServletContext();
145 // see if the URI path (webapp) is set
146 if (decorator.getURIPath() != null) {
147 // in a security conscious environment, the servlet container
148 // may return null for a given URL
149 if (context.getContext(decorator.getURIPath()) != null) {
150 context = context.getContext(decorator.getURIPath());
151 }
152 }
153 // get the dispatcher for the decorator
154 RequestDispatcher dispatcher = context.getRequestDispatcher(decorator.getPage());
155 // create a wrapper around the response
156 dispatcher.include(request, response);
157
158 // set the headers specified as decorator init params
159 while (decorator.getInitParameterNames().hasNext()) {
160 String initParam = (String) decorator.getInitParameterNames().next();
161 if (initParam.startsWith("header.")) {
162 response.setHeader(initParam.substring(initParam.indexOf('.')), decorator.getInitParameter(initParam));
163 }
164 }
165
166 request.removeAttribute(PAGE);
167 }
168 catch (RuntimeException e) {
169 // added a print message here because otherwise Tomcat swallows
170 // the error and you never see it = bad!
171 if (Container.get() == Container.TOMCAT)
172 e.printStackTrace();
173
174 throw e;
175 }
176 }
177
178 /** Write the original page data to the response. */
179 private void writeOriginal(HttpServletRequest request, HttpServletResponse response, Page page) throws IOException {
180 response.setContentLength(page.getContentLength());
181 if (request.getAttribute(USING_STREAM).equals(Boolean.TRUE))
182 {
183 PrintWriter writer = new PrintWriter(response.getOutputStream());
184 page.writePage(writer);
185 //flush writer to underlying outputStream
186 writer.flush();
187 response.getOutputStream().flush();
188 }
189 else
190 {
191 page.writePage(response.getWriter());
192 response.getWriter().flush();
193 }
194 }
195 }