blob: a4a38a0349511e1c6928a40f439fd135178ee018 [file] [log] [blame]
/*
* 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 <utility>
#include "celix/IShellCommand.h"
#include "celix/BundleActivator.h"
#include "examples/ICalc.h"
namespace /*anon*/ {
/**
* @brief A simple active consumer of the ICalc service.
*/
class DynamicConsumer {
public:
/**
* @brief create and start a new DynamicConsumer.
* @param id The consumer id
* @return A shared ptr to the newly created DynamicConsumer
*/
static std::shared_ptr<DynamicConsumer> create(int id) {
std::shared_ptr<DynamicConsumer> instance{new DynamicConsumer{id}, [](DynamicConsumer* consumer) {
consumer->stop();
delete consumer;
}};
instance->start();
return instance;
}
/**
* @brief Sets the calc service
*/
void setCalc(std::shared_ptr<examples::ICalc> _calc, const std::shared_ptr<const celix::Properties>& props) {
std::lock_guard<std::mutex> lck{mutex};
if (_calc) {
calc = std::move(_calc);
svcId = props->getAsLong(celix::SERVICE_ID, -1);
ranking = props->getAsLong(celix::SERVICE_RANKING, 0);
std::string msg = "Setted calc svc for consumer nr " + std::to_string(id);
msg += ". svc id is " + std::to_string(svcId);
msg += ", and ranking is " + std::to_string(ranking);
msg += "\n";
std::cout << msg;
} else {
std::string msg = "Resetting calc svc for consumer " + std::to_string(id) + " to nullptr\n";
std::cout << msg;
calc = nullptr;
svcId = -1;
ranking = 0;
}
}
private:
explicit DynamicConsumer(int _id) : id{_id} {}
/**
* @brief Start the dynamic consumer thread.
*
* Should be called during creation.
*/
void start() {
calcThread = std::thread{&DynamicConsumer::run, this};
}
/**
* @brief Stops the dynamic consumer thread.
*/
void stop() {
bool wasActive = active.exchange(false);
if (wasActive) {
calcThread.join();
}
}
/**
* @brief The run method. Calls the calc service (if not nullptr) and sleeps for 2 seconds
*/
void run() {
std::unique_lock<std::mutex> lck{mutex, std::defer_lock};
int count = 1;
while (active) {
lck.lock();
auto localCalc = calc;
lck.unlock();
/*
* note it is safe to use the localCalc outside a mutex,
* because the shared_prt count will ensure the service cannot be unregistered while in use.
*/
if (localCalc) {
std::cout << "Calc result is " << std::to_string(localCalc->calc(count)) << std::endl;
localCalc = nullptr;
}
std::this_thread::sleep_for(std::chrono::seconds{2});
++count;
}
}
const int id;
std::atomic<bool> active{true};
std::thread calcThread{};
std::mutex mutex{}; //protects below
std::shared_ptr<examples::ICalc> calc{};
long svcId{-1};
long ranking{0};
};
/**
* @brief A ICalc consumer factory which can be trigger with a Celix shell command.
*/
class DynamicConsumerFactory : public celix::IShellCommand {
public:
static constexpr std::size_t MAX_CONSUMERS = 10;
explicit DynamicConsumerFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {}
/**
* @brief Executes the factory command by clearing all consumers and creating new ones.
*/
void executeCommand(const std::string& /*commandLine*/, const std::vector<std::string>& /*commandArgs*/, FILE* /*outStream*/, FILE* /*errorStream*/) override {
clearConsumer();
createConsumers();
}
/**
* @brief Create new consumers (as long as MAX_CONSUMERS is not reached)
*/
void createConsumers() {
std::lock_guard<std::mutex> lock{mutex};
int nextConsumerId = 1;
while (consumers.size() < MAX_CONSUMERS) {
ctx->logInfo("Creating dynamic consumer nr %zu", consumers.size());
auto consumer = DynamicConsumer::create(nextConsumerId++);
consumers[consumer] = ctx->trackServices<examples::ICalc>()
.addSetWithPropertiesCallback([consumer](std::shared_ptr<examples::ICalc> calc, std::shared_ptr<const celix::Properties> properties) {
consumer->setCalc(std::move(calc), std::move(properties));
})
.build();
}
}
/**
* @brief Clears all consumers.
*/
void clearConsumer() {
ctx->logInfo("Resetting all trackers and consumers");
std::lock_guard<std::mutex> lock{mutex};
consumers.clear();
ctx->logInfo("Done resetting consumers");
}
private:
const std::shared_ptr<celix::BundleContext> ctx;
std::mutex mutex{}; //protects below
std::unordered_map<std::shared_ptr<DynamicConsumer>, std::shared_ptr<celix::GenericServiceTracker>> consumers{};
};
/**
* @brief A bundle activator for a dynamic ICalc consumer factory.
*/
class DynamicConsumerBundleActivator {
public:
explicit DynamicConsumerBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) : factory{std::make_shared<DynamicConsumerFactory>(ctx)} {
factory->createConsumers();
cmdReg = ctx->registerService<celix::IShellCommand>(factory)
.addProperty(celix::IShellCommand::COMMAND_NAME, "consumers_reset")
.build();
}
private:
std::shared_ptr<DynamicConsumerFactory> factory;
std::shared_ptr<celix::ServiceRegistration> cmdReg{};
};
}
CELIX_GEN_CXX_BUNDLE_ACTIVATOR(DynamicConsumerBundleActivator)