View Javadoc

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