diff --git a/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp b/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp index e986b2e75a..5a49cdc7dd 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp +++ b/Core/Code/CppMicroServices/src/service/usServiceRegistry.cpp @@ -1,327 +1,327 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed 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 #include #include #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT #include US_BASECLASS_HEADER #endif #include "usServiceRegistry_p.h" #include "usServiceFactory.h" #include "usServiceRegistry_p.h" #include "usServiceRegistrationPrivate.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" US_BEGIN_NAMESPACE typedef MutexLock MutexLocker; ServiceProperties ServiceRegistry::CreateServiceProperties(const ServiceProperties& in, const std::list& classes, long sid) { static long nextServiceID = 1; ServiceProperties props(in); if (!classes.empty()) { props.insert(std::make_pair(ServiceConstants::OBJECTCLASS(), classes)); } props.insert(std::make_pair(ServiceConstants::SERVICE_ID(), sid != -1 ? sid : nextServiceID++)); return props; } ServiceRegistry::ServiceRegistry(CoreModuleContext* coreCtx) : core(coreCtx) { } ServiceRegistry::~ServiceRegistry() { Clear(); } void ServiceRegistry::Clear() { services.clear(); serviceRegistrations.clear(); classServices.clear(); core = 0; } ServiceRegistration ServiceRegistry::RegisterService(ModulePrivate* module, const std::list& classes, US_BASECLASS_NAME* service, const ServiceProperties& properties) { if (service == 0) { throw std::invalid_argument("Can't register 0 as a service"); } // Check if service implements claimed classes and that they exist. for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { if (i->empty()) { throw std::invalid_argument("Can't register as null class"); } #ifdef US_ENABLE_SERVICE_FACTORY_SUPPORT if (!(dynamic_cast(service))) #endif { if (!CheckServiceClass(service, *i)) { std::string msg; std::stringstream ss(msg); ss << "Service class " << us_service_impl_name(service) << " is not an instance of " << (*i) << ". Maybe you forgot to export the RTTI information for the interface."; throw std::invalid_argument(msg); } } } ServiceRegistration res(module, service, CreateServiceProperties(properties, classes)); { MutexLocker lock(mutex); services.insert(std::make_pair(res, classes)); serviceRegistrations.push_back(res); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; std::list::iterator ip = std::lower_bound(s.begin(), s.end(), res); s.insert(ip, res); } } ServiceReference r = res.GetReference(); ServiceListeners::ServiceListenerEntries listeners; module->coreCtx->listeners.GetMatchingServiceListeners(r, listeners); module->coreCtx->listeners.ServiceChanged(listeners, ServiceEvent(ServiceEvent::REGISTERED, r)); return res; } void ServiceRegistry::UpdateServiceRegistrationOrder(const ServiceRegistration& sr, const std::list& classes) { MutexLocker lock(mutex); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; s.erase(std::remove(s.begin(), s.end(), sr), s.end()); s.insert(std::lower_bound(s.begin(), s.end(), sr), sr); } } bool ServiceRegistry::CheckServiceClass(US_BASECLASS_NAME* , const std::string& ) const { //return service->inherits(cls.toAscii()); // No possibility to check inheritance based on string literals. return true; } void ServiceRegistry::Get(const std::string& clazz, std::list& serviceRegs) const { MutexLocker lock(mutex); MapClassServices::const_iterator i = classServices.find(clazz); if (i != classServices.end()) { serviceRegs = i->second; } } ServiceReference ServiceRegistry::Get(ModulePrivate* module, const std::string& clazz) const { MutexLocker lock(mutex); try { std::list srs; Get_unlocked(clazz, "", module, srs); US_DEBUG << "get service ref " << clazz << " for module " << module->info.name << " = " << srs.size() << " refs"; if (!srs.empty()) { - return srs.front(); + return srs.back(); } } catch (const std::invalid_argument& ) { } return ServiceReference(); } void ServiceRegistry::Get(const std::string& clazz, const std::string& filter, ModulePrivate* module, std::list& res) const { MutexLocker lock(mutex); Get_unlocked(clazz, filter, module, res); } void ServiceRegistry::Get_unlocked(const std::string& clazz, const std::string& filter, ModulePrivate* /*module*/, std::list& res) const { std::list::const_iterator s; std::list::const_iterator send; std::list v; LDAPExpr ldap; if (clazz.empty()) { if (!filter.empty()) { ldap = LDAPExpr(filter); LDAPExpr::ObjectClassSet matched; if (ldap.GetMatchedObjectClasses(matched)) { v.clear(); for(LDAPExpr::ObjectClassSet::const_iterator className = matched.begin(); className != matched.end(); ++className) { MapClassServices::const_iterator i = classServices.find(*className); if (i != classServices.end()) { std::copy(i->second.begin(), i->second.end(), std::back_inserter(v)); } } if (!v.empty()) { s = v.begin(); send = v.end(); } else { return; } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { s = serviceRegistrations.begin(); send = serviceRegistrations.end(); } } else { MapClassServices::const_iterator it = classServices.find(clazz); if (it != classServices.end()) { s = it->second.begin(); send = it->second.end(); } else { return; } if (!filter.empty()) { ldap = LDAPExpr(filter); } } for (; s != send; ++s) { ServiceReference sri = s->GetReference(); if (filter.empty() || ldap.Evaluate(s->d->properties, false)) { res.push_back(sri); } } } void ServiceRegistry::RemoveServiceRegistration(const ServiceRegistration& sr) { MutexLocker lock(mutex); const std::list& classes = ref_any_cast >( sr.d->properties[ServiceConstants::OBJECTCLASS()]); services.erase(sr); serviceRegistrations.remove(sr); for (std::list::const_iterator i = classes.begin(); i != classes.end(); ++i) { std::list& s = classServices[*i]; if (s.size() > 1) { s.erase(std::remove(s.begin(), s.end(), sr), s.end()); } else { classServices.erase(*i); } } } void ServiceRegistry::GetRegisteredByModule(ModulePrivate* p, std::list& res) const { MutexLocker lock(mutex); for (std::list::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->module == p) { res.push_back(*i); } } } void ServiceRegistry::GetUsedByModule(Module* p, std::list& res) const { MutexLocker lock(mutex); for (std::list::const_iterator i = serviceRegistrations.begin(); i != serviceRegistrations.end(); ++i) { if (i->d->IsUsedByModule(p)) { res.push_back(*i); } } } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp b/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp index 74b5c19b3d..093e13f536 100644 --- a/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp +++ b/Core/Code/CppMicroServices/test/usServiceRegistryTest.cpp @@ -1,79 +1,138 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed 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 #include "usTestingMacros.h" #include #include #include #include US_BASECLASS_HEADER #include US_USE_NAMESPACE struct ITestServiceA { virtual ~ITestServiceA() {} }; US_DECLARE_SERVICE_INTERFACE(ITestServiceA, "org.cppmicroservices.testing.ITestServiceA") int TestMultipleServiceRegistrations() { struct TestServiceA : public US_BASECLASS_NAME, public ITestServiceA { }; ModuleContext* context = GetModuleContext(); TestServiceA s1; TestServiceA s2; ServiceRegistration reg1 = context->RegisterService(&s1); ServiceRegistration reg2 = context->RegisterService(&s2); std::list refs = context->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(refs.size() == 2, "Testing for two registered ITestServiceA services") reg2.Unregister(); refs = context->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(refs.size() == 1, "Testing for one registered ITestServiceA services") reg1.Unregister(); refs = context->GetServiceReferences(); US_TEST_CONDITION_REQUIRED(refs.empty(), "Testing for no ITestServiceA services") return EXIT_SUCCESS; } +int TestServicePropertiesUpdate() +{ + struct TestServiceA : public US_BASECLASS_NAME, public ITestServiceA + { + }; + + ModuleContext* context = GetModuleContext(); + + TestServiceA s1; + ServiceProperties props; + props["string"] = std::string("A std::string"); + props["bool"] = false; + const char* str = "A const char*"; + props["const char*"] = str; + + ServiceRegistration reg1 = context->RegisterService(&s1, props); + ServiceReference ref1 = context->GetServiceReference(); + + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences("").size() == 1, "Testing service count") + US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty("bool")) == false, "Testing bool property") + + // register second service with higher rank + TestServiceA s2; + ServiceProperties props2; + props2[ServiceConstants::SERVICE_RANKING()] = 50; + + ServiceRegistration reg2 = context->RegisterService(&s2, props2); + + // Get the service with the highest rank, this should be s2. + ServiceReference ref2 = context->GetServiceReference(); + TestServiceA* service = dynamic_cast(context->GetService(ref2)); + US_TEST_CONDITION_REQUIRED(service == &s2, "Testing highest service rank") + + props["bool"] = true; + // change the service ranking + props[ServiceConstants::SERVICE_RANKING()] = 100; + reg1.SetProperties(props); + + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences("").size() == 2, "Testing service count") + US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty("bool")) == true, "Testing bool property") + US_TEST_CONDITION_REQUIRED(any_cast(ref1.GetProperty(ServiceConstants::SERVICE_RANKING())) == 100, "Testing updated ranking") + + // Service with the highest ranking should now be s1 + service = dynamic_cast(context->GetService(ref1)); + US_TEST_CONDITION_REQUIRED(service == &s1, "Testing highest service rank") + + reg1.Unregister(); + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences("").size() == 1, "Testing service count") + + service = dynamic_cast(context->GetService(ref2)); + US_TEST_CONDITION_REQUIRED(service == &s2, "Testing highest service rank") + + reg2.Unregister(); + US_TEST_CONDITION_REQUIRED(context->GetServiceReferences("").empty(), "Testing service count") + + return EXIT_SUCCESS; +} + int usServiceRegistryTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceRegistryTest"); US_TEST_CONDITION(TestMultipleServiceRegistrations() == EXIT_SUCCESS, "Testing service registrations: ") + US_TEST_CONDITION(TestServicePropertiesUpdate() == EXIT_SUCCESS, "Testing service property update: ") US_TEST_END() }