Hello everyone,
I am tasked with writing a distributed event managing tool where each client, either a Ruby, C# or Java Client, synchronises all changes with a list of registered clients. I have to use XML-RPC to achieve the goal. My team and I have written up an XML-RPC client and server in each language and will provide the relevant source code below. If you require more code, please let me know.
The problem is that I can get Java and C# to communicate with each other. Ruby can communicate with the others but C# (and maybe Java, haven't tested yet) have problems addressing the Ruby server. I guess the problem is with the Endpoint. First let me give you some code. When reading please be aware that the code is actually written by a team and naming conventions differ a bit:
C# client
Uri _address = new Uri("http://" + _s + ":8000/xmlrpc/EventManagerService");
ChannelFactory<IEventManagerWCF_XML_RPC> _factory = new ChannelFactory<IEventManagerWCF_XML_RPC>(new WebHttpBinding(WebHttpSecurityMode.None), new EndpointAddress(_address));
_factory.Endpoint.Behaviors.Add(new XmlRpcEndpointBehavior());
IEventManagerWCF_XML_RPC _proxy = _factory.CreateChannel();
_proxy will not hold the client for a given URI. Those are stored in a dictionary and used when the need arises to synchronise events. One such synchronisation would happen in the case of a modification;
foreach(IEventManagerWCF_XML_RPC proxy in this.peers.Values)
proxy.modify(_id, _field, _newValue);
Here is an extract from the IEventManagerWCF_XML_RPC interface;
[OperationContract(Action = "EventManagerService.modify")]
bool modify(int id, string field, string newValue);
C# XML RPC service
Uri _baseAddress = new Uri("http://localhost:8000/xmlrpc");
_eventManagerHost = new ServiceHost(typeof(EventManagerService), _baseAddress);
try
{
ServiceEndpoint epXmlRpc = _eventManagerHost.AddServiceEndpoint(typeof(IEventManagerWCF_XML_RPC), new WebHttpBinding(WebHttpSecurityMode.None), "EventManagerService");
epXmlRpc.Behaviors.Add(new XmlRpcEndpointBehavior());
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
_eventManagerHost.Description.Behaviors.Add(smb);
_eventManagerHost.Open();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
_eventManagerHost.Abort();
}
Nothing special here I guess. Lets move on to the Java code!
Java Client
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
try {
config.setServerURL(new URL("http://"+ip+":8000/xmlrpc/EventManagerService"));
}
catch (MalformedURLException e) {
e.printStackTrace();
}
config.setEnabledForExtensions(true);
config.setConnectionTimeout(60 * 1000);
config.setReplyTimeout(60 * 1000);
XmlRpcClient client = new XmlRpcClient();
client.setTransportFactory(new XmlRpcCommonsTransportFactory(client));
client.setConfig(config);
xmlRpcPeers.put(ip, client);
xmlRpcPeers now holds the different clients. They are called as follows;
for(XmlRpcClient peer : this.xmlRpcPeers.values())
{
try {
peer.execute("EventManagerService.modify", params);
} catch (Exception e) {
e.printStackTrace();
}
}
The Java Server has it's own class and is instantiated with a simple new
call;
public class Server extends Thread{
/**
* Server port
*/
private static final int port = 8000;
/**
* Starts the XML-RPC server
*/
public void run(){
WebServer webServer = new WebServer(port);
XmlRpcServer xmlRpcServer = webServer.getXmlRpcServer();
PropertyHandlerMapping phm = new PropertyHandlerMapping();
try
{
phm.addHandler("EventManagerService", lu.uni.binfo.ds.EventManager_Java.EventManagerService.class);
}
catch (XmlRpcException e1)
{
e1.printStackTrace();
}
xmlRpcServer.setHandlerMapping(phm);
XmlRpcServerConfigImpl serverConfig = (XmlRpcServerConfigImpl) xmlRpcServer.getConfig();
serverConfig.setEnabledForExtensions(true);
serverConfig.setContentLengthOptional(false);
try
{
webServer.start();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
Up till now everything seemed to work fine. Adding Ruby to the mix is what gives the most trouble. Here is the relevant code;
Ruby Client Ruby clients are also stored in a dictionary. It is populated as follows;
@devices_XMLRPC[key] = EventManagerClient_XMLRPC.new(tokens[0]).device
The code for the class is:
class EventManagerClient_XMLRPC
@uri
@device
attr_accessor :uri, :device
def initialize(uri)
@uri = uri
@device = XMLRPC::Client.new2(
uri="http://" << @uri.to_s << ":8000/xmlrpc/EventManagerService", proxy=nil, timeout=30)
end
end
A call to synchronise on modifications looks like this:
@devices_XMLRPC.each_value { |client| client.call("EventManagerService.modify", tokens[0], field, value) }
Ruby Server
server = XMLRPC::Server.new(8000, "127.0.0.1")
server.add_handler("xmlrpc/EventManagerService", EventManagerService.instance)
puts "Server ready!"
server.serve
The EventManagerService class:
class EventManagerService
include Singleton
@manager
def initialize()
@manager = EventManager.instance
end
def modify(id, field, newValue)
puts "modify called"
@manager.modify([id, field, newValue], 1)
end
end
EventManager being the class where all the logic resides.
The error when trying to communicate from C# to Ruby is an EndPointNotFoundException
that reads:
There was no endpoint listening at http://ip:8000/xmlrpc/EventManagerService that could accept the message.[...]
I tried fiddling around with the endpoint declaration but cannot seem to get it to work. The Ruby documentation does not help either. I am in need of help!