0

I have 2 applications one is written in C++ and one is written in Java. the applications communicate by passing byte arrays which represent a serialized object.

each communication object represent a Command and holds the command id and the associated data.

I will use an example to describe my problem. Let's say I have 3 components. one is responsible for the data storage and file system, one is responsible for presenting UI , and one is responsible for security and authorizing users.

One way to access a specific component is by having one entry point that will do a switch case on all the possible commands ids.

pseudo:

    switch(commandId)
{
case SAVE_FILE:
DATASTORAGE::Save();
break;
case LOAD_FILE:
 DATASTORAGE::Load();
 break;
case AUTHORIZE:
 SECURITY_CONTROLLER::authorize();
 break;
case DENY_ACCESS:
 SECURITY_CONTROLLER::denyAccess();
 ...
}

In this case if I need to change the security controller, I need to change this main entry point.

so instead I added an extra field to the communication object, that holds the id of the component that need to process the command. In this case if I want to preform authorize I will do the following:

communicationObject.commandId = AUTHORIZE;
communicationObject.target = SECURITY_CONTROLLER_ID;

And the main will switch case on the possible targets and pass the command id to the specfic component which is responsible for that command.

   switch(target)
{
case SECURITY_CONTROLLER_ID:
SECURITY_CONTROLLER::Process(commandId);
break;
case DATASTORAGE_ID:
 DATASTORAGE::Process(commandId);

 ...
}

I also don't like this new design since it requires that the sender will know the inner structure of the receiver.

on the other hand, if I had direct access to the other application classes (not using byte [] for communication) then the second option will be mapped to something like: SECURITY_CONTROLLER::Authorize(); while the first option will be mapped to something like Main.AUTHORIZE(), which is definitely not the way to go!

what is the best approach in this case?

  • Why aren't you using a specialized tool for performing cross-language RPCs, like Thrift? thrift.apache.org – igorrs Apr 3 '14 at 10:24
1

Since what you're doing is write a custom binary protocol for performing cross-language RPCs, it makes sense to have separate IDs for the components and their commands.

For example: a fixed number of bytes in the beginning of the byte array could be used to specify the component, while the following bytes (another fixed number) would represent the component-specific command. Finally, all of the remaining bytes would represent the associated data.

Your top-level switch would be used to find the component for the component ID. If all components have the same superclass, you can even replace that switch with an std::map that would assing actual objects (components) to component IDs.

But each component would need to have a switch to map command IDs to actual methods.

Note that, in this solution, you can use the same command ID to represent different operations, if they aren't in the same component. This way, you are less likely to need too many bytes for the command-id section of the byte array.

0

The most modular way is to let each receiving module register itself with the main command handler for receiving the commands that it can handle.

Assuming that there is at most one handler for each command, you would get something like this (pseudo code)

class MainCommandHandler
{
public:
    RegisterCommandHandler(CommandID, ICommandHandler*);
    ProcessCommand(Command cmd)
    {
        if (commandHandlers.contains(cmd.id))
        {
            // delegate to the registered handler for this command
            commandHandlers.get(cmd.id)->HandleCommand(cmd);
        }
        else
        {
            // handle unknown commands
        }
    }
private:
    map<CommandID, ICommandHandler*> commandHandlers;
};

class SECURITY_CONTROLLER : public ICommandHandler
{
    someMethodOrConstructor() {
        mainCommandHandler.RegisterCommandHandler(AUTHORIZE, this);
        //... more registrations
    }
    HandleCommand(Command cmd)
    {
        switch(cmd.id)
        {
        case AUTHORIZE: authorize(); break;
        //... other *registered* commands
    }
};

You still have the switch-case on the command ID, but now it is in the components that actually handle the command. If there are additional commands, those components would have to be changed anyway, so the main drawback of your case #1 doesn't exist anymore.

  • But in that case how do I prevent 2 components from having the same command id? – user844541 Apr 3 '14 at 12:08
  • @user844541: By having a central authority that hands out those command id's. As your protocol crosses language boundaries, the most logical place would be (the author of) the document describing the protocol. – Bart van Ingen Schenau Apr 3 '14 at 12:12
  • @user844541: Having a component id encoded within the command id, as suggested by the answer from @igorrs, is another possibility with less bureaucratic overhead.` – Bart van Ingen Schenau Apr 3 '14 at 12:15
  • Is it better to have the component id encoded within the command id than having it in a different field? – user844541 Apr 3 '14 at 12:23
  • @user844541: From a conceptual standpoint, it doesn't really make a difference. Just pick the one you can work with the easiest. – Bart van Ingen Schenau Apr 3 '14 at 12:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.