Thursday, 21 June 2012

Recursive XSLT to filter nodes

We all know that one can surely do a lot of cool stuff with XSL but still sometimes you'll come across some small piece of code which will impress you with its simplicity and yet effectiveness. Here is my favorite piece of  XSLT code:

 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>


All it does it create a copy of input XML. But it can be modified to do a lot of neat stuff. Here are a few examples:

----------------------------- Example-1: Convert attributes to fields -----------------------------

Source XML:

<root att1="att1">
  <child1  att2="att2">
    <child2  name="field_name1" value="field_value1"/>
    <child2  name="field_name2" value="field_value2"/>
  </child1>
</root>

Lets say my target application does not like attributes and it wants data as XML nodes. So the desired output is:
<?xml version="1.0" encoding="UTF-8"?>

<root att1="att1">
    <child1 att2="att2">
        <field_name1>field_value1</field_name1>
        <field_name2>field_value2</field_name2>
    </child1>
</root>


Here is the XSL code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="child2">
    <xsl:element name="{@name}">
      <xsl:value-of select="@value"/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>


----------------------------- Example-2 Filter Specific Data -----------------------------

Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <node>
    <data1>null</data1>
    <data2>data</data2>
    <data3/>
    <data4>null</data4>
    <data5></data5>
  </node>
</root>


Desired Output is:
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <node>
    <data2>data</data2>
    <data3/>
    <data5/>
  </node>
</root>

This is very common in integration scenarios. Application-A gives out data will no value as 'null' values but Application-B takes this 'null' as a string. So best way forward is to filter out these 'null' nodes.

Here is the XSL code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>

  <xsl:template match="node()|@*">
          <xsl:if test=". !='null'">
            <xsl:copy>
              <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
          </xsl:if>
  </xsl:template>


</xsl:stylesheet>

Awesome isn't it? Other way would be to put a check on each node for value 'null' before it is sent to Application-B, which would be just painfully annoying and plainly a bad approach.


----------------------------- Example-3: Update Selective Nodes -----------------------------

How about the other way around. So now we have some empty nodes and we want their value set as 'null'

Source XML:
<root att1="a1">
  <child att2="a2">
    <data att3="a3"></data>
    <data att3="a3">data</data>
    <data att3="a3"/>
  </child>
</root>


Desired Output:
<?xml version="1.0" encoding="UTF-8"?>
<root att1="a1">
    <child att2="a2">
        <data>null</data>
        <data att3="a3">data</data>
        <data>null</data>
    </child>
</root>



Here is the XSL code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>

  <xsl:template match="node()|@*">
          <xsl:choose>
          <xsl:when test="string-length(.) = 0">
            <xsl:element name="{name()}">
                <xsl:text disable-output-escaping="no">null</xsl:text>
            </xsl:element>
          </xsl:when>
          <xsl:otherwise>
            <xsl:copy>
              <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
          </xsl:otherwise>
        </xsl:choose>
  </xsl:template>
</xsl:stylesheet>


These are just a few examples but we can use this XSL in many ways to reformat/restructure a XML. There is always a possibility for a better approach. So guys if you know anything better then do update me, either in comments or just shoot me a mail.

32 bit, 64 bit confusions: ODBC connection

Often I have seen people get confused in an mixed 32 bit, 64 bit environment. Mostly people will have 64 bit OS like Windows 7, 64 bit with 32 bit applications like MS office 2007. Like recently I found suddenly a simple JDBC code (using JDBC-ODBC bridge) which was reading Excel file, started failing in a new environment. Just listing down things to keep in mind:

- Check if you have 32 bit Java or 64 bit Java?
- Check if you have 32 bit ODBC drivers or 64 bit ODBC drivers.
- Finally check if you are using JDBC connection URL for the version of ODBC drivers you have.

So, in 32 bit environment:
run C:\windows\sysWOW64\odbcad32.exe and see if the drivers are present for excel or not.
If not then install AccessDatabaseEngine.exe from Microsoft Download Center.

Now the connection URL in the JDBC code will be:
jdbc:odbc:Driver={Microsoft Excel Driver (*.xls)};DBQ=<Path to .xls file>

And in a 64 bit environment:
Run C:\windows\system32\odbcad32.exe and see if the drivers are present.
If not then install AccessDatabaseEngine_x64.exe from the link mentioned above.

Now the connection URL will be:
jdbc:odbc:DRIVER={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=<Path to .xls file>

Sample code:
Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" );

//using DSN-less connection in 32 bit env
con = DriverManager.getConnection( "jdbc:odbc:Driver={Microsoft Excel Driver (*.xls)};DBQ=C:/Abhinay/test.xls")

//using DSN-less connection in 64 bit env

//con = DriverManager.getConnection( "jdbc:odbc:DRIVER={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=C:/Abhinay/test.xls");

stmnt = con.createStatement();
String query = "select * from [Sheet1$];";
ResultSet rs = stmnt.executeQuery( query );

Monday, 21 May 2012

Text relocation issue - 32 bit JDK on RHEL


Guys,

Found this interesting issue while toying around with latest JDK on different platforms. (JDK now includes JavaFx2.1 as native Java library! though not on Linux yet. Anyways about that later.)


Problem: java won't run!

[root@localhost ~]# /home/abhinay/Apps/jdk1.7.0_04-i586/bin/java -version

Error: dl failure on line 864

Error: failed /home/abhinay/Apps/jdk1.7.0_04-i586/jre/lib/i386/client/libjvm.so, because /home/abhinay/Apps/jdk1.7.0_04-i586/jre/lib/i386/client/libjvm.so: cannot restore segment prot after reloc: Permission denied



Cause: The java library libjvm.so is attempting text relocation which is a security concern and SELinux will deny the permission to do this.

Simplest explanation:

“A relocation is an operation that rewrites an address in a loaded segment. Such an address rewrite can happen when a segment has references to a shared object and that shared object is loaded in memory. In this case, the references are substituted with the real address values. Similar events can occur inside the shared object itself. A text relocation is a relocation in the text segment. Since text segments contain executable code, system administrators might prefer not to have these segments writable. This is perfectly possible, but since text relocations actually write in the text segment, it is not always feasible.” http://www.gentoo.org/proj/en/hardened/pax-utils.xml



A few more better ones:

"A text relocation is the result of a reference to an object with a variable address at runtime using an absolute addressing mode. The instruction encoding itself contains the address and therefore the executable text of the binary must be changed to contain the correct address when taking the actual load addresses at runtime into account.
The result of a text relocation is that the binary text is written to. This means this page of the binary cannot be physically shared with other processes on the system (this is the goal of DSOs, aka shared libraries). It also means that the binary must have permission to change the access permissions for the memory page to include writing and then back to executing. This is a privileged operation when SELinux is enabled." http://www.akkadia.org/drepper/textrelocs.html

"When code is not position-independent, the binaries must be fixed up at runtime so that the absolute address references are correct after loading. This requires rewriting the the code in memory, takes some time, prevents the memory occupied by the executable from being shared and interferes with selinux protections."
http://fedoraproject.org/wiki/User:Tibbs/Text_Relocation_Guidelines



Workaround: Allow libjvm.so to text relocate by running change context.

[root@localhost ~]# chcon -t textrel_shlib_t /home/abhinay/Apps/jdk1.7.0_04-i586/jre/lib/i386/client/libjvm.so


Wonder why Oracle is putting non-PIC in Java!