PDA

View Full Version : Spring + freemarker + sitemesh


yientau
Apr 22nd, 2007, 11:07 AM
How shall I "include" the decorator to a page ? it will be best describe in scenario.

I have have the following files and contents

/WEB-INF/web.xml
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>

<servlet>
<servlet-name>sitemesh-freemarker</servlet-name>
<servlet-class>com.opensymphony.module.sitemesh.freemarker.Freema rkerDecoratorServlet</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>default_encoding</param-name>
<param-value>ISO-8859-1</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>sitemesh-freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>


/WEB-INF/decorators.xml
<decorators defaultdir="/decorators">
<decorator name="default" page="default.ftl">
<pattern>/*</pattern>
</decorator>
<decorator name="panel" page="panel.ftl"/>
</decorators>


/decorators/default.ftl
<html>
<head>
<title>Testing</title>
${head}
</head>
<body>

${body}

</body>
</html>


/decorators/panel.ftl
<body>
Testing from panel
${body}
</body>


/WEB-INF/pages/main.ftl
<#assign page=JspTaglibs["http://www.opensymphony.com/sitemesh/page"]>
<head>
<title>Panel testing</title>
</head>
<@page.applyDecorator name="panel"/>
<body>
Test from Main
</body>


It throws me ERROR when I run the main.ftl page. It throws the following errors :-

java.lang.NullPointerException: ServletConfig cannot be null com.opensymphony.module.sitemesh.Config.<init>(Config.java:29)
com.opensymphony.module.sitemesh.taglib.page.Apply DecoratorTag.doStartTag (ApplyDecoratorTag.java:128)
freemarker.ext.jsp.TagTransformModel$TagWriter.onS tart (TagTransformModel.java:243)
freemarker.core.Environment.visit(Environment.java :205)
freemarker.core.UnifiedCall.accept(UnifiedCall.jav a:116)
freemarker.core.Environment.visit(Environment.java :171)
freemarker.core.MixedContent.accept(MixedContent.j ava:92)
freemarker.core.Environment.visit(Environment.java :171)
freemarker.core.Environment.process(Environment.ja va:156)
freemarker.template.Template.process(Template.java :219)
org.springframework.web.servlet.view.freemarker.Fr eeMarkerView.processTemplate(FreeMarkerView.java:3 33)
org.springframework.web.servlet.view.freemarker.Fr eeMarkerView.doRender (FreeMarkerView.java:269)
org.springframework.web.servlet.view.freemarker.Fr eeMarkerView.renderMerged TemplateModel(FreeMarkerView.java:214)
org.springframework.web.servlet.view.AbstractTempl ateView.renderMergedOutpu tModel(AbstractTemplateView.java:178)
org.springframework.web.servlet.view.AbstractView. render (AbstractView.java:247)
org.springframework.web.servlet.DispatcherServlet. render (DispatcherServlet.java:1103)
org.springframework.web.servlet.DispatcherServlet. doDispatch (DispatcherServlet.java:840)
org.springframework.web.servlet.DispatcherServlet. doService (DispatcherServlet.java:754)
org.springframework.web.servlet.FrameworkServlet.p rocessRequest (FrameworkServlet.java:399)
org.springframework.web.servlet.FrameworkServlet.d oGet (FrameworkServlet.java:354)
javax.servlet.http.HttpServlet.service(HttpServlet .java:689)
javax.servlet.http.HttpServlet.service(HttpServlet .java:802)
com.opensymphony.module.sitemesh.filter.PageFilter .parsePage (PageFilter.java:119)
com.opensymphony.module.sitemesh.filter.PageFilter .doFilter (PageFilter.java:55)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:264)
org.acegisecurity.intercept.web.FilterSecurityInte rceptor.invoke (FilterSecurityInterceptor.java:107)
org.acegisecurity.intercept.web.FilterSecurityInte rceptor.doFilter (FilterSecurityInterceptor.java:72)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274)
org.acegisecurity.ui.ExceptionTranslationFilter.do Filter (ExceptionTranslationFilter.java:110)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274)
org.acegisecurity.providers.anonymous.AnonymousPro cessingFilter.doFilter (AnonymousProcessingFilter.java:125)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274)
org.acegisecurity.ui.rememberme.RememberMeProcessi ngFilter.doFilter (RememberMeProcessingFilter.java:135)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274)
org.acegisecurity.wrapper.SecurityContextHolderAwa reRequestFilter.doFilter (SecurityContextHolderAwareRequestFilter.java:81)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274)
org.acegisecurity.ui.AbstractProcessingFilter.doFi lter (AbstractProcessingFilter.java:217)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274) org.acegisecurity.ui.logout.LogoutFilter.doFilter( LogoutFilter.java:108)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274)
org.acegisecurity.context.HttpSessionContextIntegr ationFilter.doFilter (HttpSessionContextIntegrationFilter.java:193)
org.acegisecurity.util.FilterChainProxy$VirtualFil terChain.doFilter (FilterChainProxy.java:274) org.acegisecurity.util.FilterChainProxy.doFilter (FilterChainProxy.java:148) org.acegisecurity.util.FilterToBeanProxy.doFilter (FilterToBeanProxy.java:98)

Anyone knows what's the reason of throwing that error? or am I calling the wrong decorator ? My main problem now is I can't access to decorator "panel" from my main.ftl.

Note also that my decorators path(/decorators) and my template path(/WEB-INF/pages) are different. Is this a bug or I am missing out some steps ? Any other references(sites) that I can refer ? Thanks in advance.

nvsc
Apr 25th, 2007, 09:13 PM
We've experienced the same problem and discovered that it is because the Spring FreeMarkerView class constructs a FreeMarker ServletContextHashModel object without a ServletConfig object, resulting in the null pointer exception.

Our solution was to extend Spring's FreeMarkerView so we could overload a couple of methods to construct a proper ServletContextHashModel. Of course, it would be best for the Spring folks to just fix FreeMarkerView so that the GenericServletAdapter has a non-null ServletConfig. But in the meantime, here you are:


import freemarker.ext.servlet.FreemarkerServlet;
import freemarker.ext.servlet.ServletContextHashModel;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.web.servlet.view.freemarker.Fr eeMarkerView;
import org.springframework.beans.BeansException;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.Enumeration;
import java.util.Collections;

public class CustomFreeMarkerView extends FreeMarkerView {
private ServletContextHashModel servletContextHashModel;

/**
* Calls super method but then creates a valid ServletContextHashModel (one with
* a non-null ServletConfig) and stores it away for later use. We can't just set
* the base class servletContextHashModel because it is private and has no setter.
*
* @throws BeansException if the super method throws the exception
*/
protected void initApplicationContext() throws BeansException {
super.initApplicationContext();
servletContextHashModel = new ServletContextHashModel(createServletAdapter(), getObjectWrapper());
}

/**
* Places our valid ServletContextHashModel (one with a non-null ServletConfig)
* into the model for the template, replacing the invalid one that the base
* class creates.
*
* @param template the template to process
* @param model the model for the template
* @param response servlet response (use this to get the OutputStream or Writer)
* @throws IOException if the template file could not be retrieved
* @throws TemplateException if thrown by FreeMarker
*/
@SuppressWarnings(value = "unchecked")
protected void processTemplate(Template template, Map model, HttpServletResponse response) throws IOException, TemplateException {
model.put(FreemarkerServlet.KEY_APPLICATION, servletContextHashModel);
super.processTemplate(template, model, response);
}

/**
* Create a generic servlet that exposes the currently active ServletContext needed for
* JSP access by FreeMarker and initializes it with a non-null ServletConfig.
*
* @return a generic servlet adapter
*/
private GenericServletAdapter createServletAdapter() {
final ServletContext servletContext = getServletContext();
final GenericServletAdapter servletAdapter = new GenericServletAdapter(servletContext);
try {
servletAdapter.init(new GenericServletConfig(servletContext));
} catch(ServletException e) {
//this should never happen since we new it up ourselves
throw new RuntimeException(e);
}
return servletAdapter;
}

/**
* Simple adapter class that extends {@link javax.servlet.GenericServlet} and exposes
* the currently active {@link javax.servlet.ServletContext}. Needed for JSP access in FreeMarker.
*
* Same as the one in the base class, but that one is private too, so we can't use it here.
*/
private static final class GenericServletAdapter extends GenericServlet {
private final ServletContext servletContext;

public GenericServletAdapter(ServletContext servletContext) {
this.servletContext = servletContext;
}

public void service(ServletRequest servletRequest, ServletResponse servletResponse) {
// no-op
}

public ServletContext getServletContext() {
return this.servletContext;
}
}

/**
* Simple {@link javax.servlet.ServletConfig} implementation for use in
* the generic servlet adapter so that a proper ServletContextHashModel
* can be created for FreeMarker.
*/
private static final class GenericServletConfig implements ServletConfig {
private ServletContext servletContext;

public GenericServletConfig(ServletContext servletContext) {
this.servletContext = servletContext;
}

public String getServletName() {
return "SPRING-FREEMARKER-VIEW-NPE-FIX";
}

public ServletContext getServletContext() {
return servletContext;
}

public String getInitParameter(String s) {
return null;
}

@SuppressWarnings(value = "unchecked")
public Enumeration getInitParameterNames() {
return Collections.enumeration(Collections.EMPTY_SET);
}
}

}