Thursday, August 15, 2013

OSB 11g XSLT multiple input parameters and the conversion error of org.apache.xmlbeans.impl.store.Xobj$DocumentXobj

OSB 11g XSLT expression editor allows you to enter multiple input parameters. The developer guide http://docs.oracle.com/cd/E23943_01/dev.1111/e15866/toc.htm (page 4-84) even contain a precise definition of each field with example. However, it doesn't work. You may see errors similar to this:
java.lang.RuntimeException: Invalid conversion from 'node-set' to 'org.apache.xmlbeans.impl.store.Xobj$DocumentXobj'

I see many frustrated bloggers posting questions. After my own long frustrating hours, i concluded it's an OSB bug. Good news is i also figured out a solution.

At the core, the issue is OSB passes in the parameter to XLST as org.apache.xmlbeans.impl.store.Xobj$DocumentXobj object (with the exception if the parameter is a simple string). But XSLT is expecting a NodeList.

Here is a quick solution. Compile this class, jar it up and copy it to your domain/lib directory:
package org.example.xslt;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
public class XObjUtil
{
  public NodeList getNodeList(Object o)
  { System.out.println("### getNodeList o class name="+o.getClass().getName());
org.w3c.dom.Document xo = (org.w3c.dom.Document)o;
Node n = (Node) xo;
NodeList nl = n.getChildNodes();
       return nl;
// lazy way:  return (((org.w3c.dom.Document)o)).getChildNodes();
 }
}

Here is my cheap script to compile, jar and copy the file:

javac -d . XObjUtil.java
del myxlstext.jar
jar cvf myxlstext.jar org
copy myxlstext.jar C:\Oracle\Middleware\user_projects\domains\osbserver\lib

On the OSB side, here is the XSLT what will parse the input parameter:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:client="http://xmlns.oracle.com/JCA_ResourceAdapter/foo/BPELProcess1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:XObj="http://www.oracle.com/XSL/Transform/java/org.example.xslt.XObjUtil"
exclude-result-prefixes="xsl client ">
<xsl:param name="car" />
<xsl:template match="/">
<client:process>
<xsl:variable name="x" select="XObj:new()" />
<xsl:variable name="obj" select="XObj:getNodeList($x, $car)" />
<client:input>
<xsl:value-of select="$obj/client:car1" />
</client:input>
<client:foo1>
<xsl:value-of select="$obj/client:car2" />
</client:foo1>
<client:foo2>
<xsl:value-of select="/client:barElem/client:bar2" />
</client:foo2>
</client:process>
</xsl:template>
</xsl:stylesheet>

My foo/bar/car sample is based on this cheap XSD
<?xml version="1.0" encoding="UTF-8" ?>
<schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://xmlns.oracle.com/JCA_ResourceAdapter/foo/BPELProcess1"
     xmlns:this="http://xmlns.oracle.com/JCA_ResourceAdapter/foo/BPELProcess1" xmlns="http://www.w3.org/2001/XMLSchema">
    <complexType name="fooType">
        <sequence>
            <element name="input" type="string"/>
            <element name="foo1" type="string"/>
            <element name="foo2" type="string"/>
        </sequence>
    </complexType>
    <complexType name="barType">
        <sequence>
            <element name="input" type="string"/>
            <element name="bar1" type="string"/>
            <element name="bar2" type="string"/>
        </sequence>
    </complexType>
    <complexType name="carType">
        <sequence>
            <element name="input" type="string"/>
            <element name="car1" type="string"/>
            <element name="car2" type="string"/>
        </sequence>
    </complexType>
    <element name="process" type="this:fooType"/>
    <element name="carElem" type="this:carType"/>
    <element name="barElem" type="this:barType"/>
    <element name="processResponse">
        <complexType>
            <sequence>
                <element name="result" type="string"/>
            </sequence>
        </complexType>
    </element>
</schema>

Here is a sample output from the OSB test console:

client:process xmlns:client="http://xmlns.oracle.com/JCA_ResourceAdapter/foo/BPELProcess1"xmlns:XObj="http://www.oracle.com/XSL/Transform/java/org.example.xslt.XObjUtil">
<client:input>car18</client:input>
<client:foo1>car29</client:foo1>
<client:foo2>bar23</client:foo2>
</client:process>

One interesting fact is this sample shows you a cheap way of extending XSLT without a full blown Xpath function project, see here http://yuanmengblog.blogspot.com/2013/04/break-comma-separated-string-into-array.html

BTW, very importantly, SOA and OSB uses different XSLT framework. OSB uses more org.w3 XML, SOA use "Oracle" own XML stuff. When SOA invokes XSLT, it passes in the parameter as type oracle.xml.parser.v2.XMLDocumentFragment, if ever want to play with that, the conversion is this:
 public NodeList getNodeList(oracle.xml.parser.v2.XMLDocumentFragment o)
 {
org.w3c.dom.NodeList nl = ((org.w3c.dom.Node)o).getChildNodes();
return  nl;
 }

Two other things you can explore:

1. instead of tossing the jar file into domain/lib directory, try if you can merely upload the jar to the proxy folder in OSB, treats it like a java callout jar. If that works, then you don't need to reboot OSB when change your jar file.

2. implement this as an Xpath function, then your JDev XSLT editor can pick it up. Of course, your XSLT syntax will be modified slightly as well. Have fun!

4 comments:

  1. Hi, this is not a bug in OSB, has to do with the xsl processor used in this case Xalan, if you use an editor like Oxygen and execute the test really get the same errror.

    Severity: fatal
    Description: org.apache.xpath.objects.XString can not be cast to org.apache.xpath.objects.XNodeSet - org.apache.xpath.objects.XString can not be cast to org.apache.xpath.objects.XNodeSet

    at the end the solution you pose is very viable


    For example when I use saxon-EE 9.4.0.4 this expect a node()

    ReplyDelete
  2. Hi Yuan

    Your java method signarue:
    public NodeList getNodeList(Object o)

    The XSLT call:


    Isn't there a parameter mismatch?

    ReplyDelete
    Replies
    1. This comment has been removed by the author.

      Delete
  3. Hi, the solution is here
    http://rickymax.wordpress.com/2014/12/15/how-work-with-xslt-input-parameters-in-oracle-service-bus/

    ReplyDelete