1
2/*!
3  @header FTTransactionManagerImpl
4  @abstract Module of FT
5
6  @availability OS X, GNUstep
7  @copyright 2004, 2005, 2006 Free Software Foundation, Inc.
8
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Lesser General Public
11  License as published by the Free Software Foundation; either
12  version 2.1 of the License, or (at your option) any later version.
13
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  Lesser General Public License for more details.
18
19  You should have received a copy of the GNU Lesser General Public
20  License along with this library; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
22
23  <pre>
24  -------------------------------------------------------------------------
25  Modification history
26
27  26.06.05 ola     initial version
28  23.08.06 ola     license changed
29  -------------------------------------------------------------------------
30  </pre>
31*/
32
33#include <Encore/Encore.h>
34#include <FT/FTTransactionManager.h>
35#include <FT/FTSession.h>
36#include <FT/FTSessionImpl.h>
37#include <FT/FTTransactionManagerImpl.h>
38#include <FT/FTTransactionImpl.h>
39#include <FT/FTExceptions.h>
40#include <FT/FTLogging.h>
41
42
43@implementation FTTransactionManagerImpl
44
45- init {
46  self = [super init];
47
48  self->globalLock = [[NSLock alloc] init];
49
50  self->sessionIdToTransactionArray = [[NSMutableDictionary alloc] init];
51  self->transactionOptimizers = [[NSMutableArray alloc] init];
52
53  return self;
54}
55
56
57- (void) dealloc {
58  [self->globalLock release];
59  [self->sessionIdToTransactionArray dealloc];
60  [self->transactionOptimizers release];
61
62  [super dealloc];
63}
64
65
66- addTransactionOptimizer: (id <FTTransactionOptimizer>) optimizerToAdd
67  withPriority: (unsigned) priority {
68
69  if( 0 != priority ) {
70    [[[ECIllegalArgumentException alloc]
71      initWithArgumentInfo: @"FTTransactionManagerImpl::addTransactionOptimizer"\
72        ": priority must equal 0 at present" ] raise];
73  }
74
75  [self->transactionOptimizers addObject: optimizerToAdd];
76
77  return self;
78}
79
80
81- (BOOL) commitTransaction: (id <FTTransaction>) transactionToCommit {
82  BOOL success = YES;
83  FTTransactionImpl *impl;
84  NSEnumerator *steps;
85  id currentStep;
86  FTTransactionUndoStack *performedActions;
87  NSException *stepException = nil;
88
89  if( [[FTLogging coreLog] isDebugEnabled] ) {
90    [[FTLogging coreLog]
91      debug: @"FTTransactionManagerImpl::commit: Starting commitment..." ];
92  }
93
94  if( ![transactionToCommit isKindOfClass: [FTTransactionImpl class]] ) {
95    [[[ECIllegalArgumentException alloc] initWithArgumentInfo:
96      @"transactionToCommit is not derived from FTTransactionImpl"] raise];
97  }
98
99  [self->globalLock lock];
100  impl = (FTTransactionImpl *) [self optimizeTransaction: transactionToCommit];
101  steps = [[impl transactionSteps] objectEnumerator];
102  performedActions = [[FTTransactionUndoStack alloc] init];
103
104  while( (currentStep = [steps nextObject]) && success ) {
105    if( [currentStep isKindOfClass: [FTTransactionStepAndContext class]] ) {
106      id <FTTransactionStep> step;
107      FTTransactionContext *context;
108
109      step = [((FTTransactionStepAndContext *) currentStep) transactionStep];
110      context = [((FTTransactionStepAndContext *) currentStep)
111        transactionContext];
112
113      if( nil != step ) {
114        NS_DURING
115          success = [step performAction: context];
116        NS_HANDLER
117          success = NO;
118          stepException = [localException retain];
119          break;
120        NS_ENDHANDLER
121      }
122
123        if( success ) {
124          [performedActions addPerformedStep: currentStep];
125        }
126    }
127  }
128
129  if( NO == success ) {
130    // at least one step failed
131    /**
132     * TODO
133     * At present we simply call "undoAction" of all actions
134     * directly launched before.
135     * TODO: Much more robust behaviour here
136     */
137    NS_DURING
138      [performedActions undoAll];
139    NS_HANDLER
140      /**
141       * Evil situation if an exception occurs here
142       */
143      [self->globalLock unlock];
144
145      if( nil != stepException ) {
146        [stepException release];
147      }
148
149      [performedActions release];
150
151      [[[FTTransactionStepException alloc]
152       initWithTransactionStepException: localException] raise];
153     NS_ENDHANDLER
154
155    if( nil != stepException ) {
156      [stepException raise];
157    }
158  }
159
160  [self->globalLock unlock];
161  [performedActions release];
162
163  return success;
164}
165
166
167- (id <FTTransaction>) createTransactionForSession: (id <FTSession>) session {
168  FTTransactionImpl *transaction = [[[FTTransactionImpl alloc]
169    initForTransactionManager: self ] autorelease];
170
171  [self->globalLock lock];
172  ECStack *transactionStack
173    = [sessionIdToTransactionArray objectForKey: [session sessionId]];
174
175  if( nil == transactionStack ) {
176    /** create stack: */
177    transactionStack = [[ECStack alloc] init];
178    [self->sessionIdToTransactionArray
179      setObject: transactionStack forKey: [session sessionId] ];
180  }
181
182  NSAssert( nil != transactionStack, @"Ups, transaction stack equals nil!" );
183
184  [transactionStack pushObject: transaction];
185
186  [self->globalLock unlock];
187
188  return transaction;
189}
190
191
192- (id <FTTransaction>) currentTransactionForSession: (id <FTSession>) session {
193  ECStack *transactionStack;
194
195  transactionStack = [self->sessionIdToTransactionArray objectForKey:
196    [session sessionId]];
197  NSAssert( nil != transactionStack, @"Cannot find stack for storing "\
198    "transactions!" );
199
200  return [transactionStack topObject];
201}
202
203
204- (id <FTTransaction>) optimizeTransaction: (id <FTTransaction>) transaction {
205  id <FTTransaction> toReturn = transaction;
206  NSEnumerator *enumerator;
207  id <FTTransactionOptimizer> transactionOptimizer;
208
209  enumerator = [self->transactionOptimizers objectEnumerator];
210
211  while( nil != (transactionOptimizer
212    = (id <FTTransactionOptimizer>) [enumerator nextObject] )) {
213    toReturn = [transactionOptimizer optimizeTransaction: toReturn];
214  }
215
216  return toReturn;
217}
218@end
219
220
221@implementation FTTransactionUndoStack
222
223- init {
224  self = [super init];
225
226  self->undoSteps = [[ECStack alloc] init];
227  self->performedUndoSteps = [[ECStack alloc] init];
228  self->currentUndoStep = nil;
229  return self;
230}
231
232
233- (void) dealloc {
234  [self->undoSteps release];
235  [self->performedUndoSteps release];
236  if( nil != self->currentUndoStep ) {
237    [self->currentUndoStep release];
238  }
239
240  [super dealloc];
241}
242
243
244- addPerformedStep: (FTTransactionStepAndContext *) stepToAdd {
245  if( nil != stepToAdd ) {
246    [self->undoSteps pushObject: stepToAdd];
247  }
248
249  return self;
250}
251
252
253- undoAll {
254  while( NO == [undoSteps isEmpty] ) {
255    self->currentUndoStep
256      = (FTTransactionStepAndContext *) [self->undoSteps popObject];
257
258    if( nil != self->currentUndoStep ) {
259      [[self->currentUndoStep transactionStep]
260        undoAction: [self->currentUndoStep transactionContext]];
261
262      [self->performedUndoSteps pushObject: self->currentUndoStep];
263      [self->currentUndoStep release];
264      self->currentUndoStep = nil;
265    }
266  }
267
268  return self;
269}
270
271@end
272