Chapter 10. .NET Remoting

10.1. Introduction

(Available in 1.1)

The main goal of Spring.Services is to provide location transparency for business services. We believe that users should be able to implement services the simplest way possible, using service interfaces and implementations in the form of plain .NET classes. We also think that decision on how a particular service is exposed to clients should be a configuration concern and not an implementation concern.

In preview status is support for exposing a plain .NET object as a web service, System.EnterpriseServices serviced component, and remoted objects. By "plain .NET object" we mean classes that do not inherit from a specific infrastructure base class (such as MarshalByRefObject) or infrastructure attributes (such as WebMethod). The current implementation relies on your plain .NET object to implement a business interface. Spring's .NET Remoting exporters will automatically create a proxy that implements MarshalByRefObject. On the server side you can register SAO types as either SingleCall or Singleton and also configure on a per-object basis lifetime and leasing parameters. Additionally for SAO objects you can export an object that has had AOP advice applied to it. On the client side you can obtain CAO references to server proxy objects in a manner that promotes interface based design best practices when developing .NET remoting applications. An remoting specific xml-schema is also provided to simplify the remoting configuration, although you can still use the standard reflection-like property based configuration schema.

If you want to use these features please get the code from CVS (instructions) or from the nightly release vailable from the following download page. A sample application, often referred to in this documentation, is under the directory "examples\Spring\Spring.Examples.Calculator"/>

10.2. Publishing a Singleton SAO on the Server

Exposing a Singleton SAO service can be done in two ways. The first is through programatic or administrative type registration that makes calls to RemotingConfiguration.RegisterWellKnownServiceType. This method has the limitation that you must use a default constructor and you can not easily configure the singleton state at runtime since it is created on demand. The second way is to publish an object instance using RemotingServices.Marshal. This method overcomes the limitations of the first method. Example server side code for publishing an SAO singleton object with a predefined state is shown below

AdvancedMBRCalculator calc = new AdvancedMBRCalculator(217);
RemotingServices.Marshal(calc, "MyRemotedCalculator");

The class AdvancedMBRCalculator used above inherits from MarshalByRefObject.

If your design calls for configuring a singleton SAO, or using a non-default constructor, you can use Spring IoC container to create the SAO instance, configure it, and register it with the .NET remoting infrastructure. The SaoExporter class performs this task and most importantly, will automatically create a proxy class that inherits from MarshalbyRefObject if your business object does not already do so. The following XML taken from the Remoting QuickStart demonstrates its usage.

<object id="singletonCalculator" type="Services.AdvancedCalculator, Services">
  <constructor-arg type="int" value="217"/>
</object>

<!-- Registers the calculator service as a SAO in 'Singleton' mode. -->
<object name="saoSingletonCalculator" type="Spring.Remoting.SaoExporter, Spring.Services">
  <property name="TargetName" value="singletonCalculator" />
  <property name="ServiceName" value="RemotedSaoSingletonCalculator" />
  <property name="Infinite" value="true" />
</object>

This XML fragment shows how an existing object "singletonCalculator" defined in the Spring context is exposed under the url-path name "RemotedSaoSingletonCalculator". (The fully qualified url is tcp://localhost:8005/RemotedSaoSingleCallCalculator using the standard .NET channel configuration shown futher below.) The AdvancedCalculator class implements the business interface IAdvancedCalculator. The current proxy implementation requires that your business objects implement an interface. The interfaces' methods will be the ones exposed in the generated .NET remoting proxy. The inital memory of the calculator is set to 217 via the constructor. The class Services.AdvancedCalcualtor does not inherit from MarshalByRefObject. The Infinite property is used to set the lifetime of the object to be infinite so that the the singleton will not be garbage collected after 5 minutes (the default lease time). Other lifecycle properties you can set are InitialLeaseTime, RenewOnCallTime, and SponsorshipTimeout.

The following XML fragment shows to to expose the calculator service in SAO 'SingleCall' mode.

<object id="prototypeCalculator" type="Services.AdvancedCalculator, Services" singleton="false">
  <constructor-arg type="int" value="217"/>
</object>

<object name="saoSingleCallCalculator" type="Spring.Remoting.SaoExporter, Spring.Services">
  <property name="TargetName" value="prototypeCalculator" />
  <property name="ServiceName" value="RemotedSaoSingleCallCalculator" />
</object>

The object refered to in the TargetName parameter can be an AOP proxy to a business object. For example, if we were to apply some simple logging advice to the singleton calculator, the following standard AOP configuration is used to create the target for the SaoExporter

<object id="singletonCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
	<property name="target" ref="singletonCalculator"/>
	<property name="interceptorNames">
		<list>
			<value>Log4NetLoggingAroundAdvice</value>
		</list>
	</property>
</object>
<object name="saoSingletonCalculatorWeaved" type="Spring.Remoting.SaoExporter, Spring.Services">
	<property name="TargetName" value="singletonCalculatorWeaved" />
	<property name="ServiceName" value="RemotedSaoSingletonCalculatorWeaved" />
	<property name="Infinite" value="true" />
</object>

When using SaoExporter you can still use the standard remoting administration section in the application configuration file to register the channel. ChannelServices as shown below

<system.runtime.remoting>
	<application>
		<channels>
			<channel ref="tcp" port="8005" />
		</channels>
	</application>
</system.runtime.remoting>

A console application that will host this remoted object needs to initialize the .NET Remoting infrastructure with a call to RemotingConfiguration (since we are using the .config file for channel registration) and then start the Spring application context. This is shown below

RemotingConfiguration.Configure("RemoteServer.exe.config");

IApplicationContext ctx = ContextRegistry.GetContext();

Console.Out.WriteLine("Server listening...");

Console.ReadLine(); 
    

The readline prevents the console application from exiting. You can refer to the the code in RemoteServer in the Remoting QuickStart to see ths code in action. Deployment as a windows service should follow in a similar manner but without the ReadLine as the process does not exit. Deployment inside IIS is not yet tested.

[Note]
As generally required with a .NET Remoting application, the arguments to your service methods should be Serializable.

10.3. Accessing a SAO on the Client

Administrative type registration on the client side lets you easily obtain a reference to a SAO object. When a type is registered on the client, using the new operator or using the reflection API will return a proxy to the remote object instead of a local reference. Administrative type registration on the client for a SAO object is performed using the wellknown element in the client configuration section. However, this approach requires that you expose the implemenation of the class on the client side. Practially speaking this would mean linking in the server assembly to the client application, a generally recognized bad practice. This is dependency can be removed by developing remote services based on a business interface. Aside from remoting considerations, the separation of interface and implementation is considered a good practice when designing OO systems. In the context of remoting, this means that the client can obtain a proxy to a specific implemenation with only a reference to the interface assembly. To achieve the decoupling of client and server, a separate assembly containing the interface definitions is created and shared between the client and server applications.

There is a simple means for following this design when the remote object is a SAO object. A call to Activator.GetObject will instantiate a SAO proxy on the client. For CAO objects another mechanism is used and is discussed later. The code to obtain the SAO proxy is shown below

ICalculator calc = (ICalculator)Activator.GetObject (
  typeof (ICalculator),
  "tcp://localhost:8005/MyRemotedCalculator");

To obtain a reference to a SAO proxy within the IoC container, you can use the object factory SaoFactoryObject in the Spring configuration file. The following XML taken from the Remoting QuickStart demonstrates its usage.

<object id="calculatorService" type="Spring.Remoting.SaoFactoryObject, Spring.Services">
	<property name="ServiceInterface" value="Interfaces.IAdvancedCalculator, Interfaces" />
	<property name="ServiceUrl" value="tcp://localhost:8005/RemotedSaoSingletonCalculator" />
</object>

The ServiceInterface property specifies the type of proxy to create while the ServiceUrl property creates a proxy bound to the specified server and published object name.

Other objects in the IoC container that depend on an implementation of the interface ICalculator can now refer to the object "calculatorService", thereby using using a remote implementation of this interface. The exposure of dependencies among objects within the IoC container lets you easily switch the implementation of ICalculator. By using the IoC container changing the application to use a local instead of remote implementation is a configuration file change, not a code change. By promoting interface based programing, the ability to switch implementation makes it easier to unit test the client application, since unit testing can be done with a mock implementation of the interface. Similarly, development of the client can proceed independent of the server implementation. This increases productivity when there are separate client and server development teams. The two teams agree on interfaces before starting development. The client team can quickly create a simple, but functional implementation and then integrate with the server implementation when it is ready.

10.4. CAO best practices

Creating a client activiated object (CAO) is typically done by administrative type registration, either programmatically or via the standard .NET remoting configuration section. The registration process allows you to use the 'new' operator to create the remote object and requires that the implementation of the object be distributed to the client. As mentioned before, this is not a desirable approach to developing distributed systems. The best practice approach that avoids this problem is to create an SAO based factory class on the server that will return CAO references to the client. In a maner similar to how Spring's generic object factory can be used as a replacement to creating a factory per class, we can create a generic SAO object factory to return CAO references to objects defined in Spring's application context. This functionality is encapsulated in Spring's CaoExporter class. On the client side a reference is obtained using CaoFactoryObject. The client side factory object supports creation of the CAO object using constructor arguments. In addition to reducing the clutter and tedium around creating factory classes specific to each object type you which to expose in this manner, this approach has the additional benefit of not requiring any type registration on the client or server side. This is because the act of returning an instance of a class that inherits from MarshalByRefObject across a remoting boundary automatically returns a CAO object reference. For more information on this best-practice, refer to the last section, Section 10.8, “Additional Resources”, for some links to additional resources.

10.5. Registering a CAO object on the Server

10.6. Accessing a CAO on the Client

Creating a client activiated object (CAO) is typically done by administrative type registration, either programmatically or via the standard .NET remoting configuration section. The registration process allows you to use the 'new' operator to create the remote object and requires that the implementation of the object be distributed to the client. As mentioned before, this is not a desirable approach to developing distributed systems. The best practice approach that avoids this problem is to create an SAO based factory class on the server that will return CAO references to the client. In a maner similar to how Spring's generic object factory can be used as a replacement to creating a factory per class, we can create a generic SAO object factory for use with remoting. An additional benefit of this approach is that you don't have to perform any type registration on the client or server side. This is because the act of returning an instance of a class that inherits from MarshalByRefObject across a remoting boundary automatically returns a CAO object reference. For more information on this best-practice, refer to the last section, Section 10.8, “Additional Resources”, for some links to additional resources.

The interface Spring.Remoting.IRemoteFactory provides this funcationality and defines the following three methods

public interface IRemoteFactory
{
  MarshalByRefObject GetObject(string name);

  MarshalByRefObject GetObject(string name, object[] constructorArguments);

  MarshalByRefObject GetSaoObject(string name);

}

The first two methods are used to return references to CAO objects by specifying the name of the object, as registered with Spring on the server side, and an optional array of constructor arguments. The matching of constructor arguments is done by type which should cover most cases. The class that you use on the server side that implements this interface is Spring.Remoting.RemoteFactory. and it contains a reference to the standard Spring application context (i.e. Object Factory) which is used to return instances of an object by name. You can use the SaoServiceExporter to publish this object to a well known location as shown below from the quick start example.

<object name="prototypeCalculator" type="Server.RemotedCalculator, Server"
			        singleton="false"/>

<object name="remoteFactory" type="Spring.Remoting.RemoteFactory, Spring.Services"/>		    

<object name="publishedRemoteFactory" type="Spring.Remoting.SaoServiceExporter, Spring.Services">
  <property name="Service" ref="remoteFactory"/>
  <property name="ServiceName" value="RemoteFactory"/>
</object>

The object that we will want to get by name on the client side is named, prototypeCalculator Be sure to remember to set the singleton attribute to false, otherwise multiple calls to GetObject will return a CAO reference to the same object instance. On the client side we can obtain a reference to the IRemoteFactory using the SaoFactoryObject helper class as described previously. If you request an object by name that does not inherit from MarshalByRefObject will throw

Although it is not commonly mentioned, one can also return a SAO reference to an object using the same mechanism. In this case, the server returns the result of calling Activator.GetObject to the client instead of returning an instance of the object itself. The method GetSaoObject of Spring.Remoting.RemoteFactory performs this task. To use this functionality you need to tell the RemoteFactory the System.Type of the object, the URI of the object as well as the name that will be used by the client to obtain a reference to the SAO object. This information is captured using the class Spring.Remoting.NamedServiceEntry. The relevant propreties of this class are shown below

public class NamedServiceEntry : IInitializingObject
{
        . . . 
        /// <summary>
        /// The name used to identify the SAO entry.
        /// </summary>
	public string Name
	{ . . . }

        /// <summary>
        /// The service to export, populated via an object reference.
        /// </summary>
	public Type ServiceType
	{ . . . }

        /// <summary>
        /// The uri of the well known object
        /// </summary>
	public string ServiceUrl
	{ . . . }
       
        . . . 
        
}

An collection of these parameters is used to configure the RemoteFactory using the property ServiceEntries. The configuration code from the quick start example is shown below.

<object name="namedSaoCalculatorService" type="Spring.Remoting.NamedServiceEntry">
  <property name="name" value="MyRemotedCalculatorFromFactory" />
  <property name="ServiceType" value="Server.RemotedCalculator, Server" />
  <property name="ServiceUrl" value="tcp://localhost:8005/MyRemotedCalculator" />		  			  
</object>

<object name="remoteFactory" type="Spring.Remoting.RemoteFactory, Spring.Services">

    <property name="serviceEntries">
        <list>
            <ref object="namedSaoCalculatorService"/>
        </list>
    </property>
    
</object>

<object name="publishedRemoteFactory" type="Spring.Remoting.SaoServiceExporter, Spring.Services">
	<property name="Service" ref="remoteFactory"/>
	<property name="ServiceName" value="RemoteFactory"/>
</object>        
        

On the client side one calls the method GetSaoObject using the name specified with for the NamedServiceEntry, i.e. MyRemotedCalculatorFromFactory.

IRemoteFactory remoteFactory = (IRemoteFactory) ctx["remoteFactory"];

ICalculator calc = (ICalculator) remoteFactory.GetSaoObject("MyRemotedCalculatorFromFactory");
              
      

This approach results in less configuration on the client side as both CAO and SAO objects can be retrieved by name using only one registered remoting class, the RemoteFactory.

10.7. XML Schema for configuration

To be done.

10.8. Additional Resources

Two articles that describe the process of creating a standard SAO factory for returning CAO objects are Implementing Broker with .NET Remoting Using Client-Activated Objects on MSDN and Step by Step guide to CAO creation through SAO class factories on Glacial Components website.