View Javadoc

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