6. Sponsorship Management
The sponsor in Example 2
is a completely general-purpose sponsor that simply returns the initial
lease time (whatever that may be) on every renewal request. The problem
with the code shown in Example 2
is that the client has to keep track of its remote objects and manually
unregister each remote lease's sponsor when the application shuts down.
However, you can automate this by providing a client-side sponsorship
manager, in the form of the SponsorshipManager helper class:
public class SponsorshipManager : MarshalByRefObject,
ISponsor,
IDisposable,
IEnumerable<ILease>
{
public void Dispose( );
public TimeSpan Renewal(ILease lease);
public IEnumerator<ILease> GetEnumerator( );
public void Register(MarshalByRefObject obj);
public void Unregister(MarshalByRefObject obj);
public void UnregisterAll( );
public void OnExit(object sender,EventArgs e);
}
Example 3 shows the implementation of SponsorshipManager.
Example 3. The SponsorshipManager helper class
public class SponsorshipManager : MarshalByRefObject,
ISponsor,
IDisposable,
IEnumerable<ILease>
{
IList<ILease> m_LeaseList = new List<ILease>( );
~SponsorshipManager( )
{
UnregisterAll( );
}
void IDisposable.Dispose( )
{
UnregisterAll( );
}
TimeSpan ISponsor.Renewal(ILease lease)
{
Debug.Assert(lease.CurrentState == LeaseState.Active);
return lease.InitialLeaseTime;
}
IEnumerator<ILease> IEnumerable<ILease>.GetEnumerator( )
{
foreach(ILease lease in m_LeaseList)
{
yield return lease;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
IEnumerable<ILease> enumerable = this;
return enumerable.GetEnumerator()
}
public void OnExit(object sender,EventArgs e)
{
UnregisterAll( );
}
public void Register(MarshalByRefObject obj)
{
ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);
Debug.Assert(lease.CurrentState == LeaseState.Active);
lease.Register(this);
lock(this)
{
m_LeaseList.Add(lease);
}
}
public void Unregister(MarshalByRefObject obj)
{
ILease lease = (ILease)RemotingServices.GetLifetimeService(obj);
Debug.Assert(lease.CurrentState == LeaseState.Active);
lease.Unregister(this);
lock(this)
{
m_LeaseList.Remove(lease);
}
}
public void UnregisterAll( )
{
lock(this)
{
while(m_LeaseList.Count>0)
{
ILease lease = m_LeaseList[0];
lease.Unregister(this);
m_LeaseList.RemoveAt(0);
}
}
}
}
|
.NET provides a class for sponsorship management called ClientSponsor, defined as:
public class ClientSponsor : MarshalByRefObject,ISponsor
{
public ClientSponsor( );
public ClientSponsor(TimeSpan renewalTime);
public TimeSpan RenewalTime{get;set;}
public void Close( );
public bool Register(MarshalByRefObject obj);
public virtual TimeSpan Renewal(ILease lease);
public void Unregister(MarshalByRefObject obj);
}
ClientSponsor is similar in its design and use to SponsorshipManager, but unlike with SponsorshipManager, which returns the initial lease timeout for each lease, with ClientSponsor
the client must explicitly set a single fixed lease renewal timeout for
all sponsored leases. Because of this behavior, I recommend that you
use SponsorshipManager instead of ClientSponsor. |
SponsorshipManager implements ISponsor by returning the initial lease time of the provided lease. In addition, it maintains in a member variable called m_LeaseList a generic linked list of all the leases it sponsors. SponsorshipManager provides the Register( ) method:
public void Register(MarshalByRefObject obj);
Register( ) accepts a remote object, extracts the lease from it, registers SponsorshipManager as the sponsor, and adds the lease to the internal list. The Unregister( ) method of the SponsorshipManager class is defined as:
public void Unregister(MarshalByRefObject obj);
This method removes the lease associated with the specified object from the list and unregisters SponsorshipManager as a sponsor. SponsorshipManager also provides the UnregisterAll( ) method:
public void UnregisterAll( );
This method unregisters SponsorshipManager from all the lease objects in the list. Note that the list access is done in a thread-safe manner by locking the SponsorshipManager on every access. Thread safety is required because multiple clients on multiple threads can use the same SponsorshipManager instance to manage their remote leases. However, the clients should not bother themselves with calling Unregister( ) or UnregisterAll( )—SponsorshipManager provides an event-handling method called OnExit( ):
public void OnExit(object sender,EventArgs e);
OnExit( ) calls UnregisterAll( ) as a response to the client application shutting down. Example 4 shows how to use SponsorshipManager by a Windows Forms client.
Example 4. Using SponsorshipManager
partial class ClientForm : Form
{
Button m_CallButton;
SponsorshipManager m_SponsorshipManager;
MyCAO m_MyCAO; //The remote CAO
public ClientForm( )
{
InitializeComponent( );
m_SponsorshipManager = new SponsorshipManager( );
m_MyCAO = new MyCAO( );
m_SponsorshipManager.Register(m_MyCAO);
Application.ApplicationExit += m_SponsorshipManager.OnExit;
}
void InitializeComponent( )
{...}
static void Main( )
{
RemotingConfigurationEx.Configure( );
Application.Run(new ClientForm( ));
}
void OnCall(object sender,EventArgs e)
{
m_MyCAO.Count( );
}
}
|
The client in Example 4 maintains as member variables a remote object of type MyCAO (defined in Example 10-11) and a SponsorshipManager object. The client registers the remote object with SponsorshipManager and hooks up the application OnExit event with the SponsorshipManager
OnExit( ) event-handling method. SponsorshipManager
does the rest—it registers itself as the sponsor, and it unregisters
all the remote leases it sponsors automatically when the client shuts
down. Finally, note the use of C# 2.0 iterators in implementing the IEnumerable<ILease> interface. The interface allows clients to iterate over the list of managed sponsors, if the need ever arises:
SponsorshipManager sponsorshipManager = new SponsorshipManager( );
//Some code to initialize sponsorshipManager, then:
foreach(ILease lease in sponsorshipManager)
{...}