Another attempt at explaining the XPATH Context Node

geoffc

By: geoffc

March 13, 2009 2:56 pm

Reads: 534

Comments:0

Rating:0

Novell Identity Manager uses a number of different languages in order to get its job done. We have the original XSLT (XML Stylesheets) that we all try to avoid as much as possible these days. XSLT went out of style with the advent of Nsure Identity Manager and DirXML Script.

Yes I know people still like XSLT and it is very powerful, but DirXML Script is really very powerful, easier to manipulate, and has a great user interface to use it (Policy Builder). This is only personal opinion, and if you happen to disagree, please let me know why, as I love finding things that you can only do in XSLT that you cannot yet do in DirXML Script. Then the challenge becomes, how can I do it in Policy Builder using DirXML Script instead! Sometimes the work around is fairly ugly and convoluted, other times it is simple and elegant. But it is usually always fun trying to figure it out.

The current examples I know about are the SOAP driver and the Delimited Text driver. For those two drivers, it is pretty tricky (if even possible) to modify the form of the document on the fly, to convert from say Comma delimited text, into an XML document that is compliant with Novell’s XML dialect for Identity Manager, XDS.

The same issue applies to the SOAP driver where an XML document, in SOAP dialect comes in, and needs to be converted to XDS format, and vica versa as it heads out the Subscriber channel.

In those cases, XSLT is easier than DirXML Script for this issue. However for most everything else, I prefer to use DirXML Script.

Regardless of XSLT or DirXML Script, there is another language in use for both approaches, called XPATH, the XML Path language. This is a tricky language to use, until you get the hang of it.

Probably the biggest problem is a lack of good documentation and examples. I am working on that aspect of it, and you can take a look at some of my other articles on this topic:

There are some good sites where people are trying to post Identity Manager focused articles like:

Of course the canonical explanation of the XML Path language would be the RFC that defines the language, at: http://www.w3.org/TR/1999/REC-xpath-19991116

Unfortunately (for me), like many RFCs it is written in its own language, unique to RFC’s. That is of course my personal opinion, and I have been informed that compared to other RFC’s this is actually a well written and straight forward example.

Having said that, some parts of it are pretty readable, like the string manipulation functions, like substring-before() and contains(). The stuff about node selection seems to be pretty dense, and personally I find very difficult to read and understand. I am sure some people who speak RFC’eze find it clear as day. Good for them! For the rest of us, I am working on providing examples that make it easier to understand.

I was discussing the article about the Context Node (XPATH and the context node) with a client I was working with, and in the discussion with her (Hi Anne!) I realized another approach to the problem that may help out. I wanted to take another try at explaining the concept.

First off it is important to remember that there are two basic uses for XPATH, doing something (like math, string manipulation) and selecting something. See the article: Some thoughts on XPATH in Novell Identity Manager for a further discussion of this topic.

In this case, the XPATH usage we want to look at is for selecting nodes. As discussed in the XPATH Context node article (XPATH and the context node) the most important thing is to know where you current context node is.

What came up in discussion with the client was that there is a really nice analogy to the context node that we should all be familiar with, and that is Directory paths, and the use of “cd” (Change Directory).

When you have an XML document and you want to select a node, you cannot do anything until you know where you are starting from.

The same is true in a file system directory structure, or even an eDirectory container or tree structure (remember the ‘cx’ tool?). You cannot start looking at the file system or doing things, until you know where you are starting from. Well you can, but the results won’t make a whole heck of a lot of sense.

In the case of the file system (cd) or an eDirectory tree (cx) you have some extra breadcrumbs that help you find the current directory or context. Usually you can type CD or CX to see your current context (in Windows anyway. “cd” on Unix like shells, often takes you your home directory path).

In XPATH, you need to understand where you are, before you can try and do whatever it is you need to accomplish.

What complicates things a little is that the way the Identity Manager engine presents an XDS document that you can use XPATH upon, is not as obvious as it might be otherwise for the generic XPATH example.

A typical XDS document might look something like this:

<nds dtdversion="3.5" ndsversion="8.x">
<source>
  <product version="3.5.1.20070411 ">DirXML</product>
  <contact>Novell, Inc.</contact>
</source>
<input>
  <add allow-adminp-support="true" certify-user="true" class-name="User"
create-mail="true" event-id="TESTACME1#20070628194836#1#2"
expire-term="2" internet-password-force-change="false"
mail-acl-manager-name="CN=IDMUSR SYSTEM/O=adtest"
mail-file-inherit-flag="true" no-id-file="false"
notes-password-change-interval="0"
notes-password-check-setting="PWD_CHK_CHECKPASSWORD"
notes-password-grace-period="0" notes-policy-name="/ADTEST"
qualified-src-dn="C=US\O=ACMEC\OU=idmusers\CN=UKane"
roaming-cleanup-period="90"
roaming-cleanup-setting="REG_ROAMING_CLEANUP_EVERY_NDAYS"
roaming-server="CN=testnl.adtest.com/O=adtest"
roaming-subdir="Roaming\UmKane" roaming-user="false"
src-dn="\TIDM\US\ACMEC\idmusers\UKane" src-entry-id="33552"
store-useridfile-in-ab="true" sync-internet-password="true"
tell-adminp-process="tell adminp process all" user-id-file="UKane.id">
    <add-attr attr-name="CN">
      <value naming="true" timestamp="1183060114#27" type="string">UKane</value>
    </add-attr>
    <add-attr attr-name="Given Name">
      <value timestamp="1183060114#5" type="string">Um</value>
    </add-attr>
    <add-attr attr-name="nspmDistributionPassword"><!-- content suppressed -->
    </add-attr>
    <add-attr attr-name="Surname">
      <value timestamp="1183060114#7" type="string">Kane</value>
    </add-attr>
    <add-attr attr-name="uniqueID">
      <value type="string">UKane</value>
    </add-attr>
    <add-attr attr-name="MailFile">
     <value>UKane.nsf</value>
    </add-attr>
    <add-attr attr-name="Internet EMail Address">
      <value>UKane@adtest.com</value>
    </add-attr>
  </add>
</input>
</nds>

This example is actually from a Lotus Notes Subscriber channel Add event. The reason I like this example is that it has lots of interesting nodes and attributes to look at.

Lets say I want to get the value of the Surname attribute for the user we are about to create in this sample document. Well an XPATH guru never having heard of Novell Identity Manager would say, thats easy: /nds/input/add/add-attr[@attr-name="Surname"]/value should do the trick.

It turns out, that will NOT work in Identity Manager. The tricky bit here is that the Identity Manager engine does us a ‘favour’ and presents the current context as the node under <input> or <output>. The reason I say the node under <input> rather than the <add> node, is to handle the case of a <modify>, <query>, or other event type.

Basically the current context is the operation node. (It actually says it in the documentation, honest, I saw it the other day. But seriously, who actually reads documentation anyway?)

Thus the real select string is add-attr[@attr-name="Surname"]/value instead. The give away that should help you remember this, is the fact that to get the XML attribute src-dn, one of the more common XPATH select statements, we would just use @src-dn for the current event.

There is a smidgen of confusion here, as we have eDirectory attributes in the document (Surname, uniqueID, etc) and we have XML attributes, which are things inside an XML node. In this example, which is JUST the operation node (Which is an <add> operation in this case) we have LOTS of XML attributes:

<add allow-adminp-support="true" certify-user="true" class-name="User" create-mail="true" event-id="TESTACME1#20070628194836#1#2" expire-term="2"
internet-password-force-change="false" mail-acl-manager-name="CN=IDMUSR SYSTEM/O=adtest" mail-file-inherit-flag="true" no id-file="false"
notes-password-change-interval="0" notes-password-check-setting="PWD_CHK_CHECKPASSWORD" notes-password-grace-period="0" notes-policy-name="/ADTEST"
qualified-src-dn="C=US\O=ACMEC\OU=idmusers\CN=UKane" roaming-cleanup-period="90" roaming-cleanup-setting="REG_ROAMING_CLEANUP_EVERY_NDAYS"
roaming-server="CN=testnl.adtest.com/O=adtest" roaming-subdir="Roaming\UmKane" roaming-user="false" src-dn="\TIDM\US\ACMEC\idmusers\UKane" 
src-entry-id="33552" store-useridfile-in-ab="true" sync-internet-password="true" tell-adminp-process="tell adminp process all" user-id-file="UKane.id">

That whole long thing is actually a single <add> node. Usually in addition to @src-dn, on most documents for IDM events, we could select @eventid or @timestamp. What I like about this example is that we could select @expire-term or @create-mail @store-useridfile-in-ab and so on if we needed too.

Once you have that concept grasped it gets relatively straight forward to select things in XPATH. Until you start getting into complex things like the node that is one above the current node, but also the sibling to the current node. All this is doable, just take it one step at a time.

Next interesting thing to know about selecting with XPATH is the use of predicates.

A very common use of this would be an XPATH statement we suggested above that looks something like:

add-attr[@attr-name="Surname"]/value

In this case, we are looking for the <add-attr> node, but there 7 such nodes in our example document (For the attributes CN, Given Name, Surname, uniqueID, MailFile, and Internet EMail Address). So how do we tell XPATH to only grab the one we care about? Through the predicate [@attr-name="Surname"] which tells it, select the <add-attr> that has an XML attribute (the @attr-name part) called attr-name that has a value of Surname.

You can get pretty complex in predicates, as I tried to show on parsing DirXML-Association values in XPATH. (See article: Using XPATH to examine Association values) DirXML-Association is a Path syntax attribute, (for more about eDirectory syntaxes, you can look at: Interesting Schema Syntaxes in eDirectory from an Identity Manager Perspective – Part 1 and
Interesting Schema Syntaxes in eDirectory from an Identity Manager Perspective – Part 2) which is structured and has three components. It is also multi valued, so I want to pick out a specific association to a specific driver in XPATH, from many possible association values.

The example I use in that article looks something like:

$ASSOC-QUERY/attr[@attr-name="DirXML-Associations"]/value/component[(@name='volume') and (text()=$TARGET-DRIVER-DN)]/parent::component[@name='nameSpace']/text()='1'

I usually find it immensely helpful to have the document I am trying to select out of, via XPATH, right in front of me as I work on the XPATH. This makes it really easy to try and figure it out. So here is an example of a Query result for DirXML-Associations of a user.

<nds dtdversion="3.5" ndsversion="8.x">
<source>
<product version="3.5.11.20080307 ">DirXML</product>
<contact>Novell, Inc.</contact>
</source>
	<output>
		<instance class-name="User" qualified-src-dn="O=LAB\OU=EMPLOYEES\OU=NEW\CN=JSmith" src-dn="\ACME-LAB\LAB\EMPLOYEES\NEW\LJohnson1" src-entry-id="56795">
			<attr attr-name="DirXML-Associations">
				<value timestamp="1217007039#74" type="structured">
					<component name="nameSpace">1</component>
					<component name="volume">\ACME-LAB\LAB\SERVICES\IDVAULT\Active Directory</component>
					<component name="path">f0648eab27d6da4283246583112d6319</component>
				</value>
<value timestamp="1217007039#71" type="structured">
					<component name="nameSpace">1</component>
					<component name="volume">\ACME-LAB\LAB\SERVICES\IDVAULT\APP-JDBC</component>
					<component name="path">PK_SEQUENCE=350110,table=CLIENTS,schema=IDM</component>
				</value>
			</attr>
		</instance>
	</output>
</nds>

My current context node for the query result is the operation node, which for a query result is the <instance> node.

Thus I am looking for the $ASSOC-QUERY/attr node, the <attr> node in the nodeset local variable, with the predicate [@attr-name="DirXML-Associations"], that means it has an XML attribute attr-name with a value of DirXML-Associations, and then I want the /value/ node underneath it. (Now we are at: $ASSOC-QUERY/attr[@attr-name="DirXML-Associations"]/value )

However, I need another predicate on the value node, because I want the one, where the component node, has the following predicate:

[(@name='volume') and (text()=$TARGET-DRIVER-DN)]

That means the XML attribute name (inside the <component> node, that has the value of volume, and the text() of the that node is equal to the $TARGET-DRIVER-DN local variable value (Which I had set earlier).

When we get that far in our XPATH:

$ASSOC-QUERY/attr[@attr-name="DirXML-Associations"]/value/component[(@name='volume') and (text()=$TARGET-DRIVER-DN)]

Lets say we are looking for the Active Directory driver, so in our TARGET-DRIVER-DN variable we have the string \ACME-LAB\LAB\SERVICES\IDVAULT\Active Directory so that we can text compare it.

That means we are inside this node of the Query result:

	<attr attr-name="DirXML-Associations">
		<value timestamp="1217007039#74" type="structured">
			<component name="nameSpace">1</component>
			<component name="volume">\ACME-LAB\LAB\SERVICES\IDVAULT\Active Directory</component>
			<component name="path">f0648eab27d6da4283246583112d6319</component>
		</value>
				

Now we want to do some funny stuff, and walk around this node. We have currently selected the node
<component name=”volume”>\ACME-LAB\LAB\SERVICES\IDVAULT\Active Directory</component> but we want to do more than that, so we add on the next bit of XPATH:

parent::component[@name='nameSpace']/text()='1'

Which tells us to now walk up one node, to the <value> node containing this <component> node, and look for a <component> node underneath it with the predicate [@name='nameSpace'] (the one with the XML attribute name, having a value of nameSpace). Then look at the text() of that node, and see if it is equal to 1.

In this context the nameSpace component (Meant originally to hold a single digit value to represent DOS, NFS, OS2, LONG, MAC traditional Netware file system) holds the Association state, with 0 being ignore, 1 being associated, and the rest not mattering all that much any more.

In the example there, I wanted to see if the user was associated. The actual use case for that, was to then figure out if the component[@name='path']/text() actually had a value.

But then that is too hard to read in a single test. Thus in the article I broke it into two tests to make my life easier to debug and troubleshoot. In principle we should be able to do it all in one long XPATH statement though.

Having worked through that example, I hope you can see how we start at the current context and walk our way through it, node by node, almost like ‘cd’ing from directory to directory in a file system, or the like.

I don’t know if that muddied the waters more, or clarified them, but since there is so little out there written about XPATH in Identity Manager, I figured it was worth another swing at it!

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)

Tags: ,
Categories: Uncategorized

Disclaimer: As with everything else at NetIQ Cool Solutions, this content is definitely not supported by NetIQ, so Customer Support will not be able to help you if it has any adverse effect on your environment.  It just worked for at least one person, and perhaps it will be useful for you too.  Be sure to test in a non-production environment.

Comment