Showing posts with label XML. Show all posts
Showing posts with label XML. Show all posts

Wednesday, 15 January 2020

Javascript in ANT

Earlier I wrote about an ANT script to scan JCA adapters files in your projects home, subversion working copy or github local repo.

In my current project we use sensors to kick-of message-archiving processes, without cluttering the BPEL process. I'm not sure if I would do that like that if I would do on a new project, but technically the idea is interesting. Unfortunately, we did not build a registry what BPEL processes make use of it and how. So I tought of how I could easily find out a way to scan that, and found that based on the script to scan JCA files, I could easily scan all the BPEL sensor files. If you have found the project folders, like I did in the JCA scan script, you can search for the *_sensor.xml files.

So in a few hours I had a basic sript. Now, in a second iteration, I would like to know what sensorActions the sensors trigger. For that I need to interpret the accompanying *_sensorAction.xml file. There for, based on the found sensor filename I need to determine the name of the sensor action file.

The first step to that is to figure out how to do a substring in ANT. With a quick google on "ant property substring", I found a nice stackoverflow thread, with a nice example of an ANT script defininition based on Javascript:
  <scriptdef name="substring" language="javascript">
    <attribute name="text"/>
    <attribute name="start"/>
    <attribute name="end"/>
    <attribute name="property"/>
    <![CDATA[
       var text = attributes.get("text");
       var start = attributes.get("start");
       var end = attributes.get("end") || text.length();
       project.setProperty(attributes.get("property"), text.substring(start, end));
     ]]>
  </scriptdef>

And that can be called like:
    <substring text="${sensor.file.name}" start="0" end="20"   property="sensorAction.file.name"/>
    <echo message="Sensor Action file: ${sensorAction.file.name1}"></echo>

The javascript substring() function is zero-based, so the first character is indexed by 0.
Not every sensor file name has the same length, the file is called after the BPEL file that it is tight too. And so to get the base name, the part without the "_sensor.xml" postfix, we need to determine the length of the filename. A script that determines that can easily be extracted from the script above:
  <scriptdef name="getlength" language="javascript">
    <attribute name="text"/>
    <attribute name="property"/>
    <![CDATA[
       var text = attributes.get("text");
       var length = text.length();
       project.setProperty(attributes.get("property"), length);
     ]]>
  </scriptdef>

Perfect! Using this I could create the logic in ANT to determine the sensorAction file name. However, I thought that it would be easier to determine the filename in Javascript all the way. Using the strength of the proper language at hand:
  <!-- Script to get the sensorAction filename based on the sensor filename. 
  1. Cut the extension "_sensor.xml" from the filename.
  2. Add "_sensorAction.xml" to the base filename.
  -->
  <scriptdef name="getsensoractionfilename" language="javascript">
    <attribute name="sensorfilename"/>
    <attribute name="property"/>
    <![CDATA[
       var sensorFilename = attributes.get("sensorfilename");
       var sensorFilenameLength = sensorFilename.length();
       var postfixLength = "_sensor.xml".length();
       var sensorFilenameBaseLength=sensorFilenameLength-postfixLength;
       var sensorActionFilename=sensorFilename.substring(0, sensorFilenameBaseLength)+"_sensorAction.xml";
       project.setProperty(attributes.get("property"), sensorActionFilename);
     ]]>
  </scriptdef>
And then I can get the sensorAction filename as follows:
    <getsensoractionfilename sensorfilename="${sensor.file.name}" property="sensorAction.file.name"/>
    <echo message="Sensor Action file: ${sensorAction.file.name}"></echo>

Superb! I found ANT a powerfull language/tool already. But with a few simple JavaScript snippets you can extend it easily.
Notice by the way also the use of xslt in the Scan JCA adapters files article. You can read xml files as properties, but to do that conveniently you need to transform a file like the sensors.xml in a way that you can easily reference the properties following the element-hierarchy. This is also explained in the Scan JCA adapters files article.
I'll go further with my sensors scan script. Maybe I'll write about it when done.

Thursday, 27 March 2014

Hierarchical XML from SQL

Years ago I wrote an article (in Dutch) on the XML functions in Oracle SQL. It can be found here.
It describes how to create an xml document as an XMLType with an Oracle SQL Query.

The query that is described is based on a pretty simple table, with no relationships. I'm creating a new course based on a datamodel we created years ago, that contains data. I wanted to abstract some of that data as xml, but then: how about the foreign key relations?

It turns out pretty straightforward, that you can probably figure out yourself. But hey, I'm not a bad guy, so how about sharing it to you?

The query selects employees with their addresses and goes as follows:

select xmlelement("emp:employees"
  , xmlattributes( 'http://xmlnls.darwin-it.nl/doe/xsd/v1/employee' as "xmlns"
                 , 'http://xmlnls.darwin-it.nl/doe/xsd/v1/employee' as "xmlns:emp"
                 , 'http://xmlnls.darwin-it.nl/doe/xsd/v1/address' as "xmlns:ads")
  , xmlagg
  ( xmlelement
    ( "emp:employee"
    , xmlforest
      ( emp.title as "emp:title"
      , emp.firstname as "emp:firstName"
      , emp.last_name as "emp:lastName"
      , emp.gender as "emp:gender"
      , emp.birth_date as "emp:birthDate"
      )
    , ( select xmlelement( "emp:addresses"
        , xmlagg(
             xmlelement("ads:address"
             , xmlattributes( ate.code as "type"
                            , ate.description as "description")
             , xmlforest
               ( ads.adress_line1 as "ads:addressLine1"
               , ads.adress_line2 as "ads:addressLine2"
               , ads.adress_line3 as "ads:addressLine3"
               , ads.postal_code as "ads:postalCode"
               , ads.city as "ads:city"
               , ads.country as "ads:country"
               )
             )
          )
        )
        from doe_party_addresses pae
        join doe_addresses ads on ads.id = pae.ads_id
        join doe_address_types ate on ate.id= pae.ate_id
        where pae.emp_id = emp.id
        )
      )
    )
  )  xml
from doe_employees emp;
The doe_addresses table is joined with the doe_employees table via a couple-table named doe_party_addresses. This is because an employee can have multiple addresses, but with different types. An employee can have a Business address and a Home address. Like a customer can have a shipping, billing and visiting addresses. Also an address can be used by multiple parties. You see here that the addresses are selected as a subselect. The output of the sub-select is an XMLType that can be embedded in the xmlelement (and other xml-) functions. What you also see is that I added namespace declarations as xmlattributes on the top-level element. The element and attribute names are prefixed with the corresponding 'emp:' or 'ads:' namespace-prefixes. Oh, and the output of the query is something like:
<?xml version="1.0" encoding="UTF-8" ?>
<emp:employees xmlns="http://xmlnls.darwin-it.nl/doe/xsd/v1/employee"
               xmlns:emp="http://xmlnls.darwin-it.nl/doe/xsd/v1/employee"
               xmlns:ads="http://xmlnls.darwin-it.nl/doe/xsd/v1/address">
  <emp:employee>
    <emp:title>Mr.</emp:title>
    <emp:firstName>Ed</emp:firstName>
    <emp:lastName>Bushes</emp:lastName>
    <emp:gender>M</emp:gender>
    <emp:birthDate>1968-01-20</emp:birthDate>
    <emp:addresses>
      <ads:address type="HOME" description="Home address">
        <ads:addressLine1>Maasstraat 19</ads:addressLine1>
        <ads:postalCode>3812HS</ads:postalCode>
        <ads:city>Amersfoort</ads:city>
        <ads:country>NETHERLANDS</ads:country>
      </ads:address>
    </emp:addresses>
  </emp:employee>
  <emp:employee>
    <emp:title>Mr.</emp:title>
    <emp:firstName>M.</emp:firstName>
    <emp:lastName>Outback</emp:lastName>
    <emp:gender>M</emp:gender>
    <emp:birthDate>1961-10-14</emp:birthDate>
    <emp:addresses>
      <ads:address type="HOME" description="Home address">
        <ads:addressLine1>Rocky Road 2</ads:addressLine1>
        <ads:postalCode>20001</ads:postalCode>
        <ads:city>StoneHench</ads:city>
        <ads:country>UNITED KINGDOM</ads:country>
      </ads:address>
    </emp:addresses>
  </emp:employee>
  <emp:employee>
    <emp:title>Mr.</emp:title>
    <emp:firstName>M.</emp:firstName>
    <emp:lastName>Outback</emp:lastName>
    <emp:gender>M</emp:gender>
    <emp:birthDate>1961-10-14</emp:birthDate>
    <emp:addresses>
      <ads:address type="HOME" description="Home address">
        <ads:addressLine1>Hofvijver 12</ads:addressLine1>
        <ads:postalCode>2000XX</ads:postalCode>
        <ads:city>Den Haag</ads:city>
        <ads:country>NETHERLANDS</ads:country>
      </ads:address>
    </emp:addresses>
  </emp:employee>
  <emp:employee>
    <emp:title>Ms.</emp:title>
    <emp:firstName>Pat</emp:firstName>
    <emp:lastName>Darwin</emp:lastName>
    <emp:gender>F</emp:gender>
    <emp:birthDate>1980-03-14</emp:birthDate>
    <emp:addresses>
      <ads:address type="HOME" description="Home address">
        <ads:addressLine1>Rijnzathe 6</ads:addressLine1>
        <ads:postalCode>3140ZP</ads:postalCode>
        <ads:city>De Meern</ads:city>
        <ads:country>NETHERLANDS</ads:country>
      </ads:address>
    </emp:addresses>
  </emp:employee>
  <emp:employee>
    <emp:title>Mr.</emp:title>
    <emp:firstName>T.</emp:firstName>
    <emp:lastName>Barakus</emp:lastName>
    <emp:gender>M</emp:gender>
    <emp:birthDate>1970-02-11</emp:birthDate>
    <emp:addresses>
      <ads:address type="HOME" description="Home address">
        <ads:addressLine1>Erasmusstraat 312</ads:addressLine1>
        <ads:postalCode>1234GK</ads:postalCode>
        <ads:city>Rotterdam</ads:city>
        <ads:country>NETHERLANDS</ads:country>
      </ads:address>
    </emp:addresses>
  </emp:employee>
  <emp:employee>
    <emp:title>Ms.</emp:title>
    <emp:firstName>Debby</emp:firstName>
    <emp:lastName>Waters</emp:lastName>
    <emp:gender>F</emp:gender>
    <emp:birthDate>1982-01-20</emp:birthDate>
    <emp:addresses>
      <ads:address type="WORK" description="Work address">
        <ads:addressLine1>Darwinplein 11</ads:addressLine1>
        <ads:postalCode>4321PS</ads:postalCode>
        <ads:city>Amsterdam</ads:city>
        <ads:country>NETHERLANDS</ads:country>
      </ads:address>
      <ads:address type="HOME" description="Home address">
        <ads:addressLine1>Newtonweg 41</ads:addressLine1>
        <ads:postalCode>6543AB</ads:postalCode>
        <ads:city>Maasland</ads:city>
        <ads:country>NETHERLANDS</ads:country>
      </ads:address>
    </emp:addresses>
  </emp:employee>
  <emp:employee>
    <emp:title>Mr.</emp:title>
    <emp:firstName>Wally</emp:firstName>
    <emp:lastName>Waters</emp:lastName>
    <emp:gender>M</emp:gender>
    <emp:birthDate>1963-06-13</emp:birthDate>
    <emp:addresses></emp:addresses>
  </emp:employee>
</emp:employees>

2014-04-25, update: I changed the namespaces and I found a curiosity in my database content: all birthdates were in the future...

Friday, 14 March 2014

Run XQuery with XqlPlus

I started today with setting up an OSB training and figuring out how to run XQuery scripts outside of OSB. I looked into the (ok a little dated) book: Oracle Database 10g XML&SQL, that I have in my library. In that book there's also a chapter about XQuery. And it shows that the Oracle XDK has a XQuery processer, but also a commandline tool like SQLPLus: XQLPlus. It is in the xquery jar, but how to run it? Well you need at least the xquery.jar, but the xmlparserv2.jar is handy for more specialized functions. For localization functionality the orai18n-collation.jar is recommended. In this blog I found some of the basic jar files to put in the classpath. To put things together in a convenient start script I created the following bash script:
export ORACLE_BASE=/u01/app/oracle
export FMW_HOME=$ORACLE_BASE/Middleware/11.1.1
export ORACLE_COMMOD=$FMW_HOME/oracle_common/modules
export XDK_HOME=$ORACLE_COMMOD/oracle.xdk_11.1.0
export NLSRTL_HOME=$ORACLE_COMMOD/oracle.nlsrtl_11.1.0
export CLASS_PATH=$XDK_HOME/xquery.jar:$XDK_HOME/xmlparserv2.jar:$NLSRTL_HOME/orai18n-collation.jar
java -cp $CLASS_PATH oracle.xquery.XQLPlus $*
Maybe it can come handy for others as well.

Wednesday, 10 July 2013

Current date and time in XLST under OSB

For my current assignment I needed to build quite complex transformations in OSB. Mainly because of my experiences I choose to do that in XSLT.
Eclipse has a quite nice XQuery mapper, but it lacks an XSLT mapper.
Since JDeveloper has a nice XSL Mapper tool, I incorporated a JDeveloper project in my OEPE OSB project.

Soon I found that the xpath2.0 functions of JDeveloper/SOASuite don't work under OSB. And I could not find how to use the XQuery functions in my Xslt, that do the same.

But I found a nice trick, mainly based on this forum post and this one. I adapted those examples a little, because of the wrong date formats and to get them working in my case.

First declare the following namspaces.
   xmlns:date="http://www.oracle.com/XSL/Transform/java/java.util.Date"
   xmlns:sdf= "http://www.oracle.com/XSL/Transform/java/java.text.SimpleDateFormat"
When examining those, it appears that "http://www.oracle.com/XSL/Transform/java/" tells the xsl-processor where to find java classes. And after that the path (package + class) to the actual java class can be appended. These prefixes should not get into the resulting xml, so you can add them to the exclude list:
     exclude-result-prefixes="... date sdf">
And then you can declare the next variables for the current date and the current time:
 
  <xsl:variable name="currentDate"><xsl:value-of select="sdf:format(sdf:new(&quot;yyyy-MM-dd&quot;),date:new())"></xsl:value-of></xsl:variable>
  <xsl:variable name="currentTime"><xsl:value-of select="sdf:format(sdf:new(&quot;hh:mm:ss&quot;),date:new())"></xsl:value-of></xsl:variable>
If you know java then you can figure out how that translates to a snippet of java code. And then you should be able to work out your way back. And doing so: you can program almost anything using java in XSLT...

Tuesday, 29 March 2011

XML to HTML through XSL

I think it was about 2005 or 2006 that we at Oracle had so-called Mudwrestle sessions. Colleagues organized workshops for each other on the latest technologies. We started at 14:00 and after some introduction we did some exercises to get familiar with the subject. Amongst subjects as Integration (InterConnect), java, linux, we had also XML. And there I got to know XSLT for the first time.

To get it "in the fingers", I thought it might be handy to have all my browser links in an xml file and have an xsl attached to have it transformed into html providing them in pop-lists and a button. With a little java script it is then possible to have the link loaded after choosing a link and pressing the button. So I created my first version of the XML and XSL. It was in the time that Internet Explorer 6 was pretty new or at least the current release. I think I was into Firefox already. IE6 and Firefox were able to load and process the xsl file when it was attached to the xml file. To do so you have to add the following tag to the top of your xml file:

<?xml-stylesheet href="links.xsl" type="text/xsl"?>

Like;
<?xml version="1.0"?>
<?xml-stylesheet href="links.xsl" type="text/xsl"?>
<link-lists
  name="Links"
  title="Internet Links"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Begin van de Lists!
  <lists>
    <list
      name="DarwinApps"
      title="Darwin Links">
      <link
        description="Darwin-IT"
        link="http://www.darwin-it.nl"
        name="Darwin-IT" />
...

The XSL that I created for it is given at the bottom of this post.
Last week I enhanced it a little to have a button to load all the links of a list at once. That is particularly handy for a list of webmail-urls to load at the start of your day.

I'm very fond of this xml/xsl combi. It is so simple, since it consist of three ascii files (xml, xsl and css). The xml file is easy to extend and after a reload in the browser the link-lists are renewed. The xsl is simple to enhance to add extra functionality. And the css makes it possible to have it a layout that complies with your taste or desktop. Skinning was never so easy. And it works in both Windows and Linux. And probably Mac OS. On Windows XP I even had it on my Active Desktop.  But I can't find how to do it on Windows 7 or Linux.

Later in history, also a few years ago (2009) I created a tool in java to edit the xml file. Also this one is enhanced a few times and last week I posted a blog on this tool. See here. I made it xml-parser-vendor-independent. And it's opensource thus you might take a look at the source.

To try the xml/xsl you can open the xml-links file from here. Of course you can download it to your laptop and edit it to add your own links and remove the ones not needed. The xsl is for download here and the css-file here. Place the xml and the xsl in the same folder, the css is expected in a subfolder called "style".

Below is the source of the XSL for investigation. As you can see I set it up in a modular way. I'm used to have a template for every hierarchical level of XML. And for particular functionalities, such as the generation of the java script for each poplist. You can use the xml-tool set in the the earlier post to perform a transform of the XML with the XSL to see the resulting HTML. Have fun with it.

<?xml version="1.0"?>
<!--  (c) 2008-2011, by Martien van den Akker  
Darwin-IT Professionals
-->

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output
    method="html" />  <!--  Variables  -->

  <xsl:variable
    name="newline">
    <xsl:text></xsl:text></xsl:variable>  <!--  main  -->

  <xsl:template
    match="/">
    <html>

      <xsl:call-template
        name="heading" />
      <body>
        <xsl:call-template
          name="body" /></body></html></xsl:template>
  <xsl:template
    name="heading">    <!--  Heading  -->

    <head>
      <title>Links</title>
      <LINK
        HREF="style/style.css"
        REL="STYLESHEET"
        TYPE="text/css" /></head>    <!--  Script to load a page  -->

    <script
      language="javascript">
      <xsl:text>function loadPage(link)
{
  newWdw=window.open(link,"_blank","");
}</xsl:text></script></xsl:template>
  <xsl:template
    name="body">    <!--  Body  -->

    <body>
      <xsl:apply-templates
        select="/link-lists" /></body></xsl:template>  <!--  /link-lists  -->

  <xsl:template
    match="link-lists"
    name="link-lists">

    <h1>
      <xsl:value-of
        select="@title" /></h1>
    <table>
      <xsl:apply-templates
        select="lists/list" /></table></xsl:template>  <!--  /link-lists/lists/list  -->

  <xsl:template
    match="lists/list"
    name="lists-list">
    <tr>
      <td>
        <xsl:value-of
          select="@title" /></td>      <!--  generate javascript for loading a link  -->

      <xsl:call-template
        name="loadLink" />      <!--  generate javascript for loading a links  -->

      <xsl:call-template
        name="loadAllLinks" />      <!--  generate a poplist and a button  -->

      <xsl:call-template
        name="writePopList" /></tr></xsl:template>  <!--  loadLink: generate a javascript that loads a link  -->

  <xsl:template
    name="loadLink">
    <xsl:text
      disable-output-escaping="yes">      <!--  script to load a link  -->

</xsl:text>
    <script
      language="javascript">
      <xsl:value-of
        select="concat($newline,'      function load', @name,'Link()')" />
      <xsl:text
        disable-output-escaping="yes">{</xsl:text>
      <xsl:value-of
        select="concat('        var l_idx = document.', @name, 'LinkSelector.select.selectedIndex;',$newline)" />
      <xsl:value-of
        select="concat('        var l_value = document.', @name, 'LinkSelector.select.options[l_idx].value;')" />
      <xsl:text
        disable-output-escaping="yes">if (l_value != "none")
        {
           loadPage(l_value);
        }
      }</xsl:text></script></xsl:template>  <!--  loadAllLinks: generate a javascript that loads all links of a poplist  -->

  <xsl:template
    name="loadAllLinks">
    <xsl:text
      disable-output-escaping="yes">      <!--  script to load a link  -->
</xsl:text> 
    <script
      language="javascript">
      <xsl:value-of
        select="concat($newline,'      function loadAll', @name,'Links()')" />
      <xsl:text
        disable-output-escaping="yes">{
        var l_idx;
        var l_value;</xsl:text>
      <xsl:value-of select="concat('        for(l_idx=0; l_idx&lt;document.', @name, 'LinkSelector.select.length; l_idx++)')"/>
   <xsl:value-of select="concat($newline,'        {',$newline)"/>
      <xsl:value-of select="concat('           l_value=document.', @name, 'LinkSelector.select.options[l_idx].value;')"/> 
      <xsl:text
        disable-output-escaping="yes">

    if (l_value != "none")
    {
            loadPage(l_value);
    }
        }
      }</xsl:text></script></xsl:template>
  <xsl:template
    name="writePopList">
    <td>
      <form
        name="{concat(@name, 'LinkSelector')}">
        <table
          border="0"
          cellspacing="0">
          <tr>
            <td
              width="250px">              <!-- Select  -->

              <select
                name="select">
                <option
                  value="none">Kies een link:</option>
                <xsl:for-each
                  select="link">
                  <option
                    value="{@link}">
                    <xsl:value-of
                      select="@name" /></option></xsl:for-each></select></td>
            <td>              <!--  Button  -->

              <input
                onclick="{concat('load',@name,'Link()')}"
                type="button"
                value="Toon" /></td>

            <td>              <!--  Button Load All -->

              <input
                onclick="{concat('loadAll',@name,'Links()')}"
                type="button"
                value="Toon alles" /></td></tr></table></form></td></xsl:template>
</xsl:stylesheet>

Tuesday, 22 March 2011

Vendor independent XML processing

A few years ago, when I was "low in work" at the particular customer, I created a little tool set on XML processing. I called it Darwin XML Editor and XML Tester. The latter name I now find not so well choosen. So I would now call it XML Tools.

The purpose of the tool set was to figure out how to process XML in java. I already had a private xml based solution to gather my frequently used browser links into an xml file and convert it using an attached xslt stylesheet to an html page. This html page provided the links in poplists and using java script it could load the link.
It would be nice to have a hierarchical editor that showed my xml in an explorer and let me edit particular attributes in a table format.
 It turned out handy for me. But the most I had a toolkit that allowed me to easily load XML as a text file and parse it, perform xpath queries and just traverse through nodes, etc. In several projects I used it to read xml-based config files. Many of those cases I probably  could have done using Apache-commons.
But it is handy to have your own toolkit. Especially in cases when you have repetitive lists of entities with properties.

My project originally was written using the Oracle XMLParser. Handy, because I use JDeveloper and that comes with the parser. For some time now I had plans to look if I could make it vendor independent.

The results can be found here. It's open source, whatever that may mean. But it would be nice to leave the credit-references in place or refer to the credits. That would be nice for my ego...

I will provide here some highlights of the changes.
I used the following sites as my references:


Parsing

It starts with parsing. The "Oracle way" I did it was:
public void parse() {

        try {
            String text = super.getText();
            if (text != null) {
                resetError();
                DOMParser parser = new DOMParser();
                InputSource inputStream = new InputSource();
                inputStream.setCharacterStream(new StringReader(text));
                parser.parse(inputStream);
                xmlDoc = parser.getDocument();

                xmlRoot = xmlDoc.getDocumentElement();
                nodeSelected = new NodeSelected((Node)xmlRoot);
                parsed = true;
                log("File: " + super.getFilePath() + " is succesfully parsed");
            } else {
                log("File: " + super.getFilePath() + " empty or not loaded!");
            }

        } catch (SAXException e) {
            setErrorCode(EC_ERROR);
            setError("Error parsing XML: " + e.toString());
            error(e);
        } catch (IOException e) {
            setErrorCode(EC_ERROR);
            setError("Error reading XML: " + e.toString());
            error(e);
        }
    }

With the following oracle imports:
import oracle.xml.parser.v2.DOMParser;
import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XMLNode;
import oracle.xml.parser.v2.XSLException;

The vendor independent way I used is:
/**
     * Create a new DocumentBuilder.
     * @return
     * @throws ParserConfigurationException
     */
    private DocumentBuilder newDocBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory domFactory = 
            DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true); // never forget this!
        DocumentBuilder docBuilder;
        docBuilder = domFactory.newDocumentBuilder();
        return docBuilder;
    }

    /**
     * Create an InputSource from the xml-text
     * @return
     */
    public InputSource getInputSource() {
        String text = super.getText();
        InputSource inputStream = null;
        if (text != null) {
            inputStream = new InputSource();
            inputStream.setCharacterStream(new StringReader(text));
        }
        return inputStream;
    }

    /**
     * Parse the XML in the file
     */
    public void parse() {
        try {
            InputSource inputSource = getInputSource();
            if (inputSource != null) {
                resetError();
                DocumentBuilder docBuilder = newDocBuilder();
                Document doc = docBuilder.parse(inputSource);
                setDoc(doc);
                parsed = true;
                log("File: " + super.getFilePath() + " is succesfully parsed");
            } else {
                log("File: " + super.getFilePath() + " empty or not loaded!");
            }
        } catch (ParserConfigurationException e) {
            setErrorCode(EC_ERROR);
            setError("Error creating parser: " + e.toString());
            error(e);
        } catch (SAXException e) {
            setErrorCode(EC_ERROR);
            setError("Error parsing XML: " + e.toString());
            error(e);
        } catch (IOException e) {
            setErrorCode(EC_ERROR);
            setError("Error reading XML: " + e.toString());
            error(e);
        }
    }
with the following imports:
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

Mark that this code is part of a XMLFile class that extends a TextFile class that gives me the methods to load the file into a String attribute (text).
Here I extracted the conversion from the text attribute to an InputSource object in a separate method. Also the creation of the parser (the DocumentBuilder) I put in a seperate method. This because I also need it to be able to create an empty Document.

XPath Expressions

The "Oracle way" is pretty straight forward:

/**
     * Select Nodes using Xpath
     * @param xpath
     * @return NodeList 
     */
    public NodeList selectNodes(String xpath) throws XSLException {
        XMLDocument xmlDoc = getXmlDoc();
        NodeList nl = xmlDoc.selectNodes(xpath);
        return nl;
    }
To have it "Namespace Aware" you'll need a Namespace Resolver, which is a simple class based on a HashMap, implementing an Interface:
package com.darwinit.xmlfiles.xml;
/**
 * Namespace Resolver: Helper class to do namespace aware XPath queries. 
 *
 * @author Martien van den Akker
 * @author Darwin IT Professionals
 *
 * @remark: changed to JSE 1.4 code because of BPEL PM 10.1.2
 */
import java.util.HashMap;

import oracle.xml.parser.v2.NSResolver;

public class XMLNSResolver implements NSResolver{
  private HashMap<String, String> nsMap = new HashMap<String, String>();
    
    public XMLNSResolver() {
    }

   public void addNS(String abbrev, String namespace){
       nsMap.put(abbrev, namespace);
   }
    public String resolveNamespacePrefix(String string) {
        return nsMap.get(string);
    }
}
Then using the namespace resolver the code would be something like:

/**
     * Select nodes using Xpath with Namespace included
     * @param xpath
     * @return Nodelist 
     */
    public NodeList selectNodesNS(String xpath) throws XSLException {
    XMLDocument xmlDoc = getXmlDoc();
    XMLNSResolver nsRes = getNsRes();
        NodeList nl = this.xmlDoc.selectNodes(xpath, nsRes);
        return nl;
    }

The vendor indepent way is a little more complex. But it gives you some more flexibility.
/**
     * Evaluate xpath expression 
     * 
     * @param xpathExpr the xpath expression
     * @param returnType the return type that is expected.
     * http://www.ibm.com/developerworks/library/x-javaxpathapi.html:
     * XPathConstants.NODESET => node-set maps to an org.w3c.dom.NodeList
     * XPathConstants.BOOLEAN => boolean maps to a java.lang.Boolean
     * XPathConstants.NUMBER => number maps to a java.lang.Double
     * XPathConstants.STRING => string maps to a java.lang.String
     * XPathConstants.NODE
     * 
     * @throws XPathExpressionException
     */
    public   Object evaluate(String xpathExpr, 
                    QName returnType) throws XPathExpressionException {
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        XMLNSResolver nsRes = getNsRes();
        if (nsRes != null) {
            xpath.setNamespaceContext(nsRes);
        }
        XPathExpression expr = xpath.compile(xpathExpr);
        Document doc = getDoc();
        Object resultObj = expr.evaluate(doc, returnType);
        return resultObj;
    }

    /**
     * Evaluate xpath expression to a double (when a number is expected from the xpath expression)
     * 
     * @throws XPathExpressionException
     */
    public Double evaluateDouble(String xpathExpr) throws XPathExpressionException {
        Double result = null;
        Object resultObj = evaluate(xpathExpr, XPathConstants.NUMBER);
        if (resultObj instanceof Double) {
            result = (Double)resultObj;
        }
        return result;
    }

    /**
     * Select Nodes using Xpath
     * 
     * @param xpath
     * @return NodeList
     * @throws XPathExpressionException 
     */
    public NodeList selectNodes(String xpath) throws XPathExpressionException {
        NodeList nl = (NodeList)evaluate(xpath, XPathConstants.NODESET);
        return nl;
    }
The Namespace resolver changed slightly. It actually implements another interface:
But the idea is the same:
package com.darwinit.xmlfiles.xml;

import java.util.HashMap;

import java.util.Iterator;

import javax.xml.namespace.NamespaceContext;


/**
 * Namespace Resolver: Helper class to do namespace aware XPath queries. 
 *
 * @author Martien van den Akker
 * @author Darwin IT Professionals
 *
 */
public class XMLNSResolver implements NamespaceContext {
    private HashMap<String, String> nsMap = new HashMap<String, String>();
   /**
     * Constructor
     */
    public XMLNSResolver() {
    }

    /**
     * Add a Namespace
     * @param prefix
     * @param namespace
     */
    public void addNS(String prefix, String namespace) {
        nsMap.put(prefix, namespace);
    }

    /**
     * Resolve a namespace from a prefix
     * @param prefix
     * @return
     */
    public String resolveNamespacePrefix(String prefix) {
        return nsMap.get(prefix);
    }

    /**
     * Resolve a namespace from a prefix
     * @param prefix
     * @return
     */
    public String getNamespaceURI(String prefix) {
        return resolveNamespacePrefix(prefix);
    }

    /**
     * Get the prefix that is registered for a NamespaceURI 
     * However, not necessary for xpath processing
     * @param namespaceURI
     * @return
     */
    public String getPrefix(String namespaceURI) {
        throw new UnsupportedOperationException();
    }

    /**
     * Get an iterator with the prefix registered for a NamespaceURI
     * @param namespaceURI
     * @return
     */
    public Iterator getPrefixes(String namespaceURI) {
        return null;
    }
}


The differences are in the fact that you first have to 'compile' an xpath expression. And that the evaluation of the XPath on a Document expects you to provide the expected result datatype. (see the comments of the evaluate method).
Then it will result an Object that you have to cast to the particular Java Class that corresponds with the Result XML DataType.

XSLT

The last thing is transforming XSLT. The "Oracle Way" I used was:

package com.darwinit.xmlfiles.xml;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Hashtable;

import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XSLException;
import oracle.xml.parser.v2.XSLProcessor;
import oracle.xml.parser.v2.XSLStylesheet;


public class XmlTransformer {
    private void pl(String text) {
        System.out.println(text);
    }

    public XmlTransformer() {
    }

    public String transform(XMLDocument xslDoc, XMLDocument xmlDoc) {     
     return transform (xslDoc, xmlDoc, null);
    }    
    
    /**
     * Transform the xmlDoc using xslDoc into String
     * @param xslDoc
     * @param xmlDoc
     * @param parameters contains parameter that are passed to the xslt
     * @return String output
     */
    public String transform(XMLDocument xslDoc, XMLDocument xmlDoc
                           , Hashtable<String, String> parameters) {
        XSLProcessor xslProcessor = new XSLProcessor();
        XSLStylesheet xslt;
        String result = "";
        try {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            xslt = xslProcessor.newXSLStylesheet(xslDoc);
            
            xslProcessor.setXSLTVersion(XSLProcessor.XSLT20);
            xslProcessor.showWarnings(true);
            xslProcessor.setErrorStream(System.err);
            
            if (parameters != null) {
            
             for (String key : parameters.keySet()) {
              String value = parameters.get(key);
              xslProcessor.setParam("", key, value);
             }
            }                      
            
            xslProcessor.processXSL(xslt, xmlDoc, pw);
            pw.flush();
            pw.close();
            sw.close();
            result = sw.toString();
        } catch (XSLException e) {

            pl(e.toString());
        } catch (IOException e) {
            pl(e.toString());
        }
        return result;
    }   
    /**
     * Clean up text from XML Leftovers
     * @param text
     * @return
     */
    public String cleanText(final String text){
        String result = text;
        result = result.replaceAll("&lt;","<");
        return result;
    }
    /**
     * Transfomr the xmlDoc using xslDoc into String
     * Cleanup XML code leftovers
     * @param xslDoc
     * @param xmlDoc
     * @return String output
     */
    public String transform2Ascii(XMLDocument xslDoc, XMLDocument xmlDoc) {
      String result = transform(xslDoc, xmlDoc);
      result = cleanText(result);
      return result;
    }

}

And the vendor indepent version of the same class:
package com.darwinit.xmlfiles.xml;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Hashtable;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import com.darwinit.xmlfiles.log.LocalStaticLogger;

/**
 * XmlTransformer: Class implementing functionality to Transform XML using XSLT.
 * 
 * See also http://www.ling.helsinki.fi/kit/2004k/ctl257/JavaXSLT/Ch05.html
 * 
 * @author Martien van den Akker
 * @author Darwin IT Professionals
 */

public abstract class XmlTransformer {
 public static final String className = "XmlTransformer";
 private static LocalStaticLogger lgr;

 /**
  * Transform the xmlDoc using xslDoc into String
  * 
  * @param xslDoc
  * @param xmlDoc
  * @return
  */
 public static String transform(Document xslDoc, Document xmlDoc) {
  return transform(xslDoc, xmlDoc, null);
 }

 /**
  * Transform the xmlDoc using xslDoc into String, with parameters
  * 
  * @param xslDoc
  * @param xmlDoc
  * @param parameters
  *            contains parameter that are passed to the xslt
  * @return String output
  */
 @SuppressWarnings("static-access")
 public static String transform(Document xslDoc, Document xmlDoc,
   Hashtable<String, String> parameters) {
  final String methodName = "transform";
  lgr.logStart(className, methodName);
  String result = "";
  try {
   DOMSource xsltSource = new DOMSource(xslDoc);
   DOMSource xmlSource = new DOMSource(xmlDoc);
   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw);
   StreamResult streamResult = new StreamResult(pw);
   // Get the transformer factory
   TransformerFactory transFact = TransformerFactory.newInstance();
   // Get a transformer for this particular stylesheet
   Transformer trans = transFact.newTransformer(xsltSource);
   // Add parameters
   if (parameters != null) {
    for (String key : parameters.keySet()) {
     String value = parameters.get(key);
     trans.setParameter(key, value);
    }
   }
   // Do the transformation
   trans.transform(xmlSource, streamResult);
   pw.flush();
   pw.close();
   sw.close();
   // Get the result string
   result = sw.toString();
  } catch (IOException e) {
   lgr.error(className, methodName, e);
  } catch (TransformerException e) {
   lgr.error(className, methodName, e);
  }
  lgr.logEnd(className, methodName);
  return result;
 }

 /**
  * Clean up text from XML Leftovers
  * 
  * @param text
  * @return
  */
 public static String cleanText(final String text) {
  String result = text;
  result = result.replaceAll("&lt;", "<");
  return result;
 }

 /**
  * Transform the xmlDoc using xslDoc into String Cleanup XML code leftovers
  * 
  * @param xslDoc
  * @param xmlDoc
  * @return String output
  */
 public static String transform2Ascii(Document xslDoc, Document xmlDoc) {
  String result = transform(xslDoc, xmlDoc);
  result = cleanText(result);
  return result;
 }

}
The code is not much different. Most important is that you have to wrap the input, xslt and result objects into particular interface objects. The examples I found worked with File(s) as Source and Result objects. But I wanted Document objects for the XLST and Input objects. And I want to have the result into a String, to be able to process it in anyway I want.

I added also the possiblity to pass XSLT parameters. But I got it from a project I worked on and see that I haven't put that in the class in my XmlFiles project.

Conclusion

I hope this helps in understanding how to work with XML parsers. Ofcourse all is rudimentary (I have several large books on the subject in the cupboard). But I have pretty much enough with the above. The code in my XmlFiles project is basically around this code. On top of these methods I have several other helper methods to do traverse nodes or to get it in a particular way.

I thought it might be helpfull to put the Oracle Native methods side by side with the vendor-independent (JAXP) code. Doing so I would not state that one way is better than the other. The vendor-independent code have clearly the advantage that you can simply replace the parser, just by changing the class path. The code will work with the Oracle XML Parser just as good as with the Apache Xerces-J parser.
The reason I did it the Oracle way before was just because I ran into these examples first, the time I started this. Probably there still are good reasons to use the native API's.

Oracle also has a Pl/Sql Wrapper around the XML Parser. So the Oracle XML Parsing code has a Pl/Sql counter part. Might be nice to do this in a Pl/Sql way sometime.
But as the code above isn't rocket science, it isn't new also. XML Parsing in Pl/Sql can be done from Oracle 8i onwards. Also roughly ten years already.

Monday, 27 September 2010

Templates in BPEL Transforms

Multiple times I encountered that the Transformation tool of de BPEL designer has difficulties to cope with xpath and xslt functions in the stylesheet that it does not 'know'.
We have for example some custom xslt functions and I use some xpath 2.0 functions. And if I use them and deploy the process, they'll work. But for the mapper -tool the transform is invalid and it will not show the transformation map.

This is especially true for BPEL 10.1.2 that is still used at my current customer (there is a upgrade to 11g project on going).

Very anoying, because we have some large xsl's that use a large number of custom xslt-functions to do a cached dvm lookup.

But today I found a very nice workaround. If you hide those functions in a custom user template then the transformation map does not have difficulties with it.


I now have the following user-defined template (put at the bottom of the xsl stylesheet):

<xsl:template name="TransformLandCode">
<xsl:param name="landCode"/>
<xsl:comment>Transform Landcode gebruikmakend van cache:lookup
</xsl:comment>
<xsl:variable name="result" select="cache:lookup($landCode,&quot;LandCodeDomain_<FromSystem>To<ToSystem>&quot;)"/>
<xsl:value-of select="$result"/>
</xsl:template> 

So where I had something like:
<xsl:value-of select="cache:lookup(/ns1:rootElement/ns1:subElement/ns1:LandCode,&quot;LandCodeDomain_&lt;FromSystem&gt;To&lt;ToSystem&gt;&quot;)"/>

I now call the template:
<xsl:call-template name="TransformLandCode">
<xsl:with-param name="landCode" select="/ns1:rootElement/ns1:subElement/ns1:LandCode"/>
</xsl:call-template>


Nice is by the way that the mapper also allows for adding the call statement in to the map using drag and drop. Also the parameters can be filled using dragging the lines:




And of course you can add code-snippets for it.

Tuesday, 31 August 2010

Index-of, replace and key-value parsing in XSLT

Lately I had to change a few BPEL Processes in a way that I had to pass multiple parameters in one field. Instead of a complete base64 encoded data-object I had to pass a key to that object in a way that I could determine that the object was in fact a key-value pair. The field contains in that case in fact two key-value pairs. I thought that if I would code the key-value pair with something like $KEY="AABBCCDDEEFF" I could search for $KEY=" and then the value after the double-quote and before the next would then be the value.

The problem with XSLT is that you have functions like substring-before(), substring-after() and positional substring, where the from-position and length can be passed as numeric values. But for the latter function you need to determine the start and end position of the sub-string to extract from the input string. But apparently XSLT does not provide something like the Pl/Sql instr or Java index-of(). Also XSLT lacks a replace function in which you can replace a string within a string with a replacement string. The xslt-replace() function does a character-by-character replace.

Fortunately you can build these functions quite easilily yourself as xslt-templates using the substring-before(), substring-after(), and string-length() functions. I found the examples somewhere and adapted them a little for my purpose. Mainly to get them case-insensitive.

Index-of-ci
Here is my Case insensitive version of the Index-of template:
<!-- index-of: find position of search within string
2010-08-31, by Martien van den Akker -->
<xsl:template name="index-of-ci">
<xsl:param name="string"/>
<xsl:param name="search"/>
<xsl:param name="startPos"/>
<xsl:variable name="searchLwr" select="xp20:lower-case($search)"/>
<xsl:variable name="work">
<xsl:choose>
<xsl:when test="string-length($startPos)&gt;0">
<xsl:value-of select="substring($string,$startPos)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="stringLwr" select="xp20:lower-case($work)"/>
<xsl:variable name="result">
<xsl:choose>
<xsl:when test="contains($stringLwr,$searchLwr)">
<xsl:variable name="stringBefore">
<xsl:value-of select="substring-before($stringLwr,$searchLwr)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length($startPos)&gt;0">
<xsl:value-of select="$startPos +string-length($stringBefore)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1 + string-length($stringBefore)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>-1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:copy-of select="$result"/>
</xsl:template>

The template expects 3 parameters:
  • string: the string in which is searched
  • search: the substring that has to be searched
  • startPos: position in string where the search is started
The first thing the template does is declaring a work-variable. If startPos is not given, the variable work will contain the complete input string. But if startPos is given work will contain the substring of string from startPos to the end.

Then the input and the search strings are converted to lower case into new variables: andinputLwr and searchLwr. These variables are to test case-insensitively if the search string is in the input. If that is the case then the part of the input string before the search string is determined with the substring-before() function. The string-length() of the result denotes in fact the numeric position of the search string within the input string. This is incremented by 1 or by startPos depending on startPos being filled.

The template can be called without the startPos parameter:
<xsl:call-template name="index-of-ci">
<xsl:with-param name="string" select="$input"/>
<xsl:with-param name="search" select="$keyStr"/>
</xsl:call-template>

Or with the parameter:
<xsl:call-template name="index-of-ci">
<xsl:with-param name="string" select="$input"/>
<xsl:with-param name="search" select="string('&quot;')"/>
<xsl:with-param name="startPos" select="$startIdx"/>
</xsl:call-template>


Replace
The next template replaces the fromStr in input to toStr.
<!-- replace-ci: case insensitive replace based on strings 
2010-08-31, by Martien van den Akker -->
<xsl:template name="replace-ci">
<xsl:param name="input"/>
<xsl:param name="fromStr"/>
<xsl:param name="toStr"/>
<xsl:param name="startStr"/>
<xsl:if test="string-length( $input ) &gt; 0">
<xsl:variable name="posStartStr">
<xsl:call-template name="index-of-ci">
<xsl:with-param name="string"
select="$input"/>
<xsl:with-param name="search"
select="$startStr" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="startPos">
<xsl:call-template name="index-of-ci">
<xsl:with-param name="string"
select="$input"/>
<xsl:with-param name="search"
select="$fromStr" />
<xsl:with-param name="startPos"
select="$posStartStr" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="inputLwr" select="xp20:lower-case($input)"/>
<xsl:variable name="startStrLwr" select="xp20:lower-case($startStr)"/>
<xsl:choose>
<xsl:when test="contains( $input, $startStrLwr ) and contains( $inputLwr, $fromStr )">
<xsl:variable name="stringBefore" select="substring($input,1,$startPos - 1)"/>   
<xsl:variable name="stringAfter" select="substring($input,$startPos + string-length($fromStr))"/>   
<xsl:value-of select="concat($stringBefore,$toStr)"/>         
<xsl:call-template name="replace-ci">
<xsl:with-param name="input"
select="$stringAfter"/>
<xsl:with-param name="fromStr"
select="$fromStr" />
<xsl:with-param name="toStr"
select="$toStr" />
<xsl:with-param name="startStr"
select="$startStr" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$input"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>

Here the pos of the from string is determined using the index-of-ci template described earlier.
But this is done from the position of the startStr that marks the start of the replacement search. For example, if you want to replace domain names in email-adresses, you want to start the search of the domain after the at-sign ( '@' ).
Having the start position of the 'from'-string the part of the input before and after the 'from'-string is taken using the string-before() and strina-after() functions.
The 'to'-string is concatenated to the result of the string-before(). The result of the string-after() is used to call the template recursively to search the remainder of the input-string.


Parsing keys
The following template parses a key value like $KEY="AABBCCDDEEFF"
<!-- Parse a KeyValue
2010-08-31, By Martien van den Akker -->
<xsl:template name="getKeyValue">
<xsl:param name="input"/>
<xsl:param name="key"/>
<xsl:param name="default"/>
<!-- Init variables -->
<xsl:variable name="keyStr" select="concat('$',$key,'=&quot;')"/>
<xsl:if test="string-length( $input ) &gt; 0">
<xsl:variable name="startIdxKey">
<xsl:call-template name="index-of-ci">
<xsl:with-param name="string" select="$input"/>
<xsl:with-param name="search" select="$keyStr"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="keyLength" select="string-length($keyStr)"/>
<xsl:variable name="startIdx" select="$startIdxKey+$keyLength"/>
<xsl:variable name="endIdx">
<xsl:call-template name="index-of-ci">
<xsl:with-param name="string" select="$input"/>
<xsl:with-param name="search" select="string('&quot;')"/>
<xsl:with-param name="startPos" select="$startIdx"/>
</xsl:call-template>
</xsl:variable>
<!-- Determine value -->
<xsl:choose>
<xsl:when test="$startIdxKey&gt;=0 and $endIdx&gt;=0">
<xsl:value-of select="substring($input,$startIdx, $endIdx - $startIdx)"/>
</xsl:when>
<xsl:when test="$startIdxKey>0 and $endIdx&lt;0">
<xsl:value-of select="substring($input,$startIdx)"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$default='Y'">
<xsl:value-of select="$input"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
The working is quite similar to the templates above. If you understand the replace-template, you wouldn't have trouble with this one. Basically the value is search using the index-of-ci template with a concatenation of '$', the key and '="'. The string after that is the value, with a double quote as the end delimiter.
Having this template you can search for the key any where in the string, even if there are multiple key-value pairs.
The template can be called like:
<ns1:Id>
<xsl:call-template name="getKeyValue">
<xsl:with-param name="input" select="/ns2:aap/ns2:noot"/>
<xsl:with-param name="key" select="'KEY'"/>
<xsl:with-param name="default" select="'Y'"/>
</xsl:call-template>
</ns1:Id>


The parameter 'default' is optional: if it is set to 'Y' and the KEY is not found in the input string, then the value of the input is returned as result. Otherwise nothing is returned.

Conclusion
I found these templates very helpful. Using them you can do almost any string replacements in XSLT. ALso to me they function as example to cope with more advanced XSLT-challenges.

Tuesday, 5 January 2010

Passing parameters to an XSLT in BPEL

Yesterday for me it became handy to be able to pass parameters to an XSLT in BPEL. I've seen the need earlier, but solved it a different way. By setting the target element with a default and then over writing that default in a later assign-copy step.

But yesterday I had to transform a document a variable number of times, for a list of elements. In my case I had a document with a list of recipients and I had to transform that to another document for each recipient. Each recipient had a number of elements with personal and address information that had to be transformed to the target. So simply defaulting and overwriting would not work, or was a lot of work.

To pass parameters as arguments to a XSLT is quite easy and neatly described in several blogs, amongst others in Sudheer Dhurjati's Blog.

In my case I had to pass an index to be able to select the particular recipient that I need to transform. And another parameter that holds a number of documents that is determined from another source:
<?xml version="1.0" encoding="UTF-8" ?>
<parameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.oracle.com/service/bpel/common /S:/DEV/Sources/BPEL/Processes/CRMI_COM_VersturenBerichtContactMgt/1.0/src/xsltparameters.xsd"
xmlns="http://schemas.oracle.com/service/bpel/common">
<item>
<name>AantalBijlagen</name>
<value>2</value>
</item>
<item>
<name>OntvangerIndex</name>
<value>1</value>
</item>
</parameters>

In Sudheer's blog (and in other examples) the parameters are initialized by copying an XML fragment with the particular parameters. If you have to change one of the parameters this could be done by doing an indexed xpath-expression in the <to> of the assign step:
[/prm:parameters/prm:item[prm:name='OntvangerIndex']/prm:value]
with [http://schemas.oracle.com/service/bpel/common] as [prm]
<value xmlns="http://schemas.oracle.com/service/bpel/common">1</value>

But for flexibilities sake, I would propose a slightly different approach. And for that I need an adapted version of the XSD:
<?xml version="1.0" encoding="windows-1252" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bplcmn="http://schemas.oracle.com/service/bpel/common"
xmlns="http://schemas.oracle.com/service/bpel/common"
targetNamespace="http://schemas.oracle.com/service/bpel/common"
elementFormDefault="qualified">
<xsd:element name="parameters">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="bplcmn:item" minOccurs="1" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="item" type="bplcmn:itemType"/>
<xsd:complexType name="itemType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="value" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

In this XSD I created 'item' as a seperate element based on a seperated (named) complex-type. I'm actually not so fond of nested complex-types, because it prevents you from using elements lower in the hierarchy for seperate variables.
Using this XSD, you can create a seperate item-variable, based on the item-element.
This is item-variable can be filled with a name and value, just by copying to the particular elements.
The item variable has then to be added to the parameters node using the addChildNode function:
ora:addChildNode(bpws:getVariableData('XsltParams','/bplcmn:parameters'),bpws:getVariableData('item','/bplcmn:item'))
The first argument of the addChildNode function denotes the element under which you want to add a child. In the expression above this is the 'parameters' element in the XsltParams variable. The second argument is the node you want to add as a child, in this case the 'item'-variable. Of course this expression is the <from>-expression in the copy-rule of the assignment step. The <to> will be most of the times the parent element used in the addChildNode expression:
<to variable="XsltParams" query="/bplcmn:parameters"/>
I found the description of the addChildNode function in the expression builder not so clear. So this might also be helpfull for other situations where you have to build up a node structure dynamically.

Wednesday, 8 April 2009

Darwin XML Tester Utilities on Sourceforge

A few weeks ago I requested a project on Sourceforge to host our XMLTester toolset. It was granted by the Sourceforge admin team, so I am now able to transform my toolset project to a real opensource project. You can find the project at darwinxmltester.sourceforge.net.
For the download I used my own deployed directories and zipped them. In the near future, when I get familiar with the sourceforge capabilities, I hope to create a more nice deployment with a nice description on the use. Also I'll include a seperate export of the sources. But for that I have to create a tag on which I also base the deployment. So that I ensure that the sources are in line with the deployement.
The toolset exist of:
  • Hierarchical XMLEditor, that is also available in a standalone app.
  • Plain XML Edit Panel
  • XPath edit/test Panel
  • XSLT edit/test Panel
The plans I have for the toolset are the following.

XMLEditor
  • Enhance the exception handling and perhaps logging
  • Add copy and paste capability to copy a child-node to another parent
  • Enhance the copy-node capability to include attributes and child nodes (now it just creates another node with the same name within the same parent).
  • Add insert new node within the child-table. Now you have to first click another sibbling node within the same parent, click copy-node and then click on the parent to edit the attributes.
  • Give the buttons nice icons instead of the text (text to tooltips).
XML-Panel
Basically this is a simple panel with a TextArea in a Scrollpane. The only smart thing it does that is that the scrollpane resizes with the main-frame.
The textarea should be replaced by a text-editor. Preferably one that supports keyword highlighting and that is expandable so that you can do neat tricks like keyword-completion etc. There are opensource plugable editors that do this. So I have to find out which one to use.

XPath Panel
Lately I found out how to use Namespaces in xpath queries in Java. I wrote about it in this blog. So the main thing I want to do here is that it finds the namespace declarations in the xml and put them in a pop-list as well as in a namespace-resolver. And of course a button that adds the choosen namespace to the xpath query. Also it should be aware of the existing nodes that are valid on the particular level. So that it adds them automatically.

XSLT Panel
Besides extending the Error handling like mentioned in the XML Editor to-do list I want to replace the TextAreas with TextEditors here as well.

Bulk XSLT
I already created a simple solution to do XSLT in batch. You can create a simle xml file that contains several rows of xml, xslt and output combinations that it will perform in sequence. I want to create a nice screen that enables you to edit and execute this batch-file.
Also I think of creating a database connection, because in some of my projects I used this to create objects based on xml-queries in the Oracle database. So if I have a table in which I have these XML files, I would be able to create this bulk file from that.

JDeveloper Plugin
Lately I downloaded a tutorial that shows how to create a JDeveloper plugin. Now I know that JDeveloper is perfectly capable of editing XMLFiles. It has much more functionality then I'll plan to include. The nice think of this toolset is that it is simple and small. It should not exceed much more than a few hundreds of kilobytes.
But the hierarchical XML Editor would be nice to have as a plugin. Also the bulk XSLT could be a nice JDev plugin.

Finally
I feel that I forgot something that I came up with lately. So ofcourse during time I'll find other nice functionality. If you have any ideas feel free to leave a comment. I keep you posted.
Of course there are much other tools that can do the same and often much more. JDeveloper I allready mentioned, is also free to use. Darwin-IT stands for knowledge sharing. Expanding the Oracle Consultant market with improved skilled people, through workshops, trainings, coaching on the job and for example this blog. I feel that this open source project is also a nice way of sharing knowledge. And an effective way to learn myself. And it gave me a productivity boost on some of my project.
I happily use the XMLEditor regularly to edit my xml file with Bookmark-links. I created a xslt that is referenced in this xml file and that transforms the XML to a html page with the bookmarks in poplists. The XML file is my Home-page in my browser. Maybe I'll package that also as an example project.

Wednesday, 7 January 2009

Xpath error in BPEL assign after transform step

Recently I ran into an xpath error in an assign step in BPEL. The error denoted that the xpath expression is in error because of a node not found. Unfortunately it does not quite clearly state if it is about the "from"-xpath expresssion or the "to"-expression. And strangely enough the copy-rule-wizard lets you select the proper element, so the xpath-expression is clearly correct!

I found out that in this case I had a xslt-transform-step somewhere before the assign step. In this step I initialized the variable, but in the later assign step I assigned some other elements that I could not determine during the transform, because these values came from other variables.
In the XSLT I did not explicitly fill the elements that I assigned later. Apparently the transform step creates only those elements that are explicitly named in the XSLT. Those that are not defaulted or filled, are thus not created.

I solved it by adding the node element to the xslt, filling it with a simple xsl:text element with a space or something.

So I conclude that an non-existing node is apparently not the same as an empty element!

Monday, 5 January 2009

Xpath expressions with namespaces on XMLType

I used to use XMLTypes with xml where I tend to prevent the usage of namespaces. Namespaces make things difficult where they are often unneeded. That is I experienced that it is hard. An expression like '/level1/level2/level3' is much simpeler then '/ns1:level1/ns2:level2/ns3:level3'. Especially if you don't know how to give in the particular namespaces.

Lately I wanted to store the results of a BPEL Process in the database to be able to query parts from that in a later process. Actually I processed several files in the one process and wanted to query the statuses in the later process again. If you work with BPEL you cannot avoid using namespaces. I also wanted to put the results in an XMLtype as is, since it prevented me to create a complete datamodel. A simple xmltype-table suffices.

I then created a pl/sql function with the filename as a parameter that fetched the right row in the results table and gave back the xmltype column in which the status of that file was stored. But then, how to query it with XPath?

I knew the xmltype.extract() function. But what next? You can avoid namespaces by using the local-name() xpath function, but then it is a little hard to get the value of the particular file.

Luckily the extract() function has another parameter, the namespace string:

extract(XMLType_instance IN XMLType,
XPath_string IN VARCHAR2,
namespace_string In VARCHAR2 := NULL) RETURN XMLType;

This namespace string can contain the namespace declarations to be used in an xpath expression. The namespace declarations look like:
namespace-shortage=uri
for example
ns1="http://www.example.org/namespace1"
You can have multiple declarations separated by white space.

An example is as follows:
declare
xp_no_data_found exception;
pragma exception_init(xp_no_data_found, -30625);
l_xpath varchar2(32767) := '/ns1:level1/ns2:level2/ns3:level3';
l_nsmap varchar2(32767) := 'ns1="http://www.example.org/namespace1" ns2="http://www.example.org/namespace2" ns3="http://www.example.org/namespace3"';
l_xml xmltype;
l_clob clob;
begin
l_xml := function_that_gets_an_xmltype_value();
l_clob := l_xml.extract(l_xpath, l_nsmap).getclobval();
end;

Read more about it on page 4.6 of: http://download-uk.oracle.com/docs/cd/B19306_01/appdev.102/b14259.pdf.

Mark also that there are actually two functions: extract() and extractvalue(). Extract() returns an xmltype, that can be 'sub-queried'. The extractvalue() function returns the datatype of the variable that the value is assigned to. With the xmltype functions getstringval(), getclobval(), etc. you can also get the particular value of a node of the xmltype. However, there is a slight difference between the result of the getstringval() and correspondingfunctions and Extractvalue. And that is that the Extractvalue returns the unescaped value of the node (the encoding entities are unescaped), while getstringval() returns the value with the entity encodings intact .

Wednesday, 10 December 2008

XPath evaluation in Java using Namespaces

Earlier this year I wrote an article on testing Xpath expressions and XSL transformations in Java. This is no that hard if you know how to do it.

What I did not mention there is how to do xpath-queries on documents with namespaces.
Take for example the following xml:



<?xml version="1.0" encoding="UTF-8" ?>
<XSLBatch xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.darwin-it.nl/XMLTypes/XSLBatch ../xsd/XSLBatch.xsd"
xmlns="http://www.darwin-it.nl/XMLTypes/XSLBatch">
<XSLTransform>
<Order>1</Order>
<SourceXMLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xml/sos_exportjobs.xml</SourceXMLFile>
<XSLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xsl/CreateObjectType_0.2.xsl</XSLFile>
<DestinationFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/output/sos_exportjobs.tps</DestinationFile>
</XSLTransform>
<XSLTransform>
<Order>2</Order>
<SourceXMLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xml/sos_exportjobs.xml</SourceXMLFile>
<XSLFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/xsl/CreateObjectTypeBody_0.2.xsl</XSLFile>
<DestinationFile>/home/makker/Projects/Java/trunk/src/JavaAndXML/XMLTester/workspace/output/sos_exportjobs.tpb</DestinationFile>
</XSLTransform>
</XSLBatch>


If you want to do a query on all XSLTransforms, you would probably write an Xpath of the form:
/XSLBatch/XSLTransform

But if you try to do that with the statement:

private XMLDocument xmlDoc;
...
public NodeList selectNodes(String xpath) throws XSLException {
NodeList nl = this.xmlDoc.selectNodes(xpath);
return nl;
}

then you find out that the NodeList nl would not deliver you any nodes.
This is because the XML document is of namespace: "http://www.darwin-it.nl/XMLTypes/XSLBatch" and this is not taken into account in the xpath expression above.
You could use an expression in the form of:
/xbt:XSLBatch/xbt:XSLTransform

But then: how does the parser know to what Namespace the abbreviation xbt resolves?

You could avoid the problem above with the expression:
/*[local-name()="XSLBatch"]/*[local-name()="XSLTransform"]
This is especially usefull if you have no means of specifying the namespaces you used.
But it makes the expressions very complex and if you need to query on a specific value of an attribute then you're out.

You can resolve this in Java quite easily with a NameSpace resolver. A what? A NameSpace resolver is a class that implements the oracle.xml.parser.v2.NSResolver interface. That is: using the Oracle XML parser.

A sample implementation of the Namespace Resolver is as follows:
package com.m10.xmlfiles;

import java.util.HashMap;

import oracle.xml.parser.v2.NSResolver;

public class XMLNSResolver implements NSResolver{
  private HashMap nsMap = new HashMap();
  
    public XMLNSResolver() {
    }

   public void addNS(String abbrev, String namespace){
       nsMap.put(abbrev, namespace);
   }
    public String resolveNamespacePrefix(String string) {
        return (String)nsMap.get(string);
    }
}

As you can see this implementation is fairly simple. It has a HashMap in which you can store your namespaces with an abbreviation as a key.
Since it inmplements the NSResolver interface it must implement the resolveNamespacePrefix. And that one does exactly what you might expect: it gives the namespace back that belongs to the abbreviation.

So if you do the following:
XMLNSResolver nsRes = new XMLNSResolver();
nsRes.addNS("xbt", "http://www.darwin-it.nl/XMLTypes/XSLBatch");
Then you can do your xpath query as follows:
NodeList nl = this.xmlDoc.selectNodes("/xbt:XSLBatch/xbt:XSLTransform", nsRes);

And that is how this is done.

Tuesday, 2 September 2008

Java Swing XML Editor and Tester

Today I fine-tuned my XML Editor and Testing tools that I published earlier. The main improvements are that I integrated the XML Editor in my XMLTesting tool. For this I had to enhance and restructure my XML Editor to make it a "plugable" JPanel. Another thing is that I had to make sure that changes in one panel is propagated to the central File-Objects. This latter need some improvements because the capturing of the window-change-events are aparently not so evident in Java Swing. And also I keep copies of the File Objects. I need to improve that in the near future.
You can download my xml-editor here. And the xmltesting-tool here. Just unpack the zips into a directory and adapt the scripts (I included a windows bat file and a linux shell script) so that the your java virtual machine is referenced properly. Maybe I should change it so that it relies on having java in your path.

Sunday, 17 August 2008

Second release XML Editor

You won't get another version of Mozilla Firefox or Thunderbird as fast as another release of my XMLEditor. But at the other hand, it might not be for long that the release frequency of Mozilla beats mine. Anyway I couldn't help hacking a little on my editor.

It struggled me that the element-content was not editable properly. So I added a button "add text node". It will add a text node with a default text "Node Text". This text is editable in the child-elements-table when selecting the parent node.

Besides that I added toolbar buttons for new file and save file.

The new version is downloadable here.

Friday, 15 August 2008

First release XML Editor

The last few weeks around my vacation, in my "in-between-hours" I created a light-weight XML Editor. As you could have read before, earlier I created an XML editor in Java Swing to test XPath expressions and XSLT's. But I lacked a nice editor to build up your xml files.

For my bookmarks I created an xml file with an xslt-stylesheet that transforms the xml to an html-page with poplists where you can choose a link, hit a button and it the browser will show you the page. Windows has a nice feature (from Windows 98, I think) that's called the Active Desktop. You can place links to html pages on your desktop and Windows will embed the page into your desktop. I used that to show my poplists right on the desktop. That's one feature that I miss in KDE at the moment. KDE seems to have such a possibility, but I could not get it working. Also Windows can have multple pages on your desktop and you can denote the area where to show this page.

Anyway, the xml-file I had to edit with a ascii-editor. Notepad++ would do the job nicely. There are applications as XMLSpy (which needs a license) or jDeveloper (which is too big for the job) that can help you. These packages are smarter then mine, since they're able to take an XSD and let you edit the xml according to the XSD. Also in the past I saw xml editors (I remember one called Peter's XML Editor). But they're nearly allways only for Windows (as the XTrans-tool that drove me to create the XMLTester tool). Having it in Java makes it portable to both platforms.

Another thing is that XML is by nature Hierarchical. So I would like to browse through my XML hierarchically and then edit the parts I want to change.
Besides that, it seems a nice project to explore Swing components like the JTree and the JTable and play with DOM to change the xml. So it'll give me a little more "Fingerspitzengefuhl" with the java DOM api's. In the past I had this with the Pl/Sql dom-api's of the database.

Yesterday I finished my first version. You can download it here. Just unzip it to a directory.
To run it I added a shell script (for Linux) and a bat file (for Windows). You'll have to edit it to change the path to your JRE (Java Runtime Environment).

It's very basic in it's functionality. But I think it's pretty straightforward and intuitive. You can start it with the xmlfile as a parameter to the script. I did that to have a link on my desktop to edit my LinkLists-xml-file. Then it will parse and process the file given. But of course you can also open the file with the open button and create a new file with the File-new Menu option.
To create a new xml file you have to give in a node name that is used for the root node.

Of course this little app might need some improvements. One of them is that I changing a node value does not behave like I expect. It's not so that a node value is seen back in the file when saved. Probably I have to explore that a little more. But adding and changing attributes, adding and copying nodes work fine. The copy node does not copy a complete node but just creates a node on the same level with the same name. I could add functionality that really copies a node tree.

Another thing I'd like to do is to integrate this into my XML Tester tool. Then I have a little "suite" to create, edit and test XML files with XSLT's.

I hope you enjoy it and find it usefull. If you have comments, ideas for improvements, feel free to post them. Since I have to do it in the scarce, free time I have I can't do promises on the proposed changes.

Tuesday, 15 July 2008

Code Generation with XSLT

Last week I read a nice article of my con-colleague Lucas Jellema about “PL/SQL Table-to-Java Bean (and Data to Java Bean Manager) generator - useful for data driven demos without database ” at the AMIS-blog on:http://technology.amis.nl/blog/?p=3272.

In this blog Lucas writes how he generated java-code from Pl/Sql. It drew me back to a little project I did for myself and my former employer as a BusinessDevelopment-job. I wanted to make a simple method to enable a convential custom development application, like a Designer/Developer Forms application, like we build a million times in the past. Since it is primarly aimed for the hundreds of Forms applications at our customers that are primarly build with Designer and Headstart, I could do it in pl/sql also or even create a Headstart utility for it. But for the latter option I would learn myself Headstart-utility principals again, that did not look to useful for me. Pl/Sql would do. But I would have it flexible having the ability to generate several pieces of code from one source.

The problem with the code of Lucas, that is when you see it as a problem, is that all the code generation is in pl/sql. To change the outcome would mean that you should change your pl/sql code. I would like to have a more template-based solution.

A few years ago I wrote an article on “XML using Sql” for our monthly consulting paper at Oracle. See my post: http://darwin-it.blogspot.com/2008/07/xml-using-sql.html. With this knowledge it is easy to generate XML out of the datadictionary of the database. Then based on this xml you can generate all you want using ... XSLT.

Read further (pdf).

Thursday, 10 July 2008

XSLT in Java with Oracle Parser part 2

Earlier I explained how to do an XSLT transformation using the Oracle Parser: http://darwin-it.blogspot.com/2008/06/xslt-in-java-with-oracle-parser.html.
I also created a Java Swing application to do transformations. I unfortunately found that my transformations did not went as expected. I'm busy with doing an XML to HTML transformation and my HTML tags did not get transformed. It works fine when the result of the transformation is just plain text.

The problem lies in the use of a XMLDocumentFragment as a result type of the xslProcessor.processXSL XSL processing. It took me a while to find a solution, since I have the requirement that the result gets into a String.

The xslProcessor has some processXSL methods that output to for example a URL or a PrintWriter. But how to get that into a String again?

Luckily today I found the solution. You have to wrap a StringWriter into a PrintWriter. Then give the PrintWriter to processXSL. Then after closing both, you can simply do a toString of the StringWriter. It looks like this:

public String transform(XMLDocument xslDoc, XMLDocument xmlDoc) {
XSLProcessor xslProcessor = new XSLProcessor();
XSLStylesheet xslt;
String result = "";
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
xslt = xslProcessor.newXSLStylesheet(xslDoc);
xslProcessor.setXSLTVersion(xslProcessor.XSLT20);
xslProcessor.showWarnings(true);
xslProcessor.setErrorStream(System.err);
xslProcessor.processXSL(xslt, xmlDoc, pw);
pw.flush();
pw.close();
sw.close();
result = sw.toString();
} catch (XSLException e) {
pl(e.toString());
} catch (IOException e) {
pl(e.toString());
}
return result;
}


I adapted it to my XMLTester tool that I just uploaded here.

XML using SQL

A few years ago already I created an article that explained how to generate XML using Oracle SQL. It works from Oracle 9iR2,10g onwards. It's in Dutch, but it contains step-by-step examples so maybe also usefull for non-Dutch.
You can download the article here.

Thursday, 26 June 2008

Testing XSL and Xpath with Java Swing

This week I finished a first version of a little java application that I created to build and test xpath expressions and xslt stylesheets.

I was very charmed with the little tool XTrans( http://www.simxtech.com/users/zc2/xtrans/). This smart tool is extreemly small (only a few KB) and enables you to edit xslt's, load xml files and transform them with the created xslt. Unfortunately for me is that it is a windows application, based on the MSXML parser. And I've not found a counterpart on Linux yet. Oh, of course you could do a lot of that with jDeveloper or XMLSpy. Maybe even better. But XTrans is so small and if you're working with xslt's, sometimes the only thing you need is a good ascii-editor and an xmlparser. And a little driver application that helps you with driving your xml and xslt trough the xmlparser.

So I found it usefull to create my own Xsl and xpath tester application.

Besides the arguments above it was also a nice excersise to play a little with the xmlparser (I used the Oracle parser shipped with jDeveloper). Although I have experience with xml, xslt and xpath, I mainly used it from Xtrans, jDeveloper or the Oracle Database (from Pl/Sql). Not directly in Java. And it is very usefull to be able do that. For example when you need to create a custom method within an ADF application, embedded Java or Java WSIF binding in BPEL.

Last year I created two little applications to test-drive messages into Integration B2b and into Axway Cyclone (another B2B product). These little applications help you to send messages with these products based on their configuration (repository or cpa). For these applications I had to parse a CPA (Collaboration Protocol Agreement) to populate the different pop-lists. Back then I did it just by browsing through the NodeLists. I created a few wrapper classes to help me with that. But I couldn't find the proper method to execute a xpath-expression. Also that turns out to be simple, when you know how:

...
private XMLDocument xmlDoc;
...
public NodeList selectNodes(String xpath) throws XSLException {
NodeList nl = this.xmlDoc.selectNodes(xpath);
return nl;
}


Another nice side effect of these little apps is that I got me to play a little with Java Swing. Nowadays people create enterprise applications using frameworks like Oracle Java ADF, JSF, JSP, ADF Components for Java, etc. But for lots of things you don't need and thus don't want the large footprint of a Application Server. And then Java Swing is very nice. It brought me back memories of Oracle Forms, with it's triggers and so on. Also it looked very familiar, thinking back, to Borland Delphi. But that's not strange: jDeveloper originated years ago from Borland jBuilder. Back then Borland used to create smart IDE's for Turbo Pascal/Delphi, C/C++ and Java. And they were in fact all the same. Oh, how much fun I had with Turbo Pascal 5.5. You could build the world with an IDE that fitted on a single floppy.

jDeveloper enables you to 'paint' your frames and panels. And double clicking on a button creates an ActionListener that you only have to fill with a call to an appropriate method.

The two B2B-applications I created last year were my first Swing apps after years. I had to re-invent how to build up an application using Swing-components and containers. When you create a Swing project in jDeveloper it gives you an application and a class that is an extension of JFrame. That's fine if you're building a one-screen application. But if you want a multi-screen application with the different screens changeable from a menu from the same main-frame, that turns out to be unhandy.

What I did was to abstract the menu and the toolbar to separate classes to maintain. In Oracle Forms the menu is also a separate module. It's a pity that there is no graphical method to build up the menu.

Also the panels became separate classes that are extensions of JPanel. I created a little method in the main-frame, that replaces the central-content-pane with one of these panels. This method I can call from the menu.

Then I created a "resize sub-components" method in the main frame class:
private void resizeSubComponents() {
int width = this.getWidth();
int height = this.getHeight();
xmlFilePanel.resizeComponents(width, height);
xpathTestPanel.resizeComponents(width, height);
xsltTestPanel.resizeComponents(width, height);
}

that is called from the Frame's component-listener:
this.addComponentListener(new ComponentListener() {
public void componentResized(ComponentEvent componentEvent) {
resizeSubComponents();
}

public void componentMoved(ComponentEvent componentEvent) {
}

public void componentShown(ComponentEvent componentEvent) {
}

public void componentHidden(ComponentEvent componentEvent) {
}
});

This method calls the resize methods in the different panels. When the mainframe is resized or maximized the different panels get resized also. Since I have several textareas with the xml-files in it, it is handy that they grow and shrink with the main frame, maximizing the available screen area.

What I have to check out is the use of inner-frames. I read about it. I want to give each panel it's own frame within the mainframe. then you have an application that is like Oracle forms, with it's different modules within the main-window. Something like a wordprocessor that can have multiple documents open.

So Swing is very nice and very powerfull to create small applications that run everywhere. But you have to find yourself a way to split up your application in smart components that are separatly maintainable. jDeveloper does not help you much with that.

I uploaded my tool on http://www.darwin-it.nl/downloads/xmltester_v0.1.zip. Just unzip it, and then change the xmltester.sh or xmltester.bat file. In the file you see a parameter "JAVA_BASE". You should change the file path to the proper location of jour Java JRE or SDK.