001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.views.freemarker;
006:
007: import java.io.IOException;
008: import java.io.Writer;
009: import java.util.Locale;
010:
011: import javax.portlet.ActionResponse;
012: import javax.portlet.PortletException;
013: import javax.portlet.PortletRequestDispatcher;
014: import javax.servlet.ServletContext;
015: import javax.servlet.http.HttpServletRequest;
016: import javax.servlet.http.HttpServletResponse;
017:
018: import com.opensymphony.webwork.ServletActionContext;
019: import com.opensymphony.webwork.dispatcher.WebWorkResultSupport;
020: import com.opensymphony.webwork.portlet.PortletActionConstants;
021: import com.opensymphony.webwork.portlet.context.PortletActionContext;
022: import com.opensymphony.webwork.views.util.ResourceUtil;
023: import com.opensymphony.xwork.ActionContext;
024: import com.opensymphony.xwork.ActionInvocation;
025: import com.opensymphony.xwork.util.OgnlValueStack;
026:
027: import freemarker.template.Configuration;
028: import freemarker.template.ObjectWrapper;
029: import freemarker.template.SimpleHash;
030: import freemarker.template.Template;
031: import freemarker.template.TemplateException;
032: import freemarker.template.TemplateModel;
033: import freemarker.template.TemplateModelException;
034:
035: /**
036: * @author CameronBraid
037: */
038: public class PortletFreemarkerResult extends WebWorkResultSupport {
039:
040: protected ActionInvocation invocation;
041:
042: protected Configuration configuration;
043:
044: protected ObjectWrapper wrapper;
045:
046: /*
047: * webwork results are constructed for each result execeution
048: *
049: * the current context is availible to subclasses via these protected fields
050: */
051: protected String location;
052:
053: private String pContentType = "text/html";
054:
055: public void setContentType(String aContentType) {
056: pContentType = aContentType;
057: }
058:
059: /**
060: * allow parameterization of the contentType the default being text/html
061: */
062: public String getContentType() {
063: return pContentType;
064: }
065:
066: /**
067: * Execute this result, using the specified template location. <p/>The
068: * template location has already been interoplated for any variable
069: * substitutions <p/>this method obtains the freemarker configuration and
070: * the object wrapper from the provided hooks. It them implements the
071: * template processing workflow by calling the hooks for preTemplateProcess
072: * and postTemplateProcess
073: */
074: public void doExecute(String location, ActionInvocation invocation)
075: throws IOException, TemplateException, PortletException {
076: if (PortletActionContext.isEvent()) {
077: executeActionResult(location, invocation);
078: } else if (PortletActionContext.isRender()) {
079: executeRenderResult(location, invocation);
080: }
081: }
082:
083: /**
084: * @param location
085: * @param invocation
086: */
087: private void executeActionResult(String location,
088: ActionInvocation invocation) {
089: ActionResponse res = PortletActionContext.getActionResponse();
090: // View is rendered outside an action...uh oh...
091: res.setRenderParameter(PortletActionConstants.ACTION_PARAM,
092: "freemarkerDirect");
093: res.setRenderParameter("location", location);
094: res.setRenderParameter(PortletActionConstants.MODE_PARAM,
095: PortletActionContext.getRequest().getPortletMode()
096: .toString());
097:
098: }
099:
100: /**
101: * @param location
102: * @param invocation
103: * @throws TemplateException
104: * @throws IOException
105: * @throws TemplateModelException
106: */
107: private void executeRenderResult(String location,
108: ActionInvocation invocation) throws TemplateException,
109: IOException, TemplateModelException, PortletException {
110: prepareServletActionContext();
111: this .location = location;
112: this .invocation = invocation;
113: this .configuration = getConfiguration();
114: this .wrapper = getObjectWrapper();
115:
116: HttpServletRequest req = ServletActionContext.getRequest();
117: HttpServletResponse res = ServletActionContext.getResponse();
118:
119: if (!location.startsWith("/")) {
120: ActionContext ctx = invocation.getInvocationContext();
121: String base = ResourceUtil.getResourceBase(req);
122: location = base + "/" + location;
123: }
124:
125: Template template = configuration.getTemplate(location,
126: deduceLocale());
127: TemplateModel model = createModel();
128: // Give subclasses a chance to hook into preprocessing
129: if (preTemplateProcess(template, model)) {
130: try {
131: // Process the template
132: PortletActionContext.getRenderResponse()
133: .setContentType(pContentType);
134: template.process(model, getWriter());
135: } finally {
136: // Give subclasses a chance to hook into postprocessing
137: postTemplateProcess(template, model);
138: }
139: }
140: }
141:
142: /**
143: *
144: */
145: private void prepareServletActionContext() throws PortletException,
146: IOException {
147: PortletRequestDispatcher disp = PortletActionContext
148: .getPortletConfig().getPortletContext()
149: .getNamedDispatcher("preparator");
150: disp.include(PortletActionContext.getRenderRequest(),
151: PortletActionContext.getRenderResponse());
152: }
153:
154: /**
155: * This method is called from {@link #doExecute(String, ActionInvocation)}
156: * to obtain the FreeMarker configuration object that this result will use
157: * for template loading. This is a hook that allows you to custom-configure
158: * the configuration object in a subclass, or to fetch it from an IoC
159: * container. <p/><b>The default implementation obtains the configuration
160: * from the ConfigurationManager instance. </b>
161: */
162: protected Configuration getConfiguration() throws TemplateException {
163: return FreemarkerManager.getInstance().getConfiguration(
164: ServletActionContext.getServletContext());
165: }
166:
167: /**
168: * This method is called from {@link #doExecute(String, ActionInvocation)}
169: * to obtain the FreeMarker object wrapper object that this result will use
170: * for adapting objects into template models. This is a hook that allows you
171: * to custom-configure the wrapper object in a subclass. <p/><b>The default
172: * implementation returns {@link Configuration#getObjectWrapper()}</b>
173: */
174: protected ObjectWrapper getObjectWrapper() {
175: return configuration.getObjectWrapper();
176: }
177:
178: /**
179: * The default writer writes directly to the response writer.
180: */
181: protected Writer getWriter() throws IOException {
182: return PortletActionContext.getRenderResponse().getWriter();
183: }
184:
185: /**
186: * Build the instance of the ScopesHashModel, including JspTagLib support
187: * <p/>Objects added to the model are <p/>
188: * <ul>
189: * <li>Application - servlet context attributes hash model
190: * <li>JspTaglibs - jsp tag lib factory model
191: * <li>Request - request attributes hash model
192: * <li>Session - session attributes hash model
193: * <li>req - the HttpServletRequst object for direct access
194: * <li>res - the HttpServletResponse object for direct access
195: * <li>stack - the OgnLValueStack instance for direct access
196: * <li>ognl - the instance of the OgnlTool
197: * <li>action - the action itself
198: * <li>exception - optional : the JSP or Servlet exception as per the
199: * servlet spec (for JSP Exception pages)
200: * <li>webwork - instance of the WebWorkUtil class
201: * </ul>
202: */
203: protected TemplateModel createModel() throws TemplateModelException {
204: ServletContext servletContext = ServletActionContext
205: .getServletContext();
206: HttpServletRequest request = ServletActionContext.getRequest();
207: HttpServletResponse response = ServletActionContext
208: .getResponse();
209: OgnlValueStack stack = ServletActionContext.getContext()
210: .getValueStack();
211: return FreemarkerManager.getInstance().buildTemplateModel(
212: stack, invocation.getAction(), servletContext, request,
213: response, wrapper);
214: }
215:
216: /**
217: * Returns the locale used for the
218: * {@link Configuration#getTemplate(String, Locale)}call. The base
219: * implementation simply returns the locale setting of the configuration.
220: * Override this method to provide different behaviour,
221: */
222: protected Locale deduceLocale() {
223: return configuration.getLocale();
224: }
225:
226: /**
227: * the default implementation of postTemplateProcess applies the contentType
228: * parameter
229: */
230: protected void postTemplateProcess(Template template,
231: TemplateModel data) throws IOException {
232: }
233:
234: /**
235: * Called before the execution is passed to template.process(). This is a
236: * generic hook you might use in subclasses to perform a specific action
237: * before the template is processed. By default does nothing. A typical
238: * action to perform here is to inject application-specific objects into the
239: * model root
240: *
241: * @return true to process the template, false to suppress template
242: * processing.
243: */
244: protected boolean preTemplateProcess(Template template,
245: TemplateModel model) throws IOException {
246: Object attrContentType = template
247: .getCustomAttribute("content_type");
248:
249: if (attrContentType != null) {
250: ServletActionContext.getResponse().setContentType(
251: attrContentType.toString());
252: } else {
253: String contentType = getContentType();
254:
255: if (contentType == null) {
256: contentType = "text/html";
257: }
258:
259: String encoding = template.getEncoding();
260:
261: if (encoding != null) {
262: contentType = contentType + "; charset=" + encoding;
263: }
264:
265: ServletActionContext.getResponse().setContentType(
266: contentType);
267: }
268:
269: return true;
270: }
271: }
|