This document contains a step-by-step introduction how you can use opencmis
to build an CMIS server. The document is divided iton the following
sections:
Getting started. (download, setup Eclipse, etc.)
Implementing the services
The ServiceWrapper
Differences between SOAP and AtomPub (ObjectHolder)
Running the server
Handling Authentication
Testing the server
Getting started:
The following step-by-step guide is a sample how to create a web
application acting as CMIS server. It will support both bindings web
services and AtomPub.
The following section describes how to initially setup a project to compile
a CMIS server. Please note that CMIS comes with two built-in servers. The
fileshare and the in-memory server. It is a good hint for all upcoming
questions to look at these implementations as example code containing
working implementations.
Using maven
Using maven is the easiest way to get started. OpenCMIS itself is using
maven and you can get easily your setup and dependencies using maven. This
requires that you have a working maven environment. In case you don't yet
have one you can find instructions here
.
The first step is to create your initial pom.xml file to build your project
and to setup the dependencies. The following steps require that you have
build opencmis and installed it to your local maven repository {{(mvn
install)}}. See here
for detailed instructions how to do this.
You can create your initial setup using maven itself (adjust package name,
version number and so on according to your needs):
Then select option 18 maven-archetype-webapp and confirm settings.
You will find a project setup then consisting of a directory mycmissvr
and subdirectories for source code and test code and the Java packages. We
need to adapt the .pom file so that maven creates a web application file
that can be deployed in a servlet container (like Apache Tomcat or Jetty).
We also need to setup the dependencies to opencmis and we need to instruct
maven generating an overlay with the existing server code in opencmis.
Please remove therefore the entire generated src/webapp directory and all
included files (.jsp, web.xml).
The final pom.xml* will look like this:
application from opencmis which you may adjust according to your needs. You
may run {{mvn eclipse:eclipse}} to generate the required {{.projects}} and
{{.classpath}} files for Eclipse that you just need to import in your
Eclipse workspace.
{{chemistry-opencmis-server-bindings-0.1-incubating-SNAPSHOT.jar}} and copy
the contents to your project. It includes all required jars, web.xml and
other supporting files. In case you prefer an empty classes directory and
use a jar instead you can use:
target directory to your project directory. Add all jar files from the
{{.war}} and the one created in the previous step to your project setup.
Add the {{repository.properties}} file to your classpath.
implement the CMIS logic. For this purpose a file named
repository.properties must be in the classpath. It must contain a property
named class that refers to your service factory:
CMIS spec. We will do this here for one example call. The important piece
is that your class implements {{CmisService}} which is central access point
to all incoming calls. Here is an implementation of an example call
getRepositoryInfo():
Now you can start implementing all the required methods with their methods.
The ServiceWrapper
For the CmisService interface exists a corresponding abstract class. This
can be used as a starting point for your implementation. This is optional
but gives you the benefit of providing many common null pointer checks and
default values for optional parameters. The abstract class also provides a
default implementation for generating the ObjectInfo needed for the AtomPub
binding. This wrapper can reduce the code required in your service
implementation. You should always start using the wrapper class and
directly implement the CmisService interface only when necessary. It is
strongly recommended however to provide a better implementation for create
the ObjectInfo however (see next section for details).
Differences between the CMIS bindings
OpenCMIS supports both the AtomPub and the web services binding interface.
It hides all the details how to handle the protocol but there are some
subtle differences that need to be reflected in the Java interfaces. This
chapter explains where the differences are and why they are needed.
The ObjectInfo interface
The methods in the interfaces CmisService are following the data model
in the CMIS specification. The specification defines the following
services:
AclService
DiscoveryService
ObjectService
MultifFlingService
NavigationService
PolicyService
RelationshipService
RepositoryService
Versioning Service
For each of these services exists a corresponding interface in opencmis.
CmisService is an interface that unifies all interfaces in one interface.
The methods in this interface correspond to the methods in the
specification. For the web service binding this is a one-to-one mapping.
For the AtomPub binding this information is not sufficient in all cases.
Take the creation of a document as an example. The web service returns in
this case a single string value containing the id of the created document.
A response in the AtomPub binding however consists of an AtomPub entry
element which is an XML fragment (for an example look at
DocumentEntry.xml in the examples directory of the CMIS specification).
You will notice that generating this XML requires much more information
than the create...() methods in the specification provide as return value.
Your implementation needs to provide all the information so that opencmis
can generate the complete response. For this purpose the ObjectInfo was
introduced. getObjectInfo is the method of the CmisService interface
that provides this information. The AbstractCmisService contains a
member objectInfoMap that you can fill with this information. If you
ignore this the class AbstractCmisService contains a default
implementation which works but is very ineffecient. You should use this
only as a starting point. Beware that a service can be called from multiple
threads, so have to take care to handle threading issues properly.
If the method you are implementing returns a list of objects and not only a
single value (for example methods like getChildren(), getDescendants()
in the navigation service) you need to provide an ObjectInfo for each
element in the collection. getObjectInfo therefore returns a map with
the object id for each object as key and the corresponding ObjectInfo
as value. You can use the method addObjectInfo to add an element to the
map.
The create() methods
The web service binding has separate calls for each object type to be
created: policies, relationshiops, documents and folders:
createDocument
createFolder
createRelationship
createPolicy
In the AtomPub binding there is one general create() method that is
used for all object types. The CmisObjectService interface therefore
contains 5 create() methods, the four specific ones are used for the
web service bindings and the general one for the AtomPub binding.
Applying ACLs
The class CmisAclService has two methods applyAcl(). One uses two
AccessControlLists with one Acl to add and one to remove. The other
method contains only one Acl to be set on the target object. The web
service binding uses the method with two parameters, the AtomPub binding
uses the method with one parameter.
Running The Server
When your services are implemented (or parts of them) you can try to run
the server in a servlet container like Tomcat. If you have used maven to
setup your project you can run
mvnpackage
to generate a war file that you will find in the target directory. If you
have not used maven create the war with the mechanisms of your build
environment. Check the war contains a web.xml, the wsdl files in
WEB-INF and all the required jars from opencmis and dependent libraries
in WEB-INF/lib. For jetty maven contains a build-in integration that
you may use if you like using mvn jetty:run. Adjust your pom.xml in
this case accordingly.
Deploy your application and start testing.
Handling Authentication
Opencmis does not provide or expect any specific mechanism how
authentication is handled. It provides some basic mechanisms hot to extract
user name and password depending on the protocol. For AtomPub binding http
basic authentication is supported, for web services WS-Security 1.1 for
Username Token Profile 1.1 is supported.
If you want to use servlet filters dealing with authentication, just
add them to your web.xml file. You also can handle authentication inside of
your code. In this case derive a class from BasicAuthCallContextHandler
and implement method getCallContextMap(). There you have access to user
name and password as provided by the user. By default opencmis does not
enforce authentication, so initially the map will be null. If you raise a
CmisPermissionDeniedException the exception is caught by the server
implementation and a 401 http return code is sent as response to the
browser. This usually opens a dialog for user name and password then.
{code:java|borderStyle=solid}
public class MyContextHandler
extends BasicAuthCallContextHandler
{
public Map getCallContextMap(HttpServletRequest request){
have to configure the class of your service factory (see above). You can
add additional properties in this file. These configuration parameters are
then passed to the {{init()}} method of your ServiceFactory method as key
value pairs in a hashmap.
unit tests that directly call your service implementations as a first step.
Opencmis also contains some basic tests that perform client-server
communication. You can choose the protocol binding and whether read-only or
read-write tests are performed. The amount of functionality tested depends
on the capabilities returned in your getRepositoryInfo return value. You
can run them using junit: