The Java Client API is an open source API for creating applications that use MarkLogic Server for document and search operations. This chapter includes the following sections:
The Java Client API provides the following capabilities:
When working with the Java API, you first create a manager for the type of document or operation you want to perform on the database (for instance, a JSONDocumentManager
to write and read JSON documents or a QueryManager
to search the database). To write or read the content for a database operation, you use standard Java APIs such as InputStream
, DOM, StAX, JAXB, and Transformer as well as Open Source APIs such as JDOM and Jackson.
The Java API provides a handle (a kind of adapter) as a uniform interface for content representation. As a result, you can use APIs as different as InputStream
and DOM to provide content for one read()
or write()
method. In addition, you can extend the Java API so you can use the existing read()
or write()
methods with new APIs that provide useful representations for your content.
This chapter covers a number of basic architecture aspects of the Java API, including fundamental structures such as database clients, managers, and handles used in almost every program you will write with it. Before starting to code, you need to understand these structures and the concepts behind them.
The MarkLogic Java Client API is built on top of the MarkLogic REST API. The REST API, in turn, is built using XQuery that is evaluated against an HTTP App Server. For this reason, you need a REST API instance on MarkLogic Server to use the Java API. A suitable REST API instance on port 8000 is pre-configured when you install MarkLogic Server. You can also create your own on another port. For details, see Choose a REST API Instance.
The Java API co-exists with the previously developed XCC API, as they are intended for different use cases.
You can use the Java Client API to quickly become productive in your existing Java environment, using the Java interfaces for search and document management. You can also use the Java Client API extension capability to invoke XQuery and Server-Side JavaScript code on MarkLogic Server. This enables you to take advantage of MarkLogic functionality not exposed directly through the Java Client API.
XCC provides a lower-level interface for running remote or ad hoc XQuery or Server-Side JavaScript. While XCC provides significant flexibility, it also has a somewhat steeper learning curve for developers. You can think of XCC as being to ODBC or JDBC: A low level API for sending query language directly to the server. By contrast, the Java Client API is a higher level API for working with database constructs in Java.
In terms of performance, the Java API is very similar to Java XCC for compatible queries. The Java API is a very thin wrapper over a REST API with negligible overhead.
For more information about XCC, see the XCC Developer's Guide.
To get started with the Java Client API, do the following:
For information about Java platform requirements, see the following page:
https://github.com/marklogic/java-client-api
The Java Client API also requires access to a MarkLogic Server installation configured with a REST Client API instance. When you install MarkLogic 8 or later, a pre-configured REST API instance is available on port 8000. For more details, see Administering REST Client API Instances in the REST Application Developer's Guide.
For information specific to rolling upgrades, see Java Client API in the Administrator's Guide.
You can make the Java Client API libraries available to your project in one of the following ways:
For more details, see the following page:
http://developer.marklogic.com/products/java
The Java Client API is an open-source project, so you can also access the sources and build your own library. For details, see Downloading the Library Source Code.
You can download a ZIP file from the following URL:
http://developer.marklogic.com/products/java
Download the ZIP file and uncompress it to a directory of your choice. The jar files you need to add to your class path are in the lib/
subdirectory.
To use the Maven repository, add the following to dependency to your Maven project POM file. (You may need to change the version
data to match the release you're using.)
<dependency> <groupId>com.marklogic</groupId> <artifactId>marklogic-client-api</artifactId> <version>4.0.3</version> </dependency>
You must also add the following to the repositories section of your pom.xml
.
<repository> <id>jcenter</id> <url>http://jcenter.bintray.com</url> </repository>
If you use Gradle as your build tool, you must use Gradle version 1.7 or later. Add the following to your build.gradle
file. Modify the version number as needed.
compile group: 'com.marklogic', name: 'marklogic-client-api', version: '4.0.3'
Add the following to your build.gradle
repositories section:
jcenter()
The Java API implementation interacts with MarkLogic Server using the MarkLogic REST Client API. Therefore you must have access to a REST API instance in MarkLogic Server before you can run an application that uses the Java Client API.
A REST API instance includes a specially configured HTTP App Server capable of handling REST Client API requests, a content database, and a modules database. MarkLogic Server comes with a suitable REST API instance attached to the Documents database, listening on port 8000.
The examples in this guide assume you're using the pre-configured REST API instance on port 8000 of localhost. If you want to create and use a different REST instance, see , see Administering REST Client API Instances in the REST Application Developer's Guide.
Each application must use a separate modules database and REST API instance.
You might need to create MarkLogic Server users with appropriate security roles, or give additional privileges to existing users.
Any user who reads data will need at least the rest-reader
role and any user that writes data will need at least the rest-writer
role.
REST instance configuration operations, such as setting instance properties require the rest-admin
role. For details, see REST Server Configuration.
Some operations require additional privileges. For example, a DatabaseClient
that connects to a database other than the default database associated with the REST instance must have the http://marklogic.com/xdmp/privileges/xdmp-eval-in
privilege. Using the ServerEvaluationCall
interface also requires special privileges; for details, see Evaluating an Ad-Hoc Query or Server-Side Module.
Note that MarkLogic Server Administration is not exposed in Java, so operations such as creating indices, creating users, creating databases, etc. must be done via the Admin Interface, REST Management API, or other MarkLogic Server administration tool. The server configuration component of the Java API is restricted to configuration operations on the REST instance.
For details, see Security Requirements in the REST Application Developer's Guide.
The Java Client API distribution includes several examples in the examples/
directory. The examples include the following packages:
com.marklogic.client.example.cookbook
: A collection of small examples of using the core features of the API, such as document operations and search. Most of the example code in this guide is drawn from the Cookbook examples.com.marklogic.client.example.handle
: Examples of using handles based on open source document models, such as JDOM or Jackson. Examples of handle extensions that read or write database documents in a new way.com.marklogic.client.example.extension
: A collection of extension classes and examples for manipulating documents in batches.For instructions on building and running the examples, see the project wiki on GitHub:
http://github.com/marklogic/java-client-api/wiki/Running-the-Examples
Your application must create at least one DatabaseClient
object before it can interact with MarkLogic using the Java Client API. The following topics cover key things you should know about the DatabaseClient
interface.
A DatabaseClient
object encapsulates the information needed to connect to MarkLogic, such as the host and port of a REST API instance, the database to operate on, and the authentication context. Internally, each DatabaseClient
object is associated with a connection pool, as described in Connection Management and Configuration.
Most tasks you perform using the Java Client API are handled by a manager object. For example, you use a QueryManager
to search the database and a DocumentManager
to read, update, and delete documents. You create manager objects using factory methods on DatabaseClient
, such as newQueryManager
and newDocumentManager
.
Best practice is to maintain a single, shared reference to a DatabaseClient
object for the lifetime of your application's interaction MarkLogic, rather than frequently creating and destroying client objects.
You need multiple DatabaseClient
objects if you need to connect to multiple databases or to connect to MarkLogic as multiple users. You must create a different DatabaseClient
instance for each combination of (host, port, database, authentication context). Again, it is best to keep these instances around throughout their potential useful lifetime, rather than repeatedly recreating them.
You can one DatabaseClient
object across multiple threads. After initial configuration, a DatabaseClient
object is thread safe.
Internally, the Java Client API maintains an OkHttpClient
connection pool that is shared by all DatabaseClient objects. The connection pool efficiently re-uses connections whether you use a single DatabaseClient
instance throughout the lifetime of your application or create and discard DatabaseClient
objects on demand.
Whenever a DatabaseClient
object makes a request to MarkLogic, an available connection is drawn from the connection pool. New connections are created on demand, as needed.
A DatabaseClient
object returns its connection to the pool once it receives and processes the HTTP request on whose behalf it claimed the connection. A connection in the pool persists until it is explicitly released or times out due to idleness. The default maximum idle time is 5 minutes.
No state information is maintained with a connection. All cookies are discarded unless a multi-statement (multi-request) transaction is in use. The cookies associated with a multi-statement transaction are cached on the transaction object rather than with the connection.
You can adjust the connection pool configuration by implementing OkHttpClientConfigurator
and calling its configure
method. However, such adjustments depend on Java Client API internals and will be ignored if a future version of the API uses a different HTTP client implementation.
To create a database client, use the com.marklogic.client.DatabaseClientFactory.newClient()
method. For example, the following client connects to the default content database associated with the REST instance on port 8000 of localhost using digest authentication.
DatabaseClient client = DatabaseClientFactory.newClient( "localhost", 8000, new DatabaseClientFactory.DigestAuthContext("myuser", "mypassword"));
You can also create clients that connect to a specific content database. For example, the following client also connects to the REST instance on port 8000 of localhost, but all operations are performed against the database MyDatabase:
DatabaseClient client = DatabaseClientFactory.newClient( "localhost", 8000, "MyDatabase", new DatabaseClientFactory.DigestAuthContext("myuser", "mypassword"));
To use a database other than the default database associated with the REST instance requires a user with the following privilege or the equivalent role: http://marklogic.com/xdmp/privileges/xdmp-eval-in
.
The host
and port
values must be those of a REST API instance. When you install MarkLogic, a REST API instance associated with the Documents database is pre-configured for port 8000. You can also create your own instance.
The authentication context object should match the configuration of the REST API instance. For more details, see Authentication and Connection Security.
When your application connects to MarkLogic through a load balancer, you should follow these guidelines:
DatabaseClient
objects to make a GATEWAY
type connection. This tells the Java Client API that direct connections to hosts in your MarkLogic cluster are not available.For most Java Client API operations, the connection type is transparent. However, features such as the Data Movement SDK need to know whether or not all traffic must go through a gateway host.
The default connection type for a DatabaseClient
is DIRECT
, meaning that the Java Client API can make direct connections to hosts in your MarkLogic cluster if necessary.
To configure a DatabaseClient
for a gateway connection, pass a DatabaseClient.ConnectionType
value of GATEWAY
as the last parameter to DatabaseClientFactory.newClient
. For example:
DatabaseClient client = DatabaseClientFactory.newClient( "localhost", 8000, "MyDatabase", new DatabaseClientFactory.DigestAuthContext("myuser", "mypassword"), DatabaseClient.ConnectionType.GATEWAY);
For additional, context-specific load balancer guidelines, see the following topics:
When you no longer need a client and want to release connection resources, use the DatabaseClient
object's release()
method.
client.release();
DatabaseClient objects efficiently manage connection resources and are expected to be long lived. You do not need to release and re-create client objects just because your application might not require a connection for an extended time. For more details, see Expected Database Client Lifetime and Connection Management and Configuration.
This section provides an overview of several methods for securing the communication between your client application and MarkLogic. See the following topics for details:
One of the inputs to DatabaseClientFactory.newClient
is a SecurityContext
object. This object tells the API what credentials to use to authenticate with MarkLogic. You can select from authentication methods such as Kerberos, digest, and basic.
For example, the database client created by the following statement uses digest authentication. The username and password are those of a user configured into MarkLogic.
import com.marklogic.client.DatabaseClientFactor.DigestAuthContext; ... DatabaseClient client = DatabaseClientFactory.newClient( "localhost", 8000, new DigestAuthContext(username, password));
The authentication context object should match the configuration of the REST API instance. Kerberos based authentication is most secure. Basic authentication sends the password in obfuscated, but not encrypted, mode. Digest authentication encrypts passwords sent over the network.
You can connect to MarkLogic using SSL by attaching SSL configuration information to the security context. For details, see Connecting to MarkLogic with SSL.
For more information about user authentication, see Authenticating Users in the Security Guide.
Use the following steps to configure your MarkLogic installation and client application environment for Kerberos authentication:
Your client host must be running Linux in order to use Kerberos with the Java Client API.
Before you can use Kerberos authentication, you must configure MarkLogic to use external security. If your installation is not already configured for Kerberos, you must perform at least the following steps:
For more details, see External Security in the Security Guide.
On the client, the Java Client API must be able to access a Ticket-Granting Ticket (TGT) from the Kerberos Key Distribution Center. The API uses the TGT to obtain a Kerberos service ticket.
Follow these steps to make a TGT available to the client application:
krb5.conf
file. For details, see https://web.mit.edu/kerberos/krb5-1.15/doc/admin/conf_files/krb5_conf.html.On Linux, Java expects this file to be located in /etc/
by default. Java uses the conf file to determine your default realm and the KDC for that realm.
If your krb5.conf
file contains a setting for default_ccache_name
, the value must be a file reference of the form FILE:/tmp/krb5cc_%{uid}
. This is required because the Java Client API sets the useTicketCache
option of Krb5LoginModule
to true
. For more details, see the javadoc for com.sun.security.auth.module.Krb5LoginModule
.
kinit
or a similar tool on your client host to create and cache a TGT with the Kerberos Key Distribution Center. The principal supplied to kinit
must be one you associated with a MarkLogic user when performing the steps in Configuring MarkLogic to Use Kerberos. For more details, see the following topics:
In your client application, use KerberosAuthContext
for your security context object. For example:
import com.marklogic.client.DatabaseClientFactory.KerberosAuthContext; ... DatabaseClient client = DatabaseClientFactory.newClient( "localhost", 8000, new KerberosAuthContext());
You do not need to pass an explicit externalName
parameter to KerberosAuthContext
unless you have multiple principals authenticated in your ticket cache and need to specify which one to use.
For a working example, see the project on GitHub:
The working example includes comments that provide suggestions for setting up a Kerberos configuration in a production environment.
https://github.com/marklogic/java-client-api/blob/master/marklogic-client-api/src/main/java/com/marklogic/client/example/cookbook/KerberosSSLClientCreator.java
You can use the security context to specify whether or not to use a secure SSL connection to communicate with MarkLogic. The App Server you connect to must also be configured to accept SSL connections. By default, the Java Client API does not use SSL.
For example, the database client created by the following statement uses digest authentication and an SSL connection:
// create a trust manager // (note: a real application should verify certificates. This // naive trust manager which accepts all the certificates should be replaced // by a valid trust manager or get a system default trust manager // which would validate whether the remote authentication credentials // should be trusted or not.) TrustManager naiveTrustMgr[] = new X509TrustManager[] { new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } }; // create an SSL context SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); /* * Here, we use a naive TrustManager which would accept any certificate * which the server produces. But in a real application, there should be a * TrustManager which is initialized with a Keystore which would determine * whether the remote authentication credentials should be trusted or not. * * If we init the sslContext with null TrustManager, it would use the * <java-home>/lib/security/cacerts file for trusted root certificates, if * javax.net.ssl.trustStore system property is not set and * <java-home>/lib/security/jssecacerts is not present. See this link for * more information on TrustManagers - * http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ * JSSERefGuide.html * * If self signed certificates, signed by CAs created internally are used, * then the internal CA's root certificate should be added to the keystore. * See this link - * https://docs.oracle.com/cd/E19226-01/821-0027/geygn/index.html for adding * a root certificate in the keystore. */ sslContext.init(null, naiveTrustMgr, null); // create the client // (note: a real application should use a COMMON, STRICT, or implemented hostname verifier) DatabaseClient client = DatabaseClientFactory.newClient( props.host, props.port, new DigestAuthContext(props.writerUser, props.writerPassword) .withSSLContext(sslContext, (X509TrustManager) naiveTrustMgr[0]) .withSSLHostnameVerifier(SSLHostnameVerifier.ANY));
The SSLContext
object represents a secure socket protocol implementation which acts as a factory for secure socket factories. For more information about creating and working with SSLContext
objects, see Accessing SSL-Enabled XDBC App Servers in the XCC Developer's Guide.
For even more security, you can also include a DatabaseClientFactory.SSLHostnameVerifier
object to check if a hostname is acceptable.
For a working example, see the project on GitHub:
The working example includes comments that provide suggestions for configuring SSL in a production environment.
https://github.com/marklogic/java-client-api/blob/master/marklogic-client-api/src/main/java/com/marklogic/client/example/cookbook/SSLClientCreator.java
For more information about secure communication with MarkLogic, see the Security Guide.
Your client application is responsible for acquiring a SAML assertions token from the SAML Identity Provider (IDP). You can then use the SAML assertions token to make requests to the MarkLogic App Server with the MarkLogic Client Java API. That division of responsibility makes it possible for your application to adapt to a wide variety of possible SAML scenarios and IDPs.
After configuring the MarkLogic App Server to authenticate with the SAML IDP, specify a SAMLAuthContext
as the SecurityContext
when calling DatabaseClientFactory
to create a new DatabaseClient
.
You can construct a SAMLAuthContext
in any of three ways, depending on your approach to authorization:
DatabaseClient
before the SAML assertions token expires, you can call the SAMLAuthContext
constructor with the SAML assertions token.DatabaseClient
, you can call the SAMLAuthContext
constructor with an ExpiringSAMLAuth
object and a callback that renews the SAML assertions token with the SAML IDP.The ExpiringSAMLAuth
object provides getters for the SAML assertions token and the expiration timestamp. Your client application can construct an ExpiringSAMLAuth
object by calling the SAMLAuthContext.newExpiringSAMLAuth
factory method.
The renewer callback conforms to the SAMLAuthContext.RenewerCallback
functional interface by taking the initial ExpiringSAMLAuth
object as input and returning an Instant with the new expiration timestamp for the renewed SAML assertions token, as in the following example:
class MyClass { Instant renewer(ExpiringSAMLAuth authorization) { .... call to IDP .... } }
DatabaseClient
, you can call the SAMLAuthContext
constructor with a callback that authorizes with the SAML IDP by getting a new SAML assertions token.The authorizer callback conforms to the SAMLAuthContext.AuthorizerCallback
functional interface that takes an ExpiringSAMLAuth
object as input and returns an ExpiringSAMLAuth
object with the new SAML assertions token and an expiration timestamp, as shown in the following example:
class MyClass { ExpiringSAMLAuth authorizer(ExpiringSAMLAuth previous) { .... call to IDP .... } }
On the first call, the ExpiringSAMLAuth
parameter is null because no existing authorization exists. Your callback can construct an ExpiringSAMLAuth
object by calling the SAMLAuthContext.newExpiringSAMLAuth
factory method.
Tradeoffs to consider when choosing whether to renew or reauthorize include the following:
You can reduce the expiration time to allow for network latency and the IDP response generation. Renewer and authorizer callbacks are called in advance of the stated expiration time to reduce the possibility that the SAML assertions token expires as a request is sent to the MarkLogic appserver.
If you need to maintain state between calls to a renewer or authorizer callback, you can implement the ExpiringSAMLAuth
interface with your own class instead of calling the SAMLAuthContext.newExpiringSAMLAuth
factory method to construct a default instance.
Apart from the specifics of acquiring the SAML assertions token, the use of a DatabaseClient
remains the same:
The following code is a basic method that creates a new document in the database. Digest authentication is used in this example; for more details, see Authentication and Connection Security.
public static void run(String host, int port, String user, String password, Authentication authType) { // Create the database client DatabaseClient client = DatabaseClientFactory.newClient( host, port, new DigestAuthContext(username, password)); // Make a document manager to work with text files. TextDocumentManager docMgr = client.newTextDocumentManager(); // Define a URI value for a document. String docId = "/example/text.txt"; // Create a handle to hold string content. StringHandle handle = new StringHandle(); // Give the handle some content handle.set("A simple text document"); // Write the document to the database with URI from docId // and content from handle docMgr.write(docId, handle); // release the client client.release(); }
The above code is a slightly modified version of the run
method from the com.marklogic.client.example.cookbook.ClientCreator
cookbook example. It, along with a number of other basic example applications for the Java API, is located in example/com/marklogic/client/example/cookbook
directory found in the zip file containing the Java API.
Different document formats are handled by different document manager objects, which serve as an interface between documents and the database connection. The package com.marklogic.client.document
includes document managers for binary, XML, JSON, and text. If you don't know the document format, or need to work with documents of multiple formats, use a generic document manager. DatabaseClient
instances have factory methods to create a new com.marklogic.client.document.DocumentManager
of any subtype.
BinaryDocumentManager binDocMgr = client.newBinaryDocumentManager(); XMLDocumentManager XMLdocMgr = client.newXMLDocumentManager(); JSONDocumentManager JSONDocMgr = client.newJSONDocumentManager(); TextDocumentManager TextDocMgr = client.newTextDocumentManager(); GenericDocumentManager genericDocMgr = client.newGenericDocumentManager();
Your application only needs to create one document manager for any given type of document, no matter how many of that type of document it works with. So, even if you expect to work with, say, 1,000,000,000 JSON documents, you only need to create one JSONDocumentManager
object.
Document managers are thread safe once initially configured; no matter how many threads you have, you only need one document manager per document type.
If you make a mistake and try to use the wrong type of document with a document manager, the result depends on the combination of types. For example, a BinaryDocumentManager
will try to interpret the document content as binary. JSONDocumentManager
and XMLDocumentManager
are the most particular, since if a document is not in their format, it will not parse. Most of the time, you will get an exception error, with FailedRequestException
the default if the manager cannot determine the document type.
To stream, you supply an InputStream
or Reader
for the data source, not only when reading from the database but also when writing to the database. This approach allows for efficient write operations that do not buffer the data in memory. You can also use an OutputWriter
to generate data as the API is writing the data to the database.
When reading from the database using a stream, be sure to close the stream explicitly if you do not read all of the data. Otherwise, the resources that support reading continue to exist.
The Java Client API uses Handles to for I/O when interacting with MarkLogic. See the following topics for more details:
Content handles are key to working with the Java Client API. Handles make use of the Adapter design pattern to enable strongly typed reading and writing of a diverse and extensible set of content formats. For example, you can create a com.marklogic.client.io.DOMHandle
to read or write XML DOM data.
// reading XMLDocumentManager docMgr = client.newXMLDocumentManager(); Document doc = docMgr.read(docURI, new DOMHandle()).get(); // writing docMgr.write(docURI, new DOMHandle(someDocument));
You can also create a com.marklogic.client.io.JacksonHandle
to read or write JSON data.
// reading JSONDocumentManager JSONDocMgr = client.newJSONDocumentManager(); JsonNode node = JSONDocMgr.read(docURI, new JacksonHandle()).get(); // writing JSONDocMgr.write(docURI, new JacksonHandle(someJsonNode));
The Java Client API pre-defines many handle implementations. The following packages contain handle classes:
com.marklogic.client.io
- Handles classes for standard representations such as String
, File
, and DOM
.com.marklogic.extra
- Handle classes for 3rd party formats such as DOM4J and GSON. Using these handle classes requires 3rd party libraries that are not included in the Java Client API distribution.Some handles support both read and write operations. For example, you can use a FileHandle
for reading and writing files. Some handles have a special purpose. For example, you use SearchHandle
for processing the results of a search operation. For a complete list of handles and what they do, see the com.marklogic.client.io
package in the Java Client API Documentation.
Handles are not thread safe. Whenever you create a new thread, you will have to also create new handle objects to use while in that thread.
Some Java Client API methods enable you to use I/O short cuts that do not require explicit creation of a handle. These shortcut methods always have an As suffix, such as readAs. For example, the XMLDocumentManager.read
method shown above has an XMLDocumentManager.readAs
counterpart that implicitly creates the handle for you. For example:
// reading Document doc = docMgr.readAs(docURI, Document.class); // writing docMgr.writeAs(docURI, someDocument);
Likewise, the JSONDocumentManger.read
method shown above has an JSONDocumentManager.readAs
counterpart that implicitly creates the handle for you.
// reading JsonNode node = JSONDocMgr.readAs(docURI, JsonNode.class); // writing JSONDocMgr.writeAs(docURI, someJsonNode);
These shortcut methods are not more efficient, but they can improve the readability of your code. For more details, see Shortcut Methods as an Alternative to Creating Handles.
Some handles can be used with multiple document formats. For example, an InputStream
can provide content in any format, so InputStreamHandle
can be used for any document format. Where content format is not explicit in the handle type, use the handle's setFormat
method to specify it. For example, the following call tells the Java Client API that the handle can be used with JSON content:
new InputStreamHandle().setFormat(Format.JSON);
You cannot set a format for all handle types. For example, a DOMHandle can only be used for reading and writing XML, so you cannot specify a format.
Not all handles support all content types. In addition, though most handles can be used for either reading or writing, some are more limited. This section provides a quick guide to the content formats, operations, and data types supported by each handle class. Special purpose handle classes, such as SearchHandle,
are not included.
Handle Class | Content Format | Supported Java Type | |||
---|---|---|---|---|---|
XML | Text | JSON | Binary | ||
BytesHandle |
RW | RW | RW | RW | byte[] |
DocumentMetadataHandle |
RW | MarkLogic proprietary XML format; for details, see XML Metadata Format in the REST Application Developer's Guide. | |||
DOMHandle |
RW | org.w3c.dom.Document |
|||
FileHandle |
RW | RW | RW | RW | java.io.File |
InputSourceHandle |
RW | org.xml.sax.InputSource |
|||
InputStreamHandle |
RW | RW | RW | RW | java.io.InputStream |
JacksonHandle |
RW | com.fasterxml.jackson.databind.JsonNode |
|||
JacksonDatabindHandle |
RW | your POJO class | |||
JacksonParserHandle |
RW | com.fasterxml.jackson.core.JsonParser |
|||
JAXBHandle |
RW | your POJO class | |||
OutputStreamHandle |
W | W | W | W | java.io.OutputStream |
ReaderHandle |
RW | RW | RW | java.io.Reader |
|
SourceHandle |
RW | javax.xml.transform.Source |
|||
StringHandle |
RW | RW | RW | String |
|
XMLEventReaderHandle |
RW | javax.xml.stream.XMLEventReader |
|||
XMLStreamReaderHandle |
RW | javax.xml.stream.XMLStreamReader |
The following code uses a DOMHandle
to read an XML document from the server into an in-memory DOM object:
XMLDocumentManager docMgr = client.newXMLDocumentManager(); DOMHandle handle = new DOMHandle(); docMgr.read(docURI, handle); org.w3c.dom.Document document = handle.get();
The following code uses a JacksonHandle
to read a JSON document from the server into an in-memory JsonNode:
JSONDocumentManager JSONDocMgr = client.newJSONDocumentManager(); JacksonHandle handleJson = new JacksonHandle(); JSONDocMgr.read(docURI, handleJson); com.fasterxml.jackson.databind.JsonNode node = handleJson.get();
The following code uses a DOMHandle
to write an XML document to MarkLogic. Assume document
is some previously initialized in-memory XML DOM document.
XMLDocumentManager docMgr = client.newXMLDocumentManager(); DOMHandle handle = new DOMHandle(); handle.set(document); docMgr.write(docId, handle);
The following code uses a JacksonHandle
to write a JSON document to MarkLogic. Assume node
is some previously initialized in-memory JsonNode document.
JSONDocumentManager JSONDocMgr = client.newJSONDocumentManager(); JacksonHandle handleJson = new JacksonHandle(); handleJson.set(node); JSONDocMgr.write(docId, handleJson);
For additional examples, see the examples in the following packages. The source is available on GitHub. For details, see Downloading the Library Source Code.
Shortcut methods enable you to pass supported data types directly into or out of an operation without explicitly creating a handle to reference the data. These convenience methods can make your code more readable.
For more details, see the following topics:
Many Java Client API classes and interfaces include shortcut methods of the form operationAs
, such as readAs
or writeAs
. These methods enable you to bypass the equivalent, more strongly typed methods that require you to pass in a handle. Using shortcut methods instead of handles can make your code more readable.
For example, the XMLDocumentManager
and JSONDocument Manager
interfaces includes both read
and readAs
methods such as the following:
// strongly typed, handle based read(String docId, T contentHanlde) // shortcut equivalent readAs(String docId, Class<T> as)
This means you can read a document from the database using a call of either of the following forms:
// strongly typed, returns the populated DOMHandle object DOMHandle handle = docMgr.read(docURI, new DOMHandle()); // shortcut, returns a DOM Document Document doc = docMgr.readAs(docURI, Document.class); // strongly typed, returns the populated JacksonHandle object JacksonHandle handleJSON = JSONDocMgr.read(docURI, new JacksonHandle()); // shortcut, returns a JsonNode JsonNode node = JSONDocMgr.readAs(docURI, JsonNode.class);
Similarly, you can use XMLDocumentManager
or JSONDocumentManager
to write a document to the database using either of the following calls:
// strongly typed docMgr.write(docURI, new DOMHandle(theDocument)); // shortcut docMgr.writeAs(docURI, theDocument); // strongly typed JSONDocMgr.write(docURI, new JacksonHandle(theJsonNode)); // shortcut JSONDocMgr.writeAs(docURI, theJsonNode));
Shortcut methods are not limited to document read and write operations. For example, you can use either QueryManager.newRawCombinedQueryDefinition
or QueryManager.newRawCombinedQueryDefinitionAs
to create a RawCombinedQueryDefinition
.
Shortcut methods are the best choice in most cases because they improve the readability and maintainability of your code. However, you should keep the following points in mind:
Handle
is still created to manage the data.The typing exposure is limited since the Java Client API pre-defines Handle classes for a broad range of types. You can register your own class-to-handle pairings to extend the supported classes. For details, see Extending Shortcuts by Registering Handle Factories.
Consider the strongly typed call form in the following cases:
Though you do not have to create a handle when using a shortcut method, the shortcut implementation still creates a Handle
to manage the data.
For example, when you issue a shortcut call such as the following, the implementation creates a DOMHandle
to receive the document read from the database.
docMgr.readAs(docURI, Document.class);
Similarly, the following implementation creates a JacksonHandle
to receive the document read from the database.
JSONDocMgr.readAs(docURI, JsonNode.class);
This means that a shortcut method must be able to create a handle capable of handling the targeted class type. This capability is provided by a registry for handle factories. The shortcut method can query the registry for a handle factory that can process a particular class type. For details, see DatabaseClientFactory.HandleFactoryRegistry
in the Java Client API Documentation.
The Java Client API automatically registers factories for the following handle classes. For details on the data types supported by each handle type, see the handle class documentation in the Java Client API Documentation.
BytesHandle |
InputStreamHandle |
SourceHandle |
DOMHandle |
JacksonHandle |
StringHandle |
FileHandle |
JacksonParserHandle |
XMLEventReaderHandle |
InputSourceHandle |
ReaderHandle |
XMLStreamReaderHandle |
If you create your own handle class, you can register a handle factory for it so that you can use shortcut methods on the classes supported by your handle class.
Handle factory registration must be completed before you create a DatabaseClient
.
You can use the same mechansim with a JAXBHandle
factory to enable shortcut methods on POJOs. For example, if you have a POJO class named Product
, then you can add it to the registry as follows:
DatabaseClientFactory.getHandleRegistry().register( JAXBHandle.newFactory(Product.class);
You can also use JacksonDatabindHandle
factory to enable shortcut methods on POJOs.
DatabaseClientFactory.getHandleRegistry().register( JacksonDatabindHandle.newFactory(Product.class);
Then you can subsequently write Product
POJOs to MarkLogic and read them back as follows:
XMLDocumentManager docMgr = client.newXMLDocumentManager(); Product product = // ...create a Product docMgr.writeAs(docURI, Product.class); // ... product = docMgr.readAs(docURI, Product.class);
Likewise with a JSONDocumentManager
:
JSONDocumentManager JSONDocMgr = client.newJSONDocumentManager(); Product product = // ...create a Product JSONDocMgr.writeAs(docURI, Product.class); // ... product = JSONDocMgr.readAs(docURI, Product.class);
Note that the Java Client API also includes a POJO data binding capability as an alternative to managing your own POJOs with JAXB. Using this feature eliminates the need for the above registration. For more details, see POJO Data Binding Interface.
You should be aware of the following API characteristics with respect to thread safety:
DatabaseClient
is thread safe after initialization.DocumentManager
, QueryManager
, ResourceManager
.StringHandle
, FileHandle
, SearchHandle.
DocumentPatchBuilder
, StructuredQueryBuilder
.For example, you can create a DocumentManager
for manipulating XML documents and share it across multiple threads. Similarly, you can create a QueryManager
, set the page length, and then share it between multiple threads.
Handles can be used across multiple requests within the same thread, but cannot be used across threads, so whenever you create a new thread, you must create new Handle objects to use in that thread.
The Java API is an open source project. Though you do not need the source code to use the library, the source is available from GitHub at the following URL:
https://github.com/marklogic/java-client-api
Assuming you have a Git client and the git
command is on your path, you can download a local copy of the latest source using the following command:
git clone https://github.com/marklogic/java-client-api.git