Customizing Remoting

I l @ ve RuBoard

By now, you should appreciate that the remoting services implemented by .NET are comprehensive and for the most part pretty simple to program against. Much of the complexity of marshaling objects, creating proxies, and transmitting data is hidden in a set of system-defined classes. However, the remoting infrastructure is highly extensible; the .NET Framework Class Library provides a set of helper classes and interfaces that you can use if you want to examine or modify the default behavior of the remoting system. There's much more to remoting than we have the time or space to cover in this chapter, so here we'll concentrate on a few of the less esoteric options.

One-Way Remoting

On some occasions, a remote method might not return a value and might have no output arguments of any description, only inputs. Such methods are sometimes termed "fire and forget." They can be executed asynchronously because there is no need for the client to wait until the method completes. This is especially useful over the HTTP channel, where the remote object can be very distant (especially if it's hosted by IIS). You can tag remote object methods that follow this pattern with System.Runtime.Remoting.Messaging.OneWayAttribute when they're defined:

 importSystem.Runtime.Remoting.*; importSystem.Runtime.Remoting.Messaging.*; publicclassRemoteObjectextendsMarshalByRefObject { /**@attributeOneWayAttribute()*/ publicvoidRemoteAsyncMethod(...) { } } 

A one-way method must be declared as a void, must use only marshal- by-value parameters, and cannot throw any exceptions. When the client calls a one-way method, it might execute asynchronously. There is no absolute guarantee, and the runtime might elect to execute the method synchronously if it determines that this would be more efficient.

The RemotingServices Class

Much of the time, you'll be content to use the remoting infrastructure as is. However, if you require more detailed control, you can use the System.Run ­time.Remoting.RemotingServices class (not to be confused with the System.Runtime.Remoting.Services.RemotingService class), which contains a collection of useful helper methods.

The RemotingServices.Marshal method can be used by a server application to dynamically publish an object that extends MarshalByRefObject . It returns an ObjRef that can be passed back to a client when it connects. (There's also an Unmarshal method that converts an ObjRef into a proxy, but this is used less frequently because the Connect method is preferable, as explained shortly.) You can optionally specify a URI that a client can request. You should register a channel before marshaling an object. One reason for adopting this approach is that it allows a server to create and activate an object using any available constructor. (The standard mechanism allows only the default constructor to be used by server-activated objects.)

The following code fragment creates and publishes an instance of the programmer-defined class MyRefObjectType . Be aware, however, that the object ( publishedObject ) is still subject to the lifetime policies implemented by the remoting infrastructure and might be garbage collected if its lease expires .

 MyRefObjectTypepublishedObject=newMyRefObjectType(...); ChannelServices.RegisterChannel(newTcpChannel(6000)); ObjRefwellKnownRef=RemotingServices.Marshal(publishedObject,  "MyRefObjectURI"); 

A client, aware of the channel and URI published by the server, can connect to this object using either the GetObject method (as shown earlier) or the RemotingServices.Connect method, which returns a proxy:

 ChannelServices.RegisterChannel(newTcpChannel()); MyRefObjectTypeproxy= (MyRefObjectType)RemotingServices.Connect(Type.GetType("MyRefObjectTypeAssembly.MyRefObjectType,MyRefObjectTypeAssembly"),  "tcp://MyServer:6000/MyRefObjectURI"); 

Any method calls made using the proxy variable will actually be remote method calls, which are dispatched to the application that's hosting the remote object ( MyServer ).

A server can cease publishing an object using the Disconnect method. This method returns a B oolean value indicating whether the operation was successful:

 booleansuccess=RemotingServices.Disconnect(MyRefObjectType); 

The Disconnect method does not destroy the object, and it can be registered again later, either on the same or a different channel or URI using the Marshal method. Do not try to call Disconnect on a client-side proxy ”the result will be a RemoteException and the message "Cannot call disconnect on a proxy."

You can use the RemotingServices.ExecuteMessage method to connect to a remote object, execute a method, obtain any return value, and disconnect from the remote object. The method expects the message data to be passed in as a System.Runtime.Remoting.Messaging.IMethodCallMessage object, and it returns an implementation of System.Runtime.Remoting.Messaging.IMethodReturnMessage . (These are both subtypes of System.Runtime.Remoting.Messaging.IMessage .) It would not be usual for a client to create its own IMessage objects, but this method is useful for a server that receives a method call on a custom message sink (as described later). Messages enter the message sink chain as IMessage objects, and a custom sink can use this method to scrutinize the message and forward it to another server.

Tip

You can extract the details of an IMethodCallMessage object by assigning it to a System.Runtime.Remoting.MethodCall object (this is the concrete type of the object) and examining its properties. Useful properties include MethodName ( get_MethodName ), TypeName ( get_TypeName ), and Uri ( get_Uri ).


The RemotingServices class interacts with the remoting infrastructure and exposes methods that a client can use to determine the nature of useful aspects of a remote object, including these:

  • IsObjectOutOfAppDomain

    This method takes a transparent proxy (returned by Connect or GetObject ) and returns a Boolean value indicating whether the proxy refers to an object in a different application domain.

  • IsObjectOutOfContext

    This method takes a transparent proxy and returns a value indicating whether the proxy refers to an object in a different context.

  • IsOneWay

    This method expects a System.Reflection.MethodBase that contains the metadata for a method (obtained using reflection) and indicates whether the method is one-way.

  • IsTransparentProxy

    This method takes an object reference and returns a Boolean value indicating whether the reference refers to a transparent proxy or a real object.

Tracking Handlers

Another interesting feature supplied by the remoting infrastructure is the ability to track when objects are marshaled and unmarshaled. You can use this feature to log object accesses . It can also give you insight into how the remoting services perform their task. Whenever objects are marshaled, unmarshaled, or disconnected, the remoting infrastructure can notify one or more registered tracking handlers about the event, passing them information about the remote object in question.

A tracking handler is a class that implements the System.Runtime.Remoting.Services.ITrackingHandler interface. This interface defines three methods:

  • DisconnectedObject(System.Object obj)

    This method is called when the specified object is disconnected by a host server application. (It can longer accept requests .)

  • MarshaledObject(System.Object obj, ObjRef ref)

    This method is called whenever a remote object ( MarshalByRefObject or Serializable ) is marshaled and an ObjRef is created.

  • UnmarshaledObject(System.Object obj, ObjRef ref)

    Similarly, this method is called whenever a remote object has been unmarshaled.

The CakeTracker class shown in the following listing (available in the TrackedCakeUtils project) shows a simple implementation of a tracking handler. All it does is print the details of the object being disconnected, marshaled, or unmarshaled, to the console. The ObjRef class has properties that supply some details about the remote object reference. The CakeTracker class uses the URI property ( get_URI ) to display the URI of the remote reference:

 packageCakeUtils; importSystem.*; importSystem.Runtime.Remoting.*; importSystem.Runtime.Remoting.Services.*; //TrackinghandlerthatmonitorshowtheCakeInfoclassisused publicclassCakeTrackerimplementsITrackingHandler { publicvoidDisconnectedObject(System.Objectobj) { Console.WriteLine("Objectdisconnected: " +obj.ToString()); Console.WriteLine(); } publicvoidMarshaledObject(System.Objectobj,ObjRefref) { Console.WriteLine("ObjectMarshaled: " +obj.ToString()+  " URI:" +ref.get_URI()); Console.WriteLine(); } publicvoidUnmarshaledObject(System.Objectobj,ObjRefref) { Console.WriteLine("Objectunmarshaled: " +obj.ToString()+  " URI: " +ref.get_URI()); Console.WriteLine(); } } 

To track remote objects, you create an instance of the CakeTracker class and register it with the remoting infrastructure. The System.Runtime.Remoting.Services.TrackingServices class contains the static method RegisterTrackingHandle r for this purpose. You can register tracking handlers on clients and host server applications. (The following example shows a client; the two lines of code that create and register a tracking handler will be the same on the server.)

 importSystem.Runtime.Remoting.Services.*; importCakeUtils.*; //TestclientfortheCakeSizeServerclass publicclassCakeClient { publicstaticvoidmain(String[]args) { try { ITrackingHandlertrackingHandler=newCakeTracker(); TrackingServices.RegisterTrackingHandler(trackingHandler); //Configuretheclientchannelandremoteobjectactivator RemotingConfiguration.Configure("CakeClient.exe.config"); } } } 

If you register more than one tracking handler, each one will be called in turn as tracking events occur. Figures 11-7 and 11-8 show the server console for tracked versions of the CakeServer and CakeClient applications, respectively, which implement client-activated remote objects. (These applications are available in the projects TrackedClientActivatedTCPCakeSizeServer and TrackedClientActivatedTCPCakeClient.)

Figure 11-7. The server console screen showing tracking messages for a client-activated object

Figure 11-8. The client console screen showing tracking messages

If you change the configuration to use server-activated objects (see the projects TrackedServerActivatedTCPCakeSizeServer and TrackedServerActivatedTCPCakeClient), you'll notice fewer marshaling messages (none on the client), highlighting the differences in the remoting protocol and the timing of marshaling calls used by the two different models. Server-activated remote objects do not create an ActivationListener , for example ”the ActivationListener object waits for activation requests from clients and then instantiates remote objects as demanded by the client. You can also see the internal URIs generated for each remote object. A further interesting exercise is to change the CakeInfo class from MarshalByRefObject to Serializable ; in this case, a copy of the object is transferred from the server application to the client when it is marshaled, and thereafter the client uses the local copy.

Custom Channel Sinks and Channels

Occasionally, you might find it useful to customize the channel sink chain, such as when you need to encrypt data or add security credential information when you use the TCP channel ”scenarios described earlier in the chapter. You can also build custom channel sinks that log remote requests or track the activity of remote objects.

Creating a custom channel sink is a matter of implementing the IClientChannelSink interface or IServerChannelSink interface (both are in the System.Runtime.Remoting.Channels namespace) and optionally the IMessageSink interface. The channel sink interfaces contain a NextChannelSink property that you can use to obtain a reference to the next sink in the chain, and they contain methods for processing an IMessage object, synchronously or asynchronously, before handing it off to the next sink in the chain.

You must also define a custom channel sink provider object that can create your custom sink. You implement the IClientChannelSinkProvider or IServer ­ChannelSinkProvider interface (also in System.Runtime.Remoting.Channels ). Both these interfaces contain the CreateSink method, which should instantiate a custom sink object. Channel sink providers are also chained together, so when a provider has finished building its custom sink, the infrastructure can invoke the next provider in the chain to build its own sinks. Both sink provider interfaces therefore include a Next property for linking sink providers together.

The simplest way to install a custom channel sink is to add the custom sink provider class to the list of providers loaded by the remoting infrastructure in the configuration file. The following example loads a server channel sink provider class called CredentialServerChannelSinkProvider in the Credentials assembly:

 <configuration> <system.runtime.remoting> <channelSinkProviders> <serverProviders> <providertype="Credentials.CredentialServerChannelSinkProvider, Credentials" /> </serverProviders> </channelSinkProviders> </system.runtime.remoting> </configuration> 

You can also create custom channels if you need to send data using a protocol other than TCP or HTTP or if you want to implement some rudimentary form of load balancing and redirect client requests to different servers based on their load. To do this, you implement the IChannelSender , IChannelReceiver and IChannel interfaces (which are in System.Runtime.Remoting.Channels ) to communicate with the channel sink chains and physically transmit or receive data. At run time, you load a custom channel using the ChannelServices.RegisterChannel method or using the configuration file:

 <?xmlversion="1.0"?> <configuration> <system.runtime.remoting> <application> <channels> <channeltype="NewProtocolAssembly.NewChannel,NewProtocolAssembly, ... ",.../> </channels> </application> </system.runtime.remoting> </configuration> 
I l @ ve RuBoard


Microsoft Visual J# .NET (Core Reference)
Microsoft Visual J# .NET (Core Reference) (Pro-Developer)
ISBN: 0735615500
EAN: 2147483647
Year: 2002
Pages: 128

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net