You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by ga...@apache.org on 2014/03/12 22:38:08 UTC

svn commit: r1576931 - in /chemistry/objectivecmis/trunk: ObjectiveCMIS/Bindings/ ObjectiveCMIS/Bindings/AtomPub/ ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/ ObjectiveCMIS/Client/ ObjectiveCMISTests/

Author: gavincornwell
Date: Wed Mar 12 21:38:07 2014
New Revision: 1576931

URL: http://svn.apache.org/r1576931
Log:
Added support for checkin, checkout and cancel checkout on the versioning service.

Modified:
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryParser.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomParserUtil.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService+Protected.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubObjectService.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubVersioningService.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.m
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISVersioningService.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.h
    chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.m
    chemistry/objectivecmis/trunk/ObjectiveCMISTests/CMISBaseTest.m
    chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryParser.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryParser.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryParser.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomEntryParser.m Wed Mar 12 21:38:07 2014
@@ -35,6 +35,7 @@
 @property (nonatomic, strong) CMISRenditionData *currentRendition;
 @property (nonatomic, strong) NSMutableArray *currentRenditions;
 @property (nonatomic, strong) NSMutableString *string;
+@property (nonatomic, assign) BOOL parsingRelationship;
 
 @property (nonatomic, weak) id<NSXMLParserDelegate, CMISAtomEntryParserDelegate> parentDelegate;
 @property (nonatomic, strong) NSDictionary *entryAttributesDict;
@@ -56,6 +57,7 @@
     self = [super init];
     if (self) {
         self.currentLinkRelations = [NSMutableSet set];
+        self.parsingRelationship = NO;
     }
     return self;
 }
@@ -100,6 +102,7 @@
         self.objectData = [[CMISObjectData alloc] init];
         self.entryAttributesDict = attributes;
         self.parentDelegate = parentDelegate;
+        self.parsingRelationship = NO;
         
         // Setting ourself, the entry parser, as the delegate, we reset back to our parent when we're done
         [parser setDelegate:self];
@@ -120,7 +123,7 @@
 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
                                             qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
 {
-    if ([namespaceURI isEqualToString:kCMISNamespaceCmis]) {
+    if ([namespaceURI isEqualToString:kCMISNamespaceCmis] && !self.parsingRelationship) {
         if ([elementName isEqualToString:kCMISAtomEntryPropertyId] ||
             [elementName isEqualToString:kCMISAtomEntryPropertyString] ||
             [elementName isEqualToString:kCMISAtomEntryPropertyInteger] ||
@@ -147,6 +150,9 @@
         } else if ([elementName isEqualToString:kCMISAtomEntryAllowableActions]) {
             // Delegate parsing to child parser for allowableActions element
             self.childParserDelegate = [CMISAllowableActionsParser allowableActionsParserWithParentDelegate:self parser:parser];
+        } else if ([elementName isEqualToString:kCMISCoreRelationship]) {
+            // NOTE: we're currently ignoring the relationship element so set a flag to check
+            self.parsingRelationship = YES;
         }
     } else if ([namespaceURI isEqualToString:kCMISNamespaceCmisRestAtom]) {
         if ([elementName isEqualToString:kCMISAtomEntryObject]) {
@@ -208,29 +214,39 @@
     }
     
     if ([namespaceURI isEqualToString:kCMISNamespaceCmis]) {
-        if ([elementName isEqualToString:kCMISAtomEntryPropertyId] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyString] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyInteger] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyDateTime] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyBoolean] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyUri] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyHtml] ||
-            [elementName isEqualToString:kCMISAtomEntryPropertyDecimal]) {            
-            // add the property to the properties dictionary
-            self.currentPropertyData.values = self.propertyValues;
-            self.propertyValues = nil;
-            [self.currentObjectProperties addProperty:self.currentPropertyData];
-            self.currentPropertyData = nil;
-        } else if ([elementName isEqualToString:kCMISCoreProperties]) {
-            // Finished parsing Properties & its ExtensionData
-            [self saveCurrentExtensionsAndPushPreviousExtensionData];
-        } else if ([elementName isEqualToString:kCMISCoreRendition]) {
-            if (self.currentRenditions == nil) {
-                self.currentRenditions = [[NSMutableArray alloc] init];
+        if (!self.parsingRelationship)
+        {
+            // ignore the properties within the relationship element
+            if ([elementName isEqualToString:kCMISAtomEntryPropertyId] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyString] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyInteger] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyDateTime] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyBoolean] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyUri] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyHtml] ||
+                [elementName isEqualToString:kCMISAtomEntryPropertyDecimal]) {            
+                // add the property to the properties dictionary
+                self.currentPropertyData.values = self.propertyValues;
+                self.propertyValues = nil;
+                [self.currentObjectProperties addProperty:self.currentPropertyData];
+                self.currentPropertyData = nil;
+            } else if ([elementName isEqualToString:kCMISCoreProperties]) {
+                // Finished parsing Properties & its ExtensionData
+                [self saveCurrentExtensionsAndPushPreviousExtensionData];
+            } else if ([elementName isEqualToString:kCMISCoreRendition]) {
+                if (self.currentRenditions == nil) {
+                    self.currentRenditions = [[NSMutableArray alloc] init];
+                }
+                [self.currentRenditions addObject:self.currentRendition];
+                self.currentRendition = nil;
             }
-            [self.currentRenditions addObject:self.currentRendition];
-            self.currentRendition = nil;
         }
+        
+        // the relationship element has ended
+        if ([elementName isEqualToString:kCMISCoreRelationship]) {
+            self.parsingRelationship = NO;
+        }
+        
     } else if ([namespaceURI isEqualToString:kCMISNamespaceAtom]) {
         if ( [elementName isEqualToString:kCMISAtomEntry]) {
             // set the properties on the objectData object

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomParserUtil.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomParserUtil.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomParserUtil.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomParserUtil.m Wed Mar 12 21:38:07 2014
@@ -46,7 +46,7 @@
     } else if ([atomPubType isEqualToString:kCMISAtomEntryPropertyUri]) {
         return CMISPropertyTypeUri;
     } else {
-        CMISLogDebug(@"Unknow property type %@. Go tell a developer to fix this.", atomPubType);
+        CMISLogDebug(@"Unknown property type %@. Go tell a developer to fix this.", atomPubType);
         return CMISPropertyTypeString;
     }
 }
@@ -68,7 +68,7 @@
     } else if ([propertyType isEqualToString:kCMISAtomEntryPropertyUri]) {
         [array addObject:[NSURL URLWithString:stringValue]];
     } else {
-        CMISLogDebug(@"Unknow property type %@. Go tell a developer to fix this.", propertyType);
+        CMISLogDebug(@"Unknown property type %@. Go tell a developer to fix this.", propertyType);
     }
 }
 

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.h?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.h Wed Mar 12 21:38:07 2014
@@ -50,6 +50,7 @@ extern NSString * const kCMISAtomEntryAl
 
 // Collections
 extern NSString * const kCMISAtomCollectionQuery;
+extern NSString * const kCMISAtomCollectionCheckedout;
 
 // Links
 extern NSString * const kCMISLinkRelationDown;
@@ -59,6 +60,7 @@ extern NSString * const kCMISLinkRelatio
 extern NSString * const kCMISLinkVersionHistory;
 extern NSString * const kCMISLinkEditMedia;
 extern NSString * const kCMISLinkRelationNext;
+extern NSString * const kCMISLinkRelationWorkingCopy;
 
 // URL parameters
 extern NSString * const kCMISParameterChangeToken;
@@ -77,7 +79,13 @@ extern NSString * const kCMISParameterAl
 extern NSString * const kCMISParameterContinueOnFailure;
 extern NSString * const kCMISParameterUnfileObjects;
 extern NSString * const kCMISParameterRelativePathSegment;
-
+extern NSString * const kCMISParameterMajor;
+extern NSString * const kCMISParameterCheckin;
+extern NSString * const kCMISParameterCheckinComment;
+
+// Parameter Values
+extern NSString * const kCMISParameterValueTrue;
+extern NSString * const kCMISParameterValueFalse;
 
 // Namespaces
 extern NSString * const kCMISNamespaceCmis;
@@ -178,6 +186,7 @@ extern NSString * const kCMISCoreHeight;
 extern NSString * const kCMISCoreWidth;
 extern NSString * const kCMISCoreTitle;
 extern NSString * const kCMISCoreRenditionDocumentId;
+extern NSString * const kCMISCoreRelationship;
 
 // URI Templates
 extern NSString * const kCMISUriTemplateObjectById;

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/AtomPubParser/CMISAtomPubConstants.m Wed Mar 12 21:38:07 2014
@@ -50,6 +50,7 @@ NSString * const kCMISAtomEntryAllowable
 
 // Collections
 NSString * const kCMISAtomCollectionQuery = @"query";
+NSString * const kCMISAtomCollectionCheckedout = @"checkedout";
 
 // Links
 NSString * const kCMISLinkRelationDown = @"down";
@@ -59,6 +60,7 @@ NSString * const kCMISLinkRelationFolder
 NSString * const kCMISLinkVersionHistory = @"version-history";
 NSString * const kCMISLinkEditMedia = @"edit-media";
 NSString * const kCMISLinkRelationNext = @"next";
+NSString * const kCMISLinkRelationWorkingCopy = @"working-copy";
 
 // Parameters
 NSString * const kCMISParameterChangeToken = @"changeToken";
@@ -77,6 +79,13 @@ NSString * const kCMISParameterAllVersio
 NSString * const kCMISParameterContinueOnFailure= @"continueOnFailure";
 NSString * const kCMISParameterUnfileObjects = @"unfileObjects";
 NSString * const kCMISParameterRelativePathSegment = @"includeRelativePathSegment";
+NSString * const kCMISParameterMajor = @"major";
+NSString * const kCMISParameterCheckin = @"checkin";
+NSString * const kCMISParameterCheckinComment = @"checkinComment";
+
+// Parameter Values
+NSString * const kCMISParameterValueTrue = @"true";
+NSString * const kCMISParameterValueFalse = @"false";
 
 // Namespaces
 NSString * const kCMISNamespaceCmis = @"http://docs.oasis-open.org/ns/cmis/core/200908/";
@@ -177,6 +186,7 @@ NSString * const kCMISCoreHeight = @"hei
 NSString * const kCMISCoreWidth = @"width";
 NSString * const kCMISCoreTitle = @"title";
 NSString * const kCMISCoreRenditionDocumentId = @"renditionDocumentId";
+NSString * const kCMISCoreRelationship = @"relationship";
 
 // URI Templates
 NSString * const kCMISUriTemplateObjectById = @"objectbyid";

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService+Protected.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService%2BProtected.h?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService+Protected.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService+Protected.h Wed Mar 12 21:38:07 2014
@@ -93,4 +93,26 @@
                 cmisRequest:(CMISRequest *)cmisRequest
             completionBlock:(void (^)(NSString *link, NSError *error))completionBlock;
 
+/**
+ Generates and sends an atom entry to the given link url
+ */
+- (void)sendAtomEntryXmlToLink:(NSString *)link
+             httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
+                    properties:(CMISProperties *)properties
+                   cmisRequest:(CMISRequest *)request
+               completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock;
+
+/**
+ Generates and sends an atom entry (including content) to the given link url
+ */
+- (void)sendAtomEntryXmlToLink:(NSString *)link
+             httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
+                    properties:(CMISProperties *)properties
+            contentInputStream:(NSInputStream *)contentInputStream
+               contentMimeType:(NSString *)contentMimeType
+                 bytesExpected:(unsigned long long)bytesExpected
+                   cmisRequest:(CMISRequest*)request
+               completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+                 progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock;
+
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubBaseService.m Wed Mar 12 21:38:07 2014
@@ -29,6 +29,7 @@
 #import "CMISTypeByIdUriBuilder.h"
 #import "CMISLinkCache.h"
 #import "CMISLog.h"
+#import "CMISAtomEntryWriter.h"
 
 @interface CMISAtomPubBaseService ()
 
@@ -90,6 +91,7 @@
                     
                     // Cache collections
                     [self.bindingSession setObject:[workspace collectionHrefForCollectionType:kCMISAtomCollectionQuery] forKey:kCMISBindingSessionKeyQueryCollection];
+                    [self.bindingSession setObject:[workspace collectionHrefForCollectionType:kCMISAtomCollectionCheckedout] forKey:kCMISBindingSessionKeyCheckedoutCollection];
                     
                     
                     // Cache uri's and uri templates
@@ -330,4 +332,121 @@
     }
 }
 
+- (void)sendAtomEntryXmlToLink:(NSString *)link
+             httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
+                    properties:(CMISProperties *)properties
+                   cmisRequest:(CMISRequest *)request
+               completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+{
+    // Validate params
+    if (link == nil) {
+        CMISLogError(@"Must provide link to send atom entry");
+        if (completionBlock) {
+            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
+        }
+        return;
+    }
+    
+    // Generate atom entry XML in memory
+    CMISAtomEntryWriter *atomEntryWriter = [[CMISAtomEntryWriter alloc] init];
+    atomEntryWriter.cmisProperties = properties;
+    atomEntryWriter.generateXmlInMemory = YES;
+    NSString *writeResult = [atomEntryWriter generateAtomEntryXml];
+    
+    // Execute call
+    [self.bindingSession.networkProvider invoke:[NSURL URLWithString:link]
+                                     httpMethod:httpRequestMethod
+                                        session:self.bindingSession
+                                           body:[writeResult dataUsingEncoding:NSUTF8StringEncoding]
+                                        headers:[NSDictionary dictionaryWithObject:kCMISMediaTypeEntry forKey:@"Content-type"]
+                                    cmisRequest:request
+                                completionBlock:^(CMISHttpResponse *response, NSError *error) {
+                                    if (error) {
+                                        CMISLogError(@"HTTP error when sending atom entry: %@", error);
+                                        if (completionBlock) {
+                                            completionBlock(nil, error);
+                                        }
+                                    } else if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204) {
+                                        if (completionBlock) {
+                                            CMISAtomEntryParser *atomEntryParser = [[CMISAtomEntryParser alloc] initWithData:response.data];
+                                            NSError *parseError = nil;
+                                            [atomEntryParser parseAndReturnError:&parseError];
+                                            if (parseError == nil) {
+                                                completionBlock(atomEntryParser.objectData, nil);
+                                            } else {
+                                                CMISLogError(@"Error while parsing response: %@", [parseError description]);
+                                                completionBlock(nil, [CMISErrors cmisError:parseError cmisErrorCode:kCMISErrorCodeRuntime]);
+                                            }
+                                        }
+                                    } else {
+                                        CMISLogError(@"Invalid http response status code when sending atom entry: %d", response.statusCode);
+                                        CMISLogError(@"Error content: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
+                                        if (completionBlock) {
+                                            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime
+                                                                                 detailedDescription:[NSString stringWithFormat:@"Failed to send atom entry: http status code %li", (long)response.statusCode]]);
+                                        }
+                                    }
+                                }];
+}
+
+- (void)sendAtomEntryXmlToLink:(NSString *)link
+             httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
+                    properties:(CMISProperties *)properties
+            contentInputStream:(NSInputStream *)contentInputStream
+               contentMimeType:(NSString *)contentMimeType
+                 bytesExpected:(unsigned long long)bytesExpected
+                   cmisRequest:(CMISRequest*)request
+               completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+                 progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
+{
+    // Validate param
+    if (link == nil) {
+        CMISLogError(@"Must provide link to send atom entry");
+        if (completionBlock) {
+            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
+        }
+        return;
+    }
+    
+    // The underlying CMISHttpUploadRequest object generates the atom entry. The base64 encoded content is generated on
+    // the fly to support very large files.
+    [self.bindingSession.networkProvider invoke:[NSURL URLWithString:link]
+                                     httpMethod:httpRequestMethod
+                                        session:self.bindingSession
+                                    inputStream:contentInputStream
+                                        headers:[NSDictionary dictionaryWithObject:kCMISMediaTypeEntry forKey:@"Content-type"]
+                                  bytesExpected:bytesExpected
+                                    cmisRequest:request
+                                 cmisProperties:properties
+                                       mimeType:contentMimeType
+                                completionBlock:^(CMISHttpResponse *response, NSError *error) {
+                                    if (error) {
+                                        CMISLogError(@"HTTP error when sending atom entry: %@", error);
+                                        if (completionBlock) {
+                                            completionBlock(nil, error);
+                                        }
+                                    } else if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204) {
+                                        if (completionBlock) {
+                                            NSError *parseError = nil;
+                                            CMISAtomEntryParser *atomEntryParser = [[CMISAtomEntryParser alloc] initWithData:response.data];
+                                            [atomEntryParser parseAndReturnError:&parseError];
+                                            if (parseError == nil) {
+                                                completionBlock(atomEntryParser.objectData, nil);
+                                            } else {
+                                                CMISLogError(@"Error while parsing response: %@", [parseError description]);
+                                                completionBlock(nil, [CMISErrors cmisError:parseError cmisErrorCode:kCMISErrorCodeRuntime]);
+                                            }
+                                        }
+                                    } else {
+                                        CMISLogError(@"Invalid http response status code when sending atom entry: %d", response.statusCode);
+                                        CMISLogError(@"Error content: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
+                                        if (completionBlock) {
+                                            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime
+                                                                                 detailedDescription:[NSString stringWithFormat:@"Failed to send atom entry: http status code %li", (long)response.statusCode]]);
+                                        }
+                                    }
+                                }
+                                  progressBlock:progressBlock];
+}
+
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubObjectService.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubObjectService.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubObjectService.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubObjectService.m Wed Mar 12 21:38:07 2014
@@ -401,7 +401,9 @@
                                            contentMimeType:mimeType
                                              bytesExpected:bytesExpected
                                                cmisRequest:request
-                                           completionBlock:completionBlock
+                                           completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                                               completionBlock(objectData.identifier, error);
+                                           }
                                              progressBlock:progressBlock];
                           }
                       }];
@@ -621,247 +623,4 @@
     return cmisRequest;
 }
 
-#pragma mark Helper methods
-
-- (void)sendAtomEntryXmlToLink:(NSString *)link
-             httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
-                    properties:(CMISProperties *)properties
-                   cmisRequest:(CMISRequest *)request
-               completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
-{
-    // Validate params
-    if (link == nil) {
-        CMISLogError(@"Could not retrieve link from object to do creation or update");
-        if (completionBlock) {
-            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
-        }
-        return;
-    }
-    
-    // Generate XML
-    NSString *writeResult = [self createAtomEntryWriter:properties
-                                        contentFilePath:nil
-                                        contentMimeType:nil
-                                    isXmlStoredInMemory:YES];
-    
-    // Execute call
-    [self.bindingSession.networkProvider invoke:[NSURL URLWithString:link]
-                                     httpMethod:httpRequestMethod
-                                        session:self.bindingSession
-                                           body:[writeResult dataUsingEncoding:NSUTF8StringEncoding]
-                                        headers:[NSDictionary dictionaryWithObject:kCMISMediaTypeEntry forKey:@"Content-type"]
-                                    cmisRequest:request
-                                completionBlock:^(CMISHttpResponse *response, NSError *error) {
-         if (error) {
-             CMISLogError(@"HTTP error when creating/uploading content: %@", error);
-             if (completionBlock) {
-                 completionBlock(nil, error);
-             }
-         } else if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204) {
-             if (completionBlock) {
-                 CMISAtomEntryParser *atomEntryParser = [[CMISAtomEntryParser alloc] initWithData:response.data];
-                 NSError *parseError = nil;
-                 [atomEntryParser parseAndReturnError:&parseError];
-                 if (parseError == nil) {
-                     completionBlock(atomEntryParser.objectData, nil);
-                 } else {
-                     CMISLogError(@"Error while parsing response: %@", [parseError description]);
-                     completionBlock(nil, [CMISErrors cmisError:parseError cmisErrorCode:kCMISErrorCodeUpdateConflict]);
-                 }
-             }
-         } else {
-             CMISLogError(@"Invalid http response status code when creating/uploading content: %d", response.statusCode);
-             CMISLogError(@"Error content: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
-             if (completionBlock) {
-                 completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeConnection]);
-             }
-         }
-     }];
-}
-
-/**
- This method uses a new invoke call on the CMISNetworkProvider. This new method was introduced to allow for base64 encoding while
- streaming. See CMISHttpUploadRequest for more details on how it is done.
- */
-- (void)sendAtomEntryXmlToLink:(NSString *)link
-             httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
-                    properties:(CMISProperties *)properties
-            contentInputStream:(NSInputStream *)contentInputStream
-               contentMimeType:(NSString *)contentMimeType
-                 bytesExpected:(unsigned long long)bytesExpected
-                   cmisRequest:(CMISRequest*)request
-               completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
-                 progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
-{
-    // Validate param
-    if (link == nil) {
-        CMISLogError(@"Could not retrieve link from object to do creation or update");
-        if (completionBlock) {
-            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
-        }
-        return;
-    }
-    
-        
-    [self.bindingSession.networkProvider invoke:[NSURL URLWithString:link]
-                                     httpMethod:HTTP_POST
-                                        session:self.bindingSession
-                                    inputStream:contentInputStream
-                                        headers:[NSDictionary dictionaryWithObject:kCMISMediaTypeEntry forKey:@"Content-type"]
-                                  bytesExpected:bytesExpected
-                                    cmisRequest:request
-                                 cmisProperties:properties
-                                       mimeType:contentMimeType
-                                completionBlock:^(CMISHttpResponse *response, NSError *error) {
-         if (error) {
-             CMISLogError(@"HTTP error when creating/uploading content: %@", error);
-             if (completionBlock) {
-                 completionBlock(nil, error);
-             }
-         } else if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204) {
-             if (completionBlock) {
-                 NSError *parseError = nil;
-                 CMISAtomEntryParser *atomEntryParser = [[CMISAtomEntryParser alloc] initWithData:response.data];
-                 [atomEntryParser parseAndReturnError:&parseError];
-                 if (parseError == nil) {
-                     completionBlock(atomEntryParser.objectData.identifier, nil);
-                 } else {
-                     CMISLogError(@"Error while parsing response: %@", [parseError description]);
-                     completionBlock(nil, [CMISErrors cmisError:parseError cmisErrorCode:kCMISErrorCodeUpdateConflict]);
-                 }
-             }
-         } else {
-             CMISLogError(@"Invalid http response status code when creating/uploading content: %d", response.statusCode);
-             CMISLogError(@"Error content: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
-             if (completionBlock) {
-                 completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime
-                                                  detailedDescription:[NSString stringWithFormat:@"Could not create content: http status code %li", (long)response.statusCode]]);
-             }
-         }
-     }
-       progressBlock:progressBlock];
-}
-
-/**
- This is the original version of the 'sendAtomEntryXmlToLink' method.
- It creates a temporary file to store the base64 encoded data in. It is from this file that the upload starts
- */
-- (void)sendAtomEntryXmlToLinkUsingTmpFile:(NSString *)link
-                         httpRequestMethod:(CMISHttpRequestMethod)httpRequestMethod
-                                properties:(CMISProperties *)properties
-                        contentInputStream:(NSInputStream *)contentInputStream
-                           contentMimeType:(NSString *)contentMimeType
-                             bytesExpected:(unsigned long long)bytesExpected
-                               cmisRequest:(CMISRequest*)request
-                           completionBlock:(void (^)(NSString *objectId, NSError *error))completionBlock
-                             progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
-{
-    // Validate param
-    if (link == nil) {
-        CMISLogError(@"Could not retrieve link from object to do creation or update");
-        if (completionBlock) {
-            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
-        }
-        return;
-    }
-    
-    
-    // Generate XML
-     NSString *writeResult = [self createAtomEntryWriter:properties
-                                      contentInputStream:contentInputStream
-                                         contentMimeType:contentMimeType
-                                     isXmlStoredInMemory:NO];
-     
-     
-     NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:writeResult];
-     NSError *fileSizeError = nil;
-     unsigned long long fileSize = [CMISFileUtil fileSizeForFileAtPath:writeResult error:&fileSizeError];
-     if (fileSizeError) {
-         CMISLogError(@"Could not determine file size of %@ : %@", writeResult, [fileSizeError description]);
-     }
-    
-    [self.bindingSession.networkProvider invoke:[NSURL URLWithString:link]
-                                     httpMethod:HTTP_POST
-                                        session:self.bindingSession
-                                    inputStream:inputStream
-                                        headers:[NSDictionary dictionaryWithObject:kCMISMediaTypeEntry forKey:@"Content-type"]
-                                  bytesExpected:fileSize
-                                    cmisRequest:request
-                                completionBlock:^(CMISHttpResponse *response, NSError *error) {
-                                    // close stream to and delete temporary file
-                                    [inputStream close];
-                                    
-                                     NSError *fileError = nil;
-                                     [[NSFileManager defaultManager] removeItemAtPath:writeResult error:&fileError];
-                                     if (fileError) {
-                                     // the upload itself is not impacted by this error, so do not report it in the completion block
-                                     CMISLogError(@"Could not delete temporary file %@: %@", writeResult, [fileError description]);
-                                     }
-                                    if (error) {
-                                        CMISLogError(@"HTTP error when creating/uploading content: %@", error);
-                                        if (completionBlock) {
-                                            completionBlock(nil, error);
-                                        }
-                                    } else if (response.statusCode == 200 || response.statusCode == 201 || response.statusCode == 204) {
-                                        if (completionBlock) {
-                                            NSError *parseError = nil;
-                                            CMISAtomEntryParser *atomEntryParser = [[CMISAtomEntryParser alloc] initWithData:response.data];
-                                            [atomEntryParser parseAndReturnError:&parseError];
-                                            if (parseError == nil) {
-                                                completionBlock(atomEntryParser.objectData.identifier, nil);
-                                            } else {
-                                                CMISLogError(@"Error while parsing response: %@", [parseError description]);
-                                                completionBlock(nil, [CMISErrors cmisError:parseError cmisErrorCode:kCMISErrorCodeUpdateConflict]);
-                                            }
-                                        }
-                                    } else {
-                                        CMISLogError(@"Invalid http response status code when creating/uploading content: %d", response.statusCode);
-                                        CMISLogError(@"Error content: %@", [[NSString alloc] initWithData:response.data encoding:NSUTF8StringEncoding]);
-                                        if (completionBlock) {
-                                            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeRuntime
-                                                                                 detailedDescription:[NSString stringWithFormat:@"Could not create content: http status code %li", (long)response.statusCode]]);
-                                        }
-                                    }
-                                }
-                                  progressBlock:progressBlock];
-}
-
-
-
-
-/**
- * Helper method: creates a writer for the xml needed to upload a file.
- * The atom entry XML can become huge, as the whole file is stored as base64 in the XML itself
- * Hence, we're allowing to store the atom entry xml in a temporary file and stream the body of the http post
- */
-- (NSString *)createAtomEntryWriter:(CMISProperties *)properties
-                    contentFilePath:(NSString *)contentFilePath
-                    contentMimeType:(NSString *)contentMimeType
-                isXmlStoredInMemory:(BOOL)isXmlStoredInMemory
-{
-    
-    CMISAtomEntryWriter *atomEntryWriter = [[CMISAtomEntryWriter alloc] init];
-    atomEntryWriter.contentFilePath = contentFilePath;
-    atomEntryWriter.mimeType = contentMimeType;
-    atomEntryWriter.cmisProperties = properties;
-    atomEntryWriter.generateXmlInMemory = isXmlStoredInMemory;
-    NSString *writeResult = [atomEntryWriter generateAtomEntryXml];
-    return writeResult;
-}
-
-- (NSString *)createAtomEntryWriter:(CMISProperties *)properties
-                 contentInputStream:(NSInputStream *)contentInputStream
-                    contentMimeType:(NSString *)contentMimeType
-                isXmlStoredInMemory:(BOOL)isXmlStoredInMemory
-{
-    
-    CMISAtomEntryWriter *atomEntryWriter = [[CMISAtomEntryWriter alloc] init];
-    atomEntryWriter.inputStream= contentInputStream;
-    atomEntryWriter.mimeType = contentMimeType;
-    atomEntryWriter.cmisProperties = properties;
-    atomEntryWriter.generateXmlInMemory = isXmlStoredInMemory;
-    NSString *writeResult = [atomEntryWriter generateAtomEntryXml];
-    return writeResult;
-}
-
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubVersioningService.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubVersioningService.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubVersioningService.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/AtomPub/CMISAtomPubVersioningService.m Wed Mar 12 21:38:07 2014
@@ -20,11 +20,14 @@
 #import "CMISAtomPubVersioningService.h"
 #import "CMISAtomPubBaseService+Protected.h"
 #import "CMISAtomPubConstants.h"
+#import "CMISAtomPubObjectService.h"
 #import "CMISHttpResponse.h"
+#import "CMISAtomEntryWriter.h"
 #import "CMISAtomFeedParser.h"
 #import "CMISErrors.h"
 #import "CMISURLUtil.h"
 #import "CMISLog.h"
+#import "CMISFileUtil.h"
 
 @implementation CMISAtomPubVersioningService
 
@@ -105,4 +108,183 @@
     return request;
 }
 
+- (CMISRequest*)checkOut:(NSString *)objectId
+         completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+{
+    // Validate params
+    if (!objectId) {
+        CMISLogError(@"Must provide an objectId when checking out");
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeObjectNotFound detailedDescription:nil]);
+        return nil;
+    }
+    
+    NSString *checkedoutUrlString = [self.bindingSession objectForKey:kCMISBindingSessionKeyCheckedoutCollection];
+    if (checkedoutUrlString == nil) {
+        CMISLogDebug(@"Checkedout not supported!");
+        completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeNotSupported detailedDescription:nil]);
+        return nil;
+    }
+    
+    CMISProperties *properties = [CMISProperties new];
+    [properties addProperty:[CMISPropertyData createPropertyForId:kCMISPropertyObjectId idValue:objectId]];
+
+    CMISRequest *request = [CMISRequest new];
+    
+    // send an atom entry to check out the file
+    [self sendAtomEntryXmlToLink:checkedoutUrlString
+               httpRequestMethod:HTTP_POST
+                      properties:properties
+                     cmisRequest:request
+                 completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                     if (error) {
+                         completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+                     } else {
+                         completionBlock(objectData, nil);
+                     }
+                 }];
+
+    return request;
+}
+
+- (CMISRequest*)cancelCheckOut:(NSString *)objectId
+               completionBlock:(void (^)(BOOL checkoutCancelled, NSError *error))completionBlock
+{
+    // Validate params
+    if (!objectId) {
+        CMISLogError(@"Must provide an objectId when cancelling check out");
+        completionBlock(NO, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeObjectNotFound detailedDescription:nil]);
+        return nil;
+    }
+    
+    CMISRequest *request = [CMISRequest new];
+
+    [self workingCopyLinkForObjectId:objectId completionBlock:^(NSString *workingCopyLink, NSError *error) {
+        NSURL *deleteUrl = [NSURL URLWithString:workingCopyLink];
+        [self.bindingSession.networkProvider invokeDELETE:deleteUrl
+                                                  session:self.bindingSession
+                                              cmisRequest:request
+                                          completionBlock:^(CMISHttpResponse *httpResponse, NSError *error) {
+            if (httpResponse) {
+                completionBlock(YES, nil);
+            } else {
+                completionBlock(NO, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+            }
+        }];
+    }];
+    
+    return request;
+}
+
+- (CMISRequest*)checkIn:(NSString *)objectId
+         asMajorVersion:(BOOL)asMajorVersion
+               filePath:(NSString *)filePath
+               mimeType:(NSString *)mimeType
+             properties:(CMISProperties *)properties
+         checkinComment:(NSString *)checkinComment
+        completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+          progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
+{
+    NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
+    if (inputStream == nil) {
+        CMISLogError(@"Could not find file %@", filePath);
+        if (completionBlock) {
+            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
+        }
+        return nil;
+    }
+    
+    NSError *fileError = nil;
+    unsigned long long fileSize = [CMISFileUtil fileSizeForFileAtPath:filePath error:&fileError];
+    if (fileError) {
+        CMISLogError(@"Could not determine size of file %@: %@", filePath, [fileError description]);
+    }
+    
+    return [self checkIn:objectId
+          asMajorVersion:asMajorVersion
+             inputStream:inputStream
+           bytesExpected:fileSize
+                mimeType:mimeType
+              properties:properties
+          checkinComment:checkinComment
+         completionBlock:completionBlock
+           progressBlock:progressBlock];
+}
+
+- (CMISRequest*)checkIn:(NSString *)objectId
+         asMajorVersion:(BOOL)asMajorVersion
+            inputStream:(NSInputStream *)inputStream
+          bytesExpected:(unsigned long long)bytesExpected
+               mimeType:(NSString *)mimeType
+             properties:(CMISProperties *)properties
+         checkinComment:(NSString *)checkinComment
+        completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+          progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
+{
+    // Validate params
+    if (!objectId) {
+        CMISLogError(@"Must provide an objectId when checking in");
+        completionBlock(NO, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeObjectNotFound detailedDescription:nil]);
+        return nil;
+    }
+    
+    CMISRequest *request = [CMISRequest new];
+    
+    [self workingCopyLinkForObjectId:objectId completionBlock:^(NSString *workingCopyLink, NSError *error) {
+        
+        // add the necessary parameters to the URL
+        NSString *link = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterCheckin value:kCMISParameterValueTrue urlString:workingCopyLink];
+        link = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterMajor
+                                                    value:asMajorVersion ? kCMISParameterValueTrue : kCMISParameterValueFalse urlString:link];
+        if (checkinComment != nil)
+        {
+            link = [CMISURLUtil urlStringByAppendingParameter:kCMISParameterCheckinComment value:checkinComment urlString:link];
+        }
+        
+        // send an atom entry to have the file checked in
+        [self sendAtomEntryXmlToLink:link
+                   httpRequestMethod:HTTP_PUT
+                          properties:properties
+                  contentInputStream:inputStream
+                     contentMimeType:mimeType
+                       bytesExpected:bytesExpected
+                         cmisRequest:request
+                     completionBlock:^(CMISObjectData *objectData, NSError *error) {
+                         if (error) {
+                             completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+                         } else {
+                             completionBlock(objectData, nil);
+                         }
+                     }
+                       progressBlock:progressBlock];
+    }];
+    
+    return request;
+}
+
+#pragma mark Internal methods
+
+- (CMISRequest *)workingCopyLinkForObjectId:(NSString *)objectId completionBlock:(void(^)(NSString *workingCopyLink, NSError *error))completionBlock
+{
+    CMISRequest *request = [CMISRequest new];
+    [self loadLinkForObjectId:objectId
+                     relation:kCMISLinkRelationSelf
+                  cmisRequest:request
+              completionBlock:^(NSString *selfLink, NSError *error) {
+        if (!selfLink) {
+            completionBlock(nil, [CMISErrors createCMISErrorWithCode:kCMISErrorCodeInvalidArgument detailedDescription:nil]);
+        } else {
+            // Prefer working copy link if available
+            [self loadLinkForObjectId:objectId
+                             relation:kCMISLinkRelationWorkingCopy
+                          cmisRequest:request
+                      completionBlock:^(NSString *workingCopyLink, NSError *error) {
+                NSString *link = (nil != workingCopyLink) ? workingCopyLink : selfLink;
+                completionBlock(link, nil);
+            }];
+        }
+    }];
+    
+    return request;
+}
+
 @end
\ No newline at end of file

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.h?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.h Wed Mar 12 21:38:07 2014
@@ -29,6 +29,7 @@ extern NSString * const kCMISBindingSess
 extern NSString * const kCMISBindingSessionKeyQueryUri;
 
 extern NSString * const kCMISBindingSessionKeyQueryCollection;
+extern NSString * const kCMISBindingSessionKeyCheckedoutCollection;
 
 extern NSString * const kCMISBindingSessionKeyLinkCache;
 

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISBindingSession.m Wed Mar 12 21:38:07 2014
@@ -26,6 +26,7 @@ NSString * const kCMISBindingSessionKeyT
 NSString * const kCMISBindingSessionKeyQueryUri = @"cmis_session_key_query_uri";
 
 NSString * const kCMISBindingSessionKeyQueryCollection = @"cmis_session_key_query_collection";
+NSString * const kCMISBindingSessionKeyCheckedoutCollection = @"cmis_session_key_checkedout_collection";
 
 NSString * const kCMISBindingSessionKeyLinkCache = @"cmis_session_key_link_cache";
 

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISVersioningService.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISVersioningService.h?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISVersioningService.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Bindings/CMISVersioningService.h Wed Mar 12 21:38:07 2014
@@ -23,6 +23,7 @@
 @class CMISCollection;
 @class CMISObject;
 @class CMISObjectData;
+@class CMISProperties;
 @class CMISRequest;
 
 @protocol CMISVersioningService <NSObject>
@@ -61,6 +62,66 @@
             includeAllowableActions:(BOOL)includeAllowableActions
                     completionBlock:(void (^)(NSArray *objects, NSError *error))completionBlock;
 
+/**
+ * Create a private working copy of a document given an object identifier.
+ *
+ * @param objectId
+ * @param completionBlock returns PWC object data or nil
+ */
+- (CMISRequest*)checkOut:(NSString *)objectId
+         completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock;
+
+/**
+ * Reverses the effect of a check-out.
+ *
+ * @param objectId
+ * @param completionBlock returns object data or nil
+ */
+- (CMISRequest*)cancelCheckOut:(NSString *)objectId
+               completionBlock:(void (^)(BOOL checkOutCancelled, NSError *error))completionBlock;
+
+/**
+ * Checks-in the private working copy (PWC) document from the given path.
+ *
+ * @param objectId the identifier for the PWC
+ * @param asMajorVersion indicator if the new version should become a major (YES) or minor (NO) version
+ * @param filePath (optional) Path to the file containing the content to be uploaded
+ * @param mimeType (optional) Mime type of the content to be uploaded
+ * @param properties (optional) the property values that must be applied to the checked-in document object
+ * @param checkinComment (optional) a version comment
+ * @param completionBlock returns object data or nil
+ * @param progressBlock periodic file upload status
+ */
+- (CMISRequest*)checkIn:(NSString *)objectId
+         asMajorVersion:(BOOL)asMajorVersion
+               filePath:(NSString *)filePath
+               mimeType:(NSString *)mimeType
+             properties:(CMISProperties *)properties
+         checkinComment:(NSString *)checkinComment
+        completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+          progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock;
 
+/**
+ * Checks-in the private working copy (PWC) document from the given an input stream.
+ *
+ * @param objectId the identifier for the PWC
+ * @param asMajorVersion indicator if the new version should become a major (YES) or minor (NO) version
+ * @param inputStream (optional) Input stream containing the content to be uploaded
+ * @param bytesExpected The size of content to be uploaded (must be provided if an inputStream is given)
+ * @param mimeType (optional) Mime type of the content to be uploaded
+ * @param properties (optional) the property values that must be applied to the checked-in document object
+ * @param checkinComment (optional) a version comment
+ * @param completionBlock returns object data or nil
+ * @param progressBlock periodic file upload status
+ */
+- (CMISRequest*)checkIn:(NSString *)objectId
+         asMajorVersion:(BOOL)asMajorVersion
+            inputStream:(NSInputStream *)inputStream
+          bytesExpected:(unsigned long long)bytesExpected
+               mimeType:(NSString *)mimeType
+             properties:(CMISProperties *)properties
+         checkinComment:(NSString *)checkinComment
+        completionBlock:(void (^)(CMISObjectData *objectData, NSError *error))completionBlock
+          progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock;
 
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.h
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.h?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.h (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.h Wed Mar 12 21:38:07 2014
@@ -120,4 +120,37 @@
  */
 - (CMISRequest*)deleteAllVersionsWithCompletionBlock:(void (^)(BOOL documentDeleted, NSError *error))completionBlock;
 
+/**
+ * Checkout the document and return the PWC via the completion block
+ */
+- (CMISRequest*)checkOutWithCompletionBlock:(void (^)(CMISDocument *privateWorkingCopy, NSError *error))completionBlock;
+
+/**
+ * Cancel checkout if this is a PWC
+ */
+- (CMISRequest*)cancelCheckOutWithCompletionBlock:(void (^)(BOOL checkoutCancelled, NSError *error))completionBlock;
+
+/**
+ * Checkin this PWC from a specified file path and return the checked-in document
+ */
+- (CMISRequest*)checkInAsMajorVersion:(BOOL)majorVersion
+                             filePath:(NSString *)filePath
+                             mimeType:(NSString *)mimeType
+                           properties:(CMISProperties *)properties
+                       checkinComment:(NSString *)checkinComment
+                      completionBlock:(void (^)(CMISDocument *document, NSError *error))completionBlock
+                        progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock;
+
+/**
+ * Checkin this PWC from a specified input stream and return the checked-in document
+ */
+- (CMISRequest*)checkInAsMajorVersion:(BOOL)majorVersion
+                          inputStream:(NSInputStream *)inputStream
+                        bytesExpected:(unsigned long long)bytesExpected
+                             mimeType:(NSString *)mimeType
+                           properties:(CMISProperties *)properties
+                       checkinComment:(NSString *)checkinComment
+                      completionBlock:(void (^)(CMISDocument *document, NSError *error))completionBlock
+                        progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock;
+
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMIS/Client/CMISDocument.m Wed Mar 12 21:38:07 2014
@@ -47,7 +47,7 @@
 - (id)initWithObjectData:(CMISObjectData *)objectData session:(CMISSession *)session
 {
     self = [super initWithObjectData:objectData session:session];
-    if (self){
+    if (self) {
         self.contentStreamId = [[objectData.properties.propertiesDictionary objectForKey:kCMISPropertyContentStreamId] firstValue];
         self.contentStreamMediaType = [[objectData.properties.propertiesDictionary objectForKey:kCMISPropertyContentStreamMediaType] firstValue];
         self.contentStreamLength = [[[objectData.properties.propertiesDictionary objectForKey:kCMISPropertyContentStreamLength] firstValue] unsignedLongLongValue];
@@ -182,4 +182,98 @@
     return [self.binding.objectService deleteObject:self.identifier allVersions:YES completionBlock:completionBlock];
 }
 
+- (CMISRequest *)checkOutWithCompletionBlock:(void (^)(CMISDocument *privateWorkingCopy, NSError *error))completionBlock
+{
+    return [self.binding.versioningService checkOut:self.identifier completionBlock:^(CMISObjectData *objectData, NSError *error) {
+        if (error) {
+            completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+        } else {
+            [self.session.objectConverter convertObject:objectData completionBlock:^(CMISObject *object, NSError *error) {
+                if (error) {
+                    [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning];
+                } else {
+                    completionBlock((CMISDocument*)object, nil);
+                }
+            }];
+        }
+    }];
+}
+
+- (CMISRequest *)cancelCheckOutWithCompletionBlock:(void (^)(BOOL, NSError *))completionBlock
+{
+    return [self.binding.versioningService cancelCheckOut:self.identifier completionBlock:^(BOOL checkOutCancelled, NSError *error) {
+        if (error) {
+            completionBlock(NO, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+        } else {
+            completionBlock(YES, nil);
+        }
+    }];
+}
+
+- (CMISRequest*)checkInAsMajorVersion:(BOOL)majorVersion
+                             filePath:(NSString *)filePath
+                             mimeType:(NSString *)mimeType
+                           properties:(CMISProperties *)properties
+                       checkinComment:(NSString *)checkinComment
+                      completionBlock:(void (^)(CMISDocument *document, NSError *error))completionBlock
+                        progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
+{
+    return [self.binding.versioningService checkIn:self.identifier
+                                    asMajorVersion:majorVersion
+                                          filePath:filePath
+                                          mimeType:mimeType
+                                        properties:properties
+                                    checkinComment:checkinComment
+                                   completionBlock:^(CMISObjectData *objectData, NSError *error) {
+        if (error) {
+            completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+        } else {
+            // convert the object data to document object
+            [self.session.objectConverter convertObject:objectData completionBlock:^(CMISObject *object, NSError *error) {
+                if (error) {
+                    [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning];
+                } else {
+                    completionBlock((CMISDocument*)object, nil);
+                }
+            }];
+        }
+    } progressBlock:^(unsigned long long bytesUploaded, unsigned long long bytesTotal) {
+        progressBlock(bytesUploaded, bytesTotal);
+    }];
+}
+
+- (CMISRequest*)checkInAsMajorVersion:(BOOL)majorVersion
+                          inputStream:(NSInputStream *)inputStream
+                        bytesExpected:(unsigned long long)bytesExpected
+                             mimeType:(NSString *)mimeType
+                           properties:(CMISProperties *)properties
+                       checkinComment:(NSString *)checkinComment
+                      completionBlock:(void (^)(CMISDocument *document, NSError *error))completionBlock
+                        progressBlock:(void (^)(unsigned long long bytesUploaded, unsigned long long bytesTotal))progressBlock
+{
+    return [self.binding.versioningService checkIn:self.identifier
+                                    asMajorVersion:majorVersion
+                                       inputStream:inputStream
+                                     bytesExpected:bytesExpected
+                                          mimeType:mimeType
+                                        properties:properties
+                                    checkinComment:checkinComment
+                                   completionBlock:^(CMISObjectData *objectData, NSError *error) {
+        if (error) {
+            completionBlock(nil, [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning]);
+        } else {
+            // convert the object data to document object
+            [self.session.objectConverter convertObject:objectData completionBlock:^(CMISObject *object, NSError *error) {
+                if (error) {
+                    [CMISErrors cmisError:error cmisErrorCode:kCMISErrorCodeVersioning];
+                } else {
+                    completionBlock((CMISDocument*)object, nil);
+                }
+            }];
+        }
+    } progressBlock:^(unsigned long long bytesUploaded, unsigned long long bytesTotal) {
+        progressBlock(bytesUploaded, bytesTotal);
+    }];
+}
+
 @end

Modified: chemistry/objectivecmis/trunk/ObjectiveCMISTests/CMISBaseTest.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMISTests/CMISBaseTest.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMISTests/CMISBaseTest.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMISTests/CMISBaseTest.m Wed Mar 12 21:38:07 2014
@@ -88,7 +88,8 @@
     }
     [CMISSession connectWithSessionParameters:self.parameters completionBlock:^(CMISSession *session, NSError *error){
         if (nil == session) {
-
+            XCTFail(@"Failed to create session: %@", error.localizedDescription);
+            self.testCompleted = YES;
         } else {
             self.session = session;
             XCTAssertTrue(self.session.isAuthenticated, @"Session should be authenticated");

Modified: chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m
URL: http://svn.apache.org/viewvc/chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m?rev=1576931&r1=1576930&r2=1576931&view=diff
==============================================================================
--- chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m (original)
+++ chemistry/objectivecmis/trunk/ObjectiveCMISTests/ObjectiveCMISTests.m Wed Mar 12 21:38:07 2014
@@ -1714,4 +1714,113 @@
      }];
 }
 
+- (void)testCheckoutCheckin
+{
+    [self runTest:^ {
+        // Upload test file
+        [self uploadTestFileWithCompletionBlock:^(CMISDocument *testDocument) {
+            
+            XCTAssertNotNil(testDocument, @"Expected testDocument to be uploaded!");
+            
+            // checkout the uploaded test document
+            [testDocument checkOutWithCompletionBlock:^(CMISDocument *privateWorkingCopy, NSError *error) {
+                
+                // check we got the working copy
+                XCTAssertNotNil(privateWorkingCopy, @"Expected to recieve the private working copy object");
+                
+                // checkin the test document
+                NSString *updatedFilePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"test_file_2.txt" ofType:nil];
+                [privateWorkingCopy checkInAsMajorVersion:YES filePath:updatedFilePath mimeType:@"text/plain" properties:nil checkinComment:@"Next version" completionBlock:^(CMISDocument *checkedInDocument, NSError *error) {
+                    
+                    // check we got back the checked in document
+                    XCTAssertNotNil(checkedInDocument, @"Expected to receive the checked in document object");
+
+                    // validate the content was updated
+                    NSString *tempDownloadFilePath = [NSString stringWithFormat:@"%@/temp_download_file.txt", NSTemporaryDirectory()];
+                    [checkedInDocument downloadContentToFile:tempDownloadFilePath completionBlock:^(NSError *error) {
+                        
+                        // check the content has been updated
+                        NSString *contentOfDownloadedFile = [NSString stringWithContentsOfFile:tempDownloadFilePath encoding:NSUTF8StringEncoding error:nil];
+                        XCTAssertEqualObjects(@"In theory, there is no difference between theory and practice. But in practice, there is.",
+                                              contentOfDownloadedFile, @"Downloaded file content does not match, it was: '%@'", contentOfDownloadedFile);
+                        
+                        // retrieve all versions of the document and make sure there are 2 and the last one has the correct info
+                        [checkedInDocument retrieveAllVersionsWithCompletionBlock:^(CMISCollection *allVersionsOfDocument, NSError *error) {
+                            
+                            // make sure there are 2 versions
+                            XCTAssertTrue(allVersionsOfDocument.items.count == 2,
+                                          @"Expected to find 2 versions but there were %lu", (unsigned long)allVersionsOfDocument.items.count);
+                            
+                            // get the first item (should be the latest one) and check the version label and checkin comment
+                            CMISDocument *secondVersion = allVersionsOfDocument.items[0];
+                            XCTAssertTrue([secondVersion.versionLabel isEqualToString:@"2.0"],
+                                          @"Expected version label to be 2.0 but was %@", secondVersion.versionLabel);
+                            XCTAssertTrue(secondVersion.isLatestVersion, @"Expected document to be the latest version");
+                            XCTAssertTrue(secondVersion.isLatestMajorVersion, @"Expected document to be the latest major version");
+                            XCTAssertTrue(secondVersion.isMajorVersion, @"Expected document to be a major version");
+                            NSString *checkinComment = [secondVersion.properties propertyValueForId:kCMISPropertyCheckinComment];
+                            XCTAssertTrue([checkinComment isEqualToString:@"Next version"],
+                                          @"Expected checkin comment to be 'Next version' but was %@", checkinComment);
+                            
+                            CMISDocument *firstVersion = allVersionsOfDocument.items[1];
+                            XCTAssertTrue([firstVersion.versionLabel isEqualToString:@"1.0"],
+                                          @"Expected version label to be 1.0 but was %@", firstVersion.versionLabel);
+                            XCTAssertFalse(firstVersion.isLatestVersion, @"Did not expect document to be the latest version");
+                            XCTAssertFalse(firstVersion.isLatestMajorVersion, @"Did not expect document to be the latest major version");
+                            XCTAssertTrue(firstVersion.isMajorVersion, @"Expected document to be a major version");
+                            
+                            // delete the document
+                            [self deleteDocumentAndVerify:checkedInDocument completionBlock:^{
+                                // mark the test as completed
+                                self.testCompleted = YES;
+                            }];
+                        }];
+                        
+                    } progressBlock:^(unsigned long long bytesDownloaded, unsigned long long bytesTotal) {
+                          CMISLogDebug(@"download progress %i/%i", bytesDownloaded, bytesTotal);
+                    }];
+                } progressBlock:^(unsigned long long bytesUploaded, unsigned long long bytesTotal) {
+                      CMISLogDebug(@"upload progress %i/%i", bytesUploaded, bytesTotal);
+                }];
+            }];
+        }];
+    }];
+}
+
+- (void)testCancelCheckout
+{
+    [self runTest:^ {
+        // Upload test file
+        [self uploadTestFileWithCompletionBlock:^(CMISDocument *testDocument) {
+            
+            XCTAssertNotNil(testDocument, @"Expected testDocument to be uploaded!");
+            
+            // checkout the uploaded test document
+            [testDocument checkOutWithCompletionBlock:^(CMISDocument *privateWorkingCopy, NSError *checkOutError) {
+                XCTAssertNotNil(privateWorkingCopy, @"Expected to recieve the private working copy object");
+                
+                // cancel checkout of the test document
+                [privateWorkingCopy cancelCheckOutWithCompletionBlock:^(BOOL checkoutCancelled, NSError *cancelError) {
+                    
+                    // make sure the pwc has been deleted
+                    [self.session retrieveObject:privateWorkingCopy.identifier completionBlock:^(CMISObject *object, NSError *retrieveError) {
+                        
+                        // make sure the object is nill and error is not nil
+                        XCTAssertNil(object, @"Did not expect to receive a document, the pwc should have been deleted");
+                        XCTAssertNotNil(retrieveError, @"Expected there to be an error object");
+                        XCTAssertTrue(retrieveError.code == kCMISErrorCodeObjectNotFound,
+                                      @"Expected the error code to be 257 (kCMISErrorCodeObjectNotFound) but was %ld", (long)retrieveError.code);
+                        
+                        // delete the document
+                        [self deleteDocumentAndVerify:testDocument completionBlock:^{
+                            // mark the test as completed
+                            self.testCompleted = YES;
+                        }];
+                    }];
+                }];
+            }];
+        }];
+    }];
+}
+
 @end