niall       2005/03/11 10:21:56

  Modified:    src/Appender FileAppender.cs
               tests/src log4net.Tests.csproj
               tests/src/Appender RollingFileAppenderTest.cs
  Log:
  Implemented locking models so tat FileAppender and it's subclasses can change 
their file access semantics.
  
  Revision  Changes    Path
  1.14      +378 -16   logging-log4net/src/Appender/FileAppender.cs
  
  Index: FileAppender.cs
  ===================================================================
  RCS file: /home/cvs/logging-log4net/src/Appender/FileAppender.cs,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- FileAppender.cs   7 Mar 2005 01:34:45 -0000       1.13
  +++ FileAppender.cs   11 Mar 2005 18:21:56 -0000      1.14
  @@ -60,8 +60,268 @@
        /// <author>Gert Driesen</author>
        /// <author>Rodrigo B. de Oliveira</author>
        /// <author>Douglas de la Torre</author>
  -     public class FileAppender : TextWriterAppender
  +     /// <author>Niall Daley</author>
  +     public class FileAppender : TextWriterAppender 
        {
  +             #region Inner Classes
  +             private sealed class LockingStream : Stream, IDisposable
  +             {
  +                     public class LockStateException : Exception
  +                     {
  +                             public LockStateException(string message): 
base(message){}
  +                     }
  +
  +                     private Stream m_realStream=null;
  +                     private LockingModelBase m_lockingModel=null;
  +
  +                     #region Stream methods
  +                     // Methods
  +                     public LockingStream(LockingModelBase locking) : base()
  +                     {
  +                             if (locking==null)
  +                             {
  +                                     throw new ArgumentException("Locking 
model may not be null","locking");
  +                             }
  +                             m_lockingModel=locking;
  +                     }
  +                     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);
  +                             EndRead(ret);
  +                             return ret;
  +                     }
  +                     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_realStream.EndRead(asyncResult);}
  +                     public override void EndWrite(IAsyncResult asyncResult) 
{AssertLocked();m_realStream.EndWrite(asyncResult);}
  +                     public override void Flush() 
{AssertLocked();m_realStream.Flush();}
  +                     public override int Read(byte[] buffer, int offset, int 
count) {AssertLocked();return m_realStream.Read(buffer,offset,count);}
  +                     public override int ReadByte() {AssertLocked();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() {this.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 
{AssertLocked();return m_realStream.CanRead;} }
  +                     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
  +
  +                     #region Locking Methods
  +
  +                     private void AssertLocked()
  +                     {
  +                             if (m_realStream==null)
  +                             {
  +                                     throw new LockStateException("The file 
is not currently locked");
  +                             }
  +                     }
  +
  +                     public void AquireLock()
  +                     {
  +                             lock(this)
  +                             {
  +                                     if (m_realStream==null)
  +                                     {       // If lock is already aquired, 
nop
  +                                             
m_realStream=m_lockingModel.AquireLock();
  +                                     }
  +                             }
  +                     }
  +
  +                     public void ReleaseLock()
  +                     {
  +                             lock(this)
  +                             {
  +                                     if (m_realStream!=null)
  +                                     {       // If already unlocked, nop
  +                                             m_lockingModel.ReleaseLock();
  +                                             m_realStream=null;
  +                                     }
  +                             }
  +                     }
  +                     #endregion
  +             }
  +
  +             #region Locking Models
  +
  +             /// <summary>
  +             /// Base class for the locking models available to the <see 
cref="FileAppender"/> derived loggers
  +             /// </summary>
  +             public abstract class LockingModelBase
  +             {
  +                     private IErrorHandler m_errorHandler=null;
  +                     /// <summary>
  +                     /// Open the file specified and prepare for logging. No 
writes will be made until AquireLock is called.
  +                     /// </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>
  +                     public abstract void OpenFile(string filename, bool 
append,Encoding encoding);
  +
  +                     /// <summary>
  +                     /// Close the file. No further writes will be made.
  +                     /// </summary>
  +                     public abstract void CloseFile();
  +
  +                     /// <summary>
  +                     /// Aquire the lock on the file in preparation for 
writing to it. Return a stream pointing to the file.
  +                     /// </summary>
  +                     /// <returns>A stream that is ready to be written 
to.</returns>
  +                     public abstract Stream AquireLock();
  +
  +                     /// <summary>
  +                     /// Release the lock on the file. No further writes 
will be made to the stream until AquireLock is called again.
  +                     /// </summary>
  +                     public abstract void ReleaseLock();
  +
  +                     /// <summary>
  +                     /// Gets or sets the <see cref="IErrorHandler"/> for 
this appender.
  +                     /// </summary>
  +                     /// <value>The <see cref="IErrorHandler"/> of the 
appender</value>
  +                     /// <remarks>
  +                     /// <para>
  +                     /// The <see cref="AppenderSkeleton"/> provides a 
default 
  +                     /// implementation for the <see cref="ErrorHandler"/> 
property. 
  +                     /// </para>
  +                     /// </remarks>
  +                     virtual public IErrorHandler ErrorHandler 
  +                     {
  +                             get { return this.m_errorHandler; }
  +                             set 
  +                             {
  +                                     lock(this) 
  +                                     {
  +                                             if (value == null) 
  +                                             {
  +                                                     // We do not throw 
exception here since the cause is probably a
  +                                                     // bad config file.
  +                                                     
LogLog.Warn("AppenderSkeleton: You have tried to set a null error-handler.");
  +                                             } 
  +                                             else 
  +                                             {
  +                                                     m_errorHandler = value;
  +                                             }
  +                                     }
  +                             }
  +                     }
  +             }
  +
  +             /// <summary>
  +             /// Open te file once for writing and hold it open until 
CloseFile is called. Maintains an exclusive lock on the file during this time.
  +             /// </summary>
  +             public class ExclusiveLock : LockingModelBase
  +             {
  +                     private Stream m_stream=null;
  +
  +                     public override void OpenFile(string filename, bool 
append,Encoding encoding)
  +                     {
  +                             try
  +                             {
  +                                     // 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;
  +                                     m_stream = new FileStream(filename, 
fileOpenMode, FileAccess.Write, FileShare.Read);
  +                             }
  +                             catch (Exception e1)
  +                             {
  +                                     ErrorHandler.Error("Unable to aquire 
lock on file "+filename+". "+e1.Message);
  +                             }
  +                     }
  +
  +                     public override void CloseFile()
  +                     {
  +                             m_stream.Close();
  +                     }
  +
  +                     public override Stream AquireLock()
  +                     {
  +                             return m_stream;
  +                     }
  +
  +                     public override void ReleaseLock()
  +                     {
  +                             //NOP
  +                     }
  +             }
  +
  +             /// <summary>
  +             /// Opens the file once for each AquireLock/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.
  +             /// </summary>
  +             public class MinimalLock : LockingModelBase
  +             {
  +                     private string m_filename;
  +                     private bool m_append;
  +                     private Stream m_stream=null;
  +
  +                     public override void OpenFile(string filename, bool 
append, Encoding encoding)
  +                     {
  +                             m_filename=filename;
  +                             m_append=append;
  +                     }
  +
  +                     public override void CloseFile()
  +                     {
  +                             //NOP
  +                     }
  +
  +                     public override Stream AquireLock()
  +                     {
  +                             if (m_stream==null)
  +                             {
  +                                     try
  +                                     {
  +                                             // Ensure that the directory 
structure exists
  +                                             string directoryFullName = 
Path.GetDirectoryName(m_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 = 
m_append ? FileMode.Append : FileMode.Create;
  +                                             m_stream = new 
FileStream(m_filename, fileOpenMode, FileAccess.Write, FileShare.Read);
  +                                             m_append=true;
  +                                     }
  +                                     catch (Exception e1)
  +                                     {
  +                                             ErrorHandler.Error("Unable to 
aquire lock on file "+m_filename+". "+e1.Message);
  +                                     }
  +                             }
  +                             return m_stream;
  +                     }
  +
  +                     public override void ReleaseLock()
  +                     {
  +                             m_stream.Close();
  +                             m_stream=null;
  +                     }
  +             }
  +             #endregion
  +             #endregion
  +
                #region Public Instance Constructors
   
                /// <summary>
  @@ -192,6 +452,25 @@
                        set { m_securityContext = value; }
                }
   
  +             /// <summary>
  +             /// Gets or sets the <see cref="FileAppender.LockingModel"/> 
used to handle locking of the file. There are two
  +             /// built in locking models, <see 
cref="FileAppender.ExclusiveLock"/> and <see cref="FileAppender.MinimalLock"/>.
  +             /// The former locks the file from the start of loggin to the 
end and the later lock only for the minimal amount of time when loggin each 
message.
  +             /// </summary>
  +             /// <value>
  +             /// The <see cref="FileAppender.LockingModel"/> used to lock 
the file.
  +             /// </value>
  +             /// <remarks>
  +             /// <para>
  +             /// Unless a value is specified here the <see 
cref="FileAppender.ExclusiveLock"/> model is used.
  +             /// </para>
  +             /// </remarks>
  +             public FileAppender.LockingModelBase LockingModel
  +             {
  +                     get { return m_lockingmodel; }
  +                     set { m_lockingmodel = value;}
  +             }
  +
                #endregion Public Instance Properties
   
                #region Override implementation of AppenderSkeleton
  @@ -224,6 +503,12 @@
                                m_securityContext = 
SecurityContextProvider.DefaultProvider.CreateSecurityContext(this);
                        }
   
  +                     if (m_lockingmodel == null)
  +                     {
  +                             m_lockingmodel = new 
FileAppender.ExclusiveLock();
  +                     }
  +                     m_lockingmodel.ErrorHandler=this.ErrorHandler;
  +
                        using(SecurityContext.Impersonate(this))
                        {
                                m_fileName = 
ConvertToFullPath(m_fileName.Trim());
  @@ -272,6 +557,81 @@
                        SafeOpenFile(m_fileName, m_appendToFile);
                }
   
  +             /// <summary>
  +             /// This method is called by the <see 
cref="AppenderSkeleton.DoAppend"/>
  +             /// 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) 
  +             {
  +                     m_stream.AquireLock();
  +                     base.Append(loggingEvent);
  +                     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.AquireLock();
  +                             base.WriteFooter();
  +                             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)
  +                     {
  +                             m_stream.AquireLock();
  +                             base.WriteHeader();
  +                             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.AquireLock();
  +                             base.CloseWriter();
  +                             m_stream.ReleaseLock();
  +                     }
  +             }
  +
                #endregion Override implementation of TextWriterAppender
   
                #region Public Instance Methods
  @@ -357,27 +717,19 @@
                                // Save these for later, allowing retries if 
file open fails
                                m_fileName = fileName;
                                m_appendToFile = append;
  -                             FileStream fileStream = null;
   
                                using(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;
  -                                     fileStream = new FileStream(fileName, 
fileOpenMode, FileAccess.Write, FileShare.Read);
  +                                     
LockingModel.ErrorHandler=this.ErrorHandler;
  +                                     
LockingModel.OpenFile(fileName,append,m_encoding);
  +                                     m_stream=new 
LockingStream(LockingModel);
                                }
   
  -                             if (fileStream != null)
  +                             if (m_stream != null)
                                {
  -                                     SetQWForFiles(new 
StreamWriter(fileStream, m_encoding));
  +                                     m_stream.AquireLock();
  +                                     SetQWForFiles(new 
StreamWriter(m_stream, m_encoding));
  +                                     m_stream.ReleaseLock();
                                }
   
                                WriteHeader();
  @@ -467,6 +819,16 @@
                /// </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
        }
   }
  
  
  
  1.8       +5 -0      logging-log4net/tests/src/log4net.Tests.csproj
  
  Index: log4net.Tests.csproj
  ===================================================================
  RCS file: /home/cvs/logging-log4net/tests/src/log4net.Tests.csproj,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- log4net.Tests.csproj      19 Aug 2004 21:39:29 -0000      1.7
  +++ log4net.Tests.csproj      11 Mar 2005 18:21:56 -0000      1.8
  @@ -87,6 +87,11 @@
                       AssemblyName = "System.Runtime.Remoting"
                       HintPath = 
"..\..\..\..\..\..\WINDOWS\Microsoft.NET\Framework\v1.0.3705\System.Runtime.Remoting.dll"
                   />
  +                <Reference
  +                    Name = "nunit.tests"
  +                    AssemblyName = "nunit.tests"
  +                    HintPath = "..\..\..\..\..\..\net\NUnit 
2.0\bin\nunit.tests.dll"
  +                />
               </References>
           </Build>
           <Files>
  
  
  
  1.6       +256 -0    
logging-log4net/tests/src/Appender/RollingFileAppenderTest.cs
  
  Index: RollingFileAppenderTest.cs
  ===================================================================
  RCS file: 
/home/cvs/logging-log4net/tests/src/Appender/RollingFileAppenderTest.cs,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- RollingFileAppenderTest.cs        17 Jan 2005 21:40:52 -0000      1.5
  +++ RollingFileAppenderTest.cs        11 Mar 2005 18:21:56 -0000      1.6
  @@ -25,6 +25,7 @@
   using log4net.Util;
   using log4net.Layout;
   using log4net.Core;
  +using log4net.Config;
   
   using NUnit.Framework;
   using log4net.Repository;
  @@ -48,6 +49,19 @@
                CountingAppender _caRoot;
                Logger _root;
   
  +             private class SilentErrorHandler : IErrorHandler
  +             {
  +                     System.Text.StringBuilder m_buffer=new 
System.Text.StringBuilder();
  +
  +                     public string Message
  +                     {
  +                             get {return m_buffer.ToString();}
  +                     }
  +
  +                     public void Error(string message)                       
                                                {m_buffer.Append(message+"\n");}
  +                     public void Error(string message, Exception e)          
                                {m_buffer.Append(message+"\n"+e.Message+"\n");}
  +                     public void Error(string message, Exception e, 
ErrorCode errorCode)     {m_buffer.Append(message+"\n"+e.Message+"\n");}
  +             }
                /// <summary>
                /// Sets up variables used for the tests
                /// </summary>
  @@ -196,6 +210,17 @@
                /// <returns></returns>
                private RollingFileAppender CreateAppender()
                {
  +                     return CreateAppender(new FileAppender.ExclusiveLock());
  +             }
  +
  +             /// <summary>
  +             /// Returns a RollingFileAppender using all the internal 
settings for maximum
  +             /// file size and number of backups
  +             /// </summary>
  +             /// <param name="lockmodel">The locking model to test</param>
  +             /// <returns></returns>
  +             private RollingFileAppender 
CreateAppender(FileAppender.LockingModelBase lockmodel)
  +             {
                        //
                        // Use a basic pattern that
                        // includes just the message and a CR/LF.
  @@ -212,6 +237,8 @@
                        appender.MaxSizeRollBackups  = _MaxSizeRollBackups;
                        appender.CountDirection   = _iCountDirection;
                        appender.RollingStyle           = 
RollingFileAppender.RollingMode.Size;
  +                     appender.LockingModel           =lockmodel;
  +                     
                        appender.ActivateOptions();
   
                        return appender;
  @@ -1307,8 +1334,237 @@
                        VerifyInitializeUpInfiniteExpectedValue( alFiles, 
_fileName, 10 );
                }
   
  +             /// <summary>
  +             /// Creates a logger hierarchy, configures a rolling file 
appender and returns an ILogger
  +             /// </summary>
  +             /// <param name="filename">The filename to log to</param>
  +             /// <param name="lockmodel">The locking model to use.</param>
  +             /// <param name="handler">The error handler to use.</param>
  +             /// <returns>A configured ILogger</returns>
  +             private ILogger CreateLogger(string 
filename,FileAppender.LockingModelBase lockmodel, IErrorHandler handler)
  +             {
  +                     log4net.Repository.Hierarchy.Hierarchy h = 
(log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.CreateRepository("TestRepository");
  +
  +                     log4net.Appender.RollingFileAppender appender = new 
log4net.Appender.RollingFileAppender();
  +                     appender.File = filename;
  +                     appender.AppendToFile = false;
  +                     appender.CountDirection=0;
  +                     
appender.RollingStyle=RollingFileAppender.RollingMode.Size;
  +                     appender.MaxFileSize=100000;
  +                     appender.Encoding=System.Text.Encoding.ASCII;
  +                     appender.ErrorHandler=handler;
  +                     if (lockmodel!=null) {appender.LockingModel=lockmodel;}
  +                     
  +                     log4net.Layout.PatternLayout layout = new 
log4net.Layout.PatternLayout();
  +                     layout.ConversionPattern = "%m%n";
  +                     layout.ActivateOptions();
  +
  +                     appender.Layout = layout;
  +                     appender.ActivateOptions();
  +
  +                     h.Root.AddAppender(appender);
  +                     h.Configured = true;
  +
  +                     ILogger log=h.GetLogger("Logger");
  +                     return log;
  +             }
  +
  +             /// <summary>
  +             /// Destroys the logger hierarchy created by <see 
cref="RollingFileAppenderTests.CreateLogger"/>
  +             /// </summary>
  +             private void DestroyLogger()
  +             {
  +                     log4net.Repository.Hierarchy.Hierarchy h = 
(log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository("TestRepository");
  +                     h.ResetConfiguration();
  +                     //Replace the repository selector so that we can 
recreate the hierarchy with the same name if necesary
  +                     LoggerManager.RepositorySelector=new 
DefaultRepositorySelector(log4net.Util.SystemInfo.GetTypeFromString("log4net.Repository.Hierarchy.Hierarchy",true,true));
  +             }
  +
  +             private void AssertFileEquals(string filename, string contents)
  +             {
  +                     StreamReader sr=new StreamReader(filename);
  +                     string logcont=sr.ReadToEnd();
  +                     sr.Close();
  +
  +                     Assertion.AssertEquals("Log contents is not what is 
expected",contents,logcont);
  +
  +                     System.IO.File.Delete(filename);
  +             }
  +
  +             /// <summary>
  +             /// Verifies that logging a messsage actually produces output
  +             /// </summary>
  +             [Test] public void TestLogOutput()
  +             {
  +                     String filename="test.log";
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.ExclusiveLock(), sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +
  +                     AssertFileEquals(filename,"This is a 
message"+Environment.NewLine+"This is a message 2"+Environment.NewLine);
  +                     Assertion.AssertEquals("Unexpeced error 
message","",sh.Message);
  +             }
  +
  +             /// <summary>
  +             /// Verifies that attempting to log to a locked file fails 
gracefully
  +             /// </summary>
  +             [Test] public void TestExclusiveLockFails()
  +             {
  +                     String filename="test.log";
  +
  +                     FileStream fs=new 
FileStream(filename,FileMode.Create,FileAccess.Write,FileShare.None);
  +                     
fs.Write(System.Text.Encoding.ASCII.GetBytes("Test"),0,4);
  +
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.ExclusiveLock(),sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +                     fs.Close();
  +
  +                     AssertFileEquals(filename,"Test");
  +                     Assertion.AssertEquals("Expecting an error 
message","Unable to aquire lock on file",sh.Message.Substring(0,29));
  +             }
  +
  +             /// <summary>
  +             /// Verifies that attempting to log to a locked file recovers 
if the lock is released
  +             /// </summary>
  +             [Test] public void TestExclusiveLockRecovers()
  +             {
  +                     String filename="test.log";
   
  +                     FileStream fs=new 
FileStream(filename,FileMode.Create,FileAccess.Write,FileShare.None);
  +                     
fs.Write(System.Text.Encoding.ASCII.GetBytes("Test"),0,4);
   
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.ExclusiveLock(),sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +                     fs.Close();
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +
  +                     AssertFileEquals(filename,"This is a message 
2"+Environment.NewLine);
  +                     Assertion.AssertEquals("Expecting an error 
message","Unable to aquire lock on file",sh.Message.Substring(0,29));
  +             }
  +
  +             /// <summary>
  +             /// Verifies that attempting to log to a file with 
ExclusiveLock really locks the file
  +             /// </summary>
  +             [Test] public void TestExclusiveLockLocks()
  +             {
  +                     String filename="test.log";
  +                     bool locked=false;
  +
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.ExclusiveLock(),sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +
  +                     try
  +                     {
  +                             FileStream fs=new 
FileStream(filename,FileMode.Create,FileAccess.Write,FileShare.None);
  +                             
fs.Write(System.Text.Encoding.ASCII.GetBytes("Test"),0,4);
  +                             fs.Close();
  +                     }
  +                     catch (System.IO.IOException e1)
  +                     {
  +                             Assertion.AssertEquals("Unexpected 
exception","The process cannot access the file ",e1.Message.Substring(0,35));
  +                             locked=true;
  +                     }
  +
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +
  +                     Assertion.Assert("File was not locked",locked);
  +                     AssertFileEquals(filename,"This is a 
message"+Environment.NewLine+"This is a message 2"+Environment.NewLine);
  +                     Assertion.AssertEquals("Unexpected error 
message","",sh.Message);
  +             }
  +
  +
  +             /// <summary>
  +             /// Verifies that attempting to log to a locked file fails 
gracefully
  +             /// </summary>
  +             [Test] public void TestMinimalLockFails()
  +             {
  +                     String filename="test.log";
  +
  +                     FileStream fs=new 
FileStream(filename,FileMode.Create,FileAccess.Write,FileShare.None);
  +                     
fs.Write(System.Text.Encoding.ASCII.GetBytes("Test"),0,4);
  +
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.MinimalLock(),sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +                     fs.Close();
  +
  +                     AssertFileEquals(filename,"Test");
  +                     Assertion.AssertEquals("Expecting an error 
message","Unable to aquire lock on file",sh.Message.Substring(0,29));
  +             }
  +
  +             /// <summary>
  +             /// Verifies that attempting to log to a locked file recovers 
if the lock is released
  +             /// </summary>
  +             [Test] public void TestMinimalLockRecovers()
  +             {
  +                     String filename="test.log";
  +
  +                     FileStream fs=new 
FileStream(filename,FileMode.Create,FileAccess.Write,FileShare.None);
  +                     
fs.Write(System.Text.Encoding.ASCII.GetBytes("Test"),0,4);
  +
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.MinimalLock(),sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +                     fs.Close();
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +
  +                     AssertFileEquals(filename,"This is a message 
2"+Environment.NewLine);
  +                     Assertion.AssertEquals("Expecting an error 
message","Unable to aquire lock on file",sh.Message.Substring(0,29));
  +             }
  +
  +             /// <summary>
  +             /// Verifies that attempting to log to a file with 
ExclusiveLock really locks the file
  +             /// </summary>
  +             [Test] public void TestMinimalLockUnlocks()
  +             {
  +                     String filename="test.log";
  +                     bool locked=false;
  +
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,new 
FileAppender.MinimalLock(),sh);
  +                     log.Log(this.GetType(),Level.Info,"This is a 
message",null);
  +
  +                     locked=true;
  +                     FileStream fs=new 
FileStream(filename,FileMode.Append,FileAccess.Write,FileShare.None);
  +                     
fs.Write(System.Text.Encoding.ASCII.GetBytes("Test"+Environment.NewLine),0,4+Environment.NewLine.Length);
  +                     fs.Close();
  +
  +                     log.Log(this.GetType(),Level.Info,"This is a message 
2",null);
  +                     DestroyLogger();
  +
  +                     Assertion.Assert("File was not locked",locked);
  +                     AssertFileEquals(filename,"This is a 
message"+Environment.NewLine+"Test"+Environment.NewLine+"This is a message 
2"+Environment.NewLine);
  +                     Assertion.AssertEquals("Unexpected error 
message","",sh.Message);
  +             }
  +
  +             /// <summary>
  +             /// Verify that the default LockModel is ExclusiveLock, to 
maintain backwards compatability with previous behaviour
  +             /// </summary>
  +             [Test] public void TestDefaultLockingModel()
  +             {
  +                     String filename="test.log";
  +                     SilentErrorHandler sh=new SilentErrorHandler();
  +                     ILogger log=CreateLogger(filename,null,sh);
  +
  +                     IAppender[] appenders=log.Repository.GetAppenders();
  +                     Assertion.AssertEquals("The wrong number of appenders 
are configured",1,appenders.Length);
  +
  +                     RollingFileAppender 
rfa=(RollingFileAppender)(appenders[0]);
  +                     Assertion.AssertEquals("The LockingModel is of an 
unexpected 
type",log4net.Util.SystemInfo.GetTypeFromString("log4net.Appender.FileAppender+ExclusiveLock",true,true),rfa.LockingModel.GetType());
  +             }
  +             
                /// <summary>
                /// Tests the count up case, with infinite max backups , to see 
that
                /// initialization of the rolling file appender results in the 
expected value
  
  
  

Reply via email to