Chapter 27. .NET Remoting

27.1. Introduction

Spring's .NET Remoting support allows you to export a 'plain CLR object' as a .NET Remoted object. By "plain CLR object" we mean classes that do not inherit from a specific infrastructure base class such as MarshalByRefObject. On the server side, Spring's .NET Remoting exporters will automatically create a proxy that implements MarshalByRefObject. You register SAO types as either SingleCall or Singleton and also configure on a per-object basis lifetime and leasing parameters. 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. The current implementation requires that your plain .NET objects implements a business service interface. Additionally you can add AOP advice to both SAO and CAO objects.

You can leverage the IoC container to configure the exporter and service endpoints. A remoting specific xml-schema is provided to simplify the remoting configuration but you can still use the standard reflection-like property based configuration schema. You may also opt to not use the IoC container to configure the objects and use Spring's .NET Remoting classes Programatically, as you would with any third party library.

A sample application, often referred to in this documentation, is in the distribution under the directory "examples\Spring\Spring.Calculator" and may also be found via the start menu by selecting the 'Calculator' item.

27.2. Publishing SAOs on the Server

Exposing a Singleton SAO service can be done in two ways. The first is through programmatic 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 the 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 to an SAO Singleton object

27.2.1. SAO Singleton

<object id="singletonCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.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" />
</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 further below.) 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 initial memory of the calculator is set to 217 via the constructor. The class AdvancedCalculator does not inherit from MarshalByRefObject. Also note that the exporter sets the lifetime of the SAO Singleton to infinite so that the singleton will not be garbage collected after 5 minutes (the .NET default lease time). If you would like to vary the lifetime properties, they are InitialLeaseTime, RenewOnCallTime, and SponsorshipTimeout.

A custom schema is provided to make the object declaration even easier and with intellisense support for the attributes. This is shown below

<objects xmlns="http://www.springframework.net" 
         xmlns:r="http://www.springframework.net/remoting">

  <r:saoExporter targetName="singletonCalculator" 
                 serviceName="RemotedSaoSingletonCalculator" />

  ... other object definitions

</objects>

Refer to the end of this chapter for more information on Spring's .NET custom schema.

27.2.2. SAO SingleCall

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

<object id="prototypeCalculator" type="Spring.Calculator.Services.AdvancedCalculator, Spring.Calculator.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>

Note that we change the singleton attribute of the plain CLR object as configured by Spring in the <object> definition and not an attribute on the SaoExporter. The object referred 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" />
</object>
[Note]Note
As generally required with a .NET Remoting application, the arguments to your service methods should be Serializable.

27.2.1. Console Application Configuration

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("RemoteApp.exe.config");

IApplicationContext ctx = ContextRegistry.GetContext();

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

Console.ReadLine(); 
    

You can also put in the configuration file an instance of the object Spring.Remoting.RemotingConfigurer to make the RemotingConfiguration call show above on your behalf during initialization of the IoC container. The RemotingConfigurer implements the IObjectFactoryPostProcessor interface, which gets called after all object definitions have been loaded but before they have been instantiated, (SeeSection 5.9.2, “Customizing configuration metadata with ObjectFactoryPostProcessors” for more information). The RemotingConfigurer has two properties you can configure. Filename, that specifies the filename to load the .NET remoting configuration from (if null the default file name is used) and EnsureSecurity which makes sure the channel in encrypted (available only on .NET 2.0). As a convenience, the custom Spring remoting schema can be used to define an instance of this class as shown below, taken from the Remoting QuickStart

<objects xmlns="http://www.springframework.net"
         xmlns:r="http://www.springframework.net/remoting">

  <r:configurer filename="Spring.Calculator.RemoteApp.exe.config" />

</objects>

The ReadLine prevents the console application from exiting. You can refer to the code in RemoteApp in the Remoting QuickStart to see this code in action.

27.2.3. IIS Application Configuration

If you are deploying a .NET remoting application inside IIS there is a sample project that demonstrates the necessary configuration using Spring.Web.

Spring.Web ensures the application context is initialized, but if you don't use Spring.Web the idea is to start the initialization of the Spring IoC container inside the application start method defined in Global.asax, as shown below

 void Application_Start(object sender, EventArgs e) 
 {
     // Code that runs on application startup

     // Ensure Spring has loaded configuration registering context
     Spring.Context.IApplicationContext ctx = new Spring.Context.Support.XmlApplicationContext(
                                                  HttpContext.Current.Server.MapPath("Spring.Config"));
     Spring.Context.Support.ContextRegistry.RegisterContext(ctx); 
 }

In this example, the Spring configuration file is named Spring.Config. Inside Web.config you add a standard <system.runtime.remoting> section. Note that you do not need to specify the port number of your channels as they will use the port number of your web site. Ambiguous results have been reported if you do specify the port number. Also, in order for IIS to recognize the remoting request, you should add the suffix '.rem' or '.soap' to the target name of your exported remote object so that the correct IIS handler can be invoked.

27.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 implementation of the class on the client side. Practically speaking this would mean linking in the server assembly to the client application, a generally recognized bad practice. This 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 implementation 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="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract" />
  <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 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.

27.4. CAO best practices

Creating a client activated object (CAO) is typically done by administrative type registration, either Programatically 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 manner similar to how Spring's generic object factory can be used as a replacement 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 wish 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 27.8, “Additional Resources”, for some links to additional resources.

27.5. Registering a CAO object on the Server

To expose an object as a CAO on the server you should declare an object in the standard Spring configuration that is a 'prototype', that is the singleton property is set to false. This results in a new object being created each time it is retrieved from Spring's IoC container. An implementation of ICaoRemoteFactory is what is exported via a call to RemotingServices.Marshal. This implementation uses Spring's IoC container to create objects and then dynamically create a .NET remoting proxy for the retrieved object. Note that the default lifetime of the remote object is set to infinite (null is returned from the implementation of InitializeLifetimeService()).

This is best shown using an example from the Remoting Quickstart application. Here is the definition of a simple calculator object,

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

To export this as a CAO object we can declare the CaoExporter object directly in the server's XML configuration file, as shown below

<object id="caoCalculator" type="Spring.Remoting.CaoExporter, Spring.Services">
  <property name="TargetName" value="prototypeCalculator" />
  <property name="Infinite" value="false" />
  <property name="InitialLeaseTime" value="2m" />
  <property name="RenewOnCallTime" value="1m" />
</object>

Note the property 'TargetName' is set to the name, not the reference, of the non-singleton declaration of the 'AdvancedCalculator' class.

Alternatively, you can use the remoting schema and declare the CAO object as shown below

<r:caoExporter targetName="prototypeCalculator" infinite="false">
  <r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" />
</r:caoExporter>

27.5.1. Applying AOP advice to exported CAO objects

Applying AOP advice to exported CAO objects is done by referencing the adviced object name to the CAO exporter. Again, taking an example from the Remoting QuickStart, a calculator with logging around advice is defined as shown below.

<object id="prototypeCalculatorWeaved" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
  <property name="targetSource">
    <object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
      <property name="TargetObjectName" value="prototypeCalculator" />
    </object>
  </property>
  <property name="interceptorNames">
    <list>
      <value>ConsoleLoggingAroundAdvice</value>
    </list>
  </property>
</object>

If this declaration is unfamiliar to you, please refer to Chapter 13, Aspect Oriented Programming with Spring.NET for more information. The CAO exporter then references with the name 'prototypeCalculatorWeaved' as shown below.

<r:caoExporter targetName="prototypeCalculatorWeaved" infinite="false">
  <r:lifeTime initialLeaseTime="2m" renewOnCallTime="1m" />
</r:caoExporter>

27.6. Accessing a CAO on the Client

On the client side a CAO reference is obtained by using the CaoFactoryObject as shown below

<object id="calculatorService" type="Spring.Remoting.CaoFactoryObject, Spring.Services">
  <property name="RemoteTargetName" value="prototypeCalculator" />
  <property name="ServiceUrl" value="tcp://localhost:8005" />
</object>

This definition corresponds to the exported calculator from the previous section. The property 'RemoteTargetName' identifies the object on the server side. Using this approach the client can obtain an reference though standard DI techniques to a remote object that implements the IAdvancedCalculator interface. (As always, that doesn't mean the client should treat the object as if it was an in-process object).

Alternatively, you can use the Remoting schema to shorten this definition and provide intellisense code completion

<r:caoFactory id="calculatorService" 
              remoteTargetName="prototypeCalculator" 
              serviceUrl="tcp://localhost:8005" />

27.6.1. Applying AOP advice to client side CAO objects.

Applying AOP advice to a client side CAO object is done just like any other object. Simply use the id of the object created by the CaoFactoryObject as the AOP target, i.e. 'calculatorService' in the previous example.

27.7. XML Schema for configuration

Please install the XSD schemas into VS.NET as described in Chapter 36, Visual Studio.NET Integration. XML intellisense for the attributes of the saoExporter, caoExporter and caoFactory should be self explanatory as they mimic the standard property names used to configure .NET remote objects.

27.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.