XInclude

As previously mentioned here and here I’m using external entities at present to redue the amount of dublicate data when sharing data among XML documents. A better choice than entities would be to use XInclude which is a recommendation from W3C on how to include XML documents in other XML documents. The difference betwwen entities and XInclude is that XInclue is an extension to the core XML and that it integrates nicely with the other XML extensions such as XSLT. An example of using XInclude is show below:

<code>
  <xi:include href="MyFile.xml" />
</code>

Unfortunately XInclude isn’t supported in the LotusScript XML classes – probably because it’s only a recommendation. It isn’t supported in the Java included in Notes/Domino either since Notes 7 has Java 1.4.x which in turn has JAXP 1.2 (Java API for XML Processing) which is too old. XInclude is however included in JAXP 1.3 so if you need it you can upgrade your XML processing libraries to the latest Xerces from Apache.

A caveat might be however that Notes/Domino is shipped with a combined Xalan (XSLT) and Xerces (XML parser) library (/jvm/lib/xml.jar). I haven’t tried updating this library but would like to hear from people having tried. An alternative is off cause to include the new xerces.jar in the agent or similar so the new classes are used.

Resources:

Inside The Net 24: The Two Mikes from Firefox

The new episode of the Inside The Net podcast is about Firefox and the Two Mikes. Show description from the TWIT homepage: “Hard on the heels of the successful launch of Firefox 1.5 comes the first beta of Firefox 2.0. Mike and Mike talk about Firefox Flicks and what’s new in 2 including lookahead searching, RSS support, and faster browsing.”

Show ‘n Tell Thursday: Using XSLT on DXL documents (8 June 2006)


Using XSLT on XML is a great way to filter the data before parsing the result document. With DXL this is even more true to the VERY verbose nature of DXL. An advantage of using XSLT to filter the DXL document before parsing is that it is much easier to filter based on XPath queries rather than walk the DOM tree or using SAX parsing and writing and handling all the events that would arise from parsing a DXL document.

If you are not familiar with XPath or XSLT I suggest you read up on the technologies. Although I haven’t used the tutorials myself the tutorials provided on w3cschools.com tends to be quite good. At the site you’ll find tutorials on both XPath and XSLT.

I would also like to plug my upcoming article in the May/June issue of THE VIEW where I also discuss this subject and how I used the techniques in LotusScript.doc… 🙂

Well basically using XSLT with DXL is quite simple if you know how (isn’t everything). The only caveat is that you have to remember that the DXL documents exported from Lotus Notes use the dxl-namespace so you’ll have to import the namespace and use the namespace in all XPath queries. Normally a XSLT stylesheet starts like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

When using XSLT on DXL documents you’ll have to import the dxl-namespace as well (change in bold):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:dxl="http://www.lotus.com/dxl" version="1.0">

Once you have done this you can use XPath as normally and make your templates match their target. The below example will create a bullet list of the script library names from a complete export of a Notes database (notice the use of the dxl-namespace in bold):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:dxl="http://www.lotus.com/dxl"
     version="1.0">
     <xsl:output method="html" version="1.0" encoding="iso-8859-1" />

     <xsl:template match="/">
       <ul>
         <xsl:apply-templates select="/dxl:database/dxl:scriptlibrary" />
       </ul>
     </xsl:template>

     <xsl:template match="//dxl:scriptlibrary">
       <li>
         <xsl:value-of select="@name" />
       </li>
     </xsl:template>

</xsl:stylesheet>

Applying the above style sheet to an export of the LotusScript.doc database will produce a bullet list like the one below:

  • CLASS: Script
  • CLASS: ScriptParser
  • CLASS: ScriptElement
  • CLASS: Collections
  • CLASS: InputStream
  • CLASS: ScriptFactory
  • CLASS: OutputStream
  • CLASS: DocumentationWriter
  • CLASS: Index
  • CLASS: OutputStreamFactory
  • CLASS: ClassHierarchyIndex
  • CLASS: ScriptSource
  • DBDesign
  • CLASS: DocumentationBuilder
  • CLASS: LSDocConstants
  • CLASS: Comment
  • CLASS: DecisionMaker
  • CLASS: ConfigDocuments
  • CLASS: TemplateVersion

An easy way to test XSLT style sheets if you do not have an IDE capable of helping you is to associate the style sheet with the DXL document and then preview using a browser. To see how see my post called Apply XSLT style sheet to an XML document for browser friendly display.

Hope the above helps someone…

Domino SuperSearch

In the June issue of the LotusUserGroup.org Developer Tips Newsletter Duffbert writes about the Domino SuperSearch over at martinscott.com. Although a nice application what if the we had an application like that which also searched through all the blogs listed at dominoblogs.com? Wouldn’t that be cool and a nice way to “take a penny, leave a penny”? 🙂

If dominoblogs.com emitted an OPML file it could be consumed by a site that allows users to search the sites. Only issue I can see it the difference between search interfaces but it could be solved by people also submitting a search URL to the dominoblogs.com site.

What do you think?

Apply XSLT style sheet to an XML document for browser friendly display

As posted posted previously I’m doing quite a lot of work with XML these days. As part of the process I’m writing XSLT style sheets to make sure the XML documents displays nicely in the browser if the user decides to open the XML document directly in the browser. Doing so is surprisingly simple.

A practical example of linking a style sheet to an XML document is used in many RSS/ATOM feeds to let the user know what is they opened and how they should really handle the feed.

Suppose you have a XML document describing a traceroute as shown below. What you really want is for the user to see a HTML table summarizing the traceroute command in the browser instead of the barebones DOM tree. To do so you only need to add a <?xml-stylesheet /> tag at the top of the XML document with the URL to a XSLT style sheet.

Below you can see an example of an XML document with an embedded <?xml-stylesheet /> tag, the XSLT style sheet and the HTML table that results from the transformation.

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="traceroute.xsl" type="text/xsl"?>
<traceRoute target="yahoo.com" hostAddress="66.94.234.13"
  hostName="w2.rc.vip.scd.yahoo.com" initiated="2006-05-17T09:46:18.378"
  completed="2006-05-17T09:46:25.668">

  <hop hopNumber="1" hostAddress="192.168.1.1"
    hostName="192.168.1.1">
    <timing responseReceived="true" roundTripTime="2" />
    <timing responseReceived="true" roundTripTime="1" />
    <timing responseReceived="true" roundTripTime="1" />
  </hop>
  <hop hopNumber="2" hostAddress="80.243.112.1"
    hostName="1.112.243.80.vl20-cph.dhcp.clearwire.dk">
    <timing responseReceived="true" roundTripTime="28" />
    <timing responseReceived="true" roundTripTime="67" />
    <timing responseReceived="true" roundTripTime="33" />
  </hop>
  <hop hopNumber="3" hostAddress="208.49.181.237"
    hostName="ge-1-1-0.400.br1.CPH1.gblx.net">
    <timing responseReceived="true" roundTripTime="37" />
    <timing responseReceived="true" roundTripTime="58" />
    <timing responseReceived="true" roundTripTime="39" />
  </hop>
  <hop hopNumber="4" hostAddress="67.17.64.69"
    hostName="so7-2-0-2488M.ar1.DCA3.gblx.net">
    <timing responseReceived="true" roundTripTime="220" />
    <timing responseReceived="true" roundTripTime="156" />
    <timing responseReceived="true" roundTripTime="117" />
  </hop>
  <hop hopNumber="5" hostAddress="208.51.74.182"
    hostName="yahoo-2.ar1.DCA3.gblx.net">
    <timing responseReceived="true" roundTripTime="115" />
    <timing responseReceived="true" roundTripTime="121" />
    <timing responseReceived="true" roundTripTime="142" />
  </hop>
  <hop hopNumber="6" hostAddress="216.115.101.130"
    hostName="so-3-1-0.pat2.pao.yahoo.com">
    <timing responseReceived="true" roundTripTime="234" />
    <timing responseReceived="true" roundTripTime="193" />
    <timing responseReceived="true" roundTripTime="195" />
  </hop>
  <hop hopNumber="7" hostAddress="216.115.106.181"
    hostName="ge-3-0-0-p250.msr2.scd.yahoo.com">
    <timing responseReceived="true" roundTripTime="189" />
    <timing responseReceived="true" roundTripTime="185" />
    <timing responseReceived="true" roundTripTime="187" />
  </hop>
  <hop hopNumber="8" hostAddress="66.218.82.219"
    hostName="ten-1-3-bas2.scd.yahoo.com">
    <timing responseReceived="true" roundTripTime="210" />
    <timing responseReceived="true" roundTripTime="206" />
    <timing responseReceived="true" roundTripTime="201" />
  </hop>
  <hop hopNumber="9" hostAddress="66.94.234.13"
    hostName="w2.rc.vip.scd.yahoo.com">
    <timing responseReceived="true" roundTripTime="186" />
    <timing responseReceived="true" roundTripTime="184" />
    <timing responseReceived="true" roundTripTime="210" />
  </hop>
</traceRoute>

<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
  <xsl:output method="html" version="1.1" encoding="iso-8859-1" />

  <xsl:template match="/traceRoute">
    <html>
    <body>
    <table border="1">
    <xsl:for-each select="//hop">
      <tr>
      <td>
      <xsl:value-of select="@hopNumber" />
      </td>
      <td>
      <xsl:value-of select="@hostAddress" />
      </td>
      <td>
      <xsl:value-of select="@hostName" />
      </td>
      </tr>
    </xsl:for-each>
    </table>
    </body>
    </html>
    </xsl:template>
</xsl:stylesheet>

1 192.168.1.1 192.168.1.1
2 80.243.112.1 1.112.243.80.vl20-cph.dhcp.clearwire.dk
3 208.49.181.237 ge-1-1-0.400.br1.CPH1.gblx.net
4 67.17.64.69 so7-2-0-2488M.ar1.DCA3.gblx.net
5 208.51.74.182 yahoo-2.ar1.DCA3.gblx.net
6 216.115.101.130 so-3-1-0.pat2.pao.yahoo.com
7 216.115.106.181 ge-3-0-0-p250.msr2.scd.yahoo.com
8 66.218.82.219 ten-1-3-bas2.scd.yahoo.com
9 66.94.234.13 w2.rc.vip.scd.yahoo.com

Rediscovering a lost gem – Stopwatch

Got a call from a customer reporting that the news on frontpage on their intranet was appearing very slowly. The news are displayed using an AJAX approach for asynchroneous loading using an agent as the data source for the XML. So far I haven’t been aware of any performance problems but “suddenly” the functionality is performing very poorly. Time to debug.

While pondering how to troubleshoot the performance problems I remembered the Stopwatch LotusScript class. I have used the class previously in other projects and it works like a charm. The class is originally from a THE VIEW article called “Performance Testing LotusScript Code Using Object-Oriented Design Techniques” by Burke LaShell (January/February 2000).

Using the class is a simple as these steps:

  1. Download the stopwatchclass.nsf database from the article abstract page.
  2. Copy the Stopwatch script library from the database to the database you would like to use it in.
  3. Use the Stopwatch class using its very simple API.
  4. After each run the class will produce a nice looking e-mail report.

Below is an example of using the class. The code should be self-explainatory but notice the use of the Stopwatch class and its Start and Stop methods.

Sub Initialize
   'declarations
   Dim session As New NotesSession
   Dim docContext As NotesDocument
   Dim subsite As String
   Dim fetcher As NewsFetcher
   Dim xml_conv As XmlConverter
   Dim repositories As Vector
   Dim xml As String
   Dim query_string As String

   'create stopwatch
   Dim stopwatch As New Stopwatch

   'error
   On Error Goto catch

   'get document context
   Set docContext = session.DocumentContext

   'get QueryString
   query_string = docContext.QUERY_STRING(0)

   'get the requested sub-site key if any (i.e. sdainfo, scaninfo)
   subsite = Strrightback(query_string, "&subsite=")

   'create a NewsFetcher
   Call stopwatch.Start(|New NewsFetcher|)
   Set fetcher = New NewsFetcher(subsite)
   Call stopwatch.Stop(|New NewsFetcher|)

   'create a XMLConverter
   Call stopwatch.Start(|New XmlConverter|)
   Set xml_conv = New XmlConverter()
   Call stopwatch.Stop(|New XmlConverter|)

   'get the news documents
   Call stopwatch.Start(|fetcher.GetRepositories()|)
   Set repositories = fetcher.GetRepositories()
   Call stopwatch.Stop(|fetcher.GetRepositories()|)

   'convert the documents to XML
   Call stopwatch.Start(|xml_conv.Convert()|)
   xml = xml_conv.Convert(repositories)
   Call stopwatch.Stop(|xml_conv.Convert()|)

   'write the XML back to the browser
   Call stopwatch.Start(|Print XML|)
   Print "Content-type: text/xml"
   Print xml
   Call stopwatch.Stop(|Print XML|)

   'exit
   Goto finally

catch:
   Print Error & ", line: " & Erl
   Resume finally
finally:

   'send
   Call stopwatch.MailAllWatchValues(|jdoe@example.com|, |Agent Debug - "(WEB - NewsFeed)"|)

   'exit
   Exit Sub
End Sub

The above code will produced a report like the one shown below for each run (the report is e-mailed to jdoe@example.com). As you can see the total run time was 8,8 seconds (!!!) with the fetcher.GetRepositories() call taking up all of the time.

seconds     %        calls     secs/call   event
===========================================================================
00008,812   100,0%   0000001   00008,812   Total run time
00008,812   100,0%   0000001   00008,812   fetcher.GetRepositories()
00008,812   100,0%   0000001   00008,812   NotesRepo.GetDocs() - get news docs
00000,000   000,0%   0000001   00000,000   New NewsFetcher
00000,000   000,0%   0000001   00000,000   New XmlConverter
00000,000   000,0%   0000001   00000,000   NotesRepo.GetDocs() - get view
00000,000   000,0%   0000001   00000,000   xml_conv.Convert()
00000,000   000,0%   0000001   00000,000   Print XML

Using this data I was able to diagnose and solve the problem. Below is performance data from the agent after solving the problem (a view index refresh problem) – notice how the runtime is now a mere 0,015 seconds…

seconds     %        calls     secs/call   event
===========================================================================
00000,015   100,0%   0000001   00000,015   Total run time
00000,015   100,0%   0000001   00000,015   xml_conv.Convert()
00000,000   000,0%   0000001   00000,000   New NewsFetcher
00000,000   000,0%   0000001   00000,000   New XmlConverter
00000,000   000,0%   0000001   00000,000   fetcher.GetRepo()
00000,000   000,0%   0000001   00000,000   NotesRepo.GetDocs() - get view
00000,000   000,0%   0000001   00000,000   NotesRepo.GetDocs() - get first document
00000,000   000,0%   0000001   00000,000   NotesRepo.GetDocs() - loop documents
00000,000   000,0%   0000001   00000,000   Print XML

There are two caveats to using the Stopwatch class:

  • The agent has to allow restricted operations since the script library uses a function from kernel32.dll.
  • Since the class uses kernel32.dll the script library only works on Windows.