diff --git a/Core/Code/CppMicroServices/src/module/usModuleContext.cpp b/Core/Code/CppMicroServices/src/module/usModuleContext.cpp index 463b314f64..2164370632 100644 --- a/Core/Code/CppMicroServices/src/module/usModuleContext.cpp +++ b/Core/Code/CppMicroServices/src/module/usModuleContext.cpp @@ -1,136 +1,157 @@ /*============================================================================= 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 "usModuleContext.h" #include "usModuleRegistry.h" #include "usModulePrivate.h" #include "usCoreModuleContext_p.h" #include "usServiceRegistry_p.h" #include "usServiceReferencePrivate.h" US_BEGIN_NAMESPACE class ModuleContextPrivate { public: ModuleContextPrivate(ModulePrivate* module) : module(module) {} ModulePrivate* module; }; ModuleContext::ModuleContext(ModulePrivate* module) : d(new ModuleContextPrivate(module)) {} ModuleContext::~ModuleContext() { delete d; } Module* ModuleContext::GetModule() const { return d->module->q; } Module* ModuleContext::GetModule(long id) const { return ModuleRegistry::GetModule(id); } void ModuleContext::GetModules(std::vector& modules) const { ModuleRegistry::GetModules(modules); } ServiceRegistration ModuleContext::RegisterService(const std::list& clazzes, US_BASECLASS_NAME* service, const ServiceProperties& properties) { return d->module->coreCtx->services.RegisterService(d->module, clazzes, service, properties); } ServiceRegistration ModuleContext::RegisterService(const char* clazz, US_BASECLASS_NAME* service, const ServiceProperties& properties) { std::list clazzes; clazzes.push_back(clazz); return d->module->coreCtx->services.RegisterService(d->module, clazzes, service, properties); } std::list ModuleContext::GetServiceReferences(const std::string& clazz, const std::string& filter) { std::list result; d->module->coreCtx->services.Get(clazz, filter, 0, result); return result; } ServiceReference ModuleContext::GetServiceReference(const std::string& clazz) { return d->module->coreCtx->services.Get(d->module, clazz); } US_BASECLASS_NAME* ModuleContext::GetService(const ServiceReference& reference) { if (!reference) { throw std::invalid_argument("Default constructed ServiceReference is not a valid input to getService()"); } ServiceReference internalRef(reference); return internalRef.d->GetService(d->module->q); } bool ModuleContext::UngetService(const ServiceReference& reference) { ServiceReference ref = reference; return ref.d->UngetService(d->module->q, true); } void ModuleContext::AddServiceListener(const ServiceListener& delegate, const std::string& filter) { - d->module->coreCtx->listeners.AddServiceListener(this, delegate, filter); + d->module->coreCtx->listeners.AddServiceListener(this, delegate, NULL, filter); } void ModuleContext::RemoveServiceListener(const ServiceListener& delegate) { - d->module->coreCtx->listeners.RemoveServiceListener(this, delegate); + d->module->coreCtx->listeners.RemoveServiceListener(this, delegate, NULL); } void ModuleContext::AddModuleListener(const ModuleListener& delegate) { - d->module->coreCtx->listeners.AddModuleListener(this, delegate); + d->module->coreCtx->listeners.AddModuleListener(this, delegate, NULL); } void ModuleContext::RemoveModuleListener(const ModuleListener& delegate) { - d->module->coreCtx->listeners.RemoveModuleListener(this, delegate); + d->module->coreCtx->listeners.RemoveModuleListener(this, delegate, NULL); +} + +void ModuleContext::AddServiceListener(const ServiceListener& delegate, void* data, + const std::string &filter) +{ + d->module->coreCtx->listeners.AddServiceListener(this, delegate, data, filter); +} + +void ModuleContext::RemoveServiceListener(const ServiceListener& delegate, void* data) +{ + d->module->coreCtx->listeners.RemoveServiceListener(this, delegate, data); +} + +void ModuleContext::AddModuleListener(const ModuleListener& delegate, void* data) +{ + d->module->coreCtx->listeners.AddModuleListener(this, delegate, data); +} + +void ModuleContext::RemoveModuleListener(const ModuleListener& delegate, void* data) +{ + d->module->coreCtx->listeners.RemoveModuleListener(this, delegate, data); } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/src/module/usModuleContext.h b/Core/Code/CppMicroServices/src/module/usModuleContext.h index 63b00ced85..bfd07de872 100644 --- a/Core/Code/CppMicroServices/src/module/usModuleContext.h +++ b/Core/Code/CppMicroServices/src/module/usModuleContext.h @@ -1,638 +1,649 @@ /*============================================================================= 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. =============================================================================*/ #ifndef USMODULECONTEXT_H_ #define USMODULECONTEXT_H_ #include "usUtils_p.h" #include "usServiceInterface.h" #include "usServiceEvent.h" #include "usServiceRegistration.h" #include "usServiceException.h" #include "usModuleEvent.h" US_BEGIN_NAMESPACE typedef US_SERVICE_LISTENER_FUNCTOR ServiceListener; typedef US_MODULE_LISTENER_FUNCTOR ModuleListener; class ModuleContextPrivate; /** * \ingroup MicroServices * * A module's execution context within the framework. The context is used to * grant access to other methods so that this module can interact with the * Micro Services Framework. * *

* ModuleContext methods allow a module to: *

    *
  • Subscribe to events published by the framework. *
  • Register service objects with the framework service registry. *
  • Retrieve ServiceReferences from the framework service * registry. *
  • Get and release service objects for a referenced service. *
  • Get the list of modules loaded in the framework. *
  • Get the {@link Module} object for a module. *
* *

* A ModuleContext object will be created and provided to the * module associated with this context when it is loaded using the * {@link ModuleActivator::Load} method. The same ModuleContext * object will be passed to the module associated with this context when it is * unloaded using the {@link ModuleActivator::Unload} method. A * ModuleContext object is generally for the private use of its * associated module and is not meant to be shared with other modules in the * module environment. * *

* The Module object associated with a ModuleContext * object is called the context module. * *

* The ModuleContext object is only valid during the execution of * its context module; that is, during the period when the context module * is loaded. If the ModuleContext * object is used subsequently, a std::logic_error is * thrown. The ModuleContext object is never reused after * its context module is unloaded. * *

* The framework is the only entity that can create ModuleContext * objects. * * @remarks This class is thread safe. */ class US_EXPORT ModuleContext { public: ~ModuleContext(); /** * Returns the Module object associated with this * ModuleContext. This module is called the context module. * * @return The Module object associated with this * ModuleContext. * @throws std::logic_error If this ModuleContext is no * longer valid. */ Module* GetModule() const; /** * Returns the module with the specified identifier. * * @param id The identifier of the module to retrieve. * @return A Module object or 0 if the * identifier does not match any previously loaded module. */ Module* GetModule(long id) const; /** * Returns a list of all known modules. *

* This method returns a list of all modules loaded in the module * environment at the time of the call to this method. This list will * also contain modules which might already have been unloaded. * * @param modules A std::vector of Module objects which * will hold one object per known module. */ void GetModules(std::vector& modules) const; /** * Registers the specified service object with the specified properties * under the specified class names into the framework. A * ServiceRegistration object is returned. The * ServiceRegistration object is for the private use of the * module registering the service and should not be shared with other * modules. The registering module is defined to be the context module. * Other modules can locate the service by using either the * {@link #GetServiceReferences} or {@link #GetServiceReference} method. * *

* A module can register a service object that implements the * {@link ServiceFactory} interface to have more flexibility in providing * service objects to other modules. * *

* The following steps are taken when registering a service: *

    *
  1. The framework adds the following service properties to the service * properties from the specified ServiceProperties (which may be * omitted):
    * A property named ServiceConstants#SERVICE_ID() identifying the * registration number of the service
    * A property named ServiceConstants#OBJECTCLASS() containing all the * specified classes.
    * Properties with these names in the specified ServiceProperties will * be ignored. *
  2. The service is added to the framework service registry and may now be * used by other modules. *
  3. A service event of type {@link ServiceEvent#REGISTERED} is fired. *
  4. A ServiceRegistration object for this registration is * returned. *
* * @param clazzes The class names under which the service can be located. * The class names will be stored in the service's * properties under the key ServiceConstants#OBJECTCLASS(). * @param service The service object or a ServiceFactory * object. * @param properties The properties for this service. The keys in the * properties object must all be std::string objects. See * {@link ServiceConstants} for a list of standard service property keys. * Changes should not be made to this object after calling this * method. To update the service's properties the * {@link ServiceRegistration::SetProperties} method must be called. * The set of properties may be omitted if the service has * no properties. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::invalid_argument If one of the following is true: *
    *
  • service is 0. *
  • properties contains case variants of the same key name. *
* @throws std::logic_error If this ModuleContext is no longer valid. * @see ServiceRegistration * @see ServiceFactory */ ServiceRegistration RegisterService(const std::list& clazzes, US_BASECLASS_NAME* service, const ServiceProperties& properties = ServiceProperties()); /** * Registers the specified service object with the specified properties * under the specified class name with the framework. * *

* This method is otherwise identical to * RegisterService(const std:list<std::string>&, us::Base*, const ServiceProperties&) * and is provided as a convenience when service will only be registered under a single * class name. Note that even in this case the value of the service's * ServiceConstants::OBJECTCLASS property will be a std::list, rather * than just a single string. * * @param clazz The class name under which the service can be located. * @param service The service object or a ServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @see RegisterService(const std:list<std::string>&, us::Base*, const ServiceProperties&) */ ServiceRegistration RegisterService(const char* clazz, US_BASECLASS_NAME* service, const ServiceProperties& properties = ServiceProperties()); /** * Registers the specified service object with the specified properties * using the specified template argument with the framework. * *

* This method is provided as a convenience when service will only be registered under * a single class name whose type is available to the caller. It is otherwise identical to * RegisterService(const char*, US_BASECLASS_NAME*, const ServiceProperties&) but should be preferred * since it avoids errors in the string literal identifying the class name or interface identifier. * * @tparam S The type under which the service can be located. * @param service The service object or a ServiceFactory object. * @param properties The properties for this service. * @return A ServiceRegistration object for use by the module * registering the service to update the service's properties or to * unregister the service. * @throws std::logic_error If this ModuleContext is no longer valid. * @see RegisterService(const char*, us::Base*, const ServiceProperties&) */ template ServiceRegistration RegisterService(US_BASECLASS_NAME* service, const ServiceProperties& properties = ServiceProperties()) { const char* clazz = us_service_interface_iid(); if (clazz == 0) { throw ServiceException(std::string("The interface class you are registering your service ") + us_service_impl_name(service) + " against has no US_DECLARE_SERVICE_INTERFACE macro"); } return RegisterService(clazz, service, properties); } /** * Returns a list of ServiceReference objects. The returned * list contains services that * were registered under the specified class and match the specified filter * expression. * *

* The list is valid at the time of the call to this method. However since * the Micro Services framework is a very dynamic environment, services can be modified or * unregistered at any time. * *

* The specified filter expression is used to select the * registered services whose service properties contain keys and values * which satisfy the filter expression. See LDAPFilter for a description * of the filter syntax. If the specified filter is * empty, all registered services are considered to match the * filter. If the specified filter expression cannot be parsed, * an std::invalid_argument will be thrown with a human readable * message where the filter became unparsable. * *

* The result is a list of ServiceReference objects for all * services that meet all of the following conditions: *

    *
  • If the specified class name, clazz, is not * empty, the service must have been registered with the * specified class name. The complete list of class names with which a * service was registered is available from the service's * {@link ServiceConstants#OBJECTCLASS() objectClass} property. *
  • If the specified filter is not empty, the * filter expression must match the service. *
* * @param clazz The class name with which the service was registered or * an empty string for all services. * @param filter The filter expression or empty for all * services. * @return A list of ServiceReference objects or * an empty list if no services are registered which satisfy the * search. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::logic_error If this ModuleContext is no longer valid. */ std::list GetServiceReferences(const std::string& clazz, const std::string& filter = std::string()); /** * Returns a list of ServiceReference objects. The returned * list contains services that * were registered under the interface id of the template argument S * and match the specified filter expression. * *

* This method is identical to GetServiceReferences(const std::string&, const std::string&) except that * the class name for the service object is automatically deduced from the template argument. * * @tparam S The type under which the requested service objects must have been registered. * @param filter The filter expression or empty for all * services. * @return A list of ServiceReference objects or * an empty list if no services are registered which satisfy the * search. * @throws std::invalid_argument If the specified filter * contains an invalid filter expression that cannot be parsed. * @throws std::logic_error If this ModuleContext is no longer valid. * @see GetServiceReferences(const std::string&, const std::string&) */ template std::list GetServiceReferences(const std::string& filter = std::string()) { const char* clazz = us_service_interface_iid(); if (clazz == 0) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); return GetServiceReferences(std::string(clazz), filter); } /** * Returns a ServiceReference object for a service that * implements and was registered under the specified class. * *

* The returned ServiceReference object is valid at the time of * the call to this method. However as the Micro Services framework is a very dynamic * environment, services can be modified or unregistered at any time. * *

* This method is the same as calling * {@link ModuleContext::GetServiceReferences(const std::string&, const std::string&)} with an * empty filter expression. It is provided as a convenience for * when the caller is interested in any service that implements the * specified class. *

* If multiple such services exist, the service with the highest ranking (as * specified in its ServiceConstants::SERVICE_RANKING() property) is returned. *

* If there is a tie in ranking, the service with the lowest service ID (as * specified in its ServiceConstants::SERVICE_ID() property); that is, the * service that was registered first is returned. * * @param clazz The class name with which the service was registered. * @return A ServiceReference object, or an invalid ServiceReference if * no services are registered which implement the named class. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException If no service was registered under the given class name. * @see #GetServiceReferences(const std::string&, const std::string&) */ ServiceReference GetServiceReference(const std::string& clazz); /** * Returns a ServiceReference object for a service that * implements and was registered under the specified template class argument. * *

* This method is identical to GetServiceReference(const std::string&) except that * the class name for the service object is automatically deduced from the template argument. * * @tparam S The type under which the requested service must have been registered. * @return A ServiceReference object, or an invalid ServiceReference if * no services are registered which implement the type S. * @throws std::logic_error If this ModuleContext is no longer valid. * @throws ServiceException It no service was registered under the given class name. * @see #GetServiceReference(const std::string&) * @see #GetServiceReferences(const std::string&) */ template ServiceReference GetServiceReference() { const char* clazz = us_service_interface_iid(); if (clazz == 0) throw ServiceException("The service interface class has no US_DECLARE_SERVICE_INTERFACE macro"); return GetServiceReference(std::string(clazz)); } /** * Returns the service object referenced by the specified * ServiceReference object. *

* A module's use of a service is tracked by the module's use count of that * service. Each time a service's service object is returned by * {@link #GetService(const ServiceReference&)} the context module's use count for * that service is incremented by one. Each time the service is released by * {@link #UngetService(const ServiceReference&)} the context module's use count * for that service is decremented by one. *

* When a module's use count for a service drops to zero, the module should * no longer use that service. * *

* This method will always return 0 when the service * associated with this reference has been unregistered. * *

* The following steps are taken to get the service object: *

    *
  1. If the service has been unregistered, 0 is returned. *
  2. The context module's use count for this service is incremented by * one. *
  3. If the context module's use count for the service is currently one * and the service was registered with an object implementing the * ServiceFactory interface, the * {@link ServiceFactory::GetService} method is * called to create a service object for the context module. This service * object is cached by the framework. While the context module's use count * for the service is greater than zero, subsequent calls to get the * services's service object for the context module will return the cached * service object.
    * If the ServiceFactory object throws an * exception, 0 is returned and a warning is logged. *
  4. The service object for the service is returned. *
* * @param reference A reference to the service. * @return A service object for the service associated with * reference or 0 if the service is not * registered or the ServiceFactory threw * an exception. * @throws std::logic_error If this ModuleContext is no * longer valid. * @throws std::invalid_argument If the specified * ServiceReference is invalid (default constructed). * @see #UngetService(const ServiceReference&) * @see ServiceFactory */ US_BASECLASS_NAME* GetService(const ServiceReference& reference); /** * Returns the service object referenced by the specified * ServiceReference object. *

* This is a convenience method which is identical to US_BASECLASS_NAME* GetService(const ServiceReference&) * except that it casts the service object to the supplied template argument type * * @tparam S The type the service object will be cast to. * @return A service object for the service associated with * reference or 0 if the service is not * registered, the ServiceFactory threw * an exception or the service could not be casted to the desired type. * @throws std::logic_error If this ModuleContext is no * longer valid. * @throws std::invalid_argument If the specified * ServiceReference is invalid (default constructed). * @see #GetService(const ServiceReference&) * @see #UngetService(const ServiceReference&) * @see ServiceFactory */ template S* GetService(const ServiceReference& reference) { return dynamic_cast(GetService(reference)); } /** * Releases the service object referenced by the specified * ServiceReference object. If the context module's use count * for the service is zero, this method returns false. * Otherwise, the context modules's use count for the service is decremented * by one. * *

* The service's service object should no longer be used and all references * to it should be destroyed when a module's use count for the service drops * to zero. * *

* The following steps are taken to unget the service object: *

    *
  1. If the context module's use count for the service is zero or the * service has been unregistered, false is returned. *
  2. The context module's use count for this service is decremented by * one. *
  3. If the context module's use count for the service is currently zero * and the service was registered with a ServiceFactory object, * the ServiceFactory#UngetService * method is called to release the service object for the context module. *
  4. true is returned. *
* * @param reference A reference to the service to be released. * @return false if the context module's use count for the * service is zero or if the service has been unregistered; * true otherwise. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see #GetService * @see ServiceFactory */ bool UngetService(const ServiceReference& reference); void AddServiceListener(const ServiceListener& delegate, const std::string& filter = std::string()); void RemoveServiceListener(const ServiceListener& delegate); void AddModuleListener(const ModuleListener& delegate); void RemoveModuleListener(const ModuleListener& delegate); /** * Adds the specified callback with the * specified filter to the context modules's list of listeners. * See LDAPFilter for a description of the filter syntax. Listeners * are notified when a service has a lifecycle state change. * *

* You must take care to remove registered listeners befor the receiver * object is destroyed. However, the Micro Services framework takes care * of removing all listeners registered by this context module's classes * after the module is unloaded. * *

* If the context module's list of listeners already contains a pair (r,c) * of receiver and callback such that * (r == receiver && c == callback), then this * method replaces that callback's filter (which may be empty) * with the specified one (which may be empty). * *

* The callback is called if the filter criteria is met. To filter based * upon the class of the service, the filter should reference the * ServiceConstants#OBJECTCLASS() property. If filter is * empty, all services are considered to match the filter. * *

* When using a filter, it is possible that the * ServiceEvents for the complete lifecycle of a service * will not be delivered to the callback. For example, if the * filter only matches when the property x has * the value 1, the callback will not be called if the * service is registered with the property x not set to the * value 1. Subsequently, when the service is modified * setting property x to the value 1, the * filter will match and the callback will be called with a * ServiceEvent of type MODIFIED. Thus, the * callback will not be called with a ServiceEvent of type * REGISTERED. * * @tparam The type of the receiver (containing the member function to be called) * @param receiver The object to connect to. * @param callback The member function pointer to call. * @param filter The filter criteria. * @throws std::invalid_argument If filter contains an * invalid filter string that cannot be parsed. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see ServiceEvent * @see RemoveServiceListener() */ template void AddServiceListener(R* receiver, void(R::*callback)(const ServiceEvent), const std::string& filter = std::string()) { - AddServiceListener(ServiceListenerMemberFunctor(receiver, callback), filter); + AddServiceListener(ServiceListenerMemberFunctor(receiver, callback), + static_cast(receiver), filter); } /** * Removes the specified callback from the context module's * list of listeners. * *

* If the (receiver,callback) pair is not contained in this * context module's list of listeners, this method does nothing. * * @tparam The type of the receiver (containing the member function to be removed) * @param receiver The object from which to disconnect. * @param callback The member function pointer to remove. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see AddServiceListener() */ template void RemoveServiceListener(R* receiver, void(R::*callback)(const ServiceEvent)) { - RemoveServiceListener(ServiceListenerMemberFunctor(receiver, callback)); + RemoveServiceListener(ServiceListenerMemberFunctor(receiver, callback), + static_cast(receiver)); } /** * Adds the specified callback to the context modules's list * of listeners. Listeners are notified when a module has a lifecycle * state change. * *

* If the context module's list of listeners already contains a pair (r,c) * of receiver and callback such that * (r == receiver && c == callback), then this method does nothing. * * @tparam The type of the receiver (containing the member function to be called) * @param receiver The object to connect to. * @param callback The member function pointer to call. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see ModuleEvent */ template void AddModuleListener(R* receiver, void(R::*callback)(const ModuleEvent)) { - AddModuleListener(ModuleListenerMemberFunctor(receiver, callback)); + AddModuleListener(ModuleListenerMemberFunctor(receiver, callback), + static_cast(receiver)); } /** * Removes the specified callback from the context module's * list of listeners. * *

* If the (receiver,callback) pair is not contained in this * context module's list of listeners, this method does nothing. * * @tparam The type of the receiver (containing the member function to be removed) * @param receiver The object from which to disconnect. * @param callback The member function pointer to remove. * @throws std::logic_error If this ModuleContext is no * longer valid. * @see AddModuleListener() */ template void RemoveModuleListener(R* receiver, void(R::*callback)(const ModuleEvent)) { - RemoveModuleListener(ModuleListenerMemberFunctor(receiver, callback)); + RemoveModuleListener(ModuleListenerMemberFunctor(receiver, callback), + static_cast(receiver)); } private: friend class Module; friend class ModulePrivate; ModuleContext(ModulePrivate* module); // purposely not implemented ModuleContext(const ModuleContext&); ModuleContext& operator=(const ModuleContext&); + void AddServiceListener(const ServiceListener& delegate, void* data, + const std::string& filter); + void RemoveServiceListener(const ServiceListener& delegate, void* data); + + void AddModuleListener(const ModuleListener& delegate, void* data); + void RemoveModuleListener(const ModuleListener& delegate, void* data); + ModuleContextPrivate * const d; }; US_END_NAMESPACE #endif /* USMODULECONTEXT_H_ */ diff --git a/Core/Code/CppMicroServices/src/service/usServiceListenerEntry.cpp b/Core/Code/CppMicroServices/src/service/usServiceListenerEntry.cpp index 681c5d5c02..f7b7262e3e 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceListenerEntry.cpp +++ b/Core/Code/CppMicroServices/src/service/usServiceListenerEntry.cpp @@ -1,147 +1,150 @@ /*============================================================================= 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 "usServiceListenerEntry_p.h" US_BEGIN_NAMESPACE class ServiceListenerEntryData : public SharedData { public: - ServiceListenerEntryData(Module* mc, const ServiceListenerEntry::ServiceListener& l, const std::string& filter) - : mc(mc), listener(l), bRemoved(false), ldap() + ServiceListenerEntryData(Module* mc, const ServiceListenerEntry::ServiceListener& l, + void* data, const std::string& filter) + : mc(mc), listener(l), data(data), bRemoved(false), ldap() { if (!filter.empty()) { ldap = LDAPExpr(filter); } } ~ServiceListenerEntryData() { } Module* const mc; ServiceListenerEntry::ServiceListener listener; + void* data; bool bRemoved; LDAPExpr ldap; /** * The elements of "simple" filters are cached, for easy lookup. * * The grammar for simple filters is as follows: * *

    * Simple = '(' attr '=' value ')'
    *        | '(' '|' Simple+ ')'
    * 
* where attr is one of {@link Constants#OBJECTCLASS}, * {@link Constants#SERVICE_ID} or {@link Constants#SERVICE_PID}, and * value must not contain a wildcard character. *

* The index of the vector determines which key the cache is for * (see {@link ServiceListenerState#hashedKeys}). For each key, there is * a vector pointing out the values which are accepted by this * ServiceListenerEntry's filter. This cache is maintained to make * it easy to remove this service listener. */ LDAPExpr::LocalCache local_cache; private: // purposely not implemented ServiceListenerEntryData(const ServiceListenerEntryData&); ServiceListenerEntryData& operator=(const ServiceListenerEntryData&); }; ServiceListenerEntry::ServiceListenerEntry(const ServiceListenerEntry& other) : d(other.d) { } ServiceListenerEntry::~ServiceListenerEntry() { } ServiceListenerEntry& ServiceListenerEntry::operator=(const ServiceListenerEntry& other) { d = other.d; return *this; } bool ServiceListenerEntry::operator==(const ServiceListenerEntry& other) const { return ((d->mc == 0 || other.d->mc == 0) || d->mc == other.d->mc) && - ServiceListenerCompare()(d->listener, other.d->listener); + (d->data == other.d->data) && ServiceListenerCompare()(d->listener, other.d->listener); } bool ServiceListenerEntry::operator<(const ServiceListenerEntry& other) const { - return d->mc < other.d->mc; + return (d->mc == other.d->mc) ? (d->data < other.d->data) : (d->mc < other.d->mc); } void ServiceListenerEntry::SetRemoved(bool removed) const { d->bRemoved = removed; } bool ServiceListenerEntry::IsRemoved() const { return d->bRemoved; } -ServiceListenerEntry::ServiceListenerEntry(Module* mc, const ServiceListener& l, const std::string& filter) - : d(new ServiceListenerEntryData(mc, l, filter)) +ServiceListenerEntry::ServiceListenerEntry(Module* mc, const ServiceListener& l, + void* data, const std::string& filter) + : d(new ServiceListenerEntryData(mc, l, data, filter)) { } Module* ServiceListenerEntry::GetModule() const { return d->mc; } std::string ServiceListenerEntry::GetFilter() const { return d->ldap.IsNull() ? std::string() : d->ldap.ToString(); } LDAPExpr ServiceListenerEntry::GetLDAPExpr() const { return d->ldap; } LDAPExpr::LocalCache& ServiceListenerEntry::GetLocalCache() const { return d->local_cache; } void ServiceListenerEntry::CallDelegate(const ServiceEvent& event) const { d->listener(event); } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/src/service/usServiceListenerEntry_p.h b/Core/Code/CppMicroServices/src/service/usServiceListenerEntry_p.h index c51f6217fd..fa5618589b 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceListenerEntry_p.h +++ b/Core/Code/CppMicroServices/src/service/usServiceListenerEntry_p.h @@ -1,97 +1,97 @@ /*============================================================================= 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. =============================================================================*/ #ifndef USSERVICELISTENERENTRY_H #define USSERVICELISTENERENTRY_H #include #include #include "usLDAPExpr_p.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4396) #endif US_BEGIN_NAMESPACE class Module; class ServiceListenerEntryData; /** * Data structure for saving service listener info. Contains * the optional service listener filter, in addition to the info * in ListenerEntry. */ class ServiceListenerEntry { public: typedef US_SERVICE_LISTENER_FUNCTOR ServiceListener; ServiceListenerEntry(const ServiceListenerEntry& other); ~ServiceListenerEntry(); ServiceListenerEntry& operator=(const ServiceListenerEntry& other); bool operator==(const ServiceListenerEntry& other) const; bool operator<(const ServiceListenerEntry& other) const; void SetRemoved(bool removed) const; bool IsRemoved() const; - ServiceListenerEntry(Module* mc, const ServiceListener& l, const std::string& filter = ""); + ServiceListenerEntry(Module* mc, const ServiceListener& l, void* data, const std::string& filter = ""); Module* GetModule() const; std::string GetFilter() const; LDAPExpr GetLDAPExpr() const; LDAPExpr::LocalCache& GetLocalCache() const; void CallDelegate(const ServiceEvent& event) const; private: US_HASH_FUNCTION_FRIEND(ServiceListenerEntry); ExplicitlySharedDataPointer d; }; US_END_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) #endif US_HASH_FUNCTION_NAMESPACE_BEGIN US_HASH_FUNCTION_BEGIN(US_PREPEND_NAMESPACE(ServiceListenerEntry)) return US_HASH_FUNCTION(const US_PREPEND_NAMESPACE(ServiceListenerEntryData)*, arg.d.ConstData()); US_HASH_FUNCTION_END US_HASH_FUNCTION_NAMESPACE_END #endif // USSERVICELISTENERENTRY_H diff --git a/Core/Code/CppMicroServices/src/service/usServiceListeners.cpp b/Core/Code/CppMicroServices/src/service/usServiceListeners.cpp index 76fc5c663f..c9036c83db 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceListeners.cpp +++ b/Core/Code/CppMicroServices/src/service/usServiceListeners.cpp @@ -1,289 +1,290 @@ /*============================================================================= 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 "usUtils_p.h" #include "usServiceListeners_p.h" #include "usServiceReferencePrivate.h" #include "usModule.h" //#include US_BEGIN_NAMESPACE const int ServiceListeners::OBJECTCLASS_IX = 0; const int ServiceListeners::SERVICE_ID_IX = 1; ServiceListeners::ServiceListeners() { hashedServiceKeys.push_back(ServiceConstants::OBJECTCLASS()); hashedServiceKeys.push_back(ServiceConstants::SERVICE_ID()); } void ServiceListeners::AddServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener, - const std::string& filter) + void* data, const std::string& filter) { MutexLocker lock(mutex); - ServiceListenerEntry sle(mc->GetModule(), listener, filter); + ServiceListenerEntry sle(mc->GetModule(), listener, data, filter); if (serviceSet.find(sle) != serviceSet.end()) { RemoveServiceListener_unlocked(sle); } serviceSet.insert(sle); CheckSimple(sle); } -void ServiceListeners::RemoveServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener) +void ServiceListeners::RemoveServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener, + void* data) { - ServiceListenerEntry entryToRemove(mc->GetModule(), listener); + ServiceListenerEntry entryToRemove(mc->GetModule(), listener, data); MutexLocker lock(mutex); RemoveServiceListener_unlocked(entryToRemove); } void ServiceListeners::RemoveServiceListener_unlocked(const ServiceListenerEntry& entryToRemove) { for (ServiceListenerEntries::iterator it = serviceSet.begin(); it != serviceSet.end(); ++it) { if (it->operator ==(entryToRemove)) { it->SetRemoved(true); RemoveFromCache(*it); serviceSet.erase(it); break; } } } -void ServiceListeners::AddModuleListener(ModuleContext* mc, const ModuleListener& listener) +void ServiceListeners::AddModuleListener(ModuleContext* mc, const ModuleListener& listener, void* data) { MutexLocker lock(moduleListenerMapMutex); - std::list& listeners = moduleListenerMap[mc]; - if (std::find_if(listeners.begin(), listeners.end(), std::bind1st(ModuleListenerCompare(), listener)) == listeners.end()) + ModuleListenerMap::value_type::second_type& listeners = moduleListenerMap[mc]; + if (std::find_if(listeners.begin(), listeners.end(), std::bind1st(ModuleListenerCompare(), std::make_pair(listener, data))) == listeners.end()) { - listeners.push_back(listener); + listeners.push_back(std::make_pair(listener, data)); } } -void ServiceListeners::RemoveModuleListener(ModuleContext* mc, const ModuleListener& listener) +void ServiceListeners::RemoveModuleListener(ModuleContext* mc, const ModuleListener& listener, void* data) { MutexLocker lock(moduleListenerMapMutex); - moduleListenerMap[mc].remove_if(std::bind1st(ModuleListenerCompare(), listener)); + moduleListenerMap[mc].remove_if(std::bind1st(ModuleListenerCompare(), std::make_pair(listener, data))); } void ServiceListeners::ModuleChanged(const ModuleEvent& evt) { MutexLocker lock(moduleListenerMapMutex); for(ModuleListenerMap::iterator i = moduleListenerMap.begin(); i != moduleListenerMap.end(); ++i) { - for(std::list::iterator j = i->second.begin(); + for(std::list >::iterator j = i->second.begin(); j != i->second.end(); ++j) { - (*j)(evt); + (j->first)(evt); } } } void ServiceListeners::RemoveAllListeners(ModuleContext* mc) { { MutexLocker lock(mutex); for (ServiceListenerEntries::iterator it = serviceSet.begin(); it != serviceSet.end(); ) { if (it->GetModule() == mc->GetModule()) { RemoveFromCache(*it); serviceSet.erase(it++); } else { ++it; } } } { MutexLocker lock(moduleListenerMapMutex); moduleListenerMap.erase(mc); } } void ServiceListeners::ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt) { ServiceListenerEntries matchBefore; ServiceChanged(receivers, evt, matchBefore); } void ServiceListeners::ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt, ServiceListenerEntries& matchBefore) { ServiceReference sr = evt.GetServiceReference(); int n = 0; for (ServiceListenerEntries::const_iterator l = receivers.begin(); l != receivers.end(); ++l) { if (!matchBefore.empty()) { matchBefore.erase(*l); } if (!l->IsRemoved()) { try { ++n; l->CallDelegate(evt); } catch (...) { US_WARN << "Service listener" #ifdef US_MODULE_SUPPORT_ENABLED << " in " << l->GetModule()->GetName() #endif << " threw an exception!"; } } } //US_DEBUG << "Notified " << n << " listeners"; } void ServiceListeners::GetMatchingServiceListeners(const ServiceReference& sr, ServiceListenerEntries& set, bool lockProps) { MutexLocker lock(mutex); // Check complicated or empty listener filters int n = 0; for (std::list::const_iterator sse = complicatedListeners.begin(); sse != complicatedListeners.end(); ++sse) { ++n; if (sse->GetLDAPExpr().IsNull() || sse->GetLDAPExpr().Evaluate(sr.d->GetProperties(), false)) { set.insert(*sse); } } //US_DEBUG << "Added " << set.size() << " out of " << n // << " listeners with complicated filters"; // Check the cache const std::list c(any_cast > (sr.d->GetProperty(ServiceConstants::OBJECTCLASS(), lockProps))); for (std::list::const_iterator objClass = c.begin(); objClass != c.end(); ++objClass) { AddToSet(set, OBJECTCLASS_IX, *objClass); } long service_id = any_cast(sr.d->GetProperty(ServiceConstants::SERVICE_ID(), lockProps)); std::stringstream ss; ss << service_id; AddToSet(set, SERVICE_ID_IX, ss.str()); } void ServiceListeners::RemoveFromCache(const ServiceListenerEntry& sle) { if (!sle.GetLocalCache().empty()) { for (std::size_t i = 0; i < hashedServiceKeys.size(); ++i) { CacheType& keymap = cache[i]; std::vector& l = sle.GetLocalCache()[i]; for (std::vector::const_iterator it = l.begin(); it != l.end(); ++it) { std::list& sles = keymap[*it]; sles.remove(sle); if (sles.empty()) { keymap.erase(*it); } } } } else { complicatedListeners.remove(sle); } } void ServiceListeners::CheckSimple(const ServiceListenerEntry& sle) { if (sle.GetLDAPExpr().IsNull()) { complicatedListeners.push_back(sle); } else { LDAPExpr::LocalCache local_cache; if (sle.GetLDAPExpr().IsSimple(hashedServiceKeys, local_cache, false)) { sle.GetLocalCache() = local_cache; for (std::size_t i = 0; i < hashedServiceKeys.size(); ++i) { for (std::vector::const_iterator it = local_cache[i].begin(); it != local_cache[i].end(); ++it) { std::list& sles = cache[i][*it]; sles.push_back(sle); } } } else { //US_DEBUG << "Too complicated filter: " << sle.GetFilter(); complicatedListeners.push_back(sle); } } } void ServiceListeners::AddToSet(ServiceListenerEntries& set, int cache_ix, const std::string& val) { std::list& l = cache[cache_ix][val]; if (!l.empty()) { //US_DEBUG << hashedServiceKeys[cache_ix] << " matches " << l.size(); for (std::list::const_iterator entry = l.begin(); entry != l.end(); ++entry) { set.insert(*entry); } } else { //US_DEBUG << hashedServiceKeys[cache_ix] << " matches none"; } } US_END_NAMESPACE diff --git a/Core/Code/CppMicroServices/src/service/usServiceListeners_p.h b/Core/Code/CppMicroServices/src/service/usServiceListeners_p.h index 7c7dbbc5c6..771f45bc0c 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceListeners_p.h +++ b/Core/Code/CppMicroServices/src/service/usServiceListeners_p.h @@ -1,174 +1,176 @@ /*============================================================================= 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. =============================================================================*/ #ifndef USSERVICELISTENERS_H #define USSERVICELISTENERS_H #include #include #include #include #include "usServiceListenerEntry_p.h" US_BEGIN_NAMESPACE class CoreModuleContext; class ModuleContext; /** * Here we handle all listeners that modules have registered. * */ class ServiceListeners { private: typedef Mutex MutexType; typedef MutexLock MutexLocker; public: typedef US_MODULE_LISTENER_FUNCTOR ModuleListener; - typedef US_UNORDERED_MAP_TYPE > ModuleListenerMap; + typedef US_UNORDERED_MAP_TYPE > > ModuleListenerMap; ModuleListenerMap moduleListenerMap; MutexType moduleListenerMapMutex; typedef US_UNORDERED_MAP_TYPE > CacheType; typedef US_UNORDERED_SET_TYPE ServiceListenerEntries; private: std::vector hashedServiceKeys; static const int OBJECTCLASS_IX; // = 0; static const int SERVICE_ID_IX; // = 1; /* Service listeners with complicated or empty filters */ std::list complicatedListeners; /* Service listeners with "simple" filters are cached. */ CacheType cache[2]; ServiceListenerEntries serviceSet; MutexType mutex; public: ServiceListeners(); /** * Add a new service listener. If an old one exists, and it has the * same owning module, the old listener is removed first. * * @param mc The module context adding this listener. * @param listener The service listener to add. + * @param data Additional data to distinguish ServiceListener objects. * @param filter An LDAP filter string to check when a service is modified. * @exception org.osgi.framework.InvalidSyntaxException * If the filter is not a correct LDAP expression. */ void AddServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener, - const std::string& filter); + void* data, const std::string& filter); /** * Remove service listener from current framework. Silently ignore * if listener doesn't exist. If listener is registered more than * once remove all instances. * * @param mc The module context who wants to remove listener. * @param listener Object to remove. + * @param data Additional data to distinguish ServiceListener objects. */ - void RemoveServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener); + void RemoveServiceListener(ModuleContext* mc, const ServiceListenerEntry::ServiceListener& listener, + void* data); /** - * Add a new service listener. + * Add a new module listener. * * @param mc The module context adding this listener. - * @param listener The service listener to add. - * @param filter An LDAP filter string to check when a service is modified. - * @exception org.osgi.framework.InvalidSyntaxException - * If the filter is not a correct LDAP expression. + * @param listener The module listener to add. + * @param data Additional data to distinguish ModuleListener objects. */ - void AddModuleListener(ModuleContext* mc, const ModuleListener& listener); + void AddModuleListener(ModuleContext* mc, const ModuleListener& listener, void* data); /** - * Remove service listener from current framework. Silently ignore + * Remove module listener from current framework. Silently ignore * if listener doesn't exist. * * @param mc The module context who wants to remove listener. * @param listener Object to remove. + * @param data Additional data to distinguish ModuleListener objects. */ - void RemoveModuleListener(ModuleContext* mc, const ModuleListener& listener); + void RemoveModuleListener(ModuleContext* mc, const ModuleListener& listener, void* data); void ModuleChanged(const ModuleEvent& evt); /** * Remove all listener registered by a module in the current framework. * * @param mc Module context which listeners we want to remove. */ void RemoveAllListeners(ModuleContext* mc); /** * Receive notification that a service has had a change occur in its lifecycle. * * @see org.osgi.framework.ServiceListener#serviceChanged */ void ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt, ServiceListenerEntries& matchBefore); void ServiceChanged(const ServiceListenerEntries& receivers, const ServiceEvent& evt); /** * * */ void GetMatchingServiceListeners(const ServiceReference& sr, ServiceListenerEntries& listeners, bool lockProps = true); private: void RemoveServiceListener_unlocked(const ServiceListenerEntry& entryToRemove); /** * Remove all references to a service listener from the service listener * cache. */ void RemoveFromCache(const ServiceListenerEntry& sle); /** * Checks if the specified service listener's filter is simple enough * to cache. */ void CheckSimple(const ServiceListenerEntry& sle); void AddToSet(ServiceListenerEntries& set, int cache_ix, const std::string& val); }; US_END_NAMESPACE #endif // USSERVICELISTENERS_H diff --git a/Core/Code/CppMicroServices/src/util/usUtils_p.h b/Core/Code/CppMicroServices/src/util/usUtils_p.h index 25a4a2d0e4..8a7c93d094 100644 --- a/Core/Code/CppMicroServices/src/util/usUtils_p.h +++ b/Core/Code/CppMicroServices/src/util/usUtils_p.h @@ -1,210 +1,213 @@ /*============================================================================= 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. =============================================================================*/ #ifndef USUTILS_H #define USUTILS_H #include #include #include #include //------------------------------------------------------------------- // Logging //------------------------------------------------------------------- US_BEGIN_NAMESPACE US_EXPORT void message_output(MsgType, const char* buf); struct LogMsg { LogMsg(int t, const char* file, int ln, const char* func) : type(static_cast(t)), enabled(true), buffer() { buffer << "In " << func << " at " << file << ":" << ln << " : "; } ~LogMsg() { if(enabled) message_output(type, buffer.str().c_str()); } template LogMsg& operator<<(T t) { if (enabled) buffer << t; return *this; } LogMsg& operator()(bool flag) { this->enabled = flag; return *this; } private: MsgType type; bool enabled; std::stringstream buffer; }; struct NoLogMsg { template NoLogMsg& operator<<(T) { return *this; } NoLogMsg& operator()(bool) { return *this; } }; US_END_NAMESPACE #if !defined(US_NO_DEBUG_OUTPUT) #define US_DEBUG US_PREPEND_NAMESPACE(LogMsg)(0, __FILE__, __LINE__, __FUNCTION__) #else #define US_DEBUG true ? US_PREPEND_NAMESPACE(NoLogMsg)() : US_PREPEND_NAMESPACE(NoLogMsg)() #endif #if !defined(US_NO_INFO_OUTPUT) #define US_INFO US_PREPEND_NAMESPACE(LogMsg)(1, __FILE__, __LINE__, __FUNCTION__) #else #define US_INFO true ? US_PREPEND_NAMESPACE(NoLogMsg)() : US_PREPEND_NAMESPACE(NoLogMsg)() #endif #if !defined(US_NO_WARNING_OUTPUT) #define US_WARN US_PREPEND_NAMESPACE(LogMsg)(2, __FILE__, __LINE__, __FUNCTION__) #else #define US_WARN true ? US_PREPEND_NAMESPACE(LogMsg)() : US_PREPEND_NAMESPACE(LogMsg)() #endif #define US_ERROR US_PREPEND_NAMESPACE(LogMsg)(3, __FILE__, __LINE__, __FUNCTION__) //------------------------------------------------------------------- // Error handling //------------------------------------------------------------------- US_BEGIN_NAMESPACE US_EXPORT std::string GetLastErrorStr(); US_END_NAMESPACE //------------------------------------------------------------------- // Functors //------------------------------------------------------------------- #include #include #if defined(US_USE_CXX11) || defined(__GNUC__) #ifdef US_USE_CXX11 #include #define US_MODULE_LISTENER_FUNCTOR std::function #define US_SERVICE_LISTENER_FUNCTOR std::function #else #include #define US_MODULE_LISTENER_FUNCTOR std::tr1::function #define US_SERVICE_LISTENER_FUNCTOR std::tr1::function #endif US_BEGIN_NAMESPACE template US_MODULE_LISTENER_FUNCTOR ModuleListenerMemberFunctor(X* x, void (X::*memFn)(const US_PREPEND_NAMESPACE(ModuleEvent))) { return std::bind1st(std::mem_fun(memFn), x); } US_END_NAMESPACE US_BEGIN_NAMESPACE - struct ModuleListenerCompare : std::binary_function + struct ModuleListenerCompare : std::binary_function, + std::pair, bool> { - bool operator()(const US_MODULE_LISTENER_FUNCTOR& f1, - const US_MODULE_LISTENER_FUNCTOR& f2) const + bool operator()(const std::pair& p1, + const std::pair& p2) const { - return f1.target() == f2.target(); + return p1.second == p2.second && + p1.first.target() == p2.first.target(); } }; US_END_NAMESPACE US_BEGIN_NAMESPACE template US_SERVICE_LISTENER_FUNCTOR ServiceListenerMemberFunctor(X* x, void (X::*memFn)(const US_PREPEND_NAMESPACE(ServiceEvent))) { return std::bind1st(std::mem_fun(memFn), x); } US_END_NAMESPACE US_BEGIN_NAMESPACE struct ServiceListenerCompare : std::binary_function { bool operator()(const US_SERVICE_LISTENER_FUNCTOR& f1, const US_SERVICE_LISTENER_FUNCTOR& f2) const { return f1.target() == f2.target(); } }; US_END_NAMESPACE #else #include #define US_MODULE_LISTENER_FUNCTOR US_PREPEND_NAMESPACE(Functor) US_BEGIN_NAMESPACE template US_MODULE_LISTENER_FUNCTOR ModuleListenerMemberFunctor(X* x, MemFn memFn) { return Functor(x, memFn); } US_END_NAMESPACE US_BEGIN_NAMESPACE - struct ModuleListenerCompare : std::binary_function + struct ModuleListenerCompare : std::binary_function, + std::pair, bool> { - bool operator()(const US_MODULE_LISTENER_FUNCTOR& f1, - const US_MODULE_LISTENER_FUNCTOR& f2) const - { return f1 == f2; } + bool operator()(const std::pair& p1, + const std::pair& p2) const + { return p1.second == p2.second && p1.first == p2.first; } }; US_END_NAMESPACE #define US_SERVICE_LISTENER_FUNCTOR US_PREPEND_NAMESPACE(Functor) US_BEGIN_NAMESPACE template US_SERVICE_LISTENER_FUNCTOR ServiceListenerMemberFunctor(X* x, MemFn memFn) { return Functor(x, memFn); } US_END_NAMESPACE US_BEGIN_NAMESPACE struct ServiceListenerCompare : std::binary_function { bool operator()(const US_SERVICE_LISTENER_FUNCTOR& f1, const US_SERVICE_LISTENER_FUNCTOR& f2) const { return f1 == f2; } }; US_END_NAMESPACE #endif #endif // USUTILS_H diff --git a/Core/Code/CppMicroServices/test/usModuleTest.cpp b/Core/Code/CppMicroServices/test/usModuleTest.cpp index e448a4d5f4..93a0438a90 100644 --- a/Core/Code/CppMicroServices/test/usModuleTest.cpp +++ b/Core/Code/CppMicroServices/test/usModuleTest.cpp @@ -1,382 +1,441 @@ /*============================================================================= 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 #include #include #include #include #include US_BASECLASS_HEADER #include "usTestUtilSharedLibrary.cpp" #include "usTestingMacros.h" US_USE_NAMESPACE extern ModuleActivator* _us_module_activator_instance_TestModuleA(); class TestModuleListener { public: TestModuleListener(ModuleContext* mc) : mc(mc), serviceEvents(), moduleEvents() {} void ModuleChanged(const ModuleEvent event) { moduleEvents.push_back(event); US_DEBUG << "ModuleEvent:" << event; } void ServiceChanged(const ServiceEvent event) { serviceEvents.push_back(event); US_DEBUG << "ServiceEvent:" << event; } ModuleEvent GetModuleEvent() const { if (moduleEvents.empty()) { return ModuleEvent(); } return moduleEvents.back(); } ServiceEvent GetServiceEvent() const { if (serviceEvents.empty()) { return ServiceEvent(); } return serviceEvents.back(); } bool CheckListenerEvents( bool pexp, ModuleEvent::Type ptype, bool sexp, ServiceEvent::Type stype, Module* moduleX, ServiceReference* servX) { std::vector pEvts; std::vector seEvts; if (pexp) pEvts.push_back(ModuleEvent(ptype, moduleX)); if (sexp) seEvts.push_back(ServiceEvent(stype, *servX)); return CheckListenerEvents(pEvts, seEvts); } bool CheckListenerEvents( const std::vector& pEvts, const std::vector& seEvts) { bool listenState = true; // assume everything will work if (pEvts.size() != moduleEvents.size()) { listenState = false; US_DEBUG << "*** Module event mismatch: expected " << pEvts.size() << " event(s), found " << moduleEvents.size() << " event(s)."; const std::size_t max = pEvts.size() > moduleEvents.size() ? pEvts.size() : moduleEvents.size(); for (std::size_t i = 0; i < max; ++i) { const ModuleEvent& pE = i < pEvts.size() ? pEvts[i] : ModuleEvent(); const ModuleEvent& pR = i < moduleEvents.size() ? moduleEvents[i] : ModuleEvent(); US_DEBUG << " " << pE << " - " << pR; } } else { for (std::size_t i = 0; i < pEvts.size(); ++i) { const ModuleEvent& pE = pEvts[i]; const ModuleEvent& pR = moduleEvents[i]; if (pE.GetType() != pR.GetType() || pE.GetModule() != pR.GetModule()) { listenState = false; US_DEBUG << "*** Wrong module event: " << pR << " expected " << pE; } } } if (seEvts.size() != serviceEvents.size()) { listenState = false; US_DEBUG << "*** Service event mismatch: expected " << seEvts.size() << " event(s), found " << serviceEvents.size() << " event(s)."; const std::size_t max = seEvts.size() > serviceEvents.size() ? seEvts.size() : serviceEvents.size(); for (std::size_t i = 0; i < max; ++i) { const ServiceEvent& seE = i < seEvts.size() ? seEvts[i] : ServiceEvent(); const ServiceEvent& seR = i < serviceEvents.size() ? serviceEvents[i] : ServiceEvent(); US_DEBUG << " " << seE << " - " << seR; } } else { for (std::size_t i = 0; i < seEvts.size(); ++i) { const ServiceEvent& seE = seEvts[i]; const ServiceEvent& seR = serviceEvents[i]; if (seE.GetType() != seR.GetType() || (!(seE.GetServiceReference() == seR.GetServiceReference()))) { listenState = false; US_DEBUG << "*** Wrong service event: " << seR << " expected " << seE; } } } moduleEvents.clear(); serviceEvents.clear(); return listenState; } private: ModuleContext* const mc; std::vector serviceEvents; std::vector moduleEvents; }; +// Verify that the same member function pointers registered as listeners +// with different receivers works. +void frame02a() +{ + ModuleContext* mc = GetModuleContext(); + + TestModuleListener listener1(mc); + TestModuleListener listener2(mc); + + try + { + mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged); + mc->AddModuleListener(&listener1, &TestModuleListener::ModuleChanged); + mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged); + mc->AddModuleListener(&listener2, &TestModuleListener::ModuleChanged); + } + catch (const std::logic_error& ise) + { + US_TEST_FAILED_MSG( << "module listener registration failed " << ise.what() + << " : frameSL02a:FAIL" ); + } + + SharedLibraryHandle target("TestModuleA"); + + // Start the test target + try + { + target.Load(); + } + catch (const std::exception& e) + { + US_TEST_FAILED_MSG( << "Failed to load module, got exception: " + << e.what() << " + in frameSL02a:FAIL" ); + } + +#ifdef US_BUILD_SHARED_LIBS + Module* moduleA = ModuleRegistry::GetModule("TestModuleA Module"); + US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") +#endif + + std::vector pEvts; +#ifdef US_BUILD_SHARED_LIBS + pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA)); + pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA)); +#endif + + std::vector seEvts; + + US_TEST_CONDITION(listener1.CheckListenerEvents(pEvts, seEvts), "Check first module listener") + US_TEST_CONDITION(listener2.CheckListenerEvents(pEvts, seEvts), "Check second module listener") + + mc->RemoveModuleListener(&listener1, &TestModuleListener::ModuleChanged); + mc->RemoveModuleListener(&listener2, &TestModuleListener::ModuleChanged); + + target.Unload(); +} + // Verify information from the ModuleInfo struct void frame005a(ModuleContext* mc) { Module* m = mc->GetModule(); // check expected headers US_TEST_CONDITION("CppMicroServicesTestDriver" == m->GetName(), "Test module name"); // US_DEBUG << "Version: " << m->GetVersion(); US_TEST_CONDITION(ModuleVersion(0,1,0) == m->GetVersion(), "Test module version") } // Get context id, location and status of the module void frame010a(ModuleContext* mc) { Module* m = mc->GetModule(); long int contextid = m->GetModuleId(); US_DEBUG << "CONTEXT ID:" << contextid; std::string location = m->GetLocation(); US_DEBUG << "LOCATION:" << location; US_TEST_CONDITION(!location.empty(), "Test for non-empty module location") US_TEST_CONDITION(m->IsLoaded(), "Test for loaded flag") } //---------------------------------------------------------------------------- //Test result of GetService(ServiceReference()). Should throw std::invalid_argument void frame018a(ModuleContext* mc) { try { US_BASECLASS_NAME* obj = mc->GetService(ServiceReference()); US_DEBUG << "Got service object = " << us_service_impl_name(obj) << ", expected std::invalid_argument exception"; US_TEST_FAILED_MSG(<< "Got service object, excpected std::invalid_argument exception") } catch (const std::invalid_argument& ) {} catch (...) { US_TEST_FAILED_MSG(<< "Got wrong exception, expected std::invalid_argument") } } // Load libA and check that it exists and that the service it registers exists, // also check that the expected events occur void frame020a(ModuleContext* mc, TestModuleListener& listener, SharedLibraryHandle& libA) { try { libA.Load(); } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "Load module exception: " << e.what()) } #ifdef US_BUILD_SHARED_LIBS Module* moduleA = ModuleRegistry::GetModule("TestModuleA Module"); US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for existing module TestModuleA") US_TEST_CONDITION(moduleA->GetName() == "TestModuleA Module", "Test module name") #endif // Check if libA registered the expected service try { ServiceReference sr1 = mc->GetServiceReference("org.cppmicroservices.TestModuleAService"); US_BASECLASS_NAME* o1 = mc->GetService(sr1); US_TEST_CONDITION(o1 != 0, "Test if service object found"); try { US_TEST_CONDITION(mc->UngetService(sr1), "Test if Service UnGet returns true"); } catch (const std::logic_error le) { US_TEST_FAILED_MSG(<< "UnGetService exception: " << le.what()) } // check the listeners for events std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::LOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::LOADED, moduleA)); #endif std::vector seEvts; seEvts.push_back(ServiceEvent(ServiceEvent::REGISTERED, sr1)); US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } catch (const ServiceException& /*se*/) { US_TEST_FAILED_MSG(<< "test module, expected service not found"); } #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleA->IsLoaded() == true, "Test if loaded correctly"); #endif } // Unload libA and check for correct events void frame030b(ModuleContext* mc, TestModuleListener& listener, SharedLibraryHandle& libA) { #ifdef US_BUILD_SHARED_LIBS Module* moduleA = ModuleRegistry::GetModule("TestModuleA Module"); US_TEST_CONDITION_REQUIRED(moduleA != 0, "Test for non-null module") #endif ServiceReference sr1 = mc->GetServiceReference("org.cppmicroservices.TestModuleAService"); US_TEST_CONDITION(sr1, "Test for valid service reference") try { libA.Unload(); #ifdef US_BUILD_SHARED_LIBS US_TEST_CONDITION(moduleA->IsLoaded() == false, "Test for unloaded state") #endif } catch (const std::exception& e) { US_TEST_FAILED_MSG(<< "UnLoad module exception: " << e.what()) } std::vector pEvts; #ifdef US_BUILD_SHARED_LIBS pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADING, moduleA)); pEvts.push_back(ModuleEvent(ModuleEvent::UNLOADED, moduleA)); #endif std::vector seEvts; seEvts.push_back(ServiceEvent(ServiceEvent::UNREGISTERING, sr1)); US_TEST_CONDITION(listener.CheckListenerEvents(pEvts, seEvts), "Test for unexpected events"); } struct LocalListener { void ServiceChanged(const ServiceEvent) {} }; // Add a service listener with a broken LDAP filter to Get an exception void frame045a(ModuleContext* mc) { LocalListener sListen1; std::string brokenFilter = "A broken LDAP filter"; try { mc->AddServiceListener(&sListen1, &LocalListener::ServiceChanged, brokenFilter); } catch (const std::invalid_argument& /*ia*/) { //assertEquals("InvalidSyntaxException.GetFilter should be same as input string", brokenFilter, ise.GetFilter()); } catch (...) { US_TEST_FAILED_MSG(<< "test module, wrong exception on broken LDAP filter:"); } } int usModuleTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ModuleTest"); + frame02a(); + ModuleContext* mc = GetModuleContext(); TestModuleListener listener(mc); try { mc->AddModuleListener(&listener, &TestModuleListener::ModuleChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "module listener registration failed " << ise.what() ); throw; } try { mc->AddServiceListener(&listener, &TestModuleListener::ServiceChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); throw; } frame005a(mc); frame010a(mc); frame018a(mc); SharedLibraryHandle libA("TestModuleA" #ifndef US_BUILD_SHARED_LIBS , _us_module_activator_instance_TestModuleA #endif ); frame020a(mc, listener, libA); frame030b(mc, listener, libA); frame045a(mc); mc->RemoveModuleListener(&listener, &TestModuleListener::ModuleChanged); mc->RemoveServiceListener(&listener, &TestModuleListener::ServiceChanged); US_TEST_END() } diff --git a/Core/Code/CppMicroServices/test/usServiceListenerTest.cpp b/Core/Code/CppMicroServices/test/usServiceListenerTest.cpp index 0c6632bb6d..b7730db42a 100644 --- a/Core/Code/CppMicroServices/test/usServiceListenerTest.cpp +++ b/Core/Code/CppMicroServices/test/usServiceListenerTest.cpp @@ -1,540 +1,586 @@ /*============================================================================= 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 #include #include #include US_BASECLASS_HEADER #include #include "usTestUtilSharedLibrary.cpp" US_USE_NAMESPACE extern ModuleActivator* _us_module_activator_instance_TestModuleA(); extern ModuleActivator* _us_module_activator_instance_TestModuleA2(); extern ModuleActivator* _us_module_activator_instance_TestModuleSL1(); extern ModuleActivator* _us_module_activator_instance_TestModuleSL3(); extern ModuleActivator* _us_module_activator_instance_TestModuleSL4(); class TestServiceListener { private: friend bool runLoadUnloadTest(const std::string&, int cnt, SharedLibraryHandle&, const std::vector&); const bool checkUsingModules; std::vector events; bool teststatus; ModuleContext* mc; public: TestServiceListener(ModuleContext* mc, bool checkUsingModules = true) : checkUsingModules(checkUsingModules), events(), teststatus(true), mc(mc) {} bool getTestStatus() const { return teststatus; } void clearEvents() { events.clear(); } bool checkEvents(const std::vector& eventTypes) { if (events.size() != eventTypes.size()) { dumpEvents(eventTypes); return false; } for (std::size_t i=0; i < eventTypes.size(); ++i) { if (eventTypes[i] != events[i].GetType()) { dumpEvents(eventTypes); return false; } } return true; } void serviceChanged(const ServiceEvent evt) { events.push_back(evt); US_TEST_OUTPUT( << "ServiceEvent: " << evt ); if (ServiceEvent::UNREGISTERING == evt.GetType()) { ServiceReference sr = evt.GetServiceReference(); // Validate that no module is marked as using the service std::vector usingModules; sr.GetUsingModules(usingModules); if (checkUsingModules && !usingModules.empty()) { teststatus = false; printUsingModules(sr, "*** Using modules (unreg) should be empty but is: "); } // Check if the service can be fetched US_BASECLASS_NAME* service = mc->GetService(sr); sr.GetUsingModules(usingModules); // if (UNREGISTERSERVICE_VALID_DURING_UNREGISTERING) { // In this mode the service shall be obtainable during // unregistration. if (service == 0) { teststatus = false; US_TEST_OUTPUT( << "*** Service should be available to ServiceListener " << "while handling unregistering event." ); } US_TEST_OUTPUT( << "Service (unreg): " << us_service_impl_name(service) ); if (checkUsingModules && usingModules.size() != 1) { teststatus = false; printUsingModules(sr, "*** One using module expected " "(unreg, after getService), found: "); } else { printUsingModules(sr, "Using modules (unreg, after getService): "); } // } else { // // In this mode the service shall NOT be obtainable during // // unregistration. // if (null!=service) { // teststatus = false; // out.print("*** Service should not be available to ServiceListener " // +"while handling unregistering event."); // } // if (checkUsingBundles && null!=usingBundles) { // teststatus = false; // printUsingBundles(sr, // "*** Using bundles (unreg, after getService), " // +"should be null but is: "); // } else { // printUsingBundles(sr, // "Using bundles (unreg, after getService): null"); // } // } mc->UngetService(sr); // Check that the UNREGISTERING service can not be looked up // using the service registry. try { long sid = any_cast(sr.GetProperty(ServiceConstants::SERVICE_ID())); std::stringstream ss; ss << "(" << ServiceConstants::SERVICE_ID() << "=" << sid << ")"; std::list srs = mc->GetServiceReferences("", ss.str()); if (srs.empty()) { US_TEST_OUTPUT( << "ServiceReference for UNREGISTERING service is not" " found in the service registry; ok." ); } else { teststatus = false; US_TEST_OUTPUT( << "*** ServiceReference for UNREGISTERING service," << sr << ", not found in the service registry; fail." ); US_TEST_OUTPUT( << "Found the following Service references:") ; for(std::list::const_iterator sr = srs.begin(); sr != srs.end(); ++sr) { US_TEST_OUTPUT( << (*sr) ); } } } catch (const std::exception& e) { teststatus = false; US_TEST_OUTPUT( << "*** Unexpected excpetion when trying to lookup a" " service while it is in state UNREGISTERING: " << e.what() ); } } } void printUsingModules(const ServiceReference& sr, const std::string& caption) { std::vector usingModules; sr.GetUsingModules(usingModules); US_TEST_OUTPUT( << (caption.empty() ? "Using modules: " : caption) ); for(std::vector::const_iterator module = usingModules.begin(); module != usingModules.end(); ++module) { US_TEST_OUTPUT( << " -" << (*module) ); } } // Print expected and actual service events. void dumpEvents(const std::vector& eventTypes) { std::size_t max = events.size() > eventTypes.size() ? events.size() : eventTypes.size(); US_TEST_OUTPUT( << "Expected event type -- Actual event" ); for (std::size_t i=0; i < max; ++i) { ServiceEvent evt = i < events.size() ? events[i] : ServiceEvent(); if (i < eventTypes.size()) { US_TEST_OUTPUT( << " " << eventTypes[i] << "--" << evt ); } else { US_TEST_OUTPUT( << " " << "- NONE - " << "--" << evt ); } } } }; // end of class ServiceListener bool runLoadUnloadTest(const std::string& name, int cnt, SharedLibraryHandle& target, const std::vector& events) { bool teststatus = true; ModuleContext* mc = GetModuleContext(); for (int i = 0; i < cnt && teststatus; ++i) { TestServiceListener sListen(mc); try { mc->AddServiceListener(&sListen, &TestServiceListener::serviceChanged); //mc->AddServiceListener(MessageDelegate1(&sListen, &ServiceListener::serviceChanged)); } catch (const std::logic_error& ise) { teststatus = false; US_TEST_FAILED_MSG( << "service listener registration failed " << ise.what() << " :" << name << ":FAIL" ); } // Start the test target to get a service published. try { target.Load(); } catch (const std::exception& e) { teststatus = false; US_TEST_FAILED_MSG( << "Failed to load module, got exception: " << e.what() << " + in " << name << ":FAIL" ); } // Stop the test target to get a service unpublished. try { target.Unload(); } catch (const std::exception& e) { teststatus = false; US_TEST_FAILED_MSG( << "Failed to unload module, got exception: " << e.what() << " + in " << name << ":FAIL" ); } if (teststatus && !sListen.checkEvents(events)) { teststatus = false; US_TEST_FAILED_MSG( << "Service listener event notification error :" << name << ":FAIL" ); } try { mc->RemoveServiceListener(&sListen, &TestServiceListener::serviceChanged); teststatus &= sListen.teststatus; sListen.clearEvents(); } catch (const std::logic_error& ise) { teststatus = false; US_TEST_FAILED_MSG( << "service listener removal failed " << ise.what() << " :" << name << ":FAIL" ); } } return teststatus; } +void frameSL02a() +{ + ModuleContext* mc = GetModuleContext(); + + TestServiceListener listener1(mc); + TestServiceListener listener2(mc); + + try + { + mc->RemoveServiceListener(&listener1, &TestServiceListener::serviceChanged); + mc->AddServiceListener(&listener1, &TestServiceListener::serviceChanged); + mc->RemoveServiceListener(&listener2, &TestServiceListener::serviceChanged); + mc->AddServiceListener(&listener2, &TestServiceListener::serviceChanged); + } + catch (const std::logic_error& ise) + { + US_TEST_FAILED_MSG( << "service listener registration failed " << ise.what() + << " : frameSL02a:FAIL" ); + } + + SharedLibraryHandle target("TestModuleA"); + + // Start the test target to get a service published. + try + { + target.Load(); + } + catch (const std::exception& e) + { + US_TEST_FAILED_MSG( << "Failed to load module, got exception: " + << e.what() << " + in frameSL02a:FAIL" ); + } + + std::vector events; + events.push_back(ServiceEvent::REGISTERED); + + US_TEST_CONDITION(listener1.checkEvents(events), "Check first service listener") + US_TEST_CONDITION(listener2.checkEvents(events), "Check second service listener") + + mc->RemoveServiceListener(&listener1, &TestServiceListener::serviceChanged); + mc->RemoveServiceListener(&listener2, &TestServiceListener::serviceChanged); + + target.Unload(); +} + void frameSL05a() { std::vector events; events.push_back(ServiceEvent::REGISTERED); events.push_back(ServiceEvent::UNREGISTERING); SharedLibraryHandle libA("TestModuleA" #ifndef US_BUILD_SHARED_LIBS , _us_module_activator_instance_TestModuleA #endif ); bool testStatus = runLoadUnloadTest("FrameSL05a", 1, libA, events); US_TEST_CONDITION(testStatus, "FrameSL05a") } void frameSL10a() { std::vector events; events.push_back(ServiceEvent::REGISTERED); events.push_back(ServiceEvent::UNREGISTERING); SharedLibraryHandle libA2("TestModuleA2" #ifndef US_BUILD_SHARED_LIBS , _us_module_activator_instance_TestModuleA2 #endif ); bool testStatus = runLoadUnloadTest("FrameSL10a", 1, libA2, events); US_TEST_CONDITION(testStatus, "FrameSL10a") } void frameSL25a() { ModuleContext* mc = GetModuleContext(); TestServiceListener sListen(mc, false); try { mc->AddServiceListener(&sListen, &TestServiceListener::serviceChanged); } catch (const std::logic_error& ise) { US_TEST_OUTPUT( << "service listener registration failed " << ise.what() ); throw; } SharedLibraryHandle libSL1("TestModuleSL1" #ifndef US_BUILD_SHARED_LIBS , _us_module_activator_instance_TestModuleSL1 #endif ); SharedLibraryHandle libSL3("TestModuleSL3" #ifndef US_BUILD_SHARED_LIBS , _us_module_activator_instance_TestModuleSL3 #endif ); SharedLibraryHandle libSL4("TestModuleSL4" #ifndef US_BUILD_SHARED_LIBS , _us_module_activator_instance_TestModuleSL4 #endif ); std::vector expectedServiceEventTypes; // Startup expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // at start of libSL1 expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // FooService at start of libSL4 expectedServiceEventTypes.push_back(ServiceEvent::REGISTERED); // at start of libSL3 // Stop libSL4 expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // FooService at first stop of libSL4 // Shutdown expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // at stop of libSL1 expectedServiceEventTypes.push_back(ServiceEvent::UNREGISTERING); // at stop of libSL3 // Start libModuleTestSL1 to ensure that the Service interface is available. try { US_TEST_OUTPUT( << "Starting libModuleTestSL1: " << libSL1.GetAbsolutePath() ); libSL1.Load(); } catch (const std::exception& e) { US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() ); throw; } // Start libModuleTestSL4 that will require the serivce interface and publish // ctkFooService try { US_TEST_OUTPUT( << "Starting libModuleTestSL4: " << libSL4.GetAbsolutePath() ); libSL4.Load(); } catch (const std::exception& e) { US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() ); throw; } // Start libModuleTestSL3 that will require the serivce interface and get the service try { US_TEST_OUTPUT( << "Starting libModuleTestSL3: " << libSL3.GetAbsolutePath() ); libSL3.Load(); } catch (const std::exception& e) { US_TEST_OUTPUT( << "Failed to load module, got exception: " << e.what() ); throw; } // Check that libSL3 has been notified about the FooService. US_TEST_OUTPUT( << "Check that FooService is added to service tracker in libSL3" ); try { ServiceReference libSL3SR = mc->GetServiceReference("ActivatorSL3"); US_BASECLASS_NAME* libSL3Activator = mc->GetService(libSL3SR); US_TEST_CONDITION_REQUIRED(libSL3Activator, "ActivatorSL3 service != 0"); ModulePropsInterface* propsInterface = dynamic_cast(libSL3Activator); US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface"); ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceAdded"); US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceAdded"); Any serviceAddedField3 = i->second; US_TEST_CONDITION_REQUIRED(!serviceAddedField3.Empty() && any_cast(serviceAddedField3), "libSL3 notified about presence of FooService"); mc->UngetService(libSL3SR); } catch (const ServiceException& se) { US_TEST_FAILED_MSG( << "Failed to get service reference:" << se ); } // Check that libSL1 has been notified about the FooService. US_TEST_OUTPUT( << "Check that FooService is added to service tracker in libSL1" ); try { ServiceReference libSL1SR = mc->GetServiceReference("ActivatorSL1"); US_BASECLASS_NAME* libSL1Activator = mc->GetService(libSL1SR); US_TEST_CONDITION_REQUIRED(libSL1Activator, "ActivatorSL1 service != 0"); ModulePropsInterface* propsInterface = dynamic_cast(libSL1Activator); US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface"); ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceAdded"); US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceAdded"); Any serviceAddedField1 = i->second; US_TEST_CONDITION_REQUIRED(!serviceAddedField1.Empty() && any_cast(serviceAddedField1), "libSL1 notified about presence of FooService"); mc->UngetService(libSL1SR); } catch (const ServiceException& se) { US_TEST_FAILED_MSG( << "Failed to get service reference:" << se ); } // Stop the service provider: libSL4 try { US_TEST_OUTPUT( << "Stop libSL4: " << libSL4.GetAbsolutePath() ); libSL4.Unload(); } catch (const std::exception& e) { US_TEST_OUTPUT( << "Failed to unload module, got exception:" << e.what() ); throw; } // Check that libSL3 has been notified about the removal of FooService. US_TEST_OUTPUT( << "Check that FooService is removed from service tracker in libSL3" ); try { ServiceReference libSL3SR = mc->GetServiceReference("ActivatorSL3"); US_BASECLASS_NAME* libSL3Activator = mc->GetService(libSL3SR); US_TEST_CONDITION_REQUIRED(libSL3Activator, "ActivatorSL3 service != 0"); ModulePropsInterface* propsInterface = dynamic_cast(libSL3Activator); US_TEST_CONDITION_REQUIRED(propsInterface, "Cast to ModulePropsInterface"); ModulePropsInterface::Properties::const_iterator i = propsInterface->GetProperties().find("serviceRemoved"); US_TEST_CONDITION_REQUIRED(i != propsInterface->GetProperties().end(), "Property serviceRemoved"); Any serviceRemovedField3 = i->second; US_TEST_CONDITION(!serviceRemovedField3.Empty() && any_cast(serviceRemovedField3), "libSL3 notified about removal of FooService"); mc->UngetService(libSL3SR); } catch (const ServiceException& se) { US_TEST_FAILED_MSG( << "Failed to get service reference:" << se ); } // Stop libSL1 try { US_TEST_OUTPUT( << "Stop libSL1:" << libSL1.GetAbsolutePath() ); libSL1.Unload(); } catch (const std::exception& e) { US_TEST_OUTPUT( << "Failed to unload module got exception" << e.what() ); throw; } // Stop pSL3 try { US_TEST_OUTPUT( << "Stop libSL3:" << libSL3.GetAbsolutePath() ); libSL3.Unload(); } catch (const std::exception& e) { US_TEST_OUTPUT( << "Failed to unload module got exception" << e.what() ); throw; } // Check service events seen by this class US_TEST_OUTPUT( << "Checking ServiceEvents(ServiceListener):" ); if (!sListen.checkEvents(expectedServiceEventTypes)) { US_TEST_FAILED_MSG( << "Service listener event notification error" ); } US_TEST_CONDITION_REQUIRED(sListen.getTestStatus(), "Service listener checks"); try { mc->RemoveServiceListener(&sListen, &TestServiceListener::serviceChanged); sListen.clearEvents(); } catch (const std::logic_error& ise) { US_TEST_FAILED_MSG( << "service listener removal failed: " << ise.what() ); } } int usServiceListenerTest(int /*argc*/, char* /*argv*/[]) { US_TEST_BEGIN("ServiceListenerTest"); + frameSL02a(); frameSL05a(); frameSL10a(); frameSL25a(); US_TEST_END() }