1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.Services
.Transaction
18 using System
.Collections
;
19 using System
.Collections
.Specialized
;
20 using Castle
.Core
.Logging
;
23 /// Helper abstract class for <see cref="ITransaction"/> implementors.
25 public abstract class AbstractTransaction
: MarshalByRefObject
, ITransaction
, IDisposable
27 private HybridDictionary context
;
28 private IList synchronizations
;
29 private TransactionStatus state
= TransactionStatus
.NoTransaction
;
30 private TransactionMode transactionMode
;
31 private IsolationMode isolationMode
;
32 private bool distributedTransaction
;
34 private ILogger logger
= NullLogger
.Instance
;
36 internal IList resources
;
38 public AbstractTransaction()
40 resources
= new ArrayList();
41 synchronizations
= new ArrayList();
42 context
= new HybridDictionary(true);
45 public AbstractTransaction(TransactionMode transactionMode
, IsolationMode isolationMode
, bool distributedTransaction
)
46 : this(transactionMode
, isolationMode
, distributedTransaction
, null)
50 public AbstractTransaction(TransactionMode transactionMode
, IsolationMode isolationMode
, bool distributedTransaction
,
53 this.transactionMode
= transactionMode
;
54 this.isolationMode
= isolationMode
;
55 this.distributedTransaction
= distributedTransaction
;
57 if (String
.IsNullOrEmpty(name
))
59 this.name
= ObtainName();
69 get { return logger; }
70 set { logger = value; }
73 #region MarshalByRefObject
75 public override object InitializeLifetimeService()
84 public virtual void Enlist(IResource resource
)
86 logger
.DebugFormat("Enlisting resource {0}", resource
);
88 if (resource
== null) throw new ArgumentNullException("resource");
90 // We can't add the resource more than once
91 if (resources
.Contains(resource
)) return;
93 if (Status
== TransactionStatus
.Active
)
101 state
= TransactionStatus
.Invalid
;
103 logger
.Error("Enlisting resource failed", ex
);
109 resources
.Add(resource
);
111 logger
.DebugFormat("Resource enlisted successfully {0}", resource
);
114 public virtual void Begin()
116 logger
.DebugFormat("Transaction '{0}' Begin", Name
);
118 AssertState(TransactionStatus
.NoTransaction
);
119 state
= TransactionStatus
.Active
;
121 foreach(IResource resource
in resources
)
129 state
= TransactionStatus
.Invalid
;
131 logger
.Error("Failed to start transaction on resource.", ex
);
138 public virtual void Rollback()
140 logger
.DebugFormat("Transaction '{0}' Rollback", Name
);
142 AssertState(TransactionStatus
.Active
);
143 state
= TransactionStatus
.RolledBack
;
145 PerformSynchronizations(false);
147 Exception error
= null;
148 ArrayList failedResources
= new ArrayList(resources
.Count
);
150 foreach(IResource resource
in resources
)
158 state
= TransactionStatus
.Invalid
;
160 logger
.Error("Failed to rollback transaction on resource.", ex
);
164 failedResources
.Add(resource
);
168 PerformSynchronizations(true);
172 throw new RollbackResourceException("Could not rollback transaction, one (or more) resources failed.", error
,
173 (IResource
) failedResources
[failedResources
.Count
- 1],
174 (IResource
[]) failedResources
.ToArray((typeof(IResource
))));
178 public virtual void Commit()
180 logger
.DebugFormat("Transaction '{0}' Commit", Name
);
182 AssertState(TransactionStatus
.Active
);
183 state
= TransactionStatus
.Committed
;
185 PerformSynchronizations(false);
187 Exception error
= null;
188 ArrayList failedResources
= new ArrayList(resources
.Count
);
190 foreach(IResource resource
in resources
)
198 state
= TransactionStatus
.Invalid
;
200 logger
.Error("Failed to commit transaction on resource.", ex
);
204 failedResources
.Add(resource
);
208 PerformSynchronizations(true);
212 throw new CommitResourceException("Could not commit transaction, one (or more) of the resources failed", error
,
213 (IResource
) failedResources
[failedResources
.Count
- 1],
214 (IResource
[]) failedResources
.ToArray((typeof(IResource
))));
218 public virtual void SetRollbackOnly()
222 public TransactionStatus Status
224 get { return state; }
227 public virtual void RegisterSynchronization(ISynchronization synchronization
)
229 logger
.DebugFormat("Registering Synchronization {0}", synchronization
);
231 if (synchronization
== null) throw new ArgumentNullException("synchronization");
233 synchronizations
.Add(synchronization
);
235 logger
.DebugFormat("Synchronization registered successfully {0}", synchronization
);
238 public virtual IDictionary Context
240 get { return context; }
243 public abstract bool IsChildTransaction { get; }
245 public abstract bool IsRollbackOnlySet { get; }
247 public TransactionMode TransactionMode
249 get { return transactionMode; }
252 public IsolationMode IsolationMode
254 get { return isolationMode; }
257 public bool DistributedTransaction
259 get { return distributedTransaction; }
267 #region ITransaction Members
269 public IResource
[] Resources
271 get { return (IResource[]) ((ArrayList) resources).ToArray(typeof(IResource)); }
278 #region IDisposable Members
280 public virtual void Dispose()
283 synchronizations
.Clear();
288 #region Helper methods
290 protected virtual void AssertState(TransactionStatus state
)
292 if (this.state
!= state
)
294 throw new TransactionException("Invalid transaction state to perform the requested action");
298 protected string ObtainName()
300 return Convert
.ToString(GetHashCode());
303 private void PerformSynchronizations(bool runAfterCompletion
)
305 foreach(ISynchronization sync
in synchronizations
)
309 if (runAfterCompletion
)
311 sync
.AfterCompletion();
315 sync
.BeforeCompletion();
320 logger
.Error("Synchronization failed", ex
);
322 // Exceptions should not be threw by syncs.
323 // They will be swalled