CMIS (Content Management Interoperability Services) is a standard for web service API to CMS/WCM/DMS/ECM (Content Management System / Web Content Management / Document Management System / Enterprise Content Management).
The standard is managed by OASIS. Version 1.0 was released in 2010 and version 1.1 was released in 2012.
CMIS supports 3 protocols:
CMIS is supported by a large number of CMS/WCM/DMS/ECM including:
CMIS client libraries are available for many programming languages including:
CMIS data model is very simple.
Basically it just consists of:
In other words a virtual file system.
Let us first try and explore a CMIS repository.
Java examples will use Apache Chemistry client library.
package cmisdemo;
import java.util.HashMap;
import java.util.Map;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.enums.BindingType;
public class Explore {
private static void list(String indent, Folder dir, boolean recurse, boolean showprops) {
for(CmisObject item : dir.getChildren()) {
System.out.printf("%s%s (%s)\n", indent, item.getName(), item.getType().getLocalName());
if(showprops) {
for(Property<?> p : item.getProperties()) {
if(p.getValue() != null) {
System.out.printf("%s- %s = %s\n", indent, p.getLocalName(), p.getValue());
}
}
}
if(recurse && item instanceof Folder) {
list(indent + " ", (Folder)item, recurse, showprops);
}
}
}
public static void test(Map<String, String> param, boolean recurse, boolean showprops) {
System.out.println(param);
Session ses = SessionFactoryImpl.newInstance().createSession(param);
// server info
System.out.println(ses.getRepositoryInfo().getName());
System.out.println(ses.getRepositoryInfo().getDescription());
System.out.println(ses.getRepositoryInfo().getProductName());
System.out.println(ses.getRepositoryInfo().getProductVersion());
// list items
list("", ses.getRootFolder(), recurse, showprops);
}
public static void main(String[] args) {
// Chemistry - AtomPub
Map<String, String> chemistry_atompub = new HashMap<String, String>();
chemistry_atompub.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
chemistry_atompub.put(SessionParameter.ATOMPUB_URL, "http://localhost:8080/chemistry/atom");
chemistry_atompub.put(SessionParameter.REPOSITORY_ID, "test");
chemistry_atompub.put(SessionParameter.USER, "test");
chemistry_atompub.put(SessionParameter.PASSWORD, "test");
test(chemistry_atompub, true, true);
// Chemistry - SOAP
Map<String, String> chemistry_soap = new HashMap<String, String>();
chemistry_soap.put(SessionParameter.BINDING_TYPE, BindingType.WEBSERVICES.value());
chemistry_soap.put(SessionParameter.WEBSERVICES_ACL_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_DISCOVERY_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_MULTIFILING_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_NAVIGATION_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_OBJECT_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_POLICY_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_RELATIONSHIP_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_REPOSITORY_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.WEBSERVICES_VERSIONING_SERVICE, "http://localhost:8080/chemistry/services/cmis?wsdl");
chemistry_soap.put(SessionParameter.REPOSITORY_ID, "test");
chemistry_soap.put(SessionParameter.USER, "test");
chemistry_soap.put(SessionParameter.PASSWORD, "test");
test(chemistry_soap, true, true);
// OpenCMS - AtomPub
Map<String, String> opencms_atompub = new HashMap<String, String>();
opencms_atompub.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
opencms_atompub.put(SessionParameter.ATOMPUB_URL, "http://localhost:8080/opencms/cmisatom");
opencms_atompub.put(SessionParameter.REPOSITORY_ID, "cmis-online");
opencms_atompub.put(SessionParameter.USER, "Admin");
opencms_atompub.put(SessionParameter.PASSWORD, "admin");
test(opencms_atompub, false, false);
// OpenCMS - SOAP
Map<String, String> opencms_soap = new HashMap<String, String>();
opencms_soap.put(SessionParameter.BINDING_TYPE, BindingType.WEBSERVICES.value());
opencms_soap.put(SessionParameter.WEBSERVICES_ACL_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_DISCOVERY_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_MULTIFILING_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_NAVIGATION_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_OBJECT_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_POLICY_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_RELATIONSHIP_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_REPOSITORY_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.WEBSERVICES_VERSIONING_SERVICE, "http://localhost:8080/opencms/services/cmis?wsdl");
opencms_soap.put(SessionParameter.REPOSITORY_ID, "cmis-online");
opencms_soap.put(SessionParameter.USER, "Admin");
opencms_soap.put(SessionParameter.PASSWORD, "admin");
test(opencms_soap, false, false);
}
}
Several CMIS client libraries exists for .NET including:
Examples will use DotCMIS.
using System;
using System.Collections.Generic;
using DotCMIS;
using DotCMIS.Client;
using DotCMIS.Client.Impl;
namespace CMISDemo.Explore
{
public class Program
{
private static void List(string indent, IFolder dir, bool recurse, bool showprops)
{
foreach(ICmisObject item in dir.GetChildren()) {
Console.WriteLine("{0}{1} ({2})", indent, item.Name, item.ObjectType.LocalName);
if(showprops)
{
foreach(IProperty p in item.Properties)
{
if(p.Value != null)
{
Console.WriteLine("{0}- {1} = {2}", indent, p.LocalName, p.Value);
}
}
}
if(recurse && item is IFolder)
{
List(indent + " ", (IFolder)item, recurse, showprops);
}
}
}
public static void Test(IDictionary<string, string> param, bool recurse, bool showprops)
{
Console.WriteLine(param);
ISession ses = SessionFactory.NewInstance().CreateSession(param);
// server info
Console.WriteLine(ses.RepositoryInfo.Name);
Console.WriteLine(ses.RepositoryInfo.Description);
Console.WriteLine(ses.RepositoryInfo.ProductName);
Console.WriteLine(ses.RepositoryInfo.ProductVersion);
// list items
List("", ses.GetRootFolder(), recurse, showprops);
}
public static void Main(string[] args)
{
// Chemistry - AtomPub
IDictionary<string, string> chemistry_atompub = new Dictionary<string, string>();
chemistry_atompub[SessionParameter.BindingType] = BindingType.AtomPub;
chemistry_atompub[SessionParameter.AtomPubUrl] = "http://localhost:8080/chemistry/atom";
chemistry_atompub[SessionParameter.RepositoryId] = "test";
chemistry_atompub[SessionParameter.User] = "test";
chemistry_atompub[SessionParameter.Password] = "test";
Test(chemistry_atompub, true, true);
// Chemistry - SOAP
IDictionary<string, string> chemistry_soap = new Dictionary<string, string>();
chemistry_soap[SessionParameter.BindingType] = BindingType.WebServices;
chemistry_soap[SessionParameter.WebServicesAclService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesDiscoveryService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesMultifilingService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesNavigationService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesObjectService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesPolicyService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesRelationshipService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesRepositoryService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.WebServicesVersioningService] = "http://localhost:8080/chemistry/services/cmis?wsdl";
chemistry_soap[SessionParameter.RepositoryId] = "test";
chemistry_soap[SessionParameter.User] = "test";
chemistry_soap[SessionParameter.Password] = "test";
Test(chemistry_soap, true, true);
// OpenCMS - AtomPub
IDictionary<string, string> opencms_atompub = new Dictionary<string, string>();
opencms_atompub[SessionParameter.BindingType] = BindingType.AtomPub;
opencms_atompub[SessionParameter.AtomPubUrl] = "http://localhost:8080/opencms/cmisatom";
opencms_atompub[SessionParameter.RepositoryId] = "cmis-online";
opencms_atompub[SessionParameter.User] = "Admin";
opencms_atompub[SessionParameter.Password] = "admin";
Test(opencms_atompub, false, false);
// OpenCMS - SOAP
IDictionary<string, string> opencms_soap = new Dictionary<string, string>();
opencms_soap[SessionParameter.BindingType] = BindingType.WebServices;
opencms_soap[SessionParameter.WebServicesAclService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesDiscoveryService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesMultifilingService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesNavigationService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesObjectService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesPolicyService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesRelationshipService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesRepositoryService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.WebServicesVersioningService] = "http://localhost:8080/opencms/services/cmis?wsdl";
opencms_soap[SessionParameter.RepositoryId] = "cmis-online";
opencms_soap[SessionParameter.User] = "Admin";
opencms_soap[SessionParameter.Password] = "admin";
Test(opencms_soap, false, false);
//
Console.ReadKey();
}
}
}
libcmis and libcmis3 modules are available via pypi.
There seems to be some problems with Python 3 and libcmis3.
from cmislib.model import CmisClient
def list(indent, dir, recurse, showprops):
for item in dir.getChildren():
typ = item.getProperties()['cmis:objectTypeId'][5:]
print('%s%s (%s)' % (indent, item.name, typ))
if showprops:
props = item.getProperties()
for p in props:
print('%s- %s = %s' % (indent, p, props[p]))
if recurse and typ == 'folder':
list(indent + ' ', item, recurse, showprops)
def test(url, usr, pwd, reponam, recurse, showprops):
client = CmisClient(url, usr, pwd)
repo = client.getRepository(reponam)
repo._cmisClient = client # hack to work around a known bug
print(repo.getRepositoryInfo()['repositoryName'])
print(repo.getRepositoryInfo()['repositoryDescription'])
print(repo.getRepositoryInfo()['productName'])
print(repo.getRepositoryInfo()['productVersion'])
list('', repo.getRootFolder(), recurse, showprops)
test('http://localhost:8080/chemistry/atom', 'test', 'test', 'test', True,True)
test('http://localhost:8080/opencms/cmisatom', 'Admin', 'admin', 'cmis-online', False, False)
Several CMIS client libraries exists for PHP including:
The examples will use Apache Chemistry client.
For production usage I would look at one of the alternatives.
<?php
include 'cmis_service.php';
function listf($indent, $client, $id, $recurse, $showprops) {
foreach($client->getChildren($id)->objectList as $item) {
$typ = substr($item->properties['cmis:objectTypeId'], 5);
echo sprintf("%s%s (%s)\r\n", $indent, $item->properties['cmis:name'], $typ);
if($showprops) {
$props = $item->properties;
foreach($props as $pk => $pv) {
echo sprintf("%s- %s = %s\r\n", $indent, $pk, $pv);
}
}
if($recurse && $typ == 'folder') {
listf($indent . ' ', $client, $item->properties['cmis:objectId'], $recurse, $showprops);
}
}
}
function test($url, $usr, $pwd, $reponam, $recurse, $showprops) {
$client = new CMISService($url, $usr, $pwd);
echo $client->getRepositoryInfo()->repositoryInfo['cmis:repositoryName'] . "\r\n";
echo $client->getRepositoryInfo()->repositoryInfo['cmis:repositoryDescription'] . "\r\n";
echo $client->getRepositoryInfo()->repositoryInfo['cmis:productName'] . "\r\n";
echo $client->getRepositoryInfo()->repositoryInfo['cmis:productVersion'] . "\r\n";
listf('', $client, $client->getObjectByPath('/')->id, $recurse, $showprops);
}
test('http://localhost:8080/chemistry/atom', 'test', 'test', 'test', true, true);
test('http://localhost:8080/opencms/cmisatom', 'Admin', 'admin', 'cmis-online', false, false)
?>
Now let us do some basic CRUD operations.
Java examples will use Apache Chemistry client library.
package cmisdemo;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.enums.BindingType;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
// Note: paths are actually implementation specific but /cmis:name/cmis:name/cmis:name is common
public class CRUD {
private static void dump(Session ses, String path) throws IOException {
try {
Document f = (Document) ses.getObjectByPath(path);
for(Property<?> p : f.getProperties()) {
if(p.getValue() != null) {
System.out.printf("%s = %s\n", p.getLocalName(), p.getValue());
}
}
ContentStream stm = f.getContentStream();
BufferedReader br = new BufferedReader(new InputStreamReader(stm.getStream()));
String line;
while((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
} catch (CmisObjectNotFoundException ex) {
System.out.println(path + " does not exist");
}
}
public static void main(String[] args) throws Exception {
Map<String, String> param = new HashMap<String, String>();
param.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
param.put(SessionParameter.ATOMPUB_URL, "http://localhost:8080/chemistry/atom");
param.put(SessionParameter.REPOSITORY_ID, "test");
param.put(SessionParameter.USER, "test");
param.put(SessionParameter.PASSWORD, "test");
Session ses = SessionFactoryImpl.newInstance().createSession(param);
// read
dump(ses, "/RW/demo.txt");
// create
String s = "A\r\nBB\r\nCCC\r\n";
Folder dir = (Folder) ses.getObjectByPath("/RW");
Map<String, Object> props = new HashMap<String, Object>();
props.put(PropertyIds.NAME, "demo.txt");
props.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
ContentStream stm = ses.getObjectFactory().createContentStream("demo.txt", s.length(), "text/plain", new ByteArrayInputStream(s.getBytes()));
try {
dir.createDocument(props, stm, VersioningState.NONE);
} catch(CmisNameConstraintViolationException ex) {
System.out.println("demo.txt did already exist");
}
// read
dump(ses, "/RW/demo.txt");
// delete
ses.getObjectByPath("/RW/demo.txt").delete(true);
// read
dump(ses, "/RW/demo.txt");
}
}
Several CMIS client libraries exists for .NET including:
Examples will use DotCMIS.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using DotCMIS;
using DotCMIS.Client;
using DotCMIS.Client.Impl;
using DotCMIS.Data;
using DotCMIS.Enums;
using DotCMIS.Exceptions;
namespace CMISDemo.CRUD
{
public class Program
{
private static void Dump(ISession ses, string path)
{
try
{
Document f = (Document) ses.GetObjectByPath(path);
foreach(Property p in f.Properties)
{
if(p.Value != null)
{
Console.WriteLine("{0} = {1}", p.LocalName, p.Value);
}
}
IContentStream stm = f.GetContentStream();
StreamReader sr = new StreamReader(stm.Stream);
string line;
while((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
sr.Close();
}
catch (CmisObjectNotFoundException)
{
Console.WriteLine(path + " does not exist");
}
}
public static void Main(string[] args)
{
IDictionary<string, string> param = new Dictionary<string, string>();
param[SessionParameter.BindingType] = BindingType.AtomPub;
param[SessionParameter.AtomPubUrl] = "http://localhost:8080/chemistry/atom";
param[SessionParameter.RepositoryId] = "test";
param[SessionParameter.User] = "test";
param[SessionParameter.Password] = "test";
ISession ses = SessionFactory.NewInstance().CreateSession(param);
// read
Dump(ses, "/RW/demo.txt");
// create
string s = "A\r\nBB\r\nCCC\r\n";
Folder dir = (Folder) ses.GetObjectByPath("/RW");
IDictionary<String, Object> props = new Dictionary<String, Object>();
props[PropertyIds.Name] = "demo.txt";
props[PropertyIds.ObjectTypeId] = "cmis:document";
IContentStream stm = ses.ObjectFactory.CreateContentStream("demo.txt", s.Length, "text/plain", new MemoryStream(Encoding.Default.GetBytes(s)));
try
{
dir.CreateDocument(props, stm, VersioningState.None);
}
catch(CmisNameConstraintViolationException)
{
Console.WriteLine("demo.txt did already exist");
}
// read
Dump(ses, "/RW/demo.txt");
// delete
ses.GetObjectByPath("/RW/demo.txt").Delete(true);
// read
Dump(ses, "/RW/demo.txt");
//
Console.ReadKey();
}
}
}
libcmis and libcmis3 modules are available via pypi.
There seems to be some problems with Python 3 and libcmis3.
from cmislib.model import CmisClient
from cmislib.exceptions import ObjectNotFoundException
from cmislib.exceptions import NameConstraintViolationException
def dump(repo, path):
try:
f = repo.getObjectByPath(path)
props = f.getProperties()
for p in props:
print('%s = %s' % (p, props[p]))
stm = f.getContentStream()
print(stm.read())
stm.close()
except ObjectNotFoundException:
print(path + ' does not exist')
client = CmisClient('http://localhost:8080/chemistry/atom', 'test', 'test')
repo = client.getRepository('test')
repo._cmisClient = client # hack to work around a known bug
# read
dump(repo, '/RW/demo.txt')
# create
s = 'A\r\nBB\r\nCCC\r\n'
dir = repo.getObjectByPath('/RW');
try:
dir.createDocumentFromString('demo.txt', contentType='text/plain', contentString=s) # broken i Python 3.x and cmslib3
except NameConstraintViolationException:
print('demo.txt did already exist')
# read
dump(repo, '/RW/demo.txt');
# delete
repo.getObjectByPath('/RW/demo.txt').delete()
# read
dump(repo, '/RW/demo.txt')
Several CMIS client libraries exists for PHP including:
The examples will use Apache Chemistry client.
For production usage I would look at one of the alternatives.
<?php
include 'cmis_service.php';
function dump($client, $path) {
try {
$f = $client->getObjectByPath($path);
$props = $f->properties;
foreach($props as $pk => $pv) {
echo sprintf("%s = %s\r\n", $pk, $pv);
}
} catch(CmisObjectNotFoundException $ex) {
echo "$path does not exist\r\n";
}
}
$client = new CMISService('http://localhost:8080/chemistry/atom', 'test', 'test');
// read
dump($client, "/RW/demo.txt");
// create
$s = "A\r\nBB\r\nCCC\r\n";
$dir = $client->getObjectByPath("/RW");
try {
$client->createDocument($dir->properties['cmis:objectId'], 'demo.txt', array(), $s, 'text/plain');
} catch(CmisConstraintException $ex) {
echo "demo.txt did already exist\r\n";
}
// read
dump($client, "/RW/demo.txt");
// delete
$client->deleteObject($client->getObjectByPath("/RW/demo.txt")->properties['cmis:objectId']);
// read
dump($client, "/RW/demo.txt");
?>
CMIS defines a query language that allows SQL like queries:
Not all CMIS servers has implemented queries, so do not assume queries will work.
CMIS also supports versions, so it has operations:
Again be careful to check the versioning capabilities of your specific CMIS server before assuming all functionality is available.
JCR (Java Content Repository) and PHPCR (PHP Content Repository) provide somewhat similar functionality to CMIS.
The differences include:
CMIS | JCR/PHPCR |
---|---|
protocol | API |
always remote access (HTTP) | remote access (any network protocol) or local access (call) |
both application-repository and repository-repository | only application-repository |
high level | low level |
intended for full documents | intended for small fragments |
Version | Date | Description |
---|---|---|
1.0 | April 11th 2021 | Initial version |
See list of all articles here
Please send comments to Arne Vajhøj