2 * Copyright 2004-2005 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.codehaus
.groovy
.grails
.web
.sitemesh
;
18 import com
.opensymphony
.module
.sitemesh
.Decorator
;
19 import com
.opensymphony
.module
.sitemesh
.DecoratorMapper
;
20 import com
.opensymphony
.module
.sitemesh
.Page
;
21 import com
.opensymphony
.module
.sitemesh
.util
.Container
;
22 import com
.opensymphony
.module
.sitemesh
.filter
.PageFilter
;
23 import com
.opensymphony
.module
.sitemesh
.filter
.PageResponseWrapper
;
24 import org
.apache
.commons
.logging
.Log
;
25 import org
.apache
.commons
.logging
.LogFactory
;
26 import org
.codehaus
.groovy
.grails
.web
.servlet
.GrailsApplicationAttributes
;
27 import org
.codehaus
.groovy
.grails
.commons
.ConfigurationHolder
;
28 import org
.springframework
.web
.util
.UrlPathHelper
;
30 import javax
.servlet
.*;
31 import javax
.servlet
.http
.HttpServletRequest
;
32 import javax
.servlet
.http
.HttpServletResponse
;
33 import java
.io
.IOException
;
34 import java
.io
.PrintWriter
;
37 * Extends the default page filter to overide the apply decorator behaviour
38 * if the page is a GSP
40 * @author Graeme Rocher
43 public class GrailsPageFilter
extends PageFilter
{
45 private static final Log LOG
= LogFactory
.getLog( GrailsPageFilter
.class );
46 private static final String HTML_EXT
= ".html";
47 private static final String UTF_8_ENCODING
= "UTF-8";
48 private static final String CONFIG_OPTION_GSP_ENCODING
= "grails.views.gsp.encoding";
51 public void init(FilterConfig filterConfig
) {
52 super.init(filterConfig
);
53 this.filterConfig
= filterConfig
;
54 FactoryHolder
.setFactory(this.factory
);
57 public void destroy() {
59 FactoryHolder
.setFactory(null);
62 * TODO: This method has been copied from the parent to fix a bug in sitemesh 2.3. When sitemesh 2.4 is release this method and the two private methods below can removed
64 * Main method of the Filter.
66 * <p>Checks if the Filter has been applied this request. If not, parses the page
67 * and applies {@link com.opensymphony.module.sitemesh.Decorator} (if found).
69 public void doFilter(ServletRequest rq
, ServletResponse rs
, FilterChain chain
)
70 throws IOException
, ServletException
{
72 HttpServletRequest request
= (HttpServletRequest
) rq
;
74 if (rq
.getAttribute(FILTER_APPLIED
) != null || factory
.isPathExcluded(extractRequestPath(request
))) {
75 // ensure that filter is only applied once per request
76 chain
.doFilter(rq
, rs
);
79 request
.setAttribute(FILTER_APPLIED
, Boolean
.TRUE
);
82 DecoratorMapper decoratorMapper
= factory
.getDecoratorMapper();
83 HttpServletResponse response
= (HttpServletResponse
) rs
;
85 // parse data into Page object (or continue as normal if Page not parseable)
86 Page page
= parsePage(request
, response
, chain
);
89 page
.setRequest(request
);
91 Decorator decorator
= decoratorMapper
.getDecorator(request
, page
);
92 if (decorator
!= null && decorator
.getPage() != null) {
93 applyDecorator(page
, decorator
, request
, response
);
96 // if we got here, an exception occured or the decorator was null,
97 // what we don't want is an exception printed to the user, so
98 // we write the original page
99 writeOriginal(request
, response
, page
);
105 * Continue in filter-chain, writing all content to buffer and parsing
106 * into returned {@link com.opensymphony.module.sitemesh.Page} object. If
107 * {@link com.opensymphony.module.sitemesh.Page} is not parseable, null is returned.
109 protected Page
parsePage(HttpServletRequest request
, HttpServletResponse response
, FilterChain chain
) throws IOException
, ServletException
{
111 PageResponseWrapper pageResponse
= new PageResponseWrapper(response
, factory
);
112 UrlPathHelper urlHelper
= new UrlPathHelper();
113 String requestURI
= urlHelper
.getOriginatingRequestUri(request
);
115 if(requestURI
.endsWith(HTML_EXT
)) {
116 String encoding
= (String
)ConfigurationHolder
.getFlatConfig().get(CONFIG_OPTION_GSP_ENCODING
);
117 if(encoding
== null) encoding
= UTF_8_ENCODING
;
118 pageResponse
.setContentType("text/html;charset="+encoding
);
123 chain
.doFilter(request
, pageResponse
);
124 // check if another servlet or filter put a page object to the request
125 Page result
= (Page
)request
.getAttribute(PAGE
);
126 if (result
== null) {
128 result
= pageResponse
.getPage();
130 request
.setAttribute(USING_STREAM
, pageResponse
.isUsingStream() ? Boolean
.TRUE
: Boolean
.FALSE
); // JDK 1.3 friendly
133 catch (IllegalStateException e
) {
134 // weblogic throws an IllegalStateException when an error page is served.
135 // it's ok to ignore this, however for all other containers it should be thrown
137 if (Container
.get() != Container
.WEBLOGIC
) throw e
;
142 private String
extractRequestPath(HttpServletRequest request
) {
143 String servletPath
= request
.getServletPath();
144 String pathInfo
= request
.getPathInfo();
145 String query
= request
.getQueryString();
146 return (servletPath
== null ?
"" : servletPath
)
147 + (pathInfo
== null ?
"" : pathInfo
)
148 + (query
== null ?
"" : ("?" + query
));
151 /** Write the original page data to the response. */
152 private void writeOriginal(HttpServletRequest request
, HttpServletResponse response
, Page page
) throws IOException
{
153 response
.setContentLength(page
.getContentLength());
154 if (request
.getAttribute(USING_STREAM
).equals(Boolean
.TRUE
))
156 PrintWriter writer
= new PrintWriter(response
.getOutputStream());
157 page
.writePage(writer
);
158 //flush writer to underlying outputStream
160 response
.getOutputStream().flush();
164 page
.writePage(response
.getWriter());
165 response
.getWriter().flush();
171 * @see com.opensymphony.module.sitemesh.filter.PageFilter#applyDecorator(com.opensymphony.module.sitemesh.Page, com.opensymphony.module.sitemesh.Decorator, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
173 protected void applyDecorator(Page page
, Decorator decorator
, HttpServletRequest request
, HttpServletResponse response
) throws ServletException
, IOException
{
174 final String uriPath
= decorator
.getURIPath();
175 if(uriPath
!= null && uriPath
.endsWith(".gsp")) {
176 request
.setAttribute(PAGE
, page
);
178 detectContentTypeFromPage(page
, response
);
180 RequestDispatcher rd
= request
.getRequestDispatcher(decorator
.getURIPath());
181 if(!response
.isCommitted()) {
182 if(LOG
.isDebugEnabled()) {
183 LOG
.debug("Rendering layout using forward: " + decorator
.getURIPath());
185 rd
.forward(request
, response
);
188 if(LOG
.isDebugEnabled()) {
189 LOG
.debug("Rendering layout using include: " + decorator
.getURIPath());
191 request
.setAttribute(GrailsApplicationAttributes
.GSP_TO_RENDER
,decorator
.getURIPath());
192 rd
.include(request
,response
);
193 request
.removeAttribute(GrailsApplicationAttributes
.GSP_TO_RENDER
);
196 // set the headers specified as decorator init params
197 while (decorator
.getInitParameterNames().hasNext()) {
198 String initParam
= (String
) decorator
.getInitParameterNames().next();
199 if (initParam
.startsWith("header.")) {
200 response
.setHeader(initParam
.substring(initParam
.indexOf('.')), decorator
.getInitParameter(initParam
));
203 request
.removeAttribute(PAGE
);
207 super.applyDecorator(page
, decorator
, request
, response
);
212 private void detectContentTypeFromPage(Page page
, HttpServletResponse response
) {
213 String contentType
= page
.getProperty("meta.http-equiv.Content-Type");
214 if(contentType
!= null && "text/html".equals(response
.getContentType())) {
215 response
.setContentType(contentType
);