0

Brief description of my project: I'm writing a java class named "GreetingsNode", that works in a distributed environment where there is a "managementNode", that is just as service repository and receives and stores info (host port number and service offered) of other nodes and dispatches RPCs of methods offered by services registered. If a node can answer to an RPC, then a thrift socket is opened and a connection is established between the calling node and the answering node, and the answering node returns the result.

I'm using Apache thrift as IDL and framework for RPCs.

Now the problem. My GreetingsNodeHandler class implements a simple thrift interface containing a single method "getHello(user)" (user being a struct containing the name of the node, which is a parameter of the constructor of GreetingsNode class). When a GreetingsNode X, connected to the management Node, makes an RPC of that method, another registered GreetingsNode must answer with the message "hello X".

I don't understand properly how to implement the part of the handler where the result is returned, and consequently I fail to understand how I should write the junit test that should check if the method implementation works correctly.

an assert like assertEquals(client.getHello(user).getMessage(), "Hello John Doe")

would work, but I don't get how, in my case, should i put the client part...

The code for GreetingService thrift service:

struct Message {
    1: string message   
}  

struct User {
    1: string name
}

service GreetingsService {
    Message getHello(1: User user)
}

Code for GreetingsServiceHandler that must implement GreetingsService method getHello()

public class GreetingsServiceHandler implements GreetingsService.Iface {

private static Random random = new Random(10);
private ManagementService.Client managementClient;
private GreetingsService.Client helloClient;

@Override
public Message getHello(User user) throws TException {
    Message answer = null;
    // class ServiceProvider is generated by thrift, part of ManagementService thrift service 
    ServiceProvider provider = null; 
    List<ServiceProvider>providers = managementClient.getProvidersForService(user.name);

    if (providers.isEmpty())
        throw new NoProviderAvailableException(); //separate file contains Exception
    else {
        provider = providers.get(random.nextInt(providers.size()));
        //connection between nodes is established here
        TTransport helloTransport = new TSocket(provider.getHostName(), provider.getPort());
        TProtocol helloProtocol = new TBinaryProtocol(helloTransport);
        helloClient = new GreetingsService.Client(helloProtocol);
        helloTransport.open();

        // here lies my problem
        answer = helloClient.getHello(user);
        //if I use this instead, then helloClient variable is clearly not used, but of course I need it to answer the method call
        answer = answer.setMessage("Ciao " + user.getName() + ", welcome among us!");

    }
    return answer;

}

and GreetingsNode code is the following:

public class GreetingsNode implements NodeIface {

private ThriftServer helloServer;
private ManagementService.Client managementClient;
private NodeManifest nodeManifest;
private User user;
private String name;

public GreetingsNode(NodeManifest nodeManifest, String name) {
    this.nodeManifest = nodeManifest;
    this.helloServer = new ThriftServer(GreetingsServiceHandler.class);
    this.name = name;
}

@Override
public void turnOn() throws TException {

    helloServer.start();

    TSocket helloServerTransport = new TSocket("localhost", Constants.SERVER_PORT);
    TBinaryProtocol helloServerProtocol = new TBinaryProtocol(helloServerTransport);
    managementClient = new ManagementService.Client(helloServerProtocol);
    this.setUser(new User(name));
    helloServerTransport.open();

    helloServer = new ThriftServer(GreetingsServiceHandler.class);
    //portNegotiator is a class described in a separate file, that handles the registration of other nodes to the managementNode. NodeManifest is a file generated by thrift, part of managementService thrift file, describing a struct that contains hostname and port number of nodes.
    PortNegotiator negotiator = new PortNegotiator(managementClient);
    negotiator.negotiate(nodeManifest, helloServer);

}

@Override
public void turnOff() {
    helloServer.stop();
}

public User getUser() {
    return user;
}

public void setUser(User user) {
    this.user = user;
}
JensG
  • 13,148
  • 4
  • 45
  • 55
Gaspare79
  • 51
  • 1
  • 9
  • Have you had a look at the [tutorial code](https://thrift.apache.org/tutorial/java)? – JensG Feb 17 '17 at 09:48
  • of course I did and I've already implemented a functional arithmetic calculator where an "arithmeticNode" answers to calls of an "executeOperation" method made by another node. I fail to translate this kind of behaviour when I use strings... Dunno why actually because the concept should be very similar if not the same – Gaspare79 Feb 17 '17 at 09:53

1 Answers1

1

The basic method impl in the handler is pretty simple, something like the following should do (disclaimer: not tested):

@Override
public Message getHello(User user) throws TException {
    Message answer = new Message();
    answer = answer.setMessage("Ciao " + user.getName() + ", welcome among us!");
    return answer;
}

if I use this instead, then helloClient variable is clearly not used, but of course I need it to answer the method call

When a GreetingsNode X, connected to the management Node, makes an RPC of that method, another registered GreetingsNode must answer with the message "hello X".

If that means that we want a call sequence like Client => ServerA => Server B then this is also possible and requires only slight modifications. Starting from our basic example above, we enhance the code accordingly:

private Message callTheOtherNode(User user) {
  // class ServiceProvider is generated by Thrift, 
  // part of ManagementService Thrift service 
  ServiceProvider provider = null; 
  List<ServiceProvider>providers = managementClient.getProvidersForService(user.name);

  if (providers.isEmpty())
      throw new NoProviderAvailableException(); //separate file contains Exception

  provider = providers.get(random.nextInt(providers.size()));
  //connection between nodes is established here
  TTransport helloTransport = new TSocket(provider.getHostName(), provider.getPort());
  TProtocol helloProtocol = new TBinaryProtocol(helloTransport);
  helloClient = new GreetingsService.Client(helloProtocol);
  helloTransport.open();
  return helloClient.getHello(user);
}

@Override
public Message getHello(User user) throws TException {
    Message answer = callTheOtherNode(user);
    return answer;
}

Of course the "other node" being called needs to actually do something with the request, instead of simply forwarding it again to yet another node.

JensG
  • 13,148
  • 4
  • 45
  • 55
  • I had the suspicion that I was overcomplicating :( A big thank you sir. – Gaspare79 Feb 17 '17 at 09:57
  • Two doubts: 1) I'm not sure where should I put the private CallOtherNode method's code, it would be inside Handler file i guess. Doing so makes eclipse throwing a warning that "The method CallOtherNode(User) from the type GreetingsServiceHandler is never used locally" 2) the second getHello() should be named differently and/or also included inside my thrift service file, otherwise eclipse keeps telling me (correctly) that "The method sayHello(User) of type GreetingsServiceHandler must override or implement a supertype method" (because it's not contained in the generated thrift file). – Gaspare79 Feb 17 '17 at 10:29
  • Yes, the call sequence would be Client (which is a GreetingsNode) ==> Server A (which is managementNode) ==> Server B (which is another Greetings Node). Then Server B establishes a connection with Client and answer the call. – Gaspare79 Feb 17 '17 at 10:38
  • "*The method sayHello(User) of type GreetingsServiceHandler must override or implement a supertype method*" -- Sure, that's the place for your code, the method from above. Maybe the method signature is not correct. As I said, not tested. – JensG Feb 17 '17 at 13:19