1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 implements PortletActionConstants{
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
67
68
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
123 invocation.getInvocationContext().getSession().put(RENDER_DIRECT_LOCATION, location);
124 res.setRenderParameter(PortletActionConstants.ACTION_PARAM, "freemarkerDirect");
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
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 * 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