IBM webMethods Hybrid Integration

IBM webMethods Hybrid Integration

Join this online group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only
  • 1.  Making my own XSLT transformer

    Posted Thu March 26, 2009 04:38 PM

    I’ve tried to create my own xslt transformer that’s more lightweight than the built in service and that is also automatically embed the stringtobytes/bytetostring function.

    Everything went fine until I tried a XSL file with Java extension, like :

    <xsl:stylesheet xmlns:xsl = “XSLT Namespace
    xmlns:xalan = “Xalan-Java version 1
    xmlns:Output = “java://com.wm.pkg.xslt.extension.Output”
    exclude-result-prefixes = “xalan xsl Output” version = “1.0”>

    And then somewhere in the XML (just an exemple from an actual file, it runs OK in the built in transform service):
    <xsl:value-of select = “Output:put($output, ‘mail’, string($mail))”/>

    I get the exception:
    "
    java.lang.NoSuchMethodException: For extension function, could not find method java.lang.String.put([ExpressionContext,] #STRING, #STRING).
    "

    This is the java code I made so far:

    //get input var
    IDataHashCursor idc = pipeline.getHashCursor(); 
    
    idc.first( "stylesheetSystemId" );
    String stylesheetSystemId = (String)idc.getValue();
    
    idc.first( "document" );
    String document = (String)idc.getValue();
    
    idc.first( "encoding" );
    String encoding = (String)idc.getValue();
    
    idc.first( "xslParamInput" );
    IData xslParamInput = (IData)idc.getValue();
    IDataHashCursor idcParamInput = null;
    if(xslParamInput != null)
    {
    idcParamInput = xslParamInput.getHashCursor(); 
    }
    
    try{
    
    //transform input string into bytes and then into InputStream and then into Source
    byte[] documentBytes = document.getBytes(encoding);
    java.io.ByteArrayInputStream documentInputStream = new java.io.ByteArrayInputStream(documentBytes);
    javax.xml.transform.stream.StreamSource documentSource = new javax.xml.transform.stream.StreamSource(documentInputStream);
    
    //create the object that will hold the output
    java.io.CharArrayWriter transformedDocWriter = new java.io.CharArrayWriter();
    javax.xml.transform.stream.StreamResult transformedDocResult = new javax.xml.transform.stream.StreamResult(transformedDocWriter);
    
    //create the XSL factory
    javax.xml.transform.TransformerFactory fabrique = javax.xml.transform.TransformerFactory.newInstance();
    
    //create the XSL template file
    java.io.File stylesheet = new java.io.File(stylesheetSystemId);
    javax.xml.transform.stream.StreamSource stylesource = new javax.xml.transform.stream.StreamSource(stylesheet);
    
    
    
    javax.xml.transform.Templates template = fabrique.newTemplates(stylesource);
    
    
    //get the transformer for this template
    javax.xml.transform.Transformer transformer = template.newTransformer();
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, encoding);
    
    //set input parameters (if applicable)
    if(idcParamInput != null)
    {
    idcParamInput.first();
    do
    {
    transformer.setParameter(idcParamInput.getKey(), idcParamInput.getValue());
    }
    while(idcParamInput.next());
    
    //destory the cursor, now useless:
    idcParamInput.destroy();
    }
    
    //transform
    transformer.transform(documentSource, transformedDocResult);
    
    //get output parameters (if any); create a webMethods document
    //and set it in the pipeline
    java.util.Properties p = transformer.getOutputProperties();
    if(p!=null)
    {
    java.util.Enumeration keys = p.keys();
    while (keys.hasMoreElements()) {
    Object key = keys.nextElement();
    
    //just for test we insert the key of the output object in the pipeline
    idc.first();
    idc.insertAfter("transformedDocument", (String)key);
    }
    }
    
    //access the result
    String transformedDocument = ((java.io.CharArrayWriter)transformedDocResult.getWriter()).toString();
    //set it in the pipeline
    idc.first();
    idc.insertAfter("transformedDocument", transformedDocument);
    
    }
    catch(Exception e){
    
    idc.first();
    idc.insertAfter("output", e.getMessage());
    
    }
    
    //destroy the pipeline cursor
    idc.destroy();

    Does someone know what the cause is?

    Thanks!


    #webMethods
    #Flow-and-Java-services
    #Integration-Server-and-ESB


  • 2.  RE: Making my own XSLT transformer

    Posted Thu March 26, 2009 06:18 PM

    Have you considered creating a wrapper service in FLOW? If what you’re after is a service that accepts and returns a string form of the input and output documents, you can easily create a service to accept the same parms as what you have above and do something like this:

    stringToBytes
    transformSerialXML
    bytesToString

    Is there more that you want/need to do? I offer this as an alternative because you’re replicating what transformSerialXML does, just with slightly different inputs and outputs.


    #Integration-Server-and-ESB
    #Flow-and-Java-services
    #webMethods


  • 3.  RE: Making my own XSLT transformer

    Posted Fri March 27, 2009 10:04 AM

    That’s true. Well in fact I wanted to do this and then benchmark the 2 solutions.
    There has to be a way to do this in Java anyway; so I’ll be glad to make this stuff work.


    #webMethods
    #Integration-Server-and-ESB
    #Flow-and-Java-services


  • 4.  RE: Making my own XSLT transformer

    Posted Fri March 27, 2009 08:02 PM

    There are a couple of issues, which are obscured by the fact that the samples in the WmXSLT package work.

    To help explain, we’ll look at two lines from the dateInt.xsl sample found in pub/samples/xdocs within the WmXSLT package.

    xmlns:Output = “java://com.wm.pkg.xslt.extension.Output”

    The line above declares a namespace and identifies the fully-qualified name of the Java class to use. Alas, in this case, this class does not exist. The sample still works, however, because the classname is never referenced within the XSL to create a new “Output” object. This leads us to the second line of interest.

    xsl:value-of select=“Output:put($output, ‘year’, $year)”

    This indicates to invoke the ‘put’ method on the $output object instance. The sample doesn’t create an instance of the Output class to assign to $output so how does this work?

    It works because transformSerialXML creates an instance of com.wm.pkg.xslt.extension.OutputMap and sets the “output” parameter (declared in the XSL but never initialized) by calling transformer.setParameter.

    The $output object is thus initialized with an OutputMap object. This class, which extends java.util.HashMap, has a put method. The XSL processor can successfully invoke the method (via reflection).

    Lastly, after the transform method is called, transformSerialXML iterates over the output object that it passed to the transform and puts each output key/value pair into the pipeline under the xslParamOutput document.

    Below is the Java code modified to create and process the output parameter.

    [highlight=java]
    IDataHashCursor idc = pipeline.getHashCursor();
    idc.first( “stylesheetSystemId” );
    String stylesheetSystemId = (String)idc.getValue();
    idc.first( “document” );
    String document = (String)idc.getValue();
    idc.first( “encoding” );
    String encoding = (String)idc.getValue();
    idc.first( “xslParamInput” );
    IData xslParamInput = (IData)idc.getValue();
    IDataHashCursor idcParamInput = null;
    if(xslParamInput != null)
    {
    idcParamInput = xslParamInput.getHashCursor();
    }
    try{
    //transform input string into bytes and then into InputStream and then into Source
    byte documentBytes = document.getBytes(encoding);
    java.io.ByteArrayInputStream documentInputStream = new java.io.ByteArrayInputStream(documentBytes);
    javax.xml.transform.stream.StreamSource documentSource = new javax.xml.transform.stream.StreamSource(documentInputStream);
    //create the object that will hold the output
    java.io.CharArrayWriter transformedDocWriter = new java.io.CharArrayWriter();
    javax.xml.transform.stream.StreamResult transformedDocResult = new javax.xml.transform.stream.StreamResult(transformedDocWriter);

    //create the XSL factory
    javax.xml.transform.TransformerFactory fabrique = javax.xml.transform.TransformerFactory.newInstance();

    //create the XSL template file
    java.io.File stylesheet = new java.io.File(stylesheetSystemId);
    javax.xml.transform.stream.StreamSource stylesource = new javax.xml.transform.stream.StreamSource(stylesheet);

    javax.xml.transform.Templates template = fabrique.newTemplates(stylesource);

    //get the transformer for this template
    javax.xml.transform.Transformer transformer = template.newTransformer();
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, “yes”);
    transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, encoding);
    //set input parameters (if applicable)
    if(idcParamInput != null)
    {
    idcParamInput.first();
    do
    {
    transformer.setParameter(idcParamInput.getKey(), idcParamInput.getValue());
    }
    while(idcParamInput.next());

    //destory the cursor, now useless:
    idcParamInput.destroy();
    

    }

    // The next 2 lines are new
    java.util.HashMap output = new java.util.HashMap();
    transformer.setParameter(“output”, output);

    //transform
    transformer.transform(documentSource, transformedDocResult);

    // This output handling block is new
    if(!output.isEmpty())
    {
    IData xslParamOutput = IDataFactory.create();
    IDataCursor paramCursor = xslParamOutput.getCursor();
    java.util.Iterator keys = output.keySet().iterator();
    Object name = null;
    Object value = null;
    for(; keys.hasNext(); paramCursor.insertAfter((String)name, value))
    {
    name = keys.next();
    value = output.get(name);
    }
    idc.insertAfter(“xslParamOutput”, xslParamOutput);
    }

    //access the result
    String transformedDocument = ((java.io.CharArrayWriter)transformedDocResult.getWriter()).toString();
    //set it in the pipeline
    idc.first();
    idc.insertAfter(“transformedDocument”, transformedDocument);
    }
    catch(Exception e){
    idc.first();
    idc.insertAfter(“output”, e.getMessage());
    }
    //destroy the pipeline cursor
    idc.destroy();[/highlight]
    Now that we’ve explored how to make it work, can I talk you out of doing this? :slight_smile:

    Reimplementing transformSerialXML in your own Java service seems really unnecessary. It isn’t any more “lightweight”–it’s doing the same thing with the same XSLT engine within IS. Working with strings as input and output may be more convenient, but that can be achieved as described above with a simple FLOW wrapper.

    Are there other goals you’re looking to achieve?


    #Integration-Server-and-ESB
    #webMethods
    #Flow-and-Java-services


  • 5.  RE: Making my own XSLT transformer

    Posted Mon March 30, 2009 11:21 AM

    Wow, thank you for your input. I would have never figured this out by myself.

    Yes it is unnecessary; it was just for the sake it ;o
    I’m running a little benchmark right now, I’ll post the results later.


    #Integration-Server-and-ESB
    #webMethods
    #Flow-and-Java-services


  • 6.  RE: Making my own XSLT transformer

    Posted Mon March 30, 2009 04:52 PM

    I see. A rolling up of the sleeves to figure out what’s going on. Cool.

    I forgot to mention that when modifying your code, why I didn’t use OutputMap and instead used HashMap. It’s because OutputMap isn’t visible outside of the WmXSLT package. Also, it isn’t documented and isn’t intended for our use. It’s a bummer that the sample had an error in the class reference in the ns decl. It should have been IOutputMap, not Output.


    #webMethods
    #Integration-Server-and-ESB
    #Flow-and-Java-services