View Javadoc

1   /*
2    * $Id: PortletFreemarkerResult.java 568895 2007-08-23 08:57:36Z rgielen $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  package org.apache.struts2.views.freemarker;
22  
23  import com.opensymphony.xwork2.ActionInvocation;
24  import com.opensymphony.xwork2.inject.Inject;
25  import com.opensymphony.xwork2.util.ValueStack;
26  import freemarker.template.*;
27  import org.apache.struts2.ServletActionContext;
28  import org.apache.struts2.dispatcher.StrutsResultSupport;
29  import org.apache.struts2.portlet.PortletActionConstants;
30  import org.apache.struts2.portlet.context.PortletActionContext;
31  import org.apache.struts2.views.util.ResourceUtil;
32  
33  import javax.portlet.ActionResponse;
34  import javax.portlet.PortletException;
35  import javax.servlet.ServletContext;
36  import javax.servlet.http.HttpServletRequest;
37  import javax.servlet.http.HttpServletResponse;
38  import java.io.IOException;
39  import java.io.Writer;
40  import java.util.Locale;
41  
42  /***
43   */
44  public class PortletFreemarkerResult extends StrutsResultSupport {
45  
46      private static final long serialVersionUID = -5570612389289887543L;
47  
48      protected ActionInvocation invocation;
49  
50      protected Configuration configuration;
51  
52      protected ObjectWrapper wrapper;
53      protected FreemarkerManager freemarkerManager;
54  
55      /*
56       * Struts results are constructed for each result execeution
57       *
58       * the current context is availible to subclasses via these protected fields
59       */
60      protected String location;
61  
62      private String pContentType = "text/html";
63  
64      public PortletFreemarkerResult() {
65          super();
66      }
67  
68      public PortletFreemarkerResult(String location) {
69          super(location);
70      }
71      
72      @Inject
73      public void setFreemarkerManager(FreemarkerManager mgr) {
74          this.freemarkerManager = mgr;
75      }
76  
77      public void setContentType(String aContentType) {
78          pContentType = aContentType;
79      }
80  
81      /***
82       * allow parameterization of the contentType the default being text/html
83       */
84      public String getContentType() {
85          return pContentType;
86      }
87  
88      /***
89       * Execute this result, using the specified template location. <p/>The
90       * template location has already been interoplated for any variable
91       * substitutions <p/>this method obtains the freemarker configuration and
92       * the object wrapper from the provided hooks. It them implements the
93       * template processing workflow by calling the hooks for preTemplateProcess
94       * and postTemplateProcess
95       */
96      public void doExecute(String location, ActionInvocation invocation)
97              throws IOException, TemplateException, PortletException {
98          if (PortletActionContext.isEvent()) {
99              executeActionResult(location, invocation);
100         } else if (PortletActionContext.isRender()) {
101             executeRenderResult(location, invocation);
102         }
103     }
104 
105     /***
106      * @param location
107      * @param invocation
108      */
109     private void executeActionResult(String location,
110                                      ActionInvocation invocation) {
111         ActionResponse res = PortletActionContext.getActionResponse();
112         // View is rendered outside an action...uh oh...
113         res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "freemarkerDirect");
114         res.setRenderParameter("location", location);
115         res.setRenderParameter(PortletActionConstants.MODE_PARAM, PortletActionContext
116                 .getRequest().getPortletMode().toString());
117 
118     }
119 
120     /***
121      * @param location
122      * @param invocation
123      * @throws TemplateException
124      * @throws IOException
125      * @throws TemplateModelException
126      */
127     private void executeRenderResult(String location,
128                                      ActionInvocation invocation) throws TemplateException, IOException,
129             TemplateModelException, PortletException {
130         this.location = location;
131         this.invocation = invocation;
132         this.configuration = getConfiguration();
133         this.wrapper = getObjectWrapper();
134 
135         HttpServletRequest req = ServletActionContext.getRequest();
136 
137         if (!location.startsWith("/")) {
138             String base = ResourceUtil.getResourceBase(req);
139             location = base + "/" + location;
140         }
141 
142         Template template = configuration.getTemplate(location, deduceLocale());
143         TemplateModel model = createModel();
144         // Give subclasses a chance to hook into preprocessing
145         if (preTemplateProcess(template, model)) {
146             try {
147                 // Process the template
148                 PortletActionContext.getRenderResponse().setContentType(pContentType);
149                 template.process(model, getWriter());
150             } finally {
151                 // Give subclasses a chance to hook into postprocessing
152                 postTemplateProcess(template, model);
153             }
154         }
155     }
156 
157     /***
158      * This method is called from {@link #doExecute(String, ActionInvocation)}
159      * to obtain the FreeMarker configuration object that this result will use
160      * for template loading. This is a hook that allows you to custom-configure
161      * the configuration object in a subclass, or to fetch it from an IoC
162      * container. <p/><b>The default implementation obtains the configuration
163      * from the ConfigurationManager instance. </b>
164      */
165     protected Configuration getConfiguration() throws TemplateException {
166         return freemarkerManager.getConfiguration(
167                 ServletActionContext.getServletContext());
168     }
169 
170     /***
171      * This method is called from {@link #doExecute(String, ActionInvocation)}
172      * to obtain the FreeMarker object wrapper object that this result will use
173      * for adapting objects into template models. This is a hook that allows you
174      * to custom-configure the wrapper object in a subclass. <p/><b>The default
175      * implementation returns {@link Configuration#getObjectWrapper()}</b>
176      */
177     protected ObjectWrapper getObjectWrapper() {
178         return configuration.getObjectWrapper();
179     }
180 
181     /***
182      * The default writer writes directly to the response writer.
183      */
184     protected Writer getWriter() throws IOException {
185         return PortletActionContext.getRenderResponse().getWriter();
186     }
187 
188     /***
189      * Build the instance of the ScopesHashModel, including JspTagLib support
190      * <p/>Objects added to the model are <p/>
191      * <ul>
192      * <li>Application - servlet context attributes hash model
193      * <li>JspTaglibs - jsp tag lib factory model
194      * <li>Request - request attributes hash model
195      * <li>Session - session attributes hash model
196      * <li>request - the HttpServletRequst object for direct access
197      * <li>response - the HttpServletResponse object for direct access
198      * <li>stack - the OgnLValueStack instance for direct access
199      * <li>ognl - the instance of the OgnlTool
200      * <li>action - the action itself
201      * <li>exception - optional : the JSP or Servlet exception as per the
202      * servlet spec (for JSP Exception pages)
203      * <li>struts - instance of the StrutsUtil class
204      * </ul>
205      */
206     protected TemplateModel createModel() throws TemplateModelException {
207         ServletContext servletContext = ServletActionContext
208                 .getServletContext();
209         HttpServletRequest request = ServletActionContext.getRequest();
210         HttpServletResponse response = ServletActionContext.getResponse();
211         ValueStack stack = ServletActionContext.getContext()
212                 .getValueStack();
213         return freemarkerManager.buildTemplateModel(stack,
214                 invocation.getAction(), servletContext, request, response,
215                 wrapper);
216     }
217 
218     /***
219      * Returns the locale used for the
220      * {@link Configuration#getTemplate(String, Locale)}call. The base
221      * implementation simply returns the locale setting of the configuration.
222      * Override this method to provide different behaviour,
223      */
224     protected Locale deduceLocale() {
225         return configuration.getLocale();
226     }
227 
228     /***
229      * the default implementation of postTemplateProcess applies the contentType
230      * parameter
231      */
232     protected void postTemplateProcess(Template template, TemplateModel data)
233             throws IOException {
234     }
235 
236     /***
237      * Called before the execution is passed to template.process(). This is a
238      * generic hook you might use in subclasses to perform a specific action
239      * before the template is processed. By default does nothing. A typical
240      * action to perform here is to inject application-specific objects into the
241      * model root
242      *
243      * @return true to process the template, false to suppress template
244      *         processing.
245      */
246     protected boolean preTemplateProcess(Template template, TemplateModel model)
247             throws IOException {
248         Object attrContentType = template.getCustomAttribute("content_type");
249 
250         if (attrContentType != null) {
251             ServletActionContext.getResponse().setContentType(
252                     attrContentType.toString());
253         } else {
254             String contentType = getContentType();
255 
256             if (contentType == null) {
257                 contentType = "text/html";
258             }
259 
260             String encoding = template.getEncoding();
261 
262             if (encoding != null) {
263                 contentType = contentType + "; charset=" + encoding;
264             }
265 
266             ServletActionContext.getResponse().setContentType(contentType);
267         }
268 
269         return true;
270     }
271 }
272