libt2n: (gerd) add command groups
authorGerd v. Egidy <gerd.von.egidy@intra2net.com>
Mon, 30 Oct 2006 18:43:58 +0000 (18:43 +0000)
committerGerd v. Egidy <gerd.von.egidy@intra2net.com>
Mon, 30 Oct 2006 18:43:58 +0000 (18:43 +0000)
src/command_server.cpp
src/command_server.hxx
test/Makefile.am
test/cmdgroup.cpp [new file with mode: 0644]

index 55c4cc1..cc66de4 100644 (file)
@@ -54,7 +54,7 @@ void command_server::send_hello(unsigned int conn_id)
 {
     server_connection* sc=s.get_connection(conn_id);
 
-    ostringstream hello;
+    std::ostringstream hello;
 
     hello << "T2Nv" << PROTOCOL_VERSION << ';';
 
@@ -72,7 +72,7 @@ void command_server::handle_packet(const std::string& packet, server_connection*
     OBJLOGSTREAM(s,debug,"handling packet from connection " << conn->get_id());
 
     // deserialize packet
-    istringstream ifs(packet);
+    std::istringstream ifs(packet);
     boost::archive::binary_iarchive ia(ifs);
     command_container ccont;
     result_container res;
@@ -83,7 +83,7 @@ void command_server::handle_packet(const std::string& packet, server_connection*
     }
     catch(boost::archive::archive_exception &e)
     {
-        ostringstream msg;
+        std::ostringstream msg;
         msg << "archive_exception while deserializing on server-side, "
                "code " << e.code << " (" << e.what() << ")";
         res.set_exception(new t2n_serialization_error(msg.str()));
@@ -101,8 +101,7 @@ void command_server::handle_packet(const std::string& packet, server_connection*
             xo << BOOST_SERIALIZATION_NVP(ccont);
         }
 
-        // TODO: cast to command subclass (template)
-        command *cmd=ccont.get_command();
+        command* cmd=cast_command(ccont.get_command());
 
         if (cmd)
         {
@@ -116,10 +115,17 @@ void command_server::handle_packet(const std::string& packet, server_connection*
                 { throw; }
         }
         else
-            throw logic_error("uninitialized command called");
+        {
+            std::ostringstream msg;
+            if (ccont.get_command()!=NULL)
+                msg << "illegal command of type " << typeid(ccont.get_command()).name() << " called";
+            else
+                msg << "NULL command called";
+            res.set_exception(new t2n_command_error(msg.str()));
+        }
     }
 
-    ostringstream ofs;
+    std::ostringstream ofs;
     boost::archive::binary_oarchive oa(ofs);
 
     try
@@ -128,7 +134,7 @@ void command_server::handle_packet(const std::string& packet, server_connection*
     }
     catch(boost::archive::archive_exception &e)
     {
-        ostringstream msg;
+        std::ostringstream msg;
         msg << "archive_exception while serializing on server-side, "
                "code " << e.code << " (" << e.what() << ")";
         res.set_exception(new t2n_serialization_error(msg.str()));
@@ -156,7 +162,7 @@ void command_server::handle(long long usec_timeout, long long* usec_timeout_rema
 {
     if (s.fill_buffer(usec_timeout,usec_timeout_remaining))
     {
-        string packet;
+        std::string packet;
         unsigned int conn_id;
 
         while (s.get_packet(packet,conn_id))
index 1926733..3c00262 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef __LIBT2N_COMMAND_SERVER
 #define __LIBT2N_COMMAND_SERVER
 
+#include "command.hxx"
 #include "server.hxx"
 
 namespace libt2n
@@ -32,6 +33,10 @@ class command_server
 
         void handle_packet(const std::string& packet, server_connection* conn);
 
+    protected:
+        virtual command* cast_command(command* input)
+            { return input; }
+
     public:
         command_server(server& _s);
 
@@ -40,7 +45,24 @@ class command_server
         void send_hello(unsigned int conn_id);
 };
 
-}
+template<class T, class B> struct Derived_from {
+        static void constraints(T* p) { B* pb = p; }
+        Derived_from() { void(*p)(T*) = constraints; }
+};
+
+template<class COMMAND_GROUP>
+class group_command_server : public command_server
+{
+    private:
+        virtual command* cast_command(command* input)
+            { return dynamic_cast<COMMAND_GROUP*>(input); }
 
+    public:
+        group_command_server(server& _s)
+            : command_server(_s)
+        { Derived_from<COMMAND_GROUP,command>(); }
+};
+
+}
 #endif
 
index bc1f77b..d41e6e4 100644 (file)
@@ -5,6 +5,6 @@ check_PROGRAMS = test
 test_LDADD = $(top_builddir)/src/libt2n.la @BOOST_SERIALIZATION_LIB@ \
        @BOOST_LDFLAGS@ @CPPUNIT_LIBS@
 test_SOURCES = test.cpp comm.cpp simplecmd.cpp callback.cpp hello.cpp \
-       timeout.cpp serialize.cpp
+       timeout.cpp serialize.cpp cmdgroup.cpp
 
 TESTS = test
diff --git a/test/cmdgroup.cpp b/test/cmdgroup.cpp
new file mode 100644 (file)
index 0000000..fc5952b
--- /dev/null
@@ -0,0 +1,277 @@
+/***************************************************************************
+ *   Copyright (C) 2004 by Intra2net AG                                    *
+ *   info@intra2net.com                                                    *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <boost/archive/binary_oarchive.hpp>
+#include <boost/archive/binary_iarchive.hpp>
+#include <boost/archive/xml_oarchive.hpp>
+#include <boost/archive/xml_iarchive.hpp>
+#include <boost/serialization/serialization.hpp>
+#include <boost/serialization/export.hpp>
+
+#include <container.hxx>
+#include <socket_client.hxx>
+#include <socket_server.hxx>
+#include <command_client.hxx>
+#include <command_server.hxx>
+
+using namespace std;
+using namespace CppUnit;
+using namespace libt2n;
+
+string testfunc4(const string& str)
+{
+    if (str=="throw")
+        throw libt2n::t2n_runtime_error("throw me around");
+    string ret(str);
+    ret+=", testfunc() was here";
+    return ret;
+}
+
+class testfunc4_res : public libt2n::result
+{
+    private:
+        string res;
+
+        friend class boost::serialization::access;
+        template<class Archive>
+        void serialize(Archive & ar, const unsigned int version)
+        {
+            ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
+            ar & BOOST_SERIALIZATION_NVP(res);
+        }
+
+    public:
+        testfunc4_res()
+            { }
+
+        testfunc4_res(const string& str)
+        {
+            res=str;
+        }
+
+        string get_data()
+        {
+            return res;
+        }
+};
+
+class cmd_group_a : public command
+{
+    private:
+        friend class boost::serialization::access;
+        template<class Archive>
+        void serialize(Archive & ar, const unsigned int version)
+        {
+            ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
+        }
+};
+
+class cmd_group_b : public command
+{
+    private:
+        friend class boost::serialization::access;
+        template<class Archive>
+        void serialize(Archive & ar, const unsigned int version)
+        {
+            ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
+        }
+};
+
+class testfunc4a_cmd : public cmd_group_a
+{
+    private:
+        string param;
+
+        friend class boost::serialization::access;
+        template<class Archive>
+        void serialize(Archive & ar, const unsigned int version)
+        {
+            ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_a);
+            ar & BOOST_SERIALIZATION_NVP(param);
+        }
+
+    public:
+        testfunc4a_cmd()
+            { }
+
+        testfunc4a_cmd(const string& str)
+        {
+            param=str;
+        }
+
+        libt2n::result* operator()()
+        {
+            return new testfunc4_res(testfunc4(param));
+        }
+};
+
+class testfunc4b_cmd : public cmd_group_b
+{
+    private:
+        string param;
+
+        friend class boost::serialization::access;
+        template<class Archive>
+        void serialize(Archive & ar, const unsigned int version)
+        {
+            ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(cmd_group_b);
+            ar & BOOST_SERIALIZATION_NVP(param);
+        }
+
+    public:
+        testfunc4b_cmd()
+            { }
+
+        testfunc4b_cmd(const string& str)
+        {
+            param=str;
+        }
+
+        libt2n::result* operator()()
+        {
+            return new testfunc4_res(testfunc4(param));
+        }
+};
+
+
+BOOST_CLASS_EXPORT(testfunc4_res)
+BOOST_CLASS_EXPORT(cmd_group_a)
+BOOST_CLASS_EXPORT(cmd_group_b)
+BOOST_CLASS_EXPORT(testfunc4a_cmd)
+BOOST_CLASS_EXPORT(testfunc4b_cmd)
+
+class test_cmdgroup : public TestFixture
+{
+    CPPUNIT_TEST_SUITE(test_cmdgroup);
+
+    CPPUNIT_TEST(GroupOk);
+    CPPUNIT_TEST(WrongGroup);
+
+    CPPUNIT_TEST_SUITE_END();
+
+    public:
+
+    void setUp()
+    { }
+
+    void tearDown()
+    { }
+
+    void GroupOk()
+    {
+        pid_t pid;
+
+        switch(pid=fork())
+        {
+            case -1:
+            {
+                CPPUNIT_FAIL("fork error");
+                break;
+            }
+            case 0:
+            // child
+            {
+                socket_server ss("./socket");
+                group_command_server<cmd_group_a> cs(ss);
+
+                // max 10 sec
+                for (int i=0; i < 10; i++)
+                    cs.handle(1000000);
+
+                // don't call atexit and stuff
+                _exit(0);
+            }
+
+            default:
+            // parent
+            {
+                // wait till server is up
+                sleep(1);
+                socket_client_connection sc("./socket");
+                command_client cc(sc);
+
+                result_container rc;
+                cc.send_command(new testfunc4a_cmd("hello"),rc);
+
+                string ret=dynamic_cast<testfunc4_res*>(rc.get_result())->get_data();
+
+                CPPUNIT_ASSERT_EQUAL(string("hello, testfunc() was here"),ret);
+            }
+        }
+    }
+
+    void WrongGroup()
+    {
+        pid_t pid;
+
+        switch(pid=fork())
+        {
+            case -1:
+            {
+                CPPUNIT_FAIL("fork error");
+                break;
+            }
+            case 0:
+            // child
+            {
+                socket_server ss("./socket");
+                group_command_server<cmd_group_b> cs(ss);
+
+                // max 10 sec
+                for (int i=0; i < 10; i++)
+                    cs.handle(1000000);
+
+                // don't call atexit and stuff
+                _exit(0);
+            }
+
+            default:
+            // parent
+            {
+                // wait till server is up
+                sleep(1);
+                socket_client_connection sc("./socket");
+                command_client cc(sc);
+
+                result_container rc;
+                cc.send_command(new testfunc4a_cmd("hello"),rc);
+
+                string ret;
+
+                try
+                {
+                    ret=dynamic_cast<testfunc4_res*>(rc.get_result())->get_data();
+                }
+                catch(t2n_command_error &e)
+                    { ret=e.what(); }
+                catch(...)
+                    { throw; }
+
+                string expected_what="illegal command of type ";
+
+                CPPUNIT_ASSERT_EQUAL(expected_what,ret.substr(0,expected_what.size()));
+            }
+        }
+    }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_cmdgroup);