Pages

Wednesday, 14 July 2010

Implementing CMIS

Since the first of may the first version of the CMIS spec is finalized. This new standard is ideally suited for Repository-to-Repository (R2R) and Application-to-Repository (A2R) CMS integration. In this post I’ll try to give you a brief overview of the possibilities and focus points concerning a CMIS integration.

The specification

CMIS allows you to communicate with a CMIS compliant CMS via a standard way. The communication can be based on Webs Services or on Restful AtomPub. If you can freely choose between these two technologies I definitely would go for AtomPub because it’s the most complete one (the spec handles AtomPub in almost 100 pages, Web Services takes only 3 pages).

So does a new standard for integrating a CMS solves all your problems/needs? The answer -of course- is No. After reading the spec this is my major concern:

2.1.1.1 Optional Capabilities “...Thus, a repository implementation may not necessarily be able to support all CMIS capabilities. A few CMIS capabilities are therefore “optional” for a repository to be compliant…”

It states clearly that a compliant CMS not necessarily will support the whole spec. Especially if you have the idea to write an implementation which can handle multiple Content Management Systems at once, this could be a pitfall.
Luckily CMIS has a query service available to retrieve the possibilities of a repository, and it is clear, you should first investigate the repository's possibilities (to figure out how much of the spec is implemented) before integrating a CMS via CMIS. You find the information about this in the spec in the following chapter:

2.2.2.2 getRepositoryInfo
Description: Returns information about the CMIS repository, the optional capabilities it supports and its Access Control information if applicable.

Apache Chemistry

Before going into detail on the architecture I would like to introduce another framework to you: Apache Chemistry. if you are a Java, Phyton, PHP or JavaScript developer you have the option to enable your application for CMIS by adding an extra layer based on this framework.
Apache Chemistry provides open source implementations of the Content Management Interoperability Services (CMIS) specification. There is an API available for different languages, but since I’m a Java guy, I will only focus on the Java API.

The Apache Chemistry Java API is named OpenCMIS.

OpenCMIS

OpenCMIS is a collection of Java libraries, frameworks and tools around the CMIS specification and is very complete. The main thing that is lacking for the moment (of course) is documentation. There are three main parts in the OpenCMIS collection: CMIS Client (consisting of Client API and Client Bindings API), OpenCMIS Server Framework and CMIS Browser. In this blog post I will only focus on the CMIS Client.

OpenCMIS Client

As said before the OpenCMIS Client contains two separate API’s: the Client API and the Client Bindings API.
The OpenCMIS Client Bindings API hides the CMIS AtomPub and Web Services bindings and provides an interface that is very similar to the CMIS domain model. The services, operations, parameters, and structures are named after the CMIS domain model and behave as described in the CMIS specification.

The primary objective of the Client Bindings API is to be complete, covering all CMIS operations and extension points. The result is a somewhat clunky interface. The Client API sits on top of the Binding API and exposes a nicer and simpler to use interface. It is the better choice for most applications.

Architectures

A2R (Application-to-Repository)

arch3

A2R (with OpenCMIS)

arch4

R2R (Repository-to-Repository)

arch5

Put it all in practice

Since there’s almost no documentation available at this point I’ll share some code with you. It’s not too hard to get something working but most of the time to get there you have to dive in the source code of OpenCMIS from time to time.
In contrast to the OpenCMIS documentation the CMIS spec however is very complete, and there are a lot of useful examples based on AtomPub.

Another big help is the Alfresco Website, there even is a test site which you can use to test you implementation.

The code
Java imports:
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectId;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.api.SessionFactory;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.enums.BindingType;




Connecting to a repository:

SessionFactory f = SessionFactoryImpl.newInstance();
Map<String, String> parameter = new HashMap<String, String>();

//user credentials
parameter.put(SessionParameter.USER, "admin");
parameter.put(SessionParameter.PASSWORD, "admin");
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
parameter.put(SessionParameter.ATOMPUB_URL,
"http://a.url.be:8888/alfresco/service/cmis");
parameter.put(SessionParameter.REPOSITORY_ID,
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx");

//session locale
parameter.put(SessionParameter.LOCALE_ISO3166_COUNTRY, "BE");
parameter.put(SessionParameter.LOCALE_ISO639_LANGUAGE, "nl");

// create session
Session s = f.createSession(parameter);




Retrieve a folder:

//Retrieve a Folder
ObjectId objectID =
s.createObjectId("workspace://SpacesStore/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
CmisObject CO = s.getObject(objectID);
System.out.println(CO.getName());
System.out.println(CO.getType().getDescription());




Query the repository:

ItemIterable<QueryResult> q =
s.query("SELECT * FROM cmis:document WHERE CONTAINS('RS232') AND " +
"cmis:createdBy = 'admin' AND IN_FOLDER(" +
"'workspace://SpacesStore/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')", true);

int i = 0;
for (QueryResult qr : q) {
System.out.println("-->");
System.out.println(qr.getPropertyById("cmis:objectTypeId").getFirstValue());
System.out.println(qr.getPropertyById("cmis:name").getFirstValue());
System.out.println(qr.getPropertyById("cmis:createdBy").getFirstValue());
System.out.println(qr.getPropertyById("cmis:objectId").getFirstValue());
System.out.println(qr.getPropertyById("cmis:contentStreamFileName")
.getFirstValue());
System.out.println(qr.getPropertyById("cmis:contentStreamMimeType")
.getFirstValue());
System.out.println(qr.getPropertyById("cmis:contentStreamLength")
.getFirstValue());
System.out.println(qr.getPropertyById("cmis:contentStreamId").getFirstValue());
System.out.println("--------------------------------------------------------");
}




Insert a new document:

//Insert new document
try {
File file = new File("C:\\afile.pdf");
Map<String, String> properties = new HashMap<String, String>();
properties.put(PropertyIds.NAME, file.getName());
properties.put(PropertyIds.CHECKIN_COMMENT, "");
properties.put(PropertyIds.OBJECT_TYPE_ID, ObjectType.DOCUMENT_BASETYPE_ID);
ContentStream contentStream = new ContentStreamImpl(file.getName(),
BigInteger.valueOf(file.length()),
new MimetypesFileTypeMap().getContentType(file), new FileInputStream(file));
session.createDocument(properties,
session.createObjectId("workspace://SpacesStore/xxxxx-xxxx-xxxx-xxxx-xxxxxxx"),
contentStream, VersioningState.MAJOR, new ArrayList(), new ArrayList(),
new ArrayList());
} catch (Exception e) {
e.printStackTrace();
}




That’s all for the moment folks.

References

CMIS Specification: http://docs.oasis-open.org/cmis/CMIS/v1.0/cmis-spec-v1.0.pdf
Apache Chemistry: https://cwiki.apache.org/CMIS/index.html
Alfresco: http://cmis.alfresco.com/, http://wiki.alfresco.com/wiki/CMIS