You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2019/01/07 20:11:50 UTC
[celix] 02/22: CELIX-438: Adds the C++ shell/shell_tui initial impl
and a small test application
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/cxx
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 9231430205ed008ae79342e25b49d1178e793e67
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Wed Jan 2 14:53:06 2019 +0100
CELIX-438: Adds the C++ shell/shell_tui initial impl and a small test application
---
bundles/shell/CMakeLists.txt | 1 +
bundles/shell/cxx_shell/CMakeLists.txt | 18 +-
bundles/shell/cxx_shell/include/celix/IShell.h | 2 +-
.../shell/cxx_shell/include/celix/IShellCommand.h | 18 +-
bundles/shell/cxx_shell/src/HelpCommand.cc | 86 +++++++++
bundles/shell/cxx_shell/src/InspectCommand.cc | 70 +++++++
bundles/shell/cxx_shell/src/LbCommand.cc | 54 ++++++
bundles/shell/cxx_shell/src/ShellActivator.cc | 157 ++++++---------
bundles/shell/cxx_shell/src/StopAndStartCommand.cc | 75 ++++++++
.../shell/cxx_shell/src/commands.h | 27 ++-
.../{cxx_shell => cxx_shell_tui}/CMakeLists.txt | 29 +--
.../shell/{ => cxx_shell_tui/gtest}/CMakeLists.txt | 16 +-
.../IShell.h => cxx_shell_tui/gtest/src/main.cc} | 23 +--
.../shell/cxx_shell_tui/src/ShellTuiActivator.cc | 150 +++++++++++++++
.../IShell.h => cxx_shell_tui/src/shell_test.cc} | 23 ++-
cmake/celix_project/AddGTest.cmake | 4 +-
libs/framework_cxx/gtest/src/Framework_tests.cc | 81 ++++----
libs/framework_cxx/include/celix/Framework.h | 45 +++--
libs/framework_cxx/include/celix/IBundle.h | 1 -
.../framework_cxx/include/celix/IBundleActivator.h | 12 +-
libs/framework_cxx/include/celix/IBundleContext.h | 7 +-
libs/framework_cxx/src/BundleImpl.h | 67 +++----
libs/framework_cxx/src/Framework.cc | 114 +++++++----
.../gtest/src/RegistryConcurrency_tests.cc | 3 +-
libs/registry/gtest/src/Registry_tests.cc | 23 ++-
libs/registry/gtest/src/ServiceTracking_tests.cc | 64 +++----
libs/registry/include/celix/Constants.h | 7 +-
libs/registry/include/celix/Properties.h | 6 +
libs/registry/include/celix/ServiceRegistry.h | 155 ++++++++-------
libs/registry/src/ServiceRegistry.cc | 211 ++++++++++++++-------
30 files changed, 1048 insertions(+), 501 deletions(-)
diff --git a/bundles/shell/CMakeLists.txt b/bundles/shell/CMakeLists.txt
index 2fd81dc..851d601 100644
--- a/bundles/shell/CMakeLists.txt
+++ b/bundles/shell/CMakeLists.txt
@@ -21,3 +21,4 @@ add_subdirectory(shell_bonjour)
add_subdirectory(shell_tui)
add_subdirectory(cxx_shell)
+add_subdirectory(cxx_shell_tui)
diff --git a/bundles/shell/cxx_shell/CMakeLists.txt b/bundles/shell/cxx_shell/CMakeLists.txt
index 070f05e..5373caf 100644
--- a/bundles/shell/cxx_shell/CMakeLists.txt
+++ b/bundles/shell/cxx_shell/CMakeLists.txt
@@ -19,18 +19,22 @@ find_package(glog REQUIRED)
#TODO rename to celix::shell && celix::shell_api
-add_library(shell_api_cxx INTERFACE)
-target_include_directories(shell_api INTERFACE
+add_library(celix_cxx_shell_api INTERFACE)
+target_include_directories(celix_cxx_shell_api INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
$<INSTALL_INTERFACE:include/celix/shell>
)
-add_library(celix_shell_cxx STATIC
+add_library(celix_cxx_shell SHARED
src/ShellActivator.cc
- include/celix/IShell.h)
-target_include_directories(celix_shell_cxx PRIVATE src)
-target_include_directories(celix_shell_cxx PUBLIC include)
-target_link_libraries(celix_shell_cxx PRIVATE glog::glog celix_framework_cxx)
+ src/LbCommand.cc
+ src/HelpCommand.cc
+ src/StopAndStartCommand.cc
+ src/InspectCommand.cc
+)
+target_include_directories(celix_cxx_shell PRIVATE src)
+target_link_libraries(celix_cxx_shell PRIVATE celix_cxx_shell_api)
+target_link_libraries(celix_cxx_shell PUBLIC glog::glog celix_framework_cxx)
#if (ENABLE_TESTING)
# add_subdirectory(gtest)
diff --git a/bundles/shell/cxx_shell/include/celix/IShell.h b/bundles/shell/cxx_shell/include/celix/IShell.h
index 9809ac5..f272c52 100644
--- a/bundles/shell/cxx_shell/include/celix/IShell.h
+++ b/bundles/shell/cxx_shell/include/celix/IShell.h
@@ -29,7 +29,7 @@ namespace celix {
virtual ~IShell() = default;
- virtual bool executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
+ virtual bool executeCommandLine(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
};
}
diff --git a/bundles/shell/cxx_shell/include/celix/IShellCommand.h b/bundles/shell/cxx_shell/include/celix/IShellCommand.h
index 8a30133..ec98c73 100644
--- a/bundles/shell/cxx_shell/include/celix/IShellCommand.h
+++ b/bundles/shell/cxx_shell/include/celix/IShellCommand.h
@@ -18,6 +18,7 @@
*/
#include <string>
+#include <vector>
#include <iostream>
#ifndef CXX_CELIX_ISHELLCOMMAND_H
@@ -29,20 +30,21 @@ namespace celix {
public:
static constexpr const char * const SERVICE_FQN = "celix::IShellCommand [Version 1]";
- static constexpr const char * const COMMAND_NAME = "name";
- static constexpr const char * const COMMAND_USAGE = "usage";
- static constexpr const char * const COMMAND_DESCRIPTION = "description";
+ static constexpr const char * const COMMAND_NAME = "COMMAND_NAME";
+ static constexpr const char * const COMMAND_USAGE = "COMMAND_USAGE";
+ static constexpr const char * const COMMAND_DESCRIPTION = "COMMAND_DESCRIPTION";
virtual ~IShellCommand() = default;
- virtual void executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
+ virtual void executeCommand(const std::string &cmdName, const std::vector<std::string> &cmdArgs, std::ostream &out, std::ostream &err) noexcept = 0;
};
- using ShellCommandFunction = std::function<void(const std::string &commandLine, std::ostream &out, std::ostream &err)>;
static constexpr const char * const SHELL_COMMAND_FUNCTION_SERVICE_FQN = "celix::ShellFunction [Version 1]";
- static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_NAME = "name";
- static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_USAGE = "usage";
- static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION = "description";
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_NAME = "COMMAND_NAME";
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_USAGE = "COMMAND_USAGE";
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION = "COMMAND_DESCRIPTION";
+ using ShellCommandFunction = std::function<void(const std::string &cmdName, const std::vector<std::string> &cmdArgs, std::ostream &out, std::ostream &err)>;
+
}
#endif //CXX_CELIX_ISHELLCOMMAND_H
diff --git a/bundles/shell/cxx_shell/src/HelpCommand.cc b/bundles/shell/cxx_shell/src/HelpCommand.cc
new file mode 100644
index 0000000..3f5f5e9
--- /dev/null
+++ b/bundles/shell/cxx_shell/src/HelpCommand.cc
@@ -0,0 +1,86 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#include "commands.h"
+
+#include "celix/api.h"
+#include "celix/IShellCommand.h"
+
+celix::ServiceRegistration impl::registerHelp(std::shared_ptr<celix::IBundleContext> ctx) {
+
+ celix::ShellCommandFunction help = [ctx](const std::string &, const std::vector<std::string> &commandArguments, std::ostream &out, std::ostream &) {
+
+ if (commandArguments.empty()) { //only command -> overview
+
+ std::string hasCommandNameFilter = std::string{"("} + celix::IShellCommand::COMMAND_NAME + "=*)";
+ std::vector<std::string> commands{};
+ ctx->useServices<celix::IShellCommand>([&](celix::IShellCommand &, const celix::Properties &props) {
+ commands.push_back(celix::getProperty(props, celix::IShellCommand::COMMAND_NAME, "!Error!"));
+ }, hasCommandNameFilter);
+
+ hasCommandNameFilter = std::string{"("} + celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME + "=*)";
+
+ std::function<void(celix::ShellCommandFunction &, const celix::Properties &)> use = [&](
+ celix::ShellCommandFunction &, const celix::Properties &props) {
+ commands.push_back(celix::getProperty(props, celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME, "!Error!"));
+ };
+ ctx->useFunctionServices(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, use, hasCommandNameFilter);
+
+ //TODO useCService with a shell command service struct
+
+ out << "Available commands: " << std::endl;
+ for (auto &name : commands) {
+ out << "|- " << name << std::endl;
+ }
+ } else { //details
+ for (auto &cmd : commandArguments) {
+ std::string commandNameFilter = std::string{"("} + celix::IShellCommand::COMMAND_NAME + "=" + cmd + ")";
+ bool found = ctx->useService<celix::IShellCommand>([&](celix::IShellCommand &, const celix::Properties &props) {
+ out << "Command Name : " << celix::getProperty(props, celix::IShellCommand::COMMAND_NAME, "!Error!") << std::endl;
+ out << "Command Usage : " << celix::getProperty(props, celix::IShellCommand::COMMAND_USAGE, "!Error!") << std::endl;
+ out << "Command Description: " << celix::getProperty(props, celix::IShellCommand::COMMAND_DESCRIPTION, "!Error!") << std::endl;
+
+ }, commandNameFilter);
+ if (!found) {
+ commandNameFilter = std::string{"("} + celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME + "=" + cmd + ")";
+ std::function<void(celix::ShellCommandFunction &, const celix::Properties &)> use = [&](
+ celix::ShellCommandFunction &, const celix::Properties &props) {
+ out << "Command Name : " << celix::getProperty(props, celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME, "!Error!") << std::endl;
+ out << "Command Usage : " << celix::getProperty(props, celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE, "!Error!") << std::endl;
+ out << "Command Description: " << celix::getProperty(props, celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION, "!Error!") << std::endl;
+ };
+ found = ctx->useFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, use, commandNameFilter);
+ }
+ if (!found) {
+ //TODO use C cmd service
+ }
+ if (!found) {
+ out << "Command '" << cmd << "' not available" << std::endl;
+ }
+ out << std::endl;
+ }
+ }
+ };
+
+ celix::Properties props{};
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "help";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "help [command name]";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "display available commands and description.";
+ return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(help), std::move(props));
+}
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/src/InspectCommand.cc b/bundles/shell/cxx_shell/src/InspectCommand.cc
new file mode 100644
index 0000000..da3bf24
--- /dev/null
+++ b/bundles/shell/cxx_shell/src/InspectCommand.cc
@@ -0,0 +1,70 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#include "commands.h"
+
+#include <functional>
+
+#include "celix/api.h"
+#include "celix/IShellCommand.h"
+
+namespace {
+
+ void inspect(std::shared_ptr<celix::IBundleContext> ctx, const std::string &, const std::vector<std::string> &cmdArgs, std::ostream &out, std::ostream &) {
+ if (cmdArgs.empty()) {
+ out << "Provide a bundle id" << std::endl;
+ } else {
+ auto &bndId = cmdArgs[0];
+ auto servicesNames = ctx->registry().listAllRegisteredServiceNames();
+
+ const std::string *what{nullptr};
+ if (cmdArgs.size() >= 2) {
+ what = &cmdArgs[1];
+ }
+
+ if (what == nullptr || *what == "provided") {
+ out << "Provided Services: \n";
+ for (auto &svcName : servicesNames) {
+ std::string filter = std::string{"("} + celix::SERVICE_BUNDLE + "=" + bndId + ")";
+ ctx->registry().useAnyServices(svcName, [&out](std::shared_ptr<void>, const celix::Properties &props, const celix::IResourceBundle &) {
+ out << "|- Service " << celix::getProperty(props, celix::SERVICE_ID, "!Error") << ":\n";
+ for (auto &pair : props) {
+ out << " |- " << pair.first << " = " << pair.second << std::endl;
+ }
+ }, filter, ctx->bundle());
+ }
+ }
+ if (what == nullptr || *what == "tracked") {
+ //TODO trackers
+ }
+ }
+ }
+}
+
+
+celix::ServiceRegistration impl::registerInspect(std::shared_ptr<celix::IBundleContext> ctx) {
+ using namespace std::placeholders;
+ celix::ShellCommandFunction cmd = std::bind(&inspect, ctx, _1, _2, _3, _4);
+
+ celix::Properties props{};
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "inspect";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "inspect bndId [provided|tracked]";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "Inspects a bundle. Showing the provided and/or tracked services";
+ return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(cmd), std::move(props));
+}
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/src/LbCommand.cc b/bundles/shell/cxx_shell/src/LbCommand.cc
new file mode 100644
index 0000000..5c1b07c
--- /dev/null
+++ b/bundles/shell/cxx_shell/src/LbCommand.cc
@@ -0,0 +1,54 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#include "commands.h"
+
+#include "celix/IShellCommand.h"
+
+namespace {
+ class LbCommand : public celix::IShellCommand {
+ public:
+ LbCommand(std::shared_ptr<celix::IBundleContext> _ctx) : ctx{std::move(_ctx)} {}
+
+ void executeCommand(const std::string &, const std::vector<std::string> &cmdArgs, std::ostream &out,
+ std::ostream &) noexcept override {
+ if (cmdArgs.empty()) {
+ out << "Bundles: " << std::endl;
+ ctx->useBundles([&out](const celix::IBundle &bnd) {
+ //TODO make aligned text table
+ out << "|- " << bnd.id() << ": " << bnd.name() << std::endl;
+ }, true);
+ }
+ //TODO parse args
+ }
+
+ private:
+ std::shared_ptr<celix::IBundleContext> ctx;
+ };
+}
+
+celix::ServiceRegistration impl::registerLb(std::shared_ptr<celix::IBundleContext> ctx) {
+ celix::Properties props{};
+ props[celix::IShellCommand::COMMAND_NAME] = "lb";
+ props[celix::IShellCommand::COMMAND_USAGE] = "list bundles. Default only the groupless bundles are listed. Use -a to list all bundles." \
+ "\nIf a group string is provided only bundles matching the group string will be listed." \
+ "\nUse -l to print the bundle locations.\nUse -s to print the bundle symbolic names\nUse -u to print the bundle update location.";
+ props[celix::IShellCommand::COMMAND_DESCRIPTION] = "lb [-l | -s | -u | -a] [group]";
+ return ctx->registerService(std::shared_ptr<celix::IShellCommand>{new LbCommand{ctx}}, std::move(props));
+}
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/src/ShellActivator.cc b/bundles/shell/cxx_shell/src/ShellActivator.cc
index 5fd4f1f..ee89d98 100644
--- a/bundles/shell/cxx_shell/src/ShellActivator.cc
+++ b/bundles/shell/cxx_shell/src/ShellActivator.cc
@@ -21,125 +21,82 @@
#include "celix/IShellCommand.h"
#include "celix/IShell.h"
+#include "commands.h"
+
namespace {
- class LbCommand : public celix::IShellCommand {
+ class Shell : public celix::IShell {
public:
- LbCommand(std::shared_ptr<celix::IBundleContext> _ctx) : ctx{std::move(_ctx)} {}
- void executeCommand(const std::string &/*command line*/, std::ostream &out, std::ostream &) noexcept override {
- //TODO parse commandLine
- out << "Bundles: " << std::endl;
- ctx->useBundles([&out](const celix::IBundle &bnd) {
- out << "|- " << bnd.id() << ": " << bnd.name() << std::endl;
- }, true);
- }
- private:
- std::shared_ptr<celix::IBundleContext> ctx;
- };
-
- celix::ServiceRegistration registerLb(std::shared_ptr<celix::IBundleContext> ctx) {
- celix::Properties props{};
- props[celix::IShellCommand::COMMAND_NAME] = "lb";
- props[celix::IShellCommand::COMMAND_USAGE] = "list installed bundles";
- props[celix::IShellCommand::COMMAND_DESCRIPTION] = "TODO";
- return ctx->registerService(std::shared_ptr<celix::IShellCommand>{new LbCommand{ctx}}, std::move(props));
- }
-
- celix::ServiceRegistration registerHelp(std::shared_ptr<celix::IBundleContext> ctx) {
-
- celix::ShellCommandFunction help = [ctx](const std::string &, std::ostream &out, std::ostream &) {
-
- std::string hasCommandNameFilter = std::string{"("} + celix::IShellCommand::COMMAND_NAME + "=*)";
- //TODO parse command line to see if details is requested instead of overview
- std::vector<std::string> commands{};
- ctx->useServices<celix::IShellCommand>([&](celix::IShellCommand&, const celix::Properties &props) {
- commands.push_back(celix::getProperty(props, celix::IShellCommand::COMMAND_NAME, "!Error!"));
- }, hasCommandNameFilter);
-
- hasCommandNameFilter = std::string{"("} + celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME + "=*)";
-
- std::function<void(celix::ShellCommandFunction&, const celix::Properties&)> use = [&](celix::ShellCommandFunction&, const celix::Properties &props) {
- commands.push_back(celix::getProperty(props, celix::IShellCommand::COMMAND_NAME, "!Error!"));
- };
- ctx->useFunctionServices(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, use, hasCommandNameFilter);
+ Shell(std::shared_ptr<celix::IBundleContext> _ctx) : ctx{std::move(_ctx)} {}
+
+ bool executeCommandLine(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept override {
+ std::string cmdName{};
+ std::vector<std::string> cmdArgs{};
+
+ char *savePtr = nullptr;
+ char *cl = strndup(commandLine.c_str(), 1024*1024);
+ char *token = strtok_r(cl, " ", &savePtr);
+ while (token != nullptr) {
+ if (cmdName.empty()) {
+ cmdName = std::string{token};
+ } else {
+ cmdArgs.emplace_back(std::string{token});
+ }
+ token = strtok_r(nullptr, " ", &savePtr);
+ }
- //TODO useCService with a shell command service struct
+ bool commandCalled = false;
- out << "Available commands: " << std::endl;
- for (auto &name : commands) {
- out << "|- " << name << std::endl;
+ if (!cmdName.empty()) {
+ std::string filter =
+ std::string{"("} + celix::IShellCommand::COMMAND_NAME + "=" + cmdName + ")";
+ commandCalled = ctx->useService<celix::IShellCommand>([&](celix::IShellCommand &cmd) {
+ cmd.executeCommand(cmdName, cmdArgs, out, err);
+ }, filter);
+ }
+ if (!cmdName.empty() && !commandCalled) {
+ std::string filter =
+ std::string{"("} + celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME + "=" + cmdName + ")";
+ std::function<void(celix::ShellCommandFunction&)> use = [&](celix::ShellCommandFunction &cmd) -> void {
+ cmd(cmdName, cmdArgs, out, err);
+ };
+ commandCalled = ctx->useFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, use, filter);
}
- };
-
- celix::Properties props{};
- props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "help";
- props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "help [command name]";
- props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "TODO";
- return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(help), std::move(props));
- }
- class Shell : public celix::IShell {
- public:
- Shell(std::shared_ptr<celix::IBundleContext> _ctx) : ctx{std::move(_ctx)} {
- celix::ServiceTrackerOptions<celix::IShellCommand> opts1{};
- opts1.updateWithProperties = [this](std::vector<std::tuple<celix::IShellCommand*,const celix::Properties*>> services) {
- std::lock_guard<std::mutex> lck(commands.mutex);
- commands.commands = std::move(services);
- };
- trk1 = ctx->trackServices(opts1);
+ //TODO C command service struct
+ if (!cmdName.empty() && !commandCalled) {
+ out << "Command '" << cmdName << "' not available. Type 'help' to see a list of available commands." << std::endl;
+ }
- celix::ServiceTrackerOptions<celix::ShellCommandFunction> opts2{};
- opts2.updateWithProperties = [this](std::vector<std::tuple<celix::ShellCommandFunction*,const celix::Properties*>> services) {
- std::lock_guard<std::mutex> lck(commands.mutex);
- commands.commandFunctions = std::move(services);
- };
- trk2 = ctx->trackFunctionServices(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, opts2);
- }
- bool executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &) noexcept override {
- out << "TODO call command '" << commandLine << "'" << std::endl;
- return false;
+ return commandCalled;
}
private:
std::shared_ptr<celix::IBundleContext> ctx;
-
- celix::ServiceTracker trk1{};
- celix::ServiceTracker trk2{};
-
- struct {
- mutable std::mutex mutex{};
- std::vector<std::tuple<celix::IShellCommand*, const celix::Properties*>> commands;
- std::vector<std::tuple<celix::ShellCommandFunction*, const celix::Properties*>> commandFunctions;
- } commands{};
};
class ShellBundleActivator : public celix::IBundleActivator {
public:
- bool start(std::shared_ptr<celix::IBundleContext> ctx) noexcept override {
- //TODO ensure fixed framework thread that call bundle activators
- registrations.push_back(registerLb(ctx));
- registrations.push_back(registerHelp(ctx));
+ ShellBundleActivator(std::shared_ptr<celix::IBundleContext> ctx) {
+ //TODO ensure fixed framework thread that call ctor/dtor bundle activators
+ registrations.push_back(impl::registerLb(ctx));
+ registrations.push_back(impl::registerHelp(ctx));
+ registrations.push_back(impl::registerStop(ctx));
+ registrations.push_back(impl::registerStart(ctx));
+ registrations.push_back(impl::registerInspect(ctx));
registrations.push_back(ctx->registerService(std::shared_ptr<celix::IShell>{new Shell{ctx}}));
-
- return true;
- }
-
- bool stop(std::shared_ptr<celix::IBundleContext>) noexcept override {
- registrations.clear();
- return true;
}
private:
- std::vector<celix::ServiceRegistration> registrations;
+ std::vector<celix::ServiceRegistration> registrations{};
};
-}
-__attribute__((constructor))
-static void registerShellBundle() {
- celix::StaticBundleOptions opts{};
- opts.bundleActivatorFactory = [](){
- return new ShellBundleActivator{};
- };
- opts.manifest[celix::MANIFEST_BUNDLE_VERSION] = "1.0.0";
- celix::registerStaticBundle("celix::Shell", opts);
-}
\ No newline at end of file
+ __attribute__((constructor))
+ static void registerShellBundle() {
+ celix::Properties manifest{};
+ manifest[celix::MANIFEST_BUNDLE_NAME] = "Shell";
+ manifest[celix::MANIFEST_BUNDLE_GROUP] = "Celix";
+ manifest[celix::MANIFEST_BUNDLE_VERSION] = "1.0.0";
+ celix::registerStaticBundle<ShellBundleActivator>("celix::Shell", manifest);
+ }
+}
diff --git a/bundles/shell/cxx_shell/src/StopAndStartCommand.cc b/bundles/shell/cxx_shell/src/StopAndStartCommand.cc
new file mode 100644
index 0000000..aae5a69
--- /dev/null
+++ b/bundles/shell/cxx_shell/src/StopAndStartCommand.cc
@@ -0,0 +1,75 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#include "commands.h"
+
+#include <functional>
+
+#include "celix/api.h"
+#include "celix/IShellCommand.h"
+
+namespace {
+
+ void stopOrStart(std::shared_ptr<celix::IBundleContext> ctx, const std::string &cmdName, const std::vector<std::string> &cmdArgs, std::ostream &out, std::ostream &err) {
+ if (cmdArgs.empty()) {
+ out << "Provide a bundle id name to " << cmdName << std::endl;
+ } else {
+ for (const auto &bndId : cmdArgs) {
+ bool isNum = true;
+ for(const char &c : bndId) {
+ isNum = isNum && isdigit(c);
+ }
+
+ if (isNum) {
+ long id = atoi(bndId.c_str());
+ if (cmdName == "stop") {
+ ctx->stopBundle(id);
+ } else {
+ ctx->startBundle(id);
+ }
+ } else {
+ err << "Cannot parse '" << bndId << "' to bundle id" << std::endl;
+ }
+ }
+ }
+ }
+}
+
+
+celix::ServiceRegistration impl::registerStop(std::shared_ptr<celix::IBundleContext> ctx) {
+ using namespace std::placeholders;
+ celix::ShellCommandFunction stop = std::bind(&stopOrStart, ctx, _1, _2, _3, _4);
+
+ celix::Properties props{};
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "stop";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "stop (bndId)+";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "Stops the provided bundles, identified by the bundle ids";
+ return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(stop), std::move(props));
+}
+
+celix::ServiceRegistration impl::registerStart(std::shared_ptr<celix::IBundleContext> ctx) {
+ using namespace std::placeholders;
+ celix::ShellCommandFunction stop = std::bind(&stopOrStart, ctx, _1, _2, _3, _4);
+
+ celix::Properties props{};
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "start";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "start (bndId)+";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "Starts the provided bundles, identified by the bundle ids";
+ return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(stop), std::move(props));
+}
\ No newline at end of file
diff --git a/libs/framework_cxx/include/celix/IBundleActivator.h b/bundles/shell/cxx_shell/src/commands.h
similarity index 53%
copy from libs/framework_cxx/include/celix/IBundleActivator.h
copy to bundles/shell/cxx_shell/src/commands.h
index 1196e99..5f641b8 100644
--- a/libs/framework_cxx/include/celix/IBundleActivator.h
+++ b/bundles/shell/cxx_shell/src/commands.h
@@ -17,22 +17,21 @@
*under the License.
*/
-#ifndef CXX_CELIX_IBUNDLEACTIVATOR_H
-#define CXX_CELIX_IBUNDLEACTIVATOR_H
+#ifndef CELIX_COMMANDS_H
+#define CELIX_COMMANDS_H
-#include <memory>
+#include "celix/api.h"
-#include "IBundleContext.h"
+namespace impl {
+ celix::ServiceRegistration registerLb(std::shared_ptr<celix::IBundleContext> ctx);
+ celix::ServiceRegistration registerHelp(std::shared_ptr<celix::IBundleContext> ctx);
+ celix::ServiceRegistration registerStop(std::shared_ptr<celix::IBundleContext> ctx);
+ celix::ServiceRegistration registerStart(std::shared_ptr<celix::IBundleContext> ctx);
+ celix::ServiceRegistration registerInspect(std::shared_ptr<celix::IBundleContext> ctx);
-namespace celix {
- class IBundleActivator {
- public:
- virtual ~IBundleActivator() = default;
-
- virtual bool resolve(std::shared_ptr<celix::IBundleContext> /*ctx*/) noexcept { return true; };
- virtual bool start(std::shared_ptr<celix::IBundleContext> ctx) noexcept = 0;
- virtual bool stop(std::shared_ptr<celix::IBundleContext> /*ctx*/) noexcept { return true; }
- };
+ //query services, trackers & meta trackers TODO
+ // celix::ServiceRegistration registerQuery(std::shared_ptr<celix::IBundleContext> ctx);
}
-#endif //CXX_CELIX_IBUNDLEACTIVATOR_H
+
+#endif //CELIX_COMMANDS_H
diff --git a/bundles/shell/cxx_shell/CMakeLists.txt b/bundles/shell/cxx_shell_tui/CMakeLists.txt
similarity index 60%
copy from bundles/shell/cxx_shell/CMakeLists.txt
copy to bundles/shell/cxx_shell_tui/CMakeLists.txt
index 070f05e..df20046 100644
--- a/bundles/shell/cxx_shell/CMakeLists.txt
+++ b/bundles/shell/cxx_shell_tui/CMakeLists.txt
@@ -17,21 +17,22 @@
find_package(glog REQUIRED)
-#TODO rename to celix::shell && celix::shell_api
+#TODO rename to celix::shell_tui
-add_library(shell_api_cxx INTERFACE)
-target_include_directories(shell_api INTERFACE
- $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
- $<INSTALL_INTERFACE:include/celix/shell>
-)
-
-add_library(celix_shell_cxx STATIC
- src/ShellActivator.cc
- include/celix/IShell.h)
-target_include_directories(celix_shell_cxx PRIVATE src)
-target_include_directories(celix_shell_cxx PUBLIC include)
-target_link_libraries(celix_shell_cxx PRIVATE glog::glog celix_framework_cxx)
+#OR static lib, but then with all symbols to force constructor attribute
+add_library(celix_cxx_shell_tui SHARED
+ src/ShellTuiActivator.cc
+ src/shell_test.cc)
+target_include_directories(celix_cxx_shell_tui PRIVATE src)
+target_link_libraries(celix_cxx_shell_tui PRIVATE celix_cxx_shell_api)
+target_link_libraries(celix_cxx_shell_tui PUBLIC glog::glog celix_framework_cxx)
#if (ENABLE_TESTING)
# add_subdirectory(gtest)
-#endif ()
\ No newline at end of file
+#endif ()
+
+
+add_executable(shell_test
+ src/shell_test.cc
+)
+target_link_libraries(shell_test PRIVATE celix_cxx_shell celix_cxx_shell_tui)
\ No newline at end of file
diff --git a/bundles/shell/CMakeLists.txt b/bundles/shell/cxx_shell_tui/gtest/CMakeLists.txt
similarity index 66%
copy from bundles/shell/CMakeLists.txt
copy to bundles/shell/cxx_shell_tui/gtest/CMakeLists.txt
index 2fd81dc..b4a7204 100644
--- a/bundles/shell/CMakeLists.txt
+++ b/bundles/shell/cxx_shell_tui/gtest/CMakeLists.txt
@@ -5,9 +5,9 @@
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -15,9 +15,11 @@
# specific language governing permissions and limitations
# under the License.
-add_subdirectory(shell)
-add_subdirectory(remote_shell)
-add_subdirectory(shell_bonjour)
-add_subdirectory(shell_tui)
+set(SOURCES
+ src/main.cc
+)
+add_executable(celix_shell_cxx_tests ${SOURCES})
+target_link_libraries(celix_shell_cxx_tests PRIVATE gtest celix_framework_cxx celix_shell_cxx)
-add_subdirectory(cxx_shell)
+add_test(NAME celix_shell_cxx_tests COMMAND celix_shell_cxx_tests)
+SETUP_TARGET_FOR_COVERAGE(celix_shell_cxx_tests_cov celix_shell_cxx_tests ${CMAKE_BINARY_DIR}/coverage/celix_shell_cxx_tests/celix_shell_cxx_tests)
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/include/celix/IShell.h b/bundles/shell/cxx_shell_tui/gtest/src/main.cc
similarity index 66%
copy from bundles/shell/cxx_shell/include/celix/IShell.h
copy to bundles/shell/cxx_shell_tui/gtest/src/main.cc
index 9809ac5..a76daa7 100644
--- a/bundles/shell/cxx_shell/include/celix/IShell.h
+++ b/bundles/shell/cxx_shell_tui/gtest/src/main.cc
@@ -17,20 +17,17 @@
*under the License.
*/
-#ifndef CXX_CELIX_ISHELL_H
-#define CXX_CELIX_ISHELL_H
+#include <gtest/gtest.h>
+#include <glog/logging.h>
-#include <iostream>
+int main(int argc, char **argv) {
+ google::InitGoogleLogging(argv[0]);
+ google::LogToStderr();
-namespace celix {
- class IShell {
- public:
- static constexpr const char * const SERVICE_FQN = "celix::IShell [Version 1]";
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
- virtual ~IShell() = default;
+ google::ShutdownGoogleLogging();
- virtual bool executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
- };
-}
-
-#endif //CXX_CELIX_ISHELL_H
+ return rc;
+}
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc b/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc
new file mode 100644
index 0000000..dc71faa
--- /dev/null
+++ b/bundles/shell/cxx_shell_tui/src/ShellTuiActivator.cc
@@ -0,0 +1,150 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#include <thread>
+#include <cstdio>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <glog/logging.h>
+
+#include "celix/api.h"
+#include "celix/IShellCommand.h"
+#include "celix/IShell.h"
+
+static constexpr int LINE_SIZE = 256;
+static constexpr const char * const PROMPT = "-> ";
+
+static constexpr int KEY_ENTER = '\n';
+
+namespace {
+
+ class ShellTui {
+ public:
+ ShellTui() {
+ int fds[2];
+ int rc = pipe(fds);
+ if (rc == 0) {
+ readPipeFd = fds[0];
+ writePipeFd = fds[1];
+ if(fcntl(writePipeFd, F_SETFL, O_NONBLOCK) == 0) {
+ readThread = std::thread{&ShellTui::runnable, this};
+ } else {
+ LOG(ERROR) << "fcntl on pipe failed" << std::endl;
+ }
+ } else {
+ LOG(ERROR) << "fcntl on pipe failed" << std::endl;
+ }
+ }
+
+ ~ShellTui() {
+ write(writePipeFd, "\0", 1); //trigger select to stop
+ readThread.join();
+ }
+
+ void runnable() {
+ //setup file descriptors
+ fd_set rfds;
+ int nfds = writePipeFd > STDIN_FILENO ? (writePipeFd +1) : (STDIN_FILENO + 1);
+
+ for (;;) {
+ writePrompt();
+ FD_ZERO(&rfds);
+ FD_SET(STDIN_FILENO, &rfds);
+ FD_SET(readPipeFd, &rfds);
+
+ if (select(nfds, &rfds, NULL, NULL, NULL) > 0) {
+ if (FD_ISSET(readPipeFd, &rfds)) {
+ break; //something is written to the pipe -> exit thread
+ } else if (FD_ISSET(STDIN_FILENO, &rfds)) {
+ parseInput();
+ }
+ }
+ }
+ }
+
+ void writePrompt() {
+ std::cout << PROMPT;
+ std::flush(std::cout);
+ }
+
+ void parseInput() {
+ char* line = NULL;
+ int nr_chars = (int)read(STDIN_FILENO, buffer, LINE_SIZE-pos-1);
+ for (int bufpos = 0; bufpos < nr_chars; bufpos++) {
+ if (buffer[bufpos] == KEY_ENTER) { //end of line -> forward command
+ line = in; // todo trim string
+ std::lock_guard<std::mutex> lck{mutex};
+ if (shell) {
+ shell->executeCommandLine(line, std::cout, std::cerr);
+ } else {
+ std::cerr << "Shell service not available\n";
+ }
+ pos = 0;
+ in[pos] = '\0';
+ } else { //text
+ in[pos] = buffer[bufpos];
+ in[pos + 1] = '\0';
+ pos++;
+ continue;
+ }
+ } //for
+ }
+
+ void setShell(std::shared_ptr<celix::IShell> _shell) {
+ std::lock_guard<std::mutex> lck{mutex};
+ shell = _shell;
+ }
+ private:
+ std::mutex mutex{};
+ std::shared_ptr<celix::IShell> shell{};
+
+ std::thread readThread;
+
+ int readPipeFd;
+ int writePipeFd;
+
+
+ char in[LINE_SIZE+1]{};
+ char buffer[LINE_SIZE+1]{};
+ int pos{};
+ };
+
+
+ class ShellTuiBundleActivator : public celix::IBundleActivator {
+ public:
+ ShellTuiBundleActivator(std::shared_ptr<celix::IBundleContext> ctx) {
+ celix::ServiceTrackerOptions<celix::IShell> opts{};
+ opts.set = std::bind(&ShellTui::setShell, &tui, std::placeholders::_1);
+ trk = ctx->trackServices(opts);
+ }
+ private:
+ ShellTui tui{};
+ celix::ServiceTracker trk;
+ };
+
+ __attribute__((constructor))
+ static void registerShellBundle() {
+ celix::Properties manifest{};
+ manifest[celix::MANIFEST_BUNDLE_NAME] = "Shell Tui";
+ manifest[celix::MANIFEST_BUNDLE_GROUP] = "Celix";
+ manifest[celix::MANIFEST_BUNDLE_VERSION] = "1.0.0";
+ celix::registerStaticBundle<ShellTuiBundleActivator>("celix::ShellTui", manifest);
+ }
+}
\ No newline at end of file
diff --git a/bundles/shell/cxx_shell/include/celix/IShell.h b/bundles/shell/cxx_shell_tui/src/shell_test.cc
similarity index 68%
copy from bundles/shell/cxx_shell/include/celix/IShell.h
copy to bundles/shell/cxx_shell_tui/src/shell_test.cc
index 9809ac5..cd3af3c 100644
--- a/bundles/shell/cxx_shell/include/celix/IShell.h
+++ b/bundles/shell/cxx_shell_tui/src/shell_test.cc
@@ -17,20 +17,19 @@
*under the License.
*/
-#ifndef CXX_CELIX_ISHELL_H
-#define CXX_CELIX_ISHELL_H
-
#include <iostream>
-namespace celix {
- class IShell {
- public:
- static constexpr const char * const SERVICE_FQN = "celix::IShell [Version 1]";
+#include <glog/logging.h>
+
+#include "celix/api.h"
- virtual ~IShell() = default;
- virtual bool executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
- };
-}
+int main(int /*argc*/, char **argv) {
+ google::InitGoogleLogging(argv[0]);
+ google::LogToStderr();
-#endif //CXX_CELIX_ISHELL_H
+ auto fw = celix::Framework{};
+ std::cout << "Waiting for Framework shutdown\n";
+ fw.waitForShutdown();
+ return 0;
+}
\ No newline at end of file
diff --git a/cmake/celix_project/AddGTest.cmake b/cmake/celix_project/AddGTest.cmake
index a1753a8..93227c1 100644
--- a/cmake/celix_project/AddGTest.cmake
+++ b/cmake/celix_project/AddGTest.cmake
@@ -30,7 +30,7 @@ file(MAKE_DIRECTORY ${source_dir}/googletest/include)
add_library(gtest IMPORTED STATIC GLOBAL)
add_dependencies(gtest googletest_project)
set_target_properties(gtest PROPERTIES
- IMPORTED_LOCATION "${binary_dir}/lib/libgtest.a"
+ IMPORTED_LOCATION "${binary_dir}/googlemock/gtest/libgtest.a"
INTERFACE_INCLUDE_DIRECTORIES "${source_dir}/googletest/include"
)
@@ -38,6 +38,6 @@ file(MAKE_DIRECTORY ${source_dir}/googlemock/include)
add_library(gmock IMPORTED STATIC GLOBAL)
add_dependencies(gmock googletest_project)
set_target_properties(gmock PROPERTIES
- IMPORTED_LOCATION "${binary_dir}/lib/libgmock.a"
+ IMPORTED_LOCATION "${binary_dir}/googlemock/libgmock.a"
INTERFACE_INCLUDE_DIRECTORIES "${source_dir}/googlemock/include"
)
diff --git a/libs/framework_cxx/gtest/src/Framework_tests.cc b/libs/framework_cxx/gtest/src/Framework_tests.cc
index 34cad54..7afd9a1 100644
--- a/libs/framework_cxx/gtest/src/Framework_tests.cc
+++ b/libs/framework_cxx/gtest/src/Framework_tests.cc
@@ -44,80 +44,71 @@ TEST_F(FrameworkTest, CreateDestroy) {
EXPECT_TRUE(isFramework);
}
-TEST_F(FrameworkTest, InstallBundle) {
+class EmbeddedActivator : public celix::IBundleActivator {
+public:
+ EmbeddedActivator(std::shared_ptr<celix::IBundleContext>) {
+ startCount++;
+ }
- class EmbeddedActivator : public celix::IBundleActivator {
- public:
- virtual ~EmbeddedActivator() = default;
+ virtual ~EmbeddedActivator() {
+ stopCount++;
+ }
- bool resolve(std::shared_ptr<celix::IBundleContext> ctx) noexcept override {
- EXPECT_GE(ctx->bundle()->id(), 1);
- resolveCalled = true;
- return true;
- }
-
- bool start(std::shared_ptr<celix::IBundleContext>) noexcept override {
- startCalled = true;
- return true;
- }
-
- bool stop(std::shared_ptr<celix::IBundleContext>) noexcept override {
- stopCalled = true;
- return true;
- }
-
- bool resolveCalled = false;
- bool startCalled = false;
- bool stopCalled = false;
- };
+ static std::atomic<int> startCount;
+ static std::atomic<int> stopCount;
+};
- long bndId1 = framework().installBundle<EmbeddedActivator>("embedded");
+std::atomic<int> EmbeddedActivator::startCount{0};
+std::atomic<int> EmbeddedActivator::stopCount{0};
+
+TEST_F(FrameworkTest, InstallBundle) {
+ EmbeddedActivator::startCount = 0;
+ EmbeddedActivator::stopCount = 0;
+
+ auto actFactory = [](std::shared_ptr<celix::IBundleContext> ctx) -> celix::IBundleActivator* {
+ return new EmbeddedActivator{std::move(ctx)};
+ };
+ long bndId1 = framework().installBundle("embedded", actFactory);
EXPECT_GE(bndId1, 0);
+ EXPECT_EQ(1, EmbeddedActivator::startCount);
+ EXPECT_EQ(0, EmbeddedActivator::stopCount);
- std::shared_ptr<EmbeddedActivator> act{new EmbeddedActivator};
- long bndId2 = framework().installBundle("embedded2", act);
+ long bndId2 = framework().installBundle<EmbeddedActivator>("embedded2");
EXPECT_GE(bndId2, 0);
EXPECT_NE(bndId1, bndId2);
- EXPECT_TRUE(act->resolveCalled);
- EXPECT_TRUE(act->startCalled);
- EXPECT_FALSE(act->stopCalled);
+ EXPECT_EQ(2, EmbeddedActivator::startCount);
+ EXPECT_EQ(0, EmbeddedActivator::stopCount);
framework().stopBundle(bndId2);
- EXPECT_TRUE(act->stopCalled);
+ EXPECT_EQ(1, EmbeddedActivator::stopCount);
- std::shared_ptr<EmbeddedActivator> act3{new EmbeddedActivator};
{
celix::Framework fw{};
- fw.installBundle("embedded3", act3);
- EXPECT_TRUE(act3->resolveCalled);
- EXPECT_TRUE(act3->startCalled);
- EXPECT_FALSE(act3->stopCalled);
+ fw.installBundle<EmbeddedActivator>("embedded3");
+ EXPECT_EQ(3, EmbeddedActivator::startCount);
+ EXPECT_EQ(1, EmbeddedActivator::stopCount);
//NOTE fw out of scope -> bundle stopped
}
- EXPECT_TRUE(act3->stopCalled);
+ EXPECT_EQ(3, EmbeddedActivator::startCount);
+ EXPECT_EQ(2, EmbeddedActivator::stopCount);
}
TEST_F(FrameworkTest, StaticBundleTest) {
class EmbeddedActivator : public celix::IBundleActivator {
public:
+ EmbeddedActivator() {}
virtual ~EmbeddedActivator() = default;
-
- bool start(std::shared_ptr<celix::IBundleContext>) noexcept override {
- return true;
- }
};
int count = 0;
- auto factory = [&]() -> celix::IBundleActivator * {
+ auto factory = [&](std::shared_ptr<celix::IBundleContext>) -> celix::IBundleActivator * {
count++;
return new EmbeddedActivator{};
};
EXPECT_EQ(0, framework().listBundles().size()); //no bundles installed;
- celix::StaticBundleOptions opts;
- opts.bundleActivatorFactory = std::move(factory);
- celix::registerStaticBundle("static", opts);
+ celix::registerStaticBundle("static", factory);
EXPECT_EQ(1, framework().listBundles().size()); //static bundle instance installed
EXPECT_EQ(1, count);
diff --git a/libs/framework_cxx/include/celix/Framework.h b/libs/framework_cxx/include/celix/Framework.h
index 60b6fca..a2d9b13 100644
--- a/libs/framework_cxx/include/celix/Framework.h
+++ b/libs/framework_cxx/include/celix/Framework.h
@@ -30,25 +30,25 @@
namespace celix {
- struct StaticBundleOptions {
- std::string name{};
- std::string group{};
- std::string version{};
- celix::Properties manifest{};
-
- std::function<celix::IBundleActivator*()> bundleActivatorFactory{};
-
- //TODO resources. poiting to bundle specific symbols which is linked zip file
- char * const resoucreZip = nullptr;
- size_t resourceZipLength = 0;
- };
-
- void registerStaticBundle(std::string symbolicName, const StaticBundleOptions &opts);
- //TODO useFrameworks with a callback with as argument a fw ref
+ //TODO resources. resolved from bundle specific symbols which is linked zip file to the library
+ void registerStaticBundle(
+ std::string symbolicName,
+ std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> bundleActivatorFactory = {},
+ celix::Properties manifest = {});
+
+ template<typename T>
+ void registerStaticBundle(
+ std::string symbolicName,
+ celix::Properties manifest = {}) {
+ auto actFactory = [](std::shared_ptr<celix::IBundleContext> ctx) {
+ return new T{std::move(ctx)};
+ };
+ celix::registerStaticBundle(std::move(symbolicName), actFactory, std::move(manifest));
+ }
class Framework {
public:
- Framework();
+ Framework(celix::Properties config = {});
~Framework();
Framework(Framework &&rhs);
Framework& operator=(Framework&& rhs);
@@ -56,14 +56,15 @@ namespace celix {
Framework(const Framework& rhs) = delete;
Framework& operator=(const Framework &rhs) = delete;
-
template<typename T>
- long installBundle(std::string symbolicName, celix::Properties manifest = {}, bool autoStart = true) {
- std::shared_ptr<celix::IBundleActivator> activator{new T{}};
- return installBundle(std::move(symbolicName), std::move(activator), std::move(manifest), autoStart);
+ long installBundle(std::string name, celix::Properties manifest = {}, bool autoStart = true) {
+ auto actFactory = [](std::shared_ptr<celix::IBundleContext> ctx) {
+ return new T{std::move(ctx)};
+ };
+ return installBundle(name, std::move(actFactory), manifest, autoStart);
}
- long installBundle(std::string symbolicName, std::shared_ptr<celix::IBundleActivator> activator, celix::Properties manifest = {}, bool autoStart = true);
+ long installBundle(std::string name, std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> actFactory, celix::Properties manifest = {}, bool autoStart = true);
//long installBundle(const std::string &path);
@@ -78,6 +79,8 @@ namespace celix {
std::vector<long> listBundles(bool includeFrameworkBundle = false) const;
celix::ServiceRegistry& registry(const std::string &lang);
+
+ bool waitForShutdown() const;
private:
class Impl;
std::unique_ptr<Impl> pimpl;
diff --git a/libs/framework_cxx/include/celix/IBundle.h b/libs/framework_cxx/include/celix/IBundle.h
index a8d0de1..9f2e8a8 100644
--- a/libs/framework_cxx/include/celix/IBundle.h
+++ b/libs/framework_cxx/include/celix/IBundle.h
@@ -27,7 +27,6 @@ namespace celix {
enum class BundleState {
INSTALLED,
- RESOLVED,
ACTIVE,
};
diff --git a/libs/framework_cxx/include/celix/IBundleActivator.h b/libs/framework_cxx/include/celix/IBundleActivator.h
index 1196e99..a3ccd92 100644
--- a/libs/framework_cxx/include/celix/IBundleActivator.h
+++ b/libs/framework_cxx/include/celix/IBundleActivator.h
@@ -25,13 +25,17 @@
#include "IBundleContext.h"
namespace celix {
+ /**
+ * The BundleActivator.
+ *
+ * This is a marker interface and contains no virtual methods.
+ *
+ * The Celix Framework will expect a constructor with a std::shared_ptr<celix::IBundleContext> argument on the
+ * contrete bundle activator. RAII will be used to start (on ctor) and stop (on dtor) a bundle.
+ */
class IBundleActivator {
public:
virtual ~IBundleActivator() = default;
-
- virtual bool resolve(std::shared_ptr<celix::IBundleContext> /*ctx*/) noexcept { return true; };
- virtual bool start(std::shared_ptr<celix::IBundleContext> ctx) noexcept = 0;
- virtual bool stop(std::shared_ptr<celix::IBundleContext> /*ctx*/) noexcept { return true; }
};
}
diff --git a/libs/framework_cxx/include/celix/IBundleContext.h b/libs/framework_cxx/include/celix/IBundleContext.h
index e77623a..553372a 100644
--- a/libs/framework_cxx/include/celix/IBundleContext.h
+++ b/libs/framework_cxx/include/celix/IBundleContext.h
@@ -51,6 +51,10 @@ namespace celix {
virtual bool useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const noexcept = 0;
virtual int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle = true) const noexcept = 0;
+ virtual bool stopBundle(long bndId) noexcept = 0;
+ virtual bool startBundle(long bndId) noexcept = 0;
+ //TODO install / uninstall bundles
+
template<typename I>
bool useService(std::function<void(I &svc)> use, const std::string &filter = "") const noexcept {
return registry().useService<I>(std::move(use), filter, bundle());
@@ -128,7 +132,8 @@ namespace celix {
//TODO track trackers
//TODO track c trackers
- private:
+
+
virtual celix::ServiceRegistry& registry() const noexcept = 0;
virtual celix::ServiceRegistry& cRegistry() const noexcept = 0;
};
diff --git a/libs/framework_cxx/src/BundleImpl.h b/libs/framework_cxx/src/BundleImpl.h
index ab49636..dd2c53f 100644
--- a/libs/framework_cxx/src/BundleImpl.h
+++ b/libs/framework_cxx/src/BundleImpl.h
@@ -47,20 +47,29 @@ namespace impl {
int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle = true) const noexcept override {
return bnd->framework().useBundles(std::move(use), includeFrameworkBundle);
}
+
+ bool stopBundle(long bndId) noexcept override {
+ return bnd->framework().stopBundle(bndId);
+ }
+
+ bool startBundle(long bndId) noexcept override {
+ return bnd->framework().startBundle(bndId);
+ }
+
private:
celix::ServiceRegistry& registry() const noexcept override { return *reg; }
celix::ServiceRegistry& cRegistry() const noexcept override { return *cReg; }
const std::shared_ptr<celix::IBundle> bnd;
- const std::shared_ptr<celix::ServiceRegistry> reg;
- const std::shared_ptr<celix::ServiceRegistry> cReg;
+ celix::ServiceRegistry * const reg; //TODO make weak_ptr
+ celix::ServiceRegistry * const cReg; //TODO make weak_ptr
};
class Bundle : public celix::IBundle {
public:
- Bundle(long _bndId, celix::Framework *_fw, celix::Properties _manifest, std::shared_ptr<celix::IBundleActivator> _activator) :
- bndId{_bndId}, fw{_fw}, bndManifest{std::move(_manifest)}, activator{std::move(_activator)} {
+ Bundle(long _bndId, celix::Framework *_fw, celix::Properties _manifest) :
+ bndId{_bndId}, fw{_fw}, bndManifest{std::move(_manifest)} {
bndState.store(BundleState::INSTALLED, std::memory_order_release);
}
@@ -100,7 +109,6 @@ namespace impl {
const long bndId;
celix::Framework * const fw;
const celix::Properties bndManifest;
- const std::shared_ptr<celix::IBundleActivator> activator;
std::weak_ptr<celix::IBundleContext> context;
std::atomic<BundleState> bndState;
@@ -109,10 +117,10 @@ namespace impl {
class BundleController {
public:
BundleController(
- std::shared_ptr<celix::IBundleActivator> _act,
+ std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> _actFactory,
std::shared_ptr<celix::impl::Bundle> _bnd,
std::shared_ptr<celix::impl::BundleContext> _ctx) :
- act{std::move(_act)}, bnd{std::move(_bnd)}, ctx{std::move(_ctx)} {}
+ actFactory{std::move(_actFactory)}, bnd{std::move(_bnd)}, ctx{std::move(_ctx)} {}
//specific part
bool transitionTo(BundleState desired) {
@@ -122,39 +130,14 @@ namespace impl {
if (state == desired) {
//nop
success = true;
- } else if (state == BundleState::INSTALLED && desired == BundleState::RESOLVED) {
- //TODO create cache dir for bundle
- bool resolved = act->resolve(ctx);
- if (resolved) {
- bnd->setState(BundleState::RESOLVED);
- success = true;
- } else {
- LOG(WARNING) << "Transition to resolved state for bundle " << bnd->symbolicName() << " (" << bnd->id() << ") failed." << std::endl;
- }
-
- } else if (state == BundleState::RESOLVED && desired == BundleState::ACTIVE) {
- bool started = act->start(ctx);
- if (started) {
- bnd->setState(BundleState::ACTIVE);
- success = true;
- } else {
- LOG(WARNING) << "Transition to active state for bundle " << bnd->symbolicName() << " (" << bnd->id() << ") failed." << std::endl;
- }
- } else if (state == BundleState::ACTIVE && desired == BundleState::RESOLVED ) {
- bool stopped = act->stop(ctx);
- if (stopped) {
- bnd->setState(BundleState::RESOLVED);
- success = true;
-
- //TODO use custom deleter to check this (use_count call is a race condition)
- bool unique = ctx.use_count() == 1;
- if (!unique) {
- LOG(WARNING) << "Bundle Context is not unique. ";
- LOG(WARNING) << "Check if there are still some dangling references to the context of the stopped bundle." << std::endl;
- }
- } else {
- LOG(WARNING) << "Transition to resolved state for bundle " << bnd->symbolicName() << " (" << bnd->id() << ") failed." << std::endl;
- }
+ } else if (state == BundleState::INSTALLED && desired == BundleState::ACTIVE) {
+ act = std::unique_ptr<celix::IBundleActivator>{actFactory(ctx)};
+ bnd->setState(BundleState::ACTIVE);
+ success = true;
+ } else if (state == BundleState::ACTIVE && desired == BundleState::INSTALLED ) {
+ act = nullptr;
+ bnd->setState(BundleState::INSTALLED);
+ success = true;
} else {
//LOG(ERROR) << "Unexpected desired state " << desired << " from state " << bndState << std::endl;
LOG(ERROR) << "Unexpected desired/form state combination " << std::endl;
@@ -162,15 +145,15 @@ namespace impl {
return success;
}
- std::shared_ptr<celix::IBundleActivator> activator() const { return act; }
std::shared_ptr<celix::impl::Bundle> bundle() const { return bnd; }
std::shared_ptr<celix::impl::BundleContext> context() const { return ctx; }
private:
- const std::shared_ptr<celix::IBundleActivator> act;
+ const std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> actFactory;
const std::shared_ptr<celix::impl::Bundle> bnd;
const std::shared_ptr<celix::impl::BundleContext> ctx;
mutable std::mutex mutex{};
+ std::unique_ptr<celix::IBundleActivator> act{nullptr};
};
}
};
diff --git a/libs/framework_cxx/src/Framework.cc b/libs/framework_cxx/src/Framework.cc
index a224720..cb5b6ed 100644
--- a/libs/framework_cxx/src/Framework.cc
+++ b/libs/framework_cxx/src/Framework.cc
@@ -26,6 +26,7 @@
#include <iostream>
#include <set>
#include <vector>
+#include <future>
#include <glog/logging.h>
@@ -36,7 +37,7 @@
struct StaticBundleEntry {
const std::string symbolicName;
const celix::Properties manifest;
- const std::function<celix::IBundleActivator*()> activatorFactory;
+ const std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> activatorFactory;
};
static struct {
@@ -51,10 +52,11 @@ static void unregisterFramework(celix::Framework *fw);
class celix::Framework::Impl : public IBundle {
public:
- Impl(celix::Framework *_fw) : fw{_fw}, bndManifest{createManifest()}, cwd{createCwd()} {}
+ Impl(celix::Framework *_fw, celix::Properties _config) : fw{_fw}, config{std::move(_config)}, bndManifest{createManifest()}, cwd{createCwd()} {}
~Impl() {
stopFramework();
+ waitForShutdown();
}
std::vector<long> listBundles(bool includeFrameworkBundle) const {
@@ -70,9 +72,15 @@ public:
return result;
}
- long installBundle(std::string symbolicName, std::shared_ptr<celix::IBundleActivator> activator, celix::Properties manifest, bool autoStart) {
+ long installBundle(std::string symbolicName, std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> actFactory, celix::Properties manifest, bool autoStart) {
+ //TODO if activator is nullptr -> use empty activator
//TODO on separate thread ?? specific bundle resolve thread ??
long bndId = -1L;
+ if (symbolicName.empty()) {
+ LOG(WARNING) << "Cannot install bundle with a empty symbolic name" << std::endl;
+ return bndId;
+ }
+
std::shared_ptr<celix::impl::BundleController> bndController{nullptr};
{
manifest[celix::MANIFEST_BUNDLE_SYMBOLIC_NAME] = symbolicName;
@@ -88,9 +96,9 @@ public:
std::lock_guard<std::mutex> lck{bundles.mutex};
bndId = bundles.nextBundleId++;
- auto bnd = std::shared_ptr<celix::impl::Bundle>{new celix::impl::Bundle{bndId, this->fw, std::move(manifest), std::move(activator)}};
+ auto bnd = std::shared_ptr<celix::impl::Bundle>{new celix::impl::Bundle{bndId, this->fw, std::move(manifest)}};
auto ctx = std::shared_ptr<celix::impl::BundleContext>{new celix::impl::BundleContext{bnd}};
- bndController = std::shared_ptr<celix::impl::BundleController>{new celix::impl::BundleController{activator, bnd, ctx}};
+ bndController = std::shared_ptr<celix::impl::BundleController>{new celix::impl::BundleController{std::move(actFactory), bnd, ctx}};
bundles.entries.emplace(std::piecewise_construct,
std::forward_as_tuple(bndId),
std::forward_as_tuple(bndController));
@@ -99,26 +107,32 @@ public:
}
if (bndController) {
- bool successful = bndController->transitionTo(BundleState::RESOLVED);
- if (successful && autoStart) {
- successful = bndController->transitionTo(BundleState::ACTIVE);
+ if (autoStart) {
+ bool successful = bndController->transitionTo(BundleState::ACTIVE);
if (!successful) {
LOG(WARNING) << "Cannot start bundle " << bndController->bundle()->symbolicName() << std::endl;
}
- } else {
- LOG(WARNING) << "Cannot resolve bundle " << bndController->bundle()->symbolicName() << std::endl;
}
- //TODO decrease bnd entry usage
}
+
return bndId;
}
bool startBundle(long bndId) {
- return transitionBundleTo(bndId, BundleState::ACTIVE);
+ if (bndId == this->fwBndId) {
+ //TODO
+ return false;
+ } else {
+ return transitionBundleTo(bndId, BundleState::ACTIVE);
+ }
}
bool stopBundle(long bndId) {
- return transitionBundleTo(bndId, BundleState::RESOLVED);
+ if (bndId == this->fwBndId) {
+ return stopFramework();
+ } else {
+ return transitionBundleTo(bndId, BundleState::INSTALLED);
+ }
}
bool uninstallBundle(long bndId) {
@@ -134,11 +148,10 @@ public:
}
}
if (removed) {
- bool resolved = removed->transitionTo(BundleState::RESOLVED);
- if (resolved) {
- uninstalled = true;
- bool unique = removed.unique();
- assert(unique); //TODO cond / wait ?
+ bool stopped = removed->transitionTo(BundleState::INSTALLED);
+ if (stopped) {
+ //TODO check and wait till bundle is not used anymore. is this needed (shared_ptr) or just let access
+ //to filesystem fail ...
} else {
//add bundle again -> not uninstalled
std::lock_guard<std::mutex> lck{bundles.mutex};
@@ -245,15 +258,29 @@ public:
celix::Framework& framework() const noexcept override { return *fw; }
bool stopFramework() {
- std::vector<long> bundles = listBundles(false);
- while (!bundles.empty()) {
- for (auto it = bundles.rbegin(); it != bundles.rend(); ++it) {
- stopBundle(*it);
- uninstallBundle(*it);
- }
- bundles = listBundles(false);
+ std::lock_guard<std::mutex> lck{shutdown.mutex};
+ if (!shutdown.shutdownStarted) {
+ shutdown.future = std::async(std::launch::async, [this]{
+ std::vector<long> bundles = listBundles(false);
+ while (!bundles.empty()) {
+ for (auto it = bundles.rbegin(); it != bundles.rend(); ++it) {
+ stopBundle(*it);
+ uninstallBundle(*it);
+ }
+ bundles = listBundles(false);
+ }
+ });
+ shutdown.shutdownStarted = true;
+ shutdown.cv.notify_all();
}
+ return true;
+ }
+ bool waitForShutdown() const {
+ std::unique_lock<std::mutex> lck{shutdown.mutex};
+ shutdown.cv.wait(lck, [this]{return this->shutdown.shutdownStarted;});
+ shutdown.future.wait();
+ lck.unlock();
return true;
}
private:
@@ -275,10 +302,22 @@ private:
}
}
+ const long fwBndId = 1L;
celix::Framework * const fw;
+ const celix::Properties config;
const celix::Properties bndManifest;
const std::string cwd;
+
+ struct {
+ mutable std::mutex mutex{};
+ mutable std::condition_variable cv{};
+ std::future<void> future{};
+ bool shutdownStarted = false;
+ } shutdown{};
+
+
+
struct {
std::unordered_map<long, std::shared_ptr<celix::impl::BundleController>> entries{};
long nextBundleId = 2;
@@ -295,9 +334,9 @@ private:
* Framework
**********************************************************************************************************************/
-celix::Framework::Framework() {
- pimpl = std::unique_ptr<Impl>{new Impl{this}};
- registerFramework(this); //TODO improve ugly.. maybe register impl.. but that is private -> so make register static member functions of impl...
+celix::Framework::Framework(celix::Properties config) {
+ pimpl = std::unique_ptr<Impl>{new Impl{this, std::move(config)}};
+ registerFramework(this);
}
celix::Framework::~Framework() {
unregisterFramework(this);
@@ -305,10 +344,12 @@ celix::Framework::~Framework() {
celix::Framework::Framework(Framework &&rhs) = default;
celix::Framework& celix::Framework::operator=(Framework&& rhs) = default;
-long celix::Framework::installBundle(std::string name, std::shared_ptr<celix::IBundleActivator> activator, celix::Properties manifest, bool autoStart) {
- return pimpl->installBundle(std::move(name), std::move(activator), std::move(manifest), autoStart);
+
+long celix::Framework::installBundle(std::string name, std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> actFactory, celix::Properties manifest, bool autoStart) {
+ return pimpl->installBundle(std::move(name), actFactory, std::move(manifest), autoStart);
}
+
std::vector<long> celix::Framework::listBundles(bool includeFrameworkBundle) const { return pimpl->listBundles(includeFrameworkBundle); }
bool celix::Framework::useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const {
@@ -324,23 +365,28 @@ bool celix::Framework::stopBundle(long bndId) { return pimpl->stopBundle(bndId);
bool celix::Framework::uninstallBundle(long bndId) { return pimpl->uninstallBundle(bndId); }
celix::ServiceRegistry& celix::Framework::registry(const std::string &lang) { return pimpl->registry(lang); }
+bool celix::Framework::waitForShutdown() const { return pimpl->waitForShutdown(); }
+
/***********************************************************************************************************************
* Celix 'global' functions
**********************************************************************************************************************/
-void celix::registerStaticBundle(std::string symbolicName, const celix::StaticBundleOptions &opts) {
+void celix::registerStaticBundle(
+ std::string symbolicName,
+ std::function<celix::IBundleActivator*(std::shared_ptr<celix::IBundleContext>)> bundleActivatorFactory,
+ celix::Properties manifest) {
std::lock_guard<std::mutex> lck{staticRegistry.mutex};
- staticRegistry.bundles.emplace_back(StaticBundleEntry{.symbolicName = std::move(symbolicName), .manifest = opts.manifest, .activatorFactory = opts.bundleActivatorFactory});
for (auto fw : staticRegistry.frameworks) {
- fw->installBundle(symbolicName, std::shared_ptr<celix::IBundleActivator>{opts.bundleActivatorFactory()}, opts.manifest);
+ fw->installBundle(symbolicName, bundleActivatorFactory, manifest);
}
+ staticRegistry.bundles.emplace_back(StaticBundleEntry{.symbolicName = std::move(symbolicName), .manifest = std::move(manifest), .activatorFactory = std::move(bundleActivatorFactory)});
}
static void registerFramework(celix::Framework *fw) {
std::lock_guard<std::mutex> lck{staticRegistry.mutex};
staticRegistry.frameworks.insert(fw);
for (auto &entry : staticRegistry.bundles) {
- fw->installBundle(entry.symbolicName, std::shared_ptr<celix::IBundleActivator>{entry.activatorFactory()}, entry.manifest);
+ fw->installBundle(entry.symbolicName, entry.activatorFactory, entry.manifest);
}
}
diff --git a/libs/registry/gtest/src/RegistryConcurrency_tests.cc b/libs/registry/gtest/src/RegistryConcurrency_tests.cc
index c77c198..f7e86d3 100644
--- a/libs/registry/gtest/src/RegistryConcurrency_tests.cc
+++ b/libs/registry/gtest/src/RegistryConcurrency_tests.cc
@@ -29,8 +29,7 @@ public:
celix::ServiceRegistry& registry() { return reg; }
private:
- celix::ServiceRegistry reg{"C/C++"};
-};
+ celix::ServiceRegistry reg{"C++"};
class ICalc {
public:
diff --git a/libs/registry/gtest/src/Registry_tests.cc b/libs/registry/gtest/src/Registry_tests.cc
index 8c00b11..9bbb899 100644
--- a/libs/registry/gtest/src/Registry_tests.cc
+++ b/libs/registry/gtest/src/Registry_tests.cc
@@ -30,7 +30,7 @@ public:
celix::ServiceRegistry& registry() { return reg; }
private:
- celix::ServiceRegistry reg{"C/C++"};
+ celix::ServiceRegistry reg{"C++"};
};
class MarkerInterface1 {
@@ -264,6 +264,27 @@ TEST_F(RegistryTest, StdFunctionTest) {
});
}
+TEST_F(RegistryTest, ListServicesTest) {
+ std::vector<std::string> serviceNames = registry().listAllRegisteredServiceNames();
+ EXPECT_EQ(0, serviceNames.size());
+
+ std::function<void()> nop = []{/*nop*/};
+ class MarkerInterface1 {};
+ MarkerInterface1 intf1;
+
+ {
+ auto reg1 = registry().registerFunctionService("nop", nop);
+ serviceNames = registry().listAllRegisteredServiceNames();
+ EXPECT_EQ(1, serviceNames.size());
+
+ auto reg2 = registry().registerService(intf1);
+ serviceNames = registry().listAllRegisteredServiceNames();
+ EXPECT_EQ(2, serviceNames.size());
+ }
+ serviceNames = registry().listAllRegisteredServiceNames();
+ EXPECT_EQ(0, serviceNames.size());
+}
+
//TODO function use with props and bnd
//TODO use with filter
//TODO use with sync test (see BundleContext tests)
\ No newline at end of file
diff --git a/libs/registry/gtest/src/ServiceTracking_tests.cc b/libs/registry/gtest/src/ServiceTracking_tests.cc
index 7a88f1b..035e8a9 100644
--- a/libs/registry/gtest/src/ServiceTracking_tests.cc
+++ b/libs/registry/gtest/src/ServiceTracking_tests.cc
@@ -28,7 +28,7 @@ class ServiceTrackingTest : public ::testing::Test {
public:
celix::ServiceRegistry& registry() { return reg; }
private:
- celix::ServiceRegistry reg{"C/C++"};
+ celix::ServiceRegistry reg{"C++"};
};
class MarkerInterface1 {
@@ -62,9 +62,9 @@ TEST_F(ServiceTrackingTest, CreateTrackersTest) {
}
TEST_F(ServiceTrackingTest, ServicesCountTrackersTest) {
- MarkerInterface1 intf1;
- MarkerInterface2 intf2;
- MarkerInterface3 intf3;
+ MarkerInterface1 intf1{};
+ MarkerInterface2 intf2{};
+ MarkerInterface3 intf3{};
auto trk1 = registry().trackServices<MarkerInterface1>();
ASSERT_EQ(0, trk1.trackCount());
@@ -103,17 +103,17 @@ TEST_F(ServiceTrackingTest, ServicesCountTrackersTest) {
}
TEST_F(ServiceTrackingTest, SetServiceTest) {
- MarkerInterface1 intf1;
- MarkerInterface2 intf2;
- MarkerInterface3 intf3;
+ MarkerInterface1 intf1{};
+ MarkerInterface2 intf2{};
+ MarkerInterface3 intf3{};
MarkerInterface1 *ptrToSvc = nullptr;
//const celix::Properties *ptrToProps = nullptr;
//const celix::IBundle *ptrToBnd = nullptr;
celix::ServiceTrackerOptions<MarkerInterface1> opts{};
- opts.set = [&ptrToSvc](MarkerInterface1* svc) {
- ptrToSvc = svc;
+ opts.set = [&ptrToSvc](std::shared_ptr<MarkerInterface1> svc) {
+ ptrToSvc = svc.get();
};
auto reg1 = registry().registerService(intf1);
@@ -162,17 +162,17 @@ TEST_F(ServiceTrackingTest, SetServiceWithPropsAndOwnderTest) {
}
TEST_F(ServiceTrackingTest, AddRemoveTest) {
- MarkerInterface1 intf1;
- MarkerInterface2 intf2;
- MarkerInterface3 intf3;
+ MarkerInterface1 intf1{};
+ MarkerInterface2 intf2{};
+ MarkerInterface3 intf3{};
- std::vector<MarkerInterface1*> services{};
+ std::vector<std::shared_ptr<MarkerInterface1>> services{};
celix::ServiceTrackerOptions<MarkerInterface1> opts{};
- opts.add = [&services](MarkerInterface1* svc) {
+ opts.add = [&services](std::shared_ptr<MarkerInterface1> svc) {
services.push_back(svc);
};
- opts.remove = [&services](MarkerInterface1* svc) {
+ opts.remove = [&services](std::shared_ptr<MarkerInterface1> svc) {
services.erase(std::remove(services.begin(), services.end(), svc), services.end());
};
@@ -182,24 +182,24 @@ TEST_F(ServiceTrackingTest, AddRemoveTest) {
auto trk1 = registry().trackServices(opts);
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf1, services[0]); //should be intf1
+ EXPECT_EQ(&intf1, services[0].get()); //should be intf1
reg1.unregister();
EXPECT_EQ(0, services.size());
reg1 = registry().registerService(intf1);
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf1, services[0]); //should be intf1 again
+ EXPECT_EQ(&intf1, services[0].get()); //should be intf1 again
MarkerInterface1 intf4{};
auto reg4 = registry().registerService(intf4);
ASSERT_EQ(2, services.size());
- EXPECT_EQ(&intf1, services[0]);
- EXPECT_EQ(&intf4, services[1]);
+ EXPECT_EQ(&intf1, services[0].get());
+ EXPECT_EQ(&intf4, services[1].get());
reg1.unregister();
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf4, services[0]); //intf1 gone -> index 0: intf4
+ EXPECT_EQ(&intf4, services[0].get()); //intf1 gone -> index 0: intf4
trk1.stop();
EXPECT_EQ(0, services.size());
@@ -207,7 +207,7 @@ TEST_F(ServiceTrackingTest, AddRemoveTest) {
{
auto trk2 = registry().trackServices(opts);
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf4, services[0]);
+ EXPECT_EQ(&intf4, services[0].get());
//out of scope -> tracker stopped
}
EXPECT_EQ(0, services.size()); //stop tracking -> services removed
@@ -218,14 +218,14 @@ TEST_F(ServiceTrackingTest, AddRemoveServicesWithPropsAndOwnderTest) {
}
TEST_F(ServiceTrackingTest, UpdateTest) {
- MarkerInterface1 intf1;
- MarkerInterface2 intf2;
- MarkerInterface3 intf3;
+ MarkerInterface1 intf1{};
+ MarkerInterface2 intf2{};
+ MarkerInterface3 intf3{};
- std::vector<MarkerInterface1*> services{};
+ std::vector<std::shared_ptr<MarkerInterface1>> services{};
celix::ServiceTrackerOptions<MarkerInterface1> opts{};
- opts.update = [&services](std::vector<MarkerInterface1*> rankedServices) {
+ opts.update = [&services](std::vector<std::shared_ptr<MarkerInterface1>> rankedServices) {
services = rankedServices;
};
@@ -236,25 +236,25 @@ TEST_F(ServiceTrackingTest, UpdateTest) {
auto trk1 = registry().trackServices(opts);
EXPECT_EQ(1, trk1.trackCount());
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf1, services[0]); //should be intf1
+ EXPECT_EQ(&intf1, services[0].get()); //should be intf1
reg1.unregister();
EXPECT_EQ(0, services.size());
reg1 = registry().registerService(intf1);
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf1, services[0]); //should be intf1 again
+ EXPECT_EQ(&intf1, services[0].get()); //should be intf1 again
MarkerInterface1 intf4{};
celix::Properties props{std::make_pair(celix::SERVICE_RANKING, "100")};
auto reg4 = registry().registerService(intf4, std::move(props));
ASSERT_EQ(2, services.size());
- EXPECT_EQ(&intf4, services[0]); //note 4 higher ranking
- EXPECT_EQ(&intf1, services[1]);
+ EXPECT_EQ(&intf4, services[0].get()); //note 4 higher ranking
+ EXPECT_EQ(&intf1, services[1].get());
reg1.unregister();
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf4, services[0]); //intf1 gone -> index 0: intf4
+ EXPECT_EQ(&intf4, services[0].get()); //intf1 gone -> index 0: intf4
trk1.stop();
EXPECT_EQ(0, services.size());
@@ -262,7 +262,7 @@ TEST_F(ServiceTrackingTest, UpdateTest) {
{
auto trk2 = registry().trackServices(opts);
ASSERT_EQ(1, services.size());
- EXPECT_EQ(&intf4, services[0]);
+ EXPECT_EQ(&intf4, services[0].get());
//out of scope -> tracker stopped
}
EXPECT_EQ(0, services.size()); //stop tracking -> services removed
diff --git a/libs/registry/include/celix/Constants.h b/libs/registry/include/celix/Constants.h
index f1fcad3..517a372 100644
--- a/libs/registry/include/celix/Constants.h
+++ b/libs/registry/include/celix/Constants.h
@@ -24,9 +24,10 @@
namespace celix {
//NOTE manually aligned with celix_constants.h
- static constexpr const char *const SERVICE_NAME = "service.name";
- static constexpr const char *const SERVICE_ID = "service.id";
- static constexpr const char *const SERVICE_RANKING = "service.ranking";
+ static constexpr const char *const SERVICE_NAME = "SERVICE_NAME";
+ static constexpr const char *const SERVICE_ID = "SERVICE_ID";
+ static constexpr const char *const SERVICE_RANKING = "SERVICE_RANKING";
+ static constexpr const char *const SERVICE_BUNDLE = "SERVICE_BUNDLE";
static constexpr const char *const FRAMEWORK_UUID = "framework.uuid";
diff --git a/libs/registry/include/celix/Properties.h b/libs/registry/include/celix/Properties.h
index 65fd4ef..1002bfc 100644
--- a/libs/registry/include/celix/Properties.h
+++ b/libs/registry/include/celix/Properties.h
@@ -55,6 +55,12 @@ namespace celix {
std::string val = getProperty(props, key, std::to_string(defaultValue));
return std::stoul(val, nullptr, 10);
}
+
+ /*TODO
+ celix::Properties loadProperties(const std::string &path);
+ celix::Properties loadProperties(std::istream stream);
+ bool storeProperties(const celix::Properties &props, const std::string &path);
+ */
}
#endif //CXX_CELIX_PROPERTIES_H
diff --git a/libs/registry/include/celix/ServiceRegistry.h b/libs/registry/include/celix/ServiceRegistry.h
index 7bbe16c..710b218 100644
--- a/libs/registry/include/celix/ServiceRegistry.h
+++ b/libs/registry/include/celix/ServiceRegistry.h
@@ -70,25 +70,23 @@ namespace celix {
std::string filter{};
- /*TODO maybe refactor all I* to std::shared_ptr and use a custom deleter to sync whether a bundle is done using
- all the functions -> i.e. safe delete and possible lock free? Not sure, because a std::shared_ptr instance
- access it not thread safe?. Investigate */
+ std::function<void(std::shared_ptr<I> svc)> set{};
+ std::function<void(std::shared_ptr<I> svc, const celix::Properties &props)> setWithProperties{};
+ std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> setWithOwner{};
- std::function<void(I *svc)> set = {};
- std::function<void(I *svc, const celix::Properties &props)> setWithProperties = {};
- std::function<void(I *svc, const celix::Properties &props, const celix::IResourceBundle &owner)> setWithOwner = {};
+ std::function<void(std::shared_ptr<I> svc)> add{};
+ std::function<void(std::shared_ptr<I> svc, const celix::Properties &props)> addWithProperties{};
+ std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> addWithOwner{};
- std::function<void(I *svc)> add = {};
- std::function<void(I *svc, const celix::Properties &props)> addWithProperties = {};
- std::function<void(I *svc, const celix::Properties &props, const celix::IResourceBundle &owner)> addWithOwner = {};
+ std::function<void(std::shared_ptr<I> svc)> remove{};
+ std::function<void(std::shared_ptr<I> svc, const celix::Properties &props)> removeWithProperties{};
+ std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> removeWithOwner{};
- std::function<void(I *svc)> remove = {};
- std::function<void(I *svc, const celix::Properties &props)> removeWithProperties = {};
- std::function<void(I *svc, const celix::Properties &props, const celix::IResourceBundle &owner)> removeWithOwner = {};
+ std::function<void(std::vector<std::shared_ptr<I>> rankedServices)> update{};
+ std::function<void(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*>> rankedServices)> updateWithProperties{};
+ std::function<void(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> updateWithOwner{};
- std::function<void(std::vector<I*> rankedServices)> update = {};
- std::function<void(std::vector<std::tuple<I*, const celix::Properties*>> rankedServices)> updateWithProperties = {};
- std::function<void(std::vector<std::tuple<I*, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> updateWithOwner = {};
+ //TODO lock free update calls atomics, rcu, hazard pointers ??
};
//RAII service tracker: out of scope -> stop tracker
@@ -107,7 +105,7 @@ namespace celix {
const std::string& filter() const;
bool valid() const;
- //TODO useService(s) calls
+ //TODO use(Function)Service(s) calls
void stop();
private:
@@ -140,7 +138,8 @@ namespace celix {
celix::ServiceRegistration registerService(std::shared_ptr<I> svc, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {}) {
//TOOD refactor to using a service factory to store the shared or unique_ptr
auto svcName = celix::serviceName<I>();
- return registerService(svcName, static_cast<std::shared_ptr<void>>(svc), std::move(props), std::move(owner));
+ auto voidSvc = std::static_pointer_cast<I>(svc);
+ return registerService(svcName, voidSvc, std::move(props), std::move(owner));
}
template<typename F>
@@ -159,7 +158,7 @@ namespace celix {
template<typename I>
//NOTE C++17 typename std::enable_if<std::is_callable<I>::value, long>::type
long findFunctionService(const std::string &functionName, const std::string &filter = "") const {
- auto services = functionServiceName<I>(functionName, filter);
+ auto services = findFunctionService<I>(functionName, filter);
return services.size() > 0 ? services[0] : -1L;
}
@@ -167,14 +166,14 @@ namespace celix {
//NOTE C++17 typename std::enable_if<!std::is_callable<I>::value, std::vector<long>>::type
std::vector<long> findServices(const std::string &filter = "") const {
auto svcName = celix::serviceName<I>();
- return findServices(svcName, filter);
+ return findAnyServices(svcName, filter);
}
template<typename F>
//NOTE C++17 typename std::enable_if<std::is_callable<I>::value, std::vector<long>>::type
std::vector<long> findFunctionServices(const std::string &functionName, const std::string &filter = "") const {
auto svcName = celix::functionServiceName<F>(functionName);
- return findServices(svcName, filter);
+ return findAnyServices(svcName, filter);
}
template<typename I>
@@ -264,6 +263,30 @@ namespace celix {
}
+ //GENERIC / ANY calls. note these work on void
+
+ int useAnyServices(
+ const std::string &svcName,
+ std::function<void(std::shared_ptr<void> svc, const celix::Properties &props,const celix::IResourceBundle &bnd)> use,
+ const std::string &filter = {},
+ std::shared_ptr<const celix::IResourceBundle> requester = {}) const;
+
+ bool useAnyService(
+ const std::string &svcName,
+ std::function<void(std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> use,
+ const std::string &filter = {},
+ std::shared_ptr<const celix::IResourceBundle> requester = {}) const;
+
+ std::vector<long> findAnyServices(const std::string &name, const std::string &filter = {}) const;
+
+
+ celix::ServiceTracker trackAnyServices(
+ std::string svcName,
+ ServiceTrackerOptions<void> options,
+ std::shared_ptr<const celix::IResourceBundle> requester = {});
+
+ //some aditional registry info
+ std::vector<std::string> listAllRegisteredServiceNames() const;
long nrOfRegisteredServices() const;
long nrOfServiceTrackers() const;
private:
@@ -283,7 +306,6 @@ namespace celix {
std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
const std::string &filter,
std::shared_ptr<const celix::IResourceBundle> requester) const;
- int useServices(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &filter, std::shared_ptr<const celix::IResourceBundle> requester) const;
template<typename I>
bool useService(
@@ -293,16 +315,14 @@ namespace celix {
std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
const std::string &filter,
std::shared_ptr<const celix::IResourceBundle> requester) const;
- bool useService(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &filter, std::shared_ptr<const celix::IResourceBundle> requester) const;
-
- //find Services
- std::vector<long> findServices(const std::string &name, const std::string &filter) const;
-
//track services
template<typename I>
- celix::ServiceTracker trackServices(std::string svcName, celix::ServiceTrackerOptions<I> options, std::shared_ptr<const celix::IResourceBundle> requester);
- celix::ServiceTracker trackServices(std::string svcName, ServiceTrackerOptions<void> options, std::shared_ptr<const celix::IResourceBundle> requester);
+ celix::ServiceTracker trackServices(
+ std::string svcName,
+ celix::ServiceTrackerOptions<I> options,
+ std::shared_ptr<const celix::IResourceBundle> requester);
+
};
}
@@ -360,8 +380,9 @@ inline int celix::ServiceRegistry::useServices(
std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
const std::string &filter,
std::shared_ptr<const celix::IResourceBundle> requester) const {
- std::function<void(void*,const celix::Properties&, const celix::IResourceBundle&)> voidUse = [&](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
- I* typedSvc = static_cast<I*>(svc);
+
+ std::function<void(std::shared_ptr<void>, const celix::Properties&, const celix::IResourceBundle&)> voidUse = [&](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
+ std::shared_ptr<I> typedSvc = std::static_pointer_cast<I>(svc);
if (use) {
use(*typedSvc);
}
@@ -372,7 +393,7 @@ inline int celix::ServiceRegistry::useServices(
useWithOwner(*typedSvc, props, bnd);
}
};
- return useServices(svcName, voidUse, filter, requester);
+ return useAnyServices(svcName, std::move(voidUse), filter, std::move(requester));
}
template<typename I>
@@ -383,8 +404,9 @@ inline bool celix::ServiceRegistry::useService(
std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
const std::string &filter,
std::shared_ptr<const celix::IResourceBundle> requester) const {
- std::function<void(void*,const celix::Properties&, const celix::IResourceBundle&)> voidUse = [&](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) -> void {
- I* typedSvc = static_cast<I*>(svc);
+
+ std::function<void(std::shared_ptr<void>,const celix::Properties&, const celix::IResourceBundle&)> voidUse = [&](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) -> void {
+ std::shared_ptr<I> typedSvc = std::static_pointer_cast<I>(svc);
if (use) {
use(*typedSvc);
}
@@ -395,115 +417,120 @@ inline bool celix::ServiceRegistry::useService(
useWithOwner(*typedSvc, props, bnd);
}
};
- return useService(svcName, voidUse, filter, requester);
+ return useAnyService(svcName, std::move(voidUse), filter, std::move(requester));
}
template<typename I>
-inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string svcName, const celix::ServiceTrackerOptions<I> options, std::shared_ptr<const celix::IResourceBundle> requester) {
+inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string svcName,
+ ServiceTrackerOptions<I> options,
+ std::shared_ptr<const celix::IResourceBundle> requester) {
ServiceTrackerOptions<void> opts{};
opts.filter = std::move(options.filter);
if (options.set != nullptr) {
auto set = std::move(options.set);
- opts.set = [set](void *svc){
- I *typedSvc = static_cast<I*>(svc);
+ opts.set = [set](std::shared_ptr<void> svc){
+ auto typedSvc = std::static_pointer_cast<I>(svc);
set(typedSvc);
};
}
if (options.setWithProperties != nullptr) {
auto set = std::move(options.setWithProperties);
- opts.setWithProperties = [set](void *svc, const celix::Properties &props){
- I *typedSvc = static_cast<I*>(svc);
+ opts.setWithProperties = [set](std::shared_ptr<void> svc, const celix::Properties &props){
+ auto typedSvc = std::static_pointer_cast<I>(svc);
set(typedSvc, props);
};
}
if (options.setWithOwner != nullptr) {
auto set = std::move(options.setWithOwner);
- opts.setWithOwner = [set](void *svc, const celix::Properties &props, const celix::IResourceBundle &owner){
- I *typedSvc = static_cast<I*>(svc);
+ opts.setWithOwner = [set](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &owner){
+ auto typedSvc = std::static_pointer_cast<I>(svc);
set(typedSvc, props, owner);
};
}
if (options.add != nullptr) {
auto add = std::move(options.add);
- opts.add = [add](void *svc) {
- I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ opts.add = [add](std::shared_ptr<void> svc) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
add(typedSvc);
};
}
if (options.addWithProperties != nullptr) {
auto add = std::move(options.addWithProperties);
- opts.addWithProperties = [add](void *svc, const celix::Properties &props) {
- I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ opts.addWithProperties = [add](std::shared_ptr<void> svc, const celix::Properties &props) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
add(typedSvc, props);
};
}
if (options.addWithOwner != nullptr) {
auto add = std::move(options.addWithOwner);
- opts.addWithOwner = [add](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
- I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ opts.addWithOwner = [add](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
add(typedSvc, props, bnd);
};
}
if (options.remove != nullptr) {
auto rem = std::move(options.remove);
- opts.remove = [rem](void *svc) {
- I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ opts.remove = [rem](std::shared_ptr<void> svc) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
rem(typedSvc);
};
}
if (options.removeWithProperties != nullptr) {
auto rem = std::move(options.removeWithProperties);
- opts.removeWithProperties = [rem](void *svc, const celix::Properties &props) {
- I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ opts.removeWithProperties = [rem](std::shared_ptr<void> svc, const celix::Properties &props) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
rem(typedSvc, props);
};
}
if (options.removeWithOwner != nullptr) {
auto rem = std::move(options.removeWithOwner);
- opts.removeWithOwner = [rem](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
- I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ opts.removeWithOwner = [rem](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
rem(typedSvc, props, bnd);
};
}
if (options.update != nullptr) {
auto update = std::move(options.update);
- opts.update = [update](std::vector<void*> rankedServices) {
- std::vector<I*> typedServices{};
+ opts.update = [update](std::vector<std::shared_ptr<void>> rankedServices) {
+ std::vector<std::shared_ptr<I>> typedServices{};
typedServices.reserve(rankedServices.size());
- for (void *svc : rankedServices) {
- typedServices.push_back(static_cast<I*>(svc));
+ for (auto &svc : rankedServices) {
+ auto typedSvc = std::static_pointer_cast<I>(svc);
+ typedServices.push_back(typedSvc);
}
update(std::move(typedServices));
};
}
if (options.updateWithProperties != nullptr) {
auto update = std::move(options.updateWithProperties);
- opts.updateWithProperties = [update](std::vector<std::tuple<void*, const celix::Properties *>> rankedServices) {
- std::vector<std::tuple<I*, const celix::Properties*>> typedServices{};
+ opts.updateWithProperties = [update](std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties *>> rankedServices) {
+ std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*>> typedServices{};
typedServices.reserve(rankedServices.size());
for (auto &tuple : rankedServices) {
- typedServices.push_back(std::make_tuple(static_cast<I*>(std::get<0>(tuple)), std::get<1>(tuple)));
+ auto typedSvc = std::static_pointer_cast<I>(std::get<0>(tuple));
+ typedServices.push_back(std::make_tuple(typedSvc, std::get<1>(tuple)));
}
update(std::move(typedServices));
};
}
if (options.updateWithOwner != nullptr) {
auto update = std::move(options.updateWithOwner);
- opts.updateWithOwner = [update](std::vector<std::tuple<void*, const celix::Properties *, const celix::IResourceBundle*>> rankedServices) {
- std::vector<std::tuple<I*, const celix::Properties*, const celix::IResourceBundle*>> typedServices{};
+ opts.updateWithOwner = [update](std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties *, const celix::IResourceBundle*>> rankedServices) {
+ std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle*>> typedServices{};
typedServices.reserve(rankedServices.size());
for (auto &tuple : rankedServices) {
- typedServices.push_back(std::make_tuple(static_cast<I*>(std::get<0>(tuple)), std::get<1>(tuple), std::get<2>(tuple)));
+ auto typedSvc = std::static_pointer_cast<I>(std::get<0>(tuple));
+ typedServices.push_back(std::make_tuple(typedSvc, std::get<1>(tuple), std::get<2>(tuple)));
}
update(std::move(typedServices));
};
}
- return trackServices(std::move(svcName), std::move(opts), requester);
+ return trackAnyServices(std::move(svcName), std::move(opts), requester);
}
#endif //CXX_CELIX_SERVICEREGISTRY_H
diff --git a/libs/registry/src/ServiceRegistry.cc b/libs/registry/src/ServiceRegistry.cc
index 862013f..ac9a9dd 100644
--- a/libs/registry/src/ServiceRegistry.cc
+++ b/libs/registry/src/ServiceRegistry.cc
@@ -23,7 +23,7 @@
#include <mutex>
#include <set>
#include <utility>
-#include <thread>
+#include <future>
#include <glog/logging.h>
@@ -85,13 +85,12 @@ namespace {
bool factory() const { return svcFactory != nullptr; }
void incrUsage() const {
- LOG(WARNING) << "TODO use shared_ptr unique instead ?? how to sync?";
+ //TODO look at atomics or shared_ptr to handled to counts / sync
std::lock_guard<std::mutex> lck{mutex};
usage += 1;
}
void decrUsage() const {
- LOG(WARNING) << "TODO use shared_ptr unique instead ?? how is sync?";
std::lock_guard<std::mutex> lck{mutex};
usage -= 1;
cond.notify_all();
@@ -140,17 +139,15 @@ namespace {
~SvcTrackerEntry() {}
void clear() {
- //TODO update, make special rem (e.g. only call the use set callbacks once with a nullptr)
std::vector<std::shared_ptr<const SvcEntry>> removeEntries{};
{
std::lock_guard<std::mutex> lck{tracked.mutex};
- for (auto &entry : tracked.entries) {
- removeEntries.push_back(entry);
+ for (const auto &entry : tracked.entries) {
+ removeEntries.push_back(entry.first);
}
- tracked.entries.clear();
}
for (auto &entry : removeEntries) {
- remMatch(entry); //note fill try to erase entry from entries again, TODO check if this is safe
+ remMatch(entry);
}
}
@@ -171,56 +168,74 @@ namespace {
void addMatch(std::shared_ptr<const SvcEntry> entry) {
//increase usage so that services cannot be removed while a service tracker is still active
+
+ //new custom deleter which arranges the count & sync for the used services
+
entry->incrUsage();
+ void *rawSvc = entry->service(*owner);
+ //NOTE creating a shared_ptr with a custom deleter, so that the SvcEntry usage is synced with this shared_ptr.
+ auto svc = std::shared_ptr<void>{rawSvc, [entry](void *) {
+ entry->decrUsage();
+ }};
+
{
std::lock_guard<std::mutex> lck{tracked.mutex};
- tracked.entries.insert(entry);
+ tracked.entries.emplace(entry, svc);
}
//call callbacks
callSetCallbacks();
- callAddRemoveCallbacks(entry, true);
+ callAddRemoveCallbacks(entry, svc, true);
callUpdateCallbacks();
}
void remMatch(const std::shared_ptr<const SvcEntry> &entry) {
+ std::shared_ptr<void> svc{};
{
std::lock_guard<std::mutex> lck{tracked.mutex};
- tracked.entries.erase(entry);
+ auto it = tracked.entries.find(entry);
+ svc = it->second;
+ tracked.entries.erase(it);
}
//call callbacks
- callSetCallbacks();
- callAddRemoveCallbacks(entry, false);
+ callSetCallbacks(); //note also removed highest if that was set to this svc
+ callAddRemoveCallbacks(entry, svc, false);
callUpdateCallbacks();
- //decrease usage so that services cannot be removed while a service tracker is still active
- entry->decrUsage();
+
+ //note sync will be done on the SvcEntry usage, which is controlled by the tracker svc shared ptr
}
- void callAddRemoveCallbacks(const std::shared_ptr<const SvcEntry> &updatedEntry, bool add) {
+ void callAddRemoveCallbacks(const std::shared_ptr<const SvcEntry> &entry, std::shared_ptr<void> &svc, bool add) {
auto &update = add ? opts.add : opts.remove;
- if (update != nullptr) {
- void *svc = updatedEntry->service(*owner);
+ auto &updateWithProps = add ? opts.addWithProperties : opts.removeWithProperties;
+ auto &updateWithOwner = add ? opts.addWithOwner : opts.removeWithOwner;
+ if (update) {
update(svc);
}
- //TODO rest of add/remove
+ if (updateWithProps) {
+ updateWithProps(svc, entry->props);
+ }
+ if (updateWithOwner) {
+ updateWithOwner(svc, entry->props, *entry->owner);
+ }
}
void callSetCallbacks() {
- std::shared_ptr<const SvcEntry> currentHighest;
+ std::shared_ptr<void> currentHighestSvc{};
+ std::shared_ptr<const SvcEntry> currentHighestSvcEntry{};
bool highestUpdated = false;
{
std::lock_guard<std::mutex> lck{tracked.mutex};
auto begin = tracked.entries.begin();
- if (begin == tracked.entries.end()) {
- currentHighest = nullptr;
- } else {
- currentHighest = *begin;
+ if (begin != tracked.entries.end()) {
+ currentHighestSvc = begin->second;
+ currentHighestSvcEntry = begin->first;
}
- if (currentHighest != tracked.highest) {
- tracked.highest = currentHighest;
+ if (currentHighestSvc != tracked.highest) {
+ tracked.highest = currentHighestSvc;
highestUpdated = true;
}
}
@@ -228,43 +243,58 @@ namespace {
//TODO race condition. highest can be updated because lock is released.
if (highestUpdated) {
- void *svc = currentHighest == nullptr ? nullptr : currentHighest->service(*owner);
- if (opts.set != nullptr) {
- opts.set(svc);
+ if (opts.set) {
+ opts.set(currentHighestSvc); //note can be nullptr
+ }
+ if (opts.setWithProperties) {
+ opts.setWithProperties(currentHighestSvc, currentHighestSvcEntry->props);
+ }
+ if (opts.setWithOwner) {
+ opts.setWithOwner(currentHighestSvc, currentHighestSvcEntry->props, *currentHighestSvcEntry->owner);
}
- //TODO rest of set
}
}
void callUpdateCallbacks() {
+ std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties*, const celix::IResourceBundle*>> rankedServices{};
+ if (opts.update || opts.updateWithProperties || opts.updateWithOwner) {
+ //fill vector
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ rankedServices.reserve(tracked.entries.size());
+ for (auto &tracked : tracked.entries) {
+ rankedServices.push_back(std::make_tuple(tracked.second, &tracked.first->props, tracked.first->owner.get()));
+ }
+ }
if (opts.update) {
- std::vector<void *> rankedServices{};
- {
- std::lock_guard<std::mutex> lck{tracked.mutex};
- rankedServices.reserve(tracked.entries.size());
- for (auto &tracked : tracked.entries) {
- rankedServices.push_back(tracked->service(*owner));
- }
+ std::vector<std::shared_ptr<void>> rnk{};
+ for (auto &tuple : rankedServices) {
+ rnk.push_back(std::get<0>(tuple));
+ }
+ opts.update(std::move(rnk));
+ }
+ if (opts.updateWithProperties) {
+ std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties*>> rnk{};
+ for (auto &tuple : rankedServices) {
+ rnk.push_back(std::make_pair(std::get<0>(tuple), std::get<1>(tuple)));
}
- opts.update(std::move(rankedServices));
+ opts.updateWithProperties(std::move(rnk));
+ }
+ if (opts.updateWithOwner) {
+ opts.updateWithOwner(std::move(rankedServices));
}
- //TODO rest of the update calls
}
int count() const {
- LOG(INFO) << "TODO use shared_ptr count instead";
std::lock_guard<std::mutex> lck{tracked.mutex};
return (int)tracked.entries.size();
}
void incrUsage() const {
- LOG(INFO) << "TODO use shared_ptr count instead";
std::lock_guard<std::mutex> lck{mutex};
usage += 1;
}
void decrUsage() const {
- LOG(INFO) << "TODO use shared_ptr count instead";
std::lock_guard<std::mutex> lck{mutex};
usage -= 1;
cond.notify_all();
@@ -277,12 +307,11 @@ namespace {
private:
struct {
mutable std::mutex mutex; //protects matchedEntries & highestRanking
- std::set<std::shared_ptr<const SvcEntry>, SvcEntryLess> entries{};
- std::shared_ptr<const SvcEntry> highest{};
+ std::map<std::shared_ptr<const SvcEntry>, std::shared_ptr<void>, SvcEntryLess> entries{};
+ std::shared_ptr<void> highest{};
} tracked{};
- //sync TODO refactor to atomics
mutable std::mutex mutex{};
mutable std::condition_variable cond{};
mutable int usage{1};
@@ -354,6 +383,7 @@ public:
//Add to registry
std::shared_ptr<const celix::IResourceBundle> bnd = owner ? owner : emptyBundle;
+ props[celix::SERVICE_BUNDLE] = std::to_string(bnd->id());
if (factory) {
VLOG(1) << "Registering service factory '" << svcName << "' from bundle id " << owner->id() << std::endl;
@@ -369,8 +399,8 @@ public:
services.cache[entry->svcId] = entry;
//update trackers
- std::thread updateThread{[&]{updateTrackers(entry, true);}};
- updateThread.join();
+ auto future = std::async([&]{updateTrackers(entry, true);});
+ future.wait();
entry->decrUsage(); //note usage started at 1 during creation
@@ -395,17 +425,22 @@ public:
{
std::lock_guard<std::mutex> lock{services.mutex};
- const auto it = services.cache.find(svcId);
- if (it != services.cache.end()) {
- match = it->second;
- services.cache.erase(it);
- services.registry.at(match->svcName).erase(match);
+ const auto cacheIter = services.cache.find(svcId);
+ if (cacheIter != services.cache.end()) {
+ match = cacheIter->second;
+ services.cache.erase(cacheIter);
+ const auto svcSetIter = services.registry.find(match->svcName);
+ svcSetIter->second.erase(match);
+ if (svcSetIter->second.empty()) {
+ //last entry in the registry for this service name.
+ services.registry.erase(svcSetIter);
+ }
}
}
if (match) {
- std::thread updateThread{[&]{updateTrackers(match, false);}};
- updateThread.join();
+ auto future = std::async([&]{updateTrackers(match, false);});
+ future.wait();
match->waitTillUnused();
} else {
LOG(WARNING) << "Cannot unregister service. Unknown service id: " << svcId << "." << std::endl;
@@ -430,8 +465,8 @@ public:
if (match) {
match->waitTillUnused();
- std::thread clearThread{[&]{match->clear();}}; //ensure that all service are removed using the callbacks
- clearThread.join();
+ auto future = std::async([&]{match->clear();}); //ensure that all service are removed using the callbacks
+ future.wait();
} else {
LOG(WARNING) << "Cannot remove tracker. Unknown tracker id: " << trkId << "." << std::endl;
}
@@ -458,6 +493,15 @@ public:
match->decrUsage();
}
}
+
+ std::vector<std::string> listAllRegisteredServiceNames() const {
+ std::vector<std::string> result{};
+ std::lock_guard<std::mutex> lck{services.mutex};
+ for (const auto& pair : services.registry) {
+ result.emplace_back(std::string{pair.first});
+ }
+ return result;
+ }
};
@@ -496,7 +540,9 @@ celix::ServiceRegistration celix::ServiceRegistry::registerServiceFactory(std::s
//TODO add useService(s) call to ServiceTracker object for fast service access
//TODO move to Impl
-celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string svcName, celix::ServiceTrackerOptions<void> options, std::shared_ptr<const celix::IResourceBundle> requester) {
+celix::ServiceTracker celix::ServiceRegistry::trackAnyServices(std::string svcName,
+ celix::ServiceTrackerOptions<void> options,
+ std::shared_ptr<const celix::IResourceBundle> requester) {
//TODO create new tracker event and start new thread to update track trackers
long trkId = 0;
{
@@ -523,13 +569,13 @@ celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string svcName,
pimpl->trackers.registry[trkEntry->svcName].insert(trkEntry);
pimpl->trackers.cache[trkEntry->id] = trkEntry;
}
- std::thread updateThread{[&]{
+ auto future = std::async([&]{
for (auto &svcEntry : services) {
trkEntry->addMatch(svcEntry);
svcEntry->decrUsage();
}
- }};
- updateThread.join();
+ });
+ future.wait();
trkEntry->decrUsage(); //note trkEntry usage started at 1
auto untrack = [this, trkId]() -> void {
@@ -556,16 +602,16 @@ long celix::ServiceRegistry::nrOfServiceTrackers() const {
//TODO unregister tracker with remove tracker event in a new thread
//TODO move to Impl
-std::vector<long> celix::ServiceRegistry::findServices(const std::string &svcName, const std::string &rawFilter) const {
+std::vector<long> celix::ServiceRegistry::findAnyServices(const std::string &name, const std::string &f) const {
std::vector<long> result{};
- celix::Filter filter = rawFilter;
+ celix::Filter filter = f;
if (!filter.valid()) {
- LOG(WARNING) << "Invalid filter (" << rawFilter << ") provided. Cannot find services" << std::endl;
+ LOG(WARNING) << "Invalid filter (" << f << ") provided. Cannot find services" << std::endl;
return result;
}
std::lock_guard<std::mutex> lock{pimpl->services.mutex};
- const auto it = pimpl->services.registry.find(svcName);
+ const auto it = pimpl->services.registry.find(name);
if (it != pimpl->services.registry.end()) {
const auto &services = it->second;
for (const auto &visit : services) {
@@ -584,10 +630,14 @@ long celix::ServiceRegistry::nrOfRegisteredServices() const {
}
//TODO move to Impl
-int celix::ServiceRegistry::useServices(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &rawFilter, std::shared_ptr<const celix::IResourceBundle> requester) const {
- celix::Filter filter = rawFilter;
+int celix::ServiceRegistry::useAnyServices(const std::string &svcName,
+ std::function<void(std::shared_ptr<void> svc, const celix::Properties &props,
+ const celix::IResourceBundle &bnd)> use,
+ const std::string &f,
+ std::shared_ptr<const celix::IResourceBundle> requester) const {
+ celix::Filter filter = f;
if (!filter.valid()) {
- LOG(WARNING) << "Invalid filter (" << rawFilter << ") provided. Cannot find services" << std::endl;
+ LOG(WARNING) << "Invalid filter (" << f << ") provided. Cannot find services" << std::endl;
return 0;
}
@@ -607,7 +657,11 @@ int celix::ServiceRegistry::useServices(const std::string &svcName, std::functio
}
for (const std::shared_ptr<const SvcEntry> &entry : matches) {
- use(entry->service(*requester), entry->props, *entry->owner);
+ void *rawSvc = entry->service(*requester);
+ std::shared_ptr<void> svc{rawSvc, [entry](void *) {
+ entry->decrUsage();
+ }};
+ use(svc, entry->props, *entry->owner);
entry->decrUsage();
}
@@ -615,10 +669,14 @@ int celix::ServiceRegistry::useServices(const std::string &svcName, std::functio
}
//TODO move to Impl
-bool celix::ServiceRegistry::useService(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &rawFilter, std::shared_ptr<const celix::IResourceBundle> requester) const {
- celix::Filter filter = rawFilter;
+bool celix::ServiceRegistry::useAnyService(const std::string &svcName,
+ std::function<void(std::shared_ptr<void> svc, const celix::Properties &props,
+ const celix::IResourceBundle &bnd)> use,
+ const std::string &f,
+ std::shared_ptr<const celix::IResourceBundle> requester) const {
+ celix::Filter filter = f;
if (!filter.valid()) {
- LOG(WARNING) << "Invalid filter (" << rawFilter << ") provided. Cannot find services" << std::endl;
+ LOG(WARNING) << "Invalid filter (" << f << ") provided. Cannot find services" << std::endl;
return false;
}
@@ -639,13 +697,20 @@ bool celix::ServiceRegistry::useService(const std::string &svcName, std::functio
}
if (match != nullptr) {
- use(match->service(*requester), match->props, *match->owner);
- match->decrUsage();
+ void *rawSvc = match->service(*requester);
+ std::shared_ptr<void> svc{rawSvc, [match](void *) {
+ match->decrUsage();
+ }};
+ use(svc, match->props, *match->owner);
}
return match != nullptr;
}
+std::vector<std::string> celix::ServiceRegistry::listAllRegisteredServiceNames() const {
+ return pimpl->listAllRegisteredServiceNames();
+}
+
/**********************************************************************************************************************
Service Registration
**********************************************************************************************************************/