/* * 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.ode.bpel.elang.xpath10.runtime; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.transform.dom.DOMSource; import net.sf.saxon.dom.NodeWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.ode.bpel.common.FaultException; import org.apache.ode.bpel.elang.XslRuntimeUriResolver; import org.apache.ode.bpel.elang.xpath10.obj.OXPath10Expression; import org.apache.ode.bpel.elang.xpath10.obj.OXPath10ExpressionBPEL20; import org.apache.ode.bpel.explang.EvaluationContext; import org.apache.ode.bpel.explang.EvaluationException; import org.apache.ode.bpel.obj.OLink; import org.apache.ode.bpel.obj.OMessageVarType; import org.apache.ode.bpel.obj.OProcess; import org.apache.ode.bpel.obj.OScope; import org.apache.ode.bpel.obj.OVarType; import org.apache.ode.bpel.obj.OXsdTypeVarType; import org.apache.ode.bpel.obj.OXslSheet; import org.apache.ode.utils.DOMUtils; import org.apache.ode.utils.xsl.XslTransformHandler; import org.jaxen.Context; import org.jaxen.Function; import org.jaxen.FunctionCallException; import org.jaxen.FunctionContext; import org.jaxen.UnresolvableException; import org.jaxen.VariableContext; import org.jaxen.XPathFunctionContext; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * Implementation of the various JAXEN evaluation contexts in terms of the * {@link EvaluationContext}. */ class JaxenContexts implements FunctionContext, VariableContext { private static final Logger __log = LoggerFactory.getLogger(JaxenContexts.class); /** Static, thread-safe singleton implementing default XPath functions */ private static final FunctionContext __defaultXPathFunctions = XPathFunctionContext.getInstance(); private static final QName BOOLEAN = new QName("http://www.w3.org/2001/XMLSchema", "boolean"); private static final QName BYTE = new QName("http://www.w3.org/2001/XMLSchema", "byte"); private static final QName INT = new QName("http://www.w3.org/2001/XMLSchema", "int"); private static final QName INTEGER = new QName("http://www.w3.org/2001/XMLSchema", "integer"); private static final QName LONG = new QName("http://www.w3.org/2001/XMLSchema", "long"); private static final QName SHORT = new QName("http://www.w3.org/2001/XMLSchema", "short"); private static final QName UNSIGNED_INT = new QName("http://www.w3.org/2001/XMLSchema", "unsignedInt"); private static final QName UNSIGNED_SHORT = new QName("http://www.w3.org/2001/XMLSchema", "unsignedShort"); private static final QName UNSIGNED_BYTE = new QName("http://www.w3.org/2001/XMLSchema", "unsignedByte"); private static final QName DECIMAL = new QName("http://www.w3.org/2001/XMLSchema", "decimal"); private static final QName FLOAT = new QName("http://www.w3.org/2001/XMLSchema", "float"); private static final QName DOUBLE = new QName("http://www.w3.org/2001/XMLSchema", "double"); private OXPath10Expression _oxpath; private EvaluationContext _xpathEvalCtx; private Function _getVariableProperty; private Function _getVariableData; private Function _getLinkStatus; private Function _doXslTransform; private Map _extensionFunctions; public JaxenContexts(OXPath10Expression oxpath, Map extensionFunctions, EvaluationContext xpathEvalCtx) { _oxpath = oxpath; _xpathEvalCtx = xpathEvalCtx; _extensionFunctions = extensionFunctions; _getVariableProperty = new BpelVariablePropertyFunction(); _getVariableData = new BpelVariableDataFunction(); _getLinkStatus = new GetLinkStatusFunction(); _doXslTransform = new DoXslTransformFunction(); } /** * @see org.jaxen.FunctionContext#getFunction(java.lang.String, * java.lang.String, java.lang.String) */ public Function getFunction(String namespaceURI, String prefix, String localName) throws UnresolvableException { if (__log.isDebugEnabled()) { __log.debug("getFunction(" + namespaceURI + "," + prefix + "," + localName); } if ((namespaceURI != null)) { QName fnQName = new QName(namespaceURI, localName); if (fnQName.equals(_oxpath.getQname_getVariableProperty())) return _getVariableProperty; if (fnQName.equals(_oxpath.getQname_getVariableData())) return _getVariableData; if (fnQName.equals(_oxpath.getQname_getLinkStatus())) return _getLinkStatus; if (_oxpath instanceof OXPath10ExpressionBPEL20) { OXPath10ExpressionBPEL20 oxpath20 = (OXPath10ExpressionBPEL20) _oxpath; if (fnQName.equals(oxpath20.getQname_doXslTransform())) { return _doXslTransform; } } Function f = (Function)_extensionFunctions.get(localName); if (f != null) { return f; } } // Defer to the default XPath context. return __defaultXPathFunctions.getFunction(null, prefix, localName); } /** * @see org.jaxen.VariableContext#getVariableValue(java.lang.String, * java.lang.String, java.lang.String) */ public Object getVariableValue(String namespaceURI, String prefix, String localName) throws UnresolvableException { if(!(_oxpath instanceof OXPath10ExpressionBPEL20)){ throw new IllegalStateException("XPath variables not supported for bpel 1.1"); } // Custom variables if ("ode".equals(prefix)) { if ("pid".equals(localName)) { return _xpathEvalCtx.getProcessId(); } } OXPath10ExpressionBPEL20 expr = (OXPath10ExpressionBPEL20)_oxpath; if(expr.isIsJoinExpression()){ OLink olink = _oxpath.getLinks().get(localName); try { return _xpathEvalCtx.isLinkActive(olink) ? Boolean.TRUE : Boolean.FALSE; } catch (FaultException e) { throw new WrappedFaultException.JaxenUnresolvableException(e); } }else{ String varName; String partName; int dotloc = localName.indexOf('.'); if (dotloc == -1) { varName = localName; partName = null; } else { varName = localName.substring(0, dotloc); partName = localName.substring(dotloc + 1); } OScope.Variable variable = _oxpath.getVars().get(varName); OMessageVarType.Part part = partName == null ? null : ((OMessageVarType)variable.getType()).getParts().get(partName); try{ Node variableNode = _xpathEvalCtx.readVariable(variable, part); if (variableNode == null) throw new WrappedFaultException.JaxenUnresolvableException( new FaultException(variable.getOwner().getConstants().getQnSelectionFailure(), "Unknown variable " + localName)); OVarType type = variable.getType(); if (type instanceof OMessageVarType) { OMessageVarType.Part typePart = ((OMessageVarType)type).getParts().get(partName); if (typePart == null) { throw new WrappedFaultException.JaxenUnresolvableException( new FaultException(variable.getOwner().getConstants().getQnSelectionFailure(), "Unknown part " + partName + " for variable " + localName)); } type = typePart.getType(); } if (_xpathEvalCtx.narrowTypes() && type instanceof OXsdTypeVarType && ((OXsdTypeVarType)type).isSimple()) { String value = variableNode.getTextContent(); OXsdTypeVarType theType = (OXsdTypeVarType)type; // cast booleans to boolean if (BOOLEAN.equals(theType.getXsdType())) { return new Boolean(value) ; } // and numbers to numbers (XPath only understands Double, so Double it shall be. if (INT.equals(theType.getXsdType()) || UNSIGNED_SHORT.equals(theType.getXsdType()) || INTEGER.equals(theType.getXsdType()) || LONG.equals(theType.getXsdType()) || UNSIGNED_INT.equals(theType.getXsdType()) || SHORT.equals(theType.getXsdType()) || UNSIGNED_BYTE.equals(theType.getXsdType()) || BYTE.equals(theType.getXsdType()) || DECIMAL.equals(theType.getXsdType()) || FLOAT.equals(theType.getXsdType()) || DOUBLE.equals(theType.getXsdType()) ) { return new Double(value); } return value; } else { return variableNode; } }catch(FaultException e){ __log.error("bpws:getVariableValue threw FaultException", e); throw new WrappedFaultException.JaxenUnresolvableException(e); } } } /** * bpws:getVariableData() */ class BpelVariableDataFunction implements Function { public Object call(Context context, List args) throws FunctionCallException { if (__log.isDebugEnabled()) { __log.debug("call(context=" + context + " args=" + args + ")"); } String varname = (String) args.get(0); String partname = args.size() > 1 ? (String) args.get(1) : null; String xpathStr = args.size() > 2 ? (String)args.get(2) : null; OXPath10Expression.OSigGetVariableData sig = _oxpath.resolveGetVariableDataSig(varname,partname,xpathStr); if (sig == null) { String msg = "InternalError: Attempt to use an unknown getVariableData signature: " + args; __log.error(msg); throw new FunctionCallException(msg); } try { Node ret = _xpathEvalCtx.readVariable(sig.getVariable(), sig.getPart()); if (sig.getLocation() != null) ret = _xpathEvalCtx.evaluateQuery(ret, sig.getLocation()); if (__log.isDebugEnabled()) { __log.debug("bpws:getVariableData(" + args + ")' = " + ret); } return ret; } catch (FaultException e) { __log.error("bpws:getVariableData(" + args + ") threw FaultException", e); throw new WrappedFaultException.JaxenFunctionException(e); } catch (EvaluationException e) { __log.error("bpws:getVariableData(" + args + ") threw EvaluationException", e); if (e.getCause() instanceof FaultException) { throw new WrappedFaultException.JaxenFunctionException((FaultException) e.getCause()); } else { throw new FunctionCallException("SubLanguageException: Unable to evaluate query expression", e); } } } } /** * bpws:getVariableProperty() */ class BpelVariablePropertyFunction implements Function { public Object call(Context context, List args) throws FunctionCallException { if (args.size() != 2) { throw new FunctionCallException("missing required arguments"); } OScope.Variable var = _oxpath.getVars().get(args.get(0)); OProcess.OProperty property = _oxpath.getProperties().get(args.get(1)); if (__log.isDebugEnabled()) { __log.debug("function call:'bpws:getVariableProperty(" + var + "," + property + ")'"); } try { return _xpathEvalCtx.readMessageProperty(var, property); } catch (FaultException e) { __log.error("bpws:getVariableProperty(" + args + ") threw FaultException", e); throw new WrappedFaultException.JaxenFunctionException(e); } } } class GetLinkStatusFunction implements Function { public Object call(Context context, List args) throws FunctionCallException { assert args.size() == 1; OLink olink = _oxpath.getLinks().get(args.get(0)); try { return _xpathEvalCtx.isLinkActive(olink) ? Boolean.TRUE : Boolean.FALSE; } catch (FaultException e) { __log.error("bpws:getLinkStatus(" + args + ") threw FaultException", e); throw new WrappedFaultException.JaxenFunctionException(e); } } } class DoXslTransformFunction implements Function { public Object call(Context context, List args) throws FunctionCallException { assert args.size() >= 2; assert args.size() % 2 == 0; if (__log.isDebugEnabled()) { __log.debug("call(context=" + context + " args=" + args + ")"); } if(!(_oxpath instanceof OXPath10ExpressionBPEL20)) { throw new IllegalStateException("XPath function bpws:doXslTransform not supported in " + "BPEL 1.1!"); } Element varElmt; try { if (args.get(1) instanceof List) { List elmts = (List)args.get(1); if (elmts.size() != 1) throw new WrappedFaultException.JaxenFunctionException( new FaultException(_oxpath.getOwner().getConstants().getQnXsltInvalidSource(), "Second parameter of the bpws:doXslTransform function MUST point to a single " + "element node.")); varElmt = (Element) elmts.get(0); } else { if (args.get(1) instanceof NodeWrapper) varElmt = (Element) ((NodeWrapper)args.get(1)).getUnderlyingNode(); else varElmt = (Element) args.get(1); // varElmt = (Element) args.get(1); } } catch (ClassCastException e) { throw new WrappedFaultException.JaxenFunctionException( new FaultException(_oxpath.getOwner().getConstants().getQnXsltInvalidSource(), "Second parameter of the bpws:doXslTransform function MUST point to a single " + "element node.")); } URI xslUri; try { xslUri = new URI((String) args.get(0)); } catch (URISyntaxException use) { // Shouldn't happen, checked at compilation time throw new FunctionCallException("First parameter of the bpws:doXslTransform isn't a valid URI!", use); } OXslSheet xslSheet = _oxpath.getXslSheet(xslUri); // Shouldn't happen, checked at compilation time if (xslSheet == null) throw new FunctionCallException("Couldn't find the XSL sheet " + args.get(0) + ", process compilation or deployment was probably incomplete!"); if (!(varElmt instanceof Element)) { throw new WrappedFaultException.JaxenFunctionException( new FaultException(_oxpath.getOwner().getConstants().getQnXsltInvalidSource(), "Second parameter of the bpws:doXslTransform function MUST point to a single " + "element node.")); } HashMap<QName, Object> parametersMap = null; if (args.size() > 2) { parametersMap = new HashMap<QName, Object>(); for (int idx = 2; idx < args.size(); idx+=2) { QName keyQName = _oxpath.getNamespaceCtx().derefQName((String) args.get(idx)); parametersMap.put(keyQName, args.get(idx + 1)); } } Document varDoc = DOMUtils.newDocument(); varDoc.appendChild(varDoc.importNode(varElmt, true)); DOMSource source = new DOMSource(varDoc); Object result; XslRuntimeUriResolver resolver = new XslRuntimeUriResolver(_oxpath, _xpathEvalCtx.getBaseResourceURI()); XslTransformHandler.getInstance().cacheXSLSheet(_xpathEvalCtx.getProcessQName(), xslUri, xslSheet.getSheetBody(), resolver); try { result = XslTransformHandler.getInstance().transform(_xpathEvalCtx.getProcessQName(), xslUri, source, parametersMap, resolver); } catch (Exception e) { throw new WrappedFaultException.JaxenFunctionException( new FaultException(_oxpath.getOwner().getConstants().getQnSubLanguageExecutionFault(), e.toString())); } return result; } } }