From 186d6d400232f9ead8d92ec6385581ffca6cd22b Mon Sep 17 00:00:00 2001
From: cramakri <cramakri>
Date: Thu, 15 Nov 2012 15:42:08 +0000
Subject: [PATCH] Added support for a Dead openBIS connection that does not
 actually retrieve data

SVN: 27645
---
 .../BisKit/Classes/CISDOBConnection.h         | 18 +++++
 .../BisKit/Classes/CISDOBConnection.m         | 18 +++++
 .../BisKit/Classes/CISDOBIpadServiceManager.h | 11 ++-
 .../BisKit/Classes/CISDOBIpadServiceManager.m | 69 ++++++++++++-------
 .../Tests/CISDOBIpadServiceManagerTest.m      | 22 ++++++
 5 files changed, 112 insertions(+), 26 deletions(-)

diff --git a/openbis-ipad/BisKit/Classes/CISDOBConnection.h b/openbis-ipad/BisKit/Classes/CISDOBConnection.h
index 0d0f19df750..49f1591d450 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBConnection.h
+++ b/openbis-ipad/BisKit/Classes/CISDOBConnection.h
@@ -24,6 +24,13 @@
 #import <Foundation/Foundation.h>
 #import "CISDOBShared.h"
 
+//! The error domain for errors in the Connection layer
+FOUNDATION_EXPORT NSString *const CISDOBConnectionErrorDomain;
+
+enum CISDOBConnectionErrorCode {
+    kCISDOBConnectionError_NoServerAvailable = 1,
+};
+
 
 /**
  *  \brief A connection to an openBIS server.
@@ -83,3 +90,14 @@
 }
 
 @end
+
+/**
+ *  \brief A fake connection to openBIS
+ *
+ */
+@interface CISDOBDeadConnection : CISDOBConnection {
+@private
+    
+}
+
+@end
diff --git a/openbis-ipad/BisKit/Classes/CISDOBConnection.m b/openbis-ipad/BisKit/Classes/CISDOBConnection.m
index 9fbc5ee99e0..600770d6d2f 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBConnection.m
+++ b/openbis-ipad/BisKit/Classes/CISDOBConnection.m
@@ -25,6 +25,8 @@
 #import "CISDOBJsonRpcCall.h"
 #import "CISDOBAsyncCall.h"
 
+NSString *const CISDOBConnectionErrorDomain = @"CISDOBConnectionErrorDomain";
+
 // Internal connection call that includes the private state
 @interface CISDOBConnectionCall : CISDOBAsyncCall {
 @private
@@ -158,6 +160,22 @@
 
 @end
 
+@implementation CISDOBDeadConnection
+
+- (void)executeCall:(CISDOBConnectionCall *)call
+{
+    void (^notifyBlock)(void) = ^ {
+        NSString *errorMessage = @"The app is not connected to an openBIS server.";
+        NSDictionary *userInfo =
+            [NSDictionary dictionaryWithObjectsAndKeys: errorMessage, NSLocalizedDescriptionKey, nil];
+        NSError *error = [NSError errorWithDomain: CISDOBConnectionErrorDomain code: kCISDOBConnectionError_NoServerAvailable userInfo: userInfo];
+        call.failWrapper(error);
+    };
+    [[NSOperationQueue mainQueue] addOperationWithBlock: notifyBlock];    
+}
+
+@end
+
 
 @implementation CISDOBConnectionCall
 
diff --git a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h
index 222f7247ccc..a11a23991f7 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h
+++ b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.h
@@ -25,7 +25,11 @@
 #import "CISDOBShared.h"
 
 //
-// The names of the notifications posted by the service manager
+// The names of the notifications posted by the service manager.
+//
+// The notifications include a userInfo dictionary with the following
+// keys and values:
+//      NSUnderlyingErrorKey If in the dictionary, the error that occured when making the call.
 //
 FOUNDATION_EXPORT NSString *const CISDOBIpadServiceWillLoginNotification;
 FOUNDATION_EXPORT NSString *const CISDOBIpadServiceDidLoginNotification;
@@ -44,12 +48,14 @@ FOUNDATION_EXPORT NSString *const CISDOBIpadServiceDidSynchEntitiesNotification;
 
 
 
+
 @class CISDOBIpadService, CISDOBAsyncCall, CISDOBIpadEntity;
 /**
  * \brief A class that manages a connection to the openBIS iPad service, caching data locally as CISDOBIpadEntity objects.
  */
 @interface CISDOBIpadServiceManager : NSObject
 
+@property (readonly) NSURL *openbisUrl;
 @property (readonly, strong) CISDOBIpadService *service;
 @property (readonly, strong) NSURL *storeUrl;
 @property (readonly, strong) NSManagedObjectContext *managedObjectContext;
@@ -63,6 +69,9 @@ FOUNDATION_EXPORT NSString *const CISDOBIpadServiceDidSynchEntitiesNotification;
 // Initialization
 - (id)initWithStoreUrl:(NSURL *)storeUrl openbisUrl:(NSURL *)openbisUrl trusted:(BOOL)trusted error:(NSError **)error;
 
+// Properties
+- (void)setOpenbisUrl:(NSURL *)openbisUrl trusted:(BOOL)trusted;
+
 // Actions
 
 //! Log the user into the openBIS instance
diff --git a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m
index 1abd1837bdc..d640fbf9d7f 100644
--- a/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m
+++ b/openbis-ipad/BisKit/Classes/CISDOBIpadServiceManager.m
@@ -42,12 +42,11 @@ NSString *const CISDOBIpadServiceDidSynchEntitiesNotification = @"CISDOBIpadServ
 // Internal service call that includes the private state
 @interface CISDOBIpadServiceManagerCall : CISDOBAsyncCall
 
-@property(weak) CISDOBIpadServiceManager *serviceManager;
+@property(weak, nonatomic) CISDOBIpadServiceManager *serviceManager;
 @property(nonatomic) CISDOBAsyncCall *serviceCall;
-@property(copy) NSString *willCallNotificationName;
-@property(copy) NSString *didCallNotificationName;
 
-@property(nonatomic) BOOL sendSynchNotifications;
+@property(copy, nonatomic) NSString *willCallNotificationName;  //<! The notification called before the call takes place, may be nil
+@property(copy, nonatomic) NSString *didCallNotificationName;   //<! The notification called after the call has completed, may be nil
 
 // Initialization
 - (id)initWithServiceManager:(CISDOBIpadServiceManager *)serviceManager serviceCall:(CISDOBAsyncCall *)call;
@@ -73,7 +72,7 @@ NSString *const CISDOBIpadServiceDidSynchEntitiesNotification = @"CISDOBIpadServ
 
 // Actions
 - (void)run;
-- (void)notifyCallOfResult:(id)args;
+- (void)notifyCallOfResult;
 
 @end
 
@@ -108,7 +107,13 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
 {
     if (!(self = [super init])) return nil;
     
-    CISDOBLiveConnection *connection = [[CISDOBLiveConnection alloc] initWithUrl: openbisUrl trusted: trusted];
+    CISDOBConnection *connection;
+    if (openbisUrl) {
+        connection = [[CISDOBLiveConnection alloc] initWithUrl: openbisUrl trusted: trusted];
+    } else {
+        connection = [[CISDOBDeadConnection alloc] init];
+    }
+    
     _storeUrl = [storeUrl copy];
     _service = [[CISDOBIpadService alloc] initWithConnection: connection];
     _managedObjectContext = GetMainThreadManagedObjectContext(self.storeUrl, error);
@@ -123,6 +128,28 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     return self;
 }
 
+- (NSURL *)openbisUrl
+{
+    if (![_service.connection isKindOfClass: [CISDOBLiveConnection class]]) return nil;
+    
+    return ((CISDOBLiveConnection *) _service.connection).url;
+
+}
+
+- (void)setOpenbisUrl:(NSURL *)openbisUrl trusted:(BOOL)trusted;
+{
+    if ([self.openbisUrl isEqual: openbisUrl]) return;
+    
+    CISDOBConnection *connection;
+    if (openbisUrl) {
+        connection = [[CISDOBLiveConnection alloc] initWithUrl: openbisUrl trusted: trusted];
+    } else {
+        connection = [[CISDOBDeadConnection alloc] init];
+    }
+    
+    _service = [[CISDOBIpadService alloc] initWithConnection: connection];
+}
+
 - (NSString *)sessionToken
 {
    return ((CISDOBLiveConnection *)(self.service.connection)).sessionToken;
@@ -131,10 +158,6 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
 - (void)syncEntities:(NSArray *)rawEntities pruning:(BOOL)prune notifying:(CISDOBIpadServiceManagerCall *)managerCall
 {
     void (^syncBlock)(void) = ^{
-        if (managerCall.didCallNotificationName) {
-            [[NSNotificationCenter defaultCenter] postNotificationName: managerCall.didCallNotificationName object: self];
-        }
-        
         [[NSNotificationCenter defaultCenter] postNotificationName: CISDOBIpadServiceWillSynchEntitiesNotification object: self];
         
         // Run the synchronizer in the background thread
@@ -153,7 +176,7 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
                     notifySynchronizer.error = error;
                 }
             }
-            [notifySynchronizer notifyCallOfResult: nil];
+            [notifySynchronizer notifyCallOfResult];
         };
         [[NSOperationQueue mainQueue] addOperationWithBlock: notifyBlock];
     };
@@ -165,11 +188,11 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     CISDOBIpadServiceManagerCall *managerCall = [[CISDOBIpadServiceManagerCall alloc] initWithServiceManager: self serviceCall: serviceCall];
     
     serviceCall.success = ^(id result) {
-        // Update the cache
+        // Update the cache and call the managerCall success when done
         [self syncEntities: result pruning: prune notifying: managerCall];
     };    
     
-    serviceCall.fail = ^(NSError *error) { if (managerCall.fail) managerCall.fail(error); };
+    serviceCall.fail = ^(NSError *error) { [managerCall notifyFailure: error]; };
     
     return managerCall;
 }
@@ -186,7 +209,6 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     call.success = ^(id result) { [managerCall notifySuccess: result]; };
     managerCall.willCallNotificationName = CISDOBIpadServiceWillLoginNotification;
     managerCall.didCallNotificationName = CISDOBIpadServiceDidLoginNotification;
-    managerCall.sendSynchNotifications = NO;
     return managerCall;
 }
 
@@ -198,7 +220,6 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     
     managerCall.willCallNotificationName = CISDOBIpadServiceWillRetrieveRootLevelEntitiesNotification;
     managerCall.didCallNotificationName = CISDOBIpadServiceDidRetrieveRootLevelEntitiesNotification;
-    managerCall.sendSynchNotifications = YES;
     
     return managerCall;
 }
@@ -210,7 +231,6 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     
     managerCall.willCallNotificationName = CISDOBIpadServiceWillDrillOnEntityNotification;
     managerCall.didCallNotificationName = CISDOBIpadServiceDidDrillOnEntityNotification;
-    managerCall.sendSynchNotifications = YES;
     
     return managerCall;
 }
@@ -222,7 +242,6 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     
     managerCall.willCallNotificationName = CISDOBIpadServiceWillRetrieveDetailsForEntityNotification;
     managerCall.didCallNotificationName = CISDOBIpadServiceDidRetrieveDetailsForEntityNotification;
-    managerCall.sendSynchNotifications = YES;
     
     return managerCall;
 }
@@ -285,25 +304,25 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     return self;
 }
 
-- (void)sendCompletionNotification
+- (void)sendCompletionNotification:(NSError *)errorOrNil
 {
-    if (self.sendSynchNotifications) {
-        // This is handled elsewhere
-    } else if (self.didCallNotificationName) {
-        [[NSNotificationCenter defaultCenter] postNotificationName: self.didCallNotificationName object: self.serviceManager];
+    if (self.didCallNotificationName) {
+        NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+        if (errorOrNil) [userInfo setValue: errorOrNil forKey: NSUnderlyingErrorKey];
+        [[NSNotificationCenter defaultCenter] postNotificationName: self.didCallNotificationName object: self.serviceManager userInfo: userInfo];
     }
 }
 
 - (void)notifySuccess:(id)result
 {
+    [self sendCompletionNotification: nil];
     if (self.success) self.success(result);
-    [self sendCompletionNotification];
 }
 
 - (void)notifyFailure:(NSError *)error
 {
+    [self sendCompletionNotification: error];
     if (self.fail) self.fail(error);
-    [self sendCompletionNotification];
 }
 
 - (void)start
@@ -388,7 +407,7 @@ static NSManagedObjectContext* GetMainThreadManagedObjectContext(NSURL* storeUrl
     self.error = nil;
 }
 
-- (void)notifyCallOfResult:(id)args
+- (void)notifyCallOfResult
 {
     if (self.error) {
         [self.managerCall notifyFailure: self.error];
diff --git a/openbis-ipad/BisKit/Tests/CISDOBIpadServiceManagerTest.m b/openbis-ipad/BisKit/Tests/CISDOBIpadServiceManagerTest.m
index d8244a0fbc4..af33e8b762c 100644
--- a/openbis-ipad/BisKit/Tests/CISDOBIpadServiceManagerTest.m
+++ b/openbis-ipad/BisKit/Tests/CISDOBIpadServiceManagerTest.m
@@ -248,4 +248,26 @@
     [self checkFindingChildren];
 }
 
+- (void)testNilUrl
+{
+    [self.serviceManager setOpenbisUrl: nil trusted: YES];
+    
+    CISDOBAsyncCall *call;
+    
+    [self assertNotLoggedIn];
+    call = [self.serviceManager loginUser: GetDefaultUserName() password: GetDefaultUserPassword()];
+    [self configureAndRunCallSynchronously: call];
+    [self assertLoggedIn];
+    
+    [self assertBeforeRootLevelCall];
+    call = [self.serviceManager retrieveRootLevelEntities];
+    [self configureAndRunCallSynchronously: call];
+    STAssertTrue(self.willRetrieveRootLevel, @"Should have retrieved root level");
+    STAssertTrue(self.didRetrieveRootLevel, @"Should have retrieved root level");
+    // No synch should have happened
+    
+    STAssertNotNil(_callError, @"The service manager should have failed to return entities.");
+}
+
+
 @end
-- 
GitLab