blob: 1d27f79082afdbb33bef394ee7af35e01971bde9 [file] [log] [blame]
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.struts2.views.freemarker;
import java.io.IOException;
import java.io.Writer;
import java.util.Locale;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.StrutsResultSupport;
import org.apache.struts2.portlet.PortletActionConstants;
import org.apache.struts2.portlet.context.PortletActionContext;
import org.apache.struts2.views.util.ResourceUtil;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
*/
public class PortletFreemarkerResult extends StrutsResultSupport implements PortletActionConstants{
private static final long serialVersionUID = -5570612389289887543L;
protected ActionInvocation invocation;
protected Configuration configuration;
protected ObjectWrapper wrapper;
protected FreemarkerManager freemarkerManager;
/*
* Struts results are constructed for each result execeution
*
* the current context is availible to subclasses via these protected fields
*/
protected String location;
private String pContentType = "text/html";
public PortletFreemarkerResult() {
super();
}
public PortletFreemarkerResult(String location) {
super(location);
}
@Inject
public void setFreemarkerManager(FreemarkerManager mgr) {
this.freemarkerManager = mgr;
}
public void setContentType(String aContentType) {
pContentType = aContentType;
}
/**
* allow parameterization of the contentType the default being text/html
*/
public String getContentType() {
return pContentType;
}
/**
* Execute this result, using the specified template location. <p/>The
* template location has already been interoplated for any variable
* substitutions <p/>this method obtains the freemarker configuration and
* the object wrapper from the provided hooks. It them implements the
* template processing workflow by calling the hooks for preTemplateProcess
* and postTemplateProcess
*/
public void doExecute(String location, ActionInvocation invocation)
throws IOException, TemplateException, PortletException {
if (PortletActionContext.isEvent()) {
executeActionResult(location, invocation);
} else if (PortletActionContext.isRender()) {
executeRenderResult(location, invocation);
}
}
/**
* @param location
* @param invocation
*/
private void executeActionResult(String location,
ActionInvocation invocation) {
ActionResponse res = PortletActionContext.getActionResponse();
// View is rendered outside an action...uh oh...
invocation.getInvocationContext().getSession().put(RENDER_DIRECT_LOCATION, location);
res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "freemarkerDirect");
res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
.getRequest().getPortletMode().toString());
}
/**
* @param location
* @param invocation
* @throws TemplateException
* @throws IOException
* @throws TemplateModelException
*/
private void executeRenderResult(String location,
ActionInvocation invocation) throws TemplateException, IOException,
TemplateModelException, PortletException {
this.location = location;
this.invocation = invocation;
this.configuration = getConfiguration();
this.wrapper = getObjectWrapper();
HttpServletRequest req = ServletActionContext.getRequest();
if (!location.startsWith("/")) {
String base = ResourceUtil.getResourceBase(req);
location = base + "/" + location;
}
Template template = configuration.getTemplate(location, deduceLocale());
TemplateModel model = createModel();
// Give subclasses a chance to hook into preprocessing
if (preTemplateProcess(template, model)) {
try {
// Process the template
PortletActionContext.getRenderResponse().setContentType(pContentType);
template.process(model, getWriter());
} finally {
// Give subclasses a chance to hook into postprocessing
postTemplateProcess(template, model);
}
}
}
/**
* This method is called from {@link #doExecute(String, ActionInvocation)}
* to obtain the FreeMarker configuration object that this result will use
* for template loading. This is a hook that allows you to custom-configure
* the configuration object in a subclass, or to fetch it from an IoC
* container. <p/><b>The default implementation obtains the configuration
* from the ConfigurationManager instance. </b>
*/
protected Configuration getConfiguration() throws TemplateException {
return freemarkerManager.getConfiguration(
ServletActionContext.getServletContext());
}
/**
* This method is called from {@link #doExecute(String, ActionInvocation)}
* to obtain the FreeMarker object wrapper object that this result will use
* for adapting objects into template models. This is a hook that allows you
* to custom-configure the wrapper object in a subclass. <p/><b>The default
* implementation returns {@link Configuration#getObjectWrapper()}</b>
*/
protected ObjectWrapper getObjectWrapper() {
return configuration.getObjectWrapper();
}
/**
* The default writer writes directly to the response writer.
*/
protected Writer getWriter() throws IOException {
return PortletActionContext.getRenderResponse().getWriter();
}
/**
* Build the instance of the ScopesHashModel, including JspTagLib support
* <p/>Objects added to the model are <p/>
* <ul>
* <li>Application - servlet context attributes hash model
* <li>JspTaglibs - jsp tag lib factory model
* <li>Request - request attributes hash model
* <li>Session - session attributes hash model
* <li>request - the HttpServletRequst object for direct access
* <li>response - the HttpServletResponse object for direct access
* <li>stack - the OgnLValueStack instance for direct access
* <li>ognl - the instance of the OgnlTool
* <li>action - the action itself
* <li>exception - optional : the JSP or Servlet exception as per the
* servlet spec (for JSP Exception pages)
* <li>struts - instance of the StrutsUtil class
* </ul>
*/
protected TemplateModel createModel() throws TemplateModelException {
ServletContext servletContext = ServletActionContext
.getServletContext();
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
ValueStack stack = ServletActionContext.getContext()
.getValueStack();
return freemarkerManager.buildTemplateModel(stack,
invocation.getAction(), servletContext, request, response,
wrapper);
}
/**
* Returns the locale used for the
* {@link Configuration#getTemplate(String, Locale)}call. The base
* implementation simply returns the locale setting of the configuration.
* Override this method to provide different behaviour,
*/
protected Locale deduceLocale() {
return configuration.getLocale();
}
/**
* the default implementation of postTemplateProcess applies the contentType
* parameter
*/
protected void postTemplateProcess(Template template, TemplateModel data)
throws IOException {
}
/**
* Called before the execution is passed to template.process(). This is a
* generic hook you might use in subclasses to perform a specific action
* before the template is processed. By default does nothing. A typical
* action to perform here is to inject application-specific objects into the
* model root
*
* @return true to process the template, false to suppress template
* processing.
*/
protected boolean preTemplateProcess(Template template, TemplateModel model)
throws IOException {
Object attrContentType = template.getCustomAttribute("content_type");
if (attrContentType != null) {
ServletActionContext.getResponse().setContentType(
attrContentType.toString());
} else {
String contentType = getContentType();
if (contentType == null) {
contentType = "text/html";
}
String encoding = template.getEncoding();
if (encoding != null) {
contentType = contentType + "; charset=" + encoding;
}
ServletActionContext.getResponse().setContentType(contentType);
}
return true;
}
}