From 55ecfddbd42a8c75e504d5b63cf9e5e1fe725399 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Wed, 23 Dec 2015 20:01:01 +0000 Subject: Create IceTray and DryIce for bootstrapping and dry running IceBox services --- icetray/Jamfile.jam | 59 +++++++++++++++++++++++++++ icetray/abstractDatabaseClient.cpp | 37 +++++++++++++++++ icetray/abstractDatabaseClient.h | 67 +++++++++++++++++++++++++++++++ icetray/defaultPool.cpp | 16 ++++++++ icetray/defaultPool.h | 15 +++++++ icetray/dryice.cpp | 49 ++++++++++++++++++++++ icetray/dryice.h | 32 +++++++++++++++ icetray/icetrayService.cpp | 40 ++++++++++++++++++ icetray/icetrayService.h | 33 +++++++++++++++ icetray/mockPool.cpp | 21 ++++++++++ icetray/mockPool.h | 20 +++++++++ icetray/test.cpp | 6 +++ icetray/unittests/Jamfile.jam | 41 +++++++++++++++++++ icetray/unittests/testDefaultPool.cpp | 25 ++++++++++++ icetray/unittests/testIceTray.cpp | 47 ++++++++++++++++++++++ icetray/unittests/testIceTrayService.ice | 6 +++ icetray/unittests/testIceTrayService.sql | 5 +++ icetray/unittests/testIceTrayServiceI.cpp | 33 +++++++++++++++ icetray/unittests/testIceTrayServiceI.h | 23 +++++++++++ 19 files changed, 575 insertions(+) create mode 100644 icetray/Jamfile.jam create mode 100644 icetray/abstractDatabaseClient.cpp create mode 100644 icetray/abstractDatabaseClient.h create mode 100644 icetray/defaultPool.cpp create mode 100644 icetray/defaultPool.h create mode 100644 icetray/dryice.cpp create mode 100644 icetray/dryice.h create mode 100644 icetray/icetrayService.cpp create mode 100644 icetray/icetrayService.h create mode 100644 icetray/mockPool.cpp create mode 100644 icetray/mockPool.h create mode 100644 icetray/test.cpp create mode 100644 icetray/unittests/Jamfile.jam create mode 100644 icetray/unittests/testDefaultPool.cpp create mode 100644 icetray/unittests/testIceTray.cpp create mode 100644 icetray/unittests/testIceTrayService.ice create mode 100644 icetray/unittests/testIceTrayService.sql create mode 100644 icetray/unittests/testIceTrayServiceI.cpp create mode 100644 icetray/unittests/testIceTrayServiceI.h diff --git a/icetray/Jamfile.jam b/icetray/Jamfile.jam new file mode 100644 index 0000000..02e7ba3 --- /dev/null +++ b/icetray/Jamfile.jam @@ -0,0 +1,59 @@ +lib adhocutil : : : : /usr/include/adhocutil ; +lib slicer : : : : /usr/include/slicer ; +lib slicer-db : : : : /usr/include/slicer ; +lib Ice ; +lib IceUtil ; +lib pthread ; +lib dl ; +lib IceBox ; +lib dbppcore : : : : /usr/include/dbpp ; +lib boost_system ; +lib boost_thread ; + +build-project unittests ; + +lib icetray : + abstractDatabaseClient.cpp + defaultPool.cpp + icetrayService.cpp + test.cpp + : + adhocutil + dbppcore + Ice + IceUtil + IceBox + pthread + slicer + slicer-db + boost_system + boost_thread + ..//glibmm + -fvisibility=hidden + : : + . + dbppcore + adhocutil + slicer + slicer-db + boost_thread + ; + +lib dryice : + dryice.cpp + mockPool.cpp + : + adhocutil + icetray + Ice + IceUtil + IceBox + pthread + boost_system + boost_thread + dl + -fvisibility=hidden + : : + . + ; + diff --git a/icetray/abstractDatabaseClient.cpp b/icetray/abstractDatabaseClient.cpp new file mode 100644 index 0000000..0d3489c --- /dev/null +++ b/icetray/abstractDatabaseClient.cpp @@ -0,0 +1,37 @@ +#include "abstractDatabaseClient.h" +#include + +template DLL_PUBLIC void AdHoc::Cache::add(const IceTray::AbstractDatabaseClient::CacheKey &, const IceTray::AbstractDatabaseClient::CacheItem &, time_t); +template DLL_PUBLIC const IceTray::AbstractDatabaseClient::CacheItem * AdHoc::Cache::get(const IceTray::AbstractDatabaseClient::CacheKey &) const; + +namespace IceTray { +AbstractDatabaseClient::AbstractDatabaseClient(boost::shared_ptr> d) : + db(d) +{ +} + +template<> +void +AbstractDatabaseClient::bind1(int o, DB::Command * cmd, const std::string & p) +{ + cmd->bindParamS(o, p); +} + +template<> +void +AbstractDatabaseClient::bind1(int o, DB::Command * cmd, const int & p) +{ + cmd->bindParamI(o, p); +} + +void +AbstractDatabaseClient::bind(int, DB::Command *) +{ +} + +void +AbstractDatabaseClient::keyPushParams(CacheKey &) +{ +} +} + diff --git a/icetray/abstractDatabaseClient.h b/icetray/abstractDatabaseClient.h new file mode 100644 index 0000000..817d1ed --- /dev/null +++ b/icetray/abstractDatabaseClient.h @@ -0,0 +1,67 @@ +#ifndef ABSTRACTDATABASECLIENT_H +#define ABSTRACTDATABASECLIENT_H + +#include +#include +#include +#include +#include +#include +#include + +namespace IceTray { +class DLL_PUBLIC AbstractDatabaseClient { + public: + AbstractDatabaseClient(boost::shared_ptr> d); + typedef std::vector CacheKey; + typedef boost::shared_ptr CacheItem; + + template + Domain inline fetch(const Params & ... params) + { + CacheKey key; + key.reserve(sizeof...(Params) + 2); + key.push_back(Sql::hash); + key.push_back(typeid(Domain).hash_code()); + keyPushParams(key, params...); + if (auto cached = cache.get(key)) { + return boost::any_cast(**cached); + } + auto c = db->get(); + auto s = DB::SelectCommandPtr(c->newSelectCommand(Sql::sql)); + bind(0, s.get(), params...); + Domain d = Slicer::DeserializeAny(*s); + s.reset(); + c.release(); + cache.add(key, CacheItem(new boost::any(d)), time(NULL) + 40); + return d; + } + + template + static void inline bind(int offset, DB::Command * cmd, const Param & p, const Params & ... params) + { + bind1(offset, cmd, p); + bind(offset + 1, cmd, params...); + } + + static void bind(int offset, DB::Command * cmd); + + template + static void bind1(int offset, DB::Command * cmd, const Param & p); + + template + static void inline keyPushParams(CacheKey & k, const Param & p, const Params & ... params) + { + k.push_back(std::hash()(p)); + keyPushParams(k, params...); + } + + static void keyPushParams(CacheKey &); + + boost::shared_ptr> db; + AdHoc::Cache cache; +}; +} + +#endif + diff --git a/icetray/defaultPool.cpp b/icetray/defaultPool.cpp new file mode 100644 index 0000000..c3f7e50 --- /dev/null +++ b/icetray/defaultPool.cpp @@ -0,0 +1,16 @@ +#include "defaultPool.h" +#include "icetrayService.h" + +namespace IceTray { + DefaultPool::DefaultPool(const std::string & name, const std::string & type, Ice::PropertiesPtr p) : + DB::ConnectionPool( + p->getPropertyAsIntWithDefault(name + ".Database.PoolMax", 10), + p->getPropertyAsIntWithDefault(name + ".Database.PoolKeep", 2), + p->getPropertyWithDefault(name + ".Database.Type", type), + p->getProperty(name + ".Database.ConnectionString")) + { + } + + FACTORY(DefaultPool, PoolProvider); +} + diff --git a/icetray/defaultPool.h b/icetray/defaultPool.h new file mode 100644 index 0000000..5b06ea2 --- /dev/null +++ b/icetray/defaultPool.h @@ -0,0 +1,15 @@ +#ifndef ICETRAY_DEFAULTPOOL_H +#define ICETRAY_DEFAULTPOOL_H + +#include +#include + +namespace IceTray { + class DefaultPool : public DB::ConnectionPool { + public: + DefaultPool(const std::string & name, const std::string & type, Ice::PropertiesPtr p); + }; +} + +#endif + diff --git a/icetray/dryice.cpp b/icetray/dryice.cpp new file mode 100644 index 0000000..50f85cb --- /dev/null +++ b/icetray/dryice.cpp @@ -0,0 +1,49 @@ +#include "dryice.h" +#include +#include +#include +#include + +namespace IceTray { + typedef IceBox::Service *(* SetupFunction)(Ice::CommunicatorPtr); + + DryIce::DryIce(const Ice::StringSeq & cmdline) + { + void * i = dlsym(NULL, "createIceTrayService"); + BOOST_VERIFY(i); + auto sf = (SetupFunction)i; + BOOST_VERIFY(sf); + Ice::StringSeq args; + Ice::InitializationData id; + id.properties = Ice::createProperties(); + id.properties->setProperty("DryIce.Endpoints", "tcp -p 9002"); + id.properties->setProperty("DryIce.PoolProvider", "MockPool"); + id.properties->parseCommandLineOptions("", cmdline); + ic = Ice::initialize(args, id); + s = sf(nullptr); + s->start("DryIce", ic, {}); + } + + DryIce::~DryIce() + { + if (s) { + s->stop(); + s = NULL; + } + if (ic) { + ic->destroy(); + ic = NULL; + } + } + + DryIceClient::DryIceClient() + { + ic = Ice::initialize(); + } + + DryIceClient::~DryIceClient() + { + ic->destroy(); + } +} + diff --git a/icetray/dryice.h b/icetray/dryice.h new file mode 100644 index 0000000..f81a48c --- /dev/null +++ b/icetray/dryice.h @@ -0,0 +1,32 @@ +#ifndef ICETRAY_TESTSETUP_H +#define ICETRAY_TESTSETUP_H + +#include +#include +#include + +namespace IceTray { + class DLL_PUBLIC DryIce { + public: + DryIce(const Ice::StringSeq & = Ice::StringSeq()); + DryIce(const DryIce &) = delete; + virtual ~DryIce(); + + void operator=(const DryIce &) = delete; + + protected: + Ice::CommunicatorPtr ic; + IceBox::ServicePtr s; + }; + + class DLL_PUBLIC DryIceClient { + public: + DryIceClient(); + virtual ~DryIceClient(); + + Ice::CommunicatorPtr ic; + }; +} + +#endif + diff --git a/icetray/icetrayService.cpp b/icetray/icetrayService.cpp new file mode 100644 index 0000000..335c08b --- /dev/null +++ b/icetray/icetrayService.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include "icetrayService.h" + +namespace IceTray { + void Service::start(const std::string & name, const Ice::CommunicatorPtr & ic, const Ice::StringSeq & args) + { + adp = ic->createObjectAdapter(name); + addObjects(name, ic, args, adp); + adp->activate(); + } + + void Service::stop() + { + adp->deactivate(); + adp->destroy(); + } + + boost::shared_ptr> Service::getConnectionPool(const Ice::CommunicatorPtr & ic, const std::string & type, const std::string & name) + { + auto p = ic->getProperties(); + return boost::shared_ptr>(PoolProvider::createNew( + p->getPropertyWithDefault("DryIce.PoolProvider", "DefaultPool"), + name, type, p)); + } +} + +extern "C" { + DLL_PUBLIC + IceBox::Service * + createIceTrayService(Ice::CommunicatorPtr) + { + return IceTray::ServiceFactory::createNew("default"); + } +} + +INSTANTIATEVOIDFACTORY(IceTray::Service); +INSTANTIATEFACTORY(IceTray::PoolType, const std::string &, const std::string &, Ice::PropertiesPtr); + diff --git a/icetray/icetrayService.h b/icetray/icetrayService.h new file mode 100644 index 0000000..10a1700 --- /dev/null +++ b/icetray/icetrayService.h @@ -0,0 +1,33 @@ +#ifndef ICETRAY_SERVICE_H +#define ICETRAY_SERVICE_H + +#include +#include +#include +#include + +namespace IceTray { + typedef AdHoc::ResourcePool PoolType; + typedef boost::shared_ptr PoolTypePtr; + + class DLL_PUBLIC Service : public IceBox::Service, public AdHoc::AbstractPluginImplementation { + public: + typedef boost::shared_ptr DBCPoolPtr; + + virtual void addObjects(const std::string & name, const Ice::CommunicatorPtr & ic, const Ice::StringSeq &, const Ice::ObjectAdapterPtr &) = 0; + + void start(const std::string & name, const Ice::CommunicatorPtr & ic, const Ice::StringSeq & args) override; + + void stop() override; + + PoolTypePtr getConnectionPool(const Ice::CommunicatorPtr & ic, const std::string & type, const std::string & prefix); + + Ice::ObjectAdapterPtr adp; + }; + + typedef AdHoc::Factory ServiceFactory; + typedef AdHoc::Factory PoolProvider; +} + +#endif + diff --git a/icetray/mockPool.cpp b/icetray/mockPool.cpp new file mode 100644 index 0000000..3035949 --- /dev/null +++ b/icetray/mockPool.cpp @@ -0,0 +1,21 @@ +#include "mockPool.h" +#include "icetrayService.h" +#include + +namespace IceTray { + MockPool::MockPool(const std::string & name, const std::string &, Ice::PropertiesPtr p) : + AdHoc::ResourcePool( + p->getPropertyAsIntWithDefault(name + ".Database.PoolMax", 10), + p->getPropertyAsIntWithDefault(name + ".Database.PoolKeep", 2)), + name(name) + { + } + + DB::Connection * MockPool::createResource() const + { + return DB::MockDatabase::openConnectionTo(name); + } + + FACTORY(MockPool, PoolProvider); +}; + diff --git a/icetray/mockPool.h b/icetray/mockPool.h new file mode 100644 index 0000000..6a2df85 --- /dev/null +++ b/icetray/mockPool.h @@ -0,0 +1,20 @@ +#ifndef ICETRAY_MOCKPOOL_H +#define ICETRAY_MOCKPOOL_H + +#include +#include +#include + +namespace IceTray { + class MockPool : public AdHoc::ResourcePool { + public: + MockPool(const std::string & name, const std::string &, Ice::PropertiesPtr p); + + DB::Connection * createResource() const override; + + const std::string name; + }; +} + +#endif + diff --git a/icetray/test.cpp b/icetray/test.cpp new file mode 100644 index 0000000..5ccaa01 --- /dev/null +++ b/icetray/test.cpp @@ -0,0 +1,6 @@ +#include +#include + +template class std::shared_ptr; +INSTANTIATEFACTORY(int, std::shared_ptr); + diff --git a/icetray/unittests/Jamfile.jam b/icetray/unittests/Jamfile.jam new file mode 100644 index 0000000..b1a9e40 --- /dev/null +++ b/icetray/unittests/Jamfile.jam @@ -0,0 +1,41 @@ +import testing ; + +lib boost_utf : : boost_unit_test_framework ; +lib boost_filesystem ; +lib dbpp-postgresql : : : : /usr/include/dbpp-postgresql ; + +path-constant me : . ; + +alias testCommon : : : : + ROOT=\"$(me)\" + ..//adhocutil + ..//dryice + ..//icetray + ..//IceBox + ..//IceUtil + ..//Ice + ..//pthread + boost_utf + ..//boost_system + BOOST_TEST_DYN_LINK + ; +run + testIceTray.cpp + testIceTrayService.ice + testIceTrayServiceI.cpp + : + testIceTrayService.sql + : : + testCommon + boost_filesystem + dbpp-postgresql + : + testIceTray ; + +run + testDefaultPool.cpp + : : : + testCommon + : + testDefaultPool ; + diff --git a/icetray/unittests/testDefaultPool.cpp b/icetray/unittests/testDefaultPool.cpp new file mode 100644 index 0000000..7dfa360 --- /dev/null +++ b/icetray/unittests/testDefaultPool.cpp @@ -0,0 +1,25 @@ +#define BOOST_TEST_MODULE TestIceTray +#include + +#include +#include +#include +#include +#include +#include +#include + +BOOST_AUTO_TEST_CASE( defaultPool ) +{ + auto p = Ice::createProperties(); + p->setProperty("testcase.Database.ConnectionString", "host=randomdan.homeip.net user=gentoo"); + auto pool = IceTray::PoolProvider::createNew("DefaultPool", "testcase", "postgresql", p); + BOOST_REQUIRE(pool); + BOOST_REQUIRE_EQUAL(0, pool->inUseCount()); + { + auto c = pool->get(); + c->ping(); + } + delete pool; +} + diff --git a/icetray/unittests/testIceTray.cpp b/icetray/unittests/testIceTray.cpp new file mode 100644 index 0000000..b3eb9dc --- /dev/null +++ b/icetray/unittests/testIceTray.cpp @@ -0,0 +1,47 @@ +#define BOOST_TEST_MODULE TestIceTray +#include + +#include +#include +#include "testIceTrayServiceI.h" + +#include +#include + +class Service : public IceTray::DryIce, PQ::Mock { + public: + Service() : + PQ::Mock("user=postgres dbname=postgres", "icetraydb", { + rootDir / "testIceTrayService.sql" + }) + { + } +}; + +BOOST_GLOBAL_FIXTURE( Service ); + +class Client : public IceTray::DryIceClient { + public: + Client() : + p(TestIceTray::TestIceTrayServicePrx::checkedCast(ic->stringToProxy("test:tcp -p 9002"))) + { + } + TestIceTray::TestIceTrayServicePrx p; +}; + +BOOST_FIXTURE_TEST_SUITE( client, Client ); + +BOOST_AUTO_TEST_CASE( startup ) +{ + BOOST_REQUIRE(ic); +} + +BOOST_AUTO_TEST_CASE( services ) +{ + BOOST_REQUIRE(p); + p->ice_ping(); + p->method(); +} + +BOOST_AUTO_TEST_SUITE_END(); + diff --git a/icetray/unittests/testIceTrayService.ice b/icetray/unittests/testIceTrayService.ice new file mode 100644 index 0000000..9c17127 --- /dev/null +++ b/icetray/unittests/testIceTrayService.ice @@ -0,0 +1,6 @@ +module TestIceTray { + interface TestIceTrayService { + void method(); + }; +}; + diff --git a/icetray/unittests/testIceTrayService.sql b/icetray/unittests/testIceTrayService.sql new file mode 100644 index 0000000..7d9a7e7 --- /dev/null +++ b/icetray/unittests/testIceTrayService.sql @@ -0,0 +1,5 @@ +CREATE TABLE testTable ( + id int, + name text, + primary key(id)); + diff --git a/icetray/unittests/testIceTrayServiceI.cpp b/icetray/unittests/testIceTrayServiceI.cpp new file mode 100644 index 0000000..501e1b1 --- /dev/null +++ b/icetray/unittests/testIceTrayServiceI.cpp @@ -0,0 +1,33 @@ +#include "testIceTrayServiceI.h" +#include +#include +#include +#include + +namespace TestIceTray { + struct TestSql { + static const std::string sql; + static const std::size_t hash; + }; + const std::string TestSql::sql("SELECT COUNT(*) FROM testTable WHERE id = ? AND name = ?"); + const std::size_t TestSql::hash(std::hash()(sql)); + TestIceTrayServiceI::TestIceTrayServiceI(boost::shared_ptr> d) : + IceTray::AbstractDatabaseClient(d) + { + } + + void TestIceTrayServiceI::method(const Ice::Current &) + { + Ice::Int id = 4; + std::string name = "test"; + BOOST_VERIFY((fetch(id, name)) == (fetch(id, name))); + } + + void TestService::addObjects(const std::string &, const Ice::CommunicatorPtr & ic, const Ice::StringSeq &, const Ice::ObjectAdapterPtr & adp) + { + adp->add(new TestIceTray::TestIceTrayServiceI(getConnectionPool(ic, "postgresql", "icetraydb")), ic->stringToIdentity("test")); + } + + NAMEDFACTORY("default", TestService, IceTray::ServiceFactory); +} + diff --git a/icetray/unittests/testIceTrayServiceI.h b/icetray/unittests/testIceTrayServiceI.h new file mode 100644 index 0000000..6bb5503 --- /dev/null +++ b/icetray/unittests/testIceTrayServiceI.h @@ -0,0 +1,23 @@ +#ifndef TEST_ICETRAY_SERVICE_IMPL_H +#define TEST_ICETRAY_SERVICE_IMPL_H + +#include +#include +#include + +namespace TestIceTray { + class TestIceTrayServiceI : IceTray::AbstractDatabaseClient, public TestIceTrayService { + public: + TestIceTrayServiceI(boost::shared_ptr> db); + + void method(const Ice::Current &) override; + }; + + class TestService : public IceTray::Service { + public: + void addObjects(const std::string &, const Ice::CommunicatorPtr &, const Ice::StringSeq &, const Ice::ObjectAdapterPtr &) override; + }; +} + +#endif + -- cgit v1.2.3