/*! @header FTTransactionManagerImpl @abstract Module of FT @availability OS X, GNUstep @copyright 2004, 2005, 2006 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  -------------------------------------------------------------------------
  Modification history

  26.06.05 ola     initial version
  23.08.06 ola     license changed
  -------------------------------------------------------------------------
  
*/ #include #include #include #include #include #include #include #include @implementation FTTransactionManagerImpl - init { self = [super init]; self->globalLock = [[NSLock alloc] init]; self->sessionIdToTransactionArray = [[NSMutableDictionary alloc] init]; self->transactionOptimizers = [[NSMutableArray alloc] init]; return self; } - (void) dealloc { [self->globalLock release]; [self->sessionIdToTransactionArray dealloc]; [self->transactionOptimizers release]; [super dealloc]; } - addTransactionOptimizer: (id ) optimizerToAdd withPriority: (unsigned) priority { if( 0 != priority ) { [[[ECIllegalArgumentException alloc] initWithArgumentInfo: @"FTTransactionManagerImpl::addTransactionOptimizer"\ ": priority must equal 0 at present" ] raise]; } [self->transactionOptimizers addObject: optimizerToAdd]; return self; } - (BOOL) commitTransaction: (id ) transactionToCommit { BOOL success = YES; FTTransactionImpl *impl; NSEnumerator *steps; id currentStep; FTTransactionUndoStack *performedActions; NSException *stepException = nil; if( [[FTLogging coreLog] isDebugEnabled] ) { [[FTLogging coreLog] debug: @"FTTransactionManagerImpl::commit: Starting commitment..." ]; } if( ![transactionToCommit isKindOfClass: [FTTransactionImpl class]] ) { [[[ECIllegalArgumentException alloc] initWithArgumentInfo: @"transactionToCommit is not derived from FTTransactionImpl"] raise]; } [self->globalLock lock]; impl = (FTTransactionImpl *) [self optimizeTransaction: transactionToCommit]; steps = [[impl transactionSteps] objectEnumerator]; performedActions = [[FTTransactionUndoStack alloc] init]; while( (currentStep = [steps nextObject]) && success ) { if( [currentStep isKindOfClass: [FTTransactionStepAndContext class]] ) { id step; FTTransactionContext *context; step = [((FTTransactionStepAndContext *) currentStep) transactionStep]; context = [((FTTransactionStepAndContext *) currentStep) transactionContext]; if( nil != step ) { NS_DURING success = [step performAction: context]; NS_HANDLER success = NO; stepException = [localException retain]; break; NS_ENDHANDLER } if( success ) { [performedActions addPerformedStep: currentStep]; } } } if( NO == success ) { // at least one step failed /** * TODO * At present we simply call "undoAction" of all actions * directly launched before. * TODO: Much more robust behaviour here */ NS_DURING [performedActions undoAll]; NS_HANDLER /** * Evil situation if an exception occurs here */ [self->globalLock unlock]; if( nil != stepException ) { [stepException release]; } [performedActions release]; [[[FTTransactionStepException alloc] initWithTransactionStepException: localException] raise]; NS_ENDHANDLER if( nil != stepException ) { [stepException raise]; } } [self->globalLock unlock]; [performedActions release]; return success; } - (id ) createTransactionForSession: (id ) session { FTTransactionImpl *transaction = [[[FTTransactionImpl alloc] initForTransactionManager: self ] autorelease]; [self->globalLock lock]; ECStack *transactionStack = [sessionIdToTransactionArray objectForKey: [session sessionId]]; if( nil == transactionStack ) { /** create stack: */ transactionStack = [[ECStack alloc] init]; [self->sessionIdToTransactionArray setObject: transactionStack forKey: [session sessionId] ]; } NSAssert( nil != transactionStack, @"Ups, transaction stack equals nil!" ); [transactionStack pushObject: transaction]; [self->globalLock unlock]; return transaction; } - (id ) currentTransactionForSession: (id ) session { ECStack *transactionStack; transactionStack = [self->sessionIdToTransactionArray objectForKey: [session sessionId]]; NSAssert( nil != transactionStack, @"Cannot find stack for storing "\ "transactions!" ); return [transactionStack topObject]; } - (id ) optimizeTransaction: (id ) transaction { id toReturn = transaction; NSEnumerator *enumerator; id transactionOptimizer; enumerator = [self->transactionOptimizers objectEnumerator]; while( nil != (transactionOptimizer = (id ) [enumerator nextObject] )) { toReturn = [transactionOptimizer optimizeTransaction: toReturn]; } return toReturn; } @end @implementation FTTransactionUndoStack - init { self = [super init]; self->undoSteps = [[ECStack alloc] init]; self->performedUndoSteps = [[ECStack alloc] init]; self->currentUndoStep = nil; return self; } - (void) dealloc { [self->undoSteps release]; [self->performedUndoSteps release]; if( nil != self->currentUndoStep ) { [self->currentUndoStep release]; } [super dealloc]; } - addPerformedStep: (FTTransactionStepAndContext *) stepToAdd { if( nil != stepToAdd ) { [self->undoSteps pushObject: stepToAdd]; } return self; } - undoAll { while( NO == [undoSteps isEmpty] ) { self->currentUndoStep = (FTTransactionStepAndContext *) [self->undoSteps popObject]; if( nil != self->currentUndoStep ) { [[self->currentUndoStep transactionStep] undoAction: [self->currentUndoStep transactionContext]]; [self->performedUndoSteps pushObject: self->currentUndoStep]; [self->currentUndoStep release]; self->currentUndoStep = nil; } } return self; } @end