XQuery TUTORIAL

Creating a Web Service Tutorial

Updated: 05 Oct 2021

Introduction

XQueryWebService is a framework that allows you to expose an XQuery as a Web service. In this section, you'll see how you can use the Data Integration Suite's XQuery product to build powerful data services that query, aggregate, and update multiple data sources using XQuery.

Get hands-on experience running XQuery Web service applications developed using the XQueryWebService framework on a live application server. Test Web service operations exposed by the Employee Lookup Example, run finished applications like the Photo Search Demonstration, use XQuery to query an incoming request as XML as shown in the Travel Reservation Demonstration, or just explore the WSDL used to describe Web service operations that query relational data sources.

Overview: The XQueryWebService Framework

XQueryWebService is a framework that allows you to expose an XQuery as a Web service. In this section, you'll see how you can use DataDirect XQuery to build powerful data services that query, aggregate, and update multiple data sources using XQuery.

About the Example

The application used to illustrate some of the features of the XQueryWebService framework is a simple employee lookup: based on the employee ID you enter, the query returns information such as first and last name, job level, date of hire, and so on.

Before You Begin

Before getting started with the example presented in this section, you'll need the following:

  • A copy of DataDirect XQuery — If you don't already have DataDirect XQuery installed, you can download an evaluation version here: http://www.xquery.com/download/.

    Note: You need to use at least DataDirect XQuery 3.1 (build 034703). If you already have installed DataDirect XQuery but are using an earlier build, you can either:
  • An XQuery editor — You will need an XQuery editor to author the XQuery you wish to expose as a Web service. You can use any XQuery editor you like, but we're partial to DataDirect's Stylus Studio and theDataDirect XQuery Editor for Eclipse.

  • A Java servlet container — The XQueryWebService framework has been tested with several Java servlet containers, including:

    You can use any Java servlet container you like; we used the open source Apache Tomcat for this example, so you will have to adjust references to Tomcat to the Java servlet container of your choice in your installation.

Getting Started

To get started with the XQueryWebService example:

  • Copy the DataDirect XQuery libraries to the Java servlet container \lib folder (<Tomcat_dir>\lib, for example).
  • Create an employee-lookup folder under the Java servlet container \webapps folder (<Tomcat_dir>\webapps\employee-lookup, for example).
  • Create a WEB-INF folder under the newly created \employee-lookup folder (<Tomcat_dir>\webapps\employee-lookup\WEB-INF, for example).
  • Place the following configuration file, web.xml, in the WEB-INF folder:

<?xml version="1.0"?>
<web-app
version="2.4">
    
<description>Employee lookup</description>
<display-name>Employee-lookup</display-name>
        
<servlet>
    <servlet-name>XQueryWebService</servlet-name>
    <servlet-class>com.ddtek.xquery.webservice.XQServlet
    </servlet-class>
    <init-param>
        <param-name>JNDIDataSourceName</param-name>
        <param-value>jdbc/employee-lookup</param-value>
    </init-param>
    <init-param>
        <param-name>QueriesFolder</param-name>
        <param-value>c:\MyQueryDir</param-value>
    </init-param>
</servlet>
<resource-ref>
    <res-ref-name>jdbc/employee-lookup</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>
<servlet-mapping>
    <servlet-name>XQueryWebService</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
What's Next?

Once you've taken care of the basics, you're ready to move on, starting with specifying relational data sources, as described in the following section.

Specifying Database Connections

If your queries access relational data, you need to register the database connection with the Java servlet container. This section describes two different ways to do this:

Specifying a Single Connection

You can use the Web Application configuration file (web.xml) to specify the database connection, as shown in this example, which specifies a connection to Microsoft SQL Server:

<init-param>
    <param-name>DDXQJDBCConnection1</param-name>
    <param-value>jdbc:xquery:sqlserver://localhost;user=sa;
    DatabaseName=pubs</param-value>
</init-param>

The name of the <param-name> element can be any string you like.

Connections specified this way are created and then discarded with each request. A more efficient technique is database connection pooling, which is discussed next.

Database Connection Pooling

Connection pooling is a technique for specifying database connections that allows a Web application to create a database connection on demand, and then return it to the pool when it is no longer need, rather than discarding it. Connection pooling can improve response time and help preserve database resources. Web server requests are locked if no connection is available in the pool.

Another benefit of using connection pooling is that it allows for connection recovery in the event that the connection is lost — if the server times out, for example. Dropped or disrupted connections are automatically replaced once the server is returned to service.

All popular Java servlet containers offer a connection pooling framework, and DataDirect XQuery can be plugged into most of them (Apache Tomcat, BEA WebLogic, IBM WebSphere, and JBoss, for example).

Creating a Connection Pool

Here's how to create a connection pool in Apache Tomcat:

  • Create a META-INF folder under the \employee-lookup folder (<Tomcat_dir>\webapps\employee-lookup\META-INF, for example).
  • Place the following configuration file, context.xml, in that folder:
  • <?xml version="1.0" encoding="ISO-8859-1"?>
    <Context path="/employee-lookup" docBase="employee-lookup"
    crossContext="false" reloadable="true" debug="0">
    <Resource name="jdbc/employee-lookup"
        auth="Container"
        type="javax.sql.DataSource"
        username="root"
        password="sa"
        driverClassName="com.ddtek.xquery3.jdbc.XQueryDriver"
    url="jdbc:datadirect:xquery3://JdbcUrl=
    {jdbc:mysql://localhost:3306/pubs_dbo?}"
        initialSize="1"
        accessToUnderlyingConnectionAllowed="true"
        validationQuery="SELECT * FROM users"/>
    </Context>

    Note that the name= attribute of the <Resource> element has to match the <res-ref-name> element (here it is "jdbc/employee-lookup") in the web.xml configuration file you described previously. This is the name that the Java servlet uses to perform the Java Naming and Directory Interface (JNDI) lookup required to retrieve the connection pool.

Creating a Connection Pool for Other Servers

As mentioned here.

Next Steps

Once you've specified your relational database connection, you can start to think about the technology you want to use to access the Web services based on your XQuery. We'll discuss two popular technologies — SOAP and REST — in the following section.

Choosing an Interface for Web Service Access

Data services — that is, your XQuery exposed as a Web service — deployed on the XQueryWebService framework can be accessed using two techniques:

SOAP is a W3C Recommendation and has been around for nearly a decade. SOAP is usually the more appropriate of the two techniques for complex processing or when security (exposing sensitive data) is an issue. But REST is gaining popularity for a couple of reasons, including minimal requirements on the client, and an interface — the URI — that is straightforward and well-understood.

Let's take a look at a simple XQuery, emp.xquery, which we have saved to our local XQuery directory (c:\MyQueryDir, as defined inweb.xml). When run against the SQL Server pubs sample database, this XQuery returns an employee record given an ID ("A-C71970F").

    (: emp.xquery :)
    
declare variable $id as xs:string external;
<root>
{
    for $employee in collection("pubs.dbo.employee")/employee
    where $employee/emp_id = $id
    return $employee
}
</root>

 

Using REST

Using REST, this XQuery can be executed from any Internet browser using just this URL:

http://localhost/XQueryWebService/emp.xquery?id=A-C71970F 

Notice that the employee ID ("id=A-C71970F") is visible in the URL.

The result would look something like this:

<dd:Output xmlns:dd="http://www.datadirect.com">
    <root>
        <employee>
            <emp_id>A-C71970F</emp_id>
            <fname>Aria</fname>
            <minit/>
            <lname>Cruz</lname>
            <job_id>10</job_id>
            <job_lvl>87</job_lvl>
            <pub_id>1389</pub_id>
            <hire_date>1991-10-26T00:00:00</hire_date>
        </employee>
    </root>
</dd:Output>

 

Using SOAP

Using SOAP, on the other hand, requires submitting the following SOAP request (XML):

<?xml version="1.0"?>
<SOAP-ENV:Envelope
    <SOAP-ENV:Body>
        <dd:emp xmlns:dd="http://www.datadirect.com">
            <dd:id>A-C71970F</dd:id>
        </dd:emp>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

The result, shown here, is pretty much the same as the one returned using REST, only now it is "wrapped" in the SOAP envelope:

<SOAP-ENV:Envelope xmlns:SOAP-ENV=
    <SOAP-ENV:Body>
        <dd:Output xmlns:dd="http://www.datadirect.com">
            <root>
                <employee>
                    <emp_id>A-C71970F</emp_id>
                    <fname>Aria</fname>
                    <minit>
                    </minit>
                    <lname>Cruz</lname>
                    <job_id>10</job_id>
                    <job_lvl>87</job_lvl>
                    <pub_id>1389</pub_id>
                    <hire_date>
                    1991-10-26T00:00:00
                    </hire_date>
                </employee>
            </root>
        </dd:Output>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

 

Next Steps

The XQueryWebService framework includes some simple tools that let you test the Web service operations you include in your applications.

Tools for Testing Web Service Operations

The XQueryWebService framework dynamically lists all the operations exposed by the Web service created from the XQuery in your Java servlet container's XQuery folder. This page, for example, was generated by the Employee Lookup example:

To display this page, just point your browser to the XQueryWebService root —http://examples.xquery.com/employee-lookup/, for example.

The HTML Test Interface

A simple HTML interface allows you to test an operation. For example, if we click the "emp" operation, the following HTML page is generated:

To test the operation, simply provide the requested value and click the "Invoke" button. Again, the testing interface is generated dynamically, so the form itself varies based on the operation — if an operation does not require a parameter, it is invoked as soon as you select it. This functionality is supported by the REST technology only.

Next Steps

If you choose to use the SOAP transport mechanism in your Web application, you might want to take advantage of the XQueryWebService framework's ability to generate a WSDL document. (A WSDL document can be used to create a set of classes that allow you to manipulate a data service as if it was a local library.) Learn about generating a WSDL document in the next section.

Generating WSDL Documents

XQueryWebService also automatically generates a Web Service Definition Language (WSDL) document based on the XQuery in your Java servlet container's XQuery folder. The WSDL document describes the services that are exposed by a given XQuery, and this can be useful if you plan to provide programmatic access to one or more of those services.

If we take a closer look, we see that the WSDL document defines a single service (<wsdl:service>), exposed through two ports: SOAP and HTTPGET.

Each query is exposed as a WSDL operation (<wsdl:operation>), with each query's external variables exposed as operation parameters. Further, all built-in schema types are preserved in the parameter declaration.

Consider the following external variable declared in our example XQuery:

declare variable $id as xs:string external;

In the <wsdl:types> section of the generated WSDL document, you find the following global element:

<xs:element name="emp">
    <xs:complexType>
        <xs:all>
            <xs:element name="id" type="xs:string"/>
        </xs:all>
    </xs:complexType>
</xs:element>

The input message references the global element, "emp":

<wsdl:message name="empInputMsg">
    <wsdl:part element="dd:emp" name="parameters"/>
</wsdl:message>

Currently, XQueryWebService does not support user-defined types for external variables.

Next Steps

You can use a WSDL document to create a set of classes that can be used to manipulate the data service as if it was a local library. Learn more about using WSDL service references in the next section.

Using WSDL Service References

Modern IDEs like Microsoft Visual Studio and Eclipse provide complete support for consuming Web services — for most of them, making the WSDL document available to the IDE is all that's needed to generate a set of classes that can be used to manipulate the data service as if it was a local library. For example, when we open the Employee Lookup WSDL in Microsoft Visual Studio as a Service Reference, the emp and empxsd operations are exposed, as shown in the following illustration:

Such a binding framework works extremely well when the WSDL document makes use of XML Schema to describe the SOAP message payloads. Consider creating a simple C# application using empxsd, as shown here:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TestWS.ServiceReference1;
namespace TestWS
{
    class Program
    {
        static void Main(string[] args)
        {
            SOAPPortClient client = new SOAPPortClient();
            rootEmployee ret = client.empxsd("A-C71970F");
            Console.WriteLine(ret.lname);
        }
    }
}
As seen in the following illustration, schema information about the employee element — employee ID, first and last name, hire date, and so on — is exposed to the IDE, simplifying and enriching the application development process.

If we run our application in debug mode inside Microsoft Visual Studio, we can see that the variables (first name, last name, and so on) are initialized with values from the Web service.


XML Schema for a WSDL Document

To illustrate how XML Schema can be used to augment a data service WSDL, let's revisit the Employee Lookup XQuery (emp.xquery), and make a few modifications, as shown:

declare variable $id as xs:string external;
<ns:root xmlns:ns="http://www.employee.com">
{
    for $employee in collection("pubs.dbo.employee")/employee
    where $employee/emp_id = $id
    return $employee
}
</ns:root>

This query is almost identical to the one introduced earlier in this tutorial, except that the root element (<ns:root> ) is now placed in a different namespace. Accordingly, we need to create an XML Schema, let's call it employee.xsd, that describes what the <ns:root> element looks like. We'll also put this XML Schema in the same folder as emp.xquery and empxsd.xquery (c:\MyQueryDir):

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
targetNamespace="http://www.employee.com"
elementFormDefault="qualified">
<xs:element name="root">
    <xs:complexType>
        
        <xs:sequence>
            <xs:element name="employee" form="unqualified">
                
                <xs:complexType>
                    <xs:sequence>
<xs:element name="emp_id" form="unqualified" type="xs:NCName"/>
<xs:element name="fname" form="unqualified" type="xs:NCName"/>
<xs:element name="minit" form="unqualified"/>
<xs:element name="lname" form="unqualified" type="xs:NCName"/>
<xs:element name="job_id" form="unqualified" type="xs:integer"/>
<xs:element name="job_lvl" form="unqualified" type="xs:integer"/>
<xs:element name="pub_id" form="unqualified" type="xs:integer"/>
<xs:element name="hire_date" form="unqualified" type="xs:NMTOKEN"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
    
    </xs:complexType>
</xs:element>
</xs:schema>

Now that this is done, we can place both the query file and the XML Schema file in the Java servlet container folder that contains our XQuery — in our example, this is c:\MyQueryDir.

If we now open our WSDL URL, we can see that the embedded XML Schema contains an import statement referencing the XML Schema associated with our WSDL:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.datadirect.com"
attributeFormDefault="unqualified"
elementFormDefault="qualified">
    <xs:import schemaLocation="employee.xsd"
        namespace="http://www.employee.com"/>
    <xs:element name="empxsd">
        <xs:complexType>
            <xs:all>
             <xs:element name="id" type="xs:string"/>
            </xs:all>
        </xs:complexType>
    </xs:element>
<xs:element name="Output" type="xs:anyType"/>
</xs:schema>

Now, the SOAP message that describes the operation's return types references the global element <ns:root> defined in the WSDL XML Schema:

<wsdl:message name="empxsdOutputMsg">
    <wsdl:part element="ns:root" name="parameters"/>
</wsdl:message>
What's Next?

Now that you know about the DataDirect XQuery XQueryWebService framework, take a look at how it was used to build an application that searches Flickr photos.

Photo Search Demonstration Overview

The Photo Search demonstration shows you how the XQueryWebService framework can be used with DataDirect XQuery to build a photo database search tool that allows you to query a database, create new user accounts, and perform other administrative tasks.

Note: The Photo Search demonstration uses the Flickr API but is not endorsed or certified by Flickr.

Getting Started

The Photo Search demonstration lets you create a user account and search Flickr for photographs using any keywords that you specify. As shown in the following illustration, to get started all you need to do is login — you can use any value you like for a user name. DataDirect does not record or share any of the information you provide (user name, search terms, and so on).

If you look closely at the URL, you'll see that the file extension is, perhaps, not what you might expect — it's .xquery. The HTML for the login page, as well as for all the other pages in this application, was generated using XQuery. (See for yourself — clicking the View Source button, which you can do from any page of the application, displays static HTML of the XQuery source responsible for generating the page of the Photo Search application you're looking at. You'll learn more about View Source and other tool bar functions in a later topic.)

Once you've logged in, the Photo Search application displays the search page. On this page, you can

  • Specify a search term (the default is Boston downtown)
  • Specify the number of results you want returned (the default is 10)
  • Display all previously run queries
  • Delete all previously run queries

When you click the Search button, the Flickr photos are searched using the keywords you specified; after a moment, thumbnails of the photos matching your search criteria are displayed. If we use the search term XQuery, for example, we find several photos of XQuery conferences and XQuery experts such as Michael Kay and Daniela Florescu.


After you've run a few search terms through the Photo Search demonstration, click the Show me my queries button. In a moment, the application displays a list, sorted chronologically, of all the queries you have run.


You can rerun a search by simply clicking the term in the list. Or, if you want to start fresh, you can click Delete my queries to delete all query history.

What's Next?

The DataDirect XQuery Photo Search demonstration application uses numerous technologies — there's XQuery and Web services, of course, but there's also the REST transport mechanism, HTML, and SQL, among others. A high-level view of the application architecture is shown on the following page.

Application Architecture

The DataDirect XQuery Photo Search demonstration application uses a number of technologies — there's XQuery and Web services, of course, but there's also the REST transport mechanism, HTML, and SQL, among others. A high-level view of the application architecture is shown here:

What's Next?

In the following sections, we'll take a closer look at the functional components that make up the Photo Search demonstration application.

Using the Development Toolbar

The Photo Search demonstration is a pretty nifty application. Of course, its real purpose is to show you how the XQueryWebService framework can be used to update a database, search Flickr photos, and generate dynamic HTML. In this section, we'll introduce you to the toolbar, which provides easy-to-use tools that can help you learn about the power of DataDirect XQuery and Web services.

Looking Under the Covers

Every screen of every XQueryWebService demonstration application includes the following toolbar:

View Source

As you might imagine, you use the View Source button to view the XQuery source code that generated the HTML page. Clicking this button displays static HTML of the XQuery code responsible for generating the HTML of the current page, as shown in the following illustration.

Get Web Services

The Get Web Services button displays a list of all the Web service operations exposed by the application. The following illustration shows the list of the Web service operations exposed by the Photo Search demonstration:

Clicking an operation in the list displays a simple interface that allows you to test the operation individually.

Learn More

Finally, the Learn More button opens a Web browser to the start of these pages.

Try It Now

Click here to start the DataDirect XQuery Photo Search demonstration. Take a few moments to familiarize yourself with the application before continuing with the next section in this discussion.

What's Next?

In the remaining sections, we'll look at the three main functions supported by the Photo Search demonstration application — logging in, searching Flickr photos, and displaying saved queries — and see how DataDirect XQuery and the XQueryWebService framework is used in all of them. We'll start at the beginning, with logging in.

Updating Relational Data

Once you start the Photo Search demonstration, you need to log in. This section describes the XQuery code that's used both to generate the HTML for the Photo Search login page and to perform login functions.

The Log In Page

As shown in the following illustration, the Log In page is generated by two XQuery files — login.xquery and toolbar.xquery. The login.xquery file, which you can see in the URL displayed in the browser's Address field, is the file directly responsible for generating the Log In page.

Note, though, that the DataDirect XQuery logo and the toolbar come from the toolbar module, which is imported from toolbar.xquery. (The XQuery code from login.xquery that calls toolbar.xquery is highlighted in the preceding illustration.) Because the toolbar is a modularized component of the Photo Search application, you'll see this import statement at the start of the XQuery for most of the pages in the application. Values for the toolbar functions (View Source, Get Web Services, Learn More) change depending on the specific XQuery that is importing the XML fragments from toolbar.xquery, as you can see in the following code:

declare option ddtek:serialize "method=html";
declare variable $toolbar :=
        toolbar:function("login.html", "/photo-search",
        web-service-example/photo-search/index.html");
Creating a New User

So far, what we've seen is pretty straightforward usage of XQuery. Let's take a closer look at the login functionality defined in the HTML inside login.xquery, where we first see how DataDirect XQuery can be used to update a relational database.

<html>
<head>
    {$toolbar/head/link}
    <title>Photo Search powered by DataDirect XQuery</title>
</head>
<script type="text/javascript" src="ajax.js"/>
<script type="text/javascript">
function checkLogin(){{
    login = this.document.getElementById("login").value;
    if(login != ""){{
        ajax_insertUser(login);
    this.window.open("main.xquery?login=" + login, "_self");
    }}
}}
</script>
...

As you can see, the second <script type="text/javascript"> element defines an inline function, function checkLogin(). If the User Name field is not empty, the ajax_insertUser() function defined in the ajax.js is called, passing the login value as the parameter.

The ajax.js Javascript

The Javascript file ajax.js is just a library of Javascript functions, like ajax_insertUser():

function ajax_insertUser(login)
{
return ajax_xmlhttp
    ("POST", "insertUser.xquery?login=" + login, false, null);
}

The ajax_insertUser() function concatenates the URL with insertUser.xquery?login= with the login value provided by the user — so, we have http://examples.xquery.com/photo-search/insertUser.xquery?login=ohenry, for example. Next, let's look at insertUser.xquery to see what it does with the login value.

The insertUser.xquery Query

The insertUser.xquery is, as the name suggests, responsible for adding new users to the database. This is accomplished using the proprietary DataDirect XQuery ddtek:sql-insert function to insert a single record into a database table.

declare variable $login as xs:string external;
if(not(exists(collection("users")/users[login = $login]))) then
    ddtek:sql-insert("users", "login", $login)
else ()

Here, the ddtek:sql-insert function is being used to insert the user name value ($login) into the "login" column of the "users" table.

Once the user name is added to the database, the ajax_insertUser() function opens a new URL for main.xquery, again creating the URL by concatenating the query name with the user name (login) value — http://examples.xquery.com/photo-search/main.xquery?login=ohenry, for example. The URL is opened in the same browser ("_self"):

...
<script type="text/javascript">
function checkLogin(){{
    login = this.document.getElementById("login").value;
    if(login != ""){{
        ajax_insertUser(login);
    this.window.open("main.xquery?login=" + login, "_self");
    }}
}}
</script>
...

It is main.xquery that provides the search and query-listing capabilities of the Photo Search demonstration application.

What's Next?

Now that you have seen how you can use XQuery to update a relational database, let's take a look at the heart of the Photo Search application — XQueryWebService framework was used to implement this search functionality.

Searching Flickr Photos

Once you have logged in to the Photo Search demonstration, the Web server displays a page created by main.xquery. This section describes how DataDirect XQuery uses the Flickr Web service API to search for photos.

The Search Page

The Search page lets you

  • Search for photos using a keyword
  • Display a list of previously run searches
  • Execute a previously run search
  • Delete all previously run searches

This section focuses on the search function. Executing a previously run search is discussed in the following section.

The Keywords and Max hits fields have default values, but you can change them to whatever you like. For the purposes of this discussion, we'll use XQuery as the keyword, and 8 for the number of hits we want returned.

What Happens When You Click Search?

The Search button is part of a typical HTML form:

...
<input type="submit" id="button_search" value="Search"/>
...

where the onsubmit="Search()" script event that invokes the "Search()=" function, which in turn calls SearchInternal. Let's take a closer look at SearchInternal to see what it does.

The SearchInternal() Function in main.xquery

The SearchInternal() function in main.xquery is defined as follows:

function SearchInternal(login, keywords, maxHits){{
    EnableButtons(false);
    keywords = keywords.replace(/"/g, '');
    keywords = keywords.replace(/>/g, '');
    keywords = keywords.replace(/</g, '');
    keywords = keywords.replace(/&/g, '');
    document.getElementById("edit_keywords").value = keywords;
    document.getElementById("edit_maxHits").value = maxHits;
    document.getElementById("divResult").innerHTML =
     '<img src="throbber.gif"/>';
    xmlhttp = ajax_getphotos(login, keywords, maxHits, true,
    onreadystatechange);
}}

After disabling the SearchShow me my queries, and Delete my queries buttons, the ajax_getphotos() function defined in ajax.js is called. Among other parameters, it passes the keywords and maxHits parameters.

function ajax_getphotos(login, keywords, maxHints, asynch,
    onreadystatechange)
{
    url = "getphotos.xquery?login=" + login + "&keywords="
    + keywords + "&maxHits=" + maxHints;
    return ajax_xmlhttp("POST", url, asynch, onreadystatechange);
}

These parameters are used to build the URL for getphotos.xquery.

The getphotos.xquery Query

The getphotos.xquery is where we first see a reference to the the Flickr API (http://api.flickr.com/services/rest/):

import module namespace flickr = "http://api.flickr.com/services/rest/"
    at "flickr.xquery";
declare option ddtek:serialize "method=html";
declare variable $login as xs:string external;
declare variable $keywords as xs:string external;
declare variable $maxHits as xs:integer external;
for $photoid in flickr:flickr_search($keywords, 1, $maxHits,
"original_format, geo")//photo/@id
return
let $url := flickr:photoURL($photoid, "small")
return
<a href="{flickr:photoURL($photoid, 'medium')}" target="NewWindow">
    <img src="{$url}"/>
</a>

It is in the XQuery module flickr.xquery that the flickr:flickr_search() and flickr:photoURL() functions called by getphotos.xquery are defined. Both of these functions rely on the Flickr API, which is exposed as a set of Web services. Let's take a closer look at flickr.xquery now.

The flickr.xquery XQuery Module

The flickr.xquery XQuery module defines numerous functions that serve as wrappers for these Flickr Web services. These functions invoke the Flickr Web services using the doc() function. The DataDirect XQuery ddtek:wscall() function is another way to invoke Web services, especially those implemented using the SOAP protocol.

The flickr:flickr_search() function builds the URL of the server hosting the Flickr photo search Web services; the search itself is executed using the values for the $keywords and $maxhits parameters passed from getphotos.xquery.

...
declare function flickr:flickr_search(
    $keyword as xs:string, $page as xs:integer,
    $per_page as xs:integer, $extras (:license, date_upload,
    date_taken, owner_name, icon_server, original_format,
    last_update, geo, tags, machine_tags:))
{
    let $url := concat(
        $flickr:flickr_base_url,
        "?method=flickr.photos.search",
        $flickr:flickr_api_key,
        concat("&page=", $page),
        concat("&per_page=", $per_page),
        concat("&tags=", $keyword)
    )
doc($url)
};
...

Once the search is complete, the flickr:photoURL() function returns a URL for each photo matching the search terms. An example of this URL is commented in the example of the flickr:photoURL() function shown here:

...
declare function flickr:photoURL($id as xs:string, $size as xs:string)
{
(:
http://farm{farm-id}.static.flickr.com/
{server-id}/{id}_{secret}_[mstb].jpg
:)
let $postfix_size :=
if( $size = "small") then '_s'
else if( $size = "thumbnail") then '_t'
else if( $size = "medium") then '_m'
else if( $size = "large") then '_b'
else if( $size = "original") then '_o'
else ()
return
let $info := flickr:flickr_getInfo($id)/rsp/photo
return
    concat("http://farm",$info/@farm,
        ".static.flickr.com/",
        $info/@server,"/",
        $info/@id,"_",$info/@secret, $postfix_size, ".jpg")
};
...
Changes to the Search Page

If we look again at getphotos.xquery, we see that it returns a thumbnail version of the photo ("small"). This image, along with any others that match the search term (up to the Max hits defined by the user), are displayed in a table inserted by main.xquery when it revises the original search page.

When the user clicks a thumbnail, a new browser instance is launched to display a larger (medium-sized) rendering of the image:

...
for $photoid in flickr:flickr_search($keywords, 1,
        $maxHits, "original_format, geo")//photo/@id
return
let $url := flickr:photoURL($photoid, "small")
return
<a href="{flickr:photoURL($photoid, 'medium')}" target="NewWindow">
<img src="{$url}"/>
</a>
...

In addition to the photo thumbnails that appear on the search page, the SearchShow me my queries, and Delete my queries buttons are again enabled. We'll take a look at how Show me my queries works next.

What's Next?

We've shown you how DataDirect XQuery can be used to invoke a Web service. We'll conclude this discussion of the Photo Search demonstration application with a look at how photo search queries can be retrieved from a relational database and run again.

Querying Relational Data

We've already shown how you can use DataDirect XQuery to update a relational database. This section describes using DataDirect XQuery and the collection() function to retrieve data from a relational database — in this case, the photo search queries that are saved by the Photo Search demonstration.

Displaying Saved Queries

Each time you execute a search of Flickr photos, the query — which includes your user name, the search term, the maximum number of hits, and some other information — is saved to the queries table in the database running on the Web application server. When you click the Show me my queries button, the Photo Search application queries the database for your queries and displays the results, as shown in the following illustration:

Clicking the Show me my queries button invokes the ajax_savedqueries function, which in turn calls savedqueries.xquery. Let's take a look at that XQuery code now.

savedqueries.xquery

As you can see, the savedqueries.xquery uses the collection() function to query the Photo Search database for saved queries:

declare option ddtek:serialize "method=html";
declare variable $login as xs:string external;
<table id="{$login}">
{
for $user in collection("users")/users
where string($user/login) = string($login)
return
    for $query in collection("queries")/queries
    where $query/user_id = $user/id
    return
        <tr>
        <td>
            <a href="null.html" target="block"
            onclick="SearchInternal('{$login}',
            '{$query/keywords}', '{$query/maxHits}')">
            {$query/keywords} (max hits {$query/maxHits})
            </a>
        </td>
        </tr>
}
</table>,
    <iframe name="block" style="display:none"/>

Notice that savedqueries.xquery contains only XQuery — the SQL statements required to connect to and query the relational database are created automatically by DataDirect XQuery. Once a user in the users table whose user_id matches the $login, the record containing the query information (the key words used for the search and the max hits specified) is returned and presented in a table, with each record appearing in its own row.

Architecture Overview and Code Samples

The XQueryWebService framework allows you to expose an XQuery as a Web service. Implemented as a library for Java classes that supports numerous Java servlet containers like Apache Tomcat, JBoss, IBM WebSphere, and BEA WebLogic, the XQueryWebService framework simplifies the design and implementation of Web Servlet Applications.

Using the Flight Reservation use case from the W3C SOAP specification, this example illustrates how you can use XQuery to query an incoming request as XML, and query relational databases or XML configuration files that are available to the implementation running on the middle tier, producing the XML needed for a response

Let's take a look at the XQueryWebService framework architecture before getting into more of the details.

XQueryWebServiceFramework Architecture Overview

A high-level illustration of the XQueryWebService framework architecture shows all the pieces at work to expose an XQuery as a Web service:


client.xquery

The Travel Reservation XQuery Web service application starts with a user executing client.xquery, whose purpose is to submit travel itinerary information to the Web service. The client.xquery code looks like this:

declare namespace dd = "http://www.datadirect.com";
ddtek:wscall(
<ddtek:location
soapaction="flights.xquery"/>,
<dd:flights xmlns:dd="http://www.datadirect.com">
<dd:in>
<p:itinerary      
<p:departure>
<p:departing>New York</p:departing>
<p:arriving>Los Angeles</p:arriving>
<p:departureDate>2009-12-14</p:departureDate>
<p:departureTime>late afternoon</p:departureTime>
<p:seatPreference>aisle</p:seatPreference>
</p:departure>
<p:return>
<p:departing>Los Angeles</p:departing>
<p:arriving>New York</p:arriving>
<p:departureDate>2009-12-20</p:departureDate>
<p:departureTime>mid-morning</p:departureTime>
<p:seatPreference/>
</p:return>
</p:itinerary>
</dd:in>
</dd:flights>
)

The itinerary element, <dd:in>, which contains the information that is consumed by the Travel Reservation XQuery Web service application to return a list of airports that provide flights that satisfy the requirements expressed by the itinerary.

SOAP Request

Using the <%=ConfigurationManager.AppSettings["DDXQ"]%> Web service call function (ddtek:wscall),client.xquery invokes the Travel Reservation XQuery Web service using soapaction="flights.xquery". The soapaction="flights.xquery" is defined in the Web Service Description Language (WSDL) that describes the operations of the Travel Reservation XQuery Web service application.

<wsdl:binding type="dd:SOAPPort" name="SOAPBinding">
<wsdlsoap:binding style="document"
            transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="flights">
<wsdlsoap:operation style="document" soapAction="flights.xquery"/>
<wsdl:input>
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<wsdlsoap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="nmtoken"/>
</wsdl:operation>
</wsdl:binding>

 

flights.xquery

On the server, the Travel Reservation XQuery Web service application runs flights.xquery. This XQuery code defines a function that looks up the airports for a given city in a relational database that is available to the Web service implementation and then looks for all the departing and returning airports in an incoming message.

declare namespace env = "http://www.w3.org/2003/05/soap-envelope";
declare variable $in as document-node(element(*, xs:untyped)) external;
declare function local:airportChoices($city as xs:string)
{
let $airports := collection("airportcodes")
/airportcodes[CityName = $city]
return
if (count($airports) = 0)
then
<error> No airports found for {$city}!</error>
else if (count($airports) > 1)
then
<airportChoices>{ string-join($airports/AirportCode, " ")}
</airportChoices>
else ()
};

... If there is more than one airport for a city, or no airport, we return a message which requests clarification ...

let $itinerary := $in//p:itinerary
let $departureDeparting :=
$itinerary/p:departure/p:departing
let $departureDepartingAirports :=
collection("airportcodes")/airportcodes[CityName = $departureDeparting]
let $departureArriving :=
$itinerary/p:departure/p:arriving
let $departureArrivingAirports :=
collection("airportcodes")/airportcodes[CityName = $departureArriving]
let $returnDeparting :=
$itinerary/p:return/p:departing
let $returnDepartingAirports :=
collection("airportcodes")/airportcodes[CityName = $returnDeparting]
let $returnArriving :=
$itinerary/p:return/p:arriving
let $returnArrivingAirports :=
collection("airportcodes")/airportcodes[CityName = $returnArriving]
return
if (count($departureDepartingAirports)=0 or count($departureDepartingAirports)>1
or count($departureArrivingAirports)=0 or count($departureArrivingAirports)>1
or count($returnDepartingAirports)=0 or count($returnDepartingAirports)>1
or count($returnArrivingAirports)=0 or count($returnArrivingAirports)>1 )
then
p:itineraryClarification>
{ local:airportChoices($departureDeparting) }
{ local:airportChoices($departureArriving) }
{ local:airportChoices($returnDeparting) }
{ local:airportChoices($returnArriving) }
/p:itineraryClarification>

... Otherwise, departing and arriving airports are provided in pairs for the departure and return legs of the trip ...

else
<p:itinerary>
<p:departure>
<p:departing>{$departureDeparting}</p:departing>
<p:arriving>{$departureArriving}</p:arriving>
</p:departure>
<p:return>
<p:departing>{$returnDeparting}</p:departing>
<p:arriving>{$returnArriving}</p:arriving>
</p:return>
</p:itinerary>
Learn More

See other examples of XQuery Web service applications built using the DataDirect XQueryWebService framework.

An XQuery Servlet for Restful Data Services

Many web applications exchange data as XML, but that data is usually stored in and queried from relational databases, CRM, ERP, proprietary repositories, and a hodgepodge of other systems. Unfortunately, the languages most commonly used for creating or processing data on the web were designed neither for processing XML nor for integrating data among multiple heterogeneous sources. These are precisely the tasks for which the XQuery language was designed.

This paper shows how to use XQuery for data integration, and how to expose an XQuery as a RESTful data service using a Java servlet. Listing 1 contains the source code for the servlet. This servlet uses the name and external variables of any XQuery to provide a REST interface to the query and deploys the query.

As an XML-oriented data integration language, XQuery can be used to access XML, relational, and flat file formats such as EDI to create complex XML and HTML results. To deploy a query, a developer saves the query into a designated deployment directory in a secure location accessible to the servlet. Subsequently, developers can invoke any query in this directory using its REST interface, which requires nothing more than an HTTP GET or POST operation using a URL that represents the query and its parameters.

Click here to read the full article: An XQuery Servlet for RESTful Data Services

Connect any application to any data source anywhere

Explore all DataDirect Connectors

Need additional help with your product?

Get Customer Support