Sunday, August 29, 2010

Extending CMIS data model for alfresco repository

My previous blog talks about extending CMIS data model for Open-CMIS in memory repository. In this blog I will explain on extending CMIS content model for alfresco repository.

Alfresco provides mechanism for extending CMIS data model. All we have to do is just register custom data model.

First step would be defining the custom content model. Defined custom content model should follow modelSchema.xsd which can be found under Alfresco\tomcat\webapps\alfresco\WEB-INF\classes\alfresco\model.

Let’s take an example where I want to define custom document type with addition properties.

<type name="Lib:BookHistory">
<title>Book History</title>
<parent>cm:doc</parent>
<properties>
<property name="Lib:BookId">
<type>d:text</type>
<mandatory>true</mandatory>
</property>
</properties>
</type>

Suppose we name this file as LibModel.xml. Complete content of the file is attached at the end of blog.

Now the next step would be registering our content model as extension. For that we need to create a file with suffix context in the name referring to custom model i.e. Lib-model-context.xml.

Name can be anything as long as it has suffix context. Custom model can be referred in the custom file as follows-


<!-- Registration of new models -->
<bean id="extension.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
<property name="models">
<list>
<value>alfresco/extension/LibModel.xml</value>
</list>
</property>
</bean>

We need to put these xml files in following directory Alfresco\tomcat\webapps\alfresco\WEB-INF\classes\alfresco\extension.

Using custom model in Open-CMIS:

Custom types can be referred with prefix D: and custom properties can be referred as it is in Open-CMIS client API. For example in case of our custom defined model type will be referred as D: Lib:BookHistory and property will be referred as Lib:BookId.

Issue with the alfresco:

Alfresco community edition 3.3 has a bug which doesn’t allow CMIS client to persist values for custom properties in alfresco repository. Fix for this is available in alfresco head build.

Content of LibModel.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Definition of new Model -->
<model name="Lib:LibModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">

<!-- Optional meta-data about the model -->
<description>Someco Model</description>
<author>Optaros</author>
<version>1.0</version>

<!-- Imports are required to allow references to definitions in other models -->
<imports>
<!-- Import Alfresco Dictionary Definitions -->
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
<!-- Import Alfresco Content Domain Model Definitions -->
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
</imports>

<!-- Introduction of new namespaces defined by this model -->
<namespaces>
<namespace uri="http://www.someco.com/model/content/1.0" prefix="Lib" />
</namespaces>

<types>
<!-- Enterprise-wide generic document type -->
<type name="Lib:BookHistory">
<title>Book Histroy</title>
<parent>cm:content</parent>
<properties>
<property name="Lib:BookId">
<type>d:text</type>
<mandatory>true</mandatory>
</property>
</properties>
</type>
</types>

</model>

Content of Lib-model-context.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<!-- Registration of new models -->
<bean id="extension.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
<property name="models">
<list>
<value>alfresco/extension/LibModel.xml</value>
</list>
</property>
</bean>
</beans>

Sunday, August 15, 2010

How to extend CMIS data model

By going through CMIS doc I got an impression that I should be able to create my custom object type by extending standard object type. That make sense, isn't it. My business model may have some specific data model and for that I should be able to extend standard data model provided by CMIS.

But now the question is how to achieve it? Do you think just adding a custom property.object type in the atom entry you send to create the document will server the purpose?

Let's try it using Open-CMIS client library.

Map parameter = new HashMap();

// connection settings
parameter.put(SessionParameter.ATOMPUB_URL, "http://localhost:8080/chemistry-opencmis-server-inmemory-0.2.0-incubating-SNAPSHOT/atom");
parameter.put(SessionParameter.BINDING_TYPE, BindingType.ATOMPUB.value());
parameter.put(SessionParameter.REPOSITORY_ID, "A1");

Session s = f.createSession(parameter);
Map pMap = new HashMap();
pMap.put("cmis:objectTypeId", "IITLib");
pMap.put("cmis:name", "MyDoc");
s.createDocument(pMap, s.createObjectId(s.getRepositoryInfo().getRootFolderId()), null, null, null, null, null));

When I ran this I got following exception.
org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException: Not Found

That says CmisObject type "IITLib" doesn't exist. But how come my client knows whether this type exist in repository where I am trying to post the document.

Well client know this by analyzing response received for the request http://localhost:8080/chemistry-opencmis-server-inmemory-0.2.0-incubating-SNAPSHOT/atom/A1/type?id=IITLib submitted to the repository.

Client generates this URL by investigating service document uritemplate of type typebyid. I have added a snippet below taken from service document of CMIS in-memory repository.

<cmisra:uritemplate>
<cmisra:template>http://localhost:8080/chemistry-opencmis-server-inmemory-0.2.0-incubating-SNAPSHOT/atom/A1/type?id={id}
</cmisra:template>
<cmisra:type>typebyid</cmisra:type>
<cmisra:mediatype>application/atom+xml;type=entry</cmisra:mediatype>
</cmisra:uritemplate>

Why this is called in-memory repository? No prize for guessing! once you restart your repository all your stored documents will be gone. So it's good for testing and not meant for production.

We still have to find out on how to extend CMIS data model.

Different repositories have different mechanism using which you can extend CMIS data model.

Lets try with in-memory Open-CMIS repository. All you have to do to register your extension is implement org.apache.chemistry.opencmis.inmemory.TypeCreator interface. class org.apache.chemistry.opencmis.inmemory.types.DefaultTypeSystemCreator.java can be used as an example. To register my extension I have added following snippet in DefaultTypeSystemCreator.java file

InMemoryDocumentTypeDefinition rootDocType = new InMemoryDocumentTypeDefinition("MyLib",
"My Type 1 Level 1",InMemoryDocumentTypeDefinition.getRootDocumentType());
Map<String, PropertyDefinition<?>> propertyDefinitions1 = new HashMap<String, PropertyDefinition<?>>();
PropertyDefinition<Boolean> prop_1 = PropertyCreationHelper.createBooleanDefinition("Is_BelongTo_Shiv_Lib",
"Sample Boolean Property");
propertyDefinitions1.put(prop_1.getId(), prop_1);
rootDocType.setPropertyDefinitions(propertyDefinitions1);
typesList.add(rootDocType);

build the in-memory repository war with changes and deploy it in your favorite servlet container. Lets try to execute our earlier code to create document

Session s = f.createSession(parameter);
Map pMap = new HashMap();
pMap.put("cmis:objectTypeId", "IITLib");
pMap.put("cmis:name", "MyDoc");
pMap.put("Is_BelongTo_Shiv_Lib", true);
s.createDocument(pMap, s.createObjectId(s.getRepositoryInfo().getRootFolderId()), null, null, null, null, null));

You should be able to see newly created document in repository.