001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.portlet.result;
006:
007: import com.opensymphony.webwork.ServletActionContext;
008: import com.opensymphony.webwork.WebWorkConstants;
009: import com.opensymphony.webwork.config.Configuration;
010: import com.opensymphony.webwork.dispatcher.WebWorkResultSupport;
011: import com.opensymphony.webwork.portlet.PortletActionConstants;
012: import com.opensymphony.webwork.portlet.context.PortletActionContext;
013: import com.opensymphony.webwork.views.JspSupportServlet;
014: import com.opensymphony.webwork.views.velocity.VelocityManager;
015: import com.opensymphony.xwork.ActionContext;
016: import com.opensymphony.xwork.ActionInvocation;
017: import com.opensymphony.xwork.util.OgnlValueStack;
018:
019: import freemarker.template.TemplateException;
020:
021: import org.apache.commons.logging.Log;
022: import org.apache.commons.logging.LogFactory;
023: import org.apache.velocity.Template;
024: import org.apache.velocity.app.VelocityEngine;
025: import org.apache.velocity.context.Context;
026:
027: import javax.portlet.ActionResponse;
028: import javax.portlet.PortletException;
029: import javax.portlet.PortletRequestDispatcher;
030: import javax.servlet.Servlet;
031: import javax.servlet.ServletContext;
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpServletResponse;
034: import javax.servlet.jsp.JspFactory;
035: import javax.servlet.jsp.PageContext;
036:
037: import java.io.IOException;
038: import java.io.OutputStreamWriter;
039: import java.io.Writer;
040:
041: /**
042: * <!-- START SNIPPET: description -->
043: *
044: * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
045: * execution environment and then displays a Velocity template that will be
046: * streamed directly to the servlet output.
047: *
048: * <!-- END SNIPPET: description --> <p/><b>This result type takes the
049: * following parameters: </b>
050: *
051: * <!-- START SNIPPET: params -->
052: *
053: * <ul>
054: *
055: * <li><b>location (default) </b>- the location of the template to process.
056: * </li>
057: *
058: * <li><b>parse </b>- true by default. If set to false, the location param
059: * will not be parsed for Ognl expressions.</li>
060: *
061: * </ul>
062: * <p>
063: * This result follows the same rules from {@link WebWorkResultSupport}.
064: * </p>
065: *
066: * <!-- END SNIPPET: params -->
067: *
068: * <b>Example: </b>
069: *
070: * <pre>
071: * <!-- START SNIPPET: example -->
072: * <result name="success" type="velocity">
073: * <param name="location">foo.vm</param>
074: * </result>
075: * <!-- END SNIPPET: example -->
076: * </pre>
077: *
078: * @author <a href="mailto:matt@indigoegg.com">Matt Ho </a>
079: */
080: public class PortletVelocityResult extends WebWorkResultSupport {
081:
082: private static final Log log = LogFactory
083: .getLog(PortletVelocityResult.class);
084:
085: public void doExecute(String location, ActionInvocation invocation)
086: throws Exception {
087: if (PortletActionContext.isEvent()) {
088: executeActionResult(location, invocation);
089: } else if (PortletActionContext.isRender()) {
090: executeRenderResult(location, invocation);
091: }
092: }
093:
094: /**
095: * @param location
096: * @param invocation
097: */
098: private void executeActionResult(String location,
099: ActionInvocation invocation) {
100: ActionResponse res = PortletActionContext.getActionResponse();
101: // View is rendered outside an action...uh oh...
102: res.setRenderParameter(PortletActionConstants.ACTION_PARAM,
103: "freemarkerDirect");
104: res.setRenderParameter("location", location);
105: res.setRenderParameter(PortletActionConstants.MODE_PARAM,
106: PortletActionContext.getRequest().getPortletMode()
107: .toString());
108:
109: }
110:
111: /**
112: * Creates a Velocity context from the action, loads a Velocity template and
113: * executes the template. Output is written to the servlet output stream.
114: *
115: * @param finalLocation the location of the Velocity template
116: * @param invocation an encapsulation of the action execution state.
117: * @throws Exception if an error occurs when creating the Velocity context,
118: * loading or executing the template or writing output to the
119: * servlet response stream.
120: */
121: public void executeRenderResult(String finalLocation,
122: ActionInvocation invocation) throws Exception {
123: prepareServletActionContext();
124: OgnlValueStack stack = ActionContext.getContext()
125: .getValueStack();
126:
127: HttpServletRequest request = ServletActionContext.getRequest();
128: HttpServletResponse response = ServletActionContext
129: .getResponse();
130: JspFactory jspFactory = null;
131: ServletContext servletContext = ServletActionContext
132: .getServletContext();
133: Servlet servlet = JspSupportServlet.jspSupportServlet;
134:
135: VelocityManager.getInstance().init(servletContext);
136:
137: boolean usedJspFactory = false;
138: PageContext pageContext = (PageContext) ActionContext
139: .getContext().get(ServletActionContext.PAGE_CONTEXT);
140:
141: if (pageContext == null && servlet != null) {
142: jspFactory = JspFactory.getDefaultFactory();
143: pageContext = jspFactory.getPageContext(servlet, request,
144: response, null, true, 8192, true);
145: ActionContext.getContext().put(
146: ServletActionContext.PAGE_CONTEXT, pageContext);
147: usedJspFactory = true;
148: }
149:
150: try {
151: String encoding = getEncoding(finalLocation);
152: String contentType = getContentType(finalLocation);
153:
154: if (encoding != null) {
155: contentType = contentType + ";charset=" + encoding;
156: }
157:
158: response.setContentType(contentType);
159:
160: VelocityManager velocityManager = VelocityManager
161: .getInstance();
162: Template t = getTemplate(stack, velocityManager
163: .getVelocityEngine(), invocation, finalLocation,
164: encoding);
165:
166: Context context = createContext(velocityManager, stack,
167: request, response, finalLocation);
168:
169: Writer writer = new OutputStreamWriter(response
170: .getOutputStream(), encoding);
171:
172: t.merge(context, writer);
173:
174: // always flush the writer (we used to only flush it if this was a
175: // jspWriter, but someone asked
176: // to do it all the time (WW-829). Since Velocity support is being
177: // deprecated, we'll oblige :)
178: writer.flush();
179: } catch (Exception e) {
180: log.error("Unable to render Velocity Template, '"
181: + finalLocation + "'", e);
182: throw e;
183: } finally {
184: if (usedJspFactory) {
185: jspFactory.releasePageContext(pageContext);
186: }
187: }
188:
189: return;
190: }
191:
192: /**
193: * Retrieve the content type for this template. <p/>People can override
194: * this method if they want to provide specific content types for specific
195: * templates (eg text/xml).
196: *
197: * @return The content type associated with this template (default
198: * "text/html")
199: */
200: protected String getContentType(String templateLocation) {
201: return "text/html";
202: }
203:
204: /**
205: * Retrieve the encoding for this template. <p/>People can override this
206: * method if they want to provide specific encodings for specific templates.
207: *
208: * @return The encoding associated with this template (defaults to the value
209: * of 'webwork.i18n.encoding' property)
210: */
211: protected String getEncoding(String templateLocation) {
212: String encoding = (String) Configuration
213: .get(WebWorkConstants.WEBWORK_I18N_ENCODING);
214: if (encoding == null) {
215: encoding = System.getProperty("file.encoding");
216: }
217: if (encoding == null) {
218: encoding = "UTF-8";
219: }
220: return encoding;
221: }
222:
223: /**
224: * Given a value stack, a Velocity engine, and an action invocation, this
225: * method returns the appropriate Velocity template to render.
226: *
227: * @param stack the value stack to resolve the location again (when parse
228: * equals true)
229: * @param velocity the velocity engine to process the request against
230: * @param invocation an encapsulation of the action execution state.
231: * @param location the location of the template
232: * @param encoding the charset encoding of the template
233: * @return the template to render
234: * @throws Exception when the requested template could not be found
235: */
236: protected Template getTemplate(OgnlValueStack stack,
237: VelocityEngine velocity, ActionInvocation invocation,
238: String location, String encoding) throws Exception {
239: if (!location.startsWith("/")) {
240: location = invocation.getProxy().getNamespace() + "/"
241: + location;
242: }
243:
244: Template template = velocity.getTemplate(location, encoding);
245:
246: return template;
247: }
248:
249: /**
250: * Creates the VelocityContext that we'll use to render this page.
251: *
252: * @param velocityManager a reference to the velocityManager to use
253: * @param stack the value stack to resolve the location against (when parse
254: * equals true)
255: * @param location the name of the template that is being used
256: * @return the a minted Velocity context.
257: */
258: protected Context createContext(VelocityManager velocityManager,
259: OgnlValueStack stack, HttpServletRequest request,
260: HttpServletResponse response, String location) {
261: return velocityManager.createContext(stack, request, response);
262: }
263:
264: /**
265: *
266: */
267: private void prepareServletActionContext() throws PortletException,
268: IOException {
269: PortletRequestDispatcher disp = PortletActionContext
270: .getPortletConfig().getPortletContext()
271: .getNamedDispatcher("preparator");
272: disp.include(PortletActionContext.getRenderRequest(),
273: PortletActionContext.getRenderResponse());
274: }
275: }
|