Using The MarkLogic XQuery JSP Tag Library

Ron Hitchens
Last updated July 18, 2004

Note:JSP Tag library was built and this tutorial was authored in 2004 before support for XCC.

This tutorial will show you how to use the MarkLogic Java Server Pages (JSP) Tag Library to access MarkLogic from within a J2EE web application. The MarkLogic tag library will work with any JSP container such as Tomcat or Jetty, as well as EJB application servers like WebLogic, WebSphere or JBoss.

The following assumes that you are familiar with Java, Java Servlets and JSP, and that you understand how to build and deploy J2EE web applications.

Introduction

While MarkLogic Server is unique in many ways, the role it plays in the IT ecosystem is typically that of datasource, a generic name for a system component that stores and retrieves data.

MarkLogic Server includes a builtin HTTP server which is sufficient to execute XQuery scripts and output HTML directly to the user's browser. But there may be cases where it's preferable to access content from within a web application that's running in a J2EE container. Reasons for this could include combining the result with data from other datasources, further processing the result to apply business rules written in Java, generating XQuery scripts dynamically at runtime from session-specific information, etc.

MarkLogic Server comes bundled with a Java connectivity package called XDBC that is modeled after the standard JDBC package. It provides a connection-oriented mechanism by which Java code can issue queries to a server and receive the results over the network.

The JSP Tag Library is built on top of XDBC and simplifies J2EE integration by interfacing to a MarkLogic Server using only standard JSP tag syntax.. It's open source and available free of charge from the developer network at http://developer.marklogic.com. The code is made available under the Apache 2.0 license.

Custom JSP Tag Libraries

The MarkLogic JSP tags are modeled after the Java Standard Tag Library (JSTL) both to respect the prevailing conventions and to make it easier to inter-operate with the JSTL tags. The tags in the SQL component of the JSTL were the direct inspiration for the Mark Logic tags. They are similar in design philosophy but vary in significant ways.

To get started, let's look at how custom JSP tags are hooked into a web application and then we'll look at some usage examples.

A custom JSP tag library consists of a set of Java classes, usually packaged into a Java Archive (JAR) file, and a descriptor file that tells the JSP container how those classes map to individual tags in a JSP page. The Java classes are written against the appropriate J2EE tag APIs and are the code that is invoked when those custom tags are processed by the JSP container.

The container maps tags encountered in a JSP to specific classes by means of a Tag Library Definition (TLD) file. The TLD contains information about the tag syntax, such as which attributes are allowed, and the names of the implementing Java classes.

The TLD for a custom tag library (there may be multiple tag libraries in use at once) is named in a web application's web.xml file which is the overall descriptor for a web application.

Working backwards now, you need to add a clause like this to your project's web.xml, so the container can find the TLD:

<taglib>
    <taglib-uri>http://marklogic.com/jsp/taglib</taglib-uri>
    <taglib-location>/WEB-INF/taglib/marklogicxquery.tld</taglib-location>
</taglib>

This defines a URI which identifies the TLD to the container when that URI is referenced in a JSP. This URI is arbitrary -- it need not be a real web address -- it's only used as a unique identifier string. The second item is the path, relative to the web application, of the actual TLD descriptor file.

Place the TLD (included in the MarkLogic JSP distribution) under the WEB-INF directory at the indicated path. It can go anywhere under the web application's root as long as it's where the taglib-location element declares it to be.

Next, make sure the JAR file with the implementation classes is in the WEB-INF/lib directory so that the container can find them at run time.

Finally, use the custom tags in a JSP. In order for a JSP to make use of a custom tag library, it needs to declare that wants to use it. This is done at the top of the JSP with a taglib declaration. Like this:

<%@ taglib uri="http://marklogic.com/jsp/taglib" prefix="xq" %>

The uri attribute must match the name in the taglib definition in the web.xml file. The prefix can be anything you like, it defines the XML name space that will identify this tag library within the scope of the JSP page. The xq prefix will be used throughout this tutorial, but you can pick something else if you prefer.

Here is a complete JSP example:

<%@ taglib uri="http://marklogic.com/jsp/taglib" prefix="xq" %>

<xq:setDataSource host="localhost" port="8003"
   user="someuser" password="secret"/>

<html><head><title>xq:query example 1</title></head>
<body>
My favorite fruits:
<ul>
<xq:execute>
    <xq:query>
        for $i in ("apple", "pear", "orange", "guava")
          return <li>{ $i }</li>
    </xq:query>
</xq:execute>
</ul>
</body>
</html>

This JSP outputs an HTML page with a bullet list that looks like this:

My favorite fruits:
  • apple
  • pear
  • orange
  • guava

We'll use this example through the rest of this tutorial, showing how to achieve the same result using different approaches.

The XQ Tags

Let's take a look now at the specifics of the MarkLogic XQuery JSP tag library, which I'll refer to from here on as the XQ tags.

Because a network connection is used to communicate with the server, it's possible for arbitrarily large results to be returned. The tags in the XQ tag library operate in either streaming or buffered modes depending on the attributes specified in an xq:execute or xq:result tag (more on those shortly).

The result of an XQuery execution is a sequence of zero or more result items, each of which may be any of the types defined by the XQuery spec. When the result is streaming, the items must be accessed sequentially and are only available one at a time.

In buffered mode, the full result is read from the XDBC connection and stored in a named container variable. There is no further dependence on the connection, so buffered results may be retained in the container as long as needed and forwarded to other JSPs or to servlets as desired. The items in the result may also be accessed randomly and/or repeatedly.

Getting Connected

Let's first look at how to establish a connection to a MarkLogic Server instance. The XQ tags use the concept of a datasource from which connections are obtained. There are two XQ tags related to datasources: xq:setDataSource and xq:unSetDataSource.

The xq:setDataSource tag is given the information needed to establish a server connection and stores an opaque object that will be used later by the xq:execute tag. If the name of a container variable to set is not provided, an internal default is used. The scope is page by default. Some examples:

<xq:setDataSource host="localhost" port="8003" user="joe" password="hush"/>
<xq:setDataSource var="myconn" host="... />
<xq:setDataSource scope="session" host="... />
<xq:setDataSource dataSource="techpubsdb" scope="request"/>
<xq:setDataSource>
    <xq:host>localhost</xq:host>
    <xq:user configParameter="cisusername"/>
</xq:setDataSource>

The first example above is the typical way of setting up an XDBC datasource with hard-coded values, using the default variable name and scope to store the datasource object. The second example gives an explicit variable name to use while the third uses the default name but stores the datasource object in session scope.

The fourth example gives the name of a pre-existing datasource (a JNDI lookup key) that should be used rather than creating a new one. Using named datasources allows the connection information to be set externally rather than in the JSP body. The drawback is that a JNDI-compliant naming service must be available at runtime.

The xq:setDataSource can also accept nested tags to specify the connection information. Each nested tag has the same name as the corresponding attribute. These tags take two forms: parameter value as the body of the tag, or an attribute naming a web application parameter (defined in web.xml) whose value should be used. This is an alternate way of externalizing connection information without requiring a JNDI service.

<xq:unSetDataSource/>
<xq:unSetDataSource var="myconn" scope="session" />

The xq:unSetDataSource tag is used to clear a variable containing an XDBC datasource object. Standard tags in the JSTL can be used to do this, if you know the name of the variable. This tag is provided so that you can clear a datasource that was set to the default internal name (which is intentionally kept hidden).

If datasource objects are stored in page or request scope, it's usually not necessary to clear them, since the variable will soon fall out of scope anyway. But a longer-lived datasource may be holding a connection pool, so it's a good idea clear it when you're done if it's in session or application scope.

Naming datasources should only be necessary if there is a chance that more than one will be active at once. In most cases you can use the default internal name, which means you won't need to name a datasource on the xq:execute tag.

Executing Queries

Now that we know how to specify where the server is, let's look at how to talk to it. The xq:execute tag encapsulates sending a query to the server and receiving the result. Here are a couple of simple examples:

<xq:execute var="result">
    <xq:query>"Hello World"</xq:query>
</xq:execute>

<xq:execute>
    <xq:query>"Hello World"</xq:query>
</xq:execute>

The first example, where the var attribute is provided, is buffered. When the tag finishes execution, the variable result will contain a Java object of type Result (more on that later) which holds the result of the query. No output will be generated by the xq:execute tag, so it effectively disappears from the page.

The second example streams its output (replacing the xq:execute tag in the page with the result of the query) because no result variable name was given.

If you want to do more with the query result than dump it to the output, an xq:result tag may be nested within xq:execute (it must follow xq:query). But the xq:result tag may only be used when streaming. It is an error to nest an xq:result tag if the enclosing xq:execute tag also has a var attribute (which implies buffered mode).

Using the xq:result tag allows you to decorate the result data with other markup. It's a looping tag whose body is executed once for each item in the result. In order for the xq:result tag to know where the result data should go, you can use the xq:streamItem to emit a string representation of the current result item (there are other ways, which we'll look at later).

That's a lot of description. Here's an example to show how it works:

<%@ taglib uri="http://marklogic.com/jsp/taglib" prefix="xq" %>

<xq:setDataSource host="localhost" port="8003"/>

<html><head><title>xq:query example 2</title></head>
<body>
My favorite fruits:
<ul>
<xq:execute>
    <xq:query>
        ("apple", "pear", "orange", "guava")
    </xq:query>
    <xq:result>
       <li><xq:streamItem/></li>
    </xq:result>
</xq:execute>
</ul>
</body>
</html>

This is a revision of the fruity example above. But in this one, the HTML markup tags have been removed from the XQuery code so that it now returns a simple sequence of strings. Each result item in the sequence is nested in an <li> tag in the JSP rather than mixing them into the XQuery script and having the server generate them. The resulting page is identical, but the data and presentation markup have been decoupled.

Playing Well With Others

Both the xq:execute tag and the xq:result tag accept a var attribute. This attribute names a variable to be set as a part of query processing. The XQ tag library defines two Java interfaces that are used by these tags. They are Result (set by xq:execute) and ResultItem (set by xq:result). Both are in the com.marklogic.jsptaglib.xquery.common package:

public interface Result
{
    int getSize();
    ResultItem [] getItems();
    ResultItem getItem (int index);
}

public interface ResultItem
{
    int getIndex();
    boolean isNode();
    Object getObject();
    String getString();
    org.w3c.dom.Document getW3cDom() throws XDBCException;
    org.jdom.Document getJDom() throws XDBCException;
    Reader getReader();
}

Let's look at an example of using the var attribute with xq:execute. This is "fully buffered mode" where the entire query result is read and buffered in memory before control returns to the JSP and an instance of Result is stored in the named container variable:

<%@ taglib uri="http://marklogic.com/jsp/taglib" prefix="xq" %>
<%@ taglib uri="http://sun.com/jstl/c" prefix="c" %>

<xq:setDataSource host="localhost" port="8003"/>
<xq:execute var="result">
    <xq:query>
        ("apple", "pear", "orange", "guava")
    </xq:query>
</xq:execute>

<html><head><title>xq:query example 3</title></head>
<body>
My favorite fruits:
<ul>
    <c:forEach var="item" items="${result.items}">
        <li><c:out value="${item.string}"/></li>
    </c:forEach>
</ul>
</body>
</html>

This example makes use of the JSTL control tags to access the Result object buffering the query result. The execution of the query has also been moved to the top of the JSP so that it occurs before any other markup is evaluated. The result variable is accessed later by the c:forEach tag.

Using this processing model, the buffered result could as easily have been stored in a request scope variable and passed on to another JSP or a servlet for formatting. Decoupling query execution from presentation markup better facilitates Model-View-Controller architectures.

The ResultItem interface provides several useful accessor methods that can be called from Java code to retrieve the data in various ways. Those accessors also constitute bean properties that can be referenced by JSP 2.0 Expression Language constructs as in the example code above.

Semi-Buffered

When the xq:execute tag is buffered (var is specified) a nested xq:result tag may not be used. This is because the xq:result tag iterates over the each item in the result stream. When xq:execute is buffered, the items have already been read and stored in a Result object instance.

However, it's often useful to access result items through the ResultItem API from within the body of an xq:result tag. If you specify a var attribute on an xq:result tag, then on each iteration of the tag that variable will be set to a ResultItem instance that holds the content of the current result item. In this case each item in the result stream is buffered, one at a time, for the duration of that iteration, then discarded.

Here's the fruit example using the semi-buffered idiom:

<%@ taglib uri="http://marklogic.com/jsp/taglib" prefix="xq" %>
<%@ taglib uri="http://sun.com/jstl/c" prefix="c" %>

<xq:setDataSource host="localhost" port="8003"/>

<html><head><title>xq:query example 4</title></head>
<body>
My favorite fruits:
<ul>
<xq:execute>
    <xq:query>
        ("apple", "pear", "orange", "guava")
    </xq:query>
    <xq:result var="item">
        <li><c:out value="${item.string}"/></li>
    </xq:result>
</xq:execute>
</ul>
</body>
</html>

Here you can see that the body of the xq:result tag is identical to that of the c:forEach in the previous example. Both are looping tags and both use the ResultItem interface in the same way. But because the ResultItem is short-lived when semi-buffering, we've had to return to the pattern of doing query execution inline rather than before the main page evaluation..

Future Enhancements

The XQ tag library also includes a tag that we haven't talked about: xq:variable. This tag will be used to pass parameters as XQuery external variables. As of this writing MarkLogic Server does not yet support this capability but it is planned for a future release.

There will also be support in the xq:execute tag to name a server-resident module, similar to a stored procedure, rather than passing a query script from the client to the server.

As soon as these features become available in the server, the XQ tags will be updated to make use of them.

Summary

The preceding has been a whirlwind tour of the MarkLogic XQ JSP tags. We can't possibly cover all the possible ways that the XQ tag library can be put to use in the real world. See the documentation included in the XQ tag library code distribution for further information.

It's our hope that this library will make it easier for developers to integrate MarkLogic Server into J2EE applications. As time goes on, we hope to see this and similar projects evolve to better meet the needs of the community.

The example JSPs shown here are included in the XQ tags source distribution that's downloadable from the developer network . There are other JSP examples included in the distribution as well. Download the code and take it for a spin.

Please join the mailing list and let us know your experiences with the XQ tag library and how you're putting it to use.

Good Luck!

Comments