 | Level: Intermediate Ben Wagner (wagnerb@us.ibm.com), Software Developer,
IBM
Wilfred Jamison, Ph.D. (wjamison@us.ibm.com), Senior Software Engineer and Manager, IBM
15 Apr 2008 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.
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).
In this article, learn step by step how to add new functions to your monitor
model using UDXF.  | | The mortgage lending scenario was first described in
Part 3
of this series. |
| 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:
- Switch to the Java perspective and create a new Java project with the name
MyUDF.
- 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
Select Monitor Runtime Library, click Next, then click
Finish.
Figure 2. Selecting the
monitor run time library
- 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
- 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.
- Switch to the business monitoring perspective, create a new business
monitoring project, then a new monitor model within it.
- 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
Click Add JARs..., and select the JAR created above.
Figure
5. Selecting the JAR file
Figure
6. Successful selection
- 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
Figure 8. Successful
UDXF addition
- 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
 |
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 type | XML datatype |
|---|
| java.lang.String |
xs:string
| | java.math.BigInteger |
xs:integer, xs:decimal | | java.math.BigDecimal |
xs:decimal
| | java.lang.Boolean |
xs:boolean
| | javax.xml.datatype.XMLGregorianCalendar | xs:date, xs:time, xs:dateTime | | javax.xml.datatype.Duration |
xs:duration, xs:dayTimeDuration, xs:yearMonthDuration | | java.util.Calendar |
xs:dateTime
| | java.net.URI |
xs:string
| | java.util.UUID |
xs:string
|
Table 2 shows the parameter types. XML dataypes in bold are defaults.
Table 2. Parameter types
| Java parameter type | XML datatype |
|---|
| java.lang.String |
xs:string
| | java.math.BigInteger |
xs:integer
| | java.math.BigDecimal |
xs:decimal, xs:integer | | java.lang.Boolean |
xs:boolean
| | javax.xml.datatype.XMLGregorianCalendar | xs:date, xs:time, xs:dateTime | | javax.xml.datatype.Duration |
xs: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 | Description | Name | Size | Download method |
|---|
| The simple examples used in this article. | example.zip | 8KB | HTTP |
|---|
| The MortgageLending example used in this article. | mortgage.zip | 41KB | HTTP |
|---|
Resources Learn
- Check out the other parts of this series:
- Part 1,
"What's new in WebSphere Business Monitor 6.1"
(developerWorks, Dec 2007)
- Part 2,
"WebSphere Business Monitor 6.1 installation improvements"
(developerWorks, Jan 2008)
- Part 3,
"Improved Unit Test Environment in IBM WebSphere Business Monitor Development Toolkit V6.1"
(developerWorks, Feb 2008)
- Part 4,
"Use the Integrated Test Client to
improve iterative development with WebSphere Business Monitor V6.1"
(developerWorks, March 2008)
- Part 5,
"Managing failed and unrecoverable events In IBM WebSphere Business Monitor V6.1"
(developerWorks, April 2008)
- Part 6,
"Combine high-level monitor models from IBM WebSphere Business Modeler with
low-level monitor models from IBM WebSphere Integration Developer"
(developerWorks, April 2008)
- Get the Javadoc for
DatatypeFactory,
XMLGregorianCalendar,
and
Duration.
-
Authoring XPath functions
has more information and examples.
- Browse the
IBM WebSphere Business Monitor documentation
for more details and reference material.
- Learn about
WebSphere Business Monitor
features and benefits, system requirements, library, services, news items and
more.
- Read about
WebSphere business process management
on developerWorks.
- Refer to
developerWorks
for more information on WebSphere business process management.
- Browse the
Jump-start business activity monitoring (BAM) series
for a wealth of WebSphere Business Monitor how-to information and samples.
- Browse the
technology bookstore
for books on these and other technical topics.
- Get an
RSS
feed
for the series Put new capabilities of business activity monitoring (BAM) to
work.
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 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 is currently a manager and team lead for the Code
Generation, Validator, and UTE components of WebSphere Business Monitor. He has
been with IBM for eight years and worked on products including IBM Firewall and
IBM WebSphere Application Server. His expertise includes network security,
performance analysis, concurrent and distributed systems, J2EE, Linux, and others.
He also worked on incubation projects for the On-Demand Software Development team.
He has worked with customers found on the New York Stock Exchange, eBay, and
Toronto Dominion Bank. |
Rate this page
|  |