blob: 94abe32178a12a89ad5c17ae056932c3a38663fb [file] [log] [blame]
/*
* 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;
}
}
}