You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2011/07/22 17:28:52 UTC
svn commit: r1149633 -
/chemistry/site/trunk/content/java/developing/guide.mdtext
Author: fmui
Date: Fri Jul 22 15:28:51 2011
New Revision: 1149633
URL: http://svn.apache.org/viewvc?rev=1149633&view=rev
Log:
OpenCMIS Client API Developer's Guide update
Modified:
chemistry/site/trunk/content/java/developing/guide.mdtext
Modified: chemistry/site/trunk/content/java/developing/guide.mdtext
URL: http://svn.apache.org/viewvc/chemistry/site/trunk/content/java/developing/guide.mdtext?rev=1149633&r1=1149632&r2=1149633&view=diff
==============================================================================
--- chemistry/site/trunk/content/java/developing/guide.mdtext (original)
+++ chemistry/site/trunk/content/java/developing/guide.mdtext Fri Jul 22 15:28:51 2011
@@ -582,7 +582,8 @@ Running the sample produces the followin
Note that `getDescendants` and `getFolderTree` are similar, but `getDescendants` finds objects of any fileable type, `getFolderTree` finds only folder
objects.
-
+
+<a name="ObjectTypes"></a>
###Object Types
CMIS Objects have an object type. There are four base types, two of which *must* be supported by a repository
@@ -811,14 +812,67 @@ by supplying a null content stream.
System.err.println("caught an " + e1.getClass().getName() + " exception with message "
+ e1.getMessage());
}
+
+
+If you are using the ATOMPUB binding, [`CmisBaseException`](http://chemistry.apache.org/java/0.4.0/maven/apidocs/org/apache/chemistry/opencmis/commons/exceptions/package-tree.html)
+provides a `getErrorContent()` method which returns the content of the error page returned from the server, if there is one. This can be extremely useful
+debugging, as the server side is normally able to provide far more information that the client.
+In the following code snippet, a `CmisObjectNotFoundException` exception is forced by
+trying to create a document with an invalid type. The catch block prints the server's error page
+and the client stack trace.
+
+ :::java
+ Folder root = session.getRootFolder();
+ HashMap<String, String> newFolderProps = new HashMap<String, String>();
+ newFolderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
+ newFolderProps.put(PropertyIds.NAME, "ADGFolderTest");
+ Folder folderTest = root.createFolder(newFolderProps);
+
+ Map<String, String> newFileProps = new HashMap<String, String>();
+
+ newFileProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:badtype");
+ newFileProps.put(PropertyIds.NAME, "ADGFileTest");
+
+ try {
+ folderTest.createDocument(newFileProps, null, VersioningState.MAJOR);
+ } catch (CmisObjectNotFoundException e1) {
+ System.err.println("server error page :\n" + e1.getErrorContent());
+ System.err.println("\nClient stack trace :\n");
+ e1.printStackTrace();
+ }
+The output from this snippet looks like this:-
+
+ :::text
+ server error page :
+ <html><head><title>Apache Chemistry OpenCMIS - objectNotFound error</title><style><!--H1 {font-size:24px;line-height:normal;font-weight:bold;background-color:#f0f0f0;color:#003366;border-bottom:1px solid #3c78b5;padding:2px;} BODY {font-family:Verdana,arial,sans-serif;color:black;font-size:14px;} HR {color:#3c78b5;height:1px;}--></style></head><body><h1>HTTP Status 404 - <!--exception-->objectNotFound<!--/exception--></h1><p><!--message-->unknown type id: cmis:badtype<!--/message--></p><hr noshade='noshade'/><!--stacktrace--><pre>
+ org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException: unknown type id: cmis:badtype
+ at org.apache.chemistry.opencmis.inmemory.server.InMemoryRepositoryServiceImpl.getTypeDefinition(InMemoryRepositoryServiceImpl.java:110)
+ at org.apache.chemistry.opencmis.inmemory.server.InMemoryService.getTypeDefinition(InMemoryService.java:110)
+ ...
+ at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1665)
+ at java.lang.Thread.run(Thread.java:662)
+
+ </pre><!--/stacktrace--><hr noshade='noshade'/></body></html>
+
+ Client stack trace :
+ org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException: Not Found
+ at org.apache.chemistry.opencmis.client.bindings.spi.atompub.AbstractAtomPubService.convertStatusCode(AbstractAtomPubService.java:408)
+ at org.apache.chemistry.opencmis.client.bindings.spi.atompub.AbstractAtomPubService.read(AbstractAtomPubService.java:488)
+ ...
+ at org.apache.chemistry.opencmis.client.runtime.SessionImpl.createDocument(SessionImpl.java:651)
+ at org.apache.chemistry.opencmis.client.runtime.FolderImpl.createDocument(FolderImpl.java:87)
+ at org.apache.chemistry.opencmis.client.runtime.FolderImpl.createDocument(FolderImpl.java:409)
+ at org.apache.chemistry.opencmis.doc.TestingException.main(TestingException.java:104)
+
+<a name="OperationContext"></a>
###Operation Context
The amount of metadata and associated information retrieved during an OpenCMIS operation could be large,
so certain OpenCMIS methods return a sensible subset of the information by default,
and provide additional methods that take an [`OperationContext`](http://chemistry.apache.org/java/0.4.0/maven/apidocs/org/apache/chemistry/opencmis/client/api/OperationContext.html).
-An OperationContext allows you to tune the amount of information returned by setting property filters, renditions filters, or by setting what. It is also used to control
-paging and caching in an operation.
-####Setting
+An OperationContext allows you to tune the amount of information returned by setting property filters, renditions filters, or by setting flags to include
+path segments, ACLs, Allowable Actions, Policies, and Relationships.
+The OperationContext is also used to control paging and caching in an operation.
####Caching
Caching of objects is turned on by default in OpenCMIS, but if you want to guarantee that `getObject()`
@@ -850,6 +904,7 @@ You can use an `OperationContext` with a
This code will retrieve any renditions of type `cmis:thumbnail` for this document object.
+<a name="Paging"></a>
###Paging
When you retrieve the children of a CMIS object, the result set returned is of an arbitrary size.
Retrieving a large result set synchronously could increase response times.
@@ -875,18 +930,18 @@ Note that `skipTo()` method returns a a
The following code snippet retrieves all objects in the result set, using the `getHasMoreItems()` method to end the `getPage()` loop.
:::java
+ System.out.println("Getting complete result set in pages of 3");
operationContext = new OperationContextImpl();
operationContext.setMaxItemsPerPage(3);
children1 = folderPaging.getChildren(operationContext);
int pageNumber = 0;
boolean finished = false;
+ count= 0;
while (!finished) {
- count = 0;
- ItemIterable<CmisObject> currentPage = children1.skipTo(
- pageNumber * operationContext.getMaxItemsPerPage()).getPage();
+ ItemIterable<CmisObject> currentPage = children1.skipTo(count).getPage();
+ System.out.println("page " + pageNumber + " has " + currentPage.getPageNumItems() + " items");
for (CmisObject item : currentPage) {
- System.out.println("object " + count + " in page" + pageNumber + " is "
- + item.getName());
+ System.out.println("object " + count + " is " + item.getName());
count++;
}
pageNumber++;
@@ -1191,16 +1246,259 @@ to retrieve all renditions of a particul
System.out
.println("Finding first object in repository with thumbnail renditions - end");
}
-
+###Advanced use of types
+Not all objects in a repository can be described by the basic CMIS types. A repository can
+define additional object-types that extend the CMIS base object-types. You can see the descendants of
+the cmis:document type in the output of code snippet in the <a href="#ObjectTypes">Object Types</a> section
+of this guide. Additional object types inherit the properties of the base type, and can define
+new custom properties. OpenCMIS does not allow you create objects with a custom type, but you can
+access existing objects of a custom type, and access the custom property values. The following
+code snippet searches for all objects in a repository of a particular custom type, and prints
+a some of each resulting object's custom properties.
+
+In this snippet the code looks for objects of type `ia:calendarEvent`, which is an Alfresco
+custom type, but you can alter the sample to access objects of a custom type within your repository.
+
+ :::java
+ if (session.getRepositoryInfo().getCapabilities().getQueryCapability()
+ .equals(CapabilityQuery.METADATAONLY)) {
+ System.out.println("Full search not supported");
+ } else {
+ String query = "SELECT * FROM ia:calendarEvent";
+ ItemIterable<QueryResult> queryResult = session.query(query, false);
+ for (QueryResult item : queryResult) {
+ System.out.println("Found "
+ + item.getPropertyByQueryName("cmis:name").getFirstValue() + " of type "
+ + item.getPropertyByQueryName("cmis:objectTypeId").getFirstValue());
+ System.out.println("property ia:descriptionEvent is "
+ + item.getPropertyByQueryName("ia:descriptionEvent").getFirstValue());
+ System.out.println("property ia:toDate is "
+ + item.getPropertyByQueryName("ia:toDate").getFirstValue());
+ System.out.println("property ia:fromDate is "
+ + item.getPropertyByQueryName("ia:fromDate").getFirstValue());
+ }
+ }
+
+The [OpenCMIS Workbench](http://chemistry.apache.org/java/developing/tools/dev-tools-workbench.html) is a useful
+tool for examining the custom types defined in your repository. Just click on **Types** in the menu bar and expand the base-type
+tree. You can view all the properties of the type by clicking on its name.
+
+
###Relationships
-<img src="images/todo.gif" title="todo">
-###Permissions
-<img src="images/todo.gif" title="todo">
+A Relationship Object a relationship between a source object and a target object. The relationship has direction, from source to target.
+It is non-invasive, in that a relationship does not modify either the source or the target object.
+A relationship object has a type, like any other CMIS object.
+The source and target objects must be independent objects, such as a document, folder, or policy objects.
+A relationship object does not have a content-stream, and is not versionable, queryable, or fileable.
+
+A repository does not have to support relationships.
+If it doesn't the relationship base object-type will not be returned by a "get types" call.
+
+Explicit relationships can be used to create an arbitrary relationship graph among independent objects.
+Such a relationship graph is only structural in nature.
+No inheritance or transitive properties are attached to a relationship graph.
+
+The following code snippet creates a relationship between 2 objects. Note that the Apache Chemistry in-memory
+repository does not support relationship objects, so this code expects to run against an Alfresco repository. This repository supports relationships,
+but the base type `cmis:relationship` is not defined as creatable, so the snippet uses an existing type `R:cmiscustom:assoc` which is
+a creatable sub-type of `cmis:relationship`. You can
+change this code to create a relationship of a type supported by your repository, between two objects of types supported by the
+relationship type.
+
+ :::java
+ // Check if the repo supports relationships
+ ObjectType relationshipType = null;
+ try {
+ relationshipType = session.getTypeDefinition("cmis:relationship");
+ } catch (CmisObjectNotFoundException e) {
+ relationshipType = null;
+ }
+
+ if (relationshipType == null) {
+ System.out.println("Repository does not support cmis:relationship objects");
+ } else {
+ ObjectType cmiscustomRelationshipType = null;
+ try {
+ cmiscustomRelationshipType = session.getTypeDefinition("R:cmiscustom:assoc");
+ } catch (CmisObjectNotFoundException e) {
+ cmiscustomRelationshipType = null;
+ }
+ if (cmiscustomRelationshipType == null) {
+ System.out.println("Repository does not support R:cmiscustom:assoc objects");
+ } else {
+
+ System.out.println("Creating folders for relationships example");
+
+ newFolderProps = new HashMap<String, String>();
+ newFolderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
+ newFolderProps.put(PropertyIds.NAME, "ADGFolderAssociations");
+ Folder folderAssociations = root.createFolder(newFolderProps);
+
+ newFileProps = new HashMap<String, String>();
+ newFileProps.put(PropertyIds.OBJECT_TYPE_ID, "D:cmiscustom:document");
+ newFileProps.put(PropertyIds.NAME, "ADGFileSource");
+ Document sourceDoc = folderAssociations.createDocument(newFileProps, null,
+ VersioningState.MAJOR);
+ newFileProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
+ newFileProps.put(PropertyIds.NAME, "ADGFileTarget");
+ Document targetDoc = folderAssociations.createDocument(newFileProps, null,
+ VersioningState.MAJOR);
+
+ Map<String, String> relProps = new HashMap<String, String>();
+ relProps.put("cmis:sourceId", sourceDoc.getId());
+ relProps.put("cmis:targetId", targetDoc.getId());
+ relProps.put("cmis:objectTypeId", "R:cmiscustom:assoc");
+ ObjectId relId = session.createRelationship(relProps, null, null, null);
+ System.out.println("created relationship");
+
+ }
+ }
+
+Each relationship type defines allowed source and target object types. The following code finds the type
+of an existing relationship, and displays the lists of allowed source types, and allowed object types.
+
+ :::java
+ RelationshipType relType = (RelationshipType) relationship.getType();
+
+ System.out.println(relType.getDisplayName()
+ + "has the following allowed source types:");
+ for (ObjectType objectType : relType.getAllowedSourceTypes()) {
+ System.out.println("\t" + objectType.getDisplayName());
+ }
+
+ System.out.println(relType.getDisplayName()
+ + "has the following allowed target types:");
+ for (ObjectType objectType : relType.getAllowedTargetTypes()) {
+ System.out.println("\t" + objectType.getDisplayName());
+ }
+
+###Access Control
+Document or folder objects can have an access control list (ACL), which
+controls access to the object. Policy objects can also control access to a document or folder.
+
+An ACL is a list of Access Control Entries (ACEs). An ACE grants one or
+more permissions to a principal. A principal is a user, group, role, or something similar.
+
+An ACE holds :-
+
+* One String with the principalid
+* One or more Strings with the names of the permissions.
+* A boolean flag `direct`, which is *true* if the ACE is directly assigned to the object, or *false* if the ACE is somehow derived
+
+There are three basic permissions predefined by CMIS:
+
+* `cmis:read`: permission for reading properties and reading content
+* `cmis:write`: permission to write properties and the content of an object. A respository can defin the property to include `cmis:read`
+* `cmis:all`: all the permissions of a repository. It includes all other basic CMIS permissions.
+
+How these basic permissions are mapped to allowable actions is repository specific.
+You can discover the repository semantics for basic permissions with regard to allowable actions by examining
+the mappings parameter returned by session method
+[`getRepositoryInfo`](http://chemistry.apache.org/java/0.4.0/maven/apidocs/org/apache/chemistry/opencmis/client/api/Session.html#getRepositoryInfo()).
+A repository can extend the basic permissions with its own repository-specific permissions. The folowing code snippet prints out the
+permissions available for a repository, and the mappings of allowable actions to repository permissions:-
+
+ :::java
+ System.out.println("getting ACL capabilities");
+ AclCapabilities aclCapabilities = session.getRepositoryInfo().getAclCapabilities();
+
+ System.out.println("Propogation for this repository is " + aclCapabilities.getAclPropagation().toString());
+
+ System.out.println("permissions for this repository are: ");
+ for (PermissionDefinition definition : aclCapabilities.getPermissions()) {
+ System.out.println(definition.toString());
+ }
+
+ System.out.println("\npermission mappings for this repository are: ");
+ Map<String, PermissionMapping> repoMapping = aclCapabilities.getPermissionMapping();
+ for (String key: repoMapping.keySet()) {
+ System.out.println(key + " maps to " + repoMapping.get(key).getPermissions());
+ }
+
+You can specify how a repository should handle non-direct ACEs when you create an ACL, by specifying one of the following acl propogation values:-
+
+* `OBJECTONLY`: apply ACEs to a document or folder, without changing the ACLs of other objects
+* `PROPAGATE`: apply ACEs to the given object and all inheriting objects
+* `REPOSITORYDETERMINED`: allow the repositoryto use its own method of computing how changing an ACL for an object influences the non-direct ACEs of other objects.
+
+The following code snippet creates a document object, and prints out the ACEs in the created document's
+ACL. It then creates a new ACL with one ACE, adds it to the document, retrieves the document again, and
+prints out the ACEs now present in the document's ACL :-
+
+ :::java
+ // Check if the repo supports ACLs
+ if (!session.getRepositoryInfo().getCapabilities().getAclCapability()
+ .equals(CapabilityAcl.MANAGE)) {
+ System.out.println("Repository does not allow ACL management");
+ } else {
+ System.out.println("Repository allows ACL management");
+
+ System.out.println("Creating folders for permissions example");
+
+ HashMap<String, String> newFolderProps = new HashMap<String, String>();
+ newFolderProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
+ newFolderProps.put(PropertyIds.NAME, "ADGFolderPermissions");
+ Folder folderAssociations = session.getRootFolder().createFolder(newFolderProps);
+
+ HashMap<String, String> newFileProps = new HashMap<String, String>();
+ ContentStream contentStream = new ContentStreamImpl("permissions.txt", null,
+ "plain/text", new ByteArrayInputStream("some content".getBytes()));
+ newFileProps.put(PropertyIds.OBJECT_TYPE_ID, "cmis:document");
+ newFileProps.put(PropertyIds.NAME, "ADGFilePermissions");
+ Document testDoc = folderAssociations.createDocument(newFileProps, contentStream,
+ VersioningState.MAJOR);
+
+ OperationContext operationContext = new OperationContextImpl();
+ operationContext.setIncludeAcls(true);
+ testDoc = (Document) session.getObject(testDoc, operationContext);
+
+ System.out.println("ACL before adding an ace...");
+ for (Ace ace : testDoc.getAcl().getAces()) {
+ System.out.println("Found ace: " + ace.getPrincipalId() + " toString "+ ace.toString());
+ }
+
+ List<String> permissions = new ArrayList<String>();
+ permissions.add("cmis:write");
+ String principal = "admin";
+ Ace aceIn = session.getObjectFactory().createAce(principal, permissions);
+ List<Ace> aceListIn = new ArrayList<Ace>();
+ aceListIn.add(aceIn);
+ testDoc.addAcl(aceListIn, AclPropagation.REPOSITORYDETERMINED);
+ testDoc = (Document) session.getObject(testDoc, operationContext);
+
+ System.out.println("ACL after adding an ace...");
+ for (Ace ace : testDoc.getAcl().getAces()) {
+ System.out.println("Found ace: " + ace.getPrincipalId() + " toString "+ ace.toString());
+ }
+ }
+
+Note the following points :-
+
+* OpenCMIS does not return ACLs by default when retrieving a CMIS object. You must provide
+an OperationContext on the method and set it to include ACLs.
+* We have set the propogation to be `AclPropagation.REPOSITORYDETERMINED` which lets the
+repository determine how the permissions are set for inheriting objects. Since this object
+is a document, there are no inheriting objects.
+
+
+
+
###Change logs
<img src="images/todo.gif" title="todo">
##OpenCMIS bindings
<img src="images/todo.gif" title="todo">
##Performance
-<img src="images/todo.gif" title="todo">
+OpenCMIS provides a number of features which you can use to avoid potential performance problems.
+
+* You can use <a href="#Paging">paging</a> to manage potentially large result sets.
+* You can use an <a href="#OperationContext">OperationContext</a> to tailor the metdata you wish to retrieve for a CMIS
+object.
##Troubleshooting
-<img src="images/todo.gif" title="todo">Common pitfalls
\ No newline at end of file
+
+* When handling CMIS exceptions in a CMIS client which connects with the AtomPub binding, remember that [`CmisBaseException`](http://chemistry.apache.org/java/0.4.0/maven/apidocs/org/apache/chemistry/opencmis/commons/exceptions/package-tree.html)
+provides a `getErrorContent()` method which returns the content of the error page returned from the server, if there is one. This can be extremely useful
+debugging, as the server side is normally able to provide far more information that the client.
+* When handling CMIS exceptions in a CMIS client which connects with the Web Services binding, [`CmisBaseException`](http://chemistry.apache.org/java/0.4.0/maven/apidocs/org/apache/chemistry/opencmis/commons/exceptions/package-tree.html)
+provides a `getCode()` method which returns the error code returned from the server, if there is one.
+* If a `java.lang.NullPointerException` is thrown when you are trying to access properties of a CMIS object such as the list of ACLs, Renditions, relationships, or allowable actions, it is likely you have either omitted an
+`OperationContext` when getting the object, or you have not set the `OperationContext` properly.
\ No newline at end of file