Tuesday, 31 December 2013

Recursive XSLT: Concat Multiple Nodes

Sharing this XSLT which I used couple of years back to concat a node set. The usecase was to extract a specific node set and concatenate their values.

Sample Input:
<root>
    <node>
        <data>123</data>
        <data>456</data>
        <junk>abc</junk>
    </node>
    <junknode>
        <junk>def</junk>
    </junknode>
    <node>
        <data>789</data>
        <junk>ghi</junk>
        <data>012</data>
    </node>
</root>


Desired Output:
<out>
  <mssg>123456789012</mssg>
  <junk>abcdefghi</junk>
</out>


XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="yes" />
  <xsl:template match="/">
    <out>
      <xsl:variable name="mssg_data">
        <xsl:call-template name="ConCat">
          <xsl:with-param name="List" select="/root/node/data" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="junk_data">
        <xsl:call-template name="ConCat">
          <xsl:with-param name="List" select="//junk" />
        </xsl:call-template>
      </xsl:variable>
      <mssg>
        <xsl:value-of select="$mssg_data" disable-output-escaping="no" />
      </mssg>
      <junk>
        <xsl:value-of select="$junk_data"/> 
      </junk>
    </out>
  </xsl:template>
  <xsl:template name="ConCat">
    <xsl:param name="List" />
    <xsl:param name="result" select="''" />
    <xsl:choose>
      <xsl:when test="$List">
        <xsl:call-template name="ConCat">
          <xsl:with-param name="List" select="$List[position() &gt; 1]" />
          <xsl:with-param name="result" select="concat($result, $List[1])" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$result" disable-output-escaping="no" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>


Note: The similar result can be achieved by using XPATH evaluator to get text of data node set (ie /root/node/data/text()), depending on the requirement one or the other approach may be more suitable.

Monday, 30 December 2013

Looping in XSLT: Recursive Template Calls

To loop in XSLT you need to call a template recursively.

In past I have done this using two approaches:

Option-1: While Loop
Logic is similar to a while loop. So the counter is decremented till it is greater than 0.
 
 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="no" />
  <xsl:template match="/">
    <LoopInput>
      <xsl:call-template name="CreateLoop">
        <xsl:with-param name="count" select="${numLoop}" />
      </xsl:call-template>
    </LoopInput>
  </xsl:template>
  <xsl:template name="CreateLoop">
    <xsl:param name="count" />
    <xsl:if test="$count &gt; 0">
      <xsl:call-template name="CreateLoop">
        <xsl:with-param name="count" select="$count - 1" />
      </xsl:call-template>
      <Line>
        <xsl:text disable-output-escaping="no">Line Data: </xsl:text>
        <xsl:value-of select="$count" disable-output-escaping="no" />
      </Line>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>


Option-2: For Loop
Here counter is incremented from a initial count till it reaches the desired loop count.

 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="no" />
  <xsl:template match="/">
    <LoopInput>
      <xsl:call-template name="CreateLoop">
        <xsl:with-param name="MaxLoop" select="${numLoop}" />
        <xsl:with-param name="iLoop" select="1" />
      </xsl:call-template>
    </LoopInput>
  </xsl:template>
  <xsl:template name="CreateLoop">
    <xsl:param name="MaxLoop" />
    <xsl:param name="iLoop" />
    <Line>
      <xsl:text disable-output-escaping="no">Line Data: </xsl:text>
      <xsl:value-of select="$iLoop" disable-output-escaping="no" />
    </Line>
    <xsl:if test="$iLoop &lt; $MaxLoop">
      <xsl:call-template name="CreateLoop">
        <xsl:with-param name="MaxLoop" select="$MaxLoop" />
        <xsl:with-param name="iLoop" select="$iLoop + 1" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Thursday, 21 November 2013

Event Driven Architecture: Using log entries as events

Let's say you want to monitor multiple applications and based on certain log events automatically trigger some mechanism - say send an email to application admin or may be start a server which went down etc etc. The easiest way to make this event driven monitoring setup, I think, is to redirect application log entries to JMS destinations instead of a logfile. Then have monitoring application subscribe to the JMS destination.

This is straightforward in log4j. Simply specify appender as JMSAppender. Below is sample configuration which is for a JMS destination of type Topic and ActiveMQ as JMS broker but this can be used for any JMS broker, all you need is client jar for it and find value of InitialContextFactoryName for it.

Once the application is restarted its logs will start going to the specified JMS destination. Here is a sample log entry as it shows up on my HermesJMS.