XPATH and math

0 Likes

XPATH and math:



Novell Identity Manager has several languages supported to process events in the flow of events. There is the original XML Style sheets (XSLT) from the DirXML 1.x days, and with the release of NSure Identity Manager 2.0, DirXML Script got added. Along the way, the ability to call out to Java classes was maintained, and with the release of Novell Identity Manager 3.5 we got the ability to call out to ECMA Script functions (aka Java Script).



Throughout all of that we have the ability to use XPATH, the XML Path language in both DirXML Script, XSLT, and I think even ECMA Script.



I have been working on a series of articles about XPATH in Novell Identity Manager, and here are some links for the ones I have written so far:












and some other good articles by others:








In the article Some thoughts on XPATH in Novell Identity Manager I talked about some of the XPATH features we can use in Identity Manager.



One of the discussions in that article was the difference between selecting a node via XPATH (aka a node test) and doing something, like math, with XPATH.



There are a couple of caveats to using XPATH for math that are worth thinking about, and once you have thought about them, they are of course very obvious, and easy to remember, but until you spend that moment to think about it, they are somewhat non obvious.



First off, there is a number() function, that is useful to use, when you want to be sure the value you are about to use is really an integer. For example, you might use a counter variable in a for-each loop. In that case, you might initialize it before the loop begins by set local variable COUNTER to the string 0. Now is that a string or an integer, as far as XPATH is concerned? In my experience, most of the time, that is sufficient to be treated as an integer. However, it is easy to be certain, just use the number($COUNTER) function, whenever you use the variable for math.



Thus inside your for-each loop, you can increment it either as. set local variable equal to the XPATH $COUNTER 1 or perhaps to be safe the XPATH number($COUNTER) 1.



Subtraction is the next issue. From the XML Path language RFC at http://www.w3.org/TR/1999/REC-xpath-19991116 there is a note.



NOTE: Since XML allows - in names, the - operator typically needs to be preceded by white space. For example, foo-bar evaluates to a node-set containing the child elements named foo-bar; foo - bar evaluates to the difference of the result of converting the string-value of the first foo child element to a number and the result of converting the string-value of the first bar child to a number.


What that is basically saying is you need a space on either side of your minus sign. So while it might look like number($COUNTER)-1 should work, you really need to write it as number($COUNTER) - 1 which is a pretty easy thing to accommodate.



The reasoning being pretty obvious once you read the note. The dash symbol is valid in variable names, so unless it has spaces to either side, it is not clear to the parser that you mean do math, instead of using it as part of the name.



Trivial, but non obvious. In fact, I had stumbled upon this issue in a funny fashion, helping out a friend. I had suggested the XPATH $COUNTER 1 and that worked, but he was trying to then do subtraction and $COUNTER-1 was not working. I suggested he try number($COUNTER) - 1 thinking it was the number() function that would do the trick, but as I typed the suggestion, I had put in spaces around the dash (minus sign), without thinking about it, aiming only for clarity. Turns out I had gotten the correct idea without knowing why. The joys of learning complicated systems!



Next up is division. There is a mod operator, that acts much as you would expect, it returns the remainder from an integer division operation. I had used that before and had no issues.



I usually use the mod operator when I want a delay loop, because of replica synchronization. That is, an event happens to two objects on one replica in a driver there. The driver on this second box needs to see the event on both of them before proceeding, so since I cannot guarantee both changes will synchronize at the same time or even close enough, I usually build a test loop.



The loop uses a counter variable, that runs for a GCV called RetryCount (defined as say 120 or so times) to use the Query token for the needed attribute. The Query token as described in the article More thoughts on Source/Destination/Operation attribute tokens in Identity Manager always checks, and does not read from the cache, so it keeps trying to look for the second object. Then after that first number of loops, I pause for a GCV RetryPauseTime (defined as say 1000 milliseconds), and finally after a GCV RetryMax, (defined as say 601, or 10,000 tries) I exit the loop entirely and decide the second object never appeared. You can use the mod operator to divide the current counter value by the RetryCount GCV and show the left over. Whenever it is zero, it is time to take a break for RetryPauseTime.



Well that was mod, which is nice, now on to division. Without thinking about it, I tried an XPATH of number($COUNTER)/number($MEM-FREE) in a rule, and the driver refused to start, with the error:



DirXML Log Event -------------------
Driver: \ACMEIDVAULT\acmeny\services\FLAT\BlockingActions
Channel: Subscriber
Status: Error
Message: Code(-9130) Error in vnd.nds.stream://ACMEIDVAULT/acmeny/services/FLAT/BlockingActions/Subscriber/[acme] acmeEnt
itlementCounter tools#XmlData:508 : An invalid XPATH expression 'number($COUNTER)/number($MEM-FREE)' is specified: java.lang.IllegalArgumentException: DOMEvaluator parser error: a node-test was expected.



Here we have the same basic issue. A node test in XPATH, usually includes the forward slash symbol, which I always think of as division. But in XPATH it is not division. Again, completely trivial once you spend a moment to think about it, but not obvious at first glance.



Turns out the real division operator is div. Easy enough to fix, change the XPATH number($COUNTER)/number($MEM-FREE) to number($COUNTER) div number($MEM-FREE) and we are good to go.



Has anyone else run into other similar obvious in hindsight things about XPATH, that are worth mentioning? Please feel free to comment or write an article about it! The more the merrier!




Labels:

How To-Best Practice
Comment List
Related
Recommended