How to Implement Complex Business Rules Using DirXML Stylesheets and Java
Articles and Tips: article
Senior Software Engineer
Novell Developer Solutions
kbunnell@novell.com
01 Jan 2002
This AppNote is the second in a series on advanced DirXML stylesheet authoring techniques. It describes how to combine XSLT and Java to implement complex business rules such as those involved in implementing an employee naming and password policy.
The first AppNote in the series is "How to Define a Corporate Naming Policy Using DirXML Stylesheets" in the December 2001 issue, available online at http://support.novell.com/techcenter/articles/ana20011205.html.) It is recommended that you also read the AppNote entitled "How to Write a Simple DirXML Stylesheet" in the March 2001 issue, available online at http://support.novell.com/techcenter/articles/ana20010304.html.
- Introduction
- DirXML and Employee Account Provisioning
- Defining the Create Rule Stylesheet
- Accessing Java Classes from a DirXML Stylesheet
- Making Your Java Classes Accessible to the XSLT Processor
- Conclusion
Topics |
DirXML, Novell eDirectory, XML, XSLT, Java |
Products |
Novell eDirectory, DirXML |
Audience |
network administrators, programmers |
Level |
advanced |
Prerequisite Skills |
Java, XML, and XSLT proficiency |
Operating System |
NetWare 5.x, Windows NT or 2000 |
Tools |
none |
Sample Code |
yes |
Introduction
DirXML stylesheets provide a powerful mechanism to implement corporate business rules, but there are times when using XSLT and XPath fall short. The task XSLT is designed to perform is data transformation. The logic required to implement complex business rules often extends beyond the capabilities of XSLT and XPath. This is where the marriage of XSLT and Java provide a powerful one-two punch that enables even the most daunting business rules to be tackled with relative ease.
This AppNote demonstrates how to instantiate a Java class, invoke its methods, and define and pass variables from within an DirXML stylesheet. These techniques are presented through the example of implementing a naming and password policy.
DirXML and Employee Account ProVisioning
For our example scenario, suppose your company recently purchased DirXML in order to automate and streamline the "new-hire" process of new employees. The costs associated with manually creating user accounts for eDirectory, e-mail, database systems, and so on, as well as the lack of productivity imposed on the new employee as they wait for corporate resources to be provisioned, has mandated a move to an auto-provisioning solution. The task you have been given is to implement a naming policy and a password policy to ensure that the account name and passwords are created according to corporate standards.
The employee "new-hire" process is initiated within the Human Resources (HR) application. The HR application is connected to eDirectory by way of a DirXML connector. This connector is a Java or native (C++) component that interfaces with the DirXML engine to govern the bi-directional flow of data between the HR application and eDirectory. Event data flows from the HR application to DirXML via the publisher channel. Bear in mind that all of your other corporate business applications are connected to eDirectory using the same mechanism, thus providing a tightly-bound, interconnected system that serves as the foundation for employee provisioning.
The "create rule" stylesheet is used to govern the creation characteristics of an object in eDirectory as it flows through the publisher channel to its destiny as a new object. This is where the work of defining the naming and password policy begins.
Note: A complete functioning DirXML driver and the associated stylesheets described in this AppNote are available for download at http://developer.novell.com/ research/downloads.htm. The file name is JavaAndXSLT.ZIP. Follow the instructions outlined in the included README.TXT file to install and configure the example driver.
Defining the Create Rule Stylesheet
The primary function of the Create Rule is to ensure that the data required to create an object in the target system is present in the <add> document before it is allowed to propagate to the target system. (For an in-depth description of how to define a Create Rule stylesheet, see the previous AppNote in this series, available online at http://support.novell.com/techcenter/articles/ana20011205.html.)
An example Create Rule "add" template is shown in the following listing.
01 <xsl:template match="add"> 02 <!-- ensure we have required NDS attributes we need for the name --> 03 <xsl:if test="add-attr[@attr-name='Surname']/value and add-attr[@attr-name='Given Name']/value"> 04 <!-- copy the add through --> 05 <xsl:copy> 06 <xsl:apply-templates select="@*"/> 07 <!-- Call java class to generate the user CN and password --> 08 <xsl:variable name="cn-value"> 09 <xsl:call-template name="generate-cn"/> 10 </xsl:variable> 11 <xsl:element name="add-attr"> 12 <xsl:attribute name="attr-name">CN</xsl:attribute> 13 <xsl:element name="value"> 14 <xsl:value-of select="$cn-value"/> 15 </xsl:element> 16 </xsl:element> 17 <xsl:variable name="password-value"> 18 <xsl:call-template name="generate-pwd"/> 19 </xsl:variable> 20 <xsl:element name="password"> 21 <xsl:value-of select="$password-value"/> 22 </xsl:element> 23 <!-- copy the rest of the stuff through, except for what we have already copied --> 24 <xsl:apply-templates select="* | comment() | processing-instruction() | text()"/> 25 </xsl:copy> 26 </xsl:if> 27 </xsl:template>
The Create Rule must filter all <add> documents and ensure that the source document contains the data set required to create the object in target system (eDirectory from the publisher channel). Notice on Line 01 that the template is defined to match on <add> elements in the source document.
The test expression on line 03 ensures that the source document has <add-attr> elements that contain values for "Surname" and "Given Name".
If the source document contains the required element data, the source document is copied to the result document using the <xsl:copy> instructions on lines 05 through 25.
In addition to copying the original source document to the result document, you must complete the task of adding the account name and password as stipulated by the corporate standards (business rules) defined by your company. The account name is added to the result document by adding an <add-attr> element with an attribute name of "CN". This happens on lines 08 through 16.
An XPath variable named "cn-value" is defined on lines 08 through 10. Calling a template named "generate-cn" creates the content of this variable. If you are the curious type, you've already looked beyond line 16 and discovered that an almost identical process is used to add a <password> element to the result document (lines 17 through 22). Calling the "generate-pwd" template creates the value for the "password-value" variable (line 18).
Since most of the magic appears to occur in the "generate-cn" and "generate-pwd" templates, let's take a closer look at them.
Accessing Java Classes from a DirXML Stylesheet
The task of implementing a naming policy or password policy can be approached several different ways. One approach is to implement these policies using strictly XSLT and XPath. Another approach is to use a combination of XSLT and Java to leverage the data transformation characteristics of XSLT and the broad range of computational capability provided by Java.
The ability to transform source data can only take you so far. Suppose you need to query an external database via JDBC to retrieve additional data to generate a password. Or consider the case where, as part of generating a unique account name, external databases need to be queried to ensure a unique name is generated for the "CN" attribute. These scenarios require that some exit from the stylesheet be provided to access these external systems.
Fortunately, the XSLT processor included with the DirXML engine provides a Java binding mechanism that allows binding to external Java functions. Let's take a close look at how this Java binding from a stylesheet takes place.
01 <xsl:template name="generate-pwd" xmlns:genPwd="http://www.novell.com/nxsl/java/ com.novell.appnote.util.GenPassword"> 02 <xsl:variable name="surname" select="add-attr[@attr-name='Surname']/value"/> 03 <xsl:variable name="givenName" select="add-attr[@attr-name='Given Name'] /value"/> 04 <xsl:variable name="asso" select="/association"/> 05 <xsl:variable name="genPwdInstance" select="genPwd:new() "/> 06 <xsl:variable name="result" select="genPwd:getPassword($genPwdInstance, $surname, $givenName, $asso )"/> 07 <!-- Return generated Password Value --> 08 <xsl:value-of select="$result"/> 09 </xsl:template> 10 <xsl:template name="generate-cn" xmlns:genCN="http://www.novell.com/nxsl/java/ com.novell.appnote.util.GenCN"> 11 <xsl:variable name="surname" select="add-attr[@attr-name='Surname']/value"/> 12 <xsl:variable name="givenName" select="add-attr[@attr-name='Given Name'] /value"/> 13 <xsl:variable name="genCNInstance" select="genCN:new() "/> 14 <xsl:variable name="result" select="genCN:getCN($genCNInstance, $surname, $givenName )"/> 15 <!-- Return generated CN Value --> 16 <xsl:value-of select="$result"/> 17 </xsl:template>
As you can see in the above listing, the "generate-pwd" and "generate-cn" templates have an "xmlns" namespace attribute added to the <template> element. Look at the namespace attribute defined on line 01:
xmlns:genPwd="http://www.novell.com/nxsl/java/com.novell.appnote.util. GenPassword
Notice the new "xmlns" namespace is called genPwd. The URI referenced by this namespace is a rather long one that first points to the location http://www.novell.com/nxsl/java/ followed by the package name of a Java class: com.novell.appnote.util.GenPassword.
The first part of this namespace URI (http://www.novell.com/nxsl/java/) signals to the XSLT processor that a Java class is to be exposed for use within the <template> tags where the namespace is defined. Following this is the actual Java class to which the namespace (genPwd) refers (com.novell.appnote.util. GenPassword). Java programmers can think of this namespace as a type of "import" statement that makes a single Java class available for access within the stylesheet. In essence, the genPwd namespace provides a binding to the external Java class.
Now that the Java class is accessible, how do you create an instance of the Java class so you can invoke its methods? Take a close look at line 05. An XPath variable named getPwdInstance is created whose content is populated with the following XSLT instruction: select="genPwd:new(). This line uses the namespace attribute "genPwd" followed by a colon and a call to a method named "new( )". This instruction tells the XSLT processor to create an instance of the Java class referenced by the genPwd namespace. This is equivalent to creating an instance of a Java class using the "new" keyword. If the Java class constructor accepts parameters, they are passed to the new( ) method.
For example, suppose the com.novell.appnote.util.GenPassword class constructor accepted a Java String parameter for surname. The XSLT instruction would be changed as follows: genPwd:new($surname). Note that an XPath variable is defined on line 02 that contains the value of "Surname" which is extracted from the source <add> document.
Now that we have an instance of our com.novell.appnote.util.GenPassword class, how do we invoke its method to generate our password? On line 06, an XPath variable named "result" is populated using the following XSLT instruction:
select="genPwd:getPassword($genPwdInstance, $surname, $givenName, $asso )
The genPwd namespace is used to invoke the getPassword method of the com.novell.appnote.util.GenPassword class. You'll notice it also passes along four parameters. Close inspection of the source of the Java class for the GenPassword class reveals that the "getPassword" method only accepts three parameters.
package com.novell.appnote.util; import java.util.*; import java.io.*; public class GenPassword { public String getPassword(String surname, String givenName, String association) { String pwd=""; try { pwd = surname.substring(0, 2) + givenName.substring(0, 2) + association; } catch (StringIndexOutOfBoundsException e) { pwd = association; } return pwd; } }
Why is the genPwdInstance variable passed as the first parameter? This is where a little XSLT processor magic takes place. In order to identify the instance of the Java class to be invoked, the XPath variable that holds a reference to the instance of the class (for example, genPwdInstance) must be passed as the first parameter to any method invoked via the xsl namespace attribute (for example, genPwd) that is bound to the Java class. So even if you wish to invoke a method of a Java class with no parameters, the instance variable must be passed as the one and only parameter.
The XPath variable named "result" is populated by the return value of the getPassword method. The getPassword method implements the corporate password policy by constructing a password that consists of a concatenation of the first two characters of the first name and last name plus the association value (for example, employee ID value). It is easy to see that this method could use much more sophisticated mechanisms to generate a more secure initial password, such as query a database via JDBC or even generate a random password that is e-mailed to the employee.
The value of the XPath "result" variable is then returned to the calling template (line 16 of the previous listing). The mechanisms discussed for the generate-pwd template are identical for the generate-cn template (lines 10 through 17). The only difference is the use of a second Java class named "GenCN".
To summarize, in order to invoke methods on a Java class, you must define a namespace attribute on the parent element where it will be instantiated (for example, <template>). The value of the namespace attribute is set to a special URI that binds it to a specific Java class. The URI is composed of two parts:
The http://www.novell.com/nxsl/java/ signals the XSLT processor that the namespace is to be bound to a Java class.
The Java class to be accessed using its full package name (com.novell. appnote.util.GenPassword). The namespace attribute is subsequently used to create an instance of the Java class by using the "new( )" instruction.
Finally, the namespace attribute is used to invoke methods of the bound Java class by passing the XPath variable that references an instance of the Java class as the first parameter to all method invocations of the class.
Making Your Java Classes Accessible to the XSLT Processor
You now know how to bind an XML namespace to a Java class and call its methods. For the XSLT processor to find the Java classes referenced in the stylesheet, simply package your Java classes into a "JAR" (Java archive) file and place them in the same directory as the DirXML engine Java libraries.
For example, on the Windows platform place your Java JAR files in the <drive>:\Novell\NDS\lib directory where <drive> is the drive where DirXML and eDirectory are installed. On NetWare, place your Java JAR files in the sys:\System\lib directory. The DirXML engine scans this directory and builds the classpath used to locate DirXML drivers, as well as any Java classes referenced by any driver stylesheets.
Java Argument Types
In the listing of the "generate-pwd" and "generate-cn" templates above, the variables passed as arguments to the Java methods (genPwd in line 06 and genCN in line 14) are actually XPath variables. Upon closer inspection you'll notice that there is no way to specify the variable type when defining the variables (lines 02, 03, 04, 11 and 12). The Java methods expect arguments of a specific type.
So how does the XSLT processor ensure that the "typeless" XPath variables are passed as appropriate Java types when invoking the Java method? The answer lies in the fact that the XSLT processor follows some rules to coerce the XPath variables to the type expected by the Java method. These rules are as follows:
If the Java method accepts an argument of a numeric type, a wrapper for a numeric type, java.lang.String, boolean, or java.lang.Boolean, the passed XPath variable will be coerced to that type if possible.
If the Java method accepts an argument of type com.novell.xsl.process. ResultTreeFragment, the XPath variable must contain a result tree fragment. The use of the word "fragment" is a bit of a misnomer. The result tree can actually consist of an entire document tree. For example, consider the following XPath variable:
<xsl:variable name="currDoc"> <xsl:copy> <xsl:apply-templates select="@*|* | comment() | processing-instruction() | text()"/> </xsl:copy> </xsl:variable>
The XPath variable "currDoc" contains the entire source document, not simply a fragment of it. Your Java code can act on the ResultTreeFragment object further by converting it to a string or a DOM document. (More information on how to use the ResultTreeFragment object will be featured in a future AppNote).
If the Java method accepts an argument of type com.novell.xml.xpath. NodeSet the XPath variable must contain a node set. For example, the following XPath variable is populated with a node set that contains all <add-attr> elements from the source document:
<xsl:variable name="node-set" select="//add-attr"/>
If the Java method accepts an argument that consists of any other Java object type, the XPath variable must contain a reference to the expected Java type. For example, suppose you want to invoke a method of a Java class that accepts a java.util.Date object. The following XSLT template demonstrates how to create an instance of the java.util.Date object and pass it as one of the arguments to a second Java class:
<xsl:template name="set-date" xmlns:tstClass="http://www.novell.com/nxsl/java/TestClass" xmlns:javaDate="http://www.novell.com/nxsl/java/java.util.Date"> <xsl:variable name="testClassInstance" select="tstClass:new() "/> <xsl:variable name="dateInstance" select="javaDate:new()"/> <xsl:variable name="result" select="tstClass:setDate($testClassInstance, $dateInstance )"/> </xsl:template>
Java Return Values
Just as the XSLT processor must manage the conversion of XPath variables to the appropriate Java types when passed as arguments to Java methods, it must also manage the conversion of Java types returned from Java methods to XPath types consumable by XPath and XSLT. The XSLT processor converts Java return value types to XPath equivalents as follows:
If the return value is a numeric type or a wrapper for a numeric type, it will be converted to an XPath number.
If the return value is boolean or java.lang.Boolean, it will be converted to an XPath boolean.
If the return value is java.lang.String, it will be converted to an XPath string.
If the return value is a DOM node, it will be converted to an XPath node set.
If the return value is any other type, it will remain a Java object. For example, suppose you have a Java method that returns a java.util.Date object. The following template could be used to retrieve a java.util.Date object and invoke its methods:
<xsl:template name="get-date" xmlns:tstClass="http://www.novell.com/nxsl/java/TestClass" xmlns:javaDate="http://www.novell.com/nxsl/java/java.util.Date"> <xsl:variable name="testClassInstance" select="tstClass:new() "/> <xsl:variable name="getDate" select="tstClass:getDate($testClassInstance )"/> <xsl:variable name="resultDate" select="javaDate:toString($getDate )"/> </xsl:template>
Notice that the xmlns (namespace) attribute still needs to be defined to create the binding to the java.util.Date object, however, rather than create an instance of the java.util.Date object within the stylesheet, it is returned by the "getDate" method of the TestClass.
Conclusion
This AppNote has demonstrated how the powerful combination of Java, XSLT, and DirXML can greatly simply the implementation of complex business rules. Business rules such as the corporate naming policy and password policy are made easy by implementing the complex logic within a Java class and retrieving the computed result by calling the Java class from a DirXML stylesheet. The policy results can then be added to the result document for processing by the DirXML engine. The results attainable by combining XSLT and Java are only limited by your imagination!
While the examples presented in this AppNote are simplistic in implementation, they open the door to endless possibilities for defining your own complex business rules. Download the sample code included with this AppNote and begin exploring the possibilities.
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.