- wcpiercepracticesenterpriselibrary.zip
- Binaries
- Microsoft.Practices.EnterpriseLibrary.Common.dll
- Microsoft.Practices.EnterpriseLibrary.Configuration.Design.dll
- Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.Design.dll
- Microsoft.Practices.EnterpriseLibrary.Logging.dll
- Microsoft.Practices.ObjectBuilder.dll
- XihSolutions.DotMSN.dll
- CodeProject.msbuild
- Logging.MsnMessenger.Configuration.Design
- Logging.MsnMessenger.Console
- Logging.MsnMessenger
- WCPierce.Practices.EnterpriseLibrary.sln
|
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is WCPierce MSN Messenger Log Listener
*
* The Initial Developer of the Original Code is William C. Pierce.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using WCPierce.Practices.EnterpriseLibrary.Logging.MsnMessenger.Configuration;
using XihSolutions.DotMSN;
using XihSolutions.DotMSN.Core;
namespace WCPierce.Practices.EnterpriseLibrary.Logging.MsnMessenger
{
/// <summary>
/// This class sends Logging messages to an MSN Messenger account. This class
/// does not guarentee delivery of Log messages so it is recommend to use this
/// Log Listener in conjuction with another Listener e.g. Flat File, Email,
/// Database, etc. The MSN Messenger Listener queues all messages recevied
/// until a connection is established with the MSN server and a conversation is
/// created with one or more recipients.
/// </summary>
[ConfigurationElementType(typeof(FormattedMsnMessengerTraceListenerData))]
public class FormattedMsnMessengerTraceListener : FormattedTraceListenerBase
{
#region Private Fields
// Values used for custom MSN Messenger clients
static readonly string CLIENT_ID = "msmsgs@msnmsgr.com";
static readonly string CLIENT_CODE = "Q1P7W2E4J9R8U3S5";
// Holds the list of Log recipients
string[] _sReceiverAccounts = null;
// The actual Messenger object
Messenger _messenger = null;
// The Conversation used to send Log Entries
Conversation _conversation = null;
// A Queue container for First-In First-Out messages
Queue<string> _messageQueue = null;
int _iMaxMessageQueue = 0;
// Holds any exceptions thrown by the Messenger
Exception _thrownException = null;
object _oExceptionLock = new object();
// True if we are in the process of connecting and signing in to the MSN network
bool _bConnecting = false;
object _oConnectingLock = new object();
// True if we are in the process of inviting recipients to the Conversation
bool _bInviting = false;
object _oInvitingLock = new object();
// The Date and Time of the last Connection attempt
DateTime _dtLastConnectionAttempt = DateTime.MinValue;
// The Date and Time of the last Invitation attempt
DateTime _dtLastInvitationAttempt = DateTime.MinValue;
// The time in Minutes between Connection attempts
TimeSpan _tsConnectionRetryInterval = TimeSpan.MaxValue;
// The time in Minute between Invitation attempts
TimeSpan _tsInvitationRetryInterval = TimeSpan.MaxValue;
#endregion // Private Fields
#region Constructor
/// <summary>
/// Initializes the Listener and begins the process to establish a connection
/// with the MSN server.
/// </summary>
/// <param name="sSenderAccount">The account used to send Log messages</param>
/// <param name="sSenderPassword">The password for sSenderAccount</param>
/// <param name="sReceiverAccounts">A semi-colon (;) delimited list of accounts
/// that will receive Log messages</param>
/// <param name="iConnectionRetryInterval">The time in Minutes between Connection attempts</param>
/// <param name="iInvitationRetryInterval">The time in Minutes between Invitation attempts</param>
/// <param name="iMaxMessageQueue">The maximum number of messages to queue for
/// deliver. If this value is exceeded, the oldest message is removed from the
/// queue before adding the latest message</param>
/// <param name="Formatter"></param>
public FormattedMsnMessengerTraceListener(string sSenderAccount, string sSenderPassword, string sReceiverAccounts, int iConnectionRetryInterval, int iInvitationRetryInterval, int iMaxMessageQueue, ILogFormatter Formatter)
: base(Formatter)
{
// Initialize Private Fields
_sReceiverAccounts = sReceiverAccounts.Split(';');
_tsConnectionRetryInterval = TimeSpan.FromMinutes(iConnectionRetryInterval);
_tsInvitationRetryInterval = TimeSpan.FromMinutes(iInvitationRetryInterval);
// Initialize the Message queue
_iMaxMessageQueue = iMaxMessageQueue;
_messageQueue = new Queue<string>(_iMaxMessageQueue);
// Initialize the Messenger
_messenger = new Messenger();
_messenger.Credentials.ClientID = CLIENT_ID;
_messenger.Credentials.ClientCode = CLIENT_CODE;
_messenger.Credentials.Account = sSenderAccount;
_messenger.Credentials.Password = sSenderPassword;
// Attach events
_messenger.Nameserver.SignedIn += new EventHandler(Nameserver_SignedIn);
_messenger.Nameserver.ExceptionOccurred += new HandlerExceptionEventHandler(Nameserver_ExceptionOccurred);
_messenger.NameserverProcessor.ConnectionException += new ProcessorExceptionEventHandler(NameserverProcessor_ConnectionException);
_messenger.NameserverProcessor.ConnectingException += new ProcessorExceptionEventHandler(NameserverProcessor_ConnectingException);
// Start the first Connection
_bConnecting = true;
_dtLastConnectionAttempt = DateTime.Now;
_messenger.Connect();
}
#endregion // Constructor
#region Overrides
/// <summary>
/// Queues the Message then attempts to send it if a connection is
/// established and a conversation has been created and joined.
/// </summary>
/// <param name="sMessage">The Log Message to send</param>
public override void Write(string sMessage)
{
// Queue the message
if (String.Empty != sMessage)
{
// If we have filled the queue, remove the oldest message before adding
// the new message
if (_iMaxMessageQueue <= _messageQueue.Count)
{
_messageQueue.Dequeue();
}
_messageQueue.Enqueue(sMessage);
}
// If an Exception was raised as the result of an event, throw it now
if (null != _thrownException)
{
lock (_oExceptionLock)
{
if (null != _thrownException)
{
try
{
throw _thrownException;
}
finally
{
_thrownException = null;
}
}
}
}
// Make sure we have a Connection and a Conversation before sending messages
if (true == _EnsureConnection() && true == _EnsureConversation())
{
// Send any messages that may have been queued while a connection was being established
while (0 < _messageQueue.Count)
{
TextMessage message = new TextMessage(_messageQueue.Dequeue());
_conversation.Switchboard.SendTextMessage(message);
}
}
}
/// <summary>
/// Write and WriteLine are the same to our Listener
/// </summary>
/// <param name="sMessage"></param>
public override void WriteLine(string sMessage)
{
Write(sMessage);
}
/// <summary>
/// Perform instrumentation before Writing a LogEntry
/// </summary>
/// <param name="EventCache"></param>
/// <param name="sSource"></param>
/// <param name="EventType"></param>
/// <param name="iId"></param>
/// <param name="oData"></param>
public override void TraceData(TraceEventCache EventCache, string sSource, TraceEventType EventType, int iId, object oData)
{
if (oData is LogEntry)
{
LogEntry le = oData as LogEntry;
Write(Formatter.Format(le));
InstrumentationProvider.FireTraceListenerEntryWrittenEvent();
}
else if (oData is string)
{
Write(oData as string);
}
else
{
base.TraceData(EventCache, sSource, EventType, iId, oData);
}
}
/// <summary>
/// Valid configuration keys
/// </summary>
/// <returns></returns>
protected override string[] GetSupportedAttributes()
{
return new string[7] { "formatter", "senderAccount", "senderPassword", "receiverAccounts", "connectionRetryInterval", "invitationRetryInterval", "maxMessageQueue" };
}
/// <summary>
/// Not sure if this is really necessary but null all objects and detach all events
/// </summary>
public override void Close()
{
_messageQueue = null;
// Kill the Conversation
if (null != _conversation)
{
if (null != _conversation.Switchboard)
{
_conversation.Switchboard.ContactJoined -= new ContactChangedEventHandler(Switchboard_ContactJoined);
}
_conversation = null;
}
// Kill the Messenger
if (null != _messenger)
{
if (true == _messenger.Connected)
{
_messenger.Disconnect();
}
_messenger.Nameserver.SignedIn -= new EventHandler(Nameserver_SignedIn);
_messenger.Nameserver.ExceptionOccurred -= new HandlerExceptionEventHandler(Nameserver_ExceptionOccurred);
_messenger.NameserverProcessor.ConnectionException -= new ProcessorExceptionEventHandler(NameserverProcessor_ConnectionException);
_messenger.NameserverProcessor.ConnectingException -= new ProcessorExceptionEventHandler(NameserverProcessor_ConnectingException);
_messenger = null;
}
base.Close();
}
/// <summary>
/// If we are really disposing then call Close
/// </summary>
/// <param name="bDisposing"></param>
protected override void Dispose(bool bDisposing)
{
if (bDisposing)
{
this.Close();
}
base.Dispose(bDisposing);
}
#endregion // Overrides
#region Private Methods
/// <summary>
/// Synchronized error handling
/// </summary>
/// <param name="Ex"></param>
void _HandleException(Exception Ex)
{
if (null == _thrownException)
{
lock (_oExceptionLock)
{
if (null == _thrownException)
{
_thrownException = Ex;
}
}
}
}
/// <summary>
/// Returns true if we have a valid Connection. If we do not have a valid
/// connection and we are not in the middle of trying to establish a
/// connection, we begin establishing a connection. If it has been a while
/// since our last connection attempt, we try again. The allows the Listener
/// to handle situations where receipients go offline then come back online
/// at a later date/time.
/// </summary>
/// <returns></returns>
bool _EnsureConnection()
{
// If we are Connected and SignedIn we are good to go
if (true == _messenger.Connected && true == _messenger.Nameserver.IsSignedIn )
{
return true;
}
// If we aren't currently attempting to connect
if (false == _bConnecting)
{
lock (_oConnectingLock)
{
if (false == _bConnecting)
{
// Initialize a connection
_messenger.Disconnect();
_bConnecting = true;
_dtLastConnectionAttempt = DateTime.Now;
_messenger.Connect();
}
}
}
// If we are trying to connect but haven't had any luck for a while we try again
else if( _tsConnectionRetryInterval <= (DateTime.Now - _dtLastConnectionAttempt) )
{
lock (_oConnectingLock)
{
_bConnecting = false;
}
}
return false;
}
/// <summary>
/// Returns true if we have a valid Conversation. If we do not have a valid
/// conversation and we are not in the middle of trying to establish a
/// conversation, we begin establishing a conversation. If it has been a
/// while since our last conversation attempt, we try again. This allows
/// the Listener to handle situations where receipients go offline then come back
/// online at a later date/time.
/// </summary>
/// <returns></returns>
bool _EnsureConversation()
{
if (null != _conversation && 0 < _conversation.Switchboard.Contacts.Count)
{
return true;
}
if (false == _bInviting)
{
lock (_oInvitingLock)
{
if (false == _bInviting)
{
_bInviting = true;
_dtLastInvitationAttempt = DateTime.Now;
foreach (string receiver in _sReceiverAccounts)
{
_conversation.Invite(receiver);
}
}
}
}
// If we are trying to converse but haven't had any luck for a while we try again
else if ( _tsInvitationRetryInterval <= (DateTime.Now - _dtLastInvitationAttempt) )
{
lock (_oInvitingLock)
{
_bInviting = false;
}
}
return false;
}
#endregion // Private Methods
#region Messenger Events
/// <summary>
/// Takes any exeception and stores it until it can be raised to the Logging framework
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void NameserverProcessor_ConnectingException(object sender, ExceptionEventArgs e)
{
_HandleException(e.Exception);
}
/// <summary>
/// Takes any exeception and stores it until it can be raised to the Logging framework
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void NameserverProcessor_ConnectionException(object sender, ExceptionEventArgs e)
{
_HandleException(e.Exception);
}
/// <summary>
/// Takes any exeception and stores it until it can be raised to the Logging framework
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Nameserver_ExceptionOccurred(object sender, ExceptionEventArgs e)
{
_HandleException(e.Exception);
}
/// <summary>
/// After a Connection is established, we must SignIn. When we SignIn successfully
/// we proceed to create a conversation and wait for receipients to join
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Nameserver_SignedIn(object sender, EventArgs e)
{
// Show use as online
_messenger.Owner.Status = PresenceStatus.Online;
if (null == _conversation)
{
// Create a new conversation
_conversation = _messenger.CreateConversation();
// We can't send a message until at least one Contact has joined
_conversation.Switchboard.ContactJoined += new ContactChangedEventHandler(Switchboard_ContactJoined);
}
// Signal that we are no longer attempting to connect
if (true == _bConnecting)
{
lock (_oConnectingLock)
{
if (true == _bConnecting)
{
_bConnecting = false;
}
}
}
}
/// <summary>
/// When a recipient joins, we signal that we are no longer in the process of
/// inviting recipients
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void Switchboard_ContactJoined(object sender, ContactEventArgs e)
{
if (true == _bInviting)
{
lock (_oInvitingLock)
{
if (true == _bInviting)
{
_bInviting = false;
}
}
}
}
#endregion // Messenger Events
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.