OpenCMIS and Oracle Documents Cloud

Introduction

Using Apache Chemistry, a CMIS server can be built to interact with Oracle Documents Cloud Service. Following the example in the OpenCMIS Server Development Guide, a CMIS server for Oracle Documents Cloud can be deployed on a Weblogic or Java Cloud Service (JCS) instance.

 

Main Article

 

Oracle Documents Cloud Service (DOCS) is an Enterprise File Sync and Share (EFSS) solution that allows for collaboration and access from any device. The use of it as a CMIS server in this example allows use of a Oracle Documents Cloud account as the backend content repository. This can allow for an application that requires a CMIS connection, such as Primavera, to create folders and files in DOCS that could be further shared out to other DOCS users on their mobile or tablet devices. The possibility of CMIS and DOCS working together can lead to other interesting use cases, since DOCS is available on any device or browser.

This article will discuss how a CMIS server could be built using the DOCS REST API. The Apache Chemistry OpenCMIS development guide provides the key infrastructure for creating a CMIS server, while the DOCS REST API can be used for the backend interactions.

 

Mappings Between DOCS Objects and CMIS Objects

Properties of folders and files have many similarities between CMIS and DOCS. The REST API for DOCS provides parallels to the CMIS properties that can be mapped directly into CMIS objects. One of the duties of a CMIS server is to provide meaningful metadata in the CMIS world for clients to consume. In the table below, the REST attributes returned from DOCS are shown in the first column and the other columns show how that DOCS property fits into the CMIS folder object type.

Folder Properties

The cmis:objectId property is a natural fit for the DOCS folder id. Many of the properties are direct mappings from DOCS to CMIS, but a few must be derived. The cmis:path would currently have to be constructed from a folder’s parents recursively, since DOCS does not have a path associated with a folder. However, the DOCS folder id is the key used to access a folder, not a path. Some CMIS fields would not have a direct mapping to a DOCS attribute at this time, such as cmis:changetoken and cmis:secondaryObjectIds.

The following folder properties from DOCS to CMIS can be mapped:

 

DOCS Folder REST Attribute Id Type Example Value
id cmis:objectId id [“F5F01D87727350F4BF624ABF15BE5DEA38AC513C7430”]
name cmis:name string [“test”]
createdBy->displayName cmis:createdBy string [“Tom Tester”]
modifiedBy->displayName cmis:lastModifiedBy string [“Tom Tester”]
createdTime cmis:creationDate datetime [2015-10-06 20:53:55 -0700]
modifiedTime cmis:lastModificationDate datetime [2015-10-06 20:54:04 -0700]
description cmis:description string []
type cmis:baseTypeId id [“cmis:folder”]
type cmis:objectTypeId id [“cmis:folder”]
Constructed from name and parent folder names cmis:path string [“/Marketing/Assets”]
parentID cmis:parentId id [“self”]

 

In a prototype, the CMIS workbench shows this in a better display when selecting a folder in the tree navigation. The REST attributes from DOCS appear in CMIS attributes.

folder-object

 

Document Properties

A Document object type in CMIS is the equivalent of a File object type in DOCS terminology. Like folders, many of the CMIS document values can be populated from the REST responses from DOCS.

The following document properties from DOCS to CMIS can be mapped:

DOCS Folder REST Attribute Id Type Value
id cmis:objectId id [“DE484E3FA0F3961FBED7551715BE5DEA38AC513C7430”]
name cmis:name string [“barcode.doc”]
createdBy->displayName cmis:createdBy string [“Tom Tester”]
modifiedBy->displayName cmis:lastModifiedBy string [“Tom Tester”]
createdTime cmis:creationDate datetime [2015-10-06 20:49:37 -0700]
modifiedTime cmis:lastModificationDate datetime [2015-10-06 20:49:37 -0700]
description cmis:description string []
type cmis:baseTypeId id [“cmis:document”]
type cmis:objectTypeId id [“cmis:document”]
version cmis:versionLabel string [“1”]
id cmis:versionSeriesId id [“DE484E3FA0F3961FBED7551715BE5DEA38AC513C7430”]
size cmis:contentStreamLength integer [27136]
Derived from extension on name attribute cmis:contentStreamMimeType string [“application/msword”]
name cmis:contentStreamFileName string [“barcode.doc”]

 

 

In a prototype of a DOCS CMIS server, the CMIS workbench shows what fields are mapped when a document is selected in the navigation tree. The above mappings shows DOCS attributes appearing in the CMIS workbench.

document-object

 

REST Client

A key toward linking the OpenCMIS development guide sample and DOCS is creating a Java REST client. This can be done using Jersey or Apache CXF. Defining POJOs to handle the REST responses is helpful in parsing the responses returned from DOCS.

The client can be created in various ways. One is by using the WADL file for the DOCS REST API, in which JDeveloper can automatically generate a client interface for the entire DOCS REST API. Another method is to create your own class. The snippet below shows use of Jersey to create a client. The client must define the methods that are needed for CMIS to call, which includes most of the folders and files REST calls in DOCS. A sample below shows a GET Personal Workspace REST request, which returns a POJO named “PersonalWorkspace”. A second method does a POST to create or upload a file into DOCS. A set of POJOs can be created to handle the responses that Documents Cloud returns.

    public RestClient(String docshostname, String username, String password) {
        super();
        m_client = getClient();
        
        String authString = username + ":" + password;

        try {
            authString = DatatypeConverter.printBase64Binary(authString.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        m_auth = authString;
        m_hostname = docshostname;
        m_restBaseUrl = m_hostname + m_restResourceStart;
    }

    public static Client getClient() {
        ClientConfig cc = new DefaultClientConfig();
        cc.getClasses().add(MultiPartWriter.class);
        cc.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
        return Client.create(cc);
    }
    
    public PersonalWorkspace getPersonalWorkspace(String orderby, int limit, int offset) throws Exception {
        String restPath = "folders/items"; 
        String fullRestUrl = m_restBaseUrl + restPath;
        
        //Set defaults for orderby, limit, and offset
        if(orderby == null){
            orderby="name:asc";
        }
        
        if(limit <= 0){
            limit = 50;
        }
        
        if(offset <= 0){
           offset = 0; 
        }
        
        fullRestUrl = fullRestUrl + "?orderby=" + orderby + "&limit=" + limit + "&offset=" + offset;
        WebResource webResource = m_client.resource(fullRestUrl);
        ClientResponse response =
            webResource.header("Authorization", "Basic " + m_auth).get(ClientResponse.class);
        
        PersonalWorkspace pw = null;
        if (response.getStatus() != 200) {                      
            throw new Exception("Failed calling REST GET folder in method getPersonalWorkspace, URL was: " + fullRestUrl + ": \nHTTP error code: " + response.getStatus() + "\nStatus Info: " +
                                       response.getStatusInfo() + "Response was: " + response.getEntity(String.class));
        }
        else{
            pw = response.getEntity(PersonalWorkspace.class);
        }
        return pw;
    }

    public Item <strong>postCreateFile</strong>(String parentFolder, String primaryFile, InputStream is) throws Exception {
        if (primaryFile == null){
            throw new Exception("postCreateFile: primaryFile cannot be null when creating a new file in DOCS.");
        }            
        String restPath = "files/data"; 
        String fullRestUrl = m_restBaseUrl + restPath;
        
        WebResource webResource = m_client.resource(fullRestUrl);
        
        String jsonPayload = "{\"parentID\":\"" + parentFolder + "\"}";
        //Add the jsonInputParameters to the multi part form
        FormDataMultiPart formDataMultiPart = new FormDataMultiPart();
        FormDataBodyPart fdpjson = new FormDataBodyPart("jsonInputParameters", jsonPayload);
        formDataMultiPart.bodyPart(fdpjson);

        //Add the file contents to the multi part form        
        StreamDataBodyPart sdp = new StreamDataBodyPart("primaryFile", is, primaryFile);
        formDataMultiPart.bodyPart(sdp);

        ClientResponse response =
            webResource.header("Authorization", "Basic " + m_auth).type(MediaType.MULTIPART_FORM_DATA).post(ClientResponse.class, formDataMultiPart);
        
        if (response.getStatus() != 200 &amp;&amp; response.getStatus() != 201) {
            throw new Exception("Failed calling REST post create file in method postCreateFile, URL was: " + fullRestUrl + ": \nHTTP error code: " + response.getStatus() + "\nStatus Info: " +
                                       response.getStatusInfo() + "Response was: " + response.getEntity(String.class));
        }
        return response.getEntity(Item.class);            
    }

 

DOCS CMIS Server

Using the FileBridge demo as an example, create classes for an Oracle DOCS CMIS Server, and define the behaviors to work with the REST API instead of a file system. Creating a CMIS project from scratch can generate class names for you and include all of the dependencies. See the section in the OpenCMIS development guide titled “Creating a new server project from scratch (Optional)”.

Once the project is generated, the CMIS methods can be written to interact with the DOCS REST client. For example, the CMIS createDocument method takes an InputStream and uploads it to the repository. In this case using the REST client can perform the task.

    /**
     * CMIS createDocument.
     */ 
    public String createDocument(CallContext context, Properties properties, String folderId,
            ContentStream contentStream, VersioningState versioningState) {
        checkUser(context, true);

        if (VersioningState.NONE != versioningState) {
            throw new CmisConstraintException("Versioning not supported!");
        }

        // check properties
        checkNewProperties(properties, BaseTypeId.CMIS_DOCUMENT);

        // check the file
        String name = OracleDocsUtils.getStringProperty(properties, PropertyIds.NAME);
        
        Item item = null;
        try {
            item = <strong>docsRestClient.postCreateFile(folderId, name, contentStream.getStream());</strong>
        } catch (Exception e) {
            e.printStackTrace();
            LOG.severe(e.getMessage());
        }
        return item.getId();
    }

 

Apache Chemistry Links and Information

Chemistry Home Page: http://chemistry.apache.org/

Older version: How to create a CMIS server using OpenCMIS: https://chemistry.apache.org/java/how-to/how-to-create-server.html

Newer version: OpenCMIS Server Development Guide on GitHub: https://github.com/cmisdocs/ServerDevelopmentGuide

This CMIS server was based on the newer version of the CMIS server development guide.

WLS 12c Deployment Notes

The development guide builds a sample server based on a file system, called the FileBridge project. The model provides a starting point for building a custom CMIS server.

The guide contains instructions on using Maven to create a base project that contains the necessary jar files, which can then be imported to Eclipse (or JDeveloper). The FileBridge demo server can be deployed to a Weblogic instance as a war file or an ear. One note is that the version of JAX-WS that the sample uses is not the same version as Weblogic 12c. This is only an issue if you intend to use the Web Services binding. This does not effect the AtomPub or browser binding. However, if using WLS 12c and the need for the web services binding is needed, deploy the project as an ear file, with the weblogic-application.xml and weblogic.xml files below.

weblogic-application.xml

A weblogic-application.xml file is needed to deploy successfully on WLS 12c. This is due to the JAX-WS version that OpenCMIS prefers to use is older than what WLS ships in 12c. This is not an issue in WLS 10.3.6 but is in 12c.  The workaround in 12c is to override the JAX-WS stack bundled with WLS 12c and use the one that Apache Chemistry OpenCMIS Server requires. The only reason the CMIS server needs to be an EAR is to handle this need, otherwise in WLS 10.3.6 it can be deployed as a WAR and a weblogic-application.xml file is not needed.

 

 <?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-application http://xmlns.oracle.com/weblogic/weblogic-application/1.5/weblogic-application.xsd"
                      xmlns="http://xmlns.oracle.com/weblogic/weblogic-application">
  <prefer-application-packages>
    <package-name>com.ctc.*</package-name>
    <package-name>com.sun.xml.*</package-name>
    <package-name>com.sun.istack.*</package-name>
    <package-name>com.sun.msv.datatype.*</package-name>
    <package-name>com.sun.msv.driver.*</package-name>
    <package-name>com.sun.msv.grammar.*</package-name>
    <package-name>com.sun.msv.reader.*</package-name>
    <package-name>com.sun.msv.relaxns.*</package-name>
    <package-name>com.sun.msv.scanner.*</package-name>
    <package-name>com.sun.msv.util.*</package-name>
    <package-name>com.sun.msv.verifier.*</package-name>
    <package-name>com.sun.msv.writer.*</package-name>
    <package-name>com.sun.org.apache.xml.internal.*</package-name>
    <package-name>com.sun.wsit.*</package-name>
    <package-name>javax.xml.activation.*</package-name>
    <package-name>javax.xml.annotation.*</package-name>
    <package-name>javax.xml.mail.*</package-name>
    <package-name>javax.xml.security.*</package-name>
    <package-name>javax.xml.registry.*</package-name>
    <package-name>javax.xml.rpc.*</package-name>
    <package-name>javax.xml.crypto.*</package-name>
    <package-name>org.apache.xerces.*</package-name>
    <package-name>javanet.staxutils.*</package-name>
    <package-name>jp.gr.xml.*</package-name>
    <package-name>org.codehaus.stax2.*</package-name>
    <package-name>org.glassfish.gmbal.*</package-name>
    <package-name>org.iso_relax.*</package-name>
    <package-name>org.jcp.xml.dsig.*</package-name>
    <package-name>org.jvnet.*</package-name>
    <package-name>org.relaxng.*</package-name>
  </prefer-application-packages>
</weblogic-application>

 

weblogic.xml

 

A weblogic.xml descriptor is needed to set prefer-web-inf-classes to true.

<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.5/weblogic-web-app.xsd"
                  xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app">
  <container-descriptor>
    <prefer-web-inf-classes>true</prefer-web-inf-classes>
  </container-descriptor>         
</weblogic-web-app>

 

How to test a custom CMIS Server

For testing, the Chemistry workbench is a useful tool that can be run on Linux or Windows. Download the CMIS Workbench from the Apache site. This is an application for connecting to a CMIS server and interacting with it.

http://chemistry.apache.org/java/developing/tools/dev-tools-workbench.html

Unzip the workbench and run it using workbench.bat (Windows) or workbench.sh (Linux).

 

 

Add Your Comment