001: /*
002: * Copyright (c) 2002-2003 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.views.xslt;
006:
007: import com.opensymphony.webwork.ServletActionContext;
008: import com.opensymphony.webwork.config.Configuration;
009: import com.opensymphony.xwork.ActionContext;
010: import com.opensymphony.xwork.ActionInvocation;
011: import com.opensymphony.xwork.Result;
012: import com.opensymphony.xwork.util.OgnlValueStack;
013: import com.opensymphony.xwork.util.TextParseUtil;
014: import org.apache.commons.logging.Log;
015: import org.apache.commons.logging.LogFactory;
016:
017: import javax.servlet.http.HttpServletResponse;
018: import javax.xml.transform.*;
019: import javax.xml.transform.dom.DOMSource;
020: import javax.xml.transform.stream.StreamResult;
021: import javax.xml.transform.stream.StreamSource;
022: import java.io.IOException;
023: import java.io.PrintWriter;
024: import java.io.Writer;
025: import java.net.URL;
026: import java.util.HashMap;
027: import java.util.Map;
028:
029: /**
030: * XSLTResult provides support for XSLT generated views of WebWork actions.
031: * XSLTResult dynamically creates a DOM representation of the invoking WebWork
032: * action object and applies a specified stylesheet to it.
033: *
034: * The DOM representation of the WebWork action is produced by JavaBeans
035: * style introspection of the properties of the action using an
036: * extensible AdapterFactory. Common Java types and collection are
037: * supported as well as arbitrary beans composed of these types. (See
038: * AdapterFactory). Action properties may also return their own DOMs
039: * (Object implementing Node, Document, or other DOM types) which will then
040: * appear directly as part of the the result DOM for the stylesheet.
041: *
042: * The contents of the result tag normally specify the location of an XSL
043: * stylesheet, relative to the servlet context. e.g.:
044: *
045: * <result-type name="xslt" class="com.opensymphony.webwork.views.xslt.XSLTResult"/>
046: * ...
047: * <result name="success" type="xslt">/foo.xsl</result>
048: *
049: * If the stylesheet location is absent, the raw XML is returned.
050: *
051: * By default, stylesheets are cached for performance. This can be disabled
052: * by setting the webwork property webwork.xslt.nocache to true.
053: *
054: * XSLTResult utilizes a servlet context sensitive URIResolve
055: *
056: * @see AdapterFactory
057: * @author <a href="mailto:meier@meisterbohne.de">Philipp Meier</a>
058: * @author Pat Niemeyer (pat@pat.net
059: */
060: public class XSLTResult implements Result {
061: //~ Static fields/initializers /////////////////////////////////////////////
062:
063: private static final Log log = LogFactory.getLog(XSLTResult.class);
064: public static final String DEFAULT_PARAM = "stylesheetLocation";
065:
066: //~ Instance fields ////////////////////////////////////////////////////////
067:
068: protected boolean noCache = false;
069: private Map templatesCache;
070: private String stylesheetLocation;
071: private boolean parse;
072: private AdapterFactory adapterFactory;
073:
074: //~ Constructors ///////////////////////////////////////////////////////////
075:
076: public XSLTResult() {
077: templatesCache = new HashMap();
078: noCache = Configuration.getString("webwork.xslt.nocache")
079: .trim().equalsIgnoreCase("true");
080: }
081:
082: //~ Methods ////////////////////////////////////////////////////////////////
083:
084: /**
085: * @deprecated Use #setStylesheetLocation(String)
086: */
087: public void setLocation(String location) {
088: setStylesheetLocation(location);
089: }
090:
091: public void setStylesheetLocation(String location) {
092: if (location == null)
093: throw new IllegalArgumentException("Null location");
094: System.out.println("location = " + location);
095: this .stylesheetLocation = location;
096: }
097:
098: public String getStylesheetLocation() {
099: return stylesheetLocation;
100: }
101:
102: /**
103: * If true, parse the stylesheet location for OGNL expressions.
104: *
105: * @param parse
106: */
107: public void setParse(boolean parse) {
108: this .parse = parse;
109: }
110:
111: public void execute(ActionInvocation invocation) throws Exception {
112: long startTime = System.currentTimeMillis();
113: String location = getStylesheetLocation();
114:
115: if (parse) {
116: OgnlValueStack stack = ActionContext.getContext()
117: .getValueStack();
118: location = TextParseUtil
119: .translateVariables(location, stack);
120: }
121:
122: try {
123: HttpServletResponse response = ServletActionContext
124: .getResponse();
125:
126: Writer writer = response.getWriter();
127:
128: // Create a transformer for the stylesheet.
129: Templates templates = null;
130: Transformer transformer;
131: if (location != null) {
132: templates = getTemplates(location);
133: transformer = templates.newTransformer();
134: } else
135: transformer = TransformerFactory.newInstance()
136: .newTransformer();
137:
138: transformer.setURIResolver(getURIResolver());
139:
140: // debug xslt
141: //PrintTraceListener ptl = new PrintTraceListener(new PrintWriter(System.out, true));
142: //ptl.m_traceElements = true;
143: //ptl.m_traceSelection = true;
144: //ptl.m_traceSelection = true;
145: //TransformerImpl ti = (TransformerImpl)transformer;
146: //ti.getTraceManager().addTraceListener( ptl );
147:
148: String mimeType = null;
149: if (templates == null)
150: mimeType = "text/xml"; // no stylesheet, raw xml
151: else
152: mimeType = templates.getOutputProperties().getProperty(
153: OutputKeys.MEDIA_TYPE);
154: if (mimeType == null) {
155: // guess (this is a servlet, so text/html might be the best guess)
156: mimeType = "text/html";
157: }
158:
159: response.setContentType(mimeType);
160:
161: Source xmlSource = getDOMSourceForStack(invocation
162: .getAction());
163:
164: // Transform the source XML to System.out.
165: PrintWriter out = response.getWriter();
166:
167: log.debug("xmlSource = " + xmlSource);
168: transformer.transform(xmlSource, new StreamResult(out));
169:
170: out.close(); // ...and flush...
171:
172: if (log.isDebugEnabled()) {
173: log.debug("Time:"
174: + (System.currentTimeMillis() - startTime)
175: + "ms");
176: }
177:
178: writer.flush();
179: } catch (Exception e) {
180: log.error("Unable to render XSLT Template, '" + location
181: + "'", e);
182: throw e;
183: }
184: }
185:
186: protected AdapterFactory getAdapterFactory() {
187: if (adapterFactory == null)
188: adapterFactory = new AdapterFactory();
189: return adapterFactory;
190: }
191:
192: protected void setAdapterFactory(AdapterFactory adapterFactory) {
193: this .adapterFactory = adapterFactory;
194: }
195:
196: /**
197: * Get the URI Resolver to be called by the processor when it encounters an xsl:include, xsl:import, or document()
198: * function. The default is an instance of ServletURIResolver, which operates relative to the servlet context.
199: */
200: protected URIResolver getURIResolver() {
201: return new ServletURIResolver(ServletActionContext
202: .getServletContext());
203: }
204:
205: protected Templates getTemplates(String path)
206: throws TransformerException, IOException {
207: String pathFromRequest = ServletActionContext.getRequest()
208: .getParameter("xslt.location");
209:
210: if (pathFromRequest != null)
211: path = pathFromRequest;
212:
213: if (path == null)
214: throw new TransformerException("Stylesheet path is null");
215:
216: Templates templates = (Templates) templatesCache.get(path);
217:
218: if (noCache || (templates == null)) {
219: synchronized (templatesCache) {
220: URL resource = ServletActionContext.getServletContext()
221: .getResource(path);
222:
223: if (resource == null) {
224: throw new TransformerException("Stylesheet " + path
225: + " not found in resources.");
226: }
227:
228: log.debug("Preparing XSLT stylesheet templates: "
229: + path);
230:
231: TransformerFactory factory = TransformerFactory
232: .newInstance();
233: templates = factory.newTemplates(new StreamSource(
234: resource.openStream()));
235: templatesCache.put(path, templates);
236: }
237: }
238:
239: return templates;
240: }
241:
242: protected Source getDOMSourceForStack(Object action)
243: throws IllegalAccessException, InstantiationException {
244: return new DOMSource(getAdapterFactory().adaptDocument(
245: "result", action));
246: }
247: }
|