blob: 7fc30e9912610e1b86ffc3de96b4fc1b4fead30b [file] [log] [blame]
/*
* 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 string location;
private bool autoCreateLocation;
private string resourceManagerId;
private readonly object syncRoot = new object();
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 = "";
}
/// <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
{
lock (syncRoot)
{
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()
{
KeyValuePair<XATransactionId, byte[]>[] result = new KeyValuePair<XATransactionId, byte[]>[0];
RecoveryInformation info = TryOpenRecoveryInfoFile();
if (info != null)
{
result = new KeyValuePair<XATransactionId, byte[]>[1];
result[0] = new KeyValuePair<XATransactionId, byte[]>(info.Xid, info.TxRecoveryInfo);
}
return result;
}
public void LogRecovered(XATransactionId xid)
{
lock (syncRoot)
{
try
{
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);
return;
}
}
}
public void Purge()
{
lock (syncRoot)
{
try
{
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);
return;
}
}
}
public string LoggerType
{
get { return "file"; }
}
#region Recovery File Opeations
private string Filename
{
get { return Location + Path.DirectorySeparatorChar + ResourceManagerId + ".bin"; }
}
[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 RecoveryInformation TryOpenRecoveryInfoFile()
{
RecoveryInformation result = null;
Tracer.Debug("Checking for Recoverable Transactions filename: " + Filename);
lock (syncRoot)
{
try
{
if (!File.Exists(Filename))
{
return null;
}
using(FileStream recoveryLog = new FileStream(Filename, FileMode.Open, FileAccess.Read))
{
Tracer.Debug("Found Recovery Log File: " + Filename);
IFormatter formatter = new BinaryFormatter();
result = formatter.Deserialize(recoveryLog) as RecoveryInformation;
}
}
catch(Exception ex)
{
Tracer.ErrorFormat("Error while opening Recovery file {0} error message: {1}", Filename, ex.Message);
// Nothing to restore.
return null;
}
}
return result;
}
#endregion
}
}