src/Transactions/RecoveryFileLogger.cs (218 lines of code) (raw):

/* * 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.Net; using System.IO; using System.Reflection; using System.Collections.Generic; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using Apache.NMS.Util; using Apache.NMS.ActiveMQ.Commands; namespace Apache.NMS.ActiveMQ.Transactions { public class RecoveryFileLogger : IRecoveryLogger { private readonly object syncRoot = new object(); private string location; private bool autoCreateLocation; private string resourceManagerId; public RecoveryFileLogger() { // Set the path by default to the location of the executing assembly. // May need to change this to current working directory, not sure. this.location = string.Empty; } /// <summary> /// The Unique Id of the Resource Manager that this logger is currently /// logging recovery information for. /// </summary> public string ResourceManagerId { get { return this.resourceManagerId; } } /// <summary> /// The Path to the location on disk where the recovery log is written /// to and read from. /// </summary> public string Location { get { if(string.IsNullOrEmpty(this.location)) { return Directory.GetCurrentDirectory(); } return this.location; } set { this.location = Uri.UnescapeDataString(value); } } /// <summary> /// Indiciate that the Logger should create and sibdirs of the /// given location that don't currently exist. /// </summary> public bool AutoCreateLocation { get { return this.autoCreateLocation; } set { this.autoCreateLocation = value; } } public void Initialize(string resourceManagerId) { this.resourceManagerId = resourceManagerId; // Test if the location configured is valid. if(!Directory.Exists(Location)) { if(AutoCreateLocation) { try { Directory.CreateDirectory(Location); } catch(Exception ex) { Tracer.Error("Failed to create log directory: " + ex.Message); throw NMSExceptionSupport.Create(ex); } } else { throw new NMSException("Configured Recovery Log Location does not exist: " + location); } } } public void LogRecoveryInfo(XATransactionId xid, byte[] recoveryInformation) { if (recoveryInformation == null || recoveryInformation.Length == 0) { return; } try { string filename = this.CreateFilename(xid); RecoveryInformation info = new RecoveryInformation(xid, recoveryInformation); Tracer.Debug("Serializing Recovery Info to file: " + filename); IFormatter formatter = new BinaryFormatter(); using (FileStream recoveryLog = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write)) { formatter.Serialize(recoveryLog, info); } } catch (Exception ex) { Tracer.Error("Error while storing TX Recovery Info, message: " + ex.Message); throw; } } public KeyValuePair<XATransactionId, byte[]>[] GetRecoverables() { IList<RecoveryInformation> infos = this.TryOpenRecoveryInfoFile(); KeyValuePair<XATransactionId, byte[]>[] results = new KeyValuePair<XATransactionId, byte[]>[infos.Count]; int index = 0; foreach (RecoveryInformation info in infos) { results[index++] = new KeyValuePair<XATransactionId, byte[]>(info.Xid, info.TxRecoveryInfo); } return results; } public void LogRecovered(XATransactionId xid) { try { string filename = this.CreateFilename(xid); Tracer.Debug("Attempting to remove stale Recovery Info file: " + filename); File.Delete(filename); } catch (Exception ex) { Tracer.Debug("Caught Exception while removing stale RecoveryInfo file: " + ex.Message); } } public void Purge() { lock (this.syncRoot) { try { IEnumerable<string> files = this.GetFilesForResourceManagerId(); foreach (var file in files) { Tracer.Debug("Attempting to remove stale Recovery Info file: " + file); File.Delete(file); } } catch (Exception ex) { Tracer.Debug("Caught Exception while removing stale RecoveryInfo file: " + ex.Message); } } } private IEnumerable<string> GetFilesForResourceManagerId() { return Directory.GetFiles(this.Location, this.ResourceManagerId + "_*.bin"); } public string LoggerType { get { return "file"; } } #region Recovery File Opeations private string CreateFilename(XATransactionId xaTransactionId) { return string.Format( "{0}{1}{2}_{3}.bin", this.Location, Path.DirectorySeparatorChar, this.ResourceManagerId, GetHexValue(xaTransactionId)); } private static string GetHexValue(XATransactionId xid) { string transactionIdHexValue = BitConverter.ToString(xid.GlobalTransactionId); return transactionIdHexValue.Replace("-", string.Empty); } [Serializable] private sealed class RecoveryInformation { private byte[] txRecoveryInfo; private byte[] globalTxId; private byte[] branchId; private int formatId; public RecoveryInformation(XATransactionId xaId, byte[] recoveryInfo) { this.Xid = xaId; this.txRecoveryInfo = recoveryInfo; } public byte[] TxRecoveryInfo { get { return this.txRecoveryInfo; } set { this.txRecoveryInfo = value; } } public XATransactionId Xid { get { XATransactionId xid = new XATransactionId(); xid.BranchQualifier = this.branchId; xid.GlobalTransactionId = this.globalTxId; xid.FormatId = this.formatId; return xid; } set { this.branchId = value.BranchQualifier; this.globalTxId = value.GlobalTransactionId; this.formatId = value.FormatId; } } } private IList<RecoveryInformation> TryOpenRecoveryInfoFile() { List<RecoveryInformation> result = new List<RecoveryInformation>(); IEnumerable<string> files = this.GetFilesForResourceManagerId(); foreach (var file in files) { Tracer.Debug("Checking for Recoverable Transactions filename: " + file); try { using (FileStream recoveryLog = new FileStream(file, FileMode.Open, FileAccess.Read)) { Tracer.Debug("Found Recovery Log File: " + file); IFormatter formatter = new BinaryFormatter(); result.Add(formatter.Deserialize(recoveryLog) as RecoveryInformation); } } catch (Exception ex) { Tracer.ErrorFormat("Error while opening Recovery file {0} error message: {1}", file, ex.Message); } } return result; } #endregion } }