/* * 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.IO; using System.Collections.Generic; using NUnit.Framework; using Apache.NMS.ActiveMQ.Transactions; using Apache.NMS.ActiveMQ.Commands; namespace Apache.NMS.ActiveMQ.Test.Transactions { using System.Threading; [TestFixture] public class RecoveryFileLoggerTest { private string resourceManagerId; private string nonExistantPath; private string autoCreatePath; private string nonDefaultLogLocation; [SetUp] public void SetUp() { this.resourceManagerId = Guid.NewGuid().ToString(); this.nonExistantPath = Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString()); this.nonDefaultLogLocation = Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString()); this.autoCreatePath = Path.Combine(Directory.GetCurrentDirectory(), Guid.NewGuid().ToString()); Directory.CreateDirectory(nonDefaultLogLocation); } [TearDown] public void TearDown() { SafeDeleteDirectory(autoCreatePath, 1000); SafeDeleteDirectory(nonDefaultLogLocation, 1000); } [Test] public void TestInitWithNoLocationSet() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Initialize(this.resourceManagerId); Assert.AreEqual(Directory.GetCurrentDirectory(), logger.Location); } [Test] public void TestInitWithNonDefaultLocationSet() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = nonDefaultLogLocation; logger.Initialize(this.resourceManagerId); Assert.AreEqual(nonDefaultLogLocation, logger.Location); } [Test] public void TestInitWithAutoCreateLocation() { RecoveryFileLogger logger = new RecoveryFileLogger(); Assert.IsFalse(Directory.Exists(autoCreatePath)); logger.AutoCreateLocation = true; logger.Location = autoCreatePath; logger.Initialize(this.resourceManagerId); Assert.IsTrue(Directory.Exists(autoCreatePath)); Assert.AreEqual(autoCreatePath, logger.Location); } [Test] public void TestInitWithLocationSetToBadPath() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = this.nonExistantPath; try { logger.Initialize(this.resourceManagerId); Assert.Fail("Should have detected an invalid dir and thrown an exception"); } catch { } } [Test] public void TestNothingToRecover() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = nonDefaultLogLocation; logger.Initialize(this.resourceManagerId); Assert.IsTrue(logger.GetRecoverables().Length == 0); } [Test] public void TestLogTransactionRecord() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = nonDefaultLogLocation; logger.Initialize(this.resourceManagerId); TransactionData transactionData = new TransactionData(); logger.LogRecoveryInfo(transactionData.Transaction, transactionData.RecoveryData); Assert.IsTrue(File.Exists(this.GetFilename(logger, transactionData)), "Recovery File was not created"); } [Test] public void TestRecoverLoggedRecord() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = this.nonDefaultLogLocation; logger.Initialize(this.resourceManagerId); TransactionData transactionData01 = new TransactionData(); logger.LogRecoveryInfo(transactionData01.Transaction, transactionData01.RecoveryData); TransactionData transactionData02 = new TransactionData(); logger.LogRecoveryInfo(transactionData02.Transaction, transactionData02.RecoveryData); Assert.IsTrue(File.Exists(this.GetFilename(logger, transactionData01)), "Recovery File was not created"); Assert.IsTrue(File.Exists(this.GetFilename(logger, transactionData02)), "Recovery File was not created"); Assert.AreEqual(2, logger.GetRecoverables().Length, "Did not recover the logged record."); KeyValuePair[] records = logger.GetRecoverables(); Assert.AreEqual(2, records.Length); foreach (var keyValuePair in records) { if (BitConverter.ToString(keyValuePair.Key.GlobalTransactionId) == BitConverter.ToString(transactionData01.Transaction.GlobalTransactionId)) { Assert.AreEqual(transactionData01.GlobalId, keyValuePair.Key.GlobalTransactionId, "Incorrect Global TX Id returned"); Assert.AreEqual(transactionData01.BranchQ, keyValuePair.Key.BranchQualifier, "Incorrect Branch Qualifier returned"); Assert.AreEqual(transactionData01.RecoveryData, keyValuePair.Value, "Incorrect Recovery Information returned"); } else if (BitConverter.ToString(keyValuePair.Key.GlobalTransactionId) == BitConverter.ToString(transactionData02.Transaction.GlobalTransactionId)) { Assert.AreEqual(transactionData02.GlobalId, keyValuePair.Key.GlobalTransactionId, "Incorrect Global TX Id returned"); Assert.AreEqual(transactionData02.BranchQ, keyValuePair.Key.BranchQualifier, "Incorrect Branch Qualifier returned"); Assert.AreEqual(transactionData02.RecoveryData, keyValuePair.Value, "Incorrect Recovery Information returned"); } else { Assert.Fail("Transaction not found."); } } } [Test] public void TestLogRecovered() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = nonDefaultLogLocation; logger.Initialize(this.resourceManagerId); TransactionData transactionData = new TransactionData(); logger.LogRecoveryInfo(transactionData.Transaction, transactionData.RecoveryData); Assert.IsTrue(File.Exists(this.GetFilename(logger, transactionData)), "Recovery File was not created"); logger.LogRecovered(transactionData.Transaction); this.AssertFileIsDeleted(this.GetFilename(logger, transactionData), 1000); } [Test] public void TestPurgeTransactionRecord() { RecoveryFileLogger logger = new RecoveryFileLogger(); logger.Location = nonDefaultLogLocation; logger.Initialize(this.resourceManagerId.ToString()); TransactionData transactionData01 = new TransactionData(); logger.LogRecoveryInfo(transactionData01.Transaction, transactionData01.RecoveryData); TransactionData transactionData02 = new TransactionData(); logger.LogRecoveryInfo(transactionData02.Transaction, transactionData02.RecoveryData); Assert.IsTrue(File.Exists(this.GetFilename(logger, transactionData01)), "Recovery File was not created"); Assert.IsTrue(File.Exists(this.GetFilename(logger, transactionData02)), "Recovery File was not created"); logger.Purge(); this.AssertFileIsDeleted(this.GetFilename(logger, transactionData01), 1000); this.AssertFileIsDeleted(this.GetFilename(logger, transactionData02), 1000); } private string GetFilename(RecoveryFileLogger logger, TransactionData transactionData) { return string.Format( "{0}{1}{2}_{3}.bin", logger.Location, Path.DirectorySeparatorChar, this.resourceManagerId.ToString(), BitConverter.ToString(transactionData.Transaction.GlobalTransactionId).Replace("-", string.Empty)); } private void AssertFileIsDeleted(string filename, int timeout) { var expiration = DateTime.Now.Add(TimeSpan.FromMilliseconds(timeout)); while (File.Exists(filename)) { if (expiration < DateTime.Now) { Assert.Fail("Recovery File was not removed"); } Thread.Sleep(5); } } private void SafeDeleteDirectory(string directory, int timeout) { var expiration = DateTime.Now.Add(TimeSpan.FromMilliseconds(timeout)); while (true) { if (!Directory.Exists(directory)) { return; } try { Directory.Delete(directory, true); return; } catch (Exception) { } if (expiration < DateTime.Now) { return; } Thread.Sleep(5); } } private class TransactionData { private static readonly Random Random = new Random(); private readonly XATransactionId xid; private readonly byte[] recoveryData = new byte[256]; private readonly byte[] globalId = new byte[32]; private readonly byte[] branchQ = new byte[32]; public TransactionData() { Random.NextBytes(this.globalId); Random.NextBytes(this.branchQ); Random.NextBytes(this.recoveryData); this.xid = new XATransactionId(); this.xid.GlobalTransactionId = this.globalId; this.xid.BranchQualifier = this.branchQ; } public XATransactionId Transaction { get { return this.xid; } } public byte[] RecoveryData { get { return this.recoveryData; } } public byte[] GlobalId { get { return this.globalId; } } public byte[] BranchQ { get { return this.branchQ; } } } } }