// // CommunicationObject.cs // // Author: // Atsushi Enomoto // // Copyright (C) 2005 Novell, Inc. http://www.novell.com // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Reflection; using System.ServiceModel; using System.Threading; namespace System.ServiceModel.Channels { public abstract class CommunicationObject : ICommunicationObject { object mutex; CommunicationState state = CommunicationState.Created; TimeSpan default_open_timeout = TimeSpan.FromMinutes (1), default_close_timeout = TimeSpan.FromMinutes (1); bool aborted; protected CommunicationObject () : this (new object ()) { } protected CommunicationObject (object mutex) { this.mutex = mutex; } #region Events public event EventHandler Closed; public event EventHandler Closing; public event EventHandler Faulted; public event EventHandler Opened; public event EventHandler Opening; #endregion #region Properties public CommunicationState State { get { return state; } } protected bool IsDisposed { get { return state == CommunicationState.Closed; } } protected object ThisLock { get { return mutex; } } protected internal abstract TimeSpan DefaultCloseTimeout { get; } protected internal abstract TimeSpan DefaultOpenTimeout { get; } #endregion #region Methods public void Abort () { if (State != CommunicationState.Closed) { OnAbort (); ProcessClosed (); } } protected void Fault () { ProcessFaulted (); } public IAsyncResult BeginClose (AsyncCallback callback, object state) { return BeginClose (default_close_timeout, callback, state); } public IAsyncResult BeginClose (TimeSpan timeout, AsyncCallback callback, object state) { if (State == CommunicationState.Created) return new EventHandler (delegate { Abort (); }).BeginInvoke (null, null, callback, state); ProcessClosing (); return OnBeginClose (timeout, callback, state); } public IAsyncResult BeginOpen (AsyncCallback callback, object state) { return BeginOpen (default_open_timeout, callback, state); } public IAsyncResult BeginOpen (TimeSpan timeout, AsyncCallback callback, object state) { ProcessOpening (); return OnBeginOpen (timeout, callback, state); } public void Close () { Close (default_close_timeout); } public void Close (TimeSpan timeout) { if (State == CommunicationState.Created) Abort (); else { ProcessClosing (); OnClose (timeout); ProcessClosed (); } } public void EndClose (IAsyncResult result) { if (State == CommunicationState.Created || State == CommunicationState.Closed) { if (!result.IsCompleted) result.AsyncWaitHandle.WaitOne (); } else { OnEndClose (result); ProcessClosed (); } } public void EndOpen (IAsyncResult result) { OnEndOpen (result); ProcessOpened (); } public void Open () { Open (default_open_timeout); } public void Open (TimeSpan timeout) { ProcessOpening (); OnOpen (timeout); ProcessOpened (); } protected abstract void OnAbort (); protected abstract IAsyncResult OnBeginClose (TimeSpan timeout, AsyncCallback callback, object state); protected abstract IAsyncResult OnBeginOpen (TimeSpan timeout, AsyncCallback callback, object state); protected abstract void OnClose (TimeSpan timeout); void ProcessClosing () { lock (ThisLock) { if (State == CommunicationState.Faulted) throw new CommunicationObjectFaultedException (); OnClosing (); if (state != CommunicationState.Closing) { state = CommunicationState.Faulted; throw new InvalidOperationException (String.Format ("Communication object {0} has an overriden OnClosing method that does not call base OnClosing method (declared in {1} type).", this.GetType (), GetType ().GetMethod ("OnClosing", BindingFlags.NonPublic | BindingFlags.Instance).DeclaringType)); } } } protected virtual void OnClosing () { state = CommunicationState.Closing; // This means, if this method is overriden, then // Opening event is surpressed. if (Closing != null) Closing (this, new EventArgs ()); } void ProcessClosed () { lock (ThisLock) { OnClosed (); if (state != CommunicationState.Closed) { state = CommunicationState.Faulted; throw new InvalidOperationException (String.Format ("Communication object {0} has an overriden OnClosed method that does not call base OnClosed method (declared in {1} type).", this.GetType (), GetType ().GetMethod ("OnClosed", BindingFlags.NonPublic | BindingFlags.Instance).DeclaringType)); } } } protected virtual void OnClosed () { state = CommunicationState.Closed; // This means, if this method is overriden, then // Closed event is surpressed. if (Closed != null) Closed (this, new EventArgs ()); } protected abstract void OnEndClose (IAsyncResult result); protected abstract void OnEndOpen (IAsyncResult result); void ProcessFaulted () { lock (ThisLock) { if (State == CommunicationState.Faulted) throw new CommunicationObjectFaultedException (); OnFaulted (); if (state != CommunicationState.Faulted) { state = CommunicationState.Faulted; // FIXME: am not sure if this makes sense ... throw new InvalidOperationException (String.Format ("Communication object {0} has an overriden OnFaulted method that does not call base OnFaulted method (declared in {1} type).", this.GetType (), GetType ().GetMethod ("OnFaulted", BindingFlags.NonPublic | BindingFlags.Instance).DeclaringType)); } } } protected virtual void OnFaulted () { state = CommunicationState.Faulted; // This means, if this method is overriden, then // Faulted event is surpressed. if (Faulted != null) Faulted (this, new EventArgs ()); } protected abstract void OnOpen (TimeSpan timeout); void ProcessOpened () { lock (ThisLock) { OnOpened (); if (state != CommunicationState.Opened) { state = CommunicationState.Faulted; throw new InvalidOperationException (String.Format ("Communication object {0} has an overriden OnOpened method that does not call base OnOpened method (declared in {1} type).", this.GetType (), GetType ().GetMethod ("OnOpened", BindingFlags.NonPublic | BindingFlags.Instance).DeclaringType)); } } } protected virtual void OnOpened () { state = CommunicationState.Opened; if (Opened != null) Opened (this, new EventArgs ()); } void ProcessOpening () { lock (ThisLock) { ThrowIfDisposedOrImmutable (); OnOpening (); if (state != CommunicationState.Opening) { state = CommunicationState.Faulted; throw new InvalidOperationException (String.Format ("Communication object {0} has an overriden OnOpening method that does not call base OnOpening method (declared in {1} type).", this.GetType (), GetType ().GetMethod ("OnOpening", BindingFlags.NonPublic | BindingFlags.Instance).DeclaringType)); } } } protected virtual void OnOpening () { state = CommunicationState.Opening; // This means, if this method is overriden, then // Opening event is surpressed. if (Opening != null) Opening (this, new EventArgs ()); } protected void ThrowIfDisposed () { if (IsDisposed) throw new ObjectDisposedException (String.Format ("This communication object {0} is already disposed.", GetCommunicationObjectType ())); } protected void ThrowIfDisposedOrNotOpen () { ThrowIfDisposed (); if (State == CommunicationState.Faulted) throw new CommunicationObjectFaultedException (); if (State != CommunicationState.Opened) throw new InvalidOperationException (String.Format ("The communication object {0} must be at opened state.", GetCommunicationObjectType ())); } protected void ThrowIfDisposedOrImmutable () { ThrowIfDisposed (); // hmm, according to msdn, Closing is OK here. switch (State) { case CommunicationState.Faulted: throw new CommunicationObjectFaultedException (); case CommunicationState.Opening: case CommunicationState.Opened: throw new InvalidOperationException (String.Format ("The communication object {0} is not at created state but at {1} state.", GetType (), State)); } } protected virtual Type GetCommunicationObjectType () { return GetType (); } #endregion class SimpleAsyncResult : IAsyncResult { CommunicationState comm_state; object async_state; public SimpleAsyncResult ( CommunicationState communicationState, TimeSpan timeout, AsyncCallback callback, object asyncState) { comm_state = communicationState; async_state = asyncState; } public object AsyncState { get { return async_state; } } // FIXME: implement public WaitHandle AsyncWaitHandle { get { throw new NotImplementedException (); } } // FIXME: implement public bool CompletedSynchronously { get { throw new NotImplementedException (); } } // FIXME: implement public bool IsCompleted { get { throw new NotImplementedException (); } } } } }