blob: 5c14b263c5914e4bbd0e29e21f72e86210ed2fc5 [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 "RemoteServiceAdmin.h"
#include "celix/BundleContext.h"
#include "celix/rsa/RemoteConstants.h"
#define L_TRACE(...) \
logHelper.trace(__VA_ARGS__);
#define L_DEBUG(...) \
logHelper.debug(__VA_ARGS__);
#define L_INFO(...) \
_logHelper.info(__VA_ARGS__);
#define L_WARN(...) \
logHelper.warning(__VA_ARGS__);
#define L_ERROR(...) \
logHelper.error(__VA_ARGS__);
celix::rsa::RemoteServiceAdmin::RemoteServiceAdmin(celix::LogHelper logHelper) : logHelper{std::move(logHelper)} {}
void celix::rsa::RemoteServiceAdmin::addEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint) {
assert(endpoint);
auto interface = endpoint->getInterface();
if (interface.empty()) {
L_WARN("Adding endpoint but missing exported interfaces");
return;
}
auto endpointId = endpoint->getId();
if (endpointId.empty()) {
L_WARN("Adding endpoint but missing service id");
return;
}
std::lock_guard l(mutex);
toBeImportedServices.emplace_back(endpoint);
createImportServices();
}
void celix::rsa::RemoteServiceAdmin::removeEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint) {
assert(endpoint);
auto id = endpoint->getId();
if (id.empty()) {
L_WARN("Cannot remove endpoint without a valid id");
return;
}
std::shared_ptr<celix::rsa::IImportRegistration> tmpStore{}; //to ensure destruction outside of lock
{
std::lock_guard l(mutex);
toBeImportedServices.erase(std::remove_if(toBeImportedServices.begin(), toBeImportedServices.end(), [&id](auto const &endpoint){
return id == endpoint->getId();
}), toBeImportedServices.end());
auto it = importedServices.find(id);
if (it != importedServices.end()) {
tmpStore = std::move(it->second);
importedServices.erase(it);
}
}
}
void celix::rsa::RemoteServiceAdmin::addImportedServiceFactory(
const std::shared_ptr<celix::rsa::IImportServiceFactory> &factory,
const std::shared_ptr<const celix::Properties> &properties) {
auto targetServiceName = properties->get(celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
if (targetServiceName.empty()) {
L_WARN("Adding service factory but missing %s property", celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
return;
}
std::lock_guard l(mutex);
auto existingFactory = importServiceFactories.find(targetServiceName);
if (existingFactory != end(importServiceFactories)) {
L_WARN("Adding imported factory but factory already exists");
return;
}
importServiceFactories.emplace(targetServiceName, factory);
createImportServices();
}
void celix::rsa::RemoteServiceAdmin::removeImportedServiceFactory(
const std::shared_ptr<celix::rsa::IImportServiceFactory> &/*factory*/,
const std::shared_ptr<const celix::Properties> &properties) {
auto targetServiceName = properties->get(celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
if (targetServiceName.empty()) {
L_WARN("Removing service factory but missing %s property", celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
return;
}
std::lock_guard l(mutex);
importServiceFactories.erase(targetServiceName);
//TODO remove imported services from this factory ??needed
//FIXME yes, it will lead to crash if we uninstall the factory: https://github.com/apache/celix/issues/653
}
void celix::rsa::RemoteServiceAdmin::addExportedServiceFactory(
const std::shared_ptr<celix::rsa::IExportServiceFactory> &factory,
const std::shared_ptr<const celix::Properties> &properties) {
auto targetServiceName = properties->get(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
if (targetServiceName.empty()) {
L_WARN("Adding service factory but missing %s property", celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
return;
}
std::lock_guard<std::mutex> lock{mutex};
exportServiceFactories.emplace(targetServiceName, factory);
createExportServices();
}
void celix::rsa::RemoteServiceAdmin::removeExportedServiceFactory(
const std::shared_ptr<celix::rsa::IExportServiceFactory> &/*factory*/,
const std::shared_ptr<const celix::Properties> &properties) {
auto targetServiceName = properties->get(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
if (targetServiceName.empty()) {
L_WARN("Removing service factory but missing %s property", celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
return;
}
std::lock_guard l(mutex);
exportServiceFactories.erase(targetServiceName);
//TODO remove exported services from this factory ??needed
}
void celix::rsa::RemoteServiceAdmin::addService(const std::shared_ptr<void>& /*svc*/, const std::shared_ptr<const celix::Properties>& props) {
auto serviceName = props->get(celix::SERVICE_NAME, "");
if (serviceName.empty()) {
L_WARN("Adding service to be exported but missing objectclass");
return;
}
std::lock_guard<std::mutex> lock{mutex};
toBeExportedServices.emplace_back(props);
createExportServices();
}
void celix::rsa::RemoteServiceAdmin::removeService(const std::shared_ptr<void>& /*svc*/, const std::shared_ptr<const celix::Properties>& props) {
long svcId = props->getAsLong(celix::SERVICE_ID, -1);
if (svcId < 0) {
L_WARN("Removing service to be exported but missing service.id");
return;
}
std::lock_guard l(mutex);
auto instanceIt = exportedServices.find(svcId);
if (instanceIt != end(exportedServices)) {
exportedServices.erase(instanceIt);
}
//remove to be exported endpoint (if present)
for (auto it = toBeExportedServices.begin(); it != toBeExportedServices.end(); ++it) {
if ((*it)->getAsLong(celix::SERVICE_ID, -1) == svcId) {
toBeExportedServices.erase(it);
break;
}
}
}
bool celix::rsa::RemoteServiceAdmin::isEndpointMatch(const celix::rsa::EndpointDescription& endpoint, const celix::rsa::IImportServiceFactory& factory) const {
if (factory.getSupportedConfigs().empty()) {
L_WARN("Matching endpoint with a import service factory with no supported configs, this will always fail");
return false;
}
if (endpoint.getConfigurationTypes().empty()) {
L_WARN("Matching endpoint with empty configuration types (%s), this will always fail", celix::rsa::SERVICE_IMPORTED_CONFIGS);
return false;
}
size_t matchCount = 0;
for (const auto& config : endpoint.getConfigurationTypes()) {
for (const auto& factoryConfig : factory.getSupportedConfigs()) {
if (config == factoryConfig) {
++matchCount;
break;
}
}
}
return matchCount == factory.getSupportedConfigs().size(); //note must match all requested configurations
}
bool celix::rsa::RemoteServiceAdmin::isExportServiceMatch(const celix::Properties& svcProperties, const celix::rsa::IExportServiceFactory& factory) const {
auto providedConfigs = celix::split(svcProperties.get(celix::rsa::SERVICE_IMPORTED_CONFIGS));
auto providedIntents = celix::split(svcProperties.get(celix::rsa::SERVICE_EXPORTED_INTENTS, "osgi.basic"));
if (providedConfigs.empty() && providedIntents.empty()) {
//note cannot match export service with no config or intent
return false;
}
if (factory.getSupportedIntents().empty() && factory.getSupportedConfigs().empty()) {
L_WARN("Matching service marked for export with a export service factory with no supported intents or configs, this will always fail");
return false;
}
if (!providedConfigs.empty()) {
size_t matchCount = 0;
for (const auto& config : providedConfigs) {
for (const auto& factoryConfig : factory.getSupportedConfigs()) {
if (config == factoryConfig) {
++matchCount;
break;
}
}
}
return matchCount == factory.getSupportedConfigs().size(); //note must match all requested configurations
} else /*match on intent*/ {
for (const auto& intent: providedIntents) {
for (const auto& factoryIntent: factory.getSupportedIntents()) {
if (intent == factoryIntent) {
return true;
}
}
}
return false;
}
}
void celix::rsa::RemoteServiceAdmin::createImportServices() {
//precondition mutex taken
auto it = toBeImportedServices.begin();
while (it != toBeImportedServices.end()) {
auto interface = (*it)->getInterface();
bool match = false;
for (auto factoryIt = importServiceFactories.lower_bound(interface); factoryIt != importServiceFactories.end() && factoryIt->first == interface; ++factoryIt) {
if (isEndpointMatch(**it, *factoryIt->second)) {
match = true;
auto endpointId = (*it)->getId();
L_DEBUG("Adding endpoint %s, created export service for %s", endpointId.c_str(), (*it)->getInterface().c_str());
importedServices.emplace(endpointId, factoryIt->second->importService(**it));
break;
} else {
L_TRACE("Config mismatch.");
}
}
if (match) {
it = toBeImportedServices.erase(it);
} else {
L_DEBUG("Adding endpoint to be imported but no matching factory available yet, delaying import");
++it;
}
}
}
void celix::rsa::RemoteServiceAdmin::createExportServices() {
//precondition mutex taken
auto it = toBeExportedServices.begin();
while (it != toBeExportedServices.end()) {
const auto& svcProperties = **it;
auto serviceName = svcProperties.get(celix::SERVICE_NAME, "");
auto svcId = svcProperties.getAsLong(celix::SERVICE_ID, -1);
bool match = false;
for (auto factoryIt = exportServiceFactories.lower_bound(serviceName); factoryIt != exportServiceFactories.end() && factoryIt->first == serviceName; ++factoryIt) {
if (isExportServiceMatch(svcProperties, *factoryIt->second)) {
match = true;
exportedServices.emplace(svcId, factoryIt->second->exportService(svcProperties));
break;
} else {
L_TRACE("Intent mismatch");
}
}
if (match) {
it = toBeExportedServices.erase(it);
} else {
L_DEBUG("Adding endpoint to be imported but no matching factory available yet, delaying import");
++it;
}
}
}