implementation
the sample solution of the remote callbacks implemenation is divided into several projects:
contractobject – common abstract definitions (see the above)
remotecallbacklib – remotecallback custom attribute (see the above)
remoteobjecta – classic .net object hosted in the hostserver
remoteobjectx – .net service (com+) hosted in the hostserver
remoteobjectws – web service hosted in the iis
hostserver – console server program
remotewindowsform – windows form client program
also the solution included the following config files:
hostserver.exe.config – the remoting config info for objects hosted by hostserver
remotewindowsform.exe.config – the config info for remoting objects using by this client
remotecallbacklib
the following code snippet shows the implementation of the remotecallbackattribute. its design is based on the refection of the "parent" assembly, where located all metadata of the callback object. the remotecallbacklib is built into the separate assembly which it can be reused by another remoting clients.
namespace rkiss.remotecallbacklib
{
[attributeusage(attributetargets.field)]
public class remotecallbackattribute : attribute
{
private string _desc;
private string _protocol;
private int _port;
public string desc
{
get { return _desc; }
set {_desc = value; }
}
public string protocol
{
get { return _protocol; }
set {_protocol = value; }
}
public int port
{
get { return _port; }
set {_port = value; }
}
public remotecallbackattribute() : this("tcp", 0) {}
public remotecallbackattribute(string sprotocol) : this(sprotocol, 0) {}
public remotecallbackattribute(string sprotocol, int iport)
{
protocol = sprotocol;
port = iport;
}
}
// creating remoting stuff based on the properties of the callbackattribute
// note that all callback class have to located in the same (parent) assembly!
public class remotecallbacksubscriber
{
private hashtable ht = hashtable.synchronized(new hashtable());
private string site = "localhost";
public remotecallbacksubscriber(object parent)
{
type typeparent = parent.gettype();
foreach(fieldinfo fi in typeparent.getfields())
{
foreach(attribute attr in fi.getcustomattributes(true))
{
if(attr is remotecallbackattribute)
{
remotecallbackattribute rca = attr as remotecallbackattribute;
// open in/out channel
int port = rca.port;
if(port == 0)
{
random rdmport = new random(~unchecked((int)datetime.now.ticks));
port = rdmport.next(1, ushort.maxvalue);
}
// create channel
ichannel channel = null;
if(rca.protocol == "tcp")
channel = new tcpchannel(port);
else
if(rca.protocol == "http")
channel = new httpchannel(port);
else
throw new exception(string.format("the {0} is not recognize protocol.", rca.protocol));
// register channel
channelservices.registerchannel(channel);
// register a callback object
string nameofcallbackobject = fi.fieldtype.fullname;
type typeofcallbackobject = typeparent.assembly.gettype(nameofcallbackobject);
remotingconfiguration.registerwellknownservicetype(
typeofcallbackobject,
nameofcallbackobject,
wellknownobjectmode.singleton);
// create proxy
string urlofcallbackobject = string.format(@"{0}://{1}:{2}/{3}",
rca.protocol, site, port, nameofcallbackobject);
object proxycb = activator.getobject(typeofcallbackobject, urlofcallbackobject);
fi.setvalue(parent, proxycb);
//
//store into the ht
ht[fi.name] = channel;
}
}
}
}
public ichannel this [string nameofcallback]
{
get { return ht[nameofcallback] as ichannel; }
}
}
}
remoteobject (a , x and ws)
the design pattern of the remoting methods is the same for any of these remoting objects. the following code snippet shows the remoteobjecta class derived from the marshalbyrefobject class and irmobject interface.
using rkiss.remoteobject; // abstract definitions
namespace rkiss.remoteobjecta
{
public class rmobjecta : marshalbyrefobject, irmobject
{
public rmobjecta()
{
trace.writeline(string.format("[{0}]rmobjecta activated", gethashcode()));
}
public string echo(string msg)
{
trace.writeline(string.format("[{0}]rmobjecta.echo({1})", gethashcode(), msg));
return msg;
}
// stateless callbacks
public string givemecallback(int timeinsec, string ticketid, object objwire)
{
remotecallback wire = objwire as remotecallback;
bool bprogress = true;
callbackeventargs cea = new callbackeventargs();
cea.ticketid = ticketid;
cea.state = "running …";
cea.sender = gettype().tostring();
wire(cea.sender, cea);
while(timeinsec– > 0 && bprogress)
{
thread.sleep(1000);
cea.state = timeinsec;
bprogress = wire(cea.sender, cea);
}
cea.state = bprogress?"done":string.format("aborted at {0}", ++timeinsec);
wire(cea.sender, cea);
//
trace.writeline(string.format("[{0}]rmobjecta.givemecallback({1}, {2}, {3})",
gethashcode(), timeinsec, ticketid, wire.gettype().fullname));
return ticketid;
}
}
}
there are implementation of two methods of the irmobject interface in the object. the first one – echo has a test purpose, the other one – givemecallback simulated some time consuming work with notification of the method state using the remoting callback mechanism. the method state is wrapped and serialized by the callbackeventargs object.
now i will show you only differencies in the following remote objects:
