BeginSynchronize is an asynchronous pattern, requiring the user to override the onSynchronize method of the SUPDefaultCallbackHandler class to check for the SQLE_UPLOAD_FAILED_AT_SERVER error.
if([AsyncCallbackHandler isRecoverFailed]) { [AsyncCallbackHandler recoverClientDatabase]; } else { @try { SUPObjectList* syncList = [SUPObjectList getInstance]; [syncList add:@"default"]; [self synchronize:syncList]; } @catch (SUPPersistenceException *ex) { NSLog(@"sync failed(%d): %@", ex.errorCode, ex.reason); } } - (void) synchronize: (SUPObjectList*) syncGroups { AsyncCallbackHandler* callback = [[[AsyncCallbackHandler alloc] init] autorelease]; SUPObjectList* sgs = [SUPObjectList getInstance]; for (NSString* sg in syncGroups) { [sgs add:[end2end_rdbEnd2end_rdbDB getSynchronizationGroup:sg]]; } callback.userContext = @"fistBeginSync"; [end2end_rdbEnd2end_rdbDB registerCallbackHandler:callback]; [end2end_rdbEnd2end_rdbDB beginSynchronize:sgs withContext:callback.userContext]; int waitCount = 0; while (![callback syncDone]) { if (waitCount++ > maxWaitTime) { NSString *msg = [NSString stringWithFormat:@"No response returned from server after waiting %d seconds", maxWaitTime]; @throw [NSException exceptionWithName:@"NSException" reason:msg userInfo:nil]; } sleep(1); } callback.recoveryInProgress = NO; callback.recoveryDone = NO; callback.syncDone = NO; callback.userContext = nil; if ([callback errorMessage] != nil) { NSString* errMessage = callback.errorMessage; callback.errorMessage = nil; @throw [NSException exceptionWithName:@"NSException" reason:errMessage userInfo:nil]; } } // AsyncCallbackHandler.h #import "SUPDefaultCallbackHandler.h" @interface AsyncCallbackHandler : SUPDefaultCallbackHandler { } @property(readwrite, nonatomic, assign) BOOL recoveryInProgress; @property(readwrite, nonatomic, assign) BOOL recoveryDone; @property(readwrite, nonatomic, assign) BOOL syncDone; @property(readwrite, nonatomic, retain) NSString* userContext; @property(readwrite, nonatomic, retain) NSString* errorMessage; + (BOOL)isRecoverFailed; + (void)recoverClientDatabase; @end // AsyncCallbackHandler.m #import "AsyncCallbackHandler.h" #import "end2end_rdbEnd2end_rdbDB.h" #import "end2end_rdbCustomerWithParam.h" #import "end2end_rdbSISSubscription.h" #import "end2end_rdbLocalBook.h" @implementation AsyncCallbackHandler @synthesize recoveryInProgress; @synthesize recoveryDone; @synthesize syncDone; @synthesize userContext; @synthesize errorMessage; - (id) init { [super init]; recoveryInProgress = NO; recoveryDone = NO; userContext = nil; errorMessage = nil; return self; } - (SUPSynchronizationActionType)onSynchronize:(SUPObjectList*)syncGroupList withContext:(SUPSynchronizationContext*)context { NSException *ex = context.exception; if ((ex != nil) && [ex isKindOfClass:[SUPPersistenceException class]]) { SUPPersistenceException *pe = (SUPPersistenceException*)ex; if (pe.errorCode == SYNC_UPLOAD_FAILED_AT_SERVER) { if (!recoveryInProgress) { recoveryInProgress = YES; recoveryDone = NO; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_async(queue, ^{ @try { [AsyncCallbackHandler recoverClientDatabase]; } @catch (NSException *exception) { errorMessage = exception.reason; } recoveryDone = YES; recoveryInProgress = NO; syncDone = YES; }); } } return SUPSynchronizationAction_CONTINUE; } if (context.userContext != nil) { if ([context.userContext isKindOfClass:[NSString class]]) { NSString *userCtx = (NSString*)context.userContext; if ([userCtx isEqualToString:@"firstBeginSync"]) { if (context.status == SUPSynchronizationStatus_FINISHING || context.status == SUPSynchronizationStatus_ERROR) { syncDone = YES; } } } } return SUPSynchronizationAction_CONTINUE; } + (BOOL)isRecoverFailed { NSString* dbFile = [end2end_rdbEnd2end_rdbDB getDbPath]; NSString* recoverDbFile = [NSString stringWithFormat:@"%@.recover.udb", dbFile]; NSFileManager *fm = [NSFileManager defaultManager]; BOOL ok = YES; if ([fm isReadableFileAtPath:recoverDbFile]) { // Recover DB exists, indicating the last recover was not finished // need to move back the DB file and do recovery. if ([fm isReadableFileAtPath:dbFile]) { if (![fm removeItemAtPath:dbFile error:nil]) { ok = NO; } } if (ok && [fm moveItemAtPath:recoverDbFile toPath:dbFile error:nil]) { return YES; } else { @throw [NSException exceptionWithName:@"NSException" reason:@"Failed to copy the recover database back." userInfo:nil]; } } return NO; } + (void) recoverClientDatabase { NSString* dbFile = [end2end_rdbEnd2end_rdbDB getDbPath]; NSString* recoverDbFile = [NSString stringWithFormat:@"%@.recover.udb", dbFile]; NSFileManager *fm = [NSFileManager defaultManager]; if (![fm copyItemAtPath:dbFile toPath:recoverDbFile error:nil]) { @throw [NSException exceptionWithName:@"NSException" reason:@"Failed to copy the recover database." userInfo:nil]; } //retrieve all the subscriptions from client database SUPObjectList *customerWithParamSubscriptions = [end2end_rdbCustomerWithParam getSubscriptions]; SUPObjectList *sisSubscriptions = [[end2end_rdbSISSubscription getInstance] findAll]; NSMutableArray *syncedPublications = [NSMutableArray arrayWithCapacity:2]; // check all the synchronization group, if is synchronized, add to new sync group to synchronize if ([end2end_rdbEnd2end_rdbDB isSynchronized:@"synchronizationGroup"]) { [syncedPublications addObject:@"synchronizationGroup"]; } //retrieve all local BO from client database SUPObjectList* localBookList = [end2end_rdbLocalBook findAll]; // Done with saving information, close connection and delete the database [end2end_rdbEnd2end_rdbDB closeConnection]; [end2end_rdbEnd2end_rdbDB deleteDatabase]; // new subscription [end2end_rdbEnd2end_rdbDB subscribe]; // merge old local BO data to new database for (id lbo in localBookList) { end2end_rdbLocalBook *savedLocalBook = (end2end_rdbLocalBook*)lbo; end2end_rdbLocalBook *localBook = [end2end_rdbLocalBook getInstance]; [localBook copyAll:savedLocalBook]; [localBook create]; } // add all the subscriptions from old database to new database for (id sub in customerWithParamSubscriptions) { end2end_rdbCustomerWithParamSubscription *csub = (end2end_rdbCustomerWithParamSubscription*)sub; [end2end_rdbCustomerWithParam addSubscription:csub]; } for (id sub in sisSubscriptions) { end2end_rdbSISSubscription* ssub = (end2end_rdbSISSubscription*)sub; id<SUPSynchronizationGroup> sg = [end2end_rdbEnd2end_rdbDB getSynchronizationGroup:ssub.syncGroup]; sg.enableSIS = ssub.enable; [sg save]; } // synchronize for the synchronized publications NSString* syncGroups = [syncedPublications componentsJoinedByString:@","]; [end2end_rdbEnd2end_rdbDB synchronize:syncGroups]; // finally delete the backup recover database file if (![fm removeItemAtPath:recoverDbFile error:nil]) { @throw [NSException exceptionWithName:@"NSException" reason:@"Failed to remove the recover database." userInfo:nil]; } } @end