|  | /* | 
|  | * Licensed to the Apache Software Foundation (ASF) under one or more | 
|  | * contributor license agreements.  See the NOTICE file distributed with | 
|  | * this work for additional information regarding copyright ownership. | 
|  | * The ASF licenses this file to You under the Apache License, Version 2.0 | 
|  | * (the "License"); you may not use this file except in compliance with | 
|  | * the License.  You may obtain a copy of the License at | 
|  | * | 
|  | *     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | using System; | 
|  | using System.Collections.Generic; | 
|  | using System.Text; | 
|  | using System.Threading; | 
|  | using System.Threading.Tasks; | 
|  | using System.Transactions; | 
|  | using Apache.NMS.ActiveMQ.Commands; | 
|  | using Apache.NMS.ActiveMQ.Transactions; | 
|  | using Apache.NMS.ActiveMQ.Util.Synchronization; | 
|  | using Apache.NMS.Util; | 
|  |  | 
|  | namespace Apache.NMS.ActiveMQ | 
|  | { | 
|  | public sealed class NetTxTransactionContext : TransactionContext, ISinglePhaseNotification | 
|  | { | 
|  | private const int XA_OK = 0; | 
|  | private const int XA_READONLY = 3; | 
|  |  | 
|  | private Enlistment currentEnlistment; | 
|  |  | 
|  | private static readonly NmsSynchronizationMonitor recoveredResourceManagerIdsLock = | 
|  | new NmsSynchronizationMonitor(); | 
|  | private static readonly Dictionary<string, bool> recoveredResourceManagerIds = new Dictionary<string, bool>(); | 
|  |  | 
|  | public NetTxTransactionContext(Session session) : base(session) | 
|  | { | 
|  | } | 
|  |  | 
|  | /// <summary> | 
|  | /// DTC recovery is performed once for each AppDomain per default. In case you want to perform | 
|  | /// it again during execution of the application you can call this method before. | 
|  | /// But ensure in this case that no connection is active anymore. | 
|  | /// </summary> | 
|  | public static void ResetDtcRecovery() | 
|  | { | 
|  | recoveredResourceManagerIds.Clear(); | 
|  | } | 
|  |  | 
|  | public override bool InLocalTransaction | 
|  | { | 
|  | get { return this.TransactionId != null && this.currentEnlistment == null; } | 
|  | } | 
|  |  | 
|  | public override void Begin() | 
|  | { | 
|  | throw new IllegalStateException("Local Transactions not supported in NetTx resources"); | 
|  | } | 
|  |  | 
|  | public override void Commit() | 
|  | { | 
|  | throw new IllegalStateException("Local Transactions not supported in NetTx resources"); | 
|  | } | 
|  |  | 
|  | public override void Rollback() | 
|  | { | 
|  | throw new IllegalStateException("Local Transactions not supported in NetTx resources"); | 
|  | } | 
|  |  | 
|  | #region Transaction Members used when dealing with .NET System Transactions. | 
|  |  | 
|  | // When DTC calls prepare we must then wait for either the TX to commit, rollback or | 
|  | // be canceled because its in doubt. | 
|  | private readonly ManualResetEvent dtcControlEvent = new ManualResetEvent(true); | 
|  |  | 
|  | // Once the DTC calls prepare we lock this object and don't unlock it again until | 
|  | // the TX has either completed or terminated, the users of this class should use | 
|  | // this sync point when the TX is a DTC version as opposed to a local one. | 
|  | private readonly NmsSynchronizationMonitor syncObject = new NmsSynchronizationMonitor(); //Mutex(); | 
|  |  | 
|  | public enum TxState | 
|  | { | 
|  | None = 0, Active = 1, Pending = 2 | 
|  | } | 
|  |  | 
|  | private TxState netTxState = TxState.None; | 
|  |  | 
|  | public NmsSynchronizationMonitor SyncRoot | 
|  | { | 
|  | get { return this.syncObject; } | 
|  | } | 
|  |  | 
|  | public bool InNetTransaction | 
|  | { | 
|  | get { return this.TransactionId != null && this.TransactionId is XATransactionId; } | 
|  | } | 
|  |  | 
|  | public TxState NetTxState | 
|  | { | 
|  | get | 
|  | { | 
|  | return this.netTxState; | 
|  | } | 
|  | } | 
|  |  | 
|  | public WaitHandle DtcWaitHandle | 
|  | { | 
|  | get { return dtcControlEvent; } | 
|  | } | 
|  |  | 
|  | public void Begin(Transaction transaction) | 
|  | { | 
|  | using(syncObject.Lock()) | 
|  | { | 
|  | dtcControlEvent.Reset(); | 
|  |  | 
|  | Tracer.Debug("Begin notification received"); | 
|  |  | 
|  | if (InNetTransaction) | 
|  | { | 
|  | throw new TransactionInProgressException("A Transaction is already in Progress"); | 
|  | } | 
|  |  | 
|  | try | 
|  | { | 
|  | Guid rmId = ResourceManagerGuid; | 
|  |  | 
|  | // Enlist this object in the transaction. | 
|  | this.currentEnlistment = transaction.EnlistDurable(rmId, this, EnlistmentOptions.None); | 
|  |  | 
|  | // In case of a exception in the current method the transaction will be rolled back. | 
|  | // Until Begin Transaction is completed we consider to be in a rollback scenario. | 
|  | this.netTxState = TxState.Pending; | 
|  |  | 
|  | Tracer.Debug("Enlisted in Durable Transaction with RM Id: " + rmId); | 
|  |  | 
|  | TransactionInformation txInfo = transaction.TransactionInformation; | 
|  |  | 
|  | XATransactionId xaId = new XATransactionId(); | 
|  | this.TransactionId = xaId; | 
|  |  | 
|  | if (txInfo.DistributedIdentifier != Guid.Empty) | 
|  | { | 
|  | xaId.GlobalTransactionId = txInfo.DistributedIdentifier.ToByteArray(); | 
|  | xaId.BranchQualifier = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); | 
|  | } | 
|  | else | 
|  | { | 
|  | xaId.GlobalTransactionId = Encoding.UTF8.GetBytes(txInfo.LocalIdentifier); | 
|  | xaId.BranchQualifier = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString()); | 
|  | } | 
|  |  | 
|  | // Now notify the broker that a new XA'ish transaction has started. | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.connection.ConnectionId; | 
|  | info.TransactionId = this.TransactionId; | 
|  | info.Type = (int)TransactionType.Begin; | 
|  |  | 
|  | this.session.Connection.Oneway(info); | 
|  |  | 
|  | // Begin Transaction is completed successfully. Change to transaction active state now. | 
|  | this.netTxState = TxState.Active; | 
|  |  | 
|  | SignalTransactionStarted(); | 
|  |  | 
|  | if (Tracer.IsDebugEnabled) | 
|  | { | 
|  | Tracer.Debug("Began XA'ish Transaction:" + xaId); | 
|  | } | 
|  | } | 
|  | catch (Exception) | 
|  | { | 
|  | // When in pending state the rollback will signal that a new transaction can be started. Otherwise do it here. | 
|  | if (netTxState != TxState.Pending) | 
|  | { | 
|  | netTxState = TxState.None; | 
|  | dtcControlEvent.Set(); | 
|  | } | 
|  | throw; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public void Prepare(PreparingEnlistment preparingEnlistment) | 
|  | { | 
|  | PrepareAsync(preparingEnlistment).GetAsyncResult(); | 
|  | } | 
|  |  | 
|  | public async Task PrepareAsync(PreparingEnlistment preparingEnlistment) | 
|  | { | 
|  | using (await this.syncObject.LockAsync().Await()) | 
|  | { | 
|  | this.netTxState = TxState.Pending; | 
|  |  | 
|  | try | 
|  | { | 
|  | Tracer.Debug("Prepare notification received for TX id: " + this.TransactionId); | 
|  |  | 
|  | await BeforeEndAsync().Await(); | 
|  |  | 
|  | // Before sending the request to the broker, log the recovery bits, if | 
|  | // this fails we can't prepare and the TX should be rolled back. | 
|  | RecoveryLogger.LogRecoveryInfo(this.TransactionId as XATransactionId, | 
|  | preparingEnlistment.RecoveryInformation()); | 
|  |  | 
|  | // Inform the broker that work on the XA'sh TX Branch is complete. | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.connection.ConnectionId; | 
|  | info.TransactionId = this.TransactionId; | 
|  | info.Type = (int)TransactionType.End; | 
|  |  | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync((TransactionInfo) info.Clone()).Await(); | 
|  |  | 
|  | // Prepare the Transaction for commit. | 
|  | info.Type = (int)TransactionType.Prepare; | 
|  | IntegerResponse response = (IntegerResponse) await this.connection.SyncRequestAsync(info).Await(); | 
|  | if (response.Result == XA_READONLY) | 
|  | { | 
|  | Tracer.Debug("Transaction Prepare done and doesn't need a commit, TX id: " + this.TransactionId); | 
|  |  | 
|  | this.TransactionId = null; | 
|  | this.currentEnlistment = null; | 
|  |  | 
|  | // Read Only means there's nothing to recover because there was no | 
|  | // change on the broker. | 
|  | RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); | 
|  |  | 
|  | // if server responds that nothing needs to be done, then reply done. | 
|  | // otherwise the DTC will call Commit or Rollback but another transaction | 
|  | // can already be in progress and this one would be commited or rolled back | 
|  | // immediately. | 
|  | preparingEnlistment.Done(); | 
|  |  | 
|  | // Done so commit won't be called. | 
|  | AfterCommit(); | 
|  |  | 
|  | // A Read-Only TX is considered closed at this point, DTC won't call us again. | 
|  | this.dtcControlEvent.Set(); | 
|  | } | 
|  | else | 
|  | { | 
|  | Tracer.Debug("Transaction Prepare succeeded TX id: " + this.TransactionId); | 
|  |  | 
|  | // If work finished correctly, reply prepared | 
|  | preparingEnlistment.Prepared(); | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | Tracer.DebugFormat("Transaction[{0}] Prepare failed with error: {1}", | 
|  | this.TransactionId, ex.Message); | 
|  |  | 
|  | AfterRollback(); | 
|  | preparingEnlistment.ForceRollback(); | 
|  | try | 
|  | { | 
|  | this.connection.OnAsyncException(ex); | 
|  | } | 
|  | catch (Exception error) | 
|  | { | 
|  | Tracer.Error(error.ToString()); | 
|  | } | 
|  |  | 
|  | this.currentEnlistment = null; | 
|  | this.TransactionId = null; | 
|  | this.netTxState = TxState.None; | 
|  | this.dtcControlEvent.Set(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public void Commit(Enlistment enlistment) | 
|  | { | 
|  | CommitAsync(enlistment).GetAsyncResult(); | 
|  | } | 
|  |  | 
|  | public async Task CommitAsync(Enlistment enlistment) | 
|  | { | 
|  | using(await this.syncObject.LockAsync().Await()) | 
|  | { | 
|  | try | 
|  | { | 
|  | Tracer.Debug("Commit notification received for TX id: " + this.TransactionId); | 
|  |  | 
|  | if (this.TransactionId != null) | 
|  | { | 
|  | // Now notify the broker that a new XA'ish transaction has completed. | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.connection.ConnectionId; | 
|  | info.TransactionId = this.TransactionId; | 
|  | info.Type = (int)TransactionType.CommitTwoPhase; | 
|  |  | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync(info).Await(); | 
|  |  | 
|  | Tracer.Debug("Transaction Commit Done TX id: " + this.TransactionId); | 
|  |  | 
|  | RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); | 
|  |  | 
|  | // if server responds that nothing needs to be done, then reply done. | 
|  | enlistment.Done(); | 
|  |  | 
|  | AfterCommit(); | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | Tracer.DebugFormat("Transaction[{0}] Commit failed with error: {1}", | 
|  | this.TransactionId, ex.Message); | 
|  | try | 
|  | { | 
|  | this.connection.OnAsyncException(ex); | 
|  | } | 
|  | catch (Exception error) | 
|  | { | 
|  | Tracer.Error(error.ToString()); | 
|  | } | 
|  | } | 
|  | finally | 
|  | { | 
|  | this.currentEnlistment = null; | 
|  | this.TransactionId = null; | 
|  | this.netTxState = TxState.None; | 
|  |  | 
|  | CountDownLatch latch = this.recoveryComplete; | 
|  | if (latch != null) | 
|  | { | 
|  | latch.countDown(); | 
|  | } | 
|  |  | 
|  | this.dtcControlEvent.Set(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | public void SinglePhaseCommit(SinglePhaseEnlistment enlistment) | 
|  | { | 
|  | SinglePhaseCommitAsync(enlistment).GetAsyncResult(); | 
|  | } | 
|  | public async Task SinglePhaseCommitAsync(SinglePhaseEnlistment enlistment) | 
|  | { | 
|  | using(await this.syncObject.LockAsync().Await()) | 
|  | { | 
|  | try | 
|  | { | 
|  | Tracer.Debug("Single Phase Commit notification received for TX id: " + this.TransactionId); | 
|  |  | 
|  | if (this.TransactionId != null) | 
|  | { | 
|  | await BeforeEndAsync().Await(); | 
|  |  | 
|  | // Now notify the broker that a new XA'ish transaction has completed. | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.connection.ConnectionId; | 
|  | info.TransactionId = this.TransactionId; | 
|  | info.Type = (int)TransactionType.CommitOnePhase; | 
|  |  | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync(info).Await(); | 
|  |  | 
|  | Tracer.Debug("Transaction Single Phase Commit Done TX id: " + this.TransactionId); | 
|  |  | 
|  | // if server responds that nothing needs to be done, then reply done. | 
|  | enlistment.Done(); | 
|  |  | 
|  | AfterCommit(); | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | Tracer.DebugFormat("Transaction[{0}] Single Phase Commit failed with error: {1}", | 
|  | this.TransactionId, ex.Message); | 
|  | AfterRollback(); | 
|  | enlistment.Done(); | 
|  | try | 
|  | { | 
|  | this.connection.OnAsyncException(ex); | 
|  | } | 
|  | catch (Exception error) | 
|  | { | 
|  | Tracer.Error(error.ToString()); | 
|  | } | 
|  | } | 
|  | finally | 
|  | { | 
|  | this.currentEnlistment = null; | 
|  | this.TransactionId = null; | 
|  | this.netTxState = TxState.None; | 
|  |  | 
|  | this.dtcControlEvent.Set(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public void Rollback(Enlistment enlistment) | 
|  | { | 
|  | RollbackAsync(enlistment).GetAsyncResult(); | 
|  | } | 
|  |  | 
|  | public async Task RollbackAsync(Enlistment enlistment) | 
|  | { | 
|  | using(await this.syncObject.LockAsync().Await()) | 
|  | { | 
|  | try | 
|  | { | 
|  | Tracer.Debug("Rollback notification received for TX id: " + this.TransactionId); | 
|  |  | 
|  | if (this.TransactionId != null) | 
|  | { | 
|  | await BeforeEndAsync().Await(); | 
|  |  | 
|  | // Now notify the broker that a new XA'ish transaction has started. | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.connection.ConnectionId; | 
|  | info.TransactionId = this.TransactionId; | 
|  | info.Type = (int)TransactionType.End; | 
|  |  | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync((TransactionInfo) info.Clone()).Await(); | 
|  |  | 
|  | info.Type = (int)TransactionType.Rollback; | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync(info).Await(); | 
|  |  | 
|  | Tracer.Debug("Transaction Rollback Done TX id: " + this.TransactionId); | 
|  |  | 
|  | RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); | 
|  |  | 
|  | // if server responds that nothing needs to be done, then reply done. | 
|  | enlistment.Done(); | 
|  |  | 
|  | AfterRollback(); | 
|  | } | 
|  | } | 
|  | catch (Exception ex) | 
|  | { | 
|  | Tracer.DebugFormat("Transaction[{0}] Rollback failed with error: {1}", | 
|  | this.TransactionId, ex.Message); | 
|  | AfterRollback(); | 
|  | try | 
|  | { | 
|  | this.connection.OnAsyncException(ex); | 
|  | } | 
|  | catch (Exception error) | 
|  | { | 
|  | Tracer.Error(error.ToString()); | 
|  | } | 
|  | } | 
|  | finally | 
|  | { | 
|  | this.currentEnlistment = null; | 
|  | this.TransactionId = null; | 
|  | this.netTxState = TxState.None; | 
|  |  | 
|  | CountDownLatch latch = this.recoveryComplete; | 
|  | if (latch != null) | 
|  | { | 
|  | latch.countDown(); | 
|  | } | 
|  |  | 
|  | this.dtcControlEvent.Set(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | public void InDoubt(Enlistment enlistment) | 
|  | { | 
|  |  | 
|  | } | 
|  | public async Task InDoubtAsync(Enlistment enlistment) | 
|  | { | 
|  | using(await syncObject.LockAsync().Await()) | 
|  | { | 
|  | try | 
|  | { | 
|  | Tracer.Debug("In Doubt notification received for TX id: " + this.TransactionId); | 
|  |  | 
|  | await BeforeEndAsync().Await(); | 
|  |  | 
|  | // Now notify the broker that Rollback should be performed. | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.connection.ConnectionId; | 
|  | info.TransactionId = this.TransactionId; | 
|  | info.Type = (int)TransactionType.End; | 
|  |  | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync((TransactionInfo) info.Clone()).Await(); | 
|  |  | 
|  | info.Type = (int)TransactionType.Rollback; | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | await this.connection.SyncRequestAsync(info).Await(); | 
|  |  | 
|  | Tracer.Debug("InDoubt Transaction Rollback Done TX id: " + this.TransactionId); | 
|  |  | 
|  | RecoveryLogger.LogRecovered(this.TransactionId as XATransactionId); | 
|  |  | 
|  | // if server responds that nothing needs to be done, then reply done. | 
|  | enlistment.Done(); | 
|  |  | 
|  | AfterRollback(); | 
|  | } | 
|  | finally | 
|  | { | 
|  | this.currentEnlistment = null; | 
|  | this.TransactionId = null; | 
|  | this.netTxState = TxState.None; | 
|  |  | 
|  | CountDownLatch latch = this.recoveryComplete; | 
|  | if (latch != null) | 
|  | { | 
|  | latch.countDown(); | 
|  | } | 
|  |  | 
|  | this.dtcControlEvent.Set(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #endregion | 
|  |  | 
|  | #region Distributed Transaction Recovery Bits | 
|  |  | 
|  | private volatile CountDownLatch recoveryComplete; | 
|  |  | 
|  | /// <summary> | 
|  | /// Should be called from NetTxSession when created to check if any TX | 
|  | /// data is stored for recovery and whether the Broker has matching info | 
|  | /// stored.  If an Transaction is found that belongs to this client and is | 
|  | /// still alive on the Broker it will be recovered, otherwise the stored | 
|  | /// data should be cleared. | 
|  | /// </summary> | 
|  | public async Task InitializeDtcTxContextAsync() | 
|  | { | 
|  | string resourceManagerId = ResourceManagerId; | 
|  |  | 
|  | // initialize the logger with the current Resource Manager Id | 
|  | RecoveryLogger.Initialize(resourceManagerId); | 
|  |  | 
|  | using(await recoveredResourceManagerIdsLock.LockAsync().Await()) | 
|  | { | 
|  | if (recoveredResourceManagerIds.ContainsKey(resourceManagerId)) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | recoveredResourceManagerIds[resourceManagerId] = true; | 
|  |  | 
|  | KeyValuePair<XATransactionId, byte[]>[] localRecoverables = RecoveryLogger.GetRecoverables(); | 
|  | if (localRecoverables.Length == 0) | 
|  | { | 
|  | Tracer.Debug("Did not detect any open DTC transaction records on disk."); | 
|  | // No local data so anything stored on the broker can't be recovered here. | 
|  | return; | 
|  | } | 
|  |  | 
|  | XATransactionId[] recoverables = await TryRecoverBrokerTXIdsAsync().Await(); | 
|  | if (recoverables.Length == 0) | 
|  | { | 
|  | Tracer.Debug("Did not detect any recoverable transactions at Broker."); | 
|  | // Broker has no recoverable data so nothing to do here, delete the | 
|  | // old recovery log as its stale. | 
|  | RecoveryLogger.Purge(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | List<KeyValuePair<XATransactionId, byte[]>> matches = new List<KeyValuePair<XATransactionId, byte[]>>(); | 
|  |  | 
|  | foreach (XATransactionId recoverable in recoverables) | 
|  | { | 
|  | foreach (KeyValuePair<XATransactionId, byte[]> entry in localRecoverables) | 
|  | { | 
|  | if (entry.Key.Equals(recoverable)) | 
|  | { | 
|  | Tracer.DebugFormat("Found a matching TX on Broker to stored Id: {0} reenlisting.", entry.Key); | 
|  | matches.Add(entry); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (matches.Count != 0) | 
|  | { | 
|  | this.recoveryComplete = new CountDownLatch(matches.Count); | 
|  |  | 
|  | foreach (KeyValuePair<XATransactionId, byte[]> recoverable in matches) | 
|  | { | 
|  | this.TransactionId = recoverable.Key; | 
|  | Tracer.Info("Reenlisting recovered TX with Id: " + this.TransactionId); | 
|  | this.currentEnlistment = | 
|  | TransactionManager.Reenlist(ResourceManagerGuid, recoverable.Value, this); | 
|  | } | 
|  |  | 
|  | RecoveryCompleteAwait(); | 
|  | Tracer.Debug("All Recovered TX enlistments Reports complete, Recovery Complete."); | 
|  | TransactionManager.RecoveryComplete(ResourceManagerGuid); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The old recovery information doesn't match what's on the broker so we | 
|  | // should discard it as its stale now. | 
|  | RecoveryLogger.Purge(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private void RecoveryCompleteAwait() | 
|  | { | 
|  | this.recoveryComplete.@await(); | 
|  | } | 
|  |  | 
|  | private async Task<XATransactionId[]> TryRecoverBrokerTXIdsAsync() | 
|  | { | 
|  | Tracer.Debug("Checking for Recoverable Transactions on Broker."); | 
|  |  | 
|  | TransactionInfo info = new TransactionInfo(); | 
|  | info.ConnectionId = this.session.Connection.ConnectionId; | 
|  | info.Type = (int)TransactionType.Recover; | 
|  |  | 
|  | await this.connection.CheckConnectedAsync().Await(); | 
|  | DataArrayResponse response = await this.connection.SyncRequestAsync(info).Await() as DataArrayResponse; | 
|  |  | 
|  | if (response != null && response.Data.Length > 0) | 
|  | { | 
|  | Tracer.DebugFormat("Broker reports there are {0} recoverable XA Transactions", response.Data.Length); | 
|  |  | 
|  | List<XATransactionId> recovered = new List<XATransactionId>(); | 
|  |  | 
|  | foreach (DataStructure ds in response.Data) | 
|  | { | 
|  | XATransactionId xid = ds as XATransactionId; | 
|  | if (xid != null) | 
|  | { | 
|  | recovered.Add(xid); | 
|  | } | 
|  | } | 
|  |  | 
|  | return recovered.ToArray(); | 
|  | } | 
|  |  | 
|  | return new XATransactionId[0]; | 
|  | } | 
|  |  | 
|  | #endregion | 
|  |  | 
|  | internal IRecoveryLogger RecoveryLogger | 
|  | { | 
|  | get { return (this.connection as NetTxConnection).RecoveryPolicy.RecoveryLogger; } | 
|  | } | 
|  |  | 
|  | internal string ResourceManagerId | 
|  | { | 
|  | get { return (this.connection as NetTxConnection).ResourceManagerGuid.ToString(); } | 
|  | } | 
|  |  | 
|  | internal Guid ResourceManagerGuid | 
|  | { | 
|  | get { return (this.connection as NetTxConnection).ResourceManagerGuid; } | 
|  | } | 
|  |  | 
|  | } | 
|  | } |