1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
66
67
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
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
155 if (preTemplateProcess(template, model)) {
156 try {
157
158 PortletActionContext.getRenderResponse().setContentType(pContentType);
159 template.process(model, getWriter());
160 } finally {
161
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