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 org
.codehaus
.groovy
.grails
.web
.sitemesh
.FactoryHolder
;
32 import javax
.servlet
.*;
33 import javax
.servlet
.http
.HttpServletRequest
;
34 import javax
.servlet
.http
.HttpServletResponse
;
35 import java
.io
.IOException
;
36 import java
.io
.PrintWriter
;
39 * Extends the default page filter to overide the apply decorator behaviour
40 * if the page is a GSP
42 * @author Graeme Rocher
45 public class ZKGrailsPageFilter
extends PageFilter
{
47 private static final Log LOG
= LogFactory
.getLog( ZKGrailsPageFilter
.class );
48 private static final String HTML_EXT
= ".html";
49 private static final String UTF_8_ENCODING
= "UTF-8";
50 private static final String CONFIG_OPTION_GSP_ENCODING
= "grails.views.gsp.encoding";
53 public void init(FilterConfig filterConfig
) {
54 super.init(filterConfig
);
55 this.filterConfig
= filterConfig
;
56 FactoryHolder
.setFactory(this.factory
);
59 public void destroy() {
61 FactoryHolder
.setFactory(null);
64 private boolean isZK(HttpServletRequest request
) {
65 String path
= extractRequestPath(request
);
66 final String
[] ext
= new String
[]{".zul",".dsp","*.zhtml", "*.svg", "*.xml2html"};
67 for(int i
=0;i
< ext
.length
; i
++) {
68 if(path
.lastIndexOf(ext
[i
])!=-1) return true;
73 * 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
75 * Main method of the Filter.
77 * <p>Checks if the Filter has been applied this request. If not, parses the page
78 * and applies {@link com.opensymphony.module.sitemesh.Decorator} (if found).
80 public void doFilter(ServletRequest rq
, ServletResponse rs
, FilterChain chain
)
81 throws IOException
, ServletException
{
83 HttpServletRequest request
= (HttpServletRequest
) rq
;
85 // System.out.println(">>> " + extractRequestPath(request));
87 if (rq
.getAttribute(FILTER_APPLIED
) != null || factory
.isPathExcluded(extractRequestPath(request
)) || isZK(request
)) {
88 // ensure that filter is only applied once per request
89 chain
.doFilter(rq
, rs
);
92 request
.setAttribute(FILTER_APPLIED
, Boolean
.TRUE
);
95 DecoratorMapper decoratorMapper
= factory
.getDecoratorMapper();
96 HttpServletResponse response
= (HttpServletResponse
) rs
;
98 // parse data into Page object (or continue as normal if Page not parseable)
99 Page page
= parsePage(request
, response
, chain
);
102 page
.setRequest(request
);
104 Decorator decorator
= decoratorMapper
.getDecorator(request
, page
);
105 if (decorator
!= null && decorator
.getPage() != null) {
106 applyDecorator(page
, decorator
, request
, response
);
109 // if we got here, an exception occured or the decorator was null,
110 // what we don't want is an exception printed to the user, so
111 // we write the original page
112 writeOriginal(request
, response
, page
);
118 * Continue in filter-chain, writing all content to buffer and parsing
119 * into returned {@link com.opensymphony.module.sitemesh.Page} object. If
120 * {@link com.opensymphony.module.sitemesh.Page} is not parseable, null is returned.
122 protected Page
parsePage(HttpServletRequest request
, HttpServletResponse response
, FilterChain chain
) throws IOException
, ServletException
{
124 PageResponseWrapper pageResponse
= new PageResponseWrapper(response
, factory
);
125 UrlPathHelper urlHelper
= new UrlPathHelper();
126 String requestURI
= urlHelper
.getOriginatingRequestUri(request
);
128 if(requestURI
.endsWith(HTML_EXT
)) {
129 String encoding
= (String
)ConfigurationHolder
.getFlatConfig().get(CONFIG_OPTION_GSP_ENCODING
);
130 if(encoding
== null) encoding
= UTF_8_ENCODING
;
131 pageResponse
.setContentType("text/html;charset="+encoding
);
136 chain
.doFilter(request
, pageResponse
);
137 // check if another servlet or filter put a page object to the request
138 Page result
= (Page
)request
.getAttribute(PAGE
);
139 if (result
== null) {
141 result
= pageResponse
.getPage();
143 request
.setAttribute(USING_STREAM
, pageResponse
.isUsingStream() ? Boolean
.TRUE
: Boolean
.FALSE
); // JDK 1.3 friendly
146 catch (IllegalStateException e
) {
147 // weblogic throws an IllegalStateException when an error page is served.
148 // it's ok to ignore this, however for all other containers it should be thrown
150 if (Container
.get() != Container
.WEBLOGIC
) throw e
;
155 private String
extractRequestPath(HttpServletRequest request
) {
156 String servletPath
= request
.getServletPath();
157 String pathInfo
= request
.getPathInfo();
158 String query
= request
.getQueryString();
159 return (servletPath
== null ?
"" : servletPath
)
160 + (pathInfo
== null ?
"" : pathInfo
)
161 + (query
== null ?
"" : ("?" + query
));
164 /** Write the original page data to the response. */
165 private void writeOriginal(HttpServletRequest request
, HttpServletResponse response
, Page page
) throws IOException
{
166 response
.setContentLength(page
.getContentLength());
167 if (request
.getAttribute(USING_STREAM
).equals(Boolean
.TRUE
))
169 PrintWriter writer
= new PrintWriter(response
.getOutputStream());
170 page
.writePage(writer
);
171 //flush writer to underlying outputStream
173 response
.getOutputStream().flush();
177 page
.writePage(response
.getWriter());
178 response
.getWriter().flush();
184 * @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)
186 protected void applyDecorator(Page page
, Decorator decorator
, HttpServletRequest request
, HttpServletResponse response
) throws ServletException
, IOException
{
187 final String uriPath
= decorator
.getURIPath();
188 if(uriPath
!= null && uriPath
.endsWith(".gsp")) {
189 request
.setAttribute(PAGE
, page
);
191 detectContentTypeFromPage(page
, response
);
193 RequestDispatcher rd
= request
.getRequestDispatcher(decorator
.getURIPath());
194 if(!response
.isCommitted()) {
195 if(LOG
.isDebugEnabled()) {
196 LOG
.debug("Rendering layout using forward: " + decorator
.getURIPath());
198 rd
.forward(request
, response
);
201 if(LOG
.isDebugEnabled()) {
202 LOG
.debug("Rendering layout using include: " + decorator
.getURIPath());
204 request
.setAttribute(GrailsApplicationAttributes
.GSP_TO_RENDER
,decorator
.getURIPath());
205 rd
.include(request
,response
);
206 request
.removeAttribute(GrailsApplicationAttributes
.GSP_TO_RENDER
);
209 // set the headers specified as decorator init params
210 while (decorator
.getInitParameterNames().hasNext()) {
211 String initParam
= (String
) decorator
.getInitParameterNames().next();
212 if (initParam
.startsWith("header.")) {
213 response
.setHeader(initParam
.substring(initParam
.indexOf('.')), decorator
.getInitParameter(initParam
));
216 request
.removeAttribute(PAGE
);
220 super.applyDecorator(page
, decorator
, request
, response
);
225 private void detectContentTypeFromPage(Page page
, HttpServletResponse response
) {
226 String contentType
= page
.getProperty("meta.http-equiv.Content-Type");
227 if(contentType
!= null && "text/html".equals(response
.getContentType())) {
228 response
.setContentType(contentType
);