This class implements a client-side pool for Enterprise Services just-in-time activation proxies.
The motivation for this class is simple: SPEED. The highest cost associated with Enterprise Services' ServicedComponents is indeed object activation; more precisely: It's the activation of the full path of communication and interception between the client-side and the server-side of a ServicedComponent instance. "Just In Time Activation" (JITA) is a feature that has been present in the MTS/COM+/Enterprise Services technology stack from the early beginnings in 1996.
In short: For a component for which JITA is enabled, the first component instance will be instantiated in a regular manner. When this component instance is being deactivated and discarded (when the deactivation flag is set), the server-side instance will go away, but the communication channel and the server-side context are being kept alive and the proxy (client-side) can be reused. The next client call on the same proxy will cause a new server-side instance to be created, but all of the effort to set up client/server communication and setting up the context is skipped.
The primary reason for the existence of JITA in MTS is transaction isolation. It cannot be permitted to make a call into and retrieve transient state from a component that has participated in any completed transaction, because that would violate the "D" and "I" properties of ACID. More and more, JITA proved to be a very valuable feature by itself. Today, in a managed environment, one of the great aspects of JITA is, for instance, that it will enable deterministic, synchronous finalization for object state without having to rely on a client calling Dispose(). A ServicedComponent implementation can make (and revoke) that decision by itself by setting the DeactivateOnReturn property.
With the class shown below, the JITAPool, enables client-side proxy caching that allows very fast links to out-of-process Enterprise Services applications. It essentially eliminates the expensive activation sequence by letting you reuse JITA proxies and complement server-side object pooling with client-side proxy pooling. Using this pool in conjeunction with server-side object pooling means that, after the initial startup, you will simple reconnect and regroup existing instances of objects and proxies but hardly ever create new ones - taking the most expensive aspect of Enterprise Services -- Activation -- out of the picture, altogether.
The pool is lock-free, SMP safe and works great for connecting out-of-process business logic to ASP.NET Web Forms and Web Services.
Usage:
(a) Store a reference to the pool somewhere in a globally accessible static variable or property. One per ServicedComponent class you want to call.
static JITAPool servicedComponentPool =
new JITAPool(typeof(AddressExport.AddressExporterPooled));
(b) Use the pool. Pop() a reference, use the reference and Push() the reference back:
AddressExporterPooled exporter = (AddressExporterPooled)servicedComponentPool.Pop();
exporter.ExportAddressBatchServer( (string) data );
servicedComponentPool.Push(exporter);
Here's the class:
|
using System; using System.EnterpriseServices; using System.Threading;
namespace newtelligence.EnterpriseServices { /// <summary> /// This class implements a client-side pool for /// just-in-time activated ServicedComponents. The pool /// is fully SMP thread safe and should be held in a /// static class variable in environments like ASP.NET. /// Goal of this pool is to eliminate the client-side /// activation work for ServicedComponents which are /// frequently used in such environments. /// </summary> /// <example> /// (TBD) /// </example> /// <remarks> /// Using this pool is permitted in all circumstances /// where holding and reusing references to just-in-time /// activated objects is permitted. It is explicitly not /// permitted (or functional) to use this pool when Pop() /// would be invoked from within a preexisting EnterpriseServices /// context other than the default context. /// </remarks> public sealed class JITAPool { /// <summary> /// Exception thrown when an incompatible type is /// envcountered at initialization time or when /// Push() is called. /// </summary> public class IncompatibleTypeException : Exception { private Type type;
internal IncompatibleTypeException( Type type ) :base("Incompatible data type for JITAPool") { this.type = type; }
public Type TypeError { get { return type; } } }
/// <summary> /// Exception thrown when the pool overflows at /// Push(). /// </summary> public class PoolOverflowException : Exception { private int capacity;
internal PoolOverflowException( int Capacity ) :base("Pool overflow, capacity reached") { this.capacity = Capacity; }
public int Capacity { get { return capacity; } } }
private Type servicedComponentType; private object[] pool; private int capacity;
/// <summary> /// Public constructor for the JITAPool object. /// Sets the pool capacity to a default size of 64 /// slots. /// </summary> /// <param name="ServicedComponentType"> /// Type of serviced component to be placed into the pool. /// </param> public JITAPool( Type ServicedComponentType ) { Initialize( ServicedComponentType, 64 ); }
/// <summary> /// Public constructor for the JITAPool object. /// </summary> /// <param name="ServicedComponentType"> /// Type of serviced component to be placed into the pool. /// </param> /// <param name="Capacity"> /// Capacity of the pool. This number must be less than /// the number of concurrent threads using objects from /// this pool. /// </param> public JITAPool( Type ServicedComponentType, int Capacity ) { Initialize( ServicedComponentType, Capacity ); }
/// <summary> /// Initializes the pool. The capacity cannot and must not /// be changed after initialization. /// </summary> /// <param name="ServicedComponentType"> /// Type of serviced component to be placed into the pool. /// </param> /// <param name="Capacity"> /// Capacity of the pool. This number must be less than /// the number of concurrent threads using objects from /// this pool. /// </param> private void Initialize( Type ServicedComponentType, int Capacity ) { if ( ServicedComponentType.IsSubclassOf(typeof(ServicedComponent)) ) { servicedComponentType = ServicedComponentType; capacity = Capacity; pool = new object[capacity]; } else { throw new IncompatibleTypeException(servicedComponentType); } }
/// <summary> /// ServicedComponent subclass for which proxy instances are /// held in this pool. /// </summary> public Type ServicedComponentType { get { return servicedComponentType; } }
/// <summary> /// Returns a ServicedComponent instance from the pool. If /// the pool is empty, a new instance of the pool's ServicedComponentType /// is instantiated and returned. /// </summary> /// <returns> /// Pooled are newly created ServicedComponent instance of the /// pool's ServicedComponentType /// </returns> public ServicedComponent Pop() { for ( int k=0;k<capacity;k++) { object tok = Interlocked.Exchange(ref pool[k],null); if ( tok != null ) { return tok as ServicedComponent; } }
ServicedComponent component;
component = (ServicedComponent)Activator.CreateInstance(servicedComponentType); return component; }
/// <summary> /// Places a ServicedComponent instance back into the pool. /// </summary> /// <param name="servicedComponent">Reference to a ServicedComponent</param> public void Push( ServicedComponent servicedComponent ) { // uncomment the directives to increase speed for release builds // optional: #if DEBUG if ( servicedComponent.GetType() != servicedComponentType && !servicedComponent.GetType().IsSubclassOf(servicedComponentType) ) { throw new IncompatibleTypeException(servicedComponent.GetType()); } else { // optional: #endif object sc = servicedComponent; for ( int k=0;k<capacity;k++) { object tok = Interlocked.CompareExchange(ref pool[k],sc,null); if ( tok == null ) { return; } } throw new PoolOverflowException(capacity); // optional: #if DEBUG } // optional: #endif }
/// <summary> /// Empties the pool. Should never be called. /// </summary> public void Flush() { for ( int k=0;k<capacity;k++) { Interlocked.Exchange(ref pool[k],null); } } } }
|
|