001: /*
002: * $Id: PortletVelocityResult.java 568895 2007-08-23 08:57:36Z rgielen $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts2.portlet.result;
022:
023: import com.opensymphony.xwork2.ActionContext;
024: import com.opensymphony.xwork2.ActionInvocation;
025: import com.opensymphony.xwork2.inject.Inject;
026: import com.opensymphony.xwork2.util.ValueStack;
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029: import org.apache.struts2.ServletActionContext;
030: import org.apache.struts2.StrutsConstants;
031: import org.apache.struts2.dispatcher.StrutsResultSupport;
032: import org.apache.struts2.portlet.PortletActionConstants;
033: import org.apache.struts2.portlet.context.PortletActionContext;
034: import org.apache.struts2.views.JspSupportServlet;
035: import org.apache.struts2.views.velocity.VelocityManager;
036: import org.apache.velocity.Template;
037: import org.apache.velocity.app.VelocityEngine;
038: import org.apache.velocity.context.Context;
039:
040: import javax.portlet.ActionResponse;
041: import javax.servlet.Servlet;
042: import javax.servlet.ServletContext;
043: import javax.servlet.http.HttpServletRequest;
044: import javax.servlet.http.HttpServletResponse;
045: import javax.servlet.jsp.JspFactory;
046: import javax.servlet.jsp.PageContext;
047: import java.io.OutputStreamWriter;
048: import java.io.Writer;
049:
050: /**
051: * <!-- START SNIPPET: description -->
052: *
053: * Using the Servlet container's {@link JspFactory}, this result mocks a JSP
054: * execution environment and then displays a Velocity template that will be
055: * streamed directly to the servlet output.
056: *
057: * <!-- END SNIPPET: description --> <p/><b>This result type takes the
058: * following parameters: </b>
059: *
060: * <!-- START SNIPPET: params -->
061: *
062: * <ul>
063: *
064: * <li><b>location (default) </b>- the location of the template to process.
065: * </li>
066: *
067: * <li><b>parse </b>- true by default. If set to false, the location param
068: * will not be parsed for Ognl expressions.</li>
069: *
070: * </ul>
071: * <p>
072: * This result follows the same rules from {@link StrutsResultSupport}.
073: * </p>
074: *
075: * <!-- END SNIPPET: params -->
076: *
077: * <b>Example: </b>
078: *
079: * <pre>
080: * <!-- START SNIPPET: example -->
081: * <result name="success" type="velocity">
082: * <param name="location">foo.vm</param>
083: * </result>
084: * <!-- END SNIPPET: example -->
085: * </pre>
086: *
087: */
088: public class PortletVelocityResult extends StrutsResultSupport {
089:
090: private static final long serialVersionUID = -8241086555872212274L;
091:
092: private static final Log log = LogFactory
093: .getLog(PortletVelocityResult.class);
094:
095: private String defaultEncoding;
096: private VelocityManager velocityManager;
097:
098: public PortletVelocityResult() {
099: super ();
100: }
101:
102: public PortletVelocityResult(String location) {
103: super (location);
104: }
105:
106: @Inject
107: public void setVelocityManager(VelocityManager mgr) {
108: this .velocityManager = mgr;
109: }
110:
111: @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
112: public void setDefaultEncoding(String encoding) {
113: this .defaultEncoding = encoding;
114: }
115:
116: /* (non-Javadoc)
117: * @see org.apache.struts2.dispatcher.StrutsResultSupport#doExecute(java.lang.String, com.opensymphony.xwork2.ActionInvocation)
118: */
119: public void doExecute(String location, ActionInvocation invocation)
120: throws Exception {
121: if (PortletActionContext.isEvent()) {
122: executeActionResult(location, invocation);
123: } else if (PortletActionContext.isRender()) {
124: executeRenderResult(location, invocation);
125: }
126: }
127:
128: /**
129: * Executes the result
130: *
131: * @param location The location string
132: * @param invocation The action invocation
133: */
134: private void executeActionResult(String location,
135: ActionInvocation invocation) {
136: ActionResponse res = PortletActionContext.getActionResponse();
137: // View is rendered outside an action...uh oh...
138: res.setRenderParameter(PortletActionConstants.ACTION_PARAM,
139: "freemarkerDirect");
140: res.setRenderParameter("location", location);
141: res.setRenderParameter(PortletActionConstants.MODE_PARAM,
142: PortletActionContext.getRequest().getPortletMode()
143: .toString());
144:
145: }
146:
147: /**
148: * Creates a Velocity context from the action, loads a Velocity template and
149: * executes the template. Output is written to the servlet output stream.
150: *
151: * @param finalLocation the location of the Velocity template
152: * @param invocation an encapsulation of the action execution state.
153: * @throws Exception if an error occurs when creating the Velocity context,
154: * loading or executing the template or writing output to the
155: * servlet response stream.
156: */
157: public void executeRenderResult(String finalLocation,
158: ActionInvocation invocation) throws Exception {
159: ValueStack stack = ActionContext.getContext().getValueStack();
160:
161: HttpServletRequest request = ServletActionContext.getRequest();
162: HttpServletResponse response = ServletActionContext
163: .getResponse();
164: JspFactory jspFactory = null;
165: ServletContext servletContext = ServletActionContext
166: .getServletContext();
167: Servlet servlet = JspSupportServlet.jspSupportServlet;
168:
169: velocityManager.init(servletContext);
170:
171: boolean usedJspFactory = false;
172: PageContext pageContext = (PageContext) ActionContext
173: .getContext().get(ServletActionContext.PAGE_CONTEXT);
174:
175: if (pageContext == null && servlet != null) {
176: jspFactory = JspFactory.getDefaultFactory();
177: pageContext = jspFactory.getPageContext(servlet, request,
178: response, null, true, 8192, true);
179: ActionContext.getContext().put(
180: ServletActionContext.PAGE_CONTEXT, pageContext);
181: usedJspFactory = true;
182: }
183:
184: try {
185: String encoding = getEncoding(finalLocation);
186: String contentType = getContentType(finalLocation);
187:
188: if (encoding != null) {
189: contentType = contentType + ";charset=" + encoding;
190: }
191: response.setContentType(contentType);
192: Template t = getTemplate(stack, velocityManager
193: .getVelocityEngine(), invocation, finalLocation,
194: encoding);
195:
196: Context context = createContext(velocityManager, stack,
197: request, response, finalLocation);
198: Writer writer = new OutputStreamWriter(response
199: .getOutputStream(), encoding);
200:
201: t.merge(context, writer);
202:
203: // always flush the writer (we used to only flush it if this was a
204: // jspWriter, but someone asked
205: // to do it all the time (WW-829). Since Velocity support is being
206: // deprecated, we'll oblige :)
207: writer.flush();
208: } catch (Exception e) {
209: log.error("Unable to render Velocity Template, '"
210: + finalLocation + "'", e);
211: throw e;
212: } finally {
213: if (usedJspFactory) {
214: jspFactory.releasePageContext(pageContext);
215: }
216: }
217:
218: return;
219: }
220:
221: /**
222: * Retrieve the content type for this template. <p/>People can override
223: * this method if they want to provide specific content types for specific
224: * templates (eg text/xml).
225: *
226: * @return The content type associated with this template (default
227: * "text/html")
228: */
229: protected String getContentType(String templateLocation) {
230: return "text/html";
231: }
232:
233: /**
234: * Retrieve the encoding for this template. <p/>People can override this
235: * method if they want to provide specific encodings for specific templates.
236: *
237: * @return The encoding associated with this template (defaults to the value
238: * of 'struts.i18n.encoding' property)
239: */
240: protected String getEncoding(String templateLocation) {
241: String encoding = defaultEncoding;
242: if (encoding == null) {
243: encoding = System.getProperty("file.encoding");
244: }
245: if (encoding == null) {
246: encoding = "UTF-8";
247: }
248: return encoding;
249: }
250:
251: /**
252: * Given a value stack, a Velocity engine, and an action invocation, this
253: * method returns the appropriate Velocity template to render.
254: *
255: * @param stack the value stack to resolve the location again (when parse
256: * equals true)
257: * @param velocity the velocity engine to process the request against
258: * @param invocation an encapsulation of the action execution state.
259: * @param location the location of the template
260: * @param encoding the charset encoding of the template
261: * @return the template to render
262: * @throws Exception when the requested template could not be found
263: */
264: protected Template getTemplate(ValueStack stack,
265: VelocityEngine velocity, ActionInvocation invocation,
266: String location, String encoding) throws Exception {
267: if (!location.startsWith("/")) {
268: location = invocation.getProxy().getNamespace() + "/"
269: + location;
270: }
271:
272: Template template = velocity.getTemplate(location, encoding);
273:
274: return template;
275: }
276:
277: /**
278: * Creates the VelocityContext that we'll use to render this page.
279: *
280: * @param velocityManager a reference to the velocityManager to use
281: * @param stack the value stack to resolve the location against (when parse
282: * equals true)
283: * @param location the name of the template that is being used
284: * @return the a minted Velocity context.
285: */
286: protected Context createContext(VelocityManager velocityManager,
287: ValueStack stack, HttpServletRequest request,
288: HttpServletResponse response, String location) {
289: return velocityManager.createContext(stack, request, response);
290: }
291:
292: }
|