#region Apache License
//
// 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.
//
#endregion

using System;
using System.IO;
using System.Text;
using System.Threading;
using log4net.Util;
using log4net.Layout;
using log4net.Core;

namespace log4net.Appender
{
#if !NETCF
	/// <summary>
	/// Appends logging events to a file.
	/// </summary>
	/// <remarks>
	/// <para>
	/// Logging events are sent to the file specified by
	/// the <see cref="File"/> property.
	/// </para>
	/// <para>
	/// The file can be opened in either append or overwrite mode 
	/// by specifying the <see cref="AppendToFile"/> property.
	/// If the file path is relative it is taken as relative from 
	/// the application base directory. The file encoding can be
	/// specified by setting the <see cref="Encoding"/> property.
	/// </para>
	/// <para>
	/// The layout's <see cref="ILayout.Header"/> and <see cref="ILayout.Footer"/>
	/// values will be written each time the file is opened and closed
	/// respectively. If the <see cref="AppendToFile"/> property is <see langword="true"/>
	/// then the file may contain multiple copies of the header and footer.
	/// </para>
	/// <para>
	/// This appender will first try to open the file for writing when <see cref="ActivateOptions"/>
	/// is called. This will typically be during configuration.
	/// If the file cannot be opened for writing the appender will attempt
	/// to open the file again each time a message is logged to the appender.
	/// If the file cannot be opened for writing when a message is logged then
	/// the message will be discarded by this appender.
	/// </para>
    /// <para>
    /// The <see cref="FileAppender"/> supports pluggable file locking models via
    /// the <see cref="LockingModel"/> property.
    /// The default behavior, implemented by <see cref="FileAppender.ExclusiveLock"/> 
    /// is to obtain an exclusive write lock on the file until this appender is closed.
    /// The alternative models only hold a
    /// write lock while the appender is writing a logging event (<see cref="FileAppender.MinimalLock"/>)
    /// or synchronize by using a named system wide Mutex (<see cref="FileAppender.InterProcessLock"/>).
    /// </para>
    /// <para>
    /// All locking strategies have issues and you should seriously consider using a different strategy that
    /// avoids having multiple processes logging to the same file.
    /// </para>
	/// </remarks>
	/// <author>Nicko Cadell</author>
	/// <author>Gert Driesen</author>
	/// <author>Rodrigo B. de Oliveira</author>
	/// <author>Douglas de la Torre</author>
	/// <author>Niall Daley</author>
#else
	/// <summary>
	/// Appends logging events to a file.
	/// </summary>
	/// <remarks>
	/// <para>
	/// Logging events are sent to the file specified by
	/// the <see cref="File"/> property.
	/// </para>
	/// <para>
	/// The file can be opened in either append or overwrite mode 
	/// by specifying the <see cref="AppendToFile"/> property.
	/// If the file path is relative it is taken as relative from 
	/// the application base directory. The file encoding can be
	/// specified by setting the <see cref="Encoding"/> property.
	/// </para>
	/// <para>
	/// The layout's <see cref="ILayout.Header"/> and <see cref="ILayout.Footer"/>
	/// values will be written each time the file is opened and closed
	/// respectively. If the <see cref="AppendToFile"/> property is <see langword="true"/>
	/// then the file may contain multiple copies of the header and footer.
	/// </para>
	/// <para>
	/// This appender will first try to open the file for writing when <see cref="ActivateOptions"/>
	/// is called. This will typically be during configuration.
	/// If the file cannot be opened for writing the appender will attempt
	/// to open the file again each time a message is logged to the appender.
	/// If the file cannot be opened for writing when a message is logged then
	/// the message will be discarded by this appender.
	/// </para>
	/// <para>
	/// The <see cref="FileAppender"/> supports pluggable file locking models via
	/// the <see cref="LockingModel"/> property.
	/// The default behavior, implemented by <see cref="FileAppender.ExclusiveLock"/> 
	/// is to obtain an exclusive write lock on the file until this appender is closed.
	/// The alternative model only holds a
    /// write lock while the appender is writing a logging event (<see cref="FileAppender.MinimalLock"/>).
	/// </para>
    /// <para>
    /// All locking strategies have issues and you should seriously consider using a different strategy that
    /// avoids having multiple processes logging to the same file.
    /// </para>
	/// </remarks>
	/// <author>Nicko Cadell</author>
	/// <author>Gert Driesen</author>
	/// <author>Rodrigo B. de Oliveira</author>
	/// <author>Douglas de la Torre</author>
	/// <author>Niall Daley</author>
#endif
    public class FileAppender : TextWriterAppender 
	{
		#region LockingStream Inner Class

		/// <summary>
		/// Write only <see cref="Stream"/> that uses the <see cref="LockingModelBase"/> 
		/// to manage access to an underlying resource.
		/// </summary>
		private sealed class LockingStream : Stream, IDisposable
		{
			public sealed class LockStateException : LogException
			{
				public LockStateException(string message): base(message)
				{
				}
			}

			private Stream m_realStream=null;
			private LockingModelBase m_lockingModel=null;
			private int m_readTotal=-1;
			private int m_lockLevel=0;

			public LockingStream(LockingModelBase locking) : base()
			{
				if (locking==null)
				{
					throw new ArgumentException("Locking model may not be null","locking");
				}
				m_lockingModel=locking;
			}

			#region Override Implementation of Stream

			// Methods
			public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
			{
				AssertLocked();
				IAsyncResult ret=m_realStream.BeginRead(buffer,offset,count,callback,state);
				m_readTotal=EndRead(ret);
				return ret;
			}

			/// <summary>
			/// True asynchronous writes are not supported, the implementation forces a synchronous write.
			/// </summary>
			public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
			{
				AssertLocked();
				IAsyncResult ret=m_realStream.BeginWrite(buffer,offset,count,callback,state);
				EndWrite(ret);
				return ret;
			}

			public override void Close() 
			{
				m_lockingModel.CloseFile();
			}

			public override int EndRead(IAsyncResult asyncResult) 
			{
				AssertLocked();
				return m_readTotal;
			}
			public override void EndWrite(IAsyncResult asyncResult) 
			{
				//No-op, it has already been handled
			}
			public override void Flush() 
			{
				AssertLocked();
				m_realStream.Flush();
			}
			public override int Read(byte[] buffer, int offset, int count) 
			{
				return m_realStream.Read(buffer,offset,count);
			}
			public override int ReadByte() 
			{
				return m_realStream.ReadByte();
			}
			public override long Seek(long offset, SeekOrigin origin) 
			{
				AssertLocked();
				return m_realStream.Seek(offset,origin);
			}
			public override void SetLength(long value) 
			{
				AssertLocked();
				m_realStream.SetLength(value);
			}
			void IDisposable.Dispose() 
			{
				Close();
			}
			public override void Write(byte[] buffer, int offset, int count) 
			{
				AssertLocked();
				m_realStream.Write(buffer,offset,count);
			}
			public override void WriteByte(byte value) 
			{
				AssertLocked();
				m_realStream.WriteByte(value);
			}

			// Properties
			public override bool CanRead 
			{ 
				get { return false; } 
			}
			public override bool CanSeek 
			{ 
				get 
				{
					AssertLocked();
					return m_realStream.CanSeek;
				} 
			}
			public override bool CanWrite 
			{ 
				get 
				{
					AssertLocked();
					return m_realStream.CanWrite;
				} 
			}
			public override long Length 
			{ 
				get 
				{
					AssertLocked();
					return m_realStream.Length;
				} 
			}
			public override long Position 
			{ 
				get 
				{
					AssertLocked();
					return m_realStream.Position;
				} 
				set 
				{
					AssertLocked();
					m_realStream.Position=value;
				} 
			}

			#endregion Override Implementation of Stream

			#region Locking Methods

			private void AssertLocked()
			{
				if (m_realStream == null)
				{
					throw new LockStateException("The file is not currently locked");
				}
			}

			public bool AcquireLock()
			{
				bool ret=false;
				lock(this)
				{
					if (m_lockLevel==0)
					{
						// If lock is already acquired, nop
						m_realStream=m_lockingModel.AcquireLock();
					}
					if (m_realStream!=null)
					{
						m_lockLevel++;
						ret=true;
					}
				}
				return ret;
			}

			public void ReleaseLock()
			{
				lock(this)
				{
					m_lockLevel--;
					if (m_lockLevel==0)
					{
						// If already unlocked, nop
						m_lockingModel.ReleaseLock();
						m_realStream=null;
					}
				}
			}

			#endregion Locking Methods
		}

		#endregion LockingStream Inner Class

		#region Locking Models

		/// <summary>
		/// Locking model base class
		/// </summary>
		/// <remarks>
		/// <para>
		/// Base class for the locking models available to the <see cref="FileAppender"/> derived loggers.
		/// </para>
		/// </remarks>
		public abstract class LockingModelBase
		{
			private FileAppender m_appender=null;

			/// <summary>
			/// Open the output file
			/// </summary>
			/// <param name="filename">The filename to use</param>
			/// <param name="append">Whether to append to the file, or overwrite</param>
			/// <param name="encoding">The encoding to use</param>
			/// <remarks>
			/// <para>
			/// Open the file specified and prepare for logging. 
			/// No writes will be made until <see cref="AcquireLock"/> is called.
			/// Must be called before any calls to <see cref="AcquireLock"/>,
			/// <see cref="ReleaseLock"/> and <see cref="CloseFile"/>.
			/// </para>
			/// </remarks>
			public abstract void OpenFile(string filename, bool append,Encoding encoding);

			/// <summary>
			/// Close the file
			/// </summary>
			/// <remarks>
			/// <para>
			/// Close the file. No further writes will be made.
			/// </para>
			/// </remarks>
			public abstract void CloseFile();

			/// <summary>
			/// Acquire the lock on the file
			/// </summary>
			/// <returns>A stream that is ready to be written to.</returns>
			/// <remarks>
			/// <para>
			/// Acquire the lock on the file in preparation for writing to it. 
			/// Return a stream pointing to the file. <see cref="ReleaseLock"/>
			/// must be called to release the lock on the output file.
			/// </para>
			/// </remarks>
			public abstract Stream AcquireLock();

			/// <summary>
			/// Release the lock on the file
			/// </summary>
			/// <remarks>
			/// <para>
			/// Release the lock on the file. No further writes will be made to the 
			/// stream until <see cref="AcquireLock"/> is called again.
			/// </para>
			/// </remarks>
			public abstract void ReleaseLock();

			/// <summary>
			/// Gets or sets the <see cref="FileAppender"/> for this LockingModel
			/// </summary>
			/// <value>
			/// The <see cref="FileAppender"/> for this LockingModel
			/// </value>
			/// <remarks>
			/// <para>
			/// The file appender this locking model is attached to and working on
			/// behalf of.
			/// </para>
			/// <para>
			/// The file appender is used to locate the security context and the error handler to use.
			/// </para>
			/// <para>
			/// The value of this property will be set before <see cref="OpenFile"/> is
			/// called.
			/// </para>
			/// </remarks>
			public FileAppender CurrentAppender
			{
				get { return m_appender; }
				set { m_appender = value; }
			}

            /// <summary>
            /// Helper method that creates a FileStream under CurrentAppender's SecurityContext.
            /// </summary>
            /// <remarks>
            /// <para>
            /// Typically called during OpenFile or AcquireLock. 
            /// </para>
            /// <para>
            /// If the directory portion of the <paramref name="filename"/> does not exist, it is created
            /// via Directory.CreateDirecctory.
            /// </para>
            /// </remarks>
            /// <param name="filename"></param>
            /// <param name="append"></param>
            /// <param name="fileShare"></param>
            /// <returns></returns>
            protected Stream CreateStream(string filename, bool append, FileShare fileShare)
            {
                using (CurrentAppender.SecurityContext.Impersonate(this))
                {
                    // Ensure that the directory structure exists
                    string directoryFullName = Path.GetDirectoryName(filename);

                    // Only create the directory if it does not exist
                    // doing this check here resolves some permissions failures
                    if (!Directory.Exists(directoryFullName))
                    {
                        Directory.CreateDirectory(directoryFullName);
                    }

                    FileMode fileOpenMode = append ? FileMode.Append : FileMode.Create;
                    return new FileStream(filename, fileOpenMode, FileAccess.Write, fileShare);
                }
            }

            /// <summary>
            /// Helper method to close <paramref name="stream"/> under CurrentAppender's SecurityContext.
            /// </summary>
            /// <remarks>
            /// Does not set <paramref name="stream"/> to null.
            /// </remarks>
            /// <param name="stream"></param>
            protected void CloseStream(Stream stream)
            {
                using (CurrentAppender.SecurityContext.Impersonate(this))
                {
                    stream.Close();
                }
           }
		}

		/// <summary>
		/// Hold an exclusive lock on the output file
		/// </summary>
		/// <remarks>
		/// <para>
		/// Open the file once for writing and hold it open until <see cref="CloseFile"/> is called. 
		/// Maintains an exclusive lock on the file during this time.
		/// </para>
		/// </remarks>
		public class ExclusiveLock : LockingModelBase
		{
			private Stream m_stream = null;

			/// <summary>
			/// Open the file specified and prepare for logging.
			/// </summary>
			/// <param name="filename">The filename to use</param>
			/// <param name="append">Whether to append to the file, or overwrite</param>
			/// <param name="encoding">The encoding to use</param>
			/// <remarks>
			/// <para>
			/// Open the file specified and prepare for logging. 
			/// No writes will be made until <see cref="AcquireLock"/> is called.
			/// Must be called before any calls to <see cref="AcquireLock"/>,
			/// <see cref="ReleaseLock"/> and <see cref="CloseFile"/>.
			/// </para>
			/// </remarks>
			public override void OpenFile(string filename, bool append,Encoding encoding)
			{
				try
				{
                    m_stream = CreateStream(filename, append, FileShare.Read);
				}
				catch (Exception e1)
				{
					CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file "+filename+". "+e1.Message);
				}
			}

			/// <summary>
			/// Close the file
			/// </summary>
			/// <remarks>
			/// <para>
			/// Close the file. No further writes will be made.
			/// </para>
			/// </remarks>
			public override void CloseFile()
			{
                CloseStream(m_stream);
                m_stream = null;
			}

			/// <summary>
			/// Acquire the lock on the file
			/// </summary>
			/// <returns>A stream that is ready to be written to.</returns>
			/// <remarks>
			/// <para>
			/// Does nothing. The lock is already taken
			/// </para>
			/// </remarks>
			public override Stream AcquireLock()
			{
				return m_stream;
			}

			/// <summary>
			/// Release the lock on the file
			/// </summary>
			/// <remarks>
			/// <para>
			/// Does nothing. The lock will be released when the file is closed.
			/// </para>
			/// </remarks>
			public override void ReleaseLock()
			{
				//NOP
			}
		}

		/// <summary>
		/// Acquires the file lock for each write
		/// </summary>
		/// <remarks>
		/// <para>
		/// Opens the file once for each <see cref="AcquireLock"/>/<see cref="ReleaseLock"/> cycle, 
		/// thus holding the lock for the minimal amount of time. This method of locking
		/// is considerably slower than <see cref="FileAppender.ExclusiveLock"/> but allows 
		/// other processes to move/delete the log file whilst logging continues.
		/// </para>
		/// </remarks>
		public class MinimalLock : LockingModelBase
		{
			private string m_filename;
			private bool m_append;
			private Stream m_stream=null;

			/// <summary>
			/// Prepares to open the file when the first message is logged.
			/// </summary>
			/// <param name="filename">The filename to use</param>
			/// <param name="append">Whether to append to the file, or overwrite</param>
			/// <param name="encoding">The encoding to use</param>
			/// <remarks>
			/// <para>
			/// Open the file specified and prepare for logging. 
			/// No writes will be made until <see cref="AcquireLock"/> is called.
			/// Must be called before any calls to <see cref="AcquireLock"/>,
			/// <see cref="ReleaseLock"/> and <see cref="CloseFile"/>.
			/// </para>
			/// </remarks>
			public override void OpenFile(string filename, bool append, Encoding encoding)
			{
				m_filename=filename;
				m_append=append;
			}

			/// <summary>
			/// Close the file
			/// </summary>
			/// <remarks>
			/// <para>
			/// Close the file. No further writes will be made.
			/// </para>
			/// </remarks>
			public override void CloseFile()
			{
				// NOP
			}

			/// <summary>
			/// Acquire the lock on the file
			/// </summary>
			/// <returns>A stream that is ready to be written to.</returns>
			/// <remarks>
			/// <para>
			/// Acquire the lock on the file in preparation for writing to it. 
			/// Return a stream pointing to the file. <see cref="ReleaseLock"/>
			/// must be called to release the lock on the output file.
			/// </para>
			/// </remarks>
			public override Stream AcquireLock()
			{
				if (m_stream==null)
				{
					try
					{
                        m_stream = CreateStream(m_filename, m_append, FileShare.Read);
                        m_append = true;
					}
					catch (Exception e1)
					{
						CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file "+m_filename+". "+e1.Message);
					}
				}
				return m_stream;
			}

			/// <summary>
			/// Release the lock on the file
			/// </summary>
			/// <remarks>
			/// <para>
			/// Release the lock on the file. No further writes will be made to the 
			/// stream until <see cref="AcquireLock"/> is called again.
			/// </para>
			/// </remarks>
			public override void ReleaseLock()
			{
                CloseStream(m_stream);
                m_stream = null;
			}
		}

#if !NETCF
        /// <summary>
        /// Provides cross-process file locking.
        /// </summary>
        /// <author>Ron Grabowski</author>
        /// <author>Steve Wranovsky</author>
        public class InterProcessLock : LockingModelBase
        {
            private Mutex m_mutex = null;
            private bool m_mutexClosed = false;
            private Stream m_stream = null;

            /// <summary>
            /// Open the file specified and prepare for logging.
            /// </summary>
            /// <param name="filename">The filename to use</param>
            /// <param name="append">Whether to append to the file, or overwrite</param>
            /// <param name="encoding">The encoding to use</param>
            /// <remarks>
            /// <para>
            /// Open the file specified and prepare for logging. 
            /// No writes will be made until <see cref="AcquireLock"/> is called.
            /// Must be called before any calls to <see cref="AcquireLock"/>,
            /// -<see cref="ReleaseLock"/> and <see cref="CloseFile"/>.
            /// </para>
            /// </remarks>
#if NET_4_0
            [System.Security.SecuritySafeCritical]
#endif
            public override void OpenFile(string filename, bool append, Encoding encoding)
            {
                try
                {
                    m_stream = CreateStream(filename, append, FileShare.ReadWrite);

                    string mutextFriendlyFilename = filename
                            .Replace("\\", "_")
                            .Replace(":", "_")
                            .Replace("/", "_");

                    m_mutex = new Mutex(false, mutextFriendlyFilename); 
                }
                catch (Exception e1)
                {
                    CurrentAppender.ErrorHandler.Error("Unable to acquire lock on file " + filename + ". " + e1.Message);
                }
            }

            /// <summary>
            /// Close the file
            /// </summary>
            /// <remarks>
            /// <para>
            /// Close the file. No further writes will be made.
            /// </para>
            /// </remarks>
            public override void CloseFile()
            {
                try {
                    CloseStream(m_stream);
                    m_stream = null;
                }
                finally {
                    m_mutex.ReleaseMutex();
                    m_mutex.Close();
                    m_mutexClosed = true;
                }
            }

            /// <summary>
            /// Acquire the lock on the file
            /// </summary>
            /// <returns>A stream that is ready to be written to.</returns>
            /// <remarks>
            /// <para>
            /// Does nothing. The lock is already taken
            /// </para>
            /// </remarks>
            public override Stream AcquireLock()
            {
                if (m_mutex != null) {
                    // TODO: add timeout?
                    m_mutex.WaitOne();

                    // should always be true (and fast) for FileStream
                    if (m_stream.CanSeek) {
                        m_stream.Seek(0, SeekOrigin.End);
                    }
                }

                return m_stream;
            }

            /// <summary>
            /// 
            /// </summary>
            public override void ReleaseLock()
            {
                if (m_mutexClosed == false && m_mutex != null)
                {
                    m_mutex.ReleaseMutex();
                }
            }
        }
#endif

		#endregion Locking Models

		#region Public Instance Constructors

		/// <summary>
		/// Default constructor
		/// </summary>
		/// <remarks>
		/// <para>
		/// Default constructor
		/// </para>
		/// </remarks>
		public FileAppender()
		{
		}

		/// <summary>
		/// Construct a new appender using the layout, file and append mode.
		/// </summary>
		/// <param name="layout">the layout to use with this appender</param>
		/// <param name="filename">the full path to the file to write to</param>
		/// <param name="append">flag to indicate if the file should be appended to</param>
		/// <remarks>
		/// <para>
		/// Obsolete constructor.
		/// </para>
		/// </remarks>
		[Obsolete("Instead use the default constructor and set the Layout, File & AppendToFile properties")]
		public FileAppender(ILayout layout, string filename, bool append) 
		{
			Layout = layout;
			File = filename;
			AppendToFile = append;
			ActivateOptions();
		}

		/// <summary>
		/// Construct a new appender using the layout and file specified.
		/// The file will be appended to.
		/// </summary>
		/// <param name="layout">the layout to use with this appender</param>
		/// <param name="filename">the full path to the file to write to</param>
		/// <remarks>
		/// <para>
		/// Obsolete constructor.
		/// </para>
		/// </remarks>
		[Obsolete("Instead use the default constructor and set the Layout & File properties")]
		public FileAppender(ILayout layout, string filename) : this(layout, filename, true)
		{
		}

		#endregion Public Instance Constructors

		#region Public Instance Properties

		/// <summary>
		/// Gets or sets the path to the file that logging will be written to.
		/// </summary>
		/// <value>
		/// The path to the file that logging will be written to.
		/// </value>
		/// <remarks>
		/// <para>
		/// If the path is relative it is taken as relative from 
		/// the application base directory.
		/// </para>
		/// </remarks>
		virtual public string File
		{
			get { return m_fileName; }
			set { m_fileName = value; }
		}

		/// <summary>
		/// Gets or sets a flag that indicates whether the file should be
		/// appended to or overwritten.
		/// </summary>
		/// <value>
		/// Indicates whether the file should be appended to or overwritten.
		/// </value>
		/// <remarks>
		/// <para>
		/// If the value is set to false then the file will be overwritten, if 
		/// it is set to true then the file will be appended to.
		/// </para>
		/// The default value is true.
		/// </remarks>
		public bool AppendToFile
		{
			get { return m_appendToFile; }
			set { m_appendToFile = value; }
		}

		/// <summary>
		/// Gets or sets <see cref="Encoding"/> used to write to the file.
		/// </summary>
		/// <value>
		/// The <see cref="Encoding"/> used to write to the file.
		/// </value>
		/// <remarks>
		/// <para>
		/// The default encoding set is <see cref="System.Text.Encoding.Default"/>
		/// which is the encoding for the system's current ANSI code page.
		/// </para>
		/// </remarks>
		public Encoding Encoding
		{
			get { return m_encoding; }
			set { m_encoding = value; }
		}

		/// <summary>
		/// Gets or sets the <see cref="SecurityContext"/> used to write to the file.
		/// </summary>
		/// <value>
		/// The <see cref="SecurityContext"/> used to write to the file.
		/// </value>
		/// <remarks>
		/// <para>
		/// Unless a <see cref="SecurityContext"/> specified here for this appender
		/// the <see cref="SecurityContextProvider.DefaultProvider"/> is queried for the
		/// security context to use. The default behavior is to use the security context
		/// of the current thread.
		/// </para>
		/// </remarks>
		public SecurityContext SecurityContext 
		{
			get { return m_securityContext; }
			set { m_securityContext = value; }
		}

#if NETCF
		/// <summary>
		/// Gets or sets the <see cref="FileAppender.LockingModel"/> used to handle locking of the file.
		/// </summary>
		/// <value>
		/// The <see cref="FileAppender.LockingModel"/> used to lock the file.
		/// </value>
		/// <remarks>
		/// <para>
		/// Gets or sets the <see cref="FileAppender.LockingModel"/> used to handle locking of the file.
		/// </para>
		/// <para>
        /// There are two built in locking models, <see cref="FileAppender.ExclusiveLock"/> and <see cref="FileAppender.MinimalLock"/>.
		/// The first locks the file from the start of logging to the end, the 
		/// second locks only for the minimal amount of time when logging each message
        /// and the last synchronizes processes using a named system wide Mutex.
		/// </para>
		/// <para>
		/// The default locking model is the <see cref="FileAppender.ExclusiveLock"/>.
		/// </para>
		/// </remarks>
#else
        /// <summary>
		/// Gets or sets the <see cref="FileAppender.LockingModel"/> used to handle locking of the file.
		/// </summary>
		/// <value>
		/// The <see cref="FileAppender.LockingModel"/> used to lock the file.
		/// </value>
		/// <remarks>
		/// <para>
		/// Gets or sets the <see cref="FileAppender.LockingModel"/> used to handle locking of the file.
		/// </para>
		/// <para>
        /// There are three built in locking models, <see cref="FileAppender.ExclusiveLock"/>, <see cref="FileAppender.MinimalLock"/> and <see cref="FileAppender.InterProcessLock"/> .
		/// The first locks the file from the start of logging to the end, the 
		/// second locks only for the minimal amount of time when logging each message
        /// and the last synchronizes processes using a named system wide Mutex.
		/// </para>
		/// <para>
		/// The default locking model is the <see cref="FileAppender.ExclusiveLock"/>.
		/// </para>
		/// </remarks>
#endif
		public FileAppender.LockingModelBase LockingModel
		{
			get { return m_lockingModel; }
			set { m_lockingModel = value; }
		}

		#endregion Public Instance Properties

		#region Override implementation of AppenderSkeleton

		/// <summary>
		/// Activate the options on the file appender. 
		/// </summary>
		/// <remarks>
		/// <para>
		/// This is part of the <see cref="IOptionHandler"/> delayed object
		/// activation scheme. The <see cref="ActivateOptions"/> method must 
		/// be called on this object after the configuration properties have
		/// been set. Until <see cref="ActivateOptions"/> is called this
		/// object is in an undefined state and must not be used. 
		/// </para>
		/// <para>
		/// If any of the configuration properties are modified then 
		/// <see cref="ActivateOptions"/> must be called again.
		/// </para>
		/// <para>
		/// This will cause the file to be opened.
		/// </para>
		/// </remarks>
		override public void ActivateOptions() 
		{	
			base.ActivateOptions();

			if (m_securityContext == null)
			{
				m_securityContext = SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
			}

			if (m_lockingModel == null)
			{
				m_lockingModel = new FileAppender.ExclusiveLock();
			}

			m_lockingModel.CurrentAppender=this;
			
			if (m_fileName != null) 
			{
				using(SecurityContext.Impersonate(this))
				{
					m_fileName = ConvertToFullPath(m_fileName.Trim());
				}
				SafeOpenFile(m_fileName, m_appendToFile);
			} 
			else 
			{
				LogLog.Warn(declaringType, "FileAppender: File option not set for appender ["+Name+"].");
				LogLog.Warn(declaringType, "FileAppender: Are you using FileAppender instead of ConsoleAppender?");
			}
		}

		#endregion Override implementation of AppenderSkeleton

		#region Override implementation of TextWriterAppender

		/// <summary>
		/// Closes any previously opened file and calls the parent's <see cref="TextWriterAppender.Reset"/>.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Resets the filename and the file stream.
		/// </para>
		/// </remarks>
		override protected void Reset() 
		{
			base.Reset();
			m_fileName = null;
		}

 		/// <summary>
 		/// Called to initialize the file writer
 		/// </summary>
 		/// <remarks>
 		/// <para>
 		/// Will be called for each logged message until the file is
 		/// successfully opened.
 		/// </para>
 		/// </remarks>
 		override protected void PrepareWriter()
 		{
			SafeOpenFile(m_fileName, m_appendToFile);
 		}

		/// <summary>
		/// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent)"/>
		/// method. 
		/// </summary>
		/// <param name="loggingEvent">The event to log.</param>
		/// <remarks>
		/// <para>
		/// Writes a log statement to the output stream if the output stream exists 
		/// and is writable.  
		/// </para>
		/// <para>
		/// The format of the output will depend on the appender's layout.
		/// </para>
		/// </remarks>
		override protected void Append(LoggingEvent loggingEvent) 
		{
			if (m_stream.AcquireLock())
			{
				try
				{
					base.Append(loggingEvent);
				}
				finally
				{
					m_stream.ReleaseLock();
				}
			}
		}

		/// <summary>
		/// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent[])"/>
		/// method. 
		/// </summary>
		/// <param name="loggingEvents">The array of events to log.</param>
		/// <remarks>
		/// <para>
		/// Acquires the output file locks once before writing all the events to
		/// the stream.
		/// </para>
		/// </remarks>
		override protected void Append(LoggingEvent[] loggingEvents) 
		{
			if (m_stream.AcquireLock())
			{
				try
				{
					base.Append(loggingEvents);
				}
				finally
				{
					m_stream.ReleaseLock();
				}
			}
		}

		/// <summary>
		/// Writes a footer as produced by the embedded layout's <see cref="ILayout.Footer"/> property.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Writes a footer as produced by the embedded layout's <see cref="ILayout.Footer"/> property.
		/// </para>
		/// </remarks>
		protected override void WriteFooter() 
		{
			if (m_stream!=null)
			{
				//WriteFooter can be called even before a file is opened
				m_stream.AcquireLock();
				try
				{
					base.WriteFooter();
				}
				finally
				{
					m_stream.ReleaseLock();
				}
			}
		}

		/// <summary>
		/// Writes a header produced by the embedded layout's <see cref="ILayout.Header"/> property.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Writes a header produced by the embedded layout's <see cref="ILayout.Header"/> property.
		/// </para>
		/// </remarks>
		protected override void WriteHeader() 
		{
			if (m_stream!=null)
			{
				if (m_stream.AcquireLock())
				{
					try
					{
						base.WriteHeader();
					}
					finally
					{
						m_stream.ReleaseLock();
					}
				}
			}
		}

		/// <summary>
		/// Closes the underlying <see cref="TextWriter"/>.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Closes the underlying <see cref="TextWriter"/>.
		/// </para>
		/// </remarks>
		protected override void CloseWriter() 
		{
			if (m_stream!=null)
			{
				m_stream.AcquireLock();
				try
				{
					base.CloseWriter();
				}
				finally
				{
					m_stream.ReleaseLock();
				}
			}
		}

		#endregion Override implementation of TextWriterAppender

		#region Public Instance Methods

		/// <summary>
		/// Closes the previously opened file.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Writes the <see cref="ILayout.Footer"/> to the file and then
		/// closes the file.
		/// </para>
		/// </remarks>
		protected void CloseFile() 
		{
			WriteFooterAndCloseWriter();
		}

		#endregion Public Instance Methods

		#region Protected Instance Methods

		/// <summary>
		/// Sets and <i>opens</i> the file where the log output will go. The specified file must be writable.
		/// </summary>
		/// <param name="fileName">The path to the log file. Must be a fully qualified path.</param>
		/// <param name="append">If true will append to fileName. Otherwise will truncate fileName</param>
		/// <remarks>
		/// <para>
		/// Calls <see cref="OpenFile"/> but guarantees not to throw an exception.
		/// Errors are passed to the <see cref="TextWriterAppender.ErrorHandler"/>.
		/// </para>
		/// </remarks>
		virtual protected void SafeOpenFile(string fileName, bool append)
		{
			try 
			{
				OpenFile(fileName, append);
			}
			catch(Exception e) 
			{
				ErrorHandler.Error("OpenFile("+fileName+","+append+") call failed.", e, ErrorCode.FileOpenFailure);
			}
		}

		/// <summary>
		/// Sets and <i>opens</i> the file where the log output will go. The specified file must be writable.
		/// </summary>
		/// <param name="fileName">The path to the log file. Must be a fully qualified path.</param>
		/// <param name="append">If true will append to fileName. Otherwise will truncate fileName</param>
		/// <remarks>
		/// <para>
		/// If there was already an opened file, then the previous file
		/// is closed first.
		/// </para>
		/// <para>
		/// This method will ensure that the directory structure
		/// for the <paramref name="fileName"/> specified exists.
		/// </para>
		/// </remarks>
		virtual protected void OpenFile(string fileName, bool append)
		{
			if (LogLog.IsErrorEnabled)
			{
				// Internal check that the fileName passed in is a rooted path
				bool isPathRooted = false;
				using(SecurityContext.Impersonate(this))
				{
					isPathRooted = Path.IsPathRooted(fileName);
				}
				if (!isPathRooted)
				{
					LogLog.Error(declaringType, "INTERNAL ERROR. OpenFile("+fileName+"): File name is not fully qualified.");
				}
			}

			lock(this)
			{
				Reset();

				LogLog.Debug(declaringType, "Opening file for writing ["+fileName+"] append ["+append+"]");

				// Save these for later, allowing retries if file open fails
				m_fileName = fileName;
				m_appendToFile = append;

				LockingModel.CurrentAppender=this;
				LockingModel.OpenFile(fileName,append,m_encoding);
				m_stream=new LockingStream(LockingModel);

				if (m_stream != null)
				{
					m_stream.AcquireLock();
					try
					{
						SetQWForFiles(new StreamWriter(m_stream, m_encoding));
					}
					finally
					{
						m_stream.ReleaseLock();
					}
				}

				WriteHeader();
			}
		}

		/// <summary>
		/// Sets the quiet writer used for file output
		/// </summary>
		/// <param name="fileStream">the file stream that has been opened for writing</param>
		/// <remarks>
		/// <para>
		/// This implementation of <see cref="M:SetQWForFiles(Stream)"/> creates a <see cref="StreamWriter"/>
		/// over the <paramref name="fileStream"/> and passes it to the 
		/// <see cref="M:SetQWForFiles(TextWriter)"/> method.
		/// </para>
		/// <para>
		/// This method can be overridden by sub classes that want to wrap the
		/// <see cref="Stream"/> in some way, for example to encrypt the output
		/// data using a <c>System.Security.Cryptography.CryptoStream</c>.
		/// </para>
		/// </remarks>
		virtual protected void SetQWForFiles(Stream fileStream) 
		{
			SetQWForFiles(new StreamWriter(fileStream, m_encoding));
		}

		/// <summary>
		/// Sets the quiet writer being used.
		/// </summary>
		/// <param name="writer">the writer over the file stream that has been opened for writing</param>
		/// <remarks>
		/// <para>
		/// This method can be overridden by sub classes that want to
		/// wrap the <see cref="TextWriter"/> in some way.
		/// </para>
		/// </remarks>
		virtual protected void SetQWForFiles(TextWriter writer) 
		{
			QuietWriter = new QuietTextWriter(writer, ErrorHandler);
		}

		#endregion Protected Instance Methods

		#region Protected Static Methods

		/// <summary>
		/// Convert a path into a fully qualified path.
		/// </summary>
		/// <param name="path">The path to convert.</param>
		/// <returns>The fully qualified path.</returns>
		/// <remarks>
		/// <para>
		/// Converts the path specified to a fully
		/// qualified path. If the path is relative it is
		/// taken as relative from the application base 
		/// directory.
		/// </para>
		/// </remarks>
		protected static string ConvertToFullPath(string path)
		{
			return SystemInfo.ConvertToFullPath(path);
		}

		#endregion Protected Static Methods

		#region Private Instance Fields

		/// <summary>
		/// Flag to indicate if we should append to the file
		/// or overwrite the file. The default is to append.
		/// </summary>
		private bool m_appendToFile = true;

		/// <summary>
		/// The name of the log file.
		/// </summary>
		private string m_fileName = null;

		/// <summary>
		/// The encoding to use for the file stream.
		/// </summary>
		private Encoding m_encoding = Encoding.Default;

		/// <summary>
		/// The security context to use for privileged calls
		/// </summary>
		private SecurityContext m_securityContext;

		/// <summary>
		/// The stream to log to. Has added locking semantics
		/// </summary>
		private FileAppender.LockingStream m_stream = null;

		/// <summary>
		/// The locking model to use
		/// </summary>
		private FileAppender.LockingModelBase m_lockingModel = new FileAppender.ExclusiveLock();

		#endregion Private Instance Fields

	    #region Private Static Fields

	    /// <summary>
	    /// The fully qualified type of the FileAppender class.
	    /// </summary>
	    /// <remarks>
	    /// Used by the internal logger to record the Type of the
	    /// log message.
	    /// </remarks>
	    private readonly static Type declaringType = typeof(FileAppender);

	    #endregion Private Static Fields
	}
}
