/* | |
* $Id: PortletVelocityResult.java 582626 2007-10-07 13:26:12Z mrdon $ | |
* | |
* 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.portlet.result; | |
import java.io.OutputStreamWriter; | |
import java.io.Writer; | |
import javax.portlet.ActionResponse; | |
import javax.servlet.Servlet; | |
import javax.servlet.ServletContext; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpServletResponse; | |
import javax.servlet.jsp.JspFactory; | |
import javax.servlet.jsp.PageContext; | |
import org.apache.struts2.ServletActionContext; | |
import org.apache.struts2.StrutsConstants; | |
import org.apache.struts2.dispatcher.StrutsResultSupport; | |
import org.apache.struts2.portlet.PortletActionConstants; | |
import org.apache.struts2.portlet.context.PortletActionContext; | |
import org.apache.struts2.views.JspSupportServlet; | |
import org.apache.struts2.views.velocity.VelocityManager; | |
import org.apache.velocity.Template; | |
import org.apache.velocity.app.VelocityEngine; | |
import org.apache.velocity.context.Context; | |
import com.opensymphony.xwork2.ActionContext; | |
import com.opensymphony.xwork2.ActionInvocation; | |
import com.opensymphony.xwork2.inject.Inject; | |
import com.opensymphony.xwork2.util.ValueStack; | |
import com.opensymphony.xwork2.util.logging.Logger; | |
import com.opensymphony.xwork2.util.logging.LoggerFactory; | |
/** | |
* <!-- START SNIPPET: description --> | |
* | |
* Using the Servlet container's {@link JspFactory}, this result mocks a JSP | |
* execution environment and then displays a Velocity template that will be | |
* streamed directly to the servlet output. | |
* | |
* <!-- END SNIPPET: description --> <p/><b>This result type takes the | |
* following parameters: </b> | |
* | |
* <!-- START SNIPPET: params --> | |
* | |
* <ul> | |
* | |
* <li><b>location (default) </b>- the location of the template to process. | |
* </li> | |
* | |
* <li><b>parse </b>- true by default. If set to false, the location param | |
* will not be parsed for Ognl expressions.</li> | |
* | |
* </ul> | |
* <p> | |
* This result follows the same rules from {@link StrutsResultSupport}. | |
* </p> | |
* | |
* <!-- END SNIPPET: params --> | |
* | |
* <b>Example: </b> | |
* | |
* <pre> | |
* <!-- START SNIPPET: example --> | |
* <result name="success" type="velocity"> | |
* <param name="location">foo.vm</param> | |
* </result> | |
* <!-- END SNIPPET: example --> | |
* </pre> | |
* | |
*/ | |
public class PortletVelocityResult extends StrutsResultSupport { | |
private static final long serialVersionUID = -8241086555872212274L; | |
private static final Logger LOG = LoggerFactory.getLogger(PortletVelocityResult.class); | |
private String defaultEncoding; | |
private VelocityManager velocityManager; | |
private JspFactory jspFactory = JspFactory.getDefaultFactory(); | |
public PortletVelocityResult() { | |
super(); | |
} | |
public PortletVelocityResult(String location) { | |
super(location); | |
} | |
@Inject | |
public void setVelocityManager(VelocityManager mgr) { | |
this.velocityManager = mgr; | |
} | |
@Inject(StrutsConstants.STRUTS_I18N_ENCODING) | |
public void setDefaultEncoding(String encoding) { | |
this.defaultEncoding = encoding; | |
} | |
/* (non-Javadoc) | |
* @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation) | |
*/ | |
public void doExecute(String location, ActionInvocation invocation) | |
throws Exception { | |
if (PortletActionContext.isAction()) { | |
executeActionResult(location, invocation); | |
} else if (PortletActionContext.isRender()) { | |
executeRenderResult(location, invocation); | |
} | |
} | |
/** | |
* Executes the result | |
* | |
* @param location The location string | |
* @param invocation The action invocation | |
*/ | |
private void executeActionResult(String location, | |
ActionInvocation invocation) { | |
ActionResponse res = PortletActionContext.getActionResponse(); | |
// View is rendered outside an action...uh oh... | |
res.setRenderParameter(PortletActionConstants.ACTION_PARAM, | |
"freemarkerDirect"); | |
res.setRenderParameter("location", location); | |
res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext | |
.getRequest().getPortletMode().toString()); | |
} | |
/** | |
* Creates a Velocity context from the action, loads a Velocity template and | |
* executes the template. Output is written to the servlet output stream. | |
* | |
* @param finalLocation the location of the Velocity template | |
* @param invocation an encapsulation of the action execution state. | |
* @throws Exception if an error occurs when creating the Velocity context, | |
* loading or executing the template or writing output to the | |
* servlet response stream. | |
*/ | |
public void executeRenderResult(String finalLocation, | |
ActionInvocation invocation) throws Exception { | |
ValueStack stack = ActionContext.getContext().getValueStack(); | |
HttpServletRequest request = ServletActionContext.getRequest(); | |
HttpServletResponse response = ServletActionContext.getResponse(); | |
ServletContext servletContext = ServletActionContext | |
.getServletContext(); | |
Servlet servlet = JspSupportServlet.jspSupportServlet; | |
velocityManager.init(servletContext); | |
boolean usedJspFactory = false; | |
PageContext pageContext = (PageContext) ActionContext.getContext().get( | |
ServletActionContext.PAGE_CONTEXT); | |
if (pageContext == null && servlet != null) { | |
pageContext = jspFactory.getPageContext(servlet, request, response, | |
null, true, 8192, true); | |
ActionContext.getContext().put(ServletActionContext.PAGE_CONTEXT, | |
pageContext); | |
usedJspFactory = true; | |
} | |
try { | |
String encoding = getEncoding(finalLocation); | |
String contentType = getContentType(finalLocation); | |
if (encoding != null) { | |
contentType = contentType + ";charset=" + encoding; | |
} | |
response.setContentType(contentType); | |
Template t = getTemplate(stack, | |
velocityManager.getVelocityEngine(), invocation, | |
finalLocation, encoding); | |
Context context = createContext(velocityManager, stack, request, | |
response, finalLocation); | |
Writer writer = new OutputStreamWriter(response.getOutputStream(), | |
encoding); | |
t.merge(context, writer); | |
// always flush the writer (we used to only flush it if this was a | |
// jspWriter, but someone asked | |
// to do it all the time (WW-829). Since Velocity support is being | |
// deprecated, we'll oblige :) | |
writer.flush(); | |
} catch (Exception e) { | |
LOG.error("Unable to render Velocity Template, '" + finalLocation | |
+ "'", e); | |
throw e; | |
} finally { | |
if (usedJspFactory) { | |
jspFactory.releasePageContext(pageContext); | |
} | |
} | |
return; | |
} | |
/** | |
* Retrieve the content type for this template. <p/>People can override | |
* this method if they want to provide specific content types for specific | |
* templates (eg text/xml). | |
* | |
* @return The content type associated with this template (default | |
* "text/html") | |
*/ | |
protected String getContentType(String templateLocation) { | |
return "text/html"; | |
} | |
/** | |
* Retrieve the encoding for this template. <p/>People can override this | |
* method if they want to provide specific encodings for specific templates. | |
* | |
* @return The encoding associated with this template (defaults to the value | |
* of 'struts.i18n.encoding' property) | |
*/ | |
protected String getEncoding(String templateLocation) { | |
String encoding = defaultEncoding; | |
if (encoding == null) { | |
encoding = System.getProperty("file.encoding"); | |
} | |
if (encoding == null) { | |
encoding = "UTF-8"; | |
} | |
return encoding; | |
} | |
/** | |
* Given a value stack, a Velocity engine, and an action invocation, this | |
* method returns the appropriate Velocity template to render. | |
* | |
* @param stack the value stack to resolve the location again (when parse | |
* equals true) | |
* @param velocity the velocity engine to process the request against | |
* @param invocation an encapsulation of the action execution state. | |
* @param location the location of the template | |
* @param encoding the charset encoding of the template | |
* @return the template to render | |
* @throws Exception when the requested template could not be found | |
*/ | |
protected Template getTemplate(ValueStack stack, | |
VelocityEngine velocity, ActionInvocation invocation, | |
String location, String encoding) throws Exception { | |
if (!location.startsWith("/")) { | |
location = invocation.getProxy().getNamespace() + "/" + location; | |
} | |
Template template = velocity.getTemplate(location, encoding); | |
return template; | |
} | |
/** | |
* Creates the VelocityContext that we'll use to render this page. | |
* | |
* @param velocityManager a reference to the velocityManager to use | |
* @param stack the value stack to resolve the location against (when parse | |
* equals true) | |
* @param location the name of the template that is being used | |
* @return the a minted Velocity context. | |
*/ | |
protected Context createContext(VelocityManager velocityManager, | |
ValueStack stack, HttpServletRequest request, | |
HttpServletResponse response, String location) { | |
return velocityManager.createContext(stack, request, response); | |
} | |
} |