apache chemistry: the alfresco open source implementation of cmis
DESCRIPTION
Learn how to make CMIS simple for Java client and server developers. Apache Chemistry is the umbrella project for all CMIS (Content Management Interoperability Services) related projects within the Apache Software Foundation and OpenCMIS is a collection of Java libraries, frameworks and tools around CMIS specification.TRANSCRIPT
Agenda
• A quick CMIS tour
• Introduction to Apache Chemistry
• OpenCMIS demos
• Outlook: CMIS, Apache Chemistry and Alfresco
• Tech Talk: Alfresco CMIS Online webinar, Wednesday, 3 August 2011
Content Management Interoperability Services
Client
Content Repository
Services
Domain Model
read write
co
nsu
me
r p
rovid
er
Vendor Mapping
CMIS lets you read, search, write, update, delete, version, control, … content and metadata!
CMIS Use Cases
Client
Content Repository
Content Repository
Content Repository Client
Content Repository Content
Repository Content Repository
• Collaborative Content Creation • Portals • Client Application Integration • Mashup
CMIS Base Types
Document • Content • Renditions • Version History
Folder • Container • Hierarchy • Filing
Relationship • Source Object • Target Object
Policy • Target Object
Described by Type Definitions
Type Definitions
*
Custom Type
Object • Type Id • Parent • Display Name • Queryable • Controllable
Document • Versionable • Allow Content
Folder Relationship • Source Types • Target Types
Policy
Property • Property Id • Display Name • Type • Required • Default Value • …
Base Properties
String! cmis:policyText !
String! cmis:name ! ID! cmis:objectId! ID! cmis:baseTypeId ! ID! cmis:objectTypeId ! String! cmis:createdBy ! DateTime! cmis:creationDate! String! cmis:lastModifiedBy ! DateTime! cmis:lastModificationDate! String! cmis:changetoken!
Boolean! cmis:isImmutable ! Boolean! cmis:isLatestVersion! Boolean! cmis:isMajorVersion! Boolean! cmis:isLatestMajorVersion! String! cmis:versionLabel! ID! cmis:versionSeriesId ! Boolean! cmis:isVersionSeriesCheckedOut ! String! cmis:versionSeriesCheckedOutBy ! ID! cmis:versionSeriesCheckedOutId ! String! cmis:checkinComment! Integer! cmis:contentStreamLength! String! cmis:contentStreamMimeType ! String! cmis:contentStreamFileName ! ID! cmis:contentStreamId !
ID! cmis:parentId! String! cmis:path ! ID (multi)! cmis:allowedChildObjectTypeIds !
ID! cmis:sourceId ! ID! cmis:targetId !
All objects Documents
Folders
Relationships Policies
Content Streams
* Document • Mime Type • Length
Content Stream • Stream Id
Rendition • Kind • Mime Type
* Maximum length is repository specific
Access Control
Object ACL
ACE • Principal • Permissions • Direct
Permissions cmis:read cmis:write cmis:all <repository specific>
Document Versioning
V1 Minor
V2 Major
Ve
rsion
Serie
s
Document
PWC
checkout
checkin
Repository Vendor May Support • Version Specific Folder Filing • Query All Versions • Create Minor, Major, PWCs
V3 Major
Policies
* Note: Optional Capability
Repository specific Policies
Client
Document
Retention Security …
apply
Change Log
Create Update Delete
Change Events
Client logs discover
Repo
Change Event: • Object Id • Change Time • Change Type – created, updated, deleted, security • Properties – for updated events
* Note: Optional Capability
CMIS Query Language
• SELECT and FROM clauses o Identify which properties from which types to return
• WHERE clause o Restrict returned rows to those that meet all constraints
o Predicates: comparison, in, like, null, any, is null o Function Predicates: contains(), in_folder(), in_tree() o Operators: and, or, not
• ORDER BY clause o Order results by one or more columns
• Each Type is projected as a Table
Do You Understand These Queries?
SELECT cmis:name, cmis:lastModificationDate FROM cmis:document WHERE cmis:lastModifiedBy = 'admin' ORDER BY cmis:lastModificationDate DESC
SELECT cmis:name FROM cmis:document WHERE contains('alfresco')
SELECT cmis:name, cmis:contentStreamLength FROM cmis:document WHERE IN_TREE('<folderObjectId>') AND cmis:contentStreamLength > 100000 ORDER BY cmis:contentStreamLength
CMIS Services
Browse Inspect
Repository - Get Server Information - Get Type Definitions
Navigation - Walk Folder / Doc Hierarchy - Get Checked-out
Discovery - Issue Query - Get Change Log
Object - Read Content - Get Properties - Get ACLs - Get Allowable Actions
Versioning - Walk Version History
Relationship - Traverse Relationship(s)
Policy - Get Applied Policies
Act
Object - Write Content - Set Properties - Create Folder / Doc / Relation - Delete - Move - Set ACLs
Versioning - Check-out / In - Cancel Check-out - Delete Version(s)
Policy - Apply / Remove
CMIS Bindings
• CMIS 1.0 defines two bindings: o AtomPub binding o Web Services binding
• CMIS 1.1 will add a new binding: o Browser binding (JSON)
• Don’t bother … There are Open Source libraries for that
Apache Chemistry
Open Source implementations of
• Apache Chemistry is the umbrella project for all CMIS related projects within the Apache Software Foundation.
• Apache Chemistry provides libraries and frameworks for Java, Python, PHP and .NET.
• Website: http://chemistry.apache.org
TM
Main Objective
• Decreased learning curve: Developers can learn just the CMIS domain model and ignore the transport details of all the binding implementations.
• Rapid start. From download to listing the first folder in minutes.
• Be compliant. Chemistry libraries have been tested against many, many repositories.
Developers should focus on the CMIS domain model!
Apache Chemistry
Application
Libraries (ORM, connection pools, etc.)
ODBC / JDBC
SQL
Relational DB Content Repository
CMIS Domain Model
Apache Chemistry
CMIS Bindings
Subprojects
• OpenCMIS (Java, server and client) o very mature o well tested against all major servers
• cmislib (Python, client)
o mature o well tested against most major servers
• phpclient (PHP, client) o basic specification coverage o used in a few production systems
• DotCMIS (.NET, client)
o an OpenCMIS port (same architecture, similar API) o works against all tested servers but needs more testing
History
May 2009: Started as an incubator project by Nuxeo and Day (now Adobe)
February 2010: Metaversant contributed cmislib
February 2010: Alfresco, OpenText and SAP contributed OpenCMIS
May 2010: Alfresco contributed phpclient
January 2011: Alfresco contributed DotCMIS
February 2011: Graduated to a top level project
OpenCMIS
OpenCMIS consists of a collection of Java libraries, frameworks and tools:
• Client library
• Server framework
• Two test repositories (InMemory and FileShare)
• CMIS Browser (web application, AtomPub only)
• CMIS Workbench (desktop application for developers)
• FIT and TCK
Get hold of OpenCMIS
OpenCMIS is available • as source code:
https://svn.apache.org/repos/asf/chemistry/opencmis/trunk/
• as release package with all dependencies:
http://chemistry.apache.org/java/download.html
• via Maven: http://chemistry.apache.org/java/developing/dev-use-with-maven.html
• Last released version is 0.4.0
OpenCMIS Client Library
• Client API o OO API o Easy to use o Build-in caching
• Client Binding API
o Low-level o Very close to the CMIS specification o More control, less comfort
You want this API!
OpenCMIS Client Interfaces
CmisObject
Session DocumentFolder Policy RelationshipSessionFactory «creates»
RepositoryInfo
ContentStream
ObjectType
FolderType DocumentType RelationshipTypePolicyType
Rendition
0..n
FileableCmisObject
Property0..n
PropertyDefinition9..n
1 0..n
ChangeEvents
ChangeEvent0..n
«loads»
Repository
«creates»«loads»
ObjectId
AllowableActions ACL
ACE0..n
QueryResult
«loads»
0..n
0..n0..n
0..n
«loads»
PropertyData0..n
«loads»
sourcetarget
OpenCMIS Main Client Interfaces
CmisObject
DocumentFolder Policy Relationship
ContentStream
Rendition
FileableCmisObject
Property0..n
ObjectIdAllowableActions ACL
ACE0..n
0..n
Aspects, Aspects, Aspects
• CMIS 1.0 has no native support for aspects
• Secondary types (= aspects) will be in CMIS 1.1
• Alfresco provides read and write access to aspects and aspect properties through CMIS extensions
• Theoretically, every CMIS client can read and write aspects but that is rather troublesome
Alfresco OpenCMIS Extension
• The Alfresco OpenCMIS Extension transparently adds aspect support to OpenCMIS
• Hosted on Apache Extras (Google Code), Apache license: http://apache-extras.org/p/alfresco-opencmis-extension
• See the project page for setup instructions. It’s really simple.
OpenCMIS Sessions
• CMIS is stateless!
• OpenCMIS introduces a session concept to support caching o It’s all about performance o Reduce the number of calls to the repository
• OpenCMIS caches:
o Repository infos o Type definitions o AtomPub links o CMIS objects
Connect to a repository – Variant 1
// set up session parameters Map<String, String> parameter = new HashMap<String, String>();
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); parameter.put(SessionParameter.ATOMPUB_URL, "http://cmis.alfresco.com/service/cmis"); parameter.put(SessionParameter.REPOSITORY_ID,
"84ccfe80-b325-4d79-ab4d-080a4bdd045b"); parameter.put(SessionParameter.USER, "admin");
parameter.put(SessionParameter.PASSWORD, "admin"); // create the session SessionFactory factory = SessionFactoryImpl.newInstance(); Session session = factory.createSession(parameter);
Connect to a repository – Variant 2
// set up session parameters Map<String, String> parameter = new HashMap<String, String>();
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value()); parameter.put(SessionParameter.ATOMPUB_URL, "http://cmis.alfresco.com/service/cmis"); parameter.put(SessionParameter.REPOSITORY_ID,
"84ccfe80-b325-4d79-ab4d-080a4bdd045b"); parameter.put(SessionParameter.USER, "admin"); parameter.put(SessionParameter.PASSWORD, "admin");
// get the list of repositories and choose the first one SessionFactory factory = SessionFactoryImpl.newInstance(); List<Repository> repositories = factory.getRepositories(parameter); Session session = repositories.get(0).createSession();
Using the Session
RepositoryInfo ri = session.getRepositoryInfo(); String id = ri.getId(); String name = ri.getName();
CmisObject object1 = session.getObject("1234567890");!CmisObject object2 = session.getObjectByPath("/my/path/doc");!
Folder rootFolder = session.getRootFolder();!String rootFolderId = rootFolder.getId();!!
for(CmisObject object: rootFolder.getChildren()) {! String name = object.getName();!
if(object instanceof Document) {! Document doc = (Document) object;! long size = doc.getContentStreamLength(); !
}!}!
Navigation
import org.apache.chemistry.opencmis.commons.*; import org.apache.chemistry.opencmis.commons.data.*; import org.apache.chemistry.opencmis.commons.enums.*; import org.apache.chemistry.opencmis.client.api.*; // get root folder Folder root = session.getRootFolder(); String rootFolderName = root.getName(); println "Root folder: ${rootFolderName}\n" // print root folder children for(CmisObject object: root.getChildren()) { String name = object.getName(); String typeId = object.getType().getId(); String path = object.getPaths().get(0); println "${name} \t${typeId} \t(${path})" // get parents /* for(CmisObject parent: object.getParents()) { String parentName = parent.getName(); println " Parent: ${parentName}" } */ }
Paging
import org.apache.chemistry.opencmis.commons.*;!import org.apache.chemistry.opencmis.commons.data.*;!import org.apache.chemistry.opencmis.commons.enums.*;!import org.apache.chemistry.opencmis.client.api.*;!!Folder root = session.getRootFolder();!!printList( root.getChildren() )!!//printList( root.getChildren().getPage(3) )!!//printList( root.getChildren().skipTo(2) )!!//printList( root.getChildren().skipTo(2).getPage(3) )!!!!void printList(ItemIterable<CmisObject> list) {! list.each { println "${it.name} \t${it.type.id}" }! ! long numItems = list.getTotalNumItems();! boolean hasMore = list.getHasMoreItems();! ! println "--------------------------------------"! println "Total number: ${numItems}"! println "Has more: ${hasMore}" ! println "--------------------------------------"!}!
Properties
import org.apache.chemistry.opencmis.commons.*; import org.apache.chemistry.opencmis.commons.data.*; import org.apache.chemistry.opencmis.commons.enums.*; import org.apache.chemistry.opencmis.client.api.*;
CmisObject object = session.getObjectByPath("/User Homes/florian/Test Folder/MyText");
for(Property<?> property: object.getProperties()) {
String propId = property.getId();
String displayName = property.getDefinition().getDisplayName(); String queryName = property.getDefinition().getQueryName(); PropertyType datatype = property.getType();
Object value = property.getFirstValue(); println "${displayName}: ${value}"
println " Data type: ${datatype}" println " Id: ${propId}"
println " Query name: ${queryName}" println "" }
Content
import org.apache.chemistry.opencmis.commons.*;!import org.apache.chemistry.opencmis.commons.data.*;!import org.apache.chemistry.opencmis.commons.enums.*;!import org.apache.chemistry.opencmis.client.api.*;!!CmisObject object = session.getObjectByPath("/User Homes/florian/Test Folder/MyText");!!if(!(object instanceof Document)) {! throw new Exception("Not a document!");!}!!Document doc = (Document) object;!!ContentStream content = doc.getContentStream();!!if(content == null) {! throw new Exception("Document has no content!");!}!!String filename = content.getFileName();!String mimeType = content.getMimeType();!long length = content.getLength();!InputStream stream = content.getStream();!!println "File: ${filename}"!println "MIME Type: ${mimeType}"!println "Size: ${length} bytes"!println "Has stream: " + (stream != null)!!
Query
import org.apache.chemistry.opencmis.commons.* import org.apache.chemistry.opencmis.commons.data.* import org.apache.chemistry.opencmis.commons.enums.* import org.apache.chemistry.opencmis.client.api.* String cql = "SELECT cmis:objectId, cmis:name, cmis:contentStreamLength FROM cmis:document"; ItemIterable<QueryResult> results = session.query(cql, false); //ItemIterable<QueryResult> results = session.query(cql, false).getPage(10); //ItemIterable<QueryResult> results = session.query(cql, false).skipTo(10).getPage(10); for(QueryResult hit: results) { for(PropertyData<?> property: hit.getProperties()) { String queryName = property.getQueryName(); Object value = property.getFirstValue(); println "${queryName}: ${value}" } println "--------------------------------------" }
Folders
import org.apache.chemistry.opencmis.commons.*;!import org.apache.chemistry.opencmis.commons.data.*;!import org.apache.chemistry.opencmis.commons.enums.*;!import org.apache.chemistry.opencmis.client.api.*;!!Folder root = session.getRootFolder();!!// create a new folder!Map<String, Object> properties = new HashMap<String, Object>();!properties.put("cmis:objectTypeId", "cmis:folder");!properties.put("cmis:name", "a new folder");!!Folder newFolder = root.createFolder(properties);!!printProperties(newFolder);!!!// update properties!Map<String, Object> updateproperties = new HashMap<String, Object>();!updateproperties.put("cmis:name", "renamed folder");!!newFolder.updateProperties(updateproperties);!!newFolder.refresh();!printProperties(newFolder);!!!// delete folder!newFolder.deleteTree(true, UnfileObject.DELETE, true);!!
Documents
import org.apache.chemistry.opencmis.commons.*; import org.apache.chemistry.opencmis.commons.data.*;
import org.apache.chemistry.opencmis.commons.enums.*; import org.apache.chemistry.opencmis.commons.impl.dataobjects.*;
import org.apache.chemistry.opencmis.client.api.*;
Folder root = session.getRootFolder();
// create a new document String name = "myNewDocument.txt";
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("cmis:objectTypeId", "cmis:document"); properties.put("cmis:name", name);
byte[] content = "Hello World!".getBytes();
InputStream stream = new ByteArrayInputStream(content); ContentStream contentStream = new ContentStreamImpl(name, content.length, "text/plain", stream);
Document newDoc = root.createDocument(properties, contentStream, VersioningState.MAJOR);
printProperties(newDoc);
// delete document
newDoc.delete(true);
void printProperties(CmisObject object) {
object.properties.each { println "${it.id}: ${it.firstValue}" } println "--------------------------------------"
}
CMIS Outlook
• The CMIS TC is working on CMIS 1.1
• CMIS 1.1 adds new features o Type mutability o Browser Binding o Secondary Types o …
• Release date: 2012???
• Will it be backwards compatible? o We don’t know yet. o Apache Chemistry will hide binding changes.
Apache Chemistry Outlook
• Tests and documentation… o Make sure it works with as many CMIS servers and
clients as possible o Make the usage of Apache Chemistry as simple as possible
• Apache Chemistry sandboxes o Implementations of CMIS 1.1 features o Feedback for the CMIS TC
Alfresco CMIS Outlook
• Alfresco 4.x o Switch to the OpenCMIS server framework o Many improvements: speed, memory consumption, compliance
o OpenCMIS client libraries are embedded and available in Web Scripts o Have a look: http://cmis.alfresco.com
• Tech Talk: Alfresco CMIS Online webinar, Wednesday, 3 August 2011 o Alfresco CMIS mapping o CMIS changes in Alfresco 4.x o CMIS 1.1 browser binding (JSON) o Performance optimization o Whatever you want to talk about…