| /* |
| * 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.velocity.result; |
| |
| import com.opensymphony.xwork2.ActionContext; |
| import com.opensymphony.xwork2.ActionInvocation; |
| import com.opensymphony.xwork2.inject.Inject; |
| import com.opensymphony.xwork2.util.ValueStack; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.struts2.ServletActionContext; |
| import org.apache.struts2.StrutsConstants; |
| import org.apache.struts2.result.StrutsResultSupport; |
| 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 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 java.io.OutputStreamWriter; |
| import java.io.Writer; |
| |
| /** |
| * <!-- 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> |
| * </p> |
| * <!-- 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 --> |
| * <p> |
| * <b>Example:</b> |
| * </p> |
| * <pre> |
| * <!-- START SNIPPET: example --> |
| * <result name="success" type="velocity"> |
| * <param name="location">foo.vm</param> |
| * </result> |
| * <!-- END SNIPPET: example --> |
| * </pre> |
| * |
| */ |
| public class VelocityResult extends StrutsResultSupport { |
| |
| private static final long serialVersionUID = 7268830767762559424L; |
| |
| private static final Logger LOG = LogManager.getLogger(VelocityResult.class); |
| |
| private String defaultEncoding; |
| private VelocityManager velocityManager; |
| private JspFactory jspFactory = JspFactory.getDefaultFactory(); |
| |
| public VelocityResult() { |
| super(); |
| } |
| |
| public VelocityResult(String location) { |
| super(location); |
| } |
| |
| @Inject(StrutsConstants.STRUTS_I18N_ENCODING) |
| public void setDefaultEncoding(String val) { |
| defaultEncoding = val; |
| } |
| |
| @Inject |
| public void setVelocityManager(VelocityManager mgr) { |
| this.velocityManager = mgr; |
| } |
| |
| /** |
| * 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 doExecute(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; |
| } |
| |
| Template t = getTemplate(stack, velocityManager.getVelocityEngine(), invocation, finalLocation, encoding); |
| |
| Context context = createContext(velocityManager, stack, request, response, finalLocation); |
| Writer writer = new OutputStreamWriter(response.getOutputStream(), encoding); |
| |
| |
| response.setContentType(contentType); |
| |
| 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); |
| } |
| } |
| } |
| |
| /** |
| * 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). |
| * </p> |
| * |
| * @param templateLocation location of template |
| * @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. |
| * </p> |
| * |
| * @param templateLocation location of template |
| * @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 request the servlet request |
| * @param response the servlet response |
| * @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); |
| } |
| } |