nuxeo/nuxeo-java-client

Name: nuxeo-java-client

Owner: Nuxeo

Description: Java Client Library for the Nuxeo Platform REST APIs

Created: 2015-11-19 22:34:48.0

Updated: 2018-05-24 15:44:09.0

Pushed: 2018-05-24 15:44:11.0

Homepage: http://nuxeo.github.io/nuxeo-java-client

Size: 7898

Language: Java

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Java Client Library 3.1.0-SNAPSHOT for the Nuxeo Platform REST APIs

The Nuxeo Java Client is a Java client library for Nuxeo Automation and REST API.

This is supported by Nuxeo and compatible with Nuxeo LTS 2015, Nuxeo LTS 2016 and latest Fast Tracks.

Here is the Documentation Website.

Jenkins master vs master Jenkins master vs 9.10 Jenkins master vs 8.10 Jenkins master vs 7.10 Dependency Status

Building

mvn clean install

Getting Started
Server
Library Import
Compatible with all Nuxeo versions as of LTS 2015

Nuxeo Java Client is compatible with:

You can download the client on our Nexus: Nuxeo Client Library 3.1.0-SNAPSHOT

Import Nuxeo Java Client with:

Maven:

endency>
roupId>org.nuxeo.client</groupId>
rtifactId>nuxeo-java-client</artifactId>
ersion>3.1.0-SNAPSHOT</version>
pendency>

ository>
d>public-releases</id>
rl>http://maven.nuxeo.com/nexus/content/repositories/public-releases/</url>
pository>
ository>
d>public-snapshots</id>
rl>http://maven.nuxeo.com/nexus/content/repositories/public-snapshots/</url>
pository>

Gradle:

ile 'org.nuxeo.client:nuxeo-java-client:3.1.0-SNAPSHOT'

Ivy:

endency org="org.nuxeo.client" name="nuxeo-java-client" rev="3.1.0-SNAPSHOT" />

SBT:

aryDependencies += "org.nuxeo.client" % "nuxeo-java-client" % "3.1.0-SNAPSHOT"
Sub-Modules Organization
Usage
Creating a Client

For a given url:

ng url = "http://localhost:8080/nuxeo";

And given credentials (by default using the Basic Auth) :

rt org.nuxeo.client.NuxeoClient;

oClient nuxeoClient = new NuxeoClient.Builder()
                                     .url(url)
                                     .authentication("Administrator", "Administrator")
                                     .connect();
Options

Options can be set on builder, client or API objects. This ensure inheritance and isolation of options on the object whose options are applied. As it, the builder gives its options to client and client gives them to API objects.

Some options are only available on some objects. This is the case for cache or voidOperation options.

o set a cache on client (this line needs the import of nuxeo-java-client-cache)
oClient = new NuxeoClient.Builder().cache(new ResultCacheInMemory());
ava
o set read and connect timeout on http client
oClient = new NuxeoClient.Builder().readTimeout(60).connectTimeout(60);
ava
o define session and transaction timeout in http headers
oClient = nuxeoClient.timeout(60).transactionTimeout(60);
ava
o define global schemas, global enrichers and global headers in general
eaders customization works by overriding - when you set a header which exist previously, client
ill remove the previous one
oClient = nuxeoClient.schemas("dublincore", "common")
                     .enrichersForDocument("acls", "preview")
                     .header(key1, value1)
                     .header(key2, value2);
ava
o fetch all schemas
oClient = nuxeoClient.schemas("*");
ava
o shutdown  the client
oClient = nuxeoClient.disconnect();
APIs

General rule:

Operation API

To use the Operation API, org.nuxeo.client.NuxeoClient#operation(String) is the entry point for all calls:

rt org.nuxeo.client.objects.Document;

etch the root document
ment doc = nuxeoClient.operation("Repository.GetDocument").param("value", "/").execute();
ava
rt org.nuxeo.client.objects.Documents;

xecute query
ments docs = nuxeoClient.operation("Repository.Query")
                        .param("query", "SELECT * FROM Document")
                        .execute();
ava
rt org.nuxeo.client.objects.blob.FileBlob;

o upload|download blob(s)

Blob fileBlob = new FileBlob(io.File file);
Blob = nuxeoClient.operation("Blob.AttachOnDocument")
                  .param("document", "/folder/file")
                  .input(fileBlob)
                  .execute();

s inputBlobs = new Blobs();
tBlobs.add(io.File file1);
tBlobs.add(io.File file2);
s blobs = nuxeoClient.operation("Blob.AttachOnDocument")
                     .param("xpath", "files:files")
                     .param("document", "/folder/file")
                     .input(inputBlobs)
                     .execute();

Blob resultBlob = nuxeoClient.operation("Document.GetBlob")
                             .input("folder/file")
                             .execute();
Repository API
rt org.nuxeo.client.objects.Document;

etch the root document
ment root = nuxeoClient.repository().fetchDocumentRoot();
ava
etch document in a specific repository
 = nuxeoClient.repository("other_repo").fetchDocumentRoot();
ava
etch document by path
ment folder = nuxeoClient.repository().fetchDocumentByPath("/folder_2");
ava
reate a document
ment document = Document.createWithName("file", "File");
ment.setPropertyValue("dc:title", "new title");
ment = nuxeoClient.repository().createDocumentByPath("/folder_1", document);
ava
andle date using ISO 8601 format
ment document = Document.createWithName("file", "File");
ment.setPropertyValue("dc:issued", "2017-02-09T00:00:00.000+01:00");

When handling date object, such as java.time.ZonedDateTime or java.util.Calendar, it should be converted to string as ISO 8601 date format “yyyy-MM-dd'T'HH:mm:ss.SSSXXX” before calling the constructor or any setter method, e.g. Document#setPropertyValue(String, Object). Otherwise, an exception will be thrown by the document.

pdate a document
ment document = nuxeoClient.repository().fetchDocumentByPath("/folder_1/note_0");
ment documentUpdated = Document.createWithId(document.getId(), "Note");
mentUpdated.setPropertyValue("dc:title", "note updated");
mentUpdated.setPropertyValue("dc:nature", "test");
mentUpdated = nuxeoClient.repository().updateDocument(documentUpdated);
ava
elete a document
ment documentToDelete = nuxeoClient.repository().fetchDocumentByPath("/folder_1/note_1");
oClient.repository().deleteDocument(documentToDelete);
ava
etch children
ment folder = nuxeoClient.repository().fetchDocumentByPath("/folder_2");
ments children = folder.fetchChildren();
ava
etch blob
ment file = nuxeoClient.repository().fetchDocumentByPath("/folder_2/file");
 blob = file.fetchBlob();
ava
rt org.nuxeo.client.api.objects.audit.Audit;

etch the document Audit
ment root = nuxeoClient.repository().fetchDocumentRoot();
t audit = root.fetchAudit();
ava
xecute query
ments documents = nuxeoClient.repository().query("SELECT * From Note");

rt org.nuxeo.client.api.objects.RecordSet;
ith RecordSets
rdSet documents = nuxeoClient.operation("Repository.ResultSetQuery")
                             .param("query", "SELECT * FROM Document")
                             .execute();
ava
rt retrofit2.Callback;

etch document asynchronously with callback
oClient.repository().fetchDocumentRoot(new Callback<Document>() {
        @Override
        public void onResponse(Call<Document> call, Response<Document>
                response) {
            if (!response.isSuccessful()) {
                ObjectMapper objectMapper = new ObjectMapper();
                NuxeoClientException nuxeoClientException;
                try {
                    nuxeoClientException = objectMapper.readValue(response.errorBody().string(),
                            NuxeoClientException.class);
                } catch (IOException reason) {
                    throw new NuxeoClientException(reason);
                }
                fail(nuxeoClientException.getRemoteStackTrace());
            }
            Document folder = response.body();
            assertNotNull(folder);
            assertEquals("Folder", folder.getType());
            assertEquals("document", folder.getEntityType());
            assertEquals("/folder_2", folder.getPath());
            assertEquals("Folder 2", folder.getTitle());
        }

        @Override
        public void onFailure(Call<Document> call, Throwable t) {
            fail(t.getMessage());
        }
    });
Permissions

To manage permission, please look inside package org.nuxeo.client.api.objects.acl to handle ACP, ACL and ACE:

etch Permissions of the current document
ment folder = nuxeoClient.repository().fetchDocumentByPath("/folder_2");
acp = folder.fetchPermissions();
rtTrue(acp.getAcls().size() != 0);
rtEquals("inherited", acp.getAcls().get(0).getName());
rtEquals("Administrator", acp.getAcls().get(0).getAces().get(0).getUsername());
ava
reate permission on the current document
orianCalendar begin = new GregorianCalendar(2015, Calendar.JUNE, 20, 12, 34, 56);
orianCalendar end = new GregorianCalendar(2015, Calendar.JULY, 14, 12, 34, 56);
ace = new ACE();
setUsername("user0");
setPermission("Write");
setCreator("Administrator");
setBegin(begin);
setEnd(end);
setBlockInheritance(true);
er.addPermission(ace);
ava
emove permissions in 'local' on the current document for a given name
er.removePermission("user0");
emove permissions on the current document for those given parameters
er.removePermission(idACE, "user0", "local");
Batch Upload

Batch uploads are executed through the org.nuxeo.client.objects.upload.BatchUploadManager.

atch Upload Manager
hUploadManager batchUploadManager = nuxeoClient.uploadManager();
hUpload batchUpload = batchUploadManager.createBatch();
ava
pload File
 file = FileUtils.getResourceFileFromContext("sample.jpg");
hUpload = batchUpload.upload("1", file);

etch/Refresh the batch file information from server
hUpload = batchUpload.fetchBatchUpload("1");

irectly from the manager
hUpload = batchUpload.fetchBatchUpload(batchUpload.getBatchId(), "1");

pload another file and check files
 = FileUtils.getResourceFileFromContext("blob.json");
hUpload.upload("2", file);
<BatchUpload> batchFiles = batchUpload.fetchBatchUploads();

Batch upload can be executed in a chunk mode.

pload file chunks
hUploadManager batchUploadManager = nuxeoClient.uploadManager();
hUpload batchUpload = batchUploadManager.createBatch();
hUpload.enableChunk();
 file = FileUtils.getResourceFileFromContext("sample.jpg");
hUpload = batchUpload.upload("1", file);

Chunk size is by default 1MB (int 1024*1024). You can update this value with:

hUpload.chunkSize(1024);

Attach batch to a document:

ment doc = new Document("file", "File");
set("dc:title", "new title");
= nuxeoClient.repository().createDocumentByPath("/folder_1", doc);
set("file:content", batchUpload.getBatchBlob());
= doc.updateDocument();

or with operation:

ment doc = new Document("file", "File");
set("dc:title", "new title");
= nuxeoClient.repository().createDocumentByPath("/folder_1", doc);
 blob = batchUpload.operation("Blob.AttachOnDocument").param("document", doc).execute();
Directories
rt org.nuxeo.client.objects.directory.DirectoryEntries;
etch a directory entries
ctoryEntries entries = nuxeoClient.directoryManager().fetchDirectoryEntries("continent");
Users/Groups
rt org.nuxeo.client.objects.user.User;
et current user used to connect to Nuxeo Server
 currentUser = nuxeoClient.getCurrentUser();

etch current user from sever
 currentUser = nuxeoClient.userManager().fetchCurrentUser();
ava
rt org.nuxeo.client.objects.user.User;
etch user
 user = nuxeoClient.userManager().fetchUser("Administrator");
ava
rt org.nuxeo.client.objects.user.Group;
etch group
p group = nuxeoClient.userManager().fetchGroup("administrators");
ava
reate User/Group

Manager userManager = nuxeoClient.userManager();
 newUser = new User();
ser.setUserName("toto");
ser.setCompany("Nuxeo");
ser.setEmail("toto@nuxeo.com");
ser.setFirstName("to");
ser.setLastName("to");
ser.setPassword("totopwd");
ser.setTenantId("mytenantid");
<String> groups = new ArrayList<>();
ps.add("members");
ser.setGroups(groups);
 user = userManager.createUser(newUser);

Manager userManager = nuxeoClient.userManager();
p group = new Group();
p.setGroupName("totogroup");
p.setGroupLabel("Toto Group");
<String> users = new ArrayList<>();
s.add("Administrator");
p.setMemberUsers(users);
p = userManager.createGroup(group);
ava
pdate User/Group
 updatedUser = userManager.updateUser(user);
p updatedGroup = userManager.updateGroup(group);
ava
emove User/Group
Manager.deleteUser("toto");
Manager.deleteGroup("totogroup");
ava
dd User to Group
Manager.addUserToGroup("Administrator", "totogroup");
Manager.attachGroupToUser("members", "Administrator");
Workflow
rt org.nuxeo.client.objects.workflow.Workflows;
etch current user workflow instances
flows workflows = nuxeoClient.userManager().fetchWorkflowInstances();
ava
etch document workflow instances
flows workflows = nuxeoClient.repository().fetchDocumentRoot().fetchWorkflowInstances();
Manual REST Calls

NuxeoClient allows manual REST calls with the 4 main methods GET, POST, PUT, DELETE and provides JSON (de)serializer helpers:

rt okhttp3.Response;

ET Method and Deserialize Json Response Payload
onse response = nuxeoClient.get("NUXEO_URL/path/");
rtEquals(true, response.isSuccessful());
ng json = response.body().string();
ment document = (Document) nuxeoClient.getConverterFactory().readJSON(json, Document.class);
ava
UT Method and Deserialize Json Response Payload
onse response = nuxeoClient.put("NUXEO_URL/path/", "{\"entity-type\": \"document\",\"properties\": {\"dc:title\": \"new title\"}}");
rtEquals(true, response.isSuccessful());
ng json = response.body().string();
ment document = (Document) nuxeoClient.getConverterFactory().readJSON(json, Document.class);
Authentication

By default, Nuxeo java client is using the basic authentication via the okhttp interceptor org.nuxeo.client.spi.auth.BasicAuthInterceptor.

The other available interceptors are: To use different interceptor(s):

Use NuxeoClientBuilder#authentication(BasicAuthInterceptor) instead of basic authentication.

To create a new interceptor:

Create a new java class implementing the interface okhttp3.Interceptor - see the okhttp documentation.

Async/Callbacks

All APIs from the client are executable in Asynchronous way.

All APIs are duplicated with an additional parameter retrofit2.Callback<T>.

When no response is needed (204 No Content Status for example), use retrofit2.Callback<ResponseBody> (okhttp3.ResponseBody). This object can be introspected like the response headers or status for instance.

Operation & Business Objects

In Operation, to use Plain Old Java Object client side for mapping custom objects server side (like document model adapter or simply a custom structure sent back by the server), it is possible to manage “business objects”:

Example:

Custom server side operation:

ration(id = CustomOperationJSONBlob.ID, category = "Document", label = "CustomOperationJSONBlob")
ic class CustomOperationJSONBlob {

public static final String ID = "CustomOperationJSONBlob";

@OperationMethod
public Blob run() {

    JSONObject attributes = new JSONObject();
    attributes.put("entity-type", "custom-json-object")
    attributes.put("userId", "1");
    attributes.put("token", "token");

    return Blobs.createBlob(attributes.toString(), "application/json");
}

This operation will create this request json payload:


ntity-type": "custom-json-object";
serId": "1",
oken": "token"

On the client side, we will have to provide:

xtending Entity is optional
rt org.nuxeo.client.objects.Entity;

ic class CustomJSONObject extends Entity {

public static final String ENTITY_TYPE = "custom-json-object";

private String userId;

private String token;

@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<>();

public CustomJSONObject() {
    super(ENTITY_TYPE);
}

public String getUserId() {
    return userId;
}
public void setUserId(String userId) {
    this.userId = userId;
}
public String getToken() {
    return token;
}
public void setToken(String token) {
    this.token = token;
}
public Map<String, Object> getAdditionalProperties() {
    return this.additionalProperties;
}
public void setAdditionalProperty(String name, Object value) {
    this.additionalProperties.put(name, value);
}


oClient client = new NuxeoClient.Builder()
                                   .url(url)
                                   .authentication(username, password)
                                   .registerEntity(CustomJSONObject.ENTITY_TYPE, CustomJSONObject.class)
                                   .connect();
omJSONObject customJSONObject = nuxeoClient.operation("CustomOperationJSONBlob").execute();
Custom Endpoints/Marshallers

nuxeo-java-client is using retrofit to deploy the endpoints and FasterXML to create marshallers.

Here an example:

age com;

rt com.Custom

rt retrofit2.Call;
rt retrofit2.http.GET;
rt retrofit2.http.Path;

ic interface CustomAPI {

@GET("custom/path")
Call<Custom> fetchCustom(@Path("example") String example);
age com;

rt com.fasterxml.jackson.annotation.JsonIgnore;

ic class Custom {

otected String path;

sonIgnore
otected transient String other;
age com;

rt org.nuxeo.client.NuxeoClient;
rt org.nuxeo.client.objects.AbstractConnectable;

ic class CustomService extends AbstractConnectable<CustomAPI> {

blic CustomService(NuxeoClient client) {
super(CustomAPI.class, client);


blic Custom fetchCustom(String example) {
return fetchResponse(api.fetchCustom(example));

And it's done!

Cache

We provide a “in memory” cache implementation using Guava. In order to use it, you need to add as dependency nuxeo-java-client-cache.

To use it, just set the cache during client construction:

rt org.nuxeo.client.NuxeoClient;

oClient client = new NuxeoClient.Builder()
                                   .url(url).authentication(username, password)
                                   .cache(new ResultCacheInMemory)
                                   .connect();
Customization Errors/Exceptions

The main exception manager for the nuxeo-java-client is org.nuxeo.client.spi.NuxeoClientException and contains:

Testing

The Testing suite or TCK can be found in this project nuxeo-java-client-test.

History

The initial nuxeo-automation-client is now old:

The nuxeo-automation-client was then forked to build a Android version with some caching.

Constraints

JVM & Android

The nuxeo-java-client must works on both a standard JVM and Android Dalvik VM.

Java 7**

Library must work on older Java versions. The goal is to be able to use nuxeo-java-client from application running in Java 7.

Light dependencies

The library should be easy to embed so we want to have as few dependencies as possible. That's why the in memory cache is in a separated module. It depends on Guava which is a heavy library.

Exception Management

Client should be able to retrieve the remote Exception easily and access to the trace feature would be ideal.

Design Principles

JS like

Make the API look like the JS one (Fluent, Promises …)

Retrolambda & Retrofit

Share the http lib between JVM and Android. Allow to use Lambda in the code.

Jackson & Marshaling

By default, the library fasterXML Jackson is used for objects marshalling in nuxeo-java-client.

Several usages:

Caching Interceptors

Goals

If needed, for example on Android, we should be able to easily add caching logic.

How?

All caches should be accessible via a generated cache key defined by the request itself:

How many?

3 caches should be implemented:

Scenarii

Pending questions: Invalidations

—-> What would be a default timeout for each cache?

Potential rules offline:

Synchronisation Potential Stores

Depending on client:

Miscellaneous

Error & Logging

The NuxeoClientException within nuxeo-java-client is consuming the default and the extended rest exception response by the server. Here the documentation

Reporting Issues

We are glad to welcome new developers on this initiative, and even simple usage feedback is great.

About third party libraries

About Nuxeo

Nuxeo dramatically improves how content-based applications are built, managed and deployed, making customers more agile, innovative and successful. Nuxeo provides a next generation, enterprise ready platform for building traditional and cutting-edge content oriented applications. Combining a powerful application development environment with SaaS-based tools and a modular architecture, the Nuxeo Platform and Products provide clear business value to some of the most recognizable brands including Verizon, Electronic Arts, Sharp, FICO, the U.S. Navy, and Boeing. Nuxeo is headquartered in New York and Paris. More information is available at www.nuxeo.com.


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.