Java Client SDK 4.0

Document

Updated: July 18, 2024

This section is about how handling documents with Nuxeo Java Client.

Almost all APIs have an asynchronous equivalent, asynchronous APIs share the same name than synchronous APIs but they return nothing and have a retrofit2.Callback parameter in addition.

Let's first retrieve repository manager to handle our documents:

Repository repository = client.repository();

Create

APIs below are available to create a document:

  • createDocumentByPath which takes parent path and a Document
  • createDocumentById which takes parent id and a Document

Let's create a folder:

Document folder = Document.createWithName("folder", "Folder");
folder = repository.createDocumentByPath("/", folder);

Now we can create a file and a note in it:

Document note = Document.createWithName("note001", "Note");
note = repository.createDocumentByPath("/folder", note);

Document file = Document.createWithName("file001", "File");
file = repository.createDocumentById(folder.getId(), file);

We can also provide some property at creation:

Document note = Document.createWithName("note002", "Note");
note.setPropertyValue("note:note", "Some note for my second note document")
note.setPropertyValue("dc:description", "Description of my document");
note = repository.createDocumentByPath("/folder", note);

Fetch

APIs below are available to fetch document:

  • fetchDocumentRoot
  • fetchDocumentById which takes document id to fetch
  • fetchDocumentByPath which takes document path to fetch
  • fetchChildrenById which takes parent id of children to fetch
  • fetchChildrenByPath which takes parent path of children to fetch

Let's fetch our folder:

Document folder = repository.fetchDocumentByPath("/folder");
String title = folder.getPropertyValue("dc:title"); // equals to folder

We can retrieve its children with either fetchChildrenById or fetchChildrenByPath which take the parent id or path:

Documents children = repository.fetchChildrenById(folder.getId());
int childrenSize = children.size() // equals to 3
List<Document> childrenList = children.getEntries();
Document note = children.streamEntries()
                        .filter(doc -> "note001".equals(doc.getName()))
                        .findFirst()
                        .orElseThrow(() -> new RuntimeException("Unable to find 'note001'"));

As Document is a connectable entity and we get folder from client we can do the following:

Documents children = folder.fetchChildren();

fetchChildren behavior
fetchChildren* returns a subset of all children. Because they leverage ChildrenAdapter server side which leverages CURRENT_DOC_CHILDREN page provider.

By default this page provider returns only 50 elements per page.

If you want to retrieve all children you should use page provider APIs instead.

Update

APIs below are available to update document:

  • updateDocument which takes a document (with an id)

During the update, only dirty properties detected on client side are sent to the server.

Let's update our note:

Document note = repository.fetchDocumentByPath("/folder/note001");
note.setPropertyValue("note:note", "Some update in content");
 // previous note is not updated, you need to get returned document for further operation
note = repository.updateDocument(note);

As Document is a connectable entity we can also do the following:

Document note = repository.fetchDocumentByPath("/folder/note001");
note.setPropertyValue("note:note", "Some update in content");
 // previous note is not updated, you need to get returned document for further operation
note = note.updateDocument();

You can also update document without fetching it first, if and only if you know its id:

Document note = Document.createWithId("UUID", "OPTIONAL_TYPE");
note.setPropertyValue("note:note", "Some update in content");
note = repository.updateDocument(note);

Id is mandatory because this is how Nuxeo Server resolved the document to update. There's currently no way to update a document with only its path. Also, with this way, you can't call Document#updateDocument because your object is not connected with client, but the one returned from repository#updateDocument is connected.

Delete

APIs below are available to delete document:

  • deleteDocument which takes document id to delete
  • deleteDocument which takes document to delete

Just simple as:

repository.deleteDocument("UUID");

Adapters

You can use the Adapters API to access the Nuxeo Web Adapter of your document with the method Document#adapter.

The Java client provides the following adapters:

  • AnnotationAdapter
  • CommentAdapter

You can use it like below:

Comment comment = new Comment();
comment.setText("A comment");
Document note = repository.fetchDocumentByPath("/folder/note001");
note.adapter(CommentAdapter::new).create(comment);

Manipulate Document Properties

Once you have retrieved your document you can get/set property values with the API below:

  • getPropertyValue
  • setPropertyValue

Example:

// getPropertyValue
Document note = repository.fetchDocumentByPath("/folder/note001");
String noteText = note.getPropertyValue("note:note");
// getPropertyValue with XPath
Document note = repository.fetchDocumentByPath("/folder/note001");
String firstContributor = note.getPropertyValue("dc:contributors/0");
// setPropertyValue
Document note = repository.fetchDocumentByPath("/folder/note001");
note.setPropertyValue("note:note", "Some update in content");
// setPropertyValue with list/complex
Document note = repository.fetchDocumentByPath("/folder/note001");
note.setPropertyValue("dc:subjects", List.of("art/architecture", "sciences/astronomy"));

Enrichers / Fetch Properties

As seen in configuration, you can configure enrichers and fetch properties for your manager, here the repository.

Depending on enricher or property to fetch your use, you will either get a proper Java Client object or a JSON representation in Map and List. Deserialization into proper object leverage entities registered during client creation, see marshalling configuration for more information.

For instance, breadcrumb enricher adds a Documents JSON representation in the context parameter:

Document note = repository.enrichersForDocument("breadcrumb").fetchDocumentByPath("/folder/note001");
Documents breadcrumb = note.getContextParameter("breadcrumb");

Whereas thumbnail enricher writes an URL:

Document file = repository.enrichersForDocument("thumbnail").fetchDocumentByPath("/folder/file");
// as thumbnail just returned an url we can cast to Map<String, String>
Map<String, String> thumbnail = note.getContextParameter("thumbnail");
String thumbnailUrl = thumbnail.get("url");

Or renditions enricher which writes several fields:

Document file = repository.enrichersForDocument("renditions").fetchDocumentByPath("/folder/file");
// as renditions returned a list of objects containing string we can cast to List<Map<String, String>>
List<Map<String, String>> renditions = note.getContextParameter("renditions");
for (Map<String, String> rendition : renditions) {
    String name = rendition.get("name");
    String kind = rendition.get("kind");
    String icon = rendition.get("icon");
    String url = rendition.get("url");
}

Same behavior applies to fetch properties.

For instance, on dc:creator we have:

Document file = repository.fetchPropertiesForDocument("dc:creator")
                          .fetchDocumentByPath("/folder/file");
User creator = file.getPropertyValue("dc:creator");

You can request several enrichers and/or properties to fetch as it:

Document file = repository.enrichersForDocument("renditions", "thumbnail", "breadcrumb")
                          .fetchPropertiesForDocument("dc:creator", "dc:contributors")
                          .fetchDocumentByPath("/folder/file");

Query and Pagination

APIs below are available to query documents:

  • query which takes an NXQL query
  • query which takes an NXQL query, page size, current page index, max results, sort by, sort order and query params (except query, others are optional)

Query method taking only an NXQL query is driven by server side configuration, see query endpoint documentation.

Let's see how retrieving a paginated query result and loop over pages:

Documents documents = null;
do {
    String pageIndex = documents == null ? "0" : String.valueOf(documents.getCurrentPageIndex() + 1);
    documents = repository.query("SELECT * FROM Document", "50", pageIndex, null,
            "dc:title,dc:description", "ASC,DESC");
    for (Document document : documents.getEntries()) {
        // do something with document
    }
} while (documents.isNextPageAvailable());

If you want to use query params, you can use the API below:

repository.query("SELECT * FROM Document WHERE dc:title= ?", "50", pageIndex, null, 
    "dc:title,dc:description", "ASC,DESC", "My Document");

Page provider

API below is available to query documents with page provider:

  • queryByProvider which takes a page provider name, page size, current page index, max results, sort by, sort order and query params (except page provider name, others are optional)
  • queryByProvider which takes a page provider name, page size, current page index, max results, sort by, sort order, named params and query params (except page provider name, others are optional)

Let's see how retrieving a paginated page provider result and loop over pages:

Documents documents = null;
do {
    String pageIndex = documents == null ? "0" : String.valueOf(documents.getCurrentPageIndex() + 1);
    documents = repository.queryByProvider("CURRENT_DOC_CHILDREN", "50", pageIndex, null,
            "dc:title,dc:description", "ASC,DESC");
    for (Document document : documents.getEntries()) {
        // do something with document
    }
} while (documents.isNextPageAvailable());

If your page provider declares query params, you can use for instance:

<coreQueryPageProvider name="search_with_params">
  <pattern>SELECT * From Note WHERE dc:title = ?</pattern>
  <pageSize>50</pageSize>
</coreQueryPageProvider>
repository.queryByProvider("search_with_params", "50", pageIndex, null,
    "dc:title,dc:description", "ASC,DESC", "My Note");

If your page provider declares named params, you can use for instance:

<coreQueryPageProvider name="search_with_named_params">
  <pattern>SELECT * From Note WHERE dc:title = :title</pattern>
  <pageSize>50</pageSize>
</coreQueryPageProvider>
repository.queryByProvider("search_with_params", "50", pageIndex, null,
    "dc:title,dc:description", "ASC,DESC", Map.of("title", "My Note"));

Permissions

APIs below are available to fetch permissions:

  • fetchACPById which takes a document id
  • fetchACPByPath which takes a document path

Let's fetch ACP of our note:

ACP acp = repository.fetchACPByPath("/folder/note001");
ACL acl = acp.getAcls()
             .stream()
             .filter(acl -> ACL.LOCAL_ACL.equals(acl.getName()))
             .findFirst()
             .orElseThrow(() -> new RuntimeException("Unable to find 'local' acl"));
List<ACE> aces = acl.getAces();

As Document is a connectable entity we can fetch ACP directly from it but also handle them:

Document note = repository.fetchDocumentByPath("/folder/note001");
// fetch ACP
ACP acp = note.fetchPermissions();
// remove all local permissions for john
note.removePermission("john"); // does a call to nuxeo server
// add a new permission for john
ACE ace = new ACE();
ace.setCreator("Administrator");
ace.setUsername("john");
ace.setPermission("READ");
ace.setBegin(new GregorianCalendar(2015, Calendar.JUNE, 20));
ace.setEnd(new GregorianCalendar(2015, Calendar.JUNE, 27));
note = note.addPermission(ace); // does a call to nuxeo server
// send an external invitation to [email protected]
ACE invitation = new ACE();
invitation.setCreator("Administrator");
invitation.setPermission("READ");
invitation.setBegin(new GregorianCalendar(2015, Calendar.JUNE, 20));
invitation.setEnd(new GregorianCalendar(2015, Calendar.JUNE, 27));
note = note.addInvitation(invitation, "[email protected]"); // does a call to nuxeo server