views:

66

answers:

1

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!

A: 

You could try inspecting the traffic that goes over the line with WireShark. With this tool, you would be able to see the actual HTTP data that is transferred between the different applications. Maybe this provides a hint as to why you have problems communicating between Ruby and C#.

Pieter