Click here to Skip to main content
15,892,768 members
Articles / Programming Languages / C++

RCF - Interprocess Communication for C++

Rate me:
Please Sign up or sign in to vote.
4.94/5 (147 votes)
25 Oct 2011CPOL20 min read 4.6M   8.4K   331  
A server/client IPC framework, using the C++ preprocessor as an IDL compiler.
#include <string>

#include <RCF/test/TestMinimal.hpp>

#include <RCF/Idl.hpp>
#include <RCF/RcfServer.hpp>
#include <RCF/ObjectFactoryService.hpp>
#include <RCF/TcpClientTransport.hpp>
#include <RCF/TcpEndpoint.hpp>
#include <RCF/test/PrintTestHeader.hpp>
#include <RCF/test/TransportFactories.hpp>
#include <RCF/util/CommandLine.hpp>
#include <RCF/util/PortNumbers.hpp>
#include <RCF/util/Platform/OS/Sleep.hpp>
#include <RCF/util/TraceCommandLineOption.hpp>

namespace Test_ObjectFactoryService {

    class Echo
    {
    public:
        Echo() : mNVal(RCF_DEFAULT_INIT), mBVal(RCF_DEFAULT_INIT), mDVal(RCF_DEFAULT_INIT), mSVal()
        {}

        // I_Echo
        std::string echo(const std::string &s)
        {
            sLog = s;
            return s;
        }
        static std::string sLog;

        void setVal(int val)
        {
            mNVal = val;
        }

        int getVal()
        {
            return mNVal;
        }

        // I_X1
        void setVal(bool val)
        {
            mBVal = val;
        }

        bool getVal(bool)
        {
            return mBVal;
        }

        // I_X2
        void setVal(double val)
        {
            mDVal = val;
        }

        double getVal(double)
        {
            return mDVal;
        }

        // I_X3
        void setVal(std::string val)
        {
            mSVal = val;
        }

        std::string getVal(std::string)
        {
            return mSVal;
        }

    private:
        int mNVal;
        bool mBVal;
        double mDVal;
        std::string mSVal;
    };

    std::string Echo::sLog;

    RCF_BEGIN(I_Echo, "I_Echo")
        RCF_METHOD_R1(std::string, echo, const std::string &)
        RCF_METHOD_V1(void, setVal, int)
        RCF_METHOD_R0(int, getVal)
    RCF_END(I_Echo)

    RCF_BEGIN(I_X1, "I_X1")
        RCF_METHOD_V1(void, setVal, bool)
        RCF_METHOD_R1(bool, getVal, bool)
    RCF_END(I_X1)

    RCF_BEGIN(I_X2, "I_X2")
        RCF_METHOD_V1(void, setVal, double)
        RCF_METHOD_R1(double, getVal, double)
    RCF_END(I_X2)

    RCF_BEGIN(I_X3, "I_X3")
        RCF_METHOD_V1(void, setVal, std::string)
        RCF_METHOD_R1(std::string, getVal, std::string)
    RCF_END(I_X3)

    void exhaustTokenSpace(int numberOfTokens, RCF::I_RcfClient &client)
    {
        int count = 0;
        for (int j=0; j<numberOfTokens+1;++j)
        {
            //if (RCF::createRemoteObject<I_Echo>(client,"Echo"))
            if (tryCreateRemoteObject<I_Echo>(client,"Echo"))
            {
                ++count;
            }
            std::cout << client.getClientStub().getTargetToken() << std::endl;
        }
        BOOST_CHECK(count == numberOfTokens);
    }

} // namespace Test_ObjectFactoryService

int RCF_TEST_MAIN(int argc, char **argv)
{
    printTestHeader(__FILE__);

    using namespace Test_ObjectFactoryService;

    util::CommandLineOption<int> clNumberOfTokens("tokens", 5, "number of tokens in object factory");
    util::CommandLineOption<int> clClientStubTimeoutS("timeout", 2, "server object timeout in seconds");
    util::CommandLineOption<int> clWaitIntervalS("wait", 5, "how long to wait for object factory to run cleanup");
    util::CommandLineOption<int> clCleanupIntervalS("cleanup", 2, "object factory cleanup interval in seconds");
    util::CommandLine::getSingleton().parse(argc, argv);

    std::string ip = "localhost";
    unsigned int numberOfTokens = clNumberOfTokens;
    unsigned int clientStubTimeoutS = clClientStubTimeoutS;
    unsigned int waitIntervalS = clWaitIntervalS;
    unsigned int cleanupIntervalS = clCleanupIntervalS;

    std::string s0 = "something special";

    {
        int port = util::PortNumbers::getSingleton().getNext();
        RCF::TcpEndpoint serverEndpoint(port);
        RCF::TcpEndpoint clientEndpoint(ip, port);

        RCF::RcfServer server( serverEndpoint );

        RCF::ObjectFactoryServicePtr objectFactoryServicePtr(
            new RCF::ObjectFactoryService(
                numberOfTokens, clientStubTimeoutS, cleanupIntervalS) );

        //objectFactoryServicePtr->bind<I_Echo, Echo>("Echo");
        objectFactoryServicePtr->bind( (I_Echo*) 0,  (Echo**) 0, "Echo");

        server.addService( objectFactoryServicePtr );

        server.start();

        RcfClient<I_Echo> client( clientEndpoint );

        //bool ok = RCF::createRemoteObject<I_Echo>(client, "Echo");
        bool ok = tryCreateRemoteObject<I_Echo>(client, "Echo");
        BOOST_CHECK(ok);
        std::string s = client.echo(s0);
        RCF::Token token = client.getClientStub().getTargetToken();
        BOOST_CHECK( s == s0 );
        BOOST_CHECK(token != RCF::Token());

        // check that we're ok w.r.t. server restarting
        s = client.echo(s0);
        server.stop();
        server.start();
        s = client.echo(s0);

        // check that the object isn't deleted before the timeout
        client.getClientStub().disconnect();
        s == client.echo(s0);

        // check that the object isn't deleted while the connection is alive
        Platform::OS::SleepMs(waitIntervalS*1000);
        s = client.echo(s0);

        for (int j=0; j<numberOfTokens-1; ++j)
        {
            //ok = RCF::createRemoteObject<I_Echo>(client, "Echo");
            ok = tryCreateRemoteObject<I_Echo>(client, "Echo");
            BOOST_CHECK(ok);
        }

        //ok = RCF::createRemoteObject<I_Echo>(client, "Echo");
        ok = tryCreateRemoteObject<I_Echo>(client, "Echo");
        BOOST_CHECK(!ok);

        std::cout << "Waiting for " << waitIntervalS << " seconds...\n";
        Platform::OS::Sleep(waitIntervalS);
        exhaustTokenSpace(numberOfTokens, client);

        server.removeService(objectFactoryServicePtr);
        server.addService(objectFactoryServicePtr);

        std::cout << "Waiting for " << waitIntervalS << " seconds...\n";
        Platform::OS::Sleep(waitIntervalS);
        exhaustTokenSpace(numberOfTokens, client);

        server.removeService(objectFactoryServicePtr);
        objectFactoryServicePtr.reset( new RCF::ObjectFactoryService(
            numberOfTokens, clientStubTimeoutS, cleanupIntervalS) );
        objectFactoryServicePtr->bind( (I_Echo*) 0,  (Echo**) 0, "Echo");
        server.addService(objectFactoryServicePtr);

        std::cout << "Waiting for " << waitIntervalS << " seconds...\n";
        Platform::OS::Sleep(waitIntervalS);
        exhaustTokenSpace(numberOfTokens, client);
    }

    {
        // try moving an object factory service from one server to another

        RCF::ObjectFactoryServicePtr objectFactoryServicePtr(
            new RCF::ObjectFactoryService(
                numberOfTokens, clientStubTimeoutS, cleanupIntervalS));
        objectFactoryServicePtr->bind( (I_Echo*) 0,  (Echo**) 0, "Echo");

        std::string ip = "localhost";
        int port1 = util::PortNumbers::getSingleton().getNext();
        int port2 = util::PortNumbers::getSingleton().getNext();
        int val = 17;

        RCF::Token token;

        {
            RCF::TcpEndpoint endpoint(port1);
            RCF::RcfServer server(endpoint);
            server.start();
            server.addService(objectFactoryServicePtr);

            RcfClient<I_Echo> client( RCF::ClientTransportAutoPtr( new RCF::TcpClientTransport("127.0.0.1", port1)));
            bool ok = tryCreateRemoteObject<I_Echo>(client, "Echo");
            BOOST_CHECK(ok);
            token = client.getClientStub().getTargetToken();
            client.setVal(val);
            exhaustTokenSpace(numberOfTokens-1, client);
        }
        {
            RCF::TcpEndpoint endpoint(port2);
            RCF::RcfServer server(endpoint);
            server.start();
            server.addService(objectFactoryServicePtr);

            {
                RcfClient<I_Echo> client( RCF::ClientTransportAutoPtr( new RCF::TcpClientTransport("127.0.0.1", port2)));
                client.getClientStub().setTargetToken(token);
                int myVal = client.getVal();
                BOOST_CHECK(val == myVal);
            }

            Platform::OS::Sleep(waitIntervalS);
            try
            {
                // the server object should have been cleaned up by now, so the next call
                // should result in an exception
                RcfClient<I_Echo> client( RCF::ClientTransportAutoPtr( new RCF::TcpClientTransport("127.0.0.1", port2)));
                client.getClientStub().setTargetToken(token);
                int myVal = client.getVal();
                BOOST_CHECK(1==0);
            }
            catch(const RCF::Exception &e)
            {
                std::string strErr = RCF::getErrorString(e.getError());
                std::string what = e.what();
                std::string context = e.getContext();
                BOOST_CHECK(1==1);
            }

        }

    }

    // faceted objects

    // TODO: testing of
    // 1) heavy concurrent access to different interfaces
    // 2) user friendly reporting of interface-not-found errors

    {
        RCF::ObjectFactoryServicePtr objectFactoryServicePtr(
            new RCF::ObjectFactoryService(
            numberOfTokens, 60, cleanupIntervalS));

        objectFactoryServicePtr->bind( (I_Echo*) 0,  (Echo**) 0, "Echo");
        objectFactoryServicePtr->bind( (I_Echo*) 0,  (I_X1*) 0,  (Echo**) 0, "Echo");
        objectFactoryServicePtr->bind( (I_Echo*) 0,  (I_X1*) 0,  (I_X2*) 0,  (Echo**) 0, "Echo");
        objectFactoryServicePtr->bind( (I_Echo*) 0,  (I_X1*) 0,  (I_X2*) 0,  (I_X3*) 0,  (Echo**) 0, "Echo");

        std::string ip = "localhost";
        int port1 = util::PortNumbers::getSingleton().getNext();

        int nVal = 17;
        bool bVal = true;
        double dVal = 3.14;
        std::string sVal = "seventeen";

        RCF::Token token;

        RCF::TcpEndpoint endpoint(port1);
        RCF::RcfServer server(endpoint);

        server.start();
        server.addService(objectFactoryServicePtr);

        {
            RcfClient<I_Echo> client(
                RCF::ClientTransportAutoPtr(
                new RCF::TcpClientTransport("127.0.0.1", port1)));
            bool ok = tryCreateRemoteObject<I_Echo>(client, "Echo");
            BOOST_CHECK(ok);
            token = client.getClientStub().getTargetToken();
        }

        {
            RcfClient<I_Echo> client(
                RCF::ClientTransportAutoPtr(
                    new RCF::TcpClientTransport("127.0.0.1", port1)));

            client.getClientStub().setTargetToken(token);
            client.setVal(nVal);
            BOOST_CHECK(client.getVal() == nVal);
        }

        {
            RcfClient<I_X1> client(
                RCF::ClientTransportAutoPtr(
                    new RCF::TcpClientTransport("127.0.0.1", port1)));

            client.getClientStub().setTargetToken(token);
            client.setVal(bVal);
            BOOST_CHECK(client.getVal(bool()) == bVal);
        }

        {
            RcfClient<I_X2> client(
                RCF::ClientTransportAutoPtr(
                    new RCF::TcpClientTransport("127.0.0.1", port1)));

            client.getClientStub().setTargetToken(token);
            client.setVal(dVal);
            BOOST_CHECK(client.getVal(double()) == dVal);
        }

        {
            RcfClient<I_X3> client(
                RCF::ClientTransportAutoPtr(
                    new RCF::TcpClientTransport("127.0.0.1", port1)));

            client.getClientStub().setTargetToken(token);
            client.setVal(sVal);
            BOOST_CHECK(client.getVal(std::string()) == sVal);
        }

        {
            // test deletion of remote objects
            RcfClient<I_X3> client(
                RCF::ClientTransportAutoPtr(
                new RCF::TcpClientTransport("127.0.0.1", port1)));

            client.getClientStub().setTargetToken(token);
            client.setVal(sVal);
            BOOST_CHECK(client.getVal(std::string()) == sVal);

            //RCF::deleteRemoteObject(client);
            client.getClientStub().deleteRemoteObject();

            try
            {
                client.getVal(std::string());
                BOOST_CHECK(1==0);
            }
            catch(const RCF::Exception &)
            {
                BOOST_CHECK(1==1);
            }

            //RCF::createRemoteSessionObject<I_X3>(client, "Echo");
            client.getClientStub().createRemoteSessionObject("Echo");
            client.setVal(sVal);
            BOOST_CHECK(client.getVal(std::string()) == sVal);
            //RCF::deleteRemoteSessionObject(client);
            client.getClientStub().deleteRemoteSessionObject();

            try
            {
                client.getVal(std::string());
                BOOST_CHECK(1==0);
            }
            catch(const RCF::Exception &)
            {
                BOOST_CHECK(1==1);
            }

        }

    }

    return boost::exit_success;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Australia Australia
Software developer, from Sweden and now living in Canberra, Australia, working on distributed C++ applications. When he is not programming, Jarl enjoys skiing and playing table tennis. He derives immense satisfaction from referring to himself in third person.

Comments and Discussions