1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
61
62
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
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
145 if (preTemplateProcess(template, model)) {
146 try {
147
148 PortletActionContext.getRenderResponse().setContentType(pContentType);
149 template.process(model, getWriter());
150 } finally {
151
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