├── eth 20170329.pdf ├── screenshot.png ├── uFW_Topology.png ├── ufw ├── topics │ ├── README.md │ ├── topics.cpp │ ├── CMakeLists.txt │ └── topics.hpp └── app │ ├── loader.hpp │ ├── library_repository.hpp │ ├── plugin_repository.hpp │ ├── lifecycle_participant.hpp │ ├── main.cpp │ ├── example.cpp │ ├── CMakeLists.txt │ ├── entity.hpp │ ├── exception_types.hpp │ ├── logger.cpp │ ├── configuration.hpp │ ├── library.hpp │ ├── logger.hpp │ ├── application.hpp │ └── application.cpp ├── benchmarks ├── ufw-sandbox-benchmarks.cpp ├── CMakeLists.txt └── main.cpp ├── tests ├── main.cpp ├── CMakeLists.txt ├── ufw_app_tests.cpp └── ufw_topics_tests.cpp ├── examples └── app.yaml ├── uFW_Topology.xml ├── CMakeLists.txt ├── .travis.yml ├── README.md └── LICENSE /eth 20170329.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrbald/ufw/HEAD/eth 20170329.pdf -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrbald/ufw/HEAD/screenshot.png -------------------------------------------------------------------------------- /uFW_Topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrbald/ufw/HEAD/uFW_Topology.png -------------------------------------------------------------------------------- /ufw/topics/README.md: -------------------------------------------------------------------------------- 1 | Topics - the μFW Message Dispatcher 2 | ====================================== 3 | 4 | -------------------------------------------------------------------------------- /ufw/topics/topics.cpp: -------------------------------------------------------------------------------- 1 | #include "topics.hpp" 2 | 3 | namespace ufw { 4 | 5 | topic_payload_id_t next_topic_payload_id() { 6 | static topic_payload_id_t topic_payload_id_seq {0}; 7 | return ++topic_payload_id_seq; 8 | } 9 | 10 | } // namespace ufw 11 | 12 | namespace { 13 | using namespace ufw; 14 | 15 | } // local namespace 16 | -------------------------------------------------------------------------------- /benchmarks/ufw-sandbox-benchmarks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | namespace { 6 | 7 | void ufw_startup_benchmark(benchmark::State& state) { 8 | std::ofstream out("/dev/null"); 9 | 10 | while (state.KeepRunning()) 11 | { 12 | out << "hello, world\n"; 13 | } 14 | } 15 | 16 | BENCHMARK(ufw_startup_benchmark); 17 | 18 | } // local namespace 19 | -------------------------------------------------------------------------------- /ufw/topics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(Threads REQUIRED) 2 | 3 | SET(Boost_USE_MULTITHREADED ON) 4 | FIND_PACKAGE(Boost REQUIRED) 5 | 6 | FIND_PACKAGE(yaml-cpp REQUIRED) 7 | LINK_DIRECTORIES(${YAML_CPP_LIBRARY_DIR}) 8 | 9 | ADD_LIBRARY(ufw_topics SHARED 10 | topics.cpp 11 | topics.hpp) 12 | 13 | SET_TARGET_PROPERTIES(ufw_topics PROPERTIES 14 | PUBLIC_HEADER topics.hpp) 15 | 16 | TARGET_LINK_LIBRARIES(ufw_topics 17 | ufw_app 18 | ${CMAKE_THREAD_LIBS_INIT}) 19 | 20 | INSTALL(TARGETS ufw_topics EXPORT UfwTargets 21 | LIBRARY DESTINATION "${INSTALL_LIB_DIR}" 22 | PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/ufw/topics" 23 | COMPONENT shlib) 24 | -------------------------------------------------------------------------------- /ufw/app/loader.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Lysyy (mrbald@github) 3 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "entity.hpp" 9 | #include "configuration.hpp" 10 | 11 | #include 12 | #include 13 | 14 | namespace ufw { 15 | 16 | struct loader: entity 17 | { 18 | using entity::entity; 19 | virtual std::unique_ptr load(entity_id const& id, resolved_entity_id rid, config_t const& cfg) = 0; 20 | }; 21 | 22 | using loader_func_t = std::function(config_t const& cfg, entity_id const& id, resolved_entity_id rid, application& app)>; 23 | 24 | } // namespace ufw 25 | -------------------------------------------------------------------------------- /benchmarks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | IF(benchmark_DIR) 2 | FIND_PACKAGE(benchmark PATHS "${benchmark_DIR}" QUIET) 3 | ELSE(benchmark_DIR) 4 | FIND_PACKAGE(benchmark QUIET) 5 | ENDIF(benchmark_DIR) 6 | 7 | IF(benchmark_FOUND) 8 | MESSAGE(STATUS "benchmarks enabled (using google.benchmark ${benchmark_VERSION} from ${benchmark_DIR})") 9 | 10 | ADD_EXECUTABLE(ufw_benchmarks 11 | ufw-sandbox-benchmarks.cpp 12 | main.cpp) 13 | 14 | TARGET_LINK_LIBRARIES(ufw_benchmarks 15 | benchmark::benchmark) 16 | 17 | ADD_CUSTOM_TARGET(benchmark ufw_benchmarks DEPENDS ufw_benchmarks USES_TERMINAL) 18 | 19 | ELSE() 20 | MESSAGE(STATUS "benchmarks disabled (google.benchmark not found)") 21 | ENDIF() 22 | -------------------------------------------------------------------------------- /benchmarks/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | BENCHMARK_MAIN() 20 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #define BOOST_TEST_MODULE "ufw" 18 | #include 19 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(Threads REQUIRED) 2 | 3 | SET(Boost_USE_MULTITHREADED ON) 4 | FIND_PACKAGE(Boost REQUIRED COMPONENTS unit_test_framework REQUIRED) 5 | ADD_DEFINITIONS("-DBOOST_TEST_DYN_LINK") 6 | 7 | FIND_PACKAGE(yaml-cpp REQUIRED) 8 | LINK_DIRECTORIES(${YAML_CPP_LIBRARY_DIR}) 9 | 10 | ADD_LIBRARY(ufw_app_tests OBJECT 11 | ufw_topics_tests.cpp 12 | ufw_app_tests.cpp) 13 | 14 | ADD_EXECUTABLE(ufw_tests 15 | $ 16 | main.cpp) 17 | 18 | TARGET_LINK_LIBRARIES(ufw_tests 19 | ufw_topics 20 | Boost::unit_test_framework) 21 | 22 | INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) 23 | 24 | ADD_CUSTOM_TARGET(unit-test ufw_tests DEPENDS ufw_tests USES_TERMINAL) 25 | 26 | ADD_TEST(ctest_ufw_tests "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target unit-test) 27 | -------------------------------------------------------------------------------- /examples/app.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | application: 3 | 4 | entities: 5 | # ====== logger ====== 6 | - name: LOGGER 7 | config: | 8 | [Core] 9 | DisableLogging=false 10 | LogSeverity=error 11 | 12 | [Sinks.Console] 13 | Destination=Console 14 | Format="%TimeStamp(format=\"%H:%M:%S.%f\")% | %Severity(format=\"%6s\")% | %ThreadPID% | %Entity% - %Tag%%Message%" 15 | Asynchronous=true 16 | AutoFlush=true 17 | 18 | # ====== a dynamic library ====== 19 | - name: example_lib 20 | loader_ref: LIBRARY 21 | config: 22 | filename: libexample.dylib 23 | # filename: libexample.so 24 | 25 | # ====== an entity -- plugin from a dynamic library ====== 26 | - name: example_plugin 27 | loader_ref: PLUGIN 28 | config: 29 | library_ref: example_lib 30 | constructor: example_ctor 31 | ... 32 | -------------------------------------------------------------------------------- /ufw/app/library_repository.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Lysyy (mrbald@github) 3 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 4 | * 5 | * 2015-03-28 - Vladimir Lysyy - Initial version 6 | * 2018-04-20 - Vladimir Lysyy - Adapted for uFW 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "library.hpp" 12 | #include "loader.hpp" 13 | #include "configuration.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace ufw { 20 | 21 | struct library_repository final: loader { 22 | using loader::loader; 23 | 24 | std::unique_ptr load(entity_id const& id, resolved_entity_id rid, config_t const& cfg) override 25 | { 26 | auto const filename = cfg["filename"].as(); 27 | return std::make_unique(library::load(filename.c_str()), id, rid, app()); 28 | } 29 | }; 30 | 31 | } // namespace ufw 32 | -------------------------------------------------------------------------------- /tests/ufw_app_tests.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | namespace { 20 | 21 | BOOST_AUTO_TEST_SUITE(ufw_app) 22 | 23 | BOOST_AUTO_TEST_CASE(placeholder_test) { 24 | BOOST_TEST_MESSAGE("hello world"); 25 | } // BOOST_AUTO_TEST_CASE(placeholder_test) 26 | 27 | BOOST_AUTO_TEST_SUITE_END(/* ufw_app */) 28 | 29 | } // local namespace 30 | -------------------------------------------------------------------------------- /tests/ufw_topics_tests.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018 Vladimir Lysyy (mrbald@github) 2 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 3 | */ 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace { 13 | 14 | BOOST_AUTO_TEST_SUITE(ufw_topics) 15 | 16 | BOOST_AUTO_TEST_CASE(topic_id_test) { 17 | using sig_1 = std::tuple; 18 | using sig_2 = std::tuple; 19 | 20 | auto const sub_1 = std::string("hello"); 21 | auto const sub_2 = std::string("olleh"); 22 | 23 | using ufw::topic_id_for; 24 | 25 | BOOST_REQUIRE_EQUAL(topic_id_for(sub_1), topic_id_for(sub_1)); 26 | BOOST_REQUIRE_NE(topic_id_for(sub_1), topic_id_for(sub_2)); 27 | BOOST_REQUIRE_NE(topic_id_for(sub_1), topic_id_for(sub_1)); 28 | } // BOOST_AUTO_TEST_CASE(topic_id_test) 29 | 30 | BOOST_AUTO_TEST_SUITE_END(/* ufw_topics */) 31 | 32 | } // local namespace 33 | -------------------------------------------------------------------------------- /ufw/app/plugin_repository.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Lysyy (mrbald@github) 3 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 4 | * 5 | * 2015-03-28 - Vladimir Lysyy - Initial version 6 | */ 7 | 8 | #pragma once 9 | 10 | #include "library_repository.hpp" 11 | #include "library.hpp" 12 | #include "loader.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ufw { 19 | 20 | struct plugin_repository final: loader { 21 | using loader::loader; 22 | 23 | std::unique_ptr load(entity_id const& id, resolved_entity_id rid, config_t const& cfg) override 24 | { 25 | auto const library_ref = cfg["library_ref"].as(); 26 | auto const constructor = cfg["constructor"].as(); 27 | 28 | entity_ref lib {library_ref, app()}; 29 | lib.resolve(); 30 | 31 | return std::unique_ptr{ lib->function(constructor)(id, rid, app()) }; 32 | } 33 | }; 34 | 35 | } // namespace ufw 36 | -------------------------------------------------------------------------------- /ufw/app/lifecycle_participant.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Lysyy (mrbald@github) 3 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "logger.hpp" 9 | 10 | namespace ufw { 11 | 12 | struct entity; 13 | 14 | struct lifecycle_participant 15 | { 16 | // at this stage participants may discover and cache references (including strongly typed) to each other 17 | virtual void init() {} 18 | 19 | // at this stage participants may establish connections, spawn threads, etc. 20 | virtual void start() {} 21 | 22 | // at this stage participants may start messaging each other 23 | virtual void up() const noexcept final 24 | { 25 | auto* entity_ptr = dynamic_cast(this); 26 | if (entity_ptr) 27 | { 28 | auto const get_logger = [&]()->logger_t& { return entity_ptr->get_logger(); }; 29 | LOG_INF << "UP"; 30 | } 31 | } 32 | 33 | // reverse of start() 34 | virtual void stop() noexcept {} 35 | 36 | // reverse of init() 37 | virtual void fini() noexcept {} 38 | 39 | virtual ~lifecycle_participant() = default; 40 | }; 41 | 42 | } // namespace ufw 43 | -------------------------------------------------------------------------------- /ufw/app/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "application.hpp" 18 | #include "exception_types.hpp" 19 | #include "logger.hpp" 20 | #include "library_repository.hpp" 21 | #include "plugin_repository.hpp" 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | int main(int argc, char const** argv) 33 | { 34 | ufw::initialize_logger(); 35 | 36 | try 37 | { 38 | LOG_INF << "starting"; 39 | ufw::application app; 40 | app.add("LIBRARY"); 41 | app.add("PLUGIN"); 42 | 43 | app.load(argc, argv); 44 | 45 | app.run(); 46 | } 47 | catch (...) 48 | { 49 | LOG_ERR << boost::current_exception_diagnostic_information(); 50 | return 1; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ufw/app/example.cpp: -------------------------------------------------------------------------------- 1 | #include "entity.hpp" 2 | #include "lifecycle_participant.hpp" 3 | #include "application.hpp" 4 | 5 | #include 6 | 7 | namespace { 8 | 9 | struct example: ufw::entity, ufw::lifecycle_participant { 10 | template 11 | example(Args&&... args): 12 | entity { std::forward(args)... }, 13 | timer_ { app().context() } 14 | { 15 | } 16 | 17 | void init() override /* from lifecycle_participant */ 18 | { 19 | LOG_INF << "initialized"; 20 | } 21 | 22 | void start() override /* from lifecycle_participant */ 23 | { 24 | LOG_INF << "started"; 25 | app().context().post([this]{ 26 | LOG_INF << "scheduling shutdown in 5 seconds"; 27 | timer_.expires_after(std::chrono::seconds(5)); 28 | timer_.async_wait([this](auto&&){ 29 | LOG_INF << ">>>>>> shutting down <<<<<<"; 30 | app().shutdown(); 31 | }); 32 | }); 33 | } 34 | 35 | void stop() noexcept override /* from lifecycle_participant */ 36 | { 37 | LOG_INF << "stopped"; 38 | } 39 | 40 | void fini() noexcept override /* from lifecycle_participant */ 41 | { 42 | LOG_INF << "finalized"; 43 | } 44 | 45 | private: 46 | boost::asio::steady_timer timer_; 47 | }; 48 | 49 | extern "C" ufw::entity* example_ctor(ufw::entity_id const& id, ufw::resolved_entity_id rid, ufw::application& app) { 50 | return new example {id, rid, app}; 51 | } 52 | 53 | } // local namespace 54 | -------------------------------------------------------------------------------- /uFW_Topology.xml: -------------------------------------------------------------------------------- 1 | 5Vpbc+MmGP01ntk+dEdCF8uPttdpO5OdZpqHbh+xhGR2ZXARiu399QUJdMWNUyNvt81L4OMiOOe7ksy89f70E4OH3UeaoHwGnOQ08z7MAHB9EIpfUnKuJRFwa0HGcKImtYJn/BUpoaOkJU5Q0ZvIKc05PvSFMSUExbwng4zRY39aSvP+Vw8wQyPBcwzzsfR3nPCdukXgtPKfEc52+suuo0a2MP6SMVoS9b0Z8NLqpx7eQ72Xml/sYEKPHZG3mXlrRimvW/vTGuUSWw1bve7hwmhzboYIv2aBVy94gXmprr48HHIcQ44pUUfkZw1LdTEklzozb3XcYY6eDzCWo0ehCEK24/tc9FzRTHGer2lOWbXWSyCK0ljIC87oF9QZCeMIbVMx8oIYF5/OlznOiBjjVG4JVY/VeK/UicVcdLp4a7fBUugoonvE2VlMUQt8hb7STjdS/WPLNQiVbNfh2dMTodKvrNm6xVg0FMxmyP0R5BvCMcdC32/Ce4hTF39CCTJBH4GtF4YXoU9gsas+79rBPejj3ph7F3ffhLtjAfdghPsjhQliN8J+ndL2kLyGmWS+2DqOHdjDPuweMMAODLC7vgXYwxHsT3mZYeFcnHfC+zGBCnByvK2cZ4GSH6x6nTRFYWz0OjYBbty/RngxRtg1K/btAM9HAH9AKSxzLhGOubjvd4zsvA+sb1DdyYBdjICdgTCvrBm/9IAM/yxlwF6Je/IflekvZXoibolYOy5amfpd7VMcINGyR5yi+ByLHYHzBKVPwWKUF3quOG13ekdcnaYvtXXA4e73jk+LcO7BcOg/zS7XvrY1WtTVNlNe4LoW1E3v0dG3j7Dyk5sTiss6IXPWlEgSpW2vKC34+2WB6Y2WrZlR2jA29ShGZlPfRoEfWDL1QXIQ+FcmB8AG9oY8WHRXZYEJKoQNOr9uP8tCAzgPUPrU8719ac8AZOcJckEWqSTA8e1wEAX9OBYYDCCYyN264wTtAgcWkUduEqC5yQau9EWTUbEY+KK5gQpT0maFinHS9tbIUnv5qyJfVf6c3xjnbFamAYoS3xCh3louWWDdG+TqvqFEcj1TCLJRIunatAMsSjL0rLqU8R3NKIH5ppWu+tB3YEYnzD8psWz/IdvvA9H7jDg/q1cfWHIqRO3ej1SC2RjXK5aGSLKUDz7yeAdEaskDllesP0z081Jrl/JOf8+PgICWLNaz5mbOGMohxy/9vUz4q6VPFBPe4doBfa6DAYUcsgxxtWrAYnOM6wx6XCT8i9559jhJKlUaOeEpLMqQVxgdqY28Qle/UxuUwIKdP+lpsvP9GltUy2rlVzBeoPhqA7w68EX3DHyPNMuq6usbBb40TUGVdr6e/CThNgxMka+x3SlMNbyjqbrjd1nJDyaZEP5CMMcCpa+SrilTj8sMNLmHBaCHOJtyy6leVfRpBzhX77C6phUC249Vb9d0CzgPS1rTw+tUJa1+F7QbenT7Pxl6ABiHnvpl4A6hB0TT8zX/PxAW3EhYtVQcHZ47Ew4yCS86Ow9y+cAduNRF0CX/1fn65aVVlvoE/zTh18h0E35C+U7GL0cX3M5vKH1X2PW03yiijVIHw590baUOotv+gb7mpv0vCG/zFw== -------------------------------------------------------------------------------- /ufw/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | FIND_PACKAGE(Threads REQUIRED) 2 | 3 | SET(Boost_USE_MULTITHREADED ON) 4 | FIND_PACKAGE(Boost REQUIRED COMPONENTS system program_options log log_setup REQUIRED) 5 | 6 | FIND_PACKAGE(yaml-cpp REQUIRED) 7 | LINK_DIRECTORIES(${YAML_CPP_LIBRARY_DIR}) 8 | 9 | ADD_LIBRARY(ufw_app SHARED 10 | application.hpp 11 | configuration.hpp 12 | entity.hpp 13 | exception_types.hpp 14 | library.hpp 15 | library_repository.hpp 16 | lifecycle_participant.hpp 17 | loader.hpp 18 | logger.hpp 19 | plugin_repository.hpp 20 | application.cpp 21 | logger.cpp) 22 | 23 | SET_TARGET_PROPERTIES(ufw_app PROPERTIES 24 | PUBLIC_HEADER "application.hpp;configuration.hpp;entity.hpp;exception_types.hpp;library.hpp;library_repository.hpp;lifecycle_participant.hpp;loader.hpp;logger.hpp;plugin_repository.hpp") 25 | 26 | TARGET_COMPILE_DEFINITIONS(ufw_app PUBLIC "-DBOOST_LOG_DYN_LINK") 27 | 28 | TARGET_LINK_LIBRARIES(ufw_app 29 | yaml-cpp 30 | Boost::log_setup 31 | Boost::program_options 32 | Boost::system 33 | Boost::log 34 | ${CMAKE_THREAD_LIBS_INIT} 35 | ) 36 | 37 | INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) 38 | 39 | INSTALL(TARGETS ufw_app EXPORT UfwTargets 40 | LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib 41 | PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/ufw/app" 42 | COMPONENT dev) 43 | 44 | 45 | ADD_LIBRARY(example SHARED 46 | example.cpp) 47 | 48 | TARGET_LINK_LIBRARIES(example 49 | ufw_app 50 | ${CMAKE_THREAD_LIBS_INIT} 51 | ) 52 | 53 | INSTALL(TARGETS example EXPORT UfwTargets 54 | LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT shlib) 55 | 56 | 57 | ADD_EXECUTABLE(ufw_launcher 58 | main.cpp) 59 | 60 | TARGET_LINK_LIBRARIES(ufw_launcher 61 | ufw_app 62 | ${CMAKE_DL_LIBS}) 63 | 64 | INSTALL(TARGETS ufw_launcher EXPORT UfwTargets 65 | RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin) 66 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # https://cmake.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file 2 | 3 | CMAKE_MINIMUM_REQUIRED(VERSION 3.27) 4 | 5 | PROJECT(Ufw VERSION 0.0.1 LANGUAGES "CXX") 6 | 7 | SET(CMAKE_CXX_STANDARD 20) 8 | SET(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | SET(CMAKE_VERBOSE_MAKEFILE ON) 10 | 11 | IF(NOT WINDOWS) 12 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra -Werror" ) 13 | ENDIF() 14 | 15 | INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}") 16 | 17 | SET(CMAKE_SKIP_BUILD_RPATH FALSE) 18 | SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 19 | SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 20 | 21 | IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 22 | SET(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/install" CACHE PATH "Project installation prefix" FORCE) 23 | ENDIF() 24 | 25 | SET(INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") 26 | SET(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") 27 | SET(INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") 28 | IF(WIN32 AND NOT CYGWIN) 29 | SET(DEF_INSTALL_CMAKE_DIR CMake) 30 | ELSE() 31 | SET(DEF_INSTALL_CMAKE_DIR lib/CMake/Ufw) 32 | ENDIF() 33 | 34 | SET(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") 35 | 36 | IF(UNIX AND NOT APPLE) 37 | IF(NOT IS_ABSOLUTE "${${INSTALL_LIB_DIR}}") 38 | SET(CMAKE_INSTALL_RPATH "$ORIGIN/../${INSTALL_LIB_DIR}") 39 | ENDIF() 40 | ENDIF() 41 | 42 | FOREACH(p LIB BIN INCLUDE CMAKE) 43 | SET(var INSTALL_${p}_DIR) 44 | IF(NOT IS_ABSOLUTE "${${var}}") 45 | SET(${var} "${CMAKE_INSTALL_PREFIX}/${${var}}") 46 | ENDIF() 47 | ENDFOREACH() 48 | 49 | ENABLE_TESTING() 50 | 51 | ADD_SUBDIRECTORY(ufw/topics) 52 | ADD_SUBDIRECTORY(ufw/app) 53 | ADD_SUBDIRECTORY(tests) 54 | ADD_SUBDIRECTORY(benchmarks) 55 | 56 | ADD_COMPILE_OPTIONS(-fsanitize=address -fsanitize=undefined) 57 | ADD_LINK_OPTIONS(-fsanitize=address -fsanitize=undefined) 58 | 59 | set(CMAKE_CXX_CLANG_TIDY 60 | clang-tidy; 61 | -header-filter=.; 62 | -checks=*; 63 | -warnings-as-errors=*;) -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: require 3 | language: 4 | - cpp 5 | compiler: 6 | - gcc 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | packages: 12 | - gcc-7 13 | - g++-7 14 | - cmake 15 | branches: 16 | only: 17 | - master 18 | env: 19 | global: 20 | - BOOST_VERSION='1.66.0' 21 | - BOOST_DIR="${HOME}/boost-${BOOST_VERSION}" 22 | - BOOST_URL="https://dl.bintray.com/boostorg/release/${BOOST_VERSION}/source/boost_${BOOST_VERSION//\./_}.tar.gz" 23 | install: 24 | - | 25 | set -e 26 | sudo ln -s /usr/bin/gcc-7 /usr/local/bin/gcc 27 | sudo ln -s /usr/bin/g++-7 /usr/local/bin/g++ 28 | export CC=/usr/bin/gcc-7 29 | export CXX=/usr/bin/g++-7 30 | gcc -v && g++ -v && cmake --version 31 | - | 32 | set -e 33 | mkdir -p "${BOOST_DIR}" 34 | travis_retry wget -O - "${BOOST_URL}" | tar --strip-components=1 -xz -C "${BOOST_DIR}" 35 | cd ${BOOST_DIR} 36 | ./bootstrap.sh --with-libraries=system,program_options,log,test 37 | ./b2 -d0 -q variant=release link=shared threading=multi runtime-link=shared stage 38 | sudo ./b2 -d0 -q variant=release link=shared threading=multi runtime-link=shared install 39 | - | 40 | set -e 41 | travis_retry git clone --depth 1 'https://github.com/jbeder/yaml-cpp.git' "${HOME}/yaml-cpp" 42 | cd "${HOME}/yaml-cpp" 43 | mkdir build && cd build 44 | cmake -G 'Unix Makefiles' -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release .. 45 | sudo make -l$(nproc) -j$(nproc) install 46 | script: | 47 | set -e 48 | cd "${TRAVIS_BUILD_DIR}" 49 | mkdir build && cd build 50 | cmake -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release .. 51 | chrt --batch 0 make -l$(nproc) -j$(nproc) all 52 | chrt --batch 0 make BOOST_TEST_LOG_LEVEL=all unit-test 53 | notifications: 54 | webhooks: 55 | urls: 56 | - https://webhooks.gitter.im/e/5bf5019382456ad872eb 57 | on_success: change # options: [always|never|change] default: always 58 | on_failure: always # options: [always|never|change] default: always 59 | on_start: never # options: [always|never|change] default: always 60 | -------------------------------------------------------------------------------- /ufw/app/entity.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Vladimir Lysyy (mrbald@github) 3 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 4 | */ 5 | 6 | #pragma once 7 | 8 | #include "logger.hpp" 9 | 10 | #include 11 | 12 | namespace ufw { 13 | 14 | using entity_id = std::string; 15 | using resolved_entity_id = size_t; 16 | 17 | struct application; 18 | struct lifecycle_participant; 19 | 20 | #define ENTITY_LOGGER \ 21 | private:\ 22 | mutable logger_t logger_ {[&]\ 23 | {\ 24 | logger_t logger;\ 25 | logger.add_attribute("Entity", attrs::constant(id()));\ 26 | return logger;\ 27 | }()};\ 28 | public:\ 29 | logger_t& get_logger() const { return logger_; } 30 | 31 | struct entity 32 | { 33 | entity(entity_id const& id, resolved_entity_id rid, application& app): 34 | id_{id}, rid_{rid}, app_{app} {} 35 | virtual ~entity() = default; 36 | 37 | entity_id const& id() const noexcept { return id_; } 38 | resolved_entity_id resolved_id() const noexcept { return rid_; } 39 | 40 | application& app() const noexcept { return app_; } 41 | 42 | private: 43 | entity_id const id_; 44 | resolved_entity_id const rid_; 45 | application& app_; 46 | 47 | ENTITY_LOGGER; 48 | friend struct lifecycle_participant; // logger access 49 | }; 50 | 51 | /** 52 | * Entity lazy references logic helper 53 | */ 54 | template 55 | struct entity_ref 56 | { 57 | T* operator->() const { return target_; } 58 | T* get() const { return target_; } 59 | 60 | explicit operator T&() { return *target_; } 61 | explicit operator T const&() const { return *target_; } 62 | 63 | explicit operator bool() const { return target_; } 64 | 65 | entity_ref(entity_id const& id, application& app): 66 | id_ {id}, 67 | app_ {app} {} 68 | 69 | entity_id const& id() { return id_; } 70 | resolved_entity_id resolved_id() const { return resolved_id_; } 71 | 72 | void resolve(); 73 | 74 | private: 75 | entity_id const id_; 76 | application& app_; 77 | 78 | resolved_entity_id resolved_id_; 79 | T* target_ {}; 80 | }; 81 | 82 | } // namespace ufw 83 | -------------------------------------------------------------------------------- /ufw/app/exception_types.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | namespace ufw { 23 | 24 | struct message; 25 | using resolved_entity_id = size_t; 26 | 27 | struct fatal_error: std::runtime_error { using std::runtime_error::runtime_error; }; 28 | struct transient_error: std::runtime_error { using std::runtime_error::runtime_error; }; 29 | 30 | struct bad_entity_reference: fatal_error { using fatal_error::fatal_error; }; 31 | 32 | 33 | struct messaging_error: transient_error 34 | { 35 | template 36 | messaging_error(std::shared_ptr msg, resolved_entity_id src, Args&&... args): 37 | transient_error(std::forward(args)...), 38 | msg_{std::move(msg)}, 39 | src_{src} {} 40 | 41 | std::shared_ptr msg() const { return msg_; } 42 | resolved_entity_id origin() { return src_; } 43 | private: 44 | std::shared_ptr const msg_; 45 | resolved_entity_id const src_; 46 | }; 47 | 48 | 49 | struct destination_unreachable_error: messaging_error 50 | { 51 | destination_unreachable_error(std::shared_ptr msg, resolved_entity_id src): 52 | messaging_error(std::move(msg), src, "destination unreachable") {} 53 | }; 54 | 55 | 56 | struct routing_error: messaging_error { using messaging_error::messaging_error; }; 57 | 58 | struct route_not_found_error: routing_error 59 | { 60 | route_not_found_error(std::shared_ptr msg, resolved_entity_id src): 61 | routing_error(std::move(msg), src, "route not found message") {} 62 | }; 63 | 64 | } // namespace ufw 65 | -------------------------------------------------------------------------------- /ufw/app/logger.cpp: -------------------------------------------------------------------------------- 1 | #include "logger.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace logging = boost::log; 16 | namespace attrs = boost::log::attributes; 17 | 18 | namespace ufw { 19 | 20 | struct TimeStampFormatterFactory: boost::log::basic_formatter_factory 21 | { 22 | formatter_type create_formatter(const boost::log::attribute_name& name, const args_map& args) 23 | { 24 | auto it = args.find("format"); 25 | if (it != args.end()) { 26 | return boost::log::expressions::stream 27 | << boost::log::expressions::format_date_time( 28 | boost::log::expressions::attr(name), it->second); 29 | } 30 | else 31 | { 32 | return boost::log::expressions::stream 33 | << boost::log::expressions::attr(name); 34 | } 35 | } 36 | }; 37 | 38 | void initialize_logger() 39 | { 40 | LOG_STAMP_THREAD; 41 | logging::register_formatter_factory("TimeStamp", boost::make_shared()); 42 | logging::register_simple_formatter_factory("Severity"); 43 | logging::core::get()->add_global_attribute("File", attrs::mutable_constant("-")); 44 | logging::core::get()->add_global_attribute("Line", attrs::mutable_constant(0)); 45 | logging::core::get()->add_global_attribute("Func", attrs::mutable_constant("-")); 46 | logging::core::get()->add_global_attribute("Tag", attrs::mutable_constant("-")); 47 | logging::add_common_attributes(); 48 | } 49 | 50 | void configure_logger(std::string const& cfg) 51 | { 52 | std::istringstream buf(cfg); 53 | boost::log::init_from_stream(buf); 54 | } 55 | 56 | uint64_t get_tid() noexcept 57 | { 58 | uint64_t tid{}; 59 | return pthread_threadid_np(pthread_self(), &tid); 60 | return int(tid); 61 | } 62 | 63 | } // namespace ufw 64 | -------------------------------------------------------------------------------- /ufw/topics/topics.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2018 Vladimir Lysyy (mrbald@github) 2 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace ufw { 19 | 20 | using topic_payload_id_t = uint32_t; 21 | using topic_subject_id_t = uint32_t; 22 | using topic_id_t = uint64_t; 23 | 24 | extern topic_payload_id_t next_topic_payload_id(); 25 | 26 | template inline 27 | topic_payload_id_t verified_topic_payload_id(topic_payload_id_t const topic_payload_id) { 28 | if (topic_payload_id == std::numeric_limits::max()) { 29 | throw ufw::fatal_error("cound not register payload type [" 30 | + boost::core::demangle(typeid(T).name()) 31 | +"] - too many payload types (" 32 | + std::to_string(topic_payload_id) 33 | + ") already registered"); 34 | } 35 | 36 | return topic_payload_id; 37 | } 38 | 39 | using topic_subject_interner_t = std::map; 40 | 41 | template inline 42 | topic_subject_id_t verified_topic_subject_id(std::string const& subject, topic_subject_interner_t& interner) { 43 | auto const next_topic_subject_id = interner.size() + 1; 44 | auto const status = interner.try_emplace(subject, next_topic_subject_id); 45 | if (status.second && next_topic_subject_id == std::numeric_limits::max()) { 46 | throw ufw::fatal_error("cound not register subject [" 47 | + subject 48 | + "] for payload type [" 49 | + boost::core::demangle(typeid(T).name()) 50 | +"] - maximum number topic subjects (" 51 | + std::to_string(next_topic_subject_id) 52 | + ") already registered for the payload type"); 53 | } 54 | 55 | return status.first->second; 56 | } 57 | 58 | template inline 59 | topic_id_t topic_id_for(std::string const& subject) { 60 | static topic_payload_id_t const topic_payload_id 61 | = verified_topic_payload_id(next_topic_payload_id()); 62 | 63 | static std::map interner; 64 | 65 | auto const topic_subject_id = verified_topic_subject_id(subject, interner); 66 | 67 | return (topic_payload_id << sizeof(topic_payload_id)) + topic_subject_id; 68 | } 69 | 70 | } // namespace ufw 71 | -------------------------------------------------------------------------------- /ufw/app/configuration.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace ufw { 26 | 27 | using config_t = YAML::Node; 28 | 29 | struct entity_config 30 | { 31 | std::string name; 32 | std::string loader_ref; 33 | 34 | config_t config; 35 | }; 36 | 37 | struct application_config 38 | { 39 | std::vector entities; 40 | }; 41 | 42 | } // namespace ufw 43 | 44 | #define CFG_NAMESPACE YAML 45 | #define CFG_ENCODE(name) do { node[#name] = rhs.name; } while (false) 46 | #define CFG_ENCODE_IF_SET(name) do { if (!rhs.name.empty()) node[#name] = rhs.name; } while (false) 47 | #define CFG_DECODE(name) do { rhs.name = node[#name].as(); } while (false) 48 | #define CFG_DECODE_IF_SET(name) do { if (node[#name]) rhs.name = node[#name].as(); } while (false) 49 | 50 | namespace CFG_NAMESPACE 51 | { 52 | 53 | 54 | template <> 55 | struct convert { 56 | static Node encode(const ufw::entity_config& rhs) { 57 | Node node; 58 | 59 | CFG_ENCODE(name); 60 | CFG_ENCODE_IF_SET(loader_ref); 61 | CFG_ENCODE(config); 62 | 63 | return node; 64 | } 65 | 66 | static bool decode(const Node& node, ufw::entity_config& rhs) 67 | { 68 | if (node.IsSequence()) 69 | return false; 70 | 71 | CFG_DECODE(name); 72 | CFG_DECODE_IF_SET(loader_ref); 73 | CFG_DECODE_IF_SET(config); 74 | 75 | return true; 76 | } 77 | }; 78 | 79 | template <> 80 | struct convert { 81 | static Node encode(const ufw::application_config& rhs) { 82 | Node node; 83 | CFG_ENCODE(entities); 84 | return node; 85 | } 86 | 87 | static bool decode(const Node& node, ufw::application_config& rhs) 88 | { 89 | if (node.IsSequence()) 90 | return false; 91 | CFG_DECODE(entities); 92 | return true; 93 | } 94 | }; 95 | 96 | } // namespace CFG_NAMESPACE 97 | -------------------------------------------------------------------------------- /ufw/app/library.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-2023 Vladimir Lysyy (mrbald@github) 3 | * ALv2 (http://www.apache.org/licenses/LICENSE-2.0) 4 | * 5 | * 2015-03-28 - Vladimir Lysyy - Initial version 6 | * 2023-09-17 - Vladimir Lysyy - Tech refresh 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "entity.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | 21 | namespace ufw { 22 | 23 | /* 24 | * Dynamic Library Abstraction 25 | */ 26 | struct library final: std::enable_shared_from_this { 27 | static std::shared_ptr load(char const* path); 28 | 29 | ~library() noexcept 30 | { 31 | if (handle_) dlclose(handle_); 32 | } 33 | 34 | template 35 | std::function function(std::string const& name) const 36 | { 37 | if (!handle_) throw std::runtime_error("library not open"); 38 | dlerror(); 39 | auto fp = reinterpret_cast::type>(dlsym(handle_, name.c_str())); 40 | const char *dlsym_error = dlerror(); 41 | if (dlsym_error) throw std::runtime_error(dlsym_error); 42 | 43 | // aliased shared_ptr to hold the library alive until all references 44 | // to functions loaded from it are disposed 45 | return func_helper::create(std::shared_ptr(shared_from_this(), fp)); 46 | } 47 | 48 | // ctors are not to be called directly, static method create() should be 49 | // used instead 50 | library(char const* path): 51 | handle_(dlopen(path, RTLD_LAZY)) 52 | { 53 | if (!handle_) throw std::runtime_error(dlerror()); 54 | } 55 | 56 | private: 57 | template struct func_helper; 58 | template struct func_helper 59 | { 60 | static std::function create(std::shared_ptr ptr) 61 | { 62 | return [=](Args&&... args) { return (**ptr)(std::forward(args)...); }; 63 | } 64 | }; 65 | 66 | void* handle_ = nullptr; 67 | }; 68 | using library_ptr = std::shared_ptr; 69 | 70 | inline library_ptr library::load(char const* path) 71 | { 72 | return std::make_shared(path); 73 | } 74 | 75 | struct library_entity: entity { 76 | template 77 | library_entity(library_ptr library, Args&&... args): 78 | entity {std::forward(args)...}, 79 | library_ {std::move(library)} {} 80 | 81 | template 82 | std::function function(std::string const& name) const 83 | { 84 | return library_->template function(name); 85 | } 86 | 87 | private: 88 | library_ptr const library_; 89 | }; 90 | 91 | } // namespace ufw 92 | -------------------------------------------------------------------------------- /ufw/app/logger.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017-2023 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace logging = boost::log; 27 | namespace attrs = boost::log::attributes; 28 | 29 | template 30 | auto set_get_attrib(const char* name, ValueType value) { 31 | auto attr = logging::attribute_cast>(logging::core::get()->get_global_attributes()[name]); 32 | attr.set(value); 33 | return attr.get(); 34 | } 35 | 36 | namespace ufw { 37 | 38 | using logger_t = boost::log::sources::severity_logger; 39 | 40 | #define SET_LOG_LEVEL(LVL) do {\ 41 | logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::LVL); } while (0) 42 | 43 | #define LOG_SEV(sev) \ 44 | BOOST_LOG_STREAM_WITH_PARAMS( \ 45 | (get_logger()), \ 46 | (set_get_attrib("File", logging::string_literal(__FILE__))) \ 47 | (set_get_attrib("Line", __LINE__)) \ 48 | (set_get_attrib("Func", logging::string_literal(__func__))) \ 49 | (set_get_attrib("Tag", logging::string_literal(""))) \ 50 | (logging::keywords::severity = (logging::trivial::sev)) \ 51 | ) << "" 52 | 53 | #define LOG_SEV_TAGGED(sev, tag) \ 54 | BOOST_LOG_STREAM_WITH_PARAMS( \ 55 | (get_logger()), \ 56 | (set_get_attrib("File", logging::string_literal(__FILE__))) \ 57 | (set_get_attrib("Line", __LINE__)) \ 58 | (set_get_attrib("Func", logging::string_literal(__func__))) \ 59 | (set_get_attrib("Tag", logging::string_literal(tag))) \ 60 | (logging::keywords::severity = (logging::trivial::sev)) \ 61 | ) << "" 62 | 63 | #define LOG_DBG LOG_SEV(debug) 64 | #define LOG_INF LOG_SEV(info) 65 | #define LOG_WRN LOG_SEV(warning) 66 | #define LOG_ERR LOG_SEV(error) 67 | 68 | uint64_t get_tid() noexcept; 69 | 70 | #define LOG_STAMP_THREAD\ 71 | BOOST_LOG_SCOPED_THREAD_ATTR("ThreadPID", attrs::constant(ufw::get_tid())) 72 | 73 | 74 | void initialize_logger(); 75 | void configure_logger(std::string const& cfg); 76 | 77 | } // namespace ufw 78 | 79 | inline decltype(auto) get_logger() { return logging::trivial::logger::get(); } 80 | -------------------------------------------------------------------------------- /ufw/app/application.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017-2023 Vladimir Lysyy (mrbald@github) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "exception_types.hpp" 20 | #include "configuration.hpp" 21 | #include "logger.hpp" 22 | #include "entity.hpp" 23 | #include "lifecycle_participant.hpp" 24 | #include "loader.hpp" 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | namespace ufw { 40 | 41 | struct application 42 | { 43 | application(); 44 | 45 | void register_loader(entity_id const& id, loader_func_t loader_func); 46 | 47 | template 48 | void register_loader(entity_id const& id) 49 | { 50 | register_loader(id, [](config_t const& cfg, entity_id const& id, resolved_entity_id rid, application& app) 51 | { 52 | return std::make_unique(cfg.as(), id, rid, app); 53 | }); 54 | } 55 | 56 | // entity public c-tor signature must be `T(..., entity_id const&, resolved_entity_id, application&)` 57 | template 58 | resolved_entity_id add(entity_id const& id, Args&&... args) 59 | { 60 | static_assert(std::is_base_of::value, ""); 61 | 62 | if (structure_locked_) 63 | throw fatal_error("cannot add entity - application structure already locked, likely a bug in the code"); 64 | 65 | resolved_entity_id const rid = entities_.size(); 66 | 67 | if (!entity_ids_.emplace(id, rid).second) 68 | throw fatal_error("duplicate entity ID, check configuration"); 69 | 70 | entities_.push_back(std::make_unique(std::forward(args)..., id, rid, *this)); 71 | 72 | LOG_INF << "loaded with ctor: " << id << "<" << rid << ">"; 73 | return rid; 74 | } 75 | 76 | // loader `loader_id` should be pre-registered for loader ID specified in the config 77 | resolved_entity_id add(entity_id const& id, entity_id const& loader_id, config_t const& cfg); 78 | 79 | resolved_entity_id resolve_entity_id(entity_id const& id) const; 80 | 81 | template 82 | T& get(resolved_entity_id rid) const 83 | { 84 | // TODO: VL: write generic exception type translator template 85 | try 86 | { 87 | return dynamic_cast(*entities_.at(rid)); 88 | } 89 | catch (std::bad_cast const&) 90 | { 91 | throw fatal_error(entities_.at(rid)->id() + "<" + std::to_string(rid) + "> is not " + boost::core::demangle(typeid(T).name())); 92 | } 93 | catch (std::out_of_range const&) 94 | { 95 | throw fatal_error("no entity with ID " + std::to_string(rid)); 96 | } 97 | } 98 | 99 | template 100 | T& get(entity_id const& id) const 101 | { 102 | auto rid = resolve_entity_id(id); 103 | if (rid >= entities_.size()) 104 | throw fatal_error("no entity with ID " + id); 105 | return get(rid); 106 | } 107 | 108 | template 109 | void for_each(F const& f) 110 | { 111 | for (auto& base_ptr: entities_) 112 | { 113 | auto* casted_ptr = dynamic_cast(base_ptr.get()); 114 | if (casted_ptr) 115 | f(*casted_ptr); 116 | } 117 | } 118 | 119 | entity& get(resolved_entity_id rid) const; 120 | 121 | void load(int argc, char const** argv); 122 | 123 | void run(); 124 | 125 | void shutdown(); 126 | 127 | boost::asio::io_context& context() { return context_; } 128 | private: 129 | entity_id id() const { return "app"; } // for ENTITY_LOGGER macro to work 130 | 131 | private: 132 | void load(application_config const& cfg); 133 | 134 | boost::asio::io_context context_; 135 | boost::asio::signal_set terminal_signals_ {context_, SIGINT/*, SIGTERM*/}; 136 | std::unique_ptr work_; 137 | 138 | std::vector> entities_; 139 | std::map entity_ids_; 140 | 141 | std::vector> lifecycle_participants_; 142 | 143 | ENTITY_LOGGER; 144 | 145 | bool structure_locked_ {false}; 146 | }; 147 | 148 | 149 | template 150 | void entity_ref::resolve() 151 | { 152 | resolved_id_ = app_.resolve_entity_id(id_); 153 | if (resolved_id_ == resolved_entity_id(-1)) 154 | throw fatal_error("no entity with ID " + id_); 155 | target_ = &app_.get(resolved_id_); 156 | } 157 | 158 | } // namespace ufw 159 | 160 | -------------------------------------------------------------------------------- /ufw/app/application.cpp: -------------------------------------------------------------------------------- 1 | #include "application.hpp" 2 | 3 | #include "exception_types.hpp" 4 | #include "configuration.hpp" 5 | #include "logger.hpp" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace ufw { 21 | 22 | struct default_loader: loader 23 | { 24 | using loader::loader; 25 | 26 | std::unique_ptr load(entity_id const& id, resolved_entity_id rid, config_t const& cfg) override 27 | { 28 | return load(id, id, rid, cfg); 29 | } 30 | 31 | std::unique_ptr load(entity_id const& lid, entity_id const& eid, resolved_entity_id rid, config_t const& cfg) 32 | { 33 | try 34 | { 35 | return loader_funcs_.at(lid)(cfg, eid, rid, app()); 36 | } 37 | catch (std::out_of_range const&) 38 | { 39 | throw fatal_error("no default loader " + lid + " registered for entity " + eid); 40 | } 41 | } 42 | 43 | void register_loader(entity_id const& id, loader_func_t loader_func) 44 | { 45 | if (!loader_funcs_.emplace(id, std::move(loader_func)).second) 46 | throw fatal_error("duplicate loader registration for enity ID " + id); 47 | LOG_INF << "registered loader function for " << id; 48 | } 49 | 50 | private: 51 | std::map loader_funcs_; 52 | }; 53 | 54 | 55 | application::application() 56 | { 57 | add(""); 58 | 59 | register_loader("LOGGER", [&](config_t const& cfg, entity_id const& id, resolved_entity_id rid, application& app) 60 | { 61 | configure_logger(cfg.Scalar()); 62 | return std::make_unique(id, rid, app); 63 | }); 64 | } 65 | 66 | void application::register_loader(entity_id const& id, loader_func_t loader_func) 67 | { 68 | if (structure_locked_) 69 | throw fatal_error("cannot register loader - application structure already locked, likely a bug in the code"); 70 | 71 | get("").register_loader(id, std::move(loader_func)); 72 | } 73 | 74 | // loader `loader_id` should be pre-registered for loader ID specified in the config 75 | resolved_entity_id application::add(entity_id const& id, entity_id const& loader_id, config_t const& cfg) 76 | { 77 | if (structure_locked_) 78 | throw fatal_error("cannot load entity - application structure already locked, likely a bug in the code"); 79 | 80 | resolved_entity_id const rid = entities_.size(); 81 | if (!entity_ids_.emplace(id, rid).second) 82 | throw fatal_error("duplicate entity ID, check configuration"); 83 | 84 | auto loader_rid = resolve_entity_id(loader_id); 85 | if (loader_rid < entities_.size()) 86 | { 87 | entities_.push_back(get(loader_rid).load(id, rid, cfg)); 88 | LOG_INF << "loaded with [" << loader_id << "<" << loader_rid << ">]: " << id << "<" << rid << ">"; 89 | } 90 | else 91 | { 92 | entities_.push_back(get("").load(loader_id, id, rid, cfg)); 93 | LOG_INF << "loaded with loader function [" << loader_id << "]: " << id << "<" << rid << ">"; 94 | } 95 | 96 | return rid; 97 | } 98 | 99 | resolved_entity_id application::resolve_entity_id(entity_id const& id) const 100 | { 101 | auto it = entity_ids_.find(id); 102 | return it == entity_ids_.end() ? -1 : it->second; 103 | } 104 | 105 | entity& application::get(resolved_entity_id rid) const 106 | { 107 | return get(rid); 108 | } 109 | 110 | void application::load(int argc, char const** argv) 111 | { 112 | namespace po = boost::program_options; 113 | 114 | std::string config_file = "config.yaml"; 115 | po::options_description desc {"Options"}; 116 | desc.add_options() 117 | ("help,h", "Print this help message") 118 | ("config,c", po::value(&config_file)->default_value(config_file), "application config file") 119 | ; 120 | 121 | po::variables_map vm; 122 | po::store(po::parse_command_line(argc, argv, desc), vm); 123 | 124 | if (vm.count("help")) 125 | { 126 | std::cout << desc << std::endl; 127 | throw fatal_error("help displayed, bye"); 128 | } 129 | 130 | po::notify(vm); 131 | 132 | LOG_INF << "loading configuration from " << config_file; 133 | std::ifstream in(config_file.c_str()); 134 | if (!in) throw std::runtime_error("config file not found"); 135 | YAML::Node node = YAML::Load(in); 136 | 137 | load(node["application"].as()); 138 | } 139 | 140 | void application::run() 141 | { 142 | LOG_STAMP_THREAD; 143 | 144 | LOG_INF << "initializing lifecycle participants"; 145 | for (lifecycle_participant& x: lifecycle_participants_) 146 | { 147 | LOG_INF << "initializing " << dynamic_cast(x).id(); 148 | x.init(); 149 | } 150 | 151 | terminal_signals_.async_wait([this](boost::system::error_code const& error, int signal_number) 152 | { 153 | if (!error) 154 | { 155 | std::map names {{SIGINT, "SIGINT"}, {SIGTERM, "SIGTERM"}}; 156 | LOG_WRN << "terminal signal " << names[signal_number] << " received, terminating main context"; 157 | shutdown(); 158 | } 159 | }); 160 | 161 | LOG_INF << "scheduling lifecycle participants ping"; 162 | for (lifecycle_participant& x: lifecycle_participants_) 163 | context_.post([&x]{ x.up(); }); // TODO: VL: ping participants via their inboxes (once inboxes are implemented) 164 | context_.post([this]{LOG_INF << "UP";}); 165 | 166 | LOG_INF << "starting lifecycle participants"; 167 | for (lifecycle_participant& x: lifecycle_participants_) 168 | { 169 | LOG_INF << "starting " << dynamic_cast(x).id(); 170 | x.start(); 171 | } 172 | 173 | work_ = std::make_unique(context_); 174 | context_.run(); 175 | 176 | std::reverse(begin(lifecycle_participants_), end(lifecycle_participants_)); 177 | 178 | LOG_INF << "stopping lifecycle participants"; 179 | for (lifecycle_participant& x: lifecycle_participants_) 180 | { 181 | LOG_INF << "stopping " << dynamic_cast(x).id(); 182 | x.stop(); 183 | } 184 | 185 | LOG_INF << "deinitializing lifecycle participants"; 186 | for (lifecycle_participant& x: lifecycle_participants_) 187 | { 188 | LOG_INF << "deinitializing " << dynamic_cast(x).id(); 189 | x.fini(); 190 | } 191 | } 192 | 193 | void application::shutdown() 194 | { 195 | work_.reset(); 196 | context_.stop(); 197 | } 198 | 199 | void application::load(application_config const& cfg) 200 | { 201 | for (auto& entity_cfg: cfg.entities) 202 | add(entity_cfg.name, entity_cfg.loader_ref, entity_cfg.config); 203 | 204 | entities_.shrink_to_fit(); 205 | 206 | for_each([&](lifecycle_participant& lp) 207 | { 208 | lifecycle_participants_.push_back(std::ref(lp)); 209 | }); 210 | 211 | lifecycle_participants_.shrink_to_fit(); 212 | 213 | structure_locked_ = true; 214 | } 215 | 216 | 217 | } // namespace ufw 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | μFW - Micro Framework 2 | ======================== 3 | [![build](https://api.travis-ci.org/mrbald/ufw.svg?branch=master)](https://travis-ci.org/mrbald/ufw) 4 | [![Join the chat at https://gitter.im/mrbald-ufw/Lobby](https://badges.gitter.im/mrbald-ufw/Lobby.svg)](https://gitter.im/mrbald-ufw/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | ![Licence - ](https://img.shields.io/github/license/mrbald/ufw.svg) 6 | 7 | μFW is a minimalist framework for rapid server side applications prototyping and experimental work on [Unix-like][1] operating systems, primarily [Linux][2] and [macOS][3]. 8 | Those familiar with [Spring][4] or [Guice][5] may experience a strong deja-vu — dependency injection support was one of the → 9 | 10 | Design Objectives 11 | ----------------- 12 | 13 | * Minimum number of lines of code — clean concepts, compact implementations 14 | * Modularity in spirit of [inversion of control][6] 15 | * [Late binding][7] support via [shared library][11]-based [plugins][12] 16 | * Consistency in module configuration, lifecycle, concurrency, and logging 17 | * Singleton-free design with traceable dependencies 18 | * Structured configuration reflecting both the application topology and the concurrency model 19 | * Zero steady state runtime overhead 20 | * Hacking-friendly design 21 | 22 | Following core C++ design principles, the rule _"you don't pay for what you don't use"_ is adhered to where possible. 23 | 24 | Introduction 25 | ------------ 26 | 27 | ![uFW Topology](uFW_Topology.png) 28 | 29 | ### Terminology 30 | 31 | The terminology used in the framework maps directly to the main building blocks, which are 32 | 33 | * _entity_ - an identifiable building block of the application (a module) 34 | * _application_ - container of _entities_ 35 | * _loader_ - an _entity_ capable of loading other _entities_ 36 | * _lifecycle_participant_ - an _entity_ with application managed lifecycle 37 | * _execution context_ - set of rules for code execution (e.g. a specific thread, a thread pool, a strand on a thread pool, ...) 38 | * _launcher_ - a binary (ufw_launcher, the entry point into an application) 39 | 40 | ### Configuration 41 | 42 | Application configuration language is hierarchical [YAML][8]. 43 | This format gives a good representation of both application bootstrap process and the runtime structure. 44 | 45 | ### Lifecycle Phases 46 | 47 | Application modules are created in the order they are defined in the configuration file and are destroyed in the reverse order. 48 | Modules can opt to participate in the structural lifecycle by extending the virtual `ufw::lifecycle_participant` base. 49 | The lifecycle phases `lifecycle_participant`-s are transitioned through are below. 50 | The order of transition among individual participants matches their declaration order in the application configuration file. 51 | 52 | * `init()` - *lifecycle_participants* may/should discover and cache strongly typed references to each other and fail fast if anything is missing or is of a wrong type 53 | * `start()` - *lifecycle_participants* may/should establish required connections, spawn threads, etc. 54 | * `up()` - *lifecycle_participants* may start messaging others 55 | * `stop()` - opposite of `start()` 56 | * `fini()` - opposite of `init()` 57 | 58 | ### Loaders 59 | 60 | A subset of entities capable of loading other entities is called _loaders_. 61 | A _loader_ _entity_ extends the virtual `ufw::loader` base. 62 | _loaders_ are _entities_. 63 | _loaders_ can load other _loaders_. 64 | A special "seed" _loader_ — the `default_loader`, is used by the _application_ to load _entities_ by name (including other loaders). 65 | Whether or not an _entity_ is loaded with a _loader_ is specified in the config (flexibility!). 66 | _entities_ can be registered in the application programmatically without _loaders_. 67 | The application registers the `default_loader` in directly in the constructor. 68 | The default launcher registers `LIBRARY` (loads shared libaries) and `PLUGIN` (loads entities from shared libraries) loaders before initiating the application bootstrap. 69 | 70 | ### Concurrency 71 | 72 | Application initialisation is done single-threaded in the application main thread. 73 | Once the application is up the main thread becomes the host of the _default execution context_. 74 | The _default execution context_ an instance of the `boost::asio::io_context` accessible from _entities_ via `this.context()`. 75 | All other concurrency models are incremental to the `ufw.application`. 76 | 77 | ### Logging 78 | 79 | Logging is a part of the framework. Modules have scoped tagged loggers (with help of macros and context-sensitive symbol lookup). 80 | The Boost.Log library was taken as Boost is already on the dependency list and the subject library is flexible and reliable. 81 | This logger is not the fastest around, but it's probably one of the cheapest to integrate with. 82 | 83 | The logger is configured the same way as any other entity. 84 | The _config_ part of the configuration is passed unchanged to the Boost.Log initializer. 85 | See the configuration file fragment below as an example. 86 | 87 | Trying It 88 | --------- 89 | 90 | μFW comes with an _example_ module packaged into a plugin shared library (`libexample.so` on [Linux][2]). 91 | 92 | The below configuration fragment has a single instance of the _example_ module. 93 | 94 | ``` 95 | --- 96 | application: 97 | 98 | entities: 99 | # ====== logger ====== 100 | - name: LOGGER 101 | config: | 102 | [Core] 103 | DisableLogging=false 104 | LogSeverity=error 105 | 106 | [Sinks.Console] 107 | Destination=Console 108 | Format="%TimeStamp(format=\"%H:%M:%S.%f\")% | %Severity(format=\"%6s\")% | %ThreadPID% | %Entity% - %Tag%%Message%" 109 | Asynchronous=true 110 | AutoFlush=true 111 | 112 | # ====== a dynamic library ====== 113 | - name: example_lib 114 | loader_ref: LIBRARY 115 | config: 116 | filename: libexample.so 117 | 118 | # ====== an entity -- plugin from a dynamic library ====== 119 | - name: example_plugin 120 | loader_ref: PLUGIN 121 | config: 122 | library_ref: example_lib 123 | constructor: example_ctor 124 | ... 125 | 126 | ``` 127 | 128 | To run it, store the above fragment into a YAML file (say config.yaml) and run the μFW launcher as `ufw_launcher -c config.yaml`. 129 | 130 | The console log should look similar to the below screenshot. 131 | 132 | ![screenshot](screenshot.png) 133 | 134 | Building Dependencies 135 | --------------------- 136 | 137 | The author's main development platforms are x86\_64 [Arch Linux][9] and [macOS + Homebrew][10]. 138 | Both have quite up to date versions of all _μFW_ dependencies. 139 | For those working in a less bleeding-edge enviroronments - below are the instructions for building the dependencies from scratch. 140 | 141 | ### Boost 142 | Use instructions from the [Boost Home Page](https://boost.org) 143 | The _μFW_ is using these modules: 144 | * system 145 | * program_options 146 | * log 147 | * boost_unit_test_framework 148 | 149 | ### YamlCPP 150 | 151 | ``` 152 | $ git clone https://github.com/jbeder/yaml-cpp.git 153 | $ mkdir yaml-cpp-build && cd yaml-cpp-build 154 | $ cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release ../yaml-cpp 155 | $ make -j$(nproc) && sudo make install 156 | ``` 157 | 158 | ### CapNProto (optional - examples) 159 | 160 | ``` 161 | $ git clone https://github.com/sandstorm-io/capnproto.git 162 | $ mkdir capnproto-build && cd capnproto-build 163 | $ cmake -DCMAKE_BUILD_TYPE=Release ../capnproto/c++ 164 | $ make -j$(nproc) && sudo make install 165 | ``` 166 | 167 | Building 168 | -------- 169 | 170 | ### Compiling 171 | 172 | $ git clone https://github.com/mrbald/ufw.git 173 | $ mkdir ufw-build && cd ufw-build 174 | $ cmake -DCMAKE_BUILD_TYPE=Release [-DCMAKE_INSTALL_PREFIX=$HOME/local] ../ufw 175 | $ make -j$(nproc) 176 | 177 | ### Running tests 178 | 179 | To run all tests run 180 | 181 | $ make unit-test 182 | 183 | To run individual tests with verbose output run 184 | 185 | $ make BOOST_TEST_LOG_LEVEL=all BOOST_TEST_RUN_FILTERS=ufw_app/* unit-test 186 | 187 | ### Running benchmarks 188 | 189 | $ make benchmark 190 | 191 | ### Installing 192 | 193 | $ sudo make install 194 | 195 | Using 196 | ----- 197 | 198 | TODO 199 | 200 | Hacking 201 | ------- 202 | 203 | TODO 204 | 205 | References 206 | ---------- 207 | [CMake/How To Find Libraries](https://cmake.org/Wiki/CMake:How_To_Find_Libraries) 208 | 209 | [Markdown Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) 210 | 211 | [Draw.io](https://www.draw.io) 212 | 213 | 214 | [1]: https://en.wikipedia.org/wiki/Unix-like 215 | [2]: https://en.wikipedia.org/wiki/Linux 216 | [3]: https://en.wikipedia.org/wiki/MacOS 217 | [4]: http://spring.io/ 218 | [5]: https://github.com/google/guice 219 | [6]: https://en.wikipedia.org/wiki/Inversion_of_control 220 | [7]: https://en.wikipedia.org/wiki/Late_binding 221 | [8]: http://yaml.org/ 222 | [9]: https://www.archlinux.org/ 223 | [10]: https://brew.sh/ 224 | [11]: https://en.wikipedia.org/wiki/Library_(computing) 225 | [12]: https://en.wikipedia.org/wiki/Plug-in_(computing) 226 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------