From 84f6969c6c261959da907eb3b9f9c5de610e160d Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Tue, 12 Mar 2019 00:19:30 +0200 Subject: [PATCH 01/17] Some #include improvements --- include/lib/graft/inout.h | 74 +---------------- include/lib/graft/jsonrpc.h | 1 + include/lib/graft/requests/health_check.h | 1 + include/lib/graft/requests/requestdefines.h | 2 +- include/lib/graft/serialize.h | 79 +++++++++++++++++++ include/supernode/requests/authorize_rta_tx.h | 1 + include/supernode/requests/pay_status.h | 1 + include/supernode/requests/reject_pay.h | 1 + include/supernode/requests/reject_sale.h | 1 + include/supernode/requests/sale_details.h | 2 +- src/lib/graft/sys_info_request.cpp | 2 +- 11 files changed, 90 insertions(+), 75 deletions(-) create mode 100644 include/lib/graft/serialize.h diff --git a/include/lib/graft/inout.h b/include/lib/graft/inout.h index bb81034c..a116c243 100644 --- a/include/lib/graft/inout.h +++ b/include/lib/graft/inout.h @@ -1,94 +1,24 @@ #pragma once -#include "lib/graft/graft_macros.h" #include "lib/graft/common/utils.h" -#include "lib/graft/reflective-rapidjson/reflector-boosthana.h" -#include "lib/graft/reflective-rapidjson/serializable.h" -#include "lib/graft/reflective-rapidjson/types.h" - #include #include #include #include #include -#include struct http_message; //from mongoose.h struct mg_str; //from mongoose.h -#define GRAFT_DEFINE_IO_STRUCT(__S__, ...) \ - struct __S__ : public ReflectiveRapidJSON::JsonSerializable<__S__> { \ - BOOST_HANA_DEFINE_STRUCT(__S__, __VA_ARGS__); \ - } - -#define GRAFT_DEFINE_IO_STRUCT_INITED(__S__, ...) \ - struct __S__ : public ReflectiveRapidJSON::JsonSerializable<__S__> { \ - __S__() : INIT_PAIRS(__VA_ARGS__) {} \ - BOOST_HANA_DEFINE_STRUCT(__S__, TN_PAIRS(__VA_ARGS__)); \ - } - -/* - * Mapping of supported C++ types to supported JSON types - * ======================================================================== - * C++ type | JSON type - * ---------------------------------------------------------+-------------- - * custom structures/classes | object - * bool | true/false - * signed and unsigned integral types | number - * float and double | number - * enum and enum class | number - * std::string | string - * const char * | string - * iteratable lists (std::vector, std::list, ...) | array - * sets (std::set, std::unordered_set, std::multiset, ...) | array - * std::tuple | array - * std::unique_ptr, std::shared_ptr | depends/null - * std::map, std::unordered_map | object - * JsonSerializable | object - * ---------------------------------------------------------+-------------- - * - * Example of structure definitions: - * ================================= - * - * GRAFT_DEFINE_IO_STRUCT(Payment, - * (uint64, amount), - * (uint32, block_height), - * (std::string, payment_id), - * (std::string, tx_hash), - * (uint32, unlock_time) - * ); - * - * or initialized with default values - * - * GRAFT_DEFINE_IO_STRUCT_INITED(Payment, - * (uint64, amount, 999), - * (uint32, block_height, 10000), - * (std::string, payment_id, "abc"), - * (std::string, tx_hash, "def"), - * (uint32, unlock_time, 555555) - * ); - * - * GRAFT_DEFINE_IO_STRUCT(Payments, - * (std::vector, payments) - * ); - */ +namespace rapidjson { class ParseResult; } namespace graft { namespace serializer { - class JsonParseError : public std::runtime_error - { - public: - JsonParseError(const rapidjson::ParseResult &pr) - : std::runtime_error( std::string("Json parse error, code: ") + std::to_string(pr.Code()) - + ", offset: " + std::to_string(pr.Offset())) - { - } - }; - + class JsonParseError; template struct JSON diff --git a/include/lib/graft/jsonrpc.h b/include/lib/graft/jsonrpc.h index 8d7fa707..6797f6e4 100644 --- a/include/lib/graft/jsonrpc.h +++ b/include/lib/graft/jsonrpc.h @@ -29,6 +29,7 @@ #pragma once +#include "lib/graft/serialize.h" #include "lib/graft/inout.h" #include diff --git a/include/lib/graft/requests/health_check.h b/include/lib/graft/requests/health_check.h index 3f0ec3e8..f7e08287 100644 --- a/include/lib/graft/requests/health_check.h +++ b/include/lib/graft/requests/health_check.h @@ -1,6 +1,7 @@ #pragma once +#include "lib/graft/serialize.h" #include "lib/graft/router.h" namespace graft::request { diff --git a/include/lib/graft/requests/requestdefines.h b/include/lib/graft/requests/requestdefines.h index c89aeb11..714658c0 100644 --- a/include/lib/graft/requests/requestdefines.h +++ b/include/lib/graft/requests/requestdefines.h @@ -2,7 +2,7 @@ #pragma once #include "lib/graft/graft_constants.h" -#include "lib/graft/inout.h" +#include "lib/graft/serialize.h" #include "lib/graft/router.h" #include "rta/supernode.h" diff --git a/include/lib/graft/serialize.h b/include/lib/graft/serialize.h new file mode 100644 index 00000000..a6ea5248 --- /dev/null +++ b/include/lib/graft/serialize.h @@ -0,0 +1,79 @@ + +#pragma once + +#include "lib/graft/graft_macros.h" + +#include "lib/graft/reflective-rapidjson/reflector-boosthana.h" +#include "lib/graft/reflective-rapidjson/serializable.h" +#include "lib/graft/reflective-rapidjson/types.h" + +#include + +#define GRAFT_DEFINE_IO_STRUCT(__S__, ...) \ + struct __S__ : public ReflectiveRapidJSON::JsonSerializable<__S__> { \ + BOOST_HANA_DEFINE_STRUCT(__S__, __VA_ARGS__); \ + } + +#define GRAFT_DEFINE_IO_STRUCT_INITED(__S__, ...) \ + struct __S__ : public ReflectiveRapidJSON::JsonSerializable<__S__> { \ + __S__() : INIT_PAIRS(__VA_ARGS__) {} \ + BOOST_HANA_DEFINE_STRUCT(__S__, TN_PAIRS(__VA_ARGS__)); \ + } + +/* + * Mapping of supported C++ types to supported JSON types + * ======================================================================== + * C++ type | JSON type + * ---------------------------------------------------------+-------------- + * custom structures/classes | object + * bool | true/false + * signed and unsigned integral types | number + * float and double | number + * enum and enum class | number + * std::string | string + * const char * | string + * iteratable lists (std::vector, std::list, ...) | array + * sets (std::set, std::unordered_set, std::multiset, ...) | array + * std::tuple | array + * std::unique_ptr, std::shared_ptr | depends/null + * std::map, std::unordered_map | object + * JsonSerializable | object + * ---------------------------------------------------------+-------------- + * + * Example of structure definitions: + * ================================= + * + * GRAFT_DEFINE_IO_STRUCT(Payment, + * (uint64, amount), + * (uint32, block_height), + * (std::string, payment_id), + * (std::string, tx_hash), + * (uint32, unlock_time) + * ); + * + * or initialized with default values + * + * GRAFT_DEFINE_IO_STRUCT_INITED(Payment, + * (uint64, amount, 999), + * (uint32, block_height, 10000), + * (std::string, payment_id, "abc"), + * (std::string, tx_hash, "def"), + * (uint32, unlock_time, 555555) + * ); + * + * GRAFT_DEFINE_IO_STRUCT(Payments, + * (std::vector, payments) + * ); + */ + +namespace graft::serializer +{ + class JsonParseError : public std::runtime_error + { + public: + JsonParseError(const rapidjson::ParseResult &pr) + : std::runtime_error( std::string("Json parse error, code: ") + std::to_string(pr.Code()) + + ", offset: " + std::to_string(pr.Offset())) + { } + }; +} //namespace graft::serializer diff --git a/include/supernode/requests/authorize_rta_tx.h b/include/supernode/requests/authorize_rta_tx.h index e1ee1e89..02d7b6d8 100644 --- a/include/supernode/requests/authorize_rta_tx.h +++ b/include/supernode/requests/authorize_rta_tx.h @@ -1,6 +1,7 @@ #pragma once +#include "lib/graft/serialize.h" #include "lib/graft/router.h" namespace graft::supernode::request { diff --git a/include/supernode/requests/pay_status.h b/include/supernode/requests/pay_status.h index b071c0f2..65652893 100644 --- a/include/supernode/requests/pay_status.h +++ b/include/supernode/requests/pay_status.h @@ -1,6 +1,7 @@ #pragma once +#include "lib/graft/serialize.h" #include "lib/graft/router.h" namespace graft::supernode::request { diff --git a/include/supernode/requests/reject_pay.h b/include/supernode/requests/reject_pay.h index 35ef63be..92b069ce 100644 --- a/include/supernode/requests/reject_pay.h +++ b/include/supernode/requests/reject_pay.h @@ -1,6 +1,7 @@ #pragma once +#include "lib/graft/serialize.h" #include "lib/graft/router.h" namespace graft::supernode::request { diff --git a/include/supernode/requests/reject_sale.h b/include/supernode/requests/reject_sale.h index c32aecc3..e7f38115 100644 --- a/include/supernode/requests/reject_sale.h +++ b/include/supernode/requests/reject_sale.h @@ -1,6 +1,7 @@ #pragma once +#include "lib/graft/serialize.h" #include "lib/graft/router.h" namespace graft::supernode::request { diff --git a/include/supernode/requests/sale_details.h b/include/supernode/requests/sale_details.h index 856d4b34..cd93ea05 100644 --- a/include/supernode/requests/sale_details.h +++ b/include/supernode/requests/sale_details.h @@ -1,7 +1,7 @@ #pragma once -#include "lib/graft/inout.h" +#include "lib/graft/serialize.h" #include "lib/graft/router.h" #include diff --git a/src/lib/graft/sys_info_request.cpp b/src/lib/graft/sys_info_request.cpp index 5cffdd14..d4d2330e 100644 --- a/src/lib/graft/sys_info_request.cpp +++ b/src/lib/graft/sys_info_request.cpp @@ -2,7 +2,7 @@ #include "lib/graft/sys_info_request.h" #include "lib/graft/context.h" -#include "lib/graft/connection.h" +#include "lib/graft/task.h" #include "lib/graft/inout.h" #include "lib/graft/router.h" #include "lib/graft/sys_info.h" From c15e345ed60e69cdde5788fad97cf129db67aa58 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 13 Mar 2019 16:51:29 +0200 Subject: [PATCH 02/17] GraftletHandlerT::getClass added --- include/lib/graft/GraftletLoader.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index df41330b..7ba834a6 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -97,6 +97,14 @@ class GraftletHandlerT struct helperSign h(this); return h.invoke(cls_method, std::forward(args)...); } + + BaseT* getClass(const ClsName& cls) + { + auto it = m_cls2any.find(cls); + if(it == m_cls2any.end()) throw std::runtime_error("Cannot find graftlet class name:" + cls); + std::shared_ptr concreteGraftlet = std::any_cast>(it->second); + return concreteGraftlet.get(); + } }; class GraftletLoader From 9c64aaf82e7732533e66f98102a7e2f7e20e3060 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 13 Mar 2019 17:08:01 +0200 Subject: [PATCH 03/17] Graftlets periodic task support. --- graftlets/TestGraftlet.cpp | 26 ++++++++++++ include/lib/graft/GraftletLoader.h | 26 ++++++++++++ include/lib/graft/IGraftlet.h | 68 ++++++++++++++++++++++++++---- include/supernode/server.h | 1 + src/supernode/server.cpp | 15 +++++++ test/graftlets_test.cpp | 21 +++++++++ 6 files changed, 149 insertions(+), 8 deletions(-) diff --git a/graftlets/TestGraftlet.cpp b/graftlets/TestGraftlet.cpp index afd30fe4..d5499b56 100644 --- a/graftlets/TestGraftlet.cpp +++ b/graftlets/TestGraftlet.cpp @@ -56,6 +56,25 @@ class TestGraftlet: public IGraftlet return graft::Status::Ok; } + static std::string value; + static int count; + + std::string resetPeriodic(const std::string& val) + { + std::string res; + res.swap(value); + count = 0; + value = val; + return res; + } + + graft::Status testPeriodic(const graft::Router::vars_t& vars, const graft::Input& input, graft::Context& ctx, graft::Output& output) + { + bool stop = value.empty(); + value = "count " + std::to_string(++count); + return (stop)? graft::Status::Stop : graft::Status::Ok; + } + virtual void initOnce(const graft::CommonOpts& opts) override { // REGISTER_ACTION(TestGraftlet, testUndefined); @@ -66,6 +85,10 @@ class TestGraftlet: public IGraftlet REGISTER_ENDPOINT("/URI/test/{id:[0-9]+}", METHOD_GET | METHOD_POST, TestGraftlet, testHandler); REGISTER_ENDPOINT("/URI/test1/{id:[0-9]+}", METHOD_GET | METHOD_POST, TestGraftlet, testHandler1); + + REGISTER_ACTION(TestGraftlet, resetPeriodic); + //Type, method, int interval_ms, int initial_interval_ms, double random_factor + REGISTER_PERIODIC(TestGraftlet, testPeriodic, 100, 100, 0); } }; @@ -75,6 +98,9 @@ GRAFTLET_EXPORTS_END GRAFTLET_PLUGIN_DEFAULT_CHECK_FW_VERSION(GRAFTLET_MKVER(0,3)) +std::string TestGraftlet::value; +int TestGraftlet::count = 0; + namespace { diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index 7ba834a6..c5fc4166 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -134,6 +134,11 @@ class GraftletLoader return getEndpointsT(); } + typename IGraftlet::PeriodicVec getPeriodics() + { + return getPeriodicsT(); + } + class DependencyGraph; friend class GraftletLoader::DependencyGraph; private: @@ -166,6 +171,27 @@ class GraftletLoader } } + template + typename BaseT::PeriodicVec getPeriodicsT() + { + prepareAllEndpoints(); + + typename BaseT::PeriodicVec res; + for(auto& it0 : m_name2gls) + { + if(it0.first.second != std::type_index(typeid(BaseT))) continue; + std::map& map = it0.second; + for(auto& it1 : map) + { + //TODO: remove shared_ptr, it does not hold something now + std::shared_ptr concreteGraftlet = std::any_cast>(it1.second); + typename BaseT::PeriodicVec vec = concreteGraftlet->getPeriodics(); + res.insert(res.end(), vec.begin(), vec.end()); + } + } + return res; + } + template typename BaseT::EndpointsVec getEndpointsT() { diff --git a/include/lib/graft/IGraftlet.h b/include/lib/graft/IGraftlet.h index 3724b4ff..57149df5 100644 --- a/include/lib/graft/IGraftlet.h +++ b/include/lib/graft/IGraftlet.h @@ -20,14 +20,26 @@ #define REGISTER_ENDPOINT(Endpoint, Methods, T, f) \ register_endpoint_memf(#f, this, &T::f, Endpoint, Methods) +#define REGISTER_PERIODIC(T, f, interval_ms, initial_interval_ms, random_factor) \ + register_periodic_memf("#"#f, this, &T::f, interval_ms, initial_interval_ms, random_factor) + class IGraftlet { public: + struct Periodic + { + graft::Router::Handler handler; + int interval_ms; + int initial_interval_ms; + double random_factor; + }; + using ClsName = std::string; using FuncName = std::string; using EndpointPath = std::string; using Methods = int; using EndpointsVec = std::vector< std::tuple >; + using PeriodicVec = std::vector< Periodic >; IGraftlet() = delete; virtual ~IGraftlet() = default; @@ -64,6 +76,24 @@ class IGraftlet return res; } + PeriodicVec getPeriodics() + { + PeriodicVec res; + std::type_index ti = std::type_index(typeid(Periodic)); + for(auto& it : m_map) + { + TypeIndex2any& ti2any = it.second; + auto it1 = ti2any.find(ti); + if(it1 == ti2any.end()) continue; + + std::any& any = std::get<0>(it1->second); + Periodic periodic = std::any_cast(any); + + res.emplace_back(std::move(periodic)); + } + return res; + } + template Res invoke(const FuncName& name, Args&&...args) { @@ -83,23 +113,29 @@ class IGraftlet return callable(std::forward(args)...); } - //It can be used to register any callable object like a function, to register member function use register_handler_memf - template - void register_handler(const FuncName& name, Callable callable, const EndpointPath& endpoint = EndpointPath(), Methods methods = 0) + template + void register_obj(const FuncName& name, Obj&& obj, const EndpointPath& endpoint = EndpointPath(), Methods methods = 0) { - std::type_index ti = std::type_index(typeid(Callable)); + std::type_index ti = std::type_index(typeid(Obj)); TypeIndex2any& ti2any = m_map[name]; - std::any any = std::make_any(callable); - assert(any.type().hash_code() == typeid(callable).hash_code()); + std::any any = std::make_any(std::forward(obj)); + assert(any.type().hash_code() == typeid(Obj).hash_code()); std::ostringstream oss; if(!endpoint.empty()) { oss << " '" << endpoint << "' " << graft::Router::methodsToString(methods); } - LOG_PRINT_L2("register_handler " << name << oss.str() << " of " << typeid(callable).name()); + LOG_PRINT_L2("register_obj " << name << oss.str() << " of " << typeid(Obj).name()); auto res = ti2any.emplace(ti, std::make_tuple(std::move(any), endpoint, methods) ); - if(!res.second) throw std::runtime_error("function " + name + " with typeid " + ti.name() + " already registered"); + if(!res.second) throw std::runtime_error("object " + name + " with typeid " + ti.name() + " already registered"); + } + + //It can be used to register any callable object like a function, to register member function use register_handler_memf + template + void register_handler(const FuncName& name, Callable callable, const EndpointPath& endpoint = EndpointPath(), Methods methods = 0) + { + register_obj(name, callable, endpoint, methods); } template @@ -129,6 +165,22 @@ class IGraftlet }; register_endpoint(name, fun, endpoint, methods); } + + template + void register_periodic_memf(const FuncName& name, Obj* p + , graft::Status (Obj::*f)(const graft::Router::vars_t& vars, const graft::Input& input, graft::Context& ctx, graft::Output& output) + , int interval_ms, int initial_interval_ms, double random_factor ) + { + std::function memf = f; + graft::Router::Handler fun = + [p,memf](const graft::Router::vars_t& vars, const graft::Input& input, graft::Context& ctx, graft::Output& output)->graft::Status + { + return memf(p,vars,input,ctx,output); + }; + Periodic periodic { fun, interval_ms, initial_interval_ms, random_factor }; + register_obj(name, std::move(periodic)); + } + protected: IGraftlet(const ClsName& name = ClsName() ) : m_clsName(name) { } virtual void initOnce(const graft::CommonOpts& opts) = 0; diff --git a/include/supernode/server.h b/include/supernode/server.h index d9594201..924227a7 100644 --- a/include/supernode/server.h +++ b/include/supernode/server.h @@ -56,6 +56,7 @@ class GraftServer void addGlobalCtxCleaner(); void initGraftlets(); void initGraftletRouters(); + void initGraftletPeriodics(); ConfigOpts& getCopts(); diff --git a/src/supernode/server.cpp b/src/supernode/server.cpp index 2bccf8e7..859c234f 100644 --- a/src/supernode/server.cpp +++ b/src/supernode/server.cpp @@ -109,6 +109,19 @@ void GraftServer::initGraftletRouters() } } +void GraftServer::initGraftletPeriodics() +{ + assert(m_graftletLoader); + IGraftlet::PeriodicVec periodics = m_graftletLoader->getPeriodics(); + for(IGraftlet::Periodic& p : periodics) + { + getLooper().addPeriodicTask(p.handler, std::chrono::milliseconds( p.interval_ms ), + std::chrono::milliseconds( p.initial_interval_ms ), + p.random_factor); + } +} + + void GraftServer::initGlobalContext() { // TODO: why context intialized second time here? @@ -176,6 +189,8 @@ bool GraftServer::init(int argc, const char** argv, ConfigOpts& configOpts) m_connectionBase->bindConnectionManagers(); + initGraftletPeriodics(); + return true; } diff --git a/test/graftlets_test.cpp b/test/graftlets_test.cpp index fb16583e..01054566 100644 --- a/test/graftlets_test.cpp +++ b/test/graftlets_test.cpp @@ -362,3 +362,24 @@ TEST_F(GraftServerTest, graftlets) stop_and_wait_for(); } + +TEST_F(GraftServerTest, graftletsPeriodic) +{ + m_copts.graftlet_dirs.emplace_back("graftlets"); + m_copts.timer_poll_interval_ms = 50; + + graft::CommonOpts opts; + graftlet::GraftletLoader loader(opts); + loader.findGraftletsInDirectory("./graftlets", "so"); + + graftlet::GraftletHandler plugin = loader.buildAndResolveGraftlet("myGraftlet"); + plugin.invoke("testGL.resetPeriodic", " "); + + run(); + + std::this_thread::sleep_for(std::chrono::milliseconds(1050)); + std::string res = plugin.invoke("testGL.resetPeriodic", ""); + EXPECT_EQ(res, "count " + std::to_string(10)); + + stop_and_wait_for(); +} From 8b9b370bb89f5e92a702ca00157c5b21b43d23ff Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Fri, 15 Mar 2019 21:35:26 +0200 Subject: [PATCH 04/17] class ConfigIni --- CMakeLists.txt | 2 + include/lib/graft/ConfigIni.h | 223 ++++++++++++++++++++++++++++++++++ src/lib/graft/ConfigIni.cpp | 179 +++++++++++++++++++++++++++ test/configini_test.cpp | 121 ++++++++++++++++++ 4 files changed, 525 insertions(+) create mode 100644 include/lib/graft/ConfigIni.h create mode 100644 src/lib/graft/ConfigIni.cpp create mode 100644 test/configini_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d4c182de..43cc8b5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,7 @@ add_library(graft STATIC ${PROJECT_SOURCE_DIR}/src/lib/graft/common/utils.cpp ${PROJECT_SOURCE_DIR}/src/lib/graft/backtrace.cpp ${PROJECT_SOURCE_DIR}/src/lib/graft/blacklist.cpp + ${PROJECT_SOURCE_DIR}/src/lib/graft/ConfigIni.cpp ${PROJECT_SOURCE_DIR}/src/lib/graft/connection.cpp ${PROJECT_SOURCE_DIR}/src/lib/graft/context.cpp ${PROJECT_SOURCE_DIR}/src/lib/graft/inout.cpp @@ -412,6 +413,7 @@ if (OPT_BUILD_TESTS) ) add_executable(supernode_test + ${PROJECT_SOURCE_DIR}/test/configini_test.cpp ${PROJECT_SOURCE_DIR}/test/expiring_set_test.cpp ${PROJECT_SOURCE_DIR}/test/upstream_test.cpp ${PROJECT_SOURCE_DIR}/test/blacklist_test.cpp diff --git a/include/lib/graft/ConfigIni.h b/include/lib/graft/ConfigIni.h new file mode 100644 index 00000000..3e8b0712 --- /dev/null +++ b/include/lib/graft/ConfigIni.h @@ -0,0 +1,223 @@ +// Copyright (c) 2018, The Graft Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include +#include +#include + +namespace graft +{ + +class ConfigIniSubtree; +class ConfigIniSubtreeRef; + +namespace hidden +{ + +class VConfigIniSubtree +{ +public: + virtual ~VConfigIniSubtree() = default; + //get_... that throw exit_error + virtual bool get_bool(const std::string& path) const = 0; + virtual long long get_long_long(const std::string& path) const = 0; + virtual unsigned long long get_u_long_long(const std::string& path) const = 0; + virtual long double get_long_double(const std::string& path) const = 0; + virtual std::string get_string(const std::string& path) const = 0; + + virtual std::unique_ptr clone() const = 0; +}; + +class VIter +{ +protected: + VIter() = default; +public: + virtual ~VIter() = default; + VIter(const VIter&) = delete; + VIter& operator=(const VIter&) = delete; + + virtual bool operator == (const VIter& it) const = 0; + virtual VIter& operator++() = 0; //prefix increment +// virtual VIter& operator++(int) = 0; //postfix increment + virtual ConfigIniSubtreeRef operator*() const = 0; +}; + +} //namespace hidden + +class ConfigIniSubtreeRef final +{ + std::unique_ptr v; + std::string n; +public: + ~ConfigIniSubtreeRef() = default; + ConfigIniSubtreeRef(const ConfigIniSubtreeRef& sr) : v(sr.v->clone()), n(sr.n) { } + ConfigIniSubtreeRef& operator = (const ConfigIniSubtreeRef& sr) { v = sr.v->clone(); n = sr.n; return *this; } + ConfigIniSubtreeRef(ConfigIniSubtreeRef&& sr) = default; + ConfigIniSubtreeRef& operator = (ConfigIniSubtreeRef&& sr) = default; + + ConfigIniSubtreeRef(std::unique_ptr&& v, std::string&& n) + : v(std::move(v)) + , n(std::move(n)) { } + const std::string& name() const { return n; } + ConfigIniSubtree value() const; +}; + +//Interface to get values from config.ini +class ConfigIniSubtree +{ +private: + +protected: + std::unique_ptr v; + ConfigIniSubtree(std::unique_ptr&& v) : v(std::move(v)) { } + friend class ConfigIniSubtreeRef; +public: + ~ConfigIniSubtree() = default; + ConfigIniSubtree(const ConfigIniSubtree& s) : v( s.v->clone() ) { } + ConfigIniSubtree& operator =(const ConfigIniSubtree& s) { v = s.v->clone(); return *this; } + ConfigIniSubtree(ConfigIniSubtree&& s) = default; + ConfigIniSubtree& operator =(ConfigIniSubtree&& s) = default; + + static ConfigIniSubtree create(const std::string& config_filename); + + class iterator + { + std::unique_ptr vi; + friend ConfigIniSubtree; + public: + ~iterator() = default; + iterator(const iterator& it); + iterator& operator =(const iterator& it); + iterator(iterator&& it) = default; + iterator& operator =(iterator&& it) = default; + + iterator(std::unique_ptr&& vi) : vi(std::move(vi)) { } + + bool operator ==(const iterator& it) const + { + vi->operator ==(*it.vi); + } + bool operator !=(const iterator& it) const + { + return ! operator ==(it); + } + + iterator& operator ++() //prefix increment + { + vi->operator ++(); + return *this; + } +// iterator& operator++(int); //postfix increment + ConfigIniSubtreeRef operator *() const + { + return vi->operator *(); + } + std::unique_ptr operator->() const + { + return std::make_unique( operator *() ); + } + }; + + iterator begin(); + iterator end(); + + //throw exit_error + ConfigIniSubtree get_child(const std::string& path) const; + + std::optional get_child_optional(const std::string& path) const + { + try + { + ConfigIniSubtree res = get_child(path); + return std::optional(std::move(res)); + } + catch(std::exception&){ return std::optional(); } + } + + //get functions that throw exit_error + template + typename std::enable_if::value, T>::type + get(const std::string& path) const + { + return v->get_bool(path); + } + + template + typename std::enable_if::value && std::is_signed::value && !std::is_same::value, T>::type + get(const std::string& path) const + { + return static_cast( v->get_long_long(path) ); + } + + template + typename std::enable_if::value && std::is_unsigned::value && !std::is_same::value, T>::type + get(const std::string& path) const + { + return static_cast( v->get_u_long_long(path) ); + } + + template + typename std::enable_if::value, T>::type + get(const std::string& path) const + { + return static_cast( v->get_long_double(path) ); + } + + template + typename std::enable_if::value, T>::type + get(const std::string& path) const + { + return v->get_string(path); + } + + //get functions with default + template + T get(const std::string& path, const T& default_value) const + { + try{ return get(path); } + catch(std::exception&){ return default_value; } + } + + //get functions with optional + template + std::optional get_optional(const std::string& path) const + { + try + { + T res = get(path); + return std::optional(std::move(res)); + } + catch(std::exception&){ return std::optional(); } + } +}; + +} //namespace graft diff --git a/src/lib/graft/ConfigIni.cpp b/src/lib/graft/ConfigIni.cpp new file mode 100644 index 00000000..5219c07e --- /dev/null +++ b/src/lib/graft/ConfigIni.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2018, The Graft Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "lib/graft/ConfigIni.h" +#include "lib/graft/graft_exception.h" +#include +#include + +namespace graft +{ + +namespace +{ + +std::string trim_comments(std::string s) +{ + //remove ;; tail + std::size_t pos = s.find(";;"); + if(pos != std::string::npos) + { + s = s.substr(0,pos); + } + boost::trim_right(s); + return s; +} + +class VConfigIniSubtreeImpl : public hidden::VConfigIniSubtree +{ +public: + boost::property_tree::ptree& ptree; + + template + T get_except(const std::string& path) const + { + try + { + return ptree.get(path); + } + catch(std::exception& ex) + { + throw graft::exit_error(ex.what()); + } + } +public: + virtual bool get_bool(const std::string& path) const override + { + return get_except(path); + } + virtual long long get_long_long(const std::string& path) const override + { + return get_except(path); + } + virtual unsigned long long get_u_long_long(const std::string& path) const override + { + return get_except(path); + } + virtual long double get_long_double(const std::string& path) const override + { + return get_except(path); + } + virtual std::string get_string(const std::string& path) const override + { + return trim_comments( get_except(path) ); + } + + virtual std::unique_ptr clone() const override + { + return std::make_unique(*this); + } + + boost::property_tree::ptree& get_child(const std::string& path) const + { + return ptree.get_child(path); + } +public: + VConfigIniSubtreeImpl(boost::property_tree::ptree& ptree) : ptree(ptree) { } + + static VConfigIniSubtreeImpl* cast(hidden::VConfigIniSubtree* v) + { + assert(dynamic_cast(v)); + return static_cast(v); + } +}; + +class VIterImpl : public hidden::VIter +{ + boost::property_tree::ptree::iterator iter; +public: + VIterImpl(boost::property_tree::ptree::iterator&& iter) : iter(std::move(iter)) { } + bool operator ==(const VIterImpl& it) const + { + return iter == it.iter; + } + virtual bool operator ==(const hidden::VIter& it) const override + { + assert(dynamic_cast(&it)); + return operator ==( static_cast(it) ); + } + virtual hidden::VIter& operator++() override //prefix increment + { + ++iter; + return *this; + } +// virtual hidden::VIter& operator++(int) = 0; //postfix increment + virtual ConfigIniSubtreeRef operator*() const override + { + return ConfigIniSubtreeRef(std::make_unique(iter->second), std::string(iter->first) ); + } +}; + +class ConfigIni : public VConfigIniSubtreeImpl +{ + boost::property_tree::ptree ptree; +public: + ConfigIni(const std::string& config_filename) : VConfigIniSubtreeImpl(ptree) + { + boost::property_tree::ini_parser::read_ini(config_filename, ptree); + } +}; + +} //namespace + + +ConfigIniSubtree ConfigIniSubtree::get_child(const std::string& path) const +{ + const VConfigIniSubtreeImpl* vcisi = VConfigIniSubtreeImpl::cast(v.get()); + return ConfigIniSubtree( std::make_unique( vcisi->get_child(path) ) ); +} + +ConfigIniSubtree ConfigIniSubtree::create(const std::string& config_filename) +{ + return ConfigIniSubtree( std::make_unique(config_filename) ); +} + +ConfigIniSubtree::iterator ConfigIniSubtree::begin() +{ + const VConfigIniSubtreeImpl* vcisi = VConfigIniSubtreeImpl::cast(v.get()); + return ConfigIniSubtree::iterator( std::make_unique(vcisi->ptree.begin()) ); +} + +ConfigIniSubtree::iterator ConfigIniSubtree::end() +{ + const VConfigIniSubtreeImpl* vcisi = VConfigIniSubtreeImpl::cast(v.get()); + return ConfigIniSubtree::iterator( std::make_unique(vcisi->ptree.end()) ); +} + +ConfigIniSubtree ConfigIniSubtreeRef::value() const +{ + assert(dynamic_cast(v.get())); + std::unique_ptr nv = v->clone(); + return ConfigIniSubtree(std::move(nv)); +} + +} //namespace graft diff --git a/test/configini_test.cpp b/test/configini_test.cpp new file mode 100644 index 00000000..15221c60 --- /dev/null +++ b/test/configini_test.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2018, The Graft Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "lib/graft/ConfigIni.h" +#include "lib/graft/graft_exception.h" + +TEST(ConfigIni, common) +{ + graft::ConfigIniSubtree ci = graft::ConfigIniSubtree::create("./config.ini"); + { + int i = ci.get("server.lru-timeout-ms"); + std::optional oi = ci.get_optional("server.lru-timeout-ms"); + EXPECT_EQ(oi.has_value(), true); + EXPECT_EQ(oi.value(), i); + int i1 = ci.get("server.bla-bla", 200); + EXPECT_EQ(i1, 200); + std::optional oi1 = ci.get_optional("server.bla-bla"); + EXPECT_EQ(oi1.has_value(), false); + bool except = false; + try + { + int i = ci.get("server.bla-bla"); + } + catch(graft::exit_error& e) + { + except = true; + } + EXPECT_EQ(except, true); + } + { + EXPECT_NO_THROW( ci.get("stun.enabled") ); + std::optional ob = ci.get_optional("stun.enabled"); + EXPECT_EQ(ob.has_value(), true); + } + { + //check comments trim + std::string s = ci.get("server.workers-expelling-interval-ms"); + EXPECT_LE(s.size(), std::string("20000").size()); + std::optional os = ci.get_optional("server.workers-expelling-interval-ms"); + EXPECT_EQ(os.has_value(), true); + EXPECT_EQ(os.value(), s); + std::string s1 = ci.get("server.bla-bla", "default"); + EXPECT_EQ(s1, "default"); + std::optional os1 = ci.get_optional("server.bla-bla"); + EXPECT_EQ(os1.has_value(), false); + bool except = false; + try + { + std::string s1 = ci.get("server.bla-bla"); + } + catch(graft::exit_error& e) + { + except = true; + } + EXPECT_EQ(except, true); + } + + graft::ConfigIniSubtree uri_subst_conf = ci.get_child("upstream"); + + { + auto opt = ci.get_child_optional("asdf"); + EXPECT_EQ(bool(opt), false); + auto opt1 = ci.get_child_optional("upstream"); + EXPECT_EQ(bool(opt1), true); + uri_subst_conf = std::move(opt1.value()); + } + + int cnt1 = 0; + uri_subst_conf.begin(); + graft::ConfigIniSubtreeRef ref = *uri_subst_conf.begin(); + std::for_each(uri_subst_conf.begin(), uri_subst_conf.end(),[&uri_subst_conf, &cnt1](auto it) + { + ++cnt1; + std::string name(it.name()); + std::string val(uri_subst_conf.get(name)); + EXPECT_EQ(val.empty(), false); + graft::ConfigIniSubtree v = it.value(); + std::string val1 = v.get(""); + graft::ConfigIniSubtreeRef ref = it; + std::string val2 = ref.value().get(""); + EXPECT_EQ(val, val1); + }); + EXPECT_LT(0, cnt1); + + int cnt2 = 0; + for(graft::ConfigIniSubtree::iterator it = uri_subst_conf.begin(), eit = uri_subst_conf.end(); it != eit; ++it) + { + ++cnt2; + std::string name = it->name(); + std::string val = it->value().get(""); + std::string val1 = uri_subst_conf.get(name); + EXPECT_EQ(val, val1); + } + EXPECT_LT(0, cnt2); +} From 1804be7ae91fd60c2d6fb96f979b8f5d29b1d5de Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 20 Mar 2019 21:37:49 +0200 Subject: [PATCH 05/17] boost::property_tree::ptree replaced with ConfigIniSubtree --- src/supernode/server.cpp | 47 ++++++++++++++++++------------------- src/supernode/supernode.cpp | 18 +++++++------- src/walletnode/server.cpp | 5 ---- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/supernode/server.cpp b/src/supernode/server.cpp index 859c234f..4e691476 100644 --- a/src/supernode/server.cpp +++ b/src/supernode/server.cpp @@ -4,10 +4,10 @@ #include "lib/graft/GraftletLoader.h" #include "lib/graft/sys_info.h" #include "lib/graft/graft_exception.h" +#include "lib/graft/ConfigIni.h" #include "version.h" #include -#include #include #undef MONERO_DEFAULT_LOG_CATEGORY @@ -279,7 +279,7 @@ std::string trim_comments(std::string s) namespace po = boost::program_options; -void init_log(const boost::property_tree::ptree& config, const po::variables_map& vm) +void init_log(const ConfigIniSubtree& config, const po::variables_map& vm) { std::string log_level = "3"; bool log_console = true; @@ -287,15 +287,15 @@ void init_log(const boost::property_tree::ptree& config, const po::variables_map std::string log_format; //from config - const boost::property_tree::ptree& log_conf = config.get_child("logging"); - boost::optional level = log_conf.get_optional("loglevel"); - if(level) log_level = trim_comments( level.get() ); - boost::optional log_file = log_conf.get_optional("logfile"); - if(log_file) log_filename = trim_comments( log_file.get() ); - boost::optional log_to_console = log_conf.get_optional("console"); - if(log_to_console) log_console = log_to_console.get(); - boost::optional log_fmt = log_conf.get_optional("log-format"); - if(log_fmt) log_format = trim_comments( log_fmt.get() ); + auto log_conf = config.get_child("logging"); + std::optional level = log_conf.get_optional("loglevel"); + if(level) log_level = trim_comments( level.value() ); + std::optional log_file = log_conf.get_optional("logfile"); + if(log_file) log_filename = trim_comments( log_file.value() ); + std::optional log_to_console = log_conf.get_optional("console"); + if(log_to_console) log_console = log_to_console.value(); + std::optional log_fmt = log_conf.get_optional("log-format"); + if(log_fmt) log_format = trim_comments( log_fmt.value() ); //override from cmdline if (vm.count("log-level")) log_level = vm["log-level"].as(); @@ -501,8 +501,7 @@ bool GraftServer::initConfigOption(int argc, const char** argv, ConfigOpts& conf config_filename = (selfpath / "config.ini").string(); } - boost::property_tree::ptree config; - boost::property_tree::ini_parser::read_ini(config_filename, config); + ConfigIniSubtree config = ConfigIniSubtree::create(config_filename); // now we have only following parameters // [server] // address : @@ -537,7 +536,7 @@ bool GraftServer::initConfigOption(int argc, const char** argv, ConfigOpts& conf configOpts.config_filename = config_filename; - const boost::property_tree::ptree& server_conf = config.get_child("server"); + ConfigIniSubtree server_conf = config.get_child("server"); configOpts.http_address = server_conf.get("http-address"); configOpts.coap_address = server_conf.get("coap-address"); configOpts.timer_poll_interval_ms = server_conf.get("timer-poll-interval-ms"); @@ -556,7 +555,7 @@ bool GraftServer::initConfigOption(int argc, const char** argv, ConfigOpts& conf if(opt_ipfilter) { IPFilterOpts& ipfilter = configOpts.ipfilter; - const auto ipfilter_conf = opt_ipfilter.get(); + const auto ipfilter_conf = opt_ipfilter.value(); ipfilter.window_size_sec = ipfilter_conf.get("window-size-sec", 0); ipfilter.requests_per_sec = ipfilter_conf.get("requests-per-sec", 0); ipfilter.ban_ip_sec = ipfilter_conf.get("ban-ip-sec", 0); @@ -577,22 +576,22 @@ bool GraftServer::initConfigOption(int argc, const char** argv, ConfigOpts& conf } //configOpts.graftlet_dirs - const boost::property_tree::ptree& graftlets_conf = config.get_child("graftlets"); - boost::optional dirs_opt = graftlets_conf.get_optional("dirs"); - details::initGraftletDirs(argc, argv, (dirs_opt)? dirs_opt.get() : "", bool(dirs_opt), configOpts.graftlet_dirs); + ConfigIniSubtree graftlets_conf = config.get_child("graftlets"); + std::optional dirs_opt = graftlets_conf.get_optional("dirs"); + details::initGraftletDirs(argc, argv, (dirs_opt)? dirs_opt.value() : "", bool(dirs_opt), configOpts.graftlet_dirs); - const boost::property_tree::ptree& cryptonode_conf = config.get_child("cryptonode"); + ConfigIniSubtree cryptonode_conf = config.get_child("cryptonode"); configOpts.cryptonode_rpc_address = cryptonode_conf.get("rpc-address"); - const boost::property_tree::ptree& log_conf = config.get_child("logging"); - boost::optional log_trunc_to_size = log_conf.get_optional("trunc-to-size"); - configOpts.log_trunc_to_size = (log_trunc_to_size)? log_trunc_to_size.get() : -1; + ConfigIniSubtree log_conf = config.get_child("logging"); + std::optional log_trunc_to_size = log_conf.get_optional("trunc-to-size"); + configOpts.log_trunc_to_size = (log_trunc_to_size)? log_trunc_to_size.value() : -1; - const boost::property_tree::ptree& uri_subst_conf = config.get_child("upstream"); + ConfigIniSubtree uri_subst_conf = config.get_child("upstream"); graft::OutHttp::uri_substitutions.clear(); std::for_each(uri_subst_conf.begin(), uri_subst_conf.end(),[&uri_subst_conf](auto it) { - std::string name(it.first); + std::string name(it.name()); std::string val(uri_subst_conf.get(name)); std::string uri; int cnt; bool keepAlive; double timeout; diff --git a/src/supernode/supernode.cpp b/src/supernode/supernode.cpp index fcba86d4..204654a9 100644 --- a/src/supernode/supernode.cpp +++ b/src/supernode/supernode.cpp @@ -9,8 +9,7 @@ #include "rta/supernode.h" #include "rta/fullsupernodelist.h" #include "lib/graft/graft_exception.h" - -#include +#include "lib/graft/ConfigIni.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "supernode.supernode" @@ -53,10 +52,9 @@ bool Supernode::initConfigOption(int argc, const char** argv, ConfigOpts& config ConfigOptsEx& coptsex = static_cast(configOpts); assert(&m_configEx == &coptsex); - boost::property_tree::ptree config; - boost::property_tree::ini_parser::read_ini(m_configEx.config_filename, config); + ConfigIniSubtree config = ConfigIniSubtree::create(m_configEx.config_filename); - const boost::property_tree::ptree& server_conf = config.get_child("server"); + const ConfigIniSubtree server_conf = config.get_child("server"); m_configEx.stake_wallet_name = server_conf.get("stake-wallet-name", "stake-wallet"); m_configEx.stake_wallet_refresh_interval_ms = server_conf.get("stake-wallet-refresh-interval-ms", consts::DEFAULT_STAKE_WALLET_REFRESH_INTERFAL_MS); @@ -64,18 +62,18 @@ bool Supernode::initConfigOption(int argc, const char** argv, ConfigOpts& config {//get external address //try get from [stun] - boost::optional stun_conf = config.get_child_optional("stun"); + std::optional stun_conf = config.get_child_optional("stun"); bool ok = !!stun_conf; if(ok) { - bool stun_enabled = stun_conf.get().get("enabled", false); + bool stun_enabled = stun_conf.value().get("enabled", false); ok = stun_enabled; } if(ok) {//using stun - std::string server = stun_conf.get().get("server", ""); - std::string port = stun_conf.get().get("port", ""); - std::string cmd = stun_conf.get().get("cmd", ""); + std::string server = stun_conf.value().get("server", ""); + std::string port = stun_conf.value().get("port", ""); + std::string cmd = stun_conf.value().get("cmd", ""); std::string env = "stun_server=" + server + " " + "stun_port=" + port + "; "; std::string external_address = exec(env + cmd); diff --git a/src/walletnode/server.cpp b/src/walletnode/server.cpp index 771b9e2b..b002e891 100644 --- a/src/walletnode/server.cpp +++ b/src/walletnode/server.cpp @@ -3,13 +3,8 @@ #include "walletnode/requests.h" #include "supernode/requestdefines.h" -#include -#include - const int WALLET_DISK_CACHES_UPDATE_TIME_MS = 10 * 60 * 1000; //TODO: move to config -namespace po = boost::program_options; - namespace graft { namespace walletnode { From ba742b6297055506e9092f8fe8aaebad38710703 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 20 Mar 2019 23:33:22 +0200 Subject: [PATCH 06/17] new parameter Context& added to IGraftlet::initOnce --- graftlets/TestGraftlet.cpp | 8 +++++++- graftlets/TestGraftlet1.cpp | 2 +- graftlets/WalletAddress.cpp | 2 +- include/lib/graft/GraftletLoader.h | 5 +++-- include/lib/graft/IGraftlet.h | 6 +++--- include/lib/graft/serveropts.h | 2 +- src/lib/graft/sys_info_request.cpp | 2 +- src/supernode/server.cpp | 4 ++-- src/supernode/supernode.cpp | 2 +- test/cryptonode_handlers_test.cpp | 2 +- test/graftlets_test.cpp | 20 ++++++++++++-------- test/json_rpc_test.cpp | 2 +- 12 files changed, 34 insertions(+), 23 deletions(-) diff --git a/graftlets/TestGraftlet.cpp b/graftlets/TestGraftlet.cpp index d5499b56..e2a70386 100644 --- a/graftlets/TestGraftlet.cpp +++ b/graftlets/TestGraftlet.cpp @@ -2,6 +2,7 @@ #define __GRAFTLET__ #include "lib/graft/GraftletRegistry.h" #include "lib/graft/IGraftlet.h" +#include "lib/graft/ConfigIni.h" #include @@ -75,8 +76,13 @@ class TestGraftlet: public IGraftlet return (stop)? graft::Status::Stop : graft::Status::Ok; } - virtual void initOnce(const graft::CommonOpts& opts) override + virtual void initOnce(const graft::CommonOpts& opts, graft::Context& ctx) override { + if(!opts.config_filename.empty()) + { + graft::ConfigIniSubtree config = graft::ConfigIniSubtree::create(opts.config_filename); + ctx.global["graftlets.dirs"] = config.get("graftlets.dirs"); + } // REGISTER_ACTION(TestGraftlet, testUndefined); REGISTER_ACTION(TestGraftlet, testInt1); REGISTER_ACTION(TestGraftlet, testInt2); diff --git a/graftlets/TestGraftlet1.cpp b/graftlets/TestGraftlet1.cpp index db700bf7..3693f9d8 100644 --- a/graftlets/TestGraftlet1.cpp +++ b/graftlets/TestGraftlet1.cpp @@ -56,7 +56,7 @@ class TestGraftlet1: public IGraftlet return graft::Status::Ok; } - virtual void initOnce(const graft::CommonOpts& opts) override + virtual void initOnce(const graft::CommonOpts& opts, graft::Context& ctx) override { // REGISTER_ACTION(TestGraftlet1, testUndefined); REGISTER_ACTION(TestGraftlet1, testInt1); diff --git a/graftlets/WalletAddress.cpp b/graftlets/WalletAddress.cpp index 32d2d9cc..8ebe81ce 100644 --- a/graftlets/WalletAddress.cpp +++ b/graftlets/WalletAddress.cpp @@ -55,7 +55,7 @@ class WalletAddress: public IGraftlet return true; } - virtual void initOnce(const graft::CommonOpts& opts) override + virtual void initOnce(const graft::CommonOpts& opts, graft::Context& ctx) override { makeGetWalletAddressResponse(opts); diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index c5fc4166..4ffe491e 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -114,7 +114,7 @@ class GraftletLoader using Version = int; using GraftletExceptionList = std::vector< std::pair< DllName, std::vector< std::pair >>>; - GraftletLoader(const graft::CommonOpts& opts) : m_opts(opts) { } + GraftletLoader(const graft::CommonOpts& opts, graft::GlobalContextMap& gcm) : m_opts(opts), m_ctx(gcm) { } static Version getFwVersion() { return m_fwVersion; } static void setFwVersion(Version fwVersion) { m_fwVersion = fwVersion; } @@ -224,7 +224,7 @@ class GraftletLoader GraftletRegistry* gr = it1->second; std::shared_ptr concreteGraftlet = gr->resolveGraftlet(); if(!concreteGraftlet.get()) throw std::runtime_error("Cannot resolve dll name:" + dllName + " type:" + typeid(BaseT).name()); - concreteGraftlet->init(m_opts); + concreteGraftlet->init(m_opts, m_ctx); ClsName name = concreteGraftlet->getClsName(); std::any any(concreteGraftlet); @@ -295,6 +295,7 @@ class GraftletLoader static ExceptionMap m_exceptionMap; const graft::CommonOpts& m_opts; + graft::Context m_ctx; //we can use functions in a dll until we release object of boost::dll::shared_library //dll name -> (lib, version, path) diff --git a/include/lib/graft/IGraftlet.h b/include/lib/graft/IGraftlet.h index 57149df5..02bf9207 100644 --- a/include/lib/graft/IGraftlet.h +++ b/include/lib/graft/IGraftlet.h @@ -46,11 +46,11 @@ class IGraftlet IGraftlet(const IGraftlet&) = delete; IGraftlet& operator = (const IGraftlet&) = delete; - void init(const graft::CommonOpts& opts) + void init(const graft::CommonOpts& opts, graft::Context& ctx) { if(m_inited) return; m_inited = true; - initOnce(opts); + initOnce(opts, ctx); } const ClsName& getClsName() const { return m_clsName; } @@ -183,7 +183,7 @@ class IGraftlet protected: IGraftlet(const ClsName& name = ClsName() ) : m_clsName(name) { } - virtual void initOnce(const graft::CommonOpts& opts) = 0; + virtual void initOnce(const graft::CommonOpts& opts, graft::Context& ctx) = 0; private: using TypeIndex2any = std::map >; using Map = std::map; diff --git a/include/lib/graft/serveropts.h b/include/lib/graft/serveropts.h index 9fc5bea5..314ed5a5 100644 --- a/include/lib/graft/serveropts.h +++ b/include/lib/graft/serveropts.h @@ -10,6 +10,7 @@ struct CommonOpts { // testnet flag bool testnet; + std::string config_filename; // data directory - base directory where supernode stake wallet and other supernodes wallets are located std::string data_dir; std::string wallet_public_address; @@ -25,7 +26,6 @@ struct IPFilterOpts struct ConfigOpts { - std::string config_filename; std::string http_address; std::string coap_address; double http_connection_timeout; diff --git a/src/lib/graft/sys_info_request.cpp b/src/lib/graft/sys_info_request.cpp index d4d2330e..e5be37ac 100644 --- a/src/lib/graft/sys_info_request.cpp +++ b/src/lib/graft/sys_info_request.cpp @@ -48,7 +48,7 @@ Status handler(const Vars& vars, const Input& input, Ctx& ctx, Output& output) auto& cfg = out.configuration; const ConfigOpts& co = ctx.handlerAPI()->configOpts(); - cfg.config_filename = co.config_filename; + cfg.config_filename = co.common.config_filename; cfg.http_address = co.http_address; cfg.coap_address = co.coap_address; cfg.http_connection_timeout = co.http_connection_timeout; diff --git a/src/supernode/server.cpp b/src/supernode/server.cpp index 4e691476..855af96e 100644 --- a/src/supernode/server.cpp +++ b/src/supernode/server.cpp @@ -78,7 +78,7 @@ void GraftServer::getThreadPoolInfo(uint64_t& activeWorkers, uint64_t& expelledW void GraftServer::initGraftlets() { if(m_graftletLoader) return; - m_graftletLoader = std::make_unique(getCopts().common); + m_graftletLoader = std::make_unique(getCopts().common, getLooper().getGcm()); LOG_PRINT_L1("Searching graftlets"); for(auto& it : getCopts().graftlet_dirs) { @@ -534,7 +534,7 @@ bool GraftServer::initConfigOption(int argc, const char** argv, ConfigOpts& conf details::init_log(config, vm); // - configOpts.config_filename = config_filename; + configOpts.common.config_filename = config_filename; ConfigIniSubtree server_conf = config.get_child("server"); configOpts.http_address = server_conf.get("http-address"); diff --git a/src/supernode/supernode.cpp b/src/supernode/supernode.cpp index 204654a9..79cf8de1 100644 --- a/src/supernode/supernode.cpp +++ b/src/supernode/supernode.cpp @@ -52,7 +52,7 @@ bool Supernode::initConfigOption(int argc, const char** argv, ConfigOpts& config ConfigOptsEx& coptsex = static_cast(configOpts); assert(&m_configEx == &coptsex); - ConfigIniSubtree config = ConfigIniSubtree::create(m_configEx.config_filename); + ConfigIniSubtree config = ConfigIniSubtree::create(m_configEx.common.config_filename); const ConfigIniSubtree server_conf = config.get_child("server"); m_configEx.stake_wallet_name = server_conf.get("stake-wallet-name", "stake-wallet"); diff --git a/test/cryptonode_handlers_test.cpp b/test/cryptonode_handlers_test.cpp index ae5aeb34..f61169b5 100644 --- a/test/cryptonode_handlers_test.cpp +++ b/test/cryptonode_handlers_test.cpp @@ -62,7 +62,7 @@ struct CryptonodeHandlersTest : public ::testing::Test LOG_PRINT_L1("L1"); LOG_PRINT_L2("L2"); - ConfigOpts copts {"", "localhost:8855", "localhost:8856", 5.0, 5.0, 0, 0, 1000, "localhost:28281/sendrawtransaction", 1000, -1, {}, 60000}; + ConfigOpts copts {"localhost:8855", "localhost:8856", 5.0, 5.0, 0, 0, 1000, "localhost:28281/sendrawtransaction", 1000, -1, {}, 60000}; Router router; diff --git a/test/graftlets_test.cpp b/test/graftlets_test.cpp index 01054566..0fbcfb87 100644 --- a/test/graftlets_test.cpp +++ b/test/graftlets_test.cpp @@ -177,7 +177,8 @@ TEST(DependencyGraph, dependencies) TEST(Graftlets, calls) { graft::CommonOpts opts; - graftlet::GraftletLoader loader(opts); + graft::GlobalContextMap gcm; + graftlet::GraftletLoader loader(opts, gcm); loader.findGraftletsInDirectory("./", "so"); loader.findGraftletsInDirectory("./graftlets", "so"); @@ -261,26 +262,27 @@ TEST(Graftlets, calls) TEST(Graftlets, exceptionList) { graft::CommonOpts opts; + graft::GlobalContextMap gcm; #define VER(a,b) GRAFTLET_MKVER(a,b) using GL = graftlet::GraftletLoader; { GL::setGraftletsExceptionList({}); - GL loader(opts); + GL loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); IGraftlet::EndpointsVec endpoints = loader.getEndpoints(); EXPECT_EQ(endpoints.size(), 4); } { GL::setGraftletsExceptionList({ {"myGraftlet", {{VER(4,2), VER(5,1)}, {VER(1,0), VER(1,0)}} } }); - GL loader(opts); + GL loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); IGraftlet::EndpointsVec endpoints = loader.getEndpoints(); EXPECT_EQ(endpoints.size(), 4); } { GL::setGraftletsExceptionList({ {"myGraftlet1", {{VER(4,2), VER(5,1)}, {VER(1,0), VER(1,0)}} } }); - GL loader(opts); + GL loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); IGraftlet::EndpointsVec endpoints = loader.getEndpoints(); EXPECT_EQ(endpoints.size(), 2); @@ -289,7 +291,7 @@ TEST(Graftlets, exceptionList) GL::setGraftletsExceptionList({ {"myGraftlet", {{VER(4,2), VER(5,1)}, {VER(1,0), VER(1,1)}} }, {"myGraftlet1", {{VER(4,2), VER(5,1)}, {VER(1,0), VER(1,0)}} } }); - GL loader(opts); + GL loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); IGraftlet::EndpointsVec endpoints = loader.getEndpoints(); EXPECT_EQ(endpoints.size(), 0); @@ -303,6 +305,7 @@ TEST(Graftlets, exceptionList) TEST(Graftlets, checkFwVersion) { graft::CommonOpts opts; + graft::GlobalContextMap gcm; #define VER(a,b) GRAFTLET_MKVER(a,b) using Version = graftlet::GraftletLoader::Version; @@ -310,7 +313,7 @@ TEST(Graftlets, checkFwVersion) Version save_ver = fwVersion; { - graftlet::GraftletLoader loader(opts); + graftlet::GraftletLoader loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); IGraftlet::EndpointsVec endpoints = loader.getEndpoints(); EXPECT_EQ(endpoints.size(), 4); @@ -318,7 +321,7 @@ TEST(Graftlets, checkFwVersion) { graftlet::GraftletLoader::setFwVersion( VER(0,5) ); - graftlet::GraftletLoader loader(opts); + graftlet::GraftletLoader loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); IGraftlet::EndpointsVec endpoints = loader.getEndpoints(); EXPECT_EQ(endpoints.size(), 2); @@ -369,7 +372,8 @@ TEST_F(GraftServerTest, graftletsPeriodic) m_copts.timer_poll_interval_ms = 50; graft::CommonOpts opts; - graftlet::GraftletLoader loader(opts); + graft::GlobalContextMap gcm; + graftlet::GraftletLoader loader(opts, gcm); loader.findGraftletsInDirectory("./graftlets", "so"); graftlet::GraftletHandler plugin = loader.buildAndResolveGraftlet("myGraftlet"); diff --git a/test/json_rpc_test.cpp b/test/json_rpc_test.cpp index 8b6f7f9e..28a3ceb3 100644 --- a/test/json_rpc_test.cpp +++ b/test/json_rpc_test.cpp @@ -157,7 +157,7 @@ struct JsonRpcTest : public ::testing::Test void startServer() { - ConfigOpts sopts {"", "localhost:8855", "localhost:8856", 5.0, 5.0, 0, 0, 1000, "localhost:28281/sendrawtransaction", 1000, -1, {}, 60000}; + ConfigOpts sopts {"localhost:8855", "localhost:8856", 5.0, 5.0, 0, 0, 1000, "localhost:28281/sendrawtransaction", 1000, -1, {}, 60000}; Router router; Router::Handler3 h3(nullptr, jsonRpcHandler, nullptr); router.addRoute("/jsonrpc/test", METHOD_POST, h3); From 8f6e3452aa1f85ca6a81d8108b19868773ac1b15 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Thu, 28 Mar 2019 17:08:03 +0200 Subject: [PATCH 07/17] Mandatory graftlet list support. --- include/lib/graft/GraftletLoader.h | 7 ++--- include/lib/graft/serveropts.h | 1 + src/lib/graft/GraftletLoader.cpp | 43 ++++++++++++++++++++++++++---- src/supernode/server.cpp | 2 +- src/supernode/supernode.cpp | 2 ++ 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index 4ffe491e..8180e631 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -112,6 +112,7 @@ class GraftletLoader public: using DllName = std::string; using Version = int; + using Mandatory = bool; using GraftletExceptionList = std::vector< std::pair< DllName, std::vector< std::pair >>>; GraftletLoader(const graft::CommonOpts& opts, graft::GlobalContextMap& gcm) : m_opts(opts), m_ctx(gcm) { } @@ -122,7 +123,7 @@ class GraftletLoader static void setGraftletsExceptionList(const GraftletExceptionList& gel); void findGraftletsInDirectory(std::string additionalDir, std::string extension); - void checkDependencies(); + void checkDependencies(const std::string& mandatories = ""); GraftletHandlerT buildAndResolveGraftlet(const DllName& dllName) { @@ -299,7 +300,7 @@ class GraftletLoader //we can use functions in a dll until we release object of boost::dll::shared_library //dll name -> (lib, version, path) - std::map> m_name2lib; + std::map> m_name2lib; //dll name -> registry std::map m_name2registries; //dll (name, type_index of BaseT) -> (class name, any of BaseT) @@ -315,7 +316,7 @@ class GraftletLoader::DependencyGraph using Version = GraftletLoader::Version; using Dependencies = GraftletLoader::Dependencies; - void initialize(GraftletLoader& gl); + void initialize(GraftletLoader& gl, const Dependencies& mandatories); void removeFailedDependants(GraftletLoader& gl); //returns error if dont_throw == true std::string findCycles(bool dont_throw = false); diff --git a/include/lib/graft/serveropts.h b/include/lib/graft/serveropts.h index 314ed5a5..160d2095 100644 --- a/include/lib/graft/serveropts.h +++ b/include/lib/graft/serveropts.h @@ -41,6 +41,7 @@ struct ConfigOpts IPFilterOpts ipfilter; CommonOpts common; bool duplicate_filter_enabled = true; + std::string mandatory_graftlet_dependencies; void check_asserts() const { diff --git a/src/lib/graft/GraftletLoader.cpp b/src/lib/graft/GraftletLoader.cpp index 4b6bddd7..a877c32f 100644 --- a/src/lib/graft/GraftletLoader.cpp +++ b/src/lib/graft/GraftletLoader.cpp @@ -126,7 +126,7 @@ void GraftletLoader::findGraftletsInDirectory(std::string directory, std::string auto res = m_name2lib.emplace( std::make_pair(dllName, - std::make_tuple( std::move(lib), graftletVersion, std::move(dll_path), std::move(dependencies) )) + std::make_tuple( std::move(lib), graftletVersion, std::move(dll_path), std::move(dependencies), false )) ); if(!res.second) throw std::runtime_error("A plugin with the name '" + dllName + "' already exists"); auto res1 = m_name2registries.emplace( std::make_pair(dllName, graftletRegistry) ); @@ -214,7 +214,7 @@ void GraftletLoader::DependencyGraph::initialize(const std::vector> vec; vec.reserve(gl.m_name2lib.size()); @@ -224,6 +224,7 @@ void GraftletLoader::DependencyGraph::initialize(GraftletLoader& gl) Version& ver = std::get<1>(item.second); vec.emplace_back(std::make_tuple(item.first, ver, deps)); } + vec.emplace_back(std::make_tuple("", 0, mandatories)); initialize(vec); } @@ -243,6 +244,10 @@ std::vector GraftletLoader::Dependency //special case if the dependency format is violated; see initialize(...) if(list.size() == 1 && list.begin()->first.empty()) { + if(name.empty()) + {//mandatory + throw graft::exit_error("Required graftlets have invalid dependency format. Cannot continue."); + } LOG_PRINT_L2("graftlet '") << name << "' has invalid dependency format. it will be unloaded."; ok = false; } @@ -256,6 +261,12 @@ std::vector GraftletLoader::Dependency auto it = m_graph.find(dep_name); if(it == m_graph.end()) { + if(name.empty()) + { + std::ostringstream oss; + oss << "The graftlet '" << dep_name << "' is required, but not found or cannot be loaded. Cannot continue."; + throw graft::exit_error(oss.str()); + } LOG_PRINT_L2("graftlet '") << name << "' depends on '" << dep_name << "' which is not found. it will be unloaded."; ok = false; break; @@ -263,7 +274,16 @@ std::vector GraftletLoader::Dependency Version dep_ver = m_dll2ver[dep_name]; if(m_RemoveIfCmpMinverVer(dep_ver, minver)) { - LOG_PRINT_L2("graftlet '") << name << "' depends on '" << dep_name << "' which verson " + std::string msg = (name.empty())? "Mandatory graftlets depend on" : "graftlet '" + name + "' depends on"; + if(name.empty()) + { + std::ostringstream oss; + oss << "The graftlet '" << dep_name << "' is required, but its version " + << GRAFTLET_Major(dep_ver) << "." << GRAFTLET_Minor(dep_ver) << " is less than required " + << GRAFTLET_Major(minver) << "." << GRAFTLET_Minor(minver) << ". Cannot continue."; + throw graft::exit_error(oss.str()); + } + LOG_PRINT_L2("graftlet '") << name << "' depends on '" << dep_name << "' which version " << GRAFTLET_Major(dep_ver) << "." << GRAFTLET_Minor(dep_ver) << " is less than required " << GRAFTLET_Major(minver) << "." << GRAFTLET_Minor(minver) << ". it will be unloaded."; ok = false; @@ -377,12 +397,25 @@ std::string GraftletLoader::DependencyGraph::findCycles(bool dont_throw) return std::string(); } -void GraftletLoader::checkDependencies() +void GraftletLoader::checkDependencies(const std::string& mandatories) { DependencyGraph graph; - graph.initialize(*this); + graph.initialize(*this, mandatories); graph.removeFailedDependants(*this); graph.findCycles(); + + {//set mandatory flags + auto it = graph.m_graph.find(""); + assert(it != graph.m_graph.end()); + for(auto& item : it->second) + { + const DllName& name = item.first; + auto it1 = m_name2lib.find(name); + assert(it1 != m_name2lib.end()); + Mandatory& mandatory = std::get<4>( it1->second ); + mandatory = true; + } + } } } //namespace graftlet diff --git a/src/supernode/server.cpp b/src/supernode/server.cpp index 855af96e..de066037 100644 --- a/src/supernode/server.cpp +++ b/src/supernode/server.cpp @@ -85,7 +85,7 @@ void GraftServer::initGraftlets() LOG_PRINT_L1("Searching graftlets in directory '") << it << "'"; m_graftletLoader->findGraftletsInDirectory(it, "so"); } - m_graftletLoader->checkDependencies(); + m_graftletLoader->checkDependencies(getCopts().mandatory_graftlet_dependencies); } void GraftServer::initGraftletRouters() diff --git a/src/supernode/supernode.cpp b/src/supernode/supernode.cpp index 79cf8de1..b1f99338 100644 --- a/src/supernode/supernode.cpp +++ b/src/supernode/supernode.cpp @@ -52,6 +52,8 @@ bool Supernode::initConfigOption(int argc, const char** argv, ConfigOpts& config ConfigOptsEx& coptsex = static_cast(configOpts); assert(&m_configEx == &coptsex); + m_configEx.mandatory_graftlet_dependencies = "walletAddress : 1.1"; + ConfigIniSubtree config = ConfigIniSubtree::create(m_configEx.common.config_filename); const ConfigIniSubtree server_conf = config.get_child("server"); From 48b475436d32c42bff491d81fc9e7f72a370f8ca Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 3 Apr 2019 15:52:18 +0300 Subject: [PATCH 08/17] JsonBlob added --- include/lib/graft/serialize.h | 64 ++++++++++++++++++++++++++++++ test/graft_server_test.cpp | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/include/lib/graft/serialize.h b/include/lib/graft/serialize.h index a6ea5248..1d9b2039 100644 --- a/include/lib/graft/serialize.h +++ b/include/lib/graft/serialize.h @@ -38,6 +38,7 @@ * std::unique_ptr, std::shared_ptr | depends/null * std::map, std::unordered_map | object * JsonSerializable | object + * JsonBlob(std::string) | object * ---------------------------------------------------------+-------------- * * Example of structure definitions: @@ -64,8 +65,71 @@ * GRAFT_DEFINE_IO_STRUCT(Payments, * (std::vector, payments) * ); + * + * You can use JsonBlob type instead of nested struct object. Thus, instead of + * + * GRAFT_DEFINE_IO_STRUCT(Nested, + * (int, x), + * (int, y) + * ); + * + * GRAFT_DEFINE_IO_STRUCT(Wrapper, + * (int, nestedType), + * (Nested, nested) + * ); + * + * following can be used + * + * GRAFT_DEFINE_IO_STRUCT(Wrapper, + * (int, nestedType), + * (JsonBlob, nested) + * ); + * + * and nested.json represents nested object as json string. + * */ +struct JsonBlob +{ + std::string json{"{}"}; +}; + +namespace ReflectiveRapidJSON +{ +namespace JsonReflector +{ + +template <> +inline void push( + const JsonBlob &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) +{ + RAPIDJSON_NAMESPACE::Document doc; + std::string s = reflectable.json; + doc.Parse(s.c_str()); + if(!doc.IsObject()) throw std::runtime_error("parsed JsonBlob is not an object"); + value = doc.GetObject(); +} + +template <> +inline void pull(JsonBlob &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (!value.IsObject()) { + if (errors) { + errors->reportTypeMismatch(value.GetType()); + } + return; + } + RAPIDJSON_NAMESPACE::StringBuffer sb; + RAPIDJSON_NAMESPACE::Writer writer( sb ); + value.Accept(writer); + reflectable.json = sb.GetString(); +} + +} //namespace JsonReflector +} //namespace ReflectiveRapidJSON + + namespace graft::serializer { class JsonParseError : public std::runtime_error diff --git a/test/graft_server_test.cpp b/test/graft_server_test.cpp index e1c7a892..25b8dade 100644 --- a/test/graft_server_test.cpp +++ b/test/graft_server_test.cpp @@ -80,6 +80,81 @@ TEST(InOut, common) EXPECT_EQ(s_out, s); } +TEST(InOut, nested) +{ + GRAFT_DEFINE_IO_STRUCT(Nested, + (uint64, amount), + (uint32, block_height), + (std::string, payment_id), + (std::string, tx_hash), + (uint32, unlock_time) + ); + + GRAFT_DEFINE_IO_STRUCT(Cont, + (int, val), + (Nested, nested), + (std::string, s) + ); + + GRAFT_DEFINE_IO_STRUCT(ContX, + (int, val), + (JsonBlob, nested), + (std::string, s) + ); + + Nested nested{ {}, 10, 20, "30", "40", 50 }; + Cont cont{ {}, 60, nested, "70" }; + + //cont -> json + graft::Output output; + output.loadT(cont); + std::string cont_json = output.body; + + //json -> cont1 + graft::Input input; input.body = cont_json; + Cont cont1; input.getT(cont1); + EXPECT_EQ(cont1.nested.amount, 10); + EXPECT_EQ(cont1.nested.block_height, 20); + EXPECT_EQ(cont1.nested.payment_id, "30"); + EXPECT_EQ(cont1.nested.tx_hash, "40"); + EXPECT_EQ(cont1.nested.unlock_time, 50); + EXPECT_EQ(cont1.val, 60); + EXPECT_EQ(cont1.s, "70"); + + + //nested -> json + output.loadT(nested); + std::string nested_json = output.body; + + //contx -> json + ContX contx; + contx.val = 60; + contx.nested.json = nested_json; + contx.s = "70"; + output.loadT(contx); + EXPECT_EQ(output.body, cont_json); + + //json -> contx1 + input.body = output.body; + ContX contx1; input.getT(contx1); + EXPECT_EQ(contx1.nested.json, nested_json); + EXPECT_EQ(contx1.val, 60); + EXPECT_EQ(contx1.s, "70"); + + bool except = false; + try + { + contx.nested.json = "{invalid : 10 "; + output.loadT(contx); + } + catch(std::exception& e) + { + except = true; + std::cout << "exception : " << e.what() << "\n"; + } + EXPECT_EQ(except, true); +} + namespace graft { namespace serializer { template From 9924a080cf8b007642f5af70f803cebc7847c27f Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 3 Apr 2019 16:18:20 +0300 Subject: [PATCH 09/17] getGraftletInfo optional exported function added --- graftlets/TestGraftlet.cpp | 25 +++++++++++++++++++++++++ include/lib/graft/GraftletLoader.h | 5 +++-- include/lib/graft/GraftletRegistry.h | 3 +++ include/lib/graft/IGraftlet.h | 7 +++++++ src/lib/graft/GraftletLoader.cpp | 9 ++++++++- 5 files changed, 46 insertions(+), 3 deletions(-) diff --git a/graftlets/TestGraftlet.cpp b/graftlets/TestGraftlet.cpp index e2a70386..d9f5ea97 100644 --- a/graftlets/TestGraftlet.cpp +++ b/graftlets/TestGraftlet.cpp @@ -3,12 +3,33 @@ #include "lib/graft/GraftletRegistry.h" #include "lib/graft/IGraftlet.h" #include "lib/graft/ConfigIni.h" +#include "lib/graft/serialize.h" #include #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "graftlet.TestGraftlet" +namespace +{ + +std::atomic testHandlerCount{0}; +std::atomic testHandler1Count{0}; + +std::string Info() +{ + GRAFT_DEFINE_IO_STRUCT(InfoStruct, + (int, testHandlerCount), + (int, testHandler1Count) + ); + + InfoStruct info{ {}, testHandlerCount, testHandler1Count }; + + return makeInfo(info); +} + +} + class TestGraftlet: public IGraftlet { public: @@ -37,6 +58,7 @@ class TestGraftlet: public IGraftlet graft::Status testHandler(const graft::Router::vars_t& vars, const graft::Input& input, graft::Context& ctx, graft::Output& output) { + ++testHandlerCount; std::string id; { auto it = vars.find("id"); @@ -48,6 +70,7 @@ class TestGraftlet: public IGraftlet graft::Status testHandler1(const graft::Router::vars_t& vars, const graft::Input& input, graft::Context& ctx, graft::Output& output) { + ++testHandler1Count; std::string id; { auto it = vars.find("id"); @@ -102,6 +125,8 @@ GRAFTLET_EXPORTS_BEGIN("myGraftlet", GRAFTLET_MKVER(1,1)); GRAFTLET_PLUGIN(TestGraftlet, IGraftlet, "testGL"); GRAFTLET_EXPORTS_END +GRAFTLET_PLUGIN_INFO(Info) + GRAFTLET_PLUGIN_DEFAULT_CHECK_FW_VERSION(GRAFTLET_MKVER(0,3)) std::string TestGraftlet::value; diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index 8180e631..2fb55c64 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -112,7 +112,6 @@ class GraftletLoader public: using DllName = std::string; using Version = int; - using Mandatory = bool; using GraftletExceptionList = std::vector< std::pair< DllName, std::vector< std::pair >>>; GraftletLoader(const graft::CommonOpts& opts, graft::GlobalContextMap& gcm) : m_opts(opts), m_ctx(gcm) { } @@ -146,6 +145,8 @@ class GraftletLoader using ClsName = std::string; using DllPath = std::string; using Dependencies = std::string; // format: dllName:minVersion,dllName1:minVersion1, ... + using Mandatory = bool; + using InfoFunction = std::function; using ExceptionRngVec = std::vector>; using ExceptionMap = std::map; @@ -300,7 +301,7 @@ class GraftletLoader //we can use functions in a dll until we release object of boost::dll::shared_library //dll name -> (lib, version, path) - std::map> m_name2lib; + std::map> m_name2lib; //dll name -> registry std::map m_name2registries; //dll (name, type_index of BaseT) -> (class name, any of BaseT) diff --git a/include/lib/graft/GraftletRegistry.h b/include/lib/graft/GraftletRegistry.h index 419bed70..9b699c78 100644 --- a/include/lib/graft/GraftletRegistry.h +++ b/include/lib/graft/GraftletRegistry.h @@ -66,6 +66,8 @@ extern "C" GRAFTLET_EXPORT const char* getGraftletDependencies() { return dependencies; } #define GRAFTLET_PLUGIN_CHECK_FW_VERSION() \ extern "C" GRAFTLET_EXPORT bool checkFwVersion( int fwVersion ); +#define GRAFTLET_PLUGIN_INFO(infoFunction) \ + extern "C" GRAFTLET_EXPORT std::string getGraftletInfo() { return infoFunction(); } #define GRAFTLET_PLUGIN_DEFAULT_CHECK_FW_VERSION(minversion) \ extern "C" GRAFTLET_EXPORT bool checkFwVersion( int fwVersion ) { return minversion <= fwVersion; } @@ -153,6 +155,7 @@ static const char* getGraftletName(); static int getGraftletVersion(); static const char* getGraftletDependencies(); static bool checkFwVersion( int fwVersion ); +static std::string getGraftletInfo(); static GraftletRegistry* getGraftletRegistry(); inline std::string getVersionStr(int version) diff --git a/include/lib/graft/IGraftlet.h b/include/lib/graft/IGraftlet.h index 02bf9207..4d3e6abd 100644 --- a/include/lib/graft/IGraftlet.h +++ b/include/lib/graft/IGraftlet.h @@ -193,3 +193,10 @@ class IGraftlet Map m_map; }; +template +std::string makeInfo(const T& t) +{ + graft::Output out; + out.loadT(t); + return out.body; +} diff --git a/src/lib/graft/GraftletLoader.cpp b/src/lib/graft/GraftletLoader.cpp index a877c32f..9b5ddd7a 100644 --- a/src/lib/graft/GraftletLoader.cpp +++ b/src/lib/graft/GraftletLoader.cpp @@ -122,11 +122,18 @@ void GraftletLoader::findGraftletsInDirectory(std::string directory, std::string continue; } + InfoFunction infoFunction = nullptr; + if(lib.has("getGraftletInfo")) + { + auto infoFunc = dll::import(lib, "getGraftletInfo" ); + infoFunction = [infoFunc]()->std::string { return infoFunc(); }; + } + LOG_PRINT_L2("The graftlet accepted '") << dllName << " version " << graftletVersion << " path " << dll_path; auto res = m_name2lib.emplace( std::make_pair(dllName, - std::make_tuple( std::move(lib), graftletVersion, std::move(dll_path), std::move(dependencies), false )) + std::make_tuple( std::move(lib), graftletVersion, std::move(dll_path), std::move(dependencies), false, std::move(infoFunction) )) ); if(!res.second) throw std::runtime_error("A plugin with the name '" + dllName + "' already exists"); auto res1 = m_name2registries.emplace( std::make_pair(dllName, graftletRegistry) ); From beb42d7d9830b299da91bcb3db3465008eedc175 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 3 Apr 2019 16:36:17 +0300 Subject: [PATCH 10/17] parseDependencies function separated --- include/lib/graft/GraftletLoader.h | 3 + src/lib/graft/GraftletLoader.cpp | 97 ++++++++++++++++-------------- 2 files changed, 54 insertions(+), 46 deletions(-) diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index 2fb55c64..4cd262fa 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -316,6 +316,9 @@ class GraftletLoader::DependencyGraph using DllName = GraftletLoader::DllName; using Version = GraftletLoader::Version; using Dependencies = GraftletLoader::Dependencies; + using DependencyList = std::list>; + + static DependencyList parseDependencies(std::string_view deps); void initialize(GraftletLoader& gl, const Dependencies& mandatories); void removeFailedDependants(GraftletLoader& gl); diff --git a/src/lib/graft/GraftletLoader.cpp b/src/lib/graft/GraftletLoader.cpp index 9b5ddd7a..db890e8d 100644 --- a/src/lib/graft/GraftletLoader.cpp +++ b/src/lib/graft/GraftletLoader.cpp @@ -157,6 +157,56 @@ void GraftletLoader::findGraftletsInDirectory(std::string directory, std::string } } +GraftletLoader::DependencyGraph::DependencyList GraftletLoader::DependencyGraph::parseDependencies(std::string_view deps) +{ + DependencyList list; + if(!deps.empty()) + { + for(std::string::size_type s = 0;;) + { + std::string::size_type e = deps.find(',',s); + std::string_view nv = (e == std::string::npos)? deps.substr(s) : deps.substr(s,e-s); + + std::string snv{nv}; + std::regex regex(R"(^\s*([^:\s]*)\s*(:\s*([0-9]+)\s*(\.\s*([0-9]+))?)?\s*$)"); + std::smatch m; + if(!std::regex_match(snv, m, regex)) + {//invalid format of dependencies, the dll should be removed later + list.clear(); + list.emplace_back(std::make_pair("",0)); + break; + } + assert(1 < m.size()); + std::string name = m[1]; + if(name.empty()) + {//invalid format + list.clear(); + list.emplace_back(std::make_pair("",0)); + break; + } + Version minver = 0; + if(2 < m.size() && m[2].matched) + { + assert(3 < m.size() && m[3].matched); + int Ma = std::stoi(m[3]); + int mi = 0; + if(4 < m.size() && m[4].matched) + { + assert(5 < m.size() && m[5].matched); + mi = std::stoi(m[5]); + } + minver = GRAFTLET_MKVER(Ma, mi); + } + + list.push_back(std::make_pair(DllName(name),minver)); + + if(e == std::string::npos) break; + s = e + 1; + } + } + return list; +} + void GraftletLoader::DependencyGraph::initialize(const std::vector>& vec) { m_graph.clear(); @@ -168,52 +218,7 @@ void GraftletLoader::DependencyGraph::initialize(const std::vector(item); std::string_view deps = std::get<2>(item); //Dependencies //make list of dependencies - std::list> list; - if(!deps.empty()) - { - for(std::string::size_type s = 0;;) - { - std::string::size_type e = deps.find(',',s); - std::string_view nv = (e == std::string::npos)? deps.substr(s) : deps.substr(s,e-s); - - std::string snv{nv}; - std::regex regex(R"(^\s*([^:\s]*)\s*(:\s*([0-9]+)\s*(\.\s*([0-9]+))?)?\s*$)"); - std::smatch m; - if(!std::regex_match(snv, m, regex)) - {//invalid format of dependencies, the dll should be removed later - list.clear(); - list.emplace_back(std::make_pair("",0)); - break; - } - assert(1 < m.size()); - std::string name = m[1]; - if(name.empty()) - {//invalid format - list.clear(); - list.emplace_back(std::make_pair("",0)); - break; - } - Version minver = 0; - if(2 < m.size() && m[2].matched) - { - assert(3 < m.size() && m[3].matched); - int Ma = std::stoi(m[3]); - int mi = 0; - if(4 < m.size() && m[4].matched) - { - assert(5 < m.size() && m[5].matched); - mi = std::stoi(m[5]); - } - minver = GRAFTLET_MKVER(Ma, mi); - } - - list.push_back(std::make_pair(DllName(name),minver)); - - if(e == std::string::npos) break; - s = e + 1; - } - } - + DependencyList list = parseDependencies(deps); auto res = m_graph.emplace(dllName, std::move(list)); assert(res.second); auto res1 = m_dll2ver.emplace(dllName, ver); From d1bb67d026518a55537936ab1b1fa5309e19aca0 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 3 Apr 2019 16:43:56 +0300 Subject: [PATCH 11/17] IGraftlet::getFuncNames added --- include/lib/graft/IGraftlet.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/lib/graft/IGraftlet.h b/include/lib/graft/IGraftlet.h index 4d3e6abd..a7e55d08 100644 --- a/include/lib/graft/IGraftlet.h +++ b/include/lib/graft/IGraftlet.h @@ -38,8 +38,9 @@ class IGraftlet using FuncName = std::string; using EndpointPath = std::string; using Methods = int; - using EndpointsVec = std::vector< std::tuple >; + using EndpointsVec = std::vector< std::tuple >; using PeriodicVec = std::vector< Periodic >; + using FuncNameVec = std::vector< FuncName >; IGraftlet() = delete; virtual ~IGraftlet() = default; @@ -55,6 +56,16 @@ class IGraftlet const ClsName& getClsName() const { return m_clsName; } + FuncNameVec getFuncNames() + { + FuncNameVec res; + for(auto& it : m_map) + { + res.emplace_back(it.first); + } + return res; + } + EndpointsVec getEndpoints() { EndpointsVec res; @@ -65,13 +76,14 @@ class IGraftlet auto it1 = ti2any.find(ti); if(it1 == ti2any.end()) continue; - std::any& any = std::get<0>(it1->second); - EndpointPath& endpoint = std::get<1>(it1->second); - Methods& methods = std::get<2>(it1->second); + const FuncName& name = it.first; + const std::any& any = std::get<0>(it1->second); + const EndpointPath& endpoint = std::get<1>(it1->second); + const Methods& methods = std::get<2>(it1->second); graft::Router::Handler handler = std::any_cast(any); - res.emplace_back(std::make_tuple(endpoint, methods, handler)); + res.emplace_back(std::make_tuple(endpoint, methods, handler, name)); } return res; } From 5837b9e620193c6d4d479fff2ad2422e6d53fed3 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Wed, 3 Apr 2019 17:06:36 +0300 Subject: [PATCH 12/17] Graftlets info added. --- include/lib/graft/GraftletLoader.h | 9 +++ include/lib/graft/graftlets_sys_info.h | 65 ++++++++++++++++++++++ include/lib/graft/sys_info_request.h | 16 +----- src/lib/graft/GraftletLoader.cpp | 77 ++++++++++++++++++++++++++ src/lib/graft/sys_info_request.cpp | 9 +++ 5 files changed, 162 insertions(+), 14 deletions(-) create mode 100644 include/lib/graft/graftlets_sys_info.h diff --git a/include/lib/graft/GraftletLoader.h b/include/lib/graft/GraftletLoader.h index 4cd262fa..4382bad1 100644 --- a/include/lib/graft/GraftletLoader.h +++ b/include/lib/graft/GraftletLoader.h @@ -31,9 +31,13 @@ #include #include +namespace graft::request::system_info { struct GraftletInfo; } + namespace graftlet { +using GraftletInfo = graft::request::system_info::GraftletInfo; + template class GraftletHandlerT { @@ -139,6 +143,8 @@ class GraftletLoader return getPeriodicsT(); } + void fillInfo(std::vector& graftletsInfo); + class DependencyGraph; friend class GraftletLoader::DependencyGraph; private: @@ -215,6 +221,9 @@ class GraftletLoader return res; } + template + void fillInfoT(std::vector& graftletsInfo); + template GraftletHandlerT buildAndResolveGraftletT(const DllName& dllName) { diff --git a/include/lib/graft/graftlets_sys_info.h b/include/lib/graft/graftlets_sys_info.h new file mode 100644 index 00000000..4e7b91f1 --- /dev/null +++ b/include/lib/graft/graftlets_sys_info.h @@ -0,0 +1,65 @@ +// Copyright (c) 2018, The Graft Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "lib/graft/serialize.h" + +namespace graft::request::system_info { + +GRAFT_DEFINE_IO_STRUCT(Dependency, + (std::string, name), + (int, min_version_major), + (int, min_version_minor) +); + +GRAFT_DEFINE_IO_STRUCT(Endpoint, + (std::string, path), + (std::string, methods), + (std::string, name) +); + +GRAFT_DEFINE_IO_STRUCT(Class, + (std::string, name), + (std::vector, methods) +); + +GRAFT_DEFINE_IO_STRUCT(GraftletInfo, + (std::string, name), + (std::string, path), + (int, version_major), + (int, version_minor), + (bool, mandatory), + (std::vector, dependencies), + (std::vector, end_points), + (std::vector, classes), + (JsonBlob, info) +); + +} //namespace graft::request::system_info + diff --git a/include/lib/graft/sys_info_request.h b/include/lib/graft/sys_info_request.h index 1ac63752..3d12583c 100644 --- a/include/lib/graft/sys_info_request.h +++ b/include/lib/graft/sys_info_request.h @@ -6,6 +6,7 @@ #include #include "lib/graft/jsonrpc.h" +#include "lib/graft/graftlets_sys_info.h" namespace graft { template class RouterT; class InHttp; class OutHttp; using Router = RouterT; } @@ -73,19 +74,6 @@ GRAFT_DEFINE_IO_STRUCT_INITED(DapiEntry, (std::vector, end_points, std::vector()) ); -GRAFT_DEFINE_IO_STRUCT_INITED(GraftletDependency, - (std::string, name, std::string()), - (std::string, min_version, std::string()) -); - -GRAFT_DEFINE_IO_STRUCT_INITED(Graftlet, - (std::string, name, std::string()), - (std::string, version, std::string()), - (std::string, status, std::string()), - (std::vector, end_points, std::vector()), - (std::vector, requires, std::vector()) -); - GRAFT_DEFINE_IO_STRUCT_INITED(Request, (int, request, 0) ); @@ -95,7 +83,7 @@ GRAFT_DEFINE_IO_STRUCT_INITED(Response, (Configuratioon, configuration, Configuratioon()), (Running, running_info, Running()), (std::vector, dapi, std::vector()), - (std::vector, graftlets, std::vector()) + (std::vector, graftlets, std::vector{}) ); GRAFT_DEFINE_JSON_RPC_REQUEST(ReqJsonRpc, Request) diff --git a/src/lib/graft/GraftletLoader.cpp b/src/lib/graft/GraftletLoader.cpp index db890e8d..ca64fbe1 100644 --- a/src/lib/graft/GraftletLoader.cpp +++ b/src/lib/graft/GraftletLoader.cpp @@ -24,6 +24,8 @@ #include "lib/graft/graft_exception.h" #define INCLUDE_DEPENDENCY_GRAPH #include "lib/graft/GraftletLoader.h" +#include "lib/graft/serialize.h" +#include "lib/graft/graftlets_sys_info.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "graftlet.GraftletLoader" @@ -157,6 +159,81 @@ void GraftletLoader::findGraftletsInDirectory(std::string directory, std::string } } +template +void GraftletLoader::fillInfoT(std::vector& graftletsInfo) +{ + using namespace graft::request::system_info; + graftletsInfo.clear(); + + prepareAllEndpoints(); + + for(auto& it0 : m_name2gls) + { + if(it0.first.second != std::type_index(typeid(BaseT))) continue; + + GraftletInfo graftlet; + + const DllName& name = it0.first.first; + graftlet.name = name; + { + auto it_lib = m_name2lib.find(name); + assert(it_lib != m_name2lib.end()); + + const Version& version = std::get<1>(it_lib->second); + const DllPath& dllPath = std::get<2>(it_lib->second); + const Dependencies& dependencies = std::get<3>(it_lib->second); + const Mandatory& mandatory = std::get<4>(it_lib->second); + const InfoFunction& info = std::get<5>(it_lib->second); + + graftlet.path = dllPath; + graftlet.version_major = GRAFTLET_Major(version); + graftlet.version_minor = GRAFTLET_Minor(version); + graftlet.mandatory = mandatory; + if(info) + { + graftlet.info.json = info(); + } + + std::list> list = DependencyGraph::parseDependencies(dependencies); + for(auto& it_d : list) + { + Dependency d; + d.name = it_d.first; + d.min_version_major = GRAFTLET_Major(it_d.second); + d.min_version_minor = GRAFTLET_Minor(it_d.second); + graftlet.dependencies.emplace_back( std::move(d) ); + } + } + + std::map& map = it0.second; + for(auto& it1 : map) + { + Class cls; + cls.name = it1.first; + //TODO: remove shared_ptr, it does not hold something now + std::shared_ptr concreteGraftlet = std::any_cast>(it1.second); + cls.methods = concreteGraftlet->getFuncNames(); + typename BaseT::EndpointsVec vec = concreteGraftlet->getEndpoints(); + for(auto& it_ep : vec) + { + Endpoint endpoint; + endpoint.path = std::get<0>(it_ep); + endpoint.methods = graft::Router::methodsToString( std::get<1>(it_ep) ); + endpoint.name = std::get<3>(it_ep); + + graftlet.end_points.emplace_back(std::move(endpoint)); + } + graftlet.classes.emplace_back(std::move(cls)); + } + graftletsInfo.emplace_back(std::move(graftlet)); + } +} + +void GraftletLoader::fillInfo(std::vector& graftletsInfo) +{ + fillInfoT(graftletsInfo); +} + GraftletLoader::DependencyGraph::DependencyList GraftletLoader::DependencyGraph::parseDependencies(std::string_view deps) { DependencyList list; diff --git a/src/lib/graft/sys_info_request.cpp b/src/lib/graft/sys_info_request.cpp index e5be37ac..5108ce5d 100644 --- a/src/lib/graft/sys_info_request.cpp +++ b/src/lib/graft/sys_info_request.cpp @@ -6,6 +6,7 @@ #include "lib/graft/inout.h" #include "lib/graft/router.h" #include "lib/graft/sys_info.h" +#include "lib/graft/GraftletLoader.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "supernode.sys_info" @@ -73,6 +74,14 @@ Status handler(const Vars& vars, const Input& input, Ctx& ctx, Output& output) cfg.log_categories = co.log_categories; */ + ctx.global.apply("graftletLoader", + [&out](graftlet::GraftletLoader* gl)->bool + { + if(!gl) return false; + gl->fillInfo(out.graftlets); + return true; + }); + output.load(out); return Status::Ok; } From 268cbc82a0f84519dfaf07fe49f7866c6fbd884e Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Thu, 4 Apr 2019 15:20:57 +0300 Subject: [PATCH 13/17] JsonBlob serialization fixed. --- include/lib/graft/serialize.h | 13 +++++++-- test/graft_server_test.cpp | 50 ++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 3 deletions(-) diff --git a/include/lib/graft/serialize.h b/include/lib/graft/serialize.h index 1d9b2039..9d5ce627 100644 --- a/include/lib/graft/serialize.h +++ b/include/lib/graft/serialize.h @@ -9,6 +9,8 @@ #include +#include + #define GRAFT_DEFINE_IO_STRUCT(__S__, ...) \ struct __S__ : public ReflectiveRapidJSON::JsonSerializable<__S__> { \ BOOST_HANA_DEFINE_STRUCT(__S__, __VA_ARGS__); \ @@ -105,9 +107,16 @@ inline void push( { RAPIDJSON_NAMESPACE::Document doc; std::string s = reflectable.json; - doc.Parse(s.c_str()); + const RAPIDJSON_NAMESPACE::ParseResult parseRes = doc.Parse(s.c_str()); + if (parseRes.IsError()) + { + std::ostringstream oss; + oss << "Error while parsing JsonBlob code:" << parseRes.Code() << ", offset:" << parseRes.Offset() + << ", json:'" << s << "'"; + throw std::runtime_error(oss.str()); + } if(!doc.IsObject()) throw std::runtime_error("parsed JsonBlob is not an object"); - value = doc.GetObject(); + value.CopyFrom(doc, allocator); } template <> diff --git a/test/graft_server_test.cpp b/test/graft_server_test.cpp index 25b8dade..0e40de5b 100644 --- a/test/graft_server_test.cpp +++ b/test/graft_server_test.cpp @@ -20,6 +20,9 @@ #include +namespace +{ + GRAFT_DEFINE_IO_STRUCT(Payment, (uint64, amount), (uint32, block_height), @@ -32,6 +35,8 @@ GRAFT_DEFINE_IO_STRUCT(Sstr, (std::string, s) ); +} //namespace + TEST(InOut, common) { using namespace graft; @@ -80,7 +85,50 @@ TEST(InOut, common) EXPECT_EQ(s_out, s); } -TEST(InOut, nested) +TEST(InOut, nested1) +{ + GRAFT_DEFINE_IO_STRUCT(Nested, + (uint32, unlock_time) + ); + + GRAFT_DEFINE_IO_STRUCT(Cont, + (Nested, nested), + (std::string, s) + ); + + GRAFT_DEFINE_IO_STRUCT(ContX, + (JsonBlob, nested), + (std::string, s) + ); + + Nested nested{ {}, 50 }; + Cont cont{ {}, nested, "70" }; + + //cont -> json + graft::Output output; + output.loadT(cont); + std::string cont_json = output.body; + + //json -> cont1 + graft::Input input; input.body = cont_json; + Cont cont1; input.getT(cont1); + EXPECT_EQ(cont1.nested.unlock_time, 50); + EXPECT_EQ(cont1.s, "70"); + + //nested -> json + output.loadT(nested); + std::string nested_json = output.body; + + //contx -> json + ContX contx; + contx.nested.json = nested_json; + contx.s = "70"; + graft::Output output1; + output1.loadT(contx); + EXPECT_EQ(output1.body, cont_json); +} + +TEST(InOut, nested2) { GRAFT_DEFINE_IO_STRUCT(Nested, (uint64, amount), From e7cf7ec0a5b1d87bd6edb7cb19ac7fe524bfcad6 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Fri, 5 Apr 2019 16:13:29 +0300 Subject: [PATCH 14/17] removeFailedDependants fixed --- src/lib/graft/GraftletLoader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/graft/GraftletLoader.cpp b/src/lib/graft/GraftletLoader.cpp index ca64fbe1..2db49059 100644 --- a/src/lib/graft/GraftletLoader.cpp +++ b/src/lib/graft/GraftletLoader.cpp @@ -381,16 +381,15 @@ std::vector GraftletLoader::Dependency if(!ok) break; } } - auto it0 = it_gr; - ++it_gr; if(!ok) { res.push_back(name); - m_graph.erase(it0); m_dll2ver.erase(name); + it_gr = m_graph.erase(it_gr); changed = true; break; } + else ++it_gr; } } return res; From ec40cd8015c99fbf0bb96042cb069ea4dbbb86bc Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Thu, 11 Apr 2019 16:24:31 +0300 Subject: [PATCH 15/17] Warnings on absence of return statements. --- CMakeLists.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 43cc8b5b..7b83bd73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,11 +35,17 @@ if(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) endif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) -set(CMAKE_C_FLAGS "-march=x86-64") -set(CMAKE_CXX_FLAGS "-march=x86-64") - project(graft_server) +set(COMMON_FLAGS "-march=x86-64") +#CMAKE_CXX_COMPILER_ID is defined after 'project' statement +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # using GCC +# set(COMMON_FLAGS "${COMMON_FLAGS} -Werror=return-type -Wall -Wno-reorder -Wno-sign-compare -Wno-unused-function") + set(COMMON_FLAGS "${COMMON_FLAGS} -Wreturn-type") +endif() +set(CMAKE_C_FLAGS "${COMMON_FLAGS}") +set(CMAKE_CXX_FLAGS "${COMMON_FLAGS}") + option(OPT_BUILD_TESTS "Build tests." OFF) option(ENABLE_SYSLOG "SYSLOG support. It can be compiled for UNIX-like platforms only." OFF) option(STATIC_LINK "Link executables and libraries statically" ON) From c9c3e6ddff2e73c4acb5b3f521d6ecb93bd774a5 Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Thu, 11 Apr 2019 16:29:18 +0300 Subject: [PATCH 16/17] return-type warnings fixed. --- include/lib/graft/ConfigIni.h | 2 +- src/lib/graft/connection.cpp | 2 +- src/supernode/requests/redirect.cpp | 3 +++ src/supernode/requests/sale_details.cpp | 4 ++++ test/graft_server_test.cpp | 5 +++++ test/sys_info.cpp | 2 +- 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/lib/graft/ConfigIni.h b/include/lib/graft/ConfigIni.h index 3e8b0712..ff4dda07 100644 --- a/include/lib/graft/ConfigIni.h +++ b/include/lib/graft/ConfigIni.h @@ -124,7 +124,7 @@ class ConfigIniSubtree bool operator ==(const iterator& it) const { - vi->operator ==(*it.vi); + return vi->operator ==(*it.vi); } bool operator !=(const iterator& it) const { diff --git a/src/lib/graft/connection.cpp b/src/lib/graft/connection.cpp index 4fc311c0..6860c545 100644 --- a/src/lib/graft/connection.cpp +++ b/src/lib/graft/connection.cpp @@ -87,7 +87,7 @@ void UpstreamSender::send(TaskManager &manager, const std::string& def_uri) std::stringstream ss; ss << "http://0.0.0.0:" << mg_port << "/callback/" << boost::uuids::to_string(callback_uuid); - auto it = std::find_if(output.headers.begin(), output.headers.end(), [](auto& v)->bool { v.first == "X-Callback"; } ); + auto it = std::find_if(output.headers.begin(), output.headers.end(), [](auto& v)->bool { return v.first == "X-Callback"; } ); if(it != output.headers.end()) { std::ostringstream oss; diff --git a/src/supernode/requests/redirect.cpp b/src/supernode/requests/redirect.cpp index 0a9de908..2d3262fb 100644 --- a/src/supernode/requests/redirect.cpp +++ b/src/supernode/requests/redirect.cpp @@ -172,6 +172,7 @@ void getSupernodesWithStake(graft::Context& ctx, IdSet& allWithStake) if(!sptr->stakeAmount()) continue; allWithStake.emplace_back(item); } + return true; } ); @@ -694,6 +695,8 @@ graft::Status onRedirectBroadcast(const graft::Router::vars_t& vars, const graft return graft::Status::Ok; } } + assert(false); + return graft::Status::Ok; } #ifdef UDHT_INFO diff --git a/src/supernode/requests/sale_details.cpp b/src/supernode/requests/sale_details.cpp index eb68b95b..d75444eb 100644 --- a/src/supernode/requests/sale_details.cpp +++ b/src/supernode/requests/sale_details.cpp @@ -385,6 +385,8 @@ Status saleDetailsClientHandler(const Router::vars_t& vars, const graft::Input& case ClientHandlerState::CallbackFromAuthSample: return handleSaleDetailsResponse(vars, input, ctx, output); } + assert(false); + return graft::Status::Ok; } // handles callback with response from remote supernode @@ -432,6 +434,8 @@ Status saleDetailsUnicastHandler(const Router::vars_t& vars, const graft::Input& ctx.local[__FUNCTION__] = State::CallbackAcknowledge; return sendOkResponseToCryptonode(output); // send ok as reply to initial request } + assert(false); + return graft::Status::Ok; } diff --git a/test/graft_server_test.cpp b/test/graft_server_test.cpp index 0e40de5b..56804020 100644 --- a/test/graft_server_test.cpp +++ b/test/graft_server_test.cpp @@ -989,6 +989,7 @@ TEST_F(GraftServerTestBase, Again) } break; default: assert(false); } + return graft::Status::Ok; }; TempCryptoNodeServer crypton; @@ -1262,7 +1263,9 @@ TEST_F(GraftServerPostponeTest, common) output.body = postpone_result; return graft::Status::Ok; } break; + default: assert(false); } + return graft::Status::Ok; }; std::atomic stop_crypton{false}; @@ -1399,7 +1402,9 @@ TEST_F(GraftServerTest, genericCallback) { return graft::Status::Ok; } break; + default: assert(false); } + return graft::Status::Ok; }; graft::supernode::request::registerForwardRequests(m_httpRouter); diff --git a/test/sys_info.cpp b/test/sys_info.cpp index 845a10bf..3477cc40 100644 --- a/test/sys_info.cpp +++ b/test/sys_info.cpp @@ -153,7 +153,7 @@ class HandlerAPIImpl : public graft::HandlerAPI virtual bool addPeriodicTask(const Router::Handler& h_worker, std::chrono::milliseconds interval_ms, std::chrono::milliseconds initial_interval_ms = std::chrono::milliseconds::max(), - double random_factor = 0) override { } + double random_factor = 0) override { return false; } virtual graft::request::system_info::Counter& runtimeSysInfo() override { return m_sic; From bb8f631ab450bfcfbd5bb26db68401ae348777bf Mon Sep 17 00:00:00 2001 From: Alexander Suprunenko Date: Thu, 11 Apr 2019 17:16:15 +0300 Subject: [PATCH 17/17] Copy data/* once. --- CMakeLists.txt | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b83bd73..583cdf73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -456,22 +456,13 @@ if (OPT_BUILD_TESTS) target_compile_definitions(supernode_test PRIVATE -DELPP_SYSLOG) endif() - if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/test_wallet.keys) - add_custom_command( - TARGET supernode_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_SOURCE_DIR}/data/test_wallet.keys - ${CMAKE_CURRENT_BINARY_DIR}/test_wallet.keys) - endif() - endif (OPT_BUILD_TESTS) -# copy config file to build directory -if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/config.ini) - add_custom_command( - TARGET supernode POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_SOURCE_DIR}/data/* - ${CMAKE_CURRENT_BINARY_DIR}/) -endif() - +# copy data/* to build directory if config.ini absent +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/config.ini + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/data/* ${CMAKE_CURRENT_BINARY_DIR}/ + DEPENDS "${CMAKE_SOURCE_DIR}/data/config.ini" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + COMMENT "Copy ${CMAKE_SOURCE_DIR}/data/ to ${CMAKE_CURRENT_BINARY_DIR}/" +) +add_custom_target(copy_data ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/config.ini)