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