alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Struts example source code file (XSLTResult.java)

This example Struts source code file (XSLTResult.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Struts tags/keywords

adapterfactory, adapterfactory, io, network, request, response, servlet, source, string, string, strutsexception, stylesheet, templates, templates, transformerexception, transformerexception, util, valuestack, xslt, xsltresult

The Struts XSLTResult.java source code

/*
 * $Id: XSLTResult.java 651946 2008-04-27 13:41:38Z apetrelli $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.struts2.views.xslt;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsException;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;


/**
 * <!-- START SNIPPET: description -->
 *
 * XSLTResult uses XSLT to transform an action object to XML. The recent version
 * has been specifically modified to deal with Xalan flaws. When using Xalan you
 * may notice that even though you have a very minimal stylesheet like this one
 * <pre>
 * <xsl:template match="/result">
 *   <result/>
 * </xsl:template></pre>
 *
 * <p>
 * Xalan would still iterate through every property of your action and all
 * its descendants.
 * </p>
 *
 * <p>
 * If you had double-linked objects, Xalan would work forever analysing an
 * infinite object tree. Even if your stylesheet was not constructed to process
 * them all. It's because the current Xalan eagerly and extensively converts
 * everything to its internal DTM model before further processing.
 * </p>
 *
 * <p>
 * That's why there's a loop eliminator added that works by indexing every
 * object-property combination during processing. If it notices that some
 * object's property was already walked through, it doesn't go any deeper.
 * Say you have two objects, x and y, with the following properties set
 * (pseudocode):
 * </p>
 * <pre>
 * x.y = y;
 * and
 * y.x = x;
 * action.x=x;</pre>
 *
 * <p>
 * Due to that modification, the resulting XML document based on x would be:
 * </p>
 *
 * <pre>
 * <result>
 *   <x>
 *     <y/>
 *   </x>
 * </result></pre>
 *
 * <p>
 * Without it there would be endless x/y/x/y/x/y/... elements.
 * </p>
 *
 * <p>
 * The XSLTResult code tries also to deal with the fact that DTM model is built
 * in a manner that children are processed before siblings. The result is that if
 * there is object x that is both set in action's x property, and very deeply
 * under action's a property then it would only appear under a, not under x.
 * That's not what we expect, and that's why XSLTResult allows objects to repeat
 * in various places to some extent.
 * </p>
 *
 * <p>
 * Sometimes the object mesh is still very dense and you may notice that even
 * though you have a relatively simple stylesheet, execution takes a tremendous
 * amount of time. To help you to deal with that obstacle of Xalan, you may
 * attach regexp filters to elements paths (xpath).
 * </p>
 *
 * <p>
 * <b>Note: In your .xsl file the root match must be named result.
 * <br/>This example will output the username by using getUsername on your
 * action class:
 * <pre>
 * <xsl:template match="result">
 *   <html>
 *   <body>
 *   Hello <xsl:value-of select="username"/> how are you?
 *   </body>
 *   </html>
 * </xsl:template>
 * </pre>
 *
 * <p>
 * In the following example the XSLT result would only walk through action's
 * properties without their childs. It would also skip every property that has
 * "hugeCollection" in their name. Element's path is first compared to
 * excludingPattern - if it matches it's no longer processed. Then it is
 * compared to matchingPattern and processed only if there's a match.
 * </p>
 *
 * <!-- END SNIPPET: description -->
 *
 * <pre>
 * <result name="success" type="xslt">
 *   <param name="location">foo.xslt</param>
 *   <param name="matchingPattern">^/result/[^/*]$</param>
 *   <param name="excludingPattern">.*(hugeCollection).*</param>
 * </result>
 * <!-- END SNIPPET: description.example -->
* * <p> * In the following example the XSLT result would use the action's user property * instead of the action as it's base document and walk through it's properties. * The exposedValue uses an ognl expression to derive it's value. * </p> * * <pre> * <result name="success" type="xslt"> * <param name="location">foo.xslt</param> * <param name="exposedValue">user$</param> * </result> * </pre> * * * <b>This result type takes the following parameters: * * <!-- START SNIPPET: params --> * * <ul> * * <li>location (default) - the location to go to after execution. * * <li>parse - true by default. If set to false, the location param will * not be parsed for Ognl expressions.</li> * * <!-- * <li>matchingPattern - Pattern that matches only desired elements, by * default it matches everything.</li> * * <li>excludingPattern - Pattern that eliminates unwanted elements, by * default it matches none.</li> * --> * * </ul> * * <p> * <code>struts.properties related configuration: * </p> * <ul> * * <li>struts.xslt.nocache - Defaults to false. If set to true, disables * stylesheet caching. Good for development, bad for production.</li> * * </ul> * * <!-- END SNIPPET: params --> * * <b>Example: * * <pre> * <result name="success" type="xslt">foo.xslt</result> * <!-- END SNIPPET: example --> * */ public class XSLTResult implements Result { private static final long serialVersionUID = 6424691441777176763L; /** Log instance for this result. */ private static final Logger LOG = LoggerFactory.getLogger(XSLTResult.class); /** 'stylesheetLocation' parameter. Points to the xsl. */ public static final String DEFAULT_PARAM = "stylesheetLocation"; /** Cache of all tempaltes. */ private static final Map<String, Templates> templatesCache; static { templatesCache = new HashMap<String, Templates>(); } // Configurable Parameters /** Determines whether or not the result should allow caching. */ protected boolean noCache; /** Indicates the location of the xsl template. */ private String stylesheetLocation; /** Indicates the property name patterns which should be exposed to the xml. */ private String matchingPattern; /** Indicates the property name patterns which should be excluded from the xml. */ private String excludingPattern; /** Indicates the ognl expression respresenting the bean which is to be exposed as xml. */ private String exposedValue; private boolean parse; private AdapterFactory adapterFactory; public XSLTResult() { } public XSLTResult(String stylesheetLocation) { this(); setStylesheetLocation(stylesheetLocation); } @Inject(StrutsConstants.STRUTS_XSLT_NOCACHE) public void setNoCache(String val) { noCache = "true".equals(val); } /** * @deprecated Use #setStylesheetLocation(String) */ public void setLocation(String location) { setStylesheetLocation(location); } public void setStylesheetLocation(String location) { if (location == null) throw new IllegalArgumentException("Null location"); this.stylesheetLocation = location; } public String getStylesheetLocation() { return stylesheetLocation; } public String getExposedValue() { return exposedValue; } public void setExposedValue(String exposedValue) { this.exposedValue = exposedValue; } /** * @deprecated Since 2.1.1 */ public String getMatchingPattern() { return matchingPattern; } /** * @deprecated Since 2.1.1 */ public void setMatchingPattern(String matchingPattern) { this.matchingPattern = matchingPattern; } /** * @deprecated Since 2.1.1 */ public String getExcludingPattern() { return excludingPattern; } /** * @deprecated Since 2.1.1 */ public void setExcludingPattern(String excludingPattern) { this.excludingPattern = excludingPattern; } /** * If true, parse the stylesheet location for OGNL expressions. * * @param parse */ public void setParse(boolean parse) { this.parse = parse; } public void execute(ActionInvocation invocation) throws Exception { long startTime = System.currentTimeMillis(); String location = getStylesheetLocation(); if (parse) { ValueStack stack = ActionContext.getContext().getValueStack(); location = TextParseUtil.translateVariables(location, stack); } try { HttpServletResponse response = ServletActionContext.getResponse(); PrintWriter writer = response.getWriter(); // Create a transformer for the stylesheet. Templates templates = null; Transformer transformer; if (location != null) { templates = getTemplates(location); transformer = templates.newTransformer(); } else transformer = TransformerFactory.newInstance().newTransformer(); transformer.setURIResolver(getURIResolver()); transformer.setErrorListener(new ErrorListener() { public void error(TransformerException exception) throws TransformerException { throw new StrutsException("Error transforming result", exception); } public void fatalError(TransformerException exception) throws TransformerException { throw new StrutsException("Fatal error transforming result", exception); } public void warning(TransformerException exception) throws TransformerException { LOG.warn(exception.getMessage(), exception); } }); String mimeType; if (templates == null) mimeType = "text/xml"; // no stylesheet, raw xml else mimeType = templates.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE); if (mimeType == null) { // guess (this is a servlet, so text/html might be the best guess) mimeType = "text/html"; } response.setContentType(mimeType); Object result = invocation.getAction(); if (exposedValue != null) { ValueStack stack = invocation.getStack(); result = stack.findValue(exposedValue); } Source xmlSource = getDOMSourceForStack(result); // Transform the source XML to System.out. LOG.debug("xmlSource = " + xmlSource); transformer.transform(xmlSource, new StreamResult(writer)); writer.flush(); // ...and flush... if (LOG.isDebugEnabled()) { LOG.debug("Time:" + (System.currentTimeMillis() - startTime) + "ms"); } } catch (Exception e) { LOG.error("Unable to render XSLT Template, '" + location + "'", e); throw e; } } protected AdapterFactory getAdapterFactory() { if (adapterFactory == null) adapterFactory = new AdapterFactory(); return adapterFactory; } protected void setAdapterFactory(AdapterFactory adapterFactory) { this.adapterFactory = adapterFactory; } /** * Get the URI Resolver to be called by the processor when it encounters an xsl:include, xsl:import, or document() * function. The default is an instance of ServletURIResolver, which operates relative to the servlet context. */ protected URIResolver getURIResolver() { return new ServletURIResolver( ServletActionContext.getServletContext()); } protected Templates getTemplates(String path) throws TransformerException, IOException { String pathFromRequest = ServletActionContext.getRequest().getParameter("xslt.location"); if (pathFromRequest != null) path = pathFromRequest; if (path == null) throw new TransformerException("Stylesheet path is null"); Templates templates = templatesCache.get(path); if (noCache || (templates == null)) { synchronized (templatesCache) { URL resource = ServletActionContext.getServletContext().getResource(path); if (resource == null) { throw new TransformerException("Stylesheet " + path + " not found in resources."); } LOG.debug("Preparing XSLT stylesheet templates: " + path); TransformerFactory factory = TransformerFactory.newInstance(); factory.setURIResolver(getURIResolver()); templates = factory.newTemplates(new StreamSource(resource.openStream())); templatesCache.put(path, templates); } } return templates; } protected Source getDOMSourceForStack(Object value) throws IllegalAccessException, InstantiationException { return new DOMSource(getAdapterFactory().adaptDocument("result", value) ); } }

Other Struts examples (source code examples)

Here is a short list of links related to this Struts XSLTResult.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.