From 8e45523e7a4f90d85aa62ae48ee0f2066889e624 Mon Sep 17 00:00:00 2001 From: Dave Ward Date: Thu, 26 Jul 2012 15:50:56 +0000 Subject: [PATCH] Merged V4.1-BUG-FIX to HEAD 39828: Merged V4.1 to V4.1-BUG-FIX 39827: Merged PATCHES/V4.0.2 to V4.1 39825: ALF-13453 / ALF-13844: Merged V3.4-BUG-FIX to PATCHES/V4.0.2 39823: ALF-13552, ALF-13978: Reverse merged the following revisions - won't fix due to regressions and not a serious vulnerability 35341: ALF-13552: Merged V4.0 to V3.4 35296: ALF-13453: Merged V4.0-BUG-FIX to V4.0 35295: Fix for ALF-13453: Remote Code Execution (can create reverse shell) 35304: ALF-13453: Extra fix to ensure xalan namespace isn't declared with global scope and can't be hijacked by an input stylesheet 35307: ALF-13453: Duplicated extra fix to duplicate code in XSLTRenderingEngine! 36101: ALF-13978: Merged V4.0-BUG-FIX to V3.4 36014: ALF-13844: XSLT Filtering Not 100% Secure - added more namespaces to the security filter. - verified that include/import uses the security filter. 36108: ALF-13978: Fixed compilation errors 39824: ALF-13552, ALF-13978: Fixed compilation errors git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@39829 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../web/forms/XSLTRenderingEngine.java | 1026 ++++++++--------- 1 file changed, 508 insertions(+), 518 deletions(-) diff --git a/source/java/org/alfresco/web/forms/XSLTRenderingEngine.java b/source/java/org/alfresco/web/forms/XSLTRenderingEngine.java index 35a686cf49..199bec815e 100644 --- a/source/java/org/alfresco/web/forms/XSLTRenderingEngine.java +++ b/source/java/org/alfresco/web/forms/XSLTRenderingEngine.java @@ -1,4 +1,4 @@ -/* +/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco @@ -14,521 +14,511 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . */ -package org.alfresco.web.forms; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.xml.transform.ErrorListener; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -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 org.alfresco.service.namespace.QName; -import org.apache.bsf.BSFManager; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.exception.ExceptionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.xml.dtm.ref.DTMNodeProxy; -import org.apache.xml.utils.Constants; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.traversal.NodeFilter; -import org.w3c.dom.traversal.NodeIterator; -import org.xml.sax.SAXException; - -import org.alfresco.util.XMLUtil; - -/** - * A rendering engine which uses xsl templates to render renditions of - * form instance data. - * - * @author Ariel Backenroth - */ -public class XSLTRenderingEngine - implements RenderingEngine -{ - - ///////////////////////////////////////////////////////////////////////////// - - public static class ProcessorMethodInvoker - { - private final static HashMap PROCESSOR_METHODS = - new HashMap(); - - public ProcessorMethodInvoker() { } - - private Object[] convertArguments(final Object[] arguments) - { - final List result = new LinkedList(); - for (int i = 0; i < arguments.length; i++) - { - LOGGER.debug("args[" + i + "] = " + arguments[i] + - "(" + (arguments[i] != null - ? arguments[i].getClass().getName() - : "null") + ")"); - if (arguments[i] == null || - arguments[i] instanceof String || - arguments[i] instanceof Number) - { - result.add(arguments[i]); - } - else if (arguments[i] instanceof DTMNodeProxy) - { - result.add(((DTMNodeProxy)arguments[i]).getStringValue()); - } - else if (arguments[i] instanceof Node) - { - LOGGER.debug("node type is " + ((Node)arguments[i]).getNodeType() + - " content " + ((Node)arguments[i]).getTextContent()); - result.add(((Node)arguments[i]).getNodeValue()); - } - else if (arguments[i] instanceof NodeIterator) - { - Node n = ((NodeIterator)arguments[i]).nextNode(); - while (n != null) - { - LOGGER.debug("iterated to node " + n + " type " + n.getNodeType() + - " value " + n.getNodeValue() + - " tc " + n.getTextContent() + - " nn " + n.getNodeName() + - " sv " + ((org.apache.xml.dtm.ref.DTMNodeProxy)n).getStringValue()); - if (n instanceof DTMNodeProxy) - { - result.add(((DTMNodeProxy)n).getStringValue()); - } - else - { - result.add(n); - } - n = ((NodeIterator)arguments[i]).nextNode(); - } - } - else - { - throw new IllegalArgumentException("unable to convert argument " + arguments[i]); - } + * along with Alfresco. If not, see . */ +package org.alfresco.web.forms; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.transform.ErrorListener; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +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 org.alfresco.service.namespace.QName; +import org.alfresco.util.XMLUtil; +import org.apache.bsf.BSFManager; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.xml.dtm.ref.DTMNodeProxy; +import org.apache.xml.utils.Constants; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; +import org.xml.sax.SAXException; + +/** + * A rendering engine which uses xsl templates to render renditions of + * form instance data. + * + * @author Ariel Backenroth + */ +public class XSLTRenderingEngine + implements RenderingEngine +{ + + ///////////////////////////////////////////////////////////////////////////// + + public static class ProcessorMethodInvoker + { + private final static HashMap PROCESSOR_METHODS = + new HashMap(); + + public ProcessorMethodInvoker() { } + + private Object[] convertArguments(final Object[] arguments) + { + final List result = new LinkedList(); + for (int i = 0; i < arguments.length; i++) + { + LOGGER.debug("args[" + i + "] = " + arguments[i] + + "(" + (arguments[i] != null + ? arguments[i].getClass().getName() + : "null") + ")"); + if (arguments[i] == null || + arguments[i] instanceof String || + arguments[i] instanceof Number) + { + result.add(arguments[i]); + } + else if (arguments[i] instanceof DTMNodeProxy) + { + result.add(((DTMNodeProxy)arguments[i]).getStringValue()); + } + else if (arguments[i] instanceof Node) + { + LOGGER.debug("node type is " + ((Node)arguments[i]).getNodeType() + + " content " + ((Node)arguments[i]).getTextContent()); + result.add(((Node)arguments[i]).getNodeValue()); + } + else if (arguments[i] instanceof NodeIterator) + { + Node n = ((NodeIterator)arguments[i]).nextNode(); + while (n != null) + { + LOGGER.debug("iterated to node " + n + " type " + n.getNodeType() + + " value " + n.getNodeValue() + + " tc " + n.getTextContent() + + " nn " + n.getNodeName() + + " sv " + ((org.apache.xml.dtm.ref.DTMNodeProxy)n).getStringValue()); + if (n instanceof DTMNodeProxy) + { + result.add(((DTMNodeProxy)n).getStringValue()); + } + else + { + result.add(n); + } + n = ((NodeIterator)arguments[i]).nextNode(); + } + } + else + { + throw new IllegalArgumentException("unable to convert argument " + arguments[i]); + } + } + + return result.toArray(new Object[result.size()]); + } + + public Object invokeMethod(final String id, Object[] arguments) + throws Exception + { + if (!PROCESSOR_METHODS.containsKey(id)) + { + throw new NullPointerException("unable to find method " + id); + } + + final TemplateProcessorMethod method = PROCESSOR_METHODS.get(id); + arguments = this.convertArguments(arguments); + LOGGER.debug("invoking " + id + " with " + arguments.length); + + Object result = method.exec(arguments); + LOGGER.debug(id + " returned a " + result); + if (result == null) + { + return null; + } + else if (result.getClass().isArray() && + Node.class.isAssignableFrom(result.getClass().getComponentType())) + { + LOGGER.debug("converting " + result + " to a node iterator"); + final Node[] array = (Node[])result; + return new NodeIterator() + { + private int index = 0; + private boolean detached = false; + + public void detach() + { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("detaching NodeIterator"); + this.detached = true; + } + + public boolean getExpandEntityReferences() { return true; } + public int getWhatToShow() { return NodeFilter.SHOW_ALL; } + + public Node getRoot() + { + return (array.length == 0 + ? null + : array[0].getOwnerDocument().getDocumentElement()); + } + + public NodeFilter getFilter() + { + return new NodeFilter() + { + public short acceptNode(final Node n) + { + return NodeFilter.FILTER_ACCEPT; + } + }; + } + + public Node nextNode() + throws DOMException + { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("NodeIterator.nextNode(" + index + ")"); + if (this.detached) + throw new DOMException(DOMException.INVALID_STATE_ERR, null); + return index == array.length ? null : array[index++]; + } + + public Node previousNode() + throws DOMException + { + if (LOGGER.isDebugEnabled()) + LOGGER.debug("NodeIterator.previousNode(" + index + ")"); + if (this.detached) + throw new DOMException(DOMException.INVALID_STATE_ERR, null); + return index == -1 ? null : array[index--]; + } + }; + } + else if (result instanceof String || + result instanceof Number || + result instanceof Node) + { + LOGGER.debug("returning " + result + " as is"); + return result; + } + else + { + throw new IllegalArgumentException("unable to convert " + result.getClass().getName()); + } + } + } + + ///////////////////////////////////////////////////////////////////////////// + + private static final Log LOGGER = LogFactory.getLog(XSLTRenderingEngine.class); + + public XSLTRenderingEngine() { } + + public String getName() { return "XSLT"; } + + public String getDefaultTemplateFileExtension() { return "xsl"; } + + /** + * Adds a script element to the xsl which makes static methods on this + * object available to the xsl tempalte. + * + * @param xslTemplate the xsl template + */ + protected List addScripts(final Map model, + final Document xslTemplate) + { + final Map>> methods = + new HashMap>>(); + for (final Map.Entry entry : model.entrySet()) + { + if (entry.getValue() instanceof TemplateProcessorMethod) + { + final String prefix = QName.splitPrefixedQName(entry.getKey().toPrefixString())[0]; + final QName qn = QName.createQName(entry.getKey().getNamespaceURI(), + prefix); + if (!methods.containsKey(qn)) + { + methods.put(qn, new LinkedList()); + } + methods.get(qn).add(entry); + } + } + + final Element docEl = xslTemplate.getDocumentElement(); + final String XALAN_NS = Constants.S_BUILTIN_EXTENSIONS_URL; + final String XALAN_NS_PREFIX = "xalan"; + docEl.setAttribute("xmlns:" + XALAN_NS_PREFIX, XALAN_NS); + + final Set excludePrefixes = new HashSet(); + if (docEl.hasAttribute("exclude-result-prefixes")) + { + excludePrefixes.addAll(Arrays.asList(docEl.getAttribute("exclude-result-prefixes").split(" "))); + } + excludePrefixes.add(XALAN_NS_PREFIX); + + final List result = new LinkedList(); + for (QName ns : methods.keySet()) + { + final String prefix = ns.getLocalName(); + docEl.setAttribute("xmlns:" + prefix, ns.getNamespaceURI()); + excludePrefixes.add(prefix); + + final Element compEl = xslTemplate.createElementNS(XALAN_NS, + XALAN_NS_PREFIX + ":component"); + compEl.setAttribute("prefix", prefix); + docEl.appendChild(compEl); + String functionNames = null; + final Element scriptEl = xslTemplate.createElementNS(XALAN_NS, + XALAN_NS_PREFIX + ":script"); + scriptEl.setAttribute("lang", "javascript"); + final StringBuilder js = + new StringBuilder("var _xsltp_invoke = java.lang.Class.forName('" + ProcessorMethodInvoker.class.getName() + + "').newInstance();\n" + + "function _xsltp_to_java_array(js_array) {\n" + + "var java_array = java.lang.reflect.Array.newInstance(java.lang.Object, js_array.length);\n" + + "for (var i = 0; i < js_array.length; i++) { java_array[i] = js_array[i]; }\n" + + "return java_array; }\n"); + for (final Map.Entry entry : methods.get(ns)) + { + if (functionNames == null) + { + functionNames = entry.getKey().getLocalName(); + } + else + { + functionNames += " " + entry.getKey().getLocalName(); + } + final String id = entry.getKey().getLocalName() + entry.getValue().hashCode(); + js.append("function " + entry.getKey().getLocalName() + + "() { return _xsltp_invoke.invokeMethod('" + id + + "', _xsltp_to_java_array(arguments)); }\n"); + ProcessorMethodInvoker.PROCESSOR_METHODS.put(id, (TemplateProcessorMethod)entry.getValue()); + result.add(id); + } + LOGGER.debug("generated JavaScript bindings:\n" + js); + scriptEl.appendChild(xslTemplate.createTextNode(js.toString())); + compEl.setAttribute("functions", functionNames); + compEl.appendChild(scriptEl); + } + docEl.setAttribute("exclude-result-prefixes", + StringUtils.join(excludePrefixes.toArray(new String[excludePrefixes.size()]), " ")); + return result; + } + + /** + * Adds the specified parameters to the xsl template as variables within the + * alfresco namespace. + * + * @param model the variables to place within the xsl template + * @param xslTemplate the xsl template + */ + protected void addParameters(final Map model, + final Document xslTemplate) + { + final Element docEl = xslTemplate.getDocumentElement(); + final String XSL_NS = docEl.getNamespaceURI(); + final String XSL_NS_PREFIX = docEl.getPrefix(); + + for (Map.Entry e : model.entrySet()) + { + if (RenderingEngine.ROOT_NAMESPACE.equals(e.getKey())) + { + continue; + } + final Element el = xslTemplate.createElementNS(XSL_NS, XSL_NS_PREFIX + ":variable"); + el.setAttribute("name", e.getKey().toPrefixString()); + final Object o = e.getValue(); + if (o instanceof String || o instanceof Number || o instanceof Boolean) + { + el.appendChild(xslTemplate.createTextNode(o.toString())); + docEl.insertBefore(el, docEl.getFirstChild()); + } + } + } + + protected Source getXMLSource(final Map model) + { + if (!model.containsKey(RenderingEngine.ROOT_NAMESPACE)) + { + return null; + } + final Object o = model.get(RenderingEngine.ROOT_NAMESPACE); + if (!(o instanceof Document)) + { + throw new IllegalArgumentException("expected root namespace object to be a " + Document.class.getName() + + ". found a " + o.getClass().getName()); + } + return new DOMSource((Document)o); + } + + public void render(final Map model, + final RenderingEngineTemplate ret, + final OutputStream out) + throws IOException, + RenderingEngine.RenderingException, + SAXException + { + this.render(model, ret, new StreamResult(out)); + } + + public void render(final Map model, + final RenderingEngineTemplate ret, + final Result result) + throws IOException, + RenderingEngine.RenderingException, + SAXException + { + System.setProperty("org.apache.xalan.extensions.bsf.BSFManager", + BSFManager.class.getName()); + Document xslTemplate = null; + try + { + xslTemplate = XMLUtil.parse(ret.getInputStream()); + } + catch (final SAXException sax) + { + throw new RenderingEngine.RenderingException(sax); + } + this.addScripts(model, xslTemplate); + this.addParameters(model, xslTemplate); + + final LinkedList errors = new LinkedList(); + final ErrorListener errorListener = new ErrorListener() + { + public void error(final TransformerException te) + throws TransformerException + { + LOGGER.debug("error " + te.getMessageAndLocation()); + errors.add(te); + } + + public void fatalError(final TransformerException te) + throws TransformerException + { + LOGGER.debug("fatalError " + te.getMessageAndLocation()); + throw te; + } + + public void warning(final TransformerException te) + throws TransformerException + { + LOGGER.debug("warning " + te.getMessageAndLocation()); + errors.add(te); + } + }; + + // create a uri resolver to resolve document() calls to the virtualized + // web application + final URIResolver uriResolver = new URIResolver() + { + public Source resolve(final String href, String base) + throws TransformerException + { + LOGGER.debug("request to resolve href " + href + + " using base " + base); + final RenderingEngine.TemplateResourceResolver trr = (RenderingEngine.TemplateResourceResolver) + model.get(RenderingEngineTemplateImpl.PROP_RESOURCE_RESOLVER); + + InputStream in = null; + try + { + in = trr.resolve(href); + } + catch (Exception e) + { + throw new TransformerException("unable to load " + href, e); + } + + if (in == null) + { + throw new TransformerException("unable to resolve href " + href); + } + + try + { + final Document d = XMLUtil.parse(in); + if (LOGGER.isDebugEnabled()) + LOGGER.debug("loaded " + XMLUtil.toString(d)); + return new DOMSource(d); + } + catch (Exception e) + { + throw new TransformerException("unable to load " + href, e); + } + } + }; + + Source xmlSource = this.getXMLSource(model); + + Transformer t = null; + try + { + final TransformerFactory tf = TransformerFactory.newInstance(); + tf.setErrorListener(errorListener); + tf.setURIResolver(uriResolver); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("xslTemplate: \n" + XMLUtil.toString(xslTemplate)); + } + + t = tf.newTransformer(new DOMSource(xslTemplate)); + + if (errors.size() != 0) + { + final StringBuilder msg = new StringBuilder("errors encountered creating tranformer ... \n"); + for (TransformerException te : errors) + { + msg.append(te.getMessageAndLocation()).append("\n"); + } + throw new RenderingEngine.RenderingException(msg.toString()); + } + + t.setErrorListener(errorListener); + t.setURIResolver(uriResolver); + t.setParameter("versionParam", "2.0"); + } + catch (TransformerConfigurationException tce) + { + LOGGER.error(tce); + throw new RenderingEngine.RenderingException(tce); + } + + try + { + t.transform(xmlSource, result); + } + catch (TransformerException te) + { + LOGGER.error(te.getMessageAndLocation()); + throw new RenderingEngine.RenderingException(te); + } + catch (Exception e) + { + LOGGER.error("unexpected error " + e); + throw new RenderingEngine.RenderingException(e); + } + + if (errors.size() != 0) + { + final StringBuilder msg = new StringBuilder("errors encountered during transformation ... \n"); + for (TransformerException te : errors) + { + msg.append(te.getMessageAndLocation()).append("\n"); } - - return result.toArray(new Object[result.size()]); - } - - public Object invokeMethod(final String id, Object[] arguments) - throws Exception - { - if (!PROCESSOR_METHODS.containsKey(id)) - { - throw new NullPointerException("unable to find method " + id); - } - - final TemplateProcessorMethod method = PROCESSOR_METHODS.get(id); - arguments = this.convertArguments(arguments); - LOGGER.debug("invoking " + id + " with " + arguments.length); - - Object result = method.exec(arguments); - LOGGER.debug(id + " returned a " + result); - if (result == null) - { - return null; - } - else if (result.getClass().isArray() && - Node.class.isAssignableFrom(result.getClass().getComponentType())) - { - LOGGER.debug("converting " + result + " to a node iterator"); - final Node[] array = (Node[])result; - return new NodeIterator() - { - private int index = 0; - private boolean detached = false; - - public void detach() - { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("detaching NodeIterator"); - this.detached = true; - } - - public boolean getExpandEntityReferences() { return true; } - public int getWhatToShow() { return NodeFilter.SHOW_ALL; } - - public Node getRoot() - { - return (array.length == 0 - ? null - : array[0].getOwnerDocument().getDocumentElement()); - } - - public NodeFilter getFilter() - { - return new NodeFilter() - { - public short acceptNode(final Node n) - { - return NodeFilter.FILTER_ACCEPT; - } - }; - } - - public Node nextNode() - throws DOMException - { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("NodeIterator.nextNode(" + index + ")"); - if (this.detached) - throw new DOMException(DOMException.INVALID_STATE_ERR, null); - return index == array.length ? null : array[index++]; - } - - public Node previousNode() - throws DOMException - { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("NodeIterator.previousNode(" + index + ")"); - if (this.detached) - throw new DOMException(DOMException.INVALID_STATE_ERR, null); - return index == -1 ? null : array[index--]; - } - }; - } - else if (result instanceof String || - result instanceof Number || - result instanceof Node) - { - LOGGER.debug("returning " + result + " as is"); - return result; - } - else - { - throw new IllegalArgumentException("unable to convert " + result.getClass().getName()); - } - } - } - - ///////////////////////////////////////////////////////////////////////////// - - private static final Log LOGGER = LogFactory.getLog(XSLTRenderingEngine.class); - - public XSLTRenderingEngine() { } - - public String getName() { return "XSLT"; } - - public String getDefaultTemplateFileExtension() { return "xsl"; } - - /** - * Adds a script element to the xsl which makes static methods on this - * object available to the xsl tempalte. - * - * @param xslTemplate the xsl template - */ - protected List addScripts(final Map model, - final Document xslTemplate) - { - final Map>> methods = - new HashMap>>(); - for (final Map.Entry entry : model.entrySet()) - { - if (entry.getValue() instanceof TemplateProcessorMethod) - { - final String prefix = QName.splitPrefixedQName(entry.getKey().toPrefixString())[0]; - final QName qn = QName.createQName(entry.getKey().getNamespaceURI(), - prefix); - if (!methods.containsKey(qn)) - { - methods.put(qn, new LinkedList()); - } - methods.get(qn).add(entry); - } - } - - final Element docEl = xslTemplate.getDocumentElement(); - final String XALAN_NS = Constants.S_BUILTIN_EXTENSIONS_URL; - final String XALAN_NS_PREFIX = "xalan"; - - final Set excludePrefixes = new HashSet(); - if (docEl.hasAttribute("exclude-result-prefixes")) - { - excludePrefixes.addAll(Arrays.asList(docEl.getAttribute("exclude-result-prefixes").split(" "))); - } - - final List result = new LinkedList(); - for (QName ns : methods.keySet()) - { - final String prefix = ns.getLocalName(); - docEl.setAttribute("xmlns:" + prefix, ns.getNamespaceURI()); - excludePrefixes.add(prefix); - - final Element compEl = xslTemplate.createElementNS(XALAN_NS, - XALAN_NS_PREFIX + ":component"); - compEl.setAttribute("prefix", prefix); - docEl.appendChild(compEl); - String functionNames = null; - final Element scriptEl = xslTemplate.createElementNS(XALAN_NS, - XALAN_NS_PREFIX + ":script"); - scriptEl.setAttribute("lang", "javascript"); - final StringBuilder js = - new StringBuilder("var _xsltp_invoke = java.lang.Class.forName('" + ProcessorMethodInvoker.class.getName() + - "').newInstance();\n" + - "function _xsltp_to_java_array(js_array) {\n" + - "var java_array = java.lang.reflect.Array.newInstance(java.lang.Object, js_array.length);\n" + - "for (var i = 0; i < js_array.length; i++) { java_array[i] = js_array[i]; }\n" + - "return java_array; }\n"); - for (final Map.Entry entry : methods.get(ns)) - { - if (functionNames == null) - { - functionNames = entry.getKey().getLocalName(); - } - else - { - functionNames += " " + entry.getKey().getLocalName(); - } - final String id = entry.getKey().getLocalName() + entry.getValue().hashCode(); - js.append("function " + entry.getKey().getLocalName() + - "() { return _xsltp_invoke.invokeMethod('" + id + - "', _xsltp_to_java_array(arguments)); }\n"); - ProcessorMethodInvoker.PROCESSOR_METHODS.put(id, (TemplateProcessorMethod)entry.getValue()); - result.add(id); - } - LOGGER.debug("generated JavaScript bindings:\n" + js); - scriptEl.appendChild(xslTemplate.createTextNode(js.toString())); - compEl.setAttribute("functions", functionNames); - compEl.appendChild(scriptEl); - } - docEl.setAttribute("exclude-result-prefixes", - StringUtils.join(excludePrefixes.toArray(new String[excludePrefixes.size()]), " ")); - return result; - } - - /** - * Adds the specified parameters to the xsl template as variables within the - * alfresco namespace. - * - * @param model the variables to place within the xsl template - * @param xslTemplate the xsl template - */ - protected void addParameters(final Map model, - final Document xslTemplate) - { - final Element docEl = xslTemplate.getDocumentElement(); - final String XSL_NS = docEl.getNamespaceURI(); - final String XSL_NS_PREFIX = docEl.getPrefix(); - - for (Map.Entry e : model.entrySet()) - { - if (RenderingEngine.ROOT_NAMESPACE.equals(e.getKey())) - { - continue; - } - final Element el = xslTemplate.createElementNS(XSL_NS, XSL_NS_PREFIX + ":variable"); - el.setAttribute("name", e.getKey().toPrefixString()); - final Object o = e.getValue(); - if (o instanceof String || o instanceof Number || o instanceof Boolean) - { - el.appendChild(xslTemplate.createTextNode(o.toString())); - docEl.insertBefore(el, docEl.getFirstChild()); - } - } - } - - protected Source getXMLSource(final Map model) - { - if (!model.containsKey(RenderingEngine.ROOT_NAMESPACE)) - { - return null; - } - final Object o = model.get(RenderingEngine.ROOT_NAMESPACE); - if (!(o instanceof Document)) - { - throw new IllegalArgumentException("expected root namespace object to be a " + Document.class.getName() + - ". found a " + o.getClass().getName()); - } - return new DOMSource((Document)o); - } - - public void render(final Map model, - final RenderingEngineTemplate ret, - final OutputStream out) - throws IOException, - RenderingEngine.RenderingException, - SAXException - { - this.render(model, ret, new StreamResult(out)); - } - - public void render(final Map model, - final RenderingEngineTemplate ret, - final Result result) - throws IOException, - RenderingEngine.RenderingException, - SAXException - { - System.setProperty("org.apache.xalan.extensions.bsf.BSFManager", - BSFManager.class.getName()); - Document xslTemplate = null; - try - { - xslTemplate = XMLUtil.secureParseXSL(ret.getInputStream()); - } - catch (final SAXException sax) - { - throw new RenderingEngine.RenderingException(sax); - } - this.addScripts(model, xslTemplate); - this.addParameters(model, xslTemplate); - - final LinkedList errors = new LinkedList(); - final ErrorListener errorListener = new ErrorListener() - { - public void error(final TransformerException te) - throws TransformerException - { - LOGGER.debug("error " + te.getMessageAndLocation()); - errors.add(te); - } - - public void fatalError(final TransformerException te) - throws TransformerException - { - LOGGER.debug("fatalError " + te.getMessageAndLocation()); - throw te; - } - - public void warning(final TransformerException te) - throws TransformerException - { - LOGGER.debug("warning " + te.getMessageAndLocation()); - errors.add(te); - } - }; - - // create a uri resolver to resolve document() calls to the virtualized - // web application - final URIResolver uriResolver = new URIResolver() - { - public Source resolve(final String href, String base) - throws TransformerException - { - LOGGER.debug("request to resolve href " + href + - " using base " + base); - final RenderingEngine.TemplateResourceResolver trr = (RenderingEngine.TemplateResourceResolver) - model.get(RenderingEngineTemplateImpl.PROP_RESOURCE_RESOLVER); - - InputStream in = null; - try - { - in = trr.resolve(href); - } - catch (Exception e) - { - throw new TransformerException("unable to load " + href, e); - } - - if (in == null) - { - throw new TransformerException("unable to resolve href " + href); - } - - try - { - final Document d = XMLUtil.secureParseXSL(in); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("loaded " + XMLUtil.toString(d)); - return new DOMSource(d); - } - catch (Exception e) - { - throw new TransformerException("unable to load " + href, e); - } - } - }; - - Source xmlSource = this.getXMLSource(model); - - Transformer t = null; - try - { - final TransformerFactory tf = TransformerFactory.newInstance(); - tf.setErrorListener(errorListener); - tf.setURIResolver(uriResolver); - - if (LOGGER.isDebugEnabled()) - { - LOGGER.debug("xslTemplate: \n" + XMLUtil.toString(xslTemplate)); - } - - t = tf.newTransformer(new DOMSource(xslTemplate)); - - if (errors.size() != 0) - { - final StringBuilder msg = new StringBuilder("errors encountered creating tranformer ... \n"); - for (TransformerException te : errors) - { - msg.append(te.getMessageAndLocation()).append("\n"); - String cause = ExceptionUtils.getRootCauseMessage(te); - if (cause != null) - { - msg.append(" caused by: " + cause); - } - } - throw new RenderingEngine.RenderingException(msg.toString()); - } - - t.setErrorListener(errorListener); - t.setURIResolver(uriResolver); - t.setParameter("versionParam", "2.0"); - } - catch (TransformerConfigurationException tce) - { - LOGGER.error(tce); - throw new RenderingEngine.RenderingException(tce); - } - - try - { - t.transform(xmlSource, result); - } - catch (TransformerException te) - { - LOGGER.error(te.getMessageAndLocation()); - throw new RenderingEngine.RenderingException(te); - } - catch (Exception e) - { - LOGGER.error("unexpected error " + e); - throw new RenderingEngine.RenderingException(e); - } - - if (errors.size() != 0) - { - final StringBuilder msg = new StringBuilder("errors encountered during transformation ... \n"); - for (TransformerException te : errors) - { - msg.append(te.getMessageAndLocation()).append("\n"); - String cause = ExceptionUtils.getRootCauseMessage(te); - if (cause != null) - { - msg.append(" caused by: " + cause); - } - } - throw new RenderingEngine.RenderingException(msg.toString()); - } - } -} + throw new RenderingEngine.RenderingException(msg.toString()); + } + } +}