All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.struts2.views.freemarker.FreemarkerResult Maven / Gradle / Ivy

There is a newer version: 6.3.0.2
Show newest version
/*
 * 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 com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.result.StrutsResultSupport;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Locale;

/**
 * Renders a view using the Freemarker template engine.
 */
public class FreemarkerResult extends StrutsResultSupport {

    private static final long serialVersionUID = -3778230771704661631L;

    private static final Logger LOG = LogManager.getLogger(FreemarkerResult.class);

    protected ActionInvocation invocation;
    protected Configuration configuration;
    protected ObjectWrapper wrapper;
    protected FreemarkerManager freemarkerManager;
    private Writer writer;
    private Boolean writeIfCompleted = null;

    /*
     * Struts results are constructed for each result execution
     *
     * the current context is available to subclasses via these protected fields
     */
    protected String location;
    private String pContentType = "text/html";
    private static final String PARENT_TEMPLATE_WRITER = FreemarkerResult.class.getName() +  ".parentWriter";

    public FreemarkerResult() {
        super();
    }

    public FreemarkerResult(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
     *
     * @return the content type
     */
    public String getContentType() {
        return pContentType;
    }

    /**
     * Execute this result, using the specified template locationArg.
     * 

* The template locationArg has already been interpolated for any variable substitutions *

*

* 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 *

* * @param locationArg location argument * @param invocation the action invocation * * @throws IOException in case of IO errors * @throws TemplateException in case of freemarker template errors */ public void doExecute(String locationArg, ActionInvocation invocation) throws IOException, TemplateException { this.location = locationArg; this.invocation = invocation; this.configuration = getConfiguration(); this.wrapper = getObjectWrapper(); ActionContext ctx = invocation.getInvocationContext(); HttpServletRequest req = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); String absoluteLocation; if (location.startsWith("/")) { absoluteLocation = location; } else { String namespace = invocation.getProxy().getNamespace(); if (namespace == null || namespace.length() == 0 || namespace.equals("/")) { absoluteLocation = "/" + location; } else if (namespace.startsWith("/")) { absoluteLocation = namespace + "/" + location; } else { absoluteLocation = "/" + namespace + "/" + location; } } Template template = configuration.getTemplate(absoluteLocation, deduceLocale()); TemplateModel model = createModel(); // Give subclasses a chance to hook into preprocessing if (preTemplateProcess(template, model)) { try { final boolean willWriteIfCompleted; if (writeIfCompleted != null) { willWriteIfCompleted = isWriteIfCompleted(); } else { willWriteIfCompleted = template.getTemplateExceptionHandler() == TemplateExceptionHandler.RETHROW_HANDLER; } // Process the template Writer writer = getWriter(); if (willWriteIfCompleted){ CharArrayWriter parentCharArrayWriter = (CharArrayWriter) req.getAttribute(PARENT_TEMPLATE_WRITER); boolean isTopTemplate; if (isTopTemplate = (parentCharArrayWriter == null)) { //this is the top template parentCharArrayWriter = new CharArrayWriter(); //set it in the request because when the "action" tag is used a new VS and ActionContext is created req.setAttribute(PARENT_TEMPLATE_WRITER, parentCharArrayWriter); } try { template.process(model, parentCharArrayWriter); if (isTopTemplate) { parentCharArrayWriter.flush(); parentCharArrayWriter.writeTo(writer); } } catch (TemplateException | IOException e) { if (LOG.isErrorEnabled()) { LOG.error("Error processing Freemarker result!", e); } throw e; } finally { if (isTopTemplate) { req.removeAttribute(PARENT_TEMPLATE_WRITER); parentCharArrayWriter.close(); } } } else { template.process(model, writer); } } 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. *

* *

* * The default implementation obtains the configuration from the ConfigurationManager instance. * *

* * @return the freemarker configuration object * @throws TemplateException in case of freemarker configuration errors */ 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. *

* *

* * The default implementation returns {@link Configuration#getObjectWrapper()} * *

* * @return the object wrapper from configuration */ protected ObjectWrapper getObjectWrapper() { return configuration.getObjectWrapper(); } public void setWriter(Writer writer) { this.writer = writer; } /** * The default writer writes directly to the response writer. * * @return Writer the response writer * @throws IOException in case of IO errors */ protected Writer getWriter() throws IOException { if(writer != null) { return writer; } return ServletActionContext.getResponse().getWriter(); } /** * Build the instance of the ScopesHashModel, including JspTagLib support *

* Objects added to the model are *

* *
    *
  • Application - servlet context attributes hash model *
  • JspTaglibs - jsp tag lib factory model *
  • Request - request attributes hash model *
  • Session - session attributes hash model *
  • request - the HttpServletRequst object for direct access *
  • response - the HttpServletResponse object for direct access *
  • stack - the OgnLValueStack instance for direct access *
  • ognl - the instance of the OgnlTool *
  • action - the action itself *
  • exception - optional : the JSP or Servlet exception as per the servlet spec (for JSP Exception pages) *
  • struts - instance of the StrutsUtil class *
* * @return TemplateModel returns the created template model * @throws TemplateModelException in case of errors during creating the model */ protected TemplateModel createModel() throws TemplateModelException { ServletContext servletContext = ServletActionContext.getServletContext(); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); ValueStack stack = ServletActionContext.getContext().getValueStack(); Object action = null; if(invocation!= null ) action = invocation.getAction(); //Added for NullPointException return freemarkerManager.buildTemplateModel(stack, action, 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 action (assuming the action implements {@link LocaleProvider}) or, if * the action does not the configuration's locale is returned. Override this method to provide different behaviour, * * @return the locale from action if action implements the {@link LocaleProvider}) or local from configuration */ protected Locale deduceLocale() { if (invocation.getAction() instanceof LocaleProvider) { return ((LocaleProvider) invocation.getAction()).getLocale(); } else { return configuration.getLocale(); } } /** * the default implementation of postTemplateProcess applies the contentType parameter * * @param template the freemarker template * @param model the template model * * @throws IOException in case of IO errors */ protected void postTemplateProcess(Template template, TemplateModel model) 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 * * @param template the freemarker template * @param model the template model * @return true to process the template, false to suppress template processing. * @throws IOException in case of IO errors */ protected boolean preTemplateProcess(Template template, TemplateModel model) throws IOException { Object attrContentType = template.getCustomAttribute("content_type"); HttpServletResponse response = ServletActionContext.getResponse(); if (response.getContentType() == null) { if (attrContentType != null) { response.setContentType(attrContentType.toString()); } else { String contentType = getContentType(); if (contentType == null) { contentType = "text/html"; } String encoding = template.getEncoding(); if (encoding != null) { contentType = contentType + "; charset=" + encoding; } response.setContentType(contentType); } } else if(isInsideActionTag()){ //trigger com.opensymphony.module.sitemesh.filter.PageResponseWrapper.deactivateSiteMesh() response.setContentType(response.getContentType()); } return true; } private boolean isInsideActionTag() { Object attribute = ServletActionContext.getRequest().getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION); return (Boolean) ObjectUtils.defaultIfNull(attribute, Boolean.FALSE); } public boolean isWriteIfCompleted() { return writeIfCompleted != null && writeIfCompleted; } /** * @param writeIfCompleted template is processed and flushed according to freemarker library policies */ public void setWriteIfCompleted(Boolean writeIfCompleted) { this.writeIfCompleted = writeIfCompleted; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy