Skip navigation links
XCC/J 9.0

MarkLogic XML Content Connector For Java (XCC/J) Version 9.0

MarkLogic XML Content Connector for Java (XCC/J).

See: Description

Packages
Package Description
com.marklogic.xcc
XCC Core Public Interfaces.
com.marklogic.xcc.examples
XCC Examples.
com.marklogic.xcc.exceptions
XCC Exceptions.
com.marklogic.xcc.jndi
XCC Java Naming and Directory Interface (JNDI) Support.
com.marklogic.xcc.spi
XCC Service Provider Interface Package.
com.marklogic.xcc.types
XCC Type Definitions.

MarkLogic XML Content Connector for Java (XCC/J).

This is XCC, the MarkLogic XML Content Connector API. This interface varies significantly from the original MarkLogic connector solution, XDBC. One of the goals of the new API is to simplify the public interface to make it easier to understand and to use.

Basic Concepts

The XCC API has been designed so that client code will be easier to write, less error prone and more robust. With those goals in mind, the new public API intentionally doesn't use abstrations like "connection" or "statement". The new model is that of a session, or conversation, between client code and a contentbase.

The session metaphor is not only simpler and more natural, it makes it easier to hide implementation details. It also emphasizes that XCC is not like JDBC. Common JDBC usage patterns, such as pooling connections, are not appropriate with XCC.

Sessions

The central metaphor used in XCC is the Session interface. A Session object is the facade through which client code performs all interaction with a contentbase.

Session instances encapsulate the location of a particular server, a contentbase on that server and authentication credentials (user identity) for that contentbase. A Session also holds dynamic state, but it is a lightweight object. It is OK to create and release Session objects as needed. Do not expend effort to pool and reuse them, they are not expensive to create.

Content Sources

Session objects are created by factory methods on a ContentSource instance. A ContentSource object statically defines where a content server (an instance of MarkLogic Server) can be found.

At a minimum, a ContentSource object identifies a MarkLogic XDBC Server (host and port). It may also optionally hold default authentication credentials (user and password) and/or a contentbase (database) identifier on that server. There are several variations of the ContentSource.newSession() factory method which take arguments to provide this Session-specific information or to override the defaults.

JNDI Binding

Like J2EE DataSource objects, a ContentSource instance may be bound to a JNDI naming service. This allows the runtime environment (a J2EE container for example) to supply a pre-initialized ContentSource instance. This allows specification of the connection details at deployment time. Application code uses a JNDI lookup key to obtain a ContentSource instance. A typical JNDI lookup in a J2EE container would look like this:

private ContentSource findContentSource() throws ServletException
{
    try {
        Context initCtx = new InitialContext();
        Context envCtx = (Context) initCtx.lookup ("java:comp/env");

        return (ContentSource) envCtx.lookup("marklogic/ContentSource");
    } catch (NamingException e) {
        throw new ServletException ("ContentSource lookup failed: " + e, e);
    }
}

See ContentSourceBeanFactory for more details about integrating with JNDI.

Content Source Factory

If using JNDI to externalize ContentSource configuration is not appropriate, the helper class ContentSourceFactory may be used to instantiate ContentSource objects directly.

Several variations of the ContentSourceFactory.newContentSource(String, int) method are available:

ContentSource contentSource =
    ContentSourceFactory.newContentSource ("somehost", 1234);
ContentSource contentSource =
    ContentSourceFactory.newContentSource ("localhost", 1234, "fred", "secret");
ContentSource contentSource =
    ContentSourceFactory.newContentSource ("host", 5678, "tony", "hush", "pubsdb");

Of note is the signature which takes a URI argument (ContentSourceFactory.newContentSource(URI)). This factory method accepts a URI of the form:

xdbc://user:password@host:port/contentbase

Which encapsulates all the connection information into a single argument. The URL scheme "xdbc" means the connection will be made to an XDBC server port. The scheme "xcc" may also be used, it's an alias for "xdbc". Additional schemes may be added in the future, but these are the only two currently supported.

Passing a single String argument may be more convenient than passing four or five discreet values. Note that the factory method takes a URI object, not a String.

URI uri = new URI ("xdbc://joe:secret@devhost:8050/techpubsdb");
ContentSource contentSource = ContentSourceFactory.newContentSource (uri);

XCC Code Samples

Following are some sample code snippets that illustrate typical usage scenarios.

Simple Query Execution

This is an example of using Session.submitRequest(Request) to run an XQuery script and then iterating over the result. There is no longer an explicit connection object. The Session implementation manages connections automatically as needed. Also, when a Session object is closed, any currently open streaming ResultSequence objects created from that Session will automatically be closed. Any cached ResultSequences no longer have any relationship to the Session that created them and will not be affected.

Session session = contentSource.newSession ("mydatabase");
Request request = session.newAdhocQuery ("\"Hello World\"");
ResultSequence rs = session.submitRequest (request);

while (rs.hasNext()) {
    ResultItem rsItem = rs.next();
    XdmItem item = rsItem.getItem();

    if (item instanceof XSString) {
        String strValue = item.asString();
        System.out.println ("String: " + strValue);
    }

    if (item instanceof XSDateTime) {
        XSDateTime dateTime = (XSDateTime) item;
        Date date = dateTime.asDate();
        System.out.println ("DateTime: " + date);
    }

    ...

    // Each ResultItem instance will be a concrete Item class
}

session.close();

Invoking a Module On The Server

In addition to ad-hoc queries, Session.submitRequest(Request) can take a ModuleInvoke or ModuleSpawn object as the argument. Rather than literal code to be executed, these specify the URI of a server-resident module to be evaluated. In all other respects they are the same.

Session session = contentSource.newSession ("mydatabase");
Request request = session.newModuleInvoke ("mymodule.xqy");
ResultSequence rs = session.submitRequest (request);

while (rs.hasNext()) {
    ResultItem item = rs.next();

    ... (same as previous example)
}

session.close();

Passing Variables With Queries

Variables may be bound to Request objects. When an execution request is issued to the server with Session.submitRequest(Request) all the variables currently bound to the Request object are sent along and defined as external variables in the execution context in the server.

XCC lets you create XdmNodes and XdmSequences, as well as XdmAtomic values. However, in the initial XCC release values of this type may not be bound as external variables because MarkLogic Server cannot yet accept them. This capability is anticipated for a future release.

A XdmVariable object is a named XdmValue. XCC defines interfaces that model the XQuery data types. See the package com.marklogic.xcc.types (click the Description link) for UML diagrams and details of the type hierarchies.

The following is a (very) verbose example of creating and setting an external variable using a namespace:

Session session = contentSource.newSession ("mydatabase");
Request request = session.newAdhocQuery (
    "xquery version \"1.0-ml\";\n" +
    "declare namespace xyz=\"http://xyz.com/foo/bar\";\n" +
    "declare variable $xyz:myvar as xs:string external;\n" +
    "data ($xyz:myvar)");

// create an unnamed xs:string value
XdmValue value = ValueFactory.newXSString ("Some string value");

// create a new XName object referencing the above namespace
XName xname = new XName ("http://xyz.com/foo/bar", "myvar");

// Create a Variable (name + value) instance
XdmVariable myVariable = ValueFactory.newVariable (xname, value);

// bind the Variable to the Request
request.setVariable (myVariable);

// "$xyz:myvar as xs:string" will be defined at query run time
ResultSequence rs = session.submitRequest (request);

See the ValueFactory helper class for more details about creating XdmVariable and XdmValue instances.

The following is an example of creating and binding a variable in the default namespace.

Session session = contentSource.newSession ("mydatabase");
Request request = session.newAdhocQuery (
    "xquery version \"1.0-ml\";\n" +
    "declare variable $myvar as xs:string external;\n" +
    "data ($myvar)");

// create an unnamed xs:string value
XdmValue value = ValueFactory.newXSString ("Some string value");

// create a new XName object referencing the default namespace
XName name = new XName ("myvar");

// create a Variable and bind it to the Request
request.setVariable (ValueFactory.newVariable (name, value));

// "$myvar as xs:string" will be defined at query run time
ResultSequence rs = session.submitRequest (request);

A conveience method on the Session object will create and bind a XdmVariable instance in one call. The following example creates the same XdmVariable as above, binds it to the Request and returns the newly created XdmVariable instance. In this example, the return value is ignored.

Session session = contentSource.newSession ("mydatabase");
Request request = session.newAdhocQuery (
    "xquery version \"1.0-ml\";\n" +
    "declare variable $myvar as xs:string external;\n" +
    "data ($myvar)");

// create Variable "myvar", bind to Request, ignore return value
request.setNewVariable ("myvar", ValueType.XS_STRING, "Some string value");

// "$myvar as xs:string" will be defined at query run time
ResultSequence rs = session.submitRequest (request);

Inserting Content (Documents)

The model for inserting content differs in a couple of significant ways from XDBC. First of all, you no longer ask for an XDMPDocInsertStream and push your data into it. You now create Content objects which describe content (a document) to be inserted and XCC pulls the data from that source.

Secondly, because content uploads can be interrupted and XCC implements automatic recovery of uploads, instances of Content should be rewindable. See the ContentFactory class for acceptable sources of content.

This example inserts an XML document from a literal String, in the "MyNameSpace" namespace and requests that no automatic document repair be done. The ContentCreateOptions instance is created with a convenience method that is preset to select XML format.

ContentCreateOptions options = ContentCreateOptions.newXmlInstance();

options.setNamespace ("MyNameSpace");
options.setRepairLevel (DocumentRepairLevel.NONE);

Content content =
    ContentFactory.newContent ("someUri", "<root attr=\"foo\">blah</root>", options);

session.insertContent (content);

This example inserts the content of a file in text format. It uses a ContentCreateOptions object to set the namespace and quality.

File file = new File ("/some/file/path");
ContentCreateOptions options = ContentCreateOptions.newTextInstance();

options.setNamespace ("MyNameSpace");
options.setQuality (20);

Content content =
    ContentFactory.newContent ("someUri", file, options);

session.insertContent (content);

This example takes a W3C (Document) Object Model (DOM) instance (an object representation of an XML tree) and inserts it via the Session object. No ContentCreateOptions object is provided, so defaults are applied. Because, by definition, the DOM is a well-formed XML tree, the content format defaults to XML. If another format (text or binary) is selected by the options, the serialized String representation of the XML tree is inserted.

DocumentBuilder builder =
    DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse ("someDocUri");

Content content = ContentFactory.newContent ("someUri", dom, null);

session.insertContent (content);

If you're using JDOM, you can use one of the outputter classes in the org.jdom.output package to convert the JDOM document to a form that can be handled by a Session. For example, this code translates a JDOM tree to a W3C DOM and inserts it:

Document dom = new org.jdom.output.DOMOutputter().output (jdomDocument);
Content content = ContentFactory.newContent ("someUri", dom, null);

session.insertContent (content);

For large JDOM trees, it may be more efficient to serialize them as Strings for insertion.

Multi-Document Insert

Multiple documents can be atomically inserted (all or none are committed as a unit) by passing an array of Content objects to Session.insertContent(Content []).

Session session = contentSource.newSession ("mydatabase");
File file = new File ("/some/file/path.xml");
String stringDoc = "<snoop attr=\"bling\">dog</snoop>";
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse ("someDocUri");

Content [] docGroup = {
    ContentFactory.newContent ("uri1", file, null),
    ContentFactory.newContent ("uri2", stringDoc, null),
    ContentFactory.newContent ("uri3", dom, null)
};

session.insertContent (docGroup);
Skip navigation links
XCC/J 9.0

Copyright © 2017 MarkLogic Corporation. All Rights Reserved.
Complete online documentation for MarkLogic Server, XQuery and related components may be found at developer.marklogic.com