Skip to main content

Put new capabilities of business activity monitoring (BAM) to work, Part 7: Creating user-defined XPath functions for IBM WebSphere Business Monitor V6.1

Add new functions in a snap

Ben Wagner (wagnerb@us.ibm.com), Software Developer, IBM Japan
Ben Wagner photo
Ben Wagner is a software developer currently working on the Validator, Interpreter, and user-defined XPath function implementation for IBM WebSphere Business Monitor. He firmly believes in the separation of mechanism and policy.
Wilfred Jamison, Ph.D., Senior Software Engineer and Manager, IBM, Software Group
Dr. Wilfred C. Jamison
Dr. Wilfred C. Jamison is a senior technical manager for the WebSphere Business Monitor development team at the IBM Research Triangle lab. He is currently involved in many projects within the Business Performance Management organization

Summary:  In this series, learn about the dramatic changes in IBM® WebSphere® Business Monitor V6.1—a major release that extends capability and simplifies how you monitor and manage the performance of your business. The user-defined XPath functions (UDXF) are a useful and powerful extension to the programming model. With this new feature, you can add function to your monitor model logic. You can write a user-defined XPath function that performs any ordinary Java™ functions, such as reading data from a remote CICS® database or calling out a Web service. In this article, learn to write your own user-defined XPath function and use it in any expression within your monitor model.

Date:  15 Apr 2008
Level:  Intermediate PDF:  A4 and Letter (888KB | 21 pages)Get Adobe® Reader®
Activity:  1803 views

Introduction

The monitor programming model receives data coming from external events, extracts these data, and maps them to metrics that may trigger other operations to occur. Expressions play an important role; they produce the values used in the monitor model and dictate the overall flow of the monitor model logic. You can define what values are eventually stored, how and when certain operations or events can take place, what events to process, and what monitor context instances to operate on—all by using XPath expressions.

Previously, expressions were built mainly from event data, previously computed expressions, and built-in XPath functions. WebSphere Business Monitor V6.1 has introduced the notion of a user-defined XPath function (UDXF). With UDXF, you can:

  • Create functions that are not currently available.
  • Pull information and event data from other sources, such as Web services, spreadsheets, and third-party databases.
  • Customize functions that vary depending on conditions, such as your current locale, time, and so forth.
  • Implement complicated rules that can affect the logic of your monitor model (for example, calendaring functions).
The mortgage lending scenario was first described in Part 3 of this series.

In this article, learn step by step how to add new functions to your monitor model using UDXF. A very simple example is used first. Then you'll follow a more complicated example with the mortgage lending business scenario.

You can download the examples used in this article.

Creating the Java project

The first step is to write the Java function that you plan to use as an additional XPath function in your XPath expressions, as follows:

  1. Switch to the Java perspective and create a new Java project with the name MyUDF.
  2. Add the monitor run time library to this Java project by right-clicking on the project and selecting Build Path -> Add Libraries.

    Figure 1. Setting the build path
    Selecting Build Path > Add Libraries

    Select Monitor Runtime Library, click Next, then click Finish.



    Figure 2. Selecting the monitor run time library
    Selecting the Monitor Runtime Library

  3. Create a new class in this project to hold the function implementations. This class must exist in a nondefault package.

    UDXF classes in the default package will cause errors, because they cannot be referenced later in the generated code of other projects.

    You may also wish to create a messages.properties file for localization. Listings 1 and 2 show examples.



    Listing 1. com/foo/bar/Truth.java
    package com.foo.bar;
    
    import static com.ibm.wbimonitor.xml.expression.udf.
    XPathFunction.CallingConvention.JAXB;
    import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Indicator.One;
    
    import com.ibm.wbimonitor.xml.expression.udf.XPathFunction;
    import com.ibm.wbimonitor.xml.expression.udf.XPathType;
    
    public class Truth {
        private static final String NAMESPACE_NAME = "http://www.foo.com/truth"; 
    
        @XPathFunction(
                namespaceName = NAMESPACE_NAME, 
                localName = "true", 
                description = "Returns true.",
                descriptionKey = "com.foo.bar.messages/Truth_trueDescription", 
                callingConvention = JAXB,
                isDeterministic = true, 
                isSelfContained = true
        )
        public static @XPathType(occurrenceIndicator = One) Boolean logicalTrue() {
            return Boolean.TRUE;
        }
    }



    Listing 2. com/foo/bar/messages.properties
    Truth_trueDescription=Returns true.

    At this point your project should look like Figure 3.



    Figure 3. Example Java project
    Example Java Project layout.

  4. Export the project as a JAR by right-clicking on the project, selecting Export..., Java -> JAR file. This JAR file will be imported for use in the monitoring model in the next section.

    Though the JAR file can be saved anywhere in your file system, it is recommended you save it within a project in your work space.

    For this tutorial, save the JAR file into the directory containing the MyUDF project.

Using the new function

Reloading an updated JAR

If you've made a change to a UDXF and want to test it with the changes, first re-export the Java project as a JAR (overwriting the old JAR). Switch to the business monitoring perspective, right-click the business monitoring project, and select External Function Libraries.... There should be a notice that one or more JAR files have changed. Click OK to reload the JAR files.

Now that the function has been written, you want to be able to use it in your expressions.

  1. Switch to the business monitoring perspective, create a new business monitoring project, then a new monitor model within it.
  2. Add the UDXF created above to the business monitoring project by right-clicking on the project and selecting External Function Libraries....

    Figure 4. Selecting external function libraries
    Selecting External Function Libraries

    Click Add JARs..., and select the JAR created above.



    Figure 5. Selecting the JAR file
    Selecting the jar file



    Figure 6. Successful selection
    Successful selection

  3. To add the UDXF to the monitor model, open the monitor model source file to edit and select the monitor details model tab. Click Edit... in the user-defined XPath functions section of the edit window, select the namespace, assign a prefix to it, and click Finish.

    Figure 7. Adding UDXF to monitor model
    Adding UDXF to Monitor Model



    Figure 8. Successful UDXF addition
    Successful UDXF addition

  4. To use the new function, create a boolean metric and enter truth:true() as the default value.

    The new function and its description are available through content assist, as shown in Figure 9.



    Figure 9. Using the new function
    new function


A closer look at the example

So far you have learned how to create a UDXF and how to include and use it within a monitor model. This section further examines the different elements used when writing a UDXF.

Imports

Listing 3 shows four import statements that must be included when writing a UDXF. The first two import static statements allow you the convenience of using "JAXB" and "One" in the code directly, instead of using their very long full names (XPathFunction.CallingConvention.JAXB and XPathType.Indicator.One, respectively). The second two import statements are for the two UDXF annotations that you use to declare a method to be a UDXF.


Listing 3. com/foo/bar/Truth.java, imports.
import static com.ibm.wbimonitor.xml.expression.udf.XPathFunction.CallingConvention.JAXB;
import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Indicator.One;

import com.ibm.wbimonitor.xml.expression.udf.XPathFunction;
import com.ibm.wbimonitor.xml.expression.udf.XPathType;

Namespace name

While not required, it's a good idea to place your namespace name (the namespace in which your functions will be found) in a constant. If you have multiple UDXFs defined in one class, it is easy to put them all in the same namespace, as shown in Listing 4.


Listing 4. com/foo/bar/Truth.java, namespace name
private static final String NAMESPACE_NAME = "http://www.foo.com/truth"; 

XPathFunction annotation

Listing 5 shows an example of using the XPathFunction annotation, which contains the information necessary to mark a method as a UDXF.


Listing 5. com/foo/bar/Truth.java, XPathFunction annotation
@XPathFunction(
            namespaceName = NAMESPACE_NAME, 
            localName = "true", 
            description = "Returns true.",
            descriptionKey = "com.foo.bar.messages/Truth_trueDescription", 
            callingConvention = JAXB,
            isDeterministic = true, 
            isSelfContained = true
    )

The XPathFunction annotation has several elements, as follows.

namespaceName
(required, String) The namespace in which the function will be found. It must parse to a valid uniform resource identifier (URI).

This namespace will be assigned a prefix in the function prefix library area when adding the functions of this namespace to a monitor model.

localName
(required, String) The XPath name of the function. It does not have to be the same or in any way related to the name of the Java method implementing this function.

This should be a valid NCName. Although everything will work when using an invalid NCName, it will not be possible to call the function from within an expression.

description
(optional, String default "") The default description of this method. Description may be displayed by content assist.
descriptionKey
(optional, String default "") Used to find a localized description of this method. The format of this String is "bundle.name/key_name".

The bundle will be searched for with the standard Java bundle resolution mechanism within the JAR in which this method is found, given the current locale settings. If the bundle or key cannot be found, the description will be used. This description may be displayed by content assist.

callingConvention
(optional, XPathFunction.CallingConvention default JAXB) Currently, only the built-in JAXB bindings are supported.

An internal binding exists but is not supported. Use it at your own risk.

isDeterministic
(required, boolean) Set to true if the output of this function only depends on its parameters. For example, you would expect add(1, 2) to always return 3 so isDeterministic would be true.

On the other hand, random() will return a different value for every call, so isDeterministic would be false. Functions marked with isDeterministic as true may be used to produce constant expressions, allowing for additional validation.

isSelfContained
(required, boolean) Set to true if the function does not use any external resources that exist only in the target environment. Functions marked with isSelfContained as true will be called during validation if the parameters can be determined, allowing for additional validation.

If you have a function that makes external connections to a database or server, it should have isSelfContained set to false so that it is not called during monitor model development.

Java types for method and parameters

For the method and parameters, only certain types are allowed. The types are based on the default JAXB bindings when the callingConvention of the XPathFunction annotation is JAXB. Listing 6 shows an example.


Listing 6. com/foo/bar/Truth.java, Java types for method and parameters
public static @XPathType(occurrenceIndicator = One) Boolean logicalTrue() {
     

Table 1 lists the Java types allowed and the corresponding XML datatype. Those XML datatypes listed in bold are defaults. To select an XML datatype other than the default, you must use the XPathType annotation.


Table 1. Method return types
Java return typeXML datatype
java.lang.Stringxs:string
java.math.BigIntegerxs:integer, xs:decimal
java.math.BigDecimalxs:decimal
java.lang.Booleanxs:boolean
javax.xml.datatype.XMLGregorianCalendarxs:date, xs:time, xs:dateTime
javax.xml.datatype.Durationxs:duration, xs:dayTimeDuration, xs:yearMonthDuration
java.util.Calendarxs:dateTime
java.net.URIxs:string
java.util.UUIDxs:string

Table 2 shows the parameter types. XML dataypes in bold are defaults.


Table 2. Parameter types
Java parameter typeXML datatype
java.lang.Stringxs:string
java.math.BigIntegerxs:integer
java.math.BigDecimalxs:decimal, xs:integer
java.lang.Booleanxs:boolean
javax.xml.datatype.XMLGregorianCalendarxs:date, xs:time, xs:dateTime
javax.xml.datatype.Durationxs:duration, xs:dayTimeDuration, xs:yearMonthDuration

XPathType annotation

The XPathType annotation may be used on the method itself to clarify the return value of the method. It can also be used on parameters of a method to clarify the type of the parameter, as shown below.


Listing 7. com/foo/bar/Truth.java, XPathType annotation
 
public static @XPathType(occurrenceIndicator = One) Boolean logicalTrue() {
     

The XPathType annotation has three elements:

name
(optional, String default "") May be used to give a name to a parameter as a form of documentation.

If the XPathType annotation is used on a method, this element is ignored.

itemType
(optional, XPathType.Type default infer) Clarifies the type of return value or parameter. When this is set to Infer, the defaults listed in the table above apply.

The XMLGregorianCalendar has no default type associated with it. The XPathType annotation must always be used with XMLGregorianCalendar, and it must specify the itemType.

occurrenceIndicator
(optional, XPathType.Indicator default ZeroOrOne) Further clarifies the type of return value or parameter. For a full understanding of occurrence indicators, you need to understand the XPath type system. For this application, however, all you really need to know is that if this is set to One on a return value, it means that you are promising to never return null. If you set this to One on a parameter, you expect that the argument should never be null. A setting of ZeroOrOne allows nulls for these values at run time, and the type will be marked with a '?' in the UI to indicate that the value may be null.

If you want to be able to use a function to assign a value to a metric marked as value is required, you will need to mark the method (the return value) with the XPathType annotation element occurrenceIndicator set to One, as metrics marked as value is required should not be assigned null.

Java method

The Java method that implements the function must be public static, as shown in Listing 8. The name of the method does not need to be related to the name of the function as specified in the XPathFunction.name annotation element.


Listing 8. com/foo/bar/Truth.java, the Java method
public static @XPathType(occurrenceIndicator = One) Boolean logicalTrue(){

There can be as many XPathFunction annotated methods in a class as desired. If there is more than one method, these methods do not need to share the same namespace, though it is recommended that they do for ease of maintenance.


Using XMLGregorianCalendar and Duration

When writing a UDXF that uses or returns a time, date, dateTime, duration, dayTimeDuration, or yearMonthDuration, the associated Java type is XMLGregorianCalendar (for time, date, dateTime) or Duration (for duration, dayTimeDuration, or yearMonthDuration). While not difficult, using these types could be confusing without an example of how to create and use them. See the snippet shown in Listing 9.


Listing 9. com/foo/bar/Sale.java
package com.foo.bar;

import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Indicator.One;
import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Type.Date;
import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Type.DayTimeDuration;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
import javax.xml.datatype.XMLGregorianCalendar;

import com.ibm.wbimonitor.xml.expression.udf.XPathFunction;
import com.ibm.wbimonitor.xml.expression.udf.XPathType;

public class Sale {
    private static final String NAMESPACE_NAME = "http://www.foo.com/sale"; 
    private static final DatatypeFactory XDF;
    static {
        try {
            XDF = DatatypeFactory.newInstance();
        } catch (final DatatypeConfigurationException e) {
            throw new IllegalStateException(e);
        }
    }
    
    @XPathFunction(
            namespaceName = NAMESPACE_NAME, 
            localName = "duration-of-sale", 
            isDeterministic = false, 
            isSelfContained = true
    )
    public static
    @XPathType(itemType = DayTimeDuration, occurrenceIndicator = One) Duration
    lengthOfSale() {
        return XDF.newDurationDayTime("P4D");
    }
    
    @XPathFunction(
            namespaceName = NAMESPACE_NAME, 
            localName = "date-of-sale", 
            isDeterministic = false, 
            isSelfContained = true
    )
    public static
    @XPathType(itemType = Date, occurrenceIndicator = One) XMLGregorianCalendar
    dateOfSale() {
        return XDF.newXMLGregorianCalendar("2007-12-05Z");
    }
}

Once a DatatypeFactory is created, use its newXXX methods to create XMLGregorianCalendars and Durations. There are several newXXX methods; the correct one should be used for best results. All XMLGregorianCalendars that are returned from a method should have a time zone set. (This limitation may be lifted in the future.) Be sure to read the documentation for DatatypeFactory, XMLGregorianCalendar, and Duration if you are planning to use them.


Errors related to UDXFs

Beware of a few kinds of errors using UDXFs.

Loading errors

Loading errors occur when you add a UDXF JAR to a business monitoring project using the external function libraries dialog box. The errors occur when there is an issue with a method marked with the XPathFunction annotation. There are many reasons this can happen, but typically they are type errors. For example, specifying a parameter of type XMLGregorianCalendar without an XPathType annotation will cause an error, as there is no default XML data type associated with XMLGregorianCalendar.

Loading errors are reported on the .monitor.classpath file in the business monitoring project. When a loading error exists in a JAR, none of the functions in that JAR will be available.

Run time errors

Run time errors occur when a UDXF is invoked. To help discover run time errors, the Validator will invoke the method if it is marked as isSelfContained and all the parameters are known. It is also possible to test UDXFs using the visual model editor (but how to do this is outside the scope of this article).

If an error is detected when the method is invoked, an error will be displayed in the problems view. These errors are normally either exceptions thrown by the method, or they are type errors when an XMLGregorianCalendar is stated to be a date or time but the fields are not set correctly.


The mortgage lending example

Sometimes there are complex business rules for simple things. In such cases, it can be useful to wrap the rules inside a UDXF. The following example demonstrates a UDXF to be used with the mortgage lending example previously used in this series.

The problem: Application to Funded Duration Days needs to be changed to reflect not the number of days or duration of time elapsed, but the number of business days between the start and finish times. The concept of "business day" varies greatly, and perhaps even independently of the model itself. The example in Listing 10 implements one set of rules about what makes a business day.


Listing 10. com/mortgage/lending/UDF.java
package com.mortgage.lending;

import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Indicator.One;
import static com.ibm.wbimonitor.xml.expression.udf.XPathType.Type.DateTime;

import java.math.BigInteger;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.xml.datatype.XMLGregorianCalendar;

import com.ibm.wbimonitor.xml.expression.udf.XPathFunction;
import com.ibm.wbimonitor.xml.expression.udf.XPathType;

public class UDF {
    private static final String NAMESPACE_NAME
         = "http://www.mortgage.com/lending"; 
    
    private static final long MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
    private static final int END_HOUR_OF_DAY = 17; //5:00PM
    @XPathFunction(
            namespaceName = NAMESPACE_NAME, 
            localName = "business-days-between", 
            description = "(start, end) Computes the number of end-of-business
             days between start and end. When there are more than 100 days,
             the result is an estimate.",
            isDeterministic = true, 
            isSelfContained = true
    )
    public static @XPathType(occurrenceIndicator = One) BigInteger businessDaysBetween(
            final @XPathType(itemType = DateTime, occurrenceIndicator = One)
                XMLGregorianCalendar day1
            , final @XPathType(itemType = DateTime, occurrenceIndicator = One)
                XMLGregorianCalendar day2
    ) {
        final GregorianCalendar current;
        final GregorianCalendar end;
        {
            final GregorianCalendar day1g = day1.toGregorianCalendar();
            final GregorianCalendar day2g = day2.toGregorianCalendar();
            
            final int cmp = day1g.compareTo(day2g);
            if (cmp == 0) {
                return BigInteger.ZERO;
            } else if (cmp > 0) {
                end = day1g;
                current = day2g;
            } else { //cmp < 0
                end = day2g;
                current = day1g;
            }
        }
        
        //greater than 100 days, estimate
        final long days = (end.getTimeInMillis() - current.getTimeInMillis())
            / MILLIS_PER_DAY;
        if (days > 100) {
            //the simple estimate is that five of every seven days are a business day.
            return BigInteger.valueOf((days * 5) / 7);
        }
        
        //After 5PM, advance to next day
        if (current.get(Calendar.HOUR_OF_DAY) >= END_HOUR_OF_DAY) {
            current.add(Calendar.DATE, 1);
        }
        //Start at the beginning of the day
        current.set(Calendar.HOUR_OF_DAY, 0);
        current.set(Calendar.MINUTE, 0);
        current.set(Calendar.SECOND, 0);
        current.set(Calendar.MILLISECOND, 0);
        
        //advance to the first business day
        while (end.compareTo(current) > 0) {
            if (isBusinessDay(current)) break;
            current.add(Calendar.DATE, 1);
        }
        
        //advance past the first partial day
        current.add(Calendar.DATE, 1);
        
        //count the business days until we pass the end day 
        long businessDays = 0;
        while (end.compareTo(current) > 0) {
            if (isBusinessDay(current)) ++businessDays;
            current.add(Calendar.DATE, 1);
        }
        
        return BigInteger.valueOf(businessDays);
    }
    
    /**
     * Checks to see if a given day is a business day.
     * @param day the day to check.
     * @return true is the day is a business day, false if not.
     */
    private static boolean isBusinessDay(final GregorianCalendar day) {
        //Business rule: everyone is off on Programmer's Day
        final int dayOfYear = day.get(Calendar.DAY_OF_YEAR);
        if (dayOfYear == 256) return false;
        
        //Business rule: no one works on Talk Like a Pirate Day
        final int month = day.get(Calendar.MONTH);
        final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
        if (month == Calendar.SEPTEMBER && dayOfMonth == 19) return false;
        
        //Business rule: Sunday and Saturday are not business days
        final int dayOfWeek = day.get(Calendar.DAY_OF_WEEK);
        if (dayOfWeek == Calendar.SATURDAY || dayOfWeek == Calendar.SUNDAY) return false;
        
        //Otherwise: back to work!
        return true;
    }
}

To use this in the model, follow the steps listed above to create the JAR and add it to the business monitoring project. Then change the metric value expression of Application to Funded Duration Days to:


Listing 11. MortgageLendingBAM/Mortgage_Lending_BAM_MC/Application_to_Funded_Duration_Days
time:business-days-between(aux_Application_Start_Time
    , Funding_Complete/fundingCompletePart/mor:dateCompleted)


Conclusion

In this article, you learned how to write and use a user-defined XPath function in a monitor model. UDXF is new in IBM WebSphere Business Monitor V6.1 and is one of the most useful additions to the monitor programming model. You are no longer limited to the built-in XPath functions. With the ability to write and implement an XPath function using Java, there are endless possibilities for what you can accomplish, whether it's getting the current stock quote of IBM or getting the status of an order from a spreadsheet.



Downloads

DescriptionNameSizeDownload method
The simple examples used in this article.example.zip8KB HTTP
The MortgageLending example used in this article.mortgage.zip41KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

  • Download IBM product evaluation versions and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

Discuss

About the authors

Ben Wagner photo

Ben Wagner is a software developer currently working on the Validator, Interpreter, and user-defined XPath function implementation for IBM WebSphere Business Monitor. He firmly believes in the separation of mechanism and policy.

Dr. Wilfred C. Jamison

Dr. Wilfred C. Jamison is a senior technical manager for the WebSphere Business Monitor development team at the IBM Research Triangle lab. He is currently involved in many projects within the Business Performance Management organization

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Sample IT projects, Architecture, WebSphere
ArticleID=301246
ArticleTitle=Put new capabilities of business activity monitoring (BAM) to work, Part 7: Creating user-defined XPath functions for IBM WebSphere Business Monitor V6.1
publish-date=04152008
author1-email=wagnerb@us.ibm.com
author1-email-cc=
author2-email=wjamison@us.ibm.com
author2-email-cc=wjamison@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).