In this chapter we implement a server extension function
that compares two date
or dateTime
values. As we have
seen in
From
Conceptual Model to Schema::Constraints across
documents, the comparison of values of these datatypes using
XPath's native facilities is awkward; anything that facilitates this is
beneficial. Note that this is only a deficiency of XPath 1.0; in contrast, both
XPath 2.0 and XQuery support XML Schema datatypes, including date and time
formats. The module concept of XQuery offers a powerful mechanism for managing
user-defined functions.
In addition, we implement a function that returns a
dateTime
string of the current time of day. (The complete source
code including an Install.xml file and Javadoc HTML file
is contained in the directory sxsjxsd in the documentation
set.)
We implement these functions in Java, and because we are lazy we implement them by using Java's sophisticated calendar support. After all, object-oriented programming is about re-use.
We start the Tamino X-Tension Builder and
create a new server extension which we call xsd
. We call the
package tamino.SXS.xsd
and the class simply xsd
. The
tool will generate a code framework for a Tamino
server extension.
We can now add our new query functions as new methods into this framework. To do so, we use the menu function
:The first function we add is called dtComp()
. We define it
as a query function. The tool asks us for the type of the result, which we set
to integer
. Then we add two operands, op1
and
op2
, of type charstr
.
The second function is called current()
. Again, we define
it as a query function and set the result type to charstr
. There
are no operands.
The X-Tension Builder generates the required method code frames into the class code. We can now implement our custom logic (emphasized):
// xsd.java: Implementation of Server Extension xsd // // Tamino Server Extension xsd // // $javadoc:on package tamino.SXS.xsd; import com.softwareag.ino.sxs.*; import java.text.SimpleDateFormat; import java.util.Date; // Javadoc comments. // TODO: Add more detailed description if necessary /** * Tamino Server Extension xsd * @author My name * @version 1.0 */ public class xsd extends ASXJBase { // Version information for current Server Extension xsd. // TODO: Change SXS Version here if necessary: static final SXSVersion sxsVersion = new SXSVersion (1, 0); // Description of current Server Extension: // TODO: Change this string if necessary static final String sxsAbout = "Tamino Server Extension xsd"; // TODO: Enter further class variables here. /** * The default constructor * (No other constructor allowed.) */ public xsd () { // TODO: enter SXS initialization here. } // Description of Server Extension Function dtcomp // TODO: Change description if necessary static final String dtcompAbout = "Query Function dtcomp"; /** * comparison of XSD date/time * @param op1 parameter (XML Schema date, dateTime, time) * @param op2 parameter (XML Schema date, dateTime, time) * @return comparison result: * -1 (op1 < op2), 0 (op1 = op2), 1 (op1 > op2) */ public int dtcomp (String op1, String op2) throws java.text.ParseException { // To delete dtcomp, remove it here and from Install.xml. Date d1 = toDate(op1); Date d2 = toDate(op2); return d1.compareTo(d2); } // Description of Server Extension Function current // TODO: Change description if necessary static final String currentAbout = "Query Function current"; public String current () { // To delete current, remove it here and from Install.xml. SimpleDateFormat df2 = new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss" ); return df2.format(new Date()); } /** * Convert lexical XML Schema date/time representation * to Java Date format * @param s input string * @return Date value */ private Date toDate(String s) throws java.text.ParseException { // determine formatting string String f = (s.indexOf("T",0) >= 0 ? "yyyy-MM-dd'T'HH:mm:ss" : (s.indexOf(":", 1) >= 0 ? "HH:mm:ss" : "yyyy-MM-dd")); // check for explicit time zone int p = Math.max (s.indexOf("+", 8),s.indexOf("-", 8)); if (p >= 0) { f += "z"; // indicate time zone in formatting string s = s.substring(0,p-1)+"GMT"+s.substring(p); // keep SimpleDateFormat happy } else if (s.charAt(s.length()-1) == 'Z') { // check for UTC time zone f += "z"; s = s.substring(0,s.length()-1) + "UTC"; } // create SimpleDateFormat object SimpleDateFormat df = new SimpleDateFormat ( f ); // and use it as a parser return df.parse(s); } }
Tip:
A good way of developing such a server function is first
to create and debug it in your preferred Java development environment, and then
copy the code into the X-Tension Builder.
Along with the Java code, the X-Tension Builder has created a control file, called Install.xml. Remember that Tamino server extensions can be written in a variety of programming languages. This file describes the specific extension module in a generic, language-neutral format. It also specifies the datatypes of input and output parameters in terms of server extensions. The following table shows how Java datatypes relate to server extension specific types:
server extension type |
Java input and result parameters |
Java output and in/out parameters |
---|---|---|
ino:XML-OBJ (i.e. node list) |
String | StringBuffer |
xs:string |
String | StringBuffer |
xs:boolean |
boolean | BooleanRef |
xs:int |
int | IntRef |
xs:double |
double | DoubleRef |
xs:float |
float |
And this is how an Install.xml file looks:
<?xml version="1.0" encoding="ISO-8859-1"?> <ino:Administration xmlns:ino="http://namespaces.softwareag.com/tamino/response2" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <ino:Object Name="xsd" Id="tamino/SXS/xsd/xsd" Infrastructure="Java"> <ino:About Author="My name" CreationDate="2008-08-14" /> <ino:Function Name="dtcomp" Usage="Query"> <ino:Parameter Name="op1" TSDType="xs:string" /> <ino:Parameter Name="op2" TSDType="xs:string" /> <ino:Parameter Name="result" TSDType="xs:int" /> <ino:About>Query Function dtcomp</ino:About> </ino:Function> <ino:Function Name="current" Usage="Query"> <ino:Parameter Name="result" TSDType="xs:string" /> <ino:About>Query Function current</ino:About> </ino:Function> </ino:Object> </ino:Administration>
As you can see, this file describes all the functions implemented in that particular server extension module, together with their respective parameters. This file must be modified manually if we subsequently decide to change the name of any function, the name and number of parameters, or their type.
After compiling, we pack this class into an
.sxp file using the jazz
database. Thereafter, we can use the new
functions within queries, for example:
jazzMusician[xsd.dtComp(birthDate,"1920-01-01") >= 0]
which returns all jazz musicians born in 1920 or later;
jazzMusician[xsd.dtComp(birthDate,xsd.current()) > 0]
which should result in an empty set of documents.
In XQuery 4 we can similarly write:
for $j in input()/jazzMusician[xsd.dtComp(birthDate,"1920-01-01") >= 0] return $j
and:
for $j in input()/jazzMusician[xsd.dtComp(birthDate,xsd.current()) > 0] return $j
The first case can be expressed solely with the means of XQuery 4 because XQuery 4 supports XML Schema datatypes. We could write:
declare namespace xs="http://www.w3.org/2001/XMLSchema" for $j in input()/jazzMusician[birthDate >= xs:date("1920-01-01")] return $j
The second case, however, still requires a server extension to fetch the current date.
Note that search criteria that involve server extension functions are
always processed in the post-processing phase of a query (see
From Schema to
Tamino::Efficient
Queries). So, even if birthDate
is defined as an
index, this search criterion still results in a scan through all documents.
Tip:
An alternative way to develop
Tamino Server Extensions is to use the X-Tension
Object Analyzer. This tool can be invoked from the
Tamino Manager. Using this method, we would first
develop the necessary Java classes using an IDE of our choice. Then, we can
load the executable (.class or .jar
file) into the X-Tension Object Analyzer. Now we can edit the details of the
imported executable, such as the server extension name, description, author,
and help files. The function creates a
server extension package file that can be installed in a Tamino database. This
is a map-out function with the onCompose
property
defined.