View Javadoc

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