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