/*!
@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