| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <celix_api.h> |
| |
| #include "celix_bundle_private.h" |
| #include "celix_constants.h" |
| #include "celix_stdlib_cleanup.h" |
| #include "celix_version_range.h" |
| #include "framework_private.h" |
| #include "listener_hook_service.h" |
| #include "service_reference_private.h" |
| #include "service_registration_private.h" |
| #include "service_registry_private.h" |
| |
| static celix_status_t serviceRegistry_registerServiceInternal(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void * serviceObject, celix_properties_t* dictionary, long reservedId, enum celix_service_type svcType, service_registration_pt *registration); |
| static celix_status_t serviceRegistry_unregisterService(service_registry_pt registry, bundle_pt bundle, service_registration_pt registration); |
| static bool serviceRegistry_tryRemoveServiceReference(service_registry_pt registry, service_reference_pt ref); |
| static celix_status_t serviceRegistry_addHooks(service_registry_pt registry, const char* serviceName, const void *serviceObject, service_registration_pt registration); |
| static celix_status_t serviceRegistry_removeHook(service_registry_pt registry, service_registration_pt registration); |
| static void serviceRegistry_logWarningServiceReferenceUsageCount(service_registry_pt registry, bundle_pt bundle, service_reference_pt ref, size_t usageCount, size_t refCount); |
| static celix_status_t serviceRegistry_getUsingBundles(service_registry_pt registry, service_registration_pt reg, celix_array_list_t** bundles); |
| static celix_status_t serviceRegistry_getServiceReference_internal(service_registry_pt registry, bundle_pt owner, service_registration_pt registration, service_reference_pt *out); |
| static void celix_serviceRegistry_serviceChanged(celix_service_registry_t *registry, celix_service_event_type_t eventType, service_registration_pt registration); |
| static void serviceRegistry_callHooksForListenerFilter(service_registry_pt registry, celix_bundle_t *owner, const celix_filter_t *filter, bool removed); |
| |
| static celix_service_registry_listener_hook_entry_t* celix_createHookEntry(long svcId, celix_listener_hook_service_t*); |
| static void celix_waitAndDestroyHookEntry(celix_service_registry_listener_hook_entry_t *entry); |
| static void celix_increaseCountHook(celix_service_registry_listener_hook_entry_t *entry); |
| static void celix_decreaseCountHook(celix_service_registry_listener_hook_entry_t *entry); |
| |
| static void celix_increaseCountServiceListener(celix_service_registry_service_listener_entry_t *entry); |
| static void celix_decreaseCountServiceListener(celix_service_registry_service_listener_entry_t *entry); |
| static void celix_waitAndDestroyServiceListener(celix_service_registry_service_listener_entry_t *entry); |
| |
| static void celix_increasePendingRegisteredEvent(celix_service_registry_t *registry, long svcId); |
| static void celix_decreasePendingRegisteredEvent(celix_service_registry_t *registry, long svcId); |
| static void celix_waitForPendingRegisteredEvents(celix_service_registry_t *registry, long svcId); |
| |
| celix_service_registry_t* celix_serviceRegistry_create(framework_pt framework) { |
| celix_service_registry_t* reg = calloc(1, sizeof(*reg)); |
| |
| reg->callback.handle = reg; |
| reg->callback.getUsingBundles = (void *)serviceRegistry_getUsingBundles; |
| reg->callback.unregister = (void *) serviceRegistry_unregisterService; |
| reg->callback.tryRemoveServiceReference = (void *) serviceRegistry_tryRemoveServiceReference; |
| |
| reg->serviceRegistrations = hashMap_create(NULL, NULL, NULL, NULL); |
| reg->framework = framework; |
| reg->nextServiceId = 1L; |
| reg->serviceReferences = hashMap_create(NULL, NULL, NULL, NULL); |
| |
| reg->listenerHooks = celix_arrayList_create(); |
| reg->serviceListeners = celix_arrayList_create(); |
| |
| celixThreadMutex_create(®->pendingRegisterEvents.mutex, NULL); |
| celixThreadCondition_init(®->pendingRegisterEvents.cond, NULL); |
| celixThreadRwlock_create(®->lock, NULL); |
| reg->pendingRegisterEvents.map = hashMap_create(NULL, NULL, NULL, NULL); |
| |
| |
| return reg; |
| } |
| |
| void celix_serviceRegistry_destroy(celix_service_registry_t* registry) { |
| celixThreadRwlock_destroy(®istry->lock); |
| |
| //remove service listeners |
| int size = celix_arrayList_size(registry->serviceListeners); |
| if (size > 0) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "%i dangling service listeners\n", size); |
| } |
| for (int i = 0; i < size; ++i) { |
| celix_service_registry_service_listener_entry_t *entry = celix_arrayList_get(registry->serviceListeners, i); |
| celix_decreaseCountServiceListener(entry); |
| celix_waitAndDestroyServiceListener(entry); |
| } |
| celix_arrayList_destroy(registry->serviceListeners); |
| |
| //destroy service registration map |
| size = hashMap_size(registry->serviceRegistrations); |
| if (size > 0) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "%i bundles with dangling service registration\n", size); |
| } |
| hash_map_iterator_t iter = hashMapIterator_construct(registry->serviceRegistrations); |
| while (hashMapIterator_hasNext(&iter)) { |
| hash_map_entry_t *entry = hashMapIterator_nextEntry(&iter); |
| celix_bundle_t *bnd = hashMapEntry_getKey(entry); |
| celix_array_list_t *registrations = hashMapEntry_getValue(entry); |
| for (int i = 0; i < celix_arrayList_size(registrations); ++i) { |
| service_registration_pt reg = celix_arrayList_get(registrations, i); |
| const char *svcName = NULL; |
| serviceRegistration_getServiceName(reg, &svcName); |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Bundle %s (bundle id: %li) still has a %s service registered\n", celix_bundle_getSymbolicName(bnd), celix_bundle_getId(bnd), svcName); |
| } |
| } |
| |
| assert(size == 0); |
| hashMap_destroy(registry->serviceRegistrations, false, false); |
| |
| //destroy service references (double) map); |
| size = hashMap_size(registry->serviceReferences); |
| if (size > 0) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected service references left in the service registry! Nr of references: %i", size); |
| } |
| hashMap_destroy(registry->serviceReferences, false, false); |
| |
| //destroy listener hooks |
| size = celix_arrayList_size(registry->listenerHooks); |
| for (int i = 0; i < celix_arrayList_size(registry->listenerHooks); ++i) { |
| celix_service_registry_listener_hook_entry_t *entry = celix_arrayList_get(registry->listenerHooks, i); |
| celix_waitAndDestroyHookEntry(entry); |
| } |
| celix_arrayList_destroy(registry->listenerHooks); |
| |
| size = hashMap_size(registry->pendingRegisterEvents.map); |
| assert(size == 0); |
| celixThreadMutex_destroy(®istry->pendingRegisterEvents.mutex); |
| celixThreadCondition_destroy(®istry->pendingRegisterEvents.cond); |
| hashMap_destroy(registry->pendingRegisterEvents.map, false, false); |
| |
| free(registry); |
| } |
| |
| celix_status_t serviceRegistry_getRegisteredServices(service_registry_pt registry, bundle_pt bundle, celix_array_list_t** services) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| |
| celix_array_list_t* regs = (celix_array_list_t*) hashMap_get(registry->serviceRegistrations, bundle); |
| if (regs != NULL) { |
| unsigned int i; |
| *services = celix_arrayList_create(); |
| |
| for (i = 0; i < celix_arrayList_size(regs); i++) { |
| service_registration_pt reg = celix_arrayList_get(regs, i); |
| service_reference_pt reference = NULL; |
| //assert(serviceRegistration_isValid(reg)); |
| status = serviceRegistry_getServiceReference_internal(registry, bundle, reg, &reference); |
| if (status == CELIX_SUCCESS) { |
| celix_arrayList_add(*services, reference); |
| } |
| } |
| } |
| |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| framework_logIfError(registry->framework->logger, status, NULL, "Cannot get registered services"); |
| |
| return status; |
| } |
| |
| celix_status_t serviceRegistry_registerService(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void* serviceObject, celix_properties_t* dictionary, service_registration_pt *registration) { |
| return serviceRegistry_registerServiceInternal(registry, bundle, serviceName, serviceObject, dictionary, 0 /*TODO*/, CELIX_PLAIN_SERVICE, registration); |
| } |
| |
| celix_status_t serviceRegistry_registerServiceFactory(service_registry_pt registry, bundle_pt bundle, const char* serviceName, service_factory_pt factory, celix_properties_t* dictionary, service_registration_pt *registration) { |
| return serviceRegistry_registerServiceInternal(registry, bundle, serviceName, (const void *) factory, dictionary, 0 /*TODO*/, CELIX_DEPRECATED_FACTORY_SERVICE, registration); |
| } |
| |
| static celix_status_t serviceRegistry_registerServiceInternal(service_registry_pt registry, bundle_pt bundle, const char* serviceName, const void * serviceObject, celix_properties_t* dictionary, long reservedId, enum celix_service_type svcType, service_registration_pt *registration) { |
| celix_array_list_t* regs; |
| long svcId = reservedId > 0 ? reservedId : celix_serviceRegistry_nextSvcId(registry); |
| |
| celix_properties_setLong(dictionary, CELIX_FRAMEWORK_SERVICE_BUNDLE_ID, celix_bundle_getId(bundle)); |
| |
| if (svcType == CELIX_DEPRECATED_FACTORY_SERVICE) { |
| celix_properties_set(dictionary, CELIX_FRAMEWORK_SERVICE_SCOPE, CELIX_FRAMEWORK_SERVICE_SCOPE_BUNDLE); |
| *registration = serviceRegistration_createServiceFactory(registry->callback, bundle, serviceName, |
| svcId, serviceObject, |
| dictionary); |
| } else if (svcType == CELIX_FACTORY_SERVICE) { |
| celix_properties_set(dictionary, CELIX_FRAMEWORK_SERVICE_SCOPE, CELIX_FRAMEWORK_SERVICE_SCOPE_BUNDLE); |
| *registration = celix_serviceRegistration_createServiceFactory(registry->callback, bundle, serviceName, svcId, (celix_service_factory_t*)serviceObject, dictionary); |
| } else { //plain |
| celix_properties_set(dictionary, CELIX_FRAMEWORK_SERVICE_SCOPE, CELIX_FRAMEWORK_SERVICE_SCOPE_SINGLETON); |
| *registration = serviceRegistration_create(registry->callback, bundle, serviceName, svcId, serviceObject, dictionary); |
| } |
| //printf("Registering service %li with name %s\n", svcId, serviceName); |
| if (strcmp(OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, serviceName) == 0) { |
| serviceRegistry_addHooks(registry, serviceName, serviceObject, *registration); |
| } |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| regs = (celix_array_list_t*) hashMap_get(registry->serviceRegistrations, bundle); |
| if (regs == NULL) { |
| regs = celix_arrayList_create(); |
| hashMap_put(registry->serviceRegistrations, bundle, regs); |
| } |
| celix_arrayList_add(regs, *registration); |
| |
| //update pending register event |
| celix_increasePendingRegisteredEvent(registry, svcId); |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| |
| //NOTE there is a race condition with celix_serviceRegistry_addServiceListener, as result |
| //a REGISTERED event can be triggered twice instead of once. The service tracker can deal with this. |
| //The handling of pending registered events is to ensure that the UNREGISTERING event is always |
| //after the 1 or 2 REGISTERED events. |
| |
| celix_serviceRegistry_serviceChanged(registry, OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED, *registration); |
| //update pending register event count |
| celix_decreasePendingRegisteredEvent(registry, svcId); |
| |
| return CELIX_SUCCESS; |
| } |
| |
| static celix_status_t serviceRegistry_unregisterService(service_registry_pt registry, |
| bundle_pt bundle, |
| service_registration_pt registration) { |
| // array_list_t clients; |
| celix_array_list_t* regs; |
| |
| // fprintf(stderr, "REG: Unregistering service registration with pointer %p\n", registration); |
| |
| long svcId = serviceRegistration_getServiceId(registration); |
| const char* svcName = NULL; |
| serviceRegistration_getServiceName(registration, &svcName); |
| // printf("Unregistering service %li with name %s\n", svcId, svcName); |
| |
| if (strcmp(OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, svcName) == 0) { |
| serviceRegistry_removeHook(registry, registration); |
| } |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| regs = (celix_array_list_t*)hashMap_get(registry->serviceRegistrations, bundle); |
| if (regs != NULL) { |
| celix_arrayList_remove(regs, registration); |
| int size = celix_arrayList_size(regs); |
| if (size == 0) { |
| celix_arrayList_destroy(regs); |
| hashMap_remove(registry->serviceRegistrations, bundle); |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| // check and wait for pending register events |
| celix_waitForPendingRegisteredEvents(registry, svcId); |
| |
| celix_serviceRegistry_serviceChanged(registry, OSGI_FRAMEWORK_SERVICE_EVENT_UNREGISTERING, registration); |
| |
| celixThreadRwlock_readLock(®istry->lock); |
| // invalidate service references |
| hash_map_iterator_pt iter = hashMapIterator_create(registry->serviceReferences); |
| while (hashMapIterator_hasNext(iter)) { |
| hash_map_pt refsMap = hashMapIterator_nextValue(iter); |
| service_reference_pt ref = refsMap != NULL ? hashMap_get(refsMap, (void*)registration->serviceId) : NULL; |
| if (ref != NULL) { |
| serviceReference_invalidateCache(ref); |
| } |
| } |
| hashMapIterator_destroy(iter); |
| serviceRegistration_invalidate(registration); |
| celixThreadRwlock_unlock(®istry->lock); |
| serviceRegistration_release(registration); |
| |
| return CELIX_SUCCESS; |
| } |
| |
| celix_status_t serviceRegistry_getServiceReference(service_registry_pt registry, bundle_pt owner, |
| service_registration_pt registration, service_reference_pt *out) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| if (celixThreadRwlock_writeLock(®istry->lock) == CELIX_SUCCESS) { |
| status = serviceRegistry_getServiceReference_internal(registry, owner, registration, out); |
| celixThreadRwlock_unlock(®istry->lock); |
| } |
| |
| return status; |
| } |
| |
| static celix_status_t serviceRegistry_getServiceReference_internal(service_registry_pt registry, bundle_pt owner, |
| service_registration_pt registration, service_reference_pt *out) { |
| //only call after locked registry RWlock |
| celix_status_t status = CELIX_SUCCESS; |
| service_reference_pt ref = NULL; |
| hash_map_pt references = NULL; |
| |
| references = hashMap_get(registry->serviceReferences, owner); |
| if (references == NULL) { |
| references = hashMap_create(NULL, NULL, NULL, NULL); |
| hashMap_put(registry->serviceReferences, owner, references); |
| } |
| |
| ref = hashMap_get(references, (void*)registration->serviceId); |
| |
| if (ref == NULL) { |
| status = serviceReference_create(registry->callback, owner, registration, &ref); |
| if (status == CELIX_SUCCESS) { |
| hashMap_put(references, (void*)registration->serviceId, ref); |
| } |
| } else { |
| serviceReference_retain(ref); |
| } |
| |
| if (status == CELIX_SUCCESS) { |
| *out = ref; |
| } |
| |
| framework_logIfError(registry->framework->logger, status, NULL, "Cannot create service reference"); |
| |
| |
| return status; |
| } |
| |
| celix_status_t serviceRegistry_getServiceReferences(service_registry_pt registry, |
| bundle_pt owner, |
| const char* serviceName, |
| filter_pt filter, |
| celix_array_list_t** out) { |
| bool matchResult; |
| celix_autoptr(celix_array_list_t) references = celix_arrayList_create(); |
| celix_autoptr(celix_array_list_t) matchingRegistrations = celix_arrayList_create(); |
| |
| if (!references || !matchingRegistrations) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot create service references, out of memory"); |
| return CELIX_ENOMEM; |
| } |
| |
| celix_status_t status = CELIX_SUCCESS; |
| celixThreadRwlock_readLock(®istry->lock); |
| hash_map_iterator_t iterator = hashMapIterator_construct(registry->serviceRegistrations); |
| while (status == CELIX_SUCCESS && hashMapIterator_hasNext(&iterator)) { |
| celix_array_list_t* regs = hashMapIterator_nextValue(&iterator); |
| unsigned int regIdx; |
| for (regIdx = 0; (regs != NULL) && regIdx < celix_arrayList_size(regs); regIdx++) { |
| service_registration_pt registration = celix_arrayList_get(regs, regIdx); |
| celix_properties_t* props = NULL; |
| |
| status = serviceRegistration_getProperties(registration, &props); |
| if (status == CELIX_SUCCESS) { |
| bool matched = false; |
| matchResult = false; |
| if (filter != NULL) { |
| filter_match(filter, props, &matchResult); |
| } |
| if ((serviceName == NULL) && ((filter == NULL) || matchResult)) { |
| matched = true; |
| } else if (serviceName != NULL) { |
| const char* className = NULL; |
| matchResult = false; |
| serviceRegistration_getServiceName(registration, &className); |
| if (filter != NULL) { |
| filter_match(filter, props, &matchResult); |
| } |
| if ((strcmp(className, serviceName) == 0) && ((filter == NULL) || matchResult)) { |
| matched = true; |
| } |
| } |
| if (matched) { |
| // assert(serviceRegistration_isValid(registration)); |
| serviceRegistration_retain(registration); |
| celix_arrayList_add(matchingRegistrations, registration); |
| } |
| } |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| if (status == CELIX_SUCCESS) { |
| unsigned int i; |
| unsigned int size = celix_arrayList_size(matchingRegistrations); |
| |
| for (i = 0; i < size; i += 1) { |
| service_registration_pt reg = celix_arrayList_get(matchingRegistrations, i); |
| service_reference_pt reference = NULL; |
| celix_status_t subStatus = serviceRegistry_getServiceReference(registry, owner, reg, &reference); |
| if (subStatus == CELIX_SUCCESS) { |
| celix_arrayList_add(references, reference); |
| } else { |
| status = CELIX_BUNDLE_EXCEPTION; |
| } |
| serviceRegistration_release(reg); |
| } |
| } |
| |
| if (status == CELIX_SUCCESS) { |
| *out = celix_steal_ptr(references); |
| } else { |
| // TODO: test this branch using malloc mock |
| unsigned int size = celix_arrayList_size(references); |
| for (unsigned int i = 0; i < size; i++) { |
| serviceReference_release(celix_arrayList_get(references, i), NULL); |
| } |
| framework_logIfError(registry->framework->logger, status, NULL, "Cannot get service references"); |
| } |
| |
| return status; |
| } |
| |
| static bool serviceRegistry_tryRemoveServiceReference(service_registry_pt registry, service_reference_pt reference) { |
| size_t refCount = 0; |
| size_t usageCount = 0; |
| service_reference_pt ref = NULL; |
| celixThreadRwlock_writeLock(®istry->lock); |
| serviceReference_getReferenceCount(reference, &refCount); |
| if (refCount == 0) { |
| serviceReference_getUsageCount(reference, &usageCount); |
| if (usageCount > 0) { |
| serviceRegistry_logWarningServiceReferenceUsageCount(registry, reference->referenceOwner, reference, |
| usageCount, refCount); |
| } |
| |
| hash_map_pt refsMap = hashMap_get(registry->serviceReferences, reference->referenceOwner); |
| |
| unsigned long refId = 0UL; |
| |
| if (refsMap != NULL) { |
| hash_map_iterator_t iter = hashMapIterator_construct(refsMap); |
| while (hashMapIterator_hasNext(&iter)) { |
| hash_map_entry_pt entry = hashMapIterator_nextEntry(&iter); |
| refId = (unsigned long) hashMapEntry_getKey(entry); //note could be invalid e.g. freed |
| ref = hashMapEntry_getValue(entry); |
| |
| if (ref == reference) { |
| break; |
| } else { |
| ref = NULL; |
| refId = 0UL; |
| } |
| } |
| } |
| |
| if (ref != NULL) { |
| hashMap_remove(refsMap, (void *) refId); |
| int size = hashMap_size(refsMap); |
| if (size == 0) { |
| hashMap_destroy(refsMap, false, false); |
| hashMap_remove(registry->serviceReferences, reference->referenceOwner); |
| } |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| return refCount == 0 && ref != NULL; |
| |
| } |
| |
| static void serviceRegistry_logWarningServiceReferenceUsageCount(service_registry_pt registry, bundle_pt bundle, service_reference_pt ref, size_t usageCount, size_t refCount) { |
| if (usageCount > 0) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_WARNING, "Service Reference destroyed with usage count is %zu, expected 0. Look for missing bundleContext_ungetService calls.", usageCount); |
| } |
| if (refCount > 0) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_WARNING, "Dangling service reference. Reference count is %zu, expected 1. Look for missing bundleContext_ungetServiceReference calls.", refCount); |
| } |
| |
| if (usageCount > 0 || refCount > 0) { |
| const char* bundle_name = celix_bundle_getSymbolicName(bundle); |
| const char* service_name = "unknown"; |
| const char* bundle_provider_name = "unknown"; |
| if (refCount > 0 && ref != NULL) { |
| serviceReference_getProperty(ref, CELIX_FRAMEWORK_SERVICE_NAME, &service_name); |
| service_registration_pt reg = NULL; |
| bundle_pt providedBnd = NULL; |
| serviceReference_getServiceRegistration(ref, ®); |
| if (reg != NULL) { |
| serviceRegistration_getBundle(reg, &providedBnd); |
| if (providedBnd != NULL) { |
| bundle_provider_name = celix_bundle_getSymbolicName(providedBnd); |
| } |
| } |
| } |
| |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_WARNING, "Previous Dangling service reference warnings caused by bundle '%s', for service '%s', provided by bundle '%s'", bundle_name, service_name, bundle_provider_name); |
| } |
| } |
| |
| |
| celix_status_t serviceRegistry_clearReferencesFor(service_registry_pt registry, bundle_pt bundle) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| |
| hash_map_pt refsMap = hashMap_remove(registry->serviceReferences, bundle); |
| if (refsMap != NULL) { |
| hash_map_iterator_pt iter = hashMapIterator_create(refsMap); |
| while (hashMapIterator_hasNext(iter)) { |
| service_reference_pt ref = hashMapIterator_nextValue(iter); |
| size_t refCount; |
| size_t usageCount; |
| |
| serviceReference_getUsageCount(ref, &usageCount); |
| serviceReference_getReferenceCount(ref, &refCount); |
| serviceRegistry_logWarningServiceReferenceUsageCount(registry, bundle, ref, usageCount, refCount); |
| |
| bool destroyed = false; |
| while (!destroyed) { |
| serviceReference_release(ref, &destroyed); |
| } |
| } |
| hashMapIterator_destroy(iter); |
| hashMap_destroy(refsMap, false, false); |
| } |
| |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| return status; |
| } |
| |
| celix_status_t |
| serviceRegistry_getServicesInUse(service_registry_pt registry, bundle_pt bundle, celix_array_list_t** out) { |
| celix_array_list_t* result = celix_arrayList_create(); |
| |
| // LOCK |
| celixThreadRwlock_readLock(®istry->lock); |
| |
| hash_map_pt refsMap = hashMap_get(registry->serviceReferences, bundle); |
| |
| if (refsMap) { |
| hash_map_iterator_pt iter = hashMapIterator_create(refsMap); |
| while (hashMapIterator_hasNext(iter)) { |
| service_reference_pt ref = hashMapIterator_nextValue(iter); |
| celix_arrayList_add(result, ref); |
| } |
| hashMapIterator_destroy(iter); |
| } |
| |
| // UNLOCK |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| *out = result; |
| |
| return CELIX_SUCCESS; |
| } |
| |
| static celix_status_t serviceRegistry_addHooks(service_registry_pt registry, const char* serviceName, const void* serviceObject, service_registration_pt registration) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| struct listener_hook_info info = { .context = NULL, .filter = NULL, .removed = false }; |
| celix_array_list_t* infos = NULL; |
| celix_array_list_t* listeners = NULL; |
| celix_service_registry_listener_hook_entry_t* entry = NULL; |
| |
| |
| infos = celix_arrayList_create(); |
| celix_arrayList_add(infos, &info); |
| listeners = celix_arrayList_create(); |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| long svcId = serviceRegistration_getServiceId(registration); |
| entry = celix_createHookEntry(svcId, (celix_listener_hook_service_t*)serviceObject); |
| celix_increaseCountHook(entry); |
| celix_arrayList_add(registry->listenerHooks, entry); |
| |
| for (int i = 0; i < celix_arrayList_size(registry->serviceListeners); ++i) { |
| celix_service_registry_service_listener_entry_t *listenerEntry = celix_arrayList_get(registry->serviceListeners, i); |
| celix_increaseCountServiceListener(listenerEntry); |
| celix_arrayList_add(listeners, listenerEntry); |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| for (int i = 0; i < celix_arrayList_size(listeners); ++i) { |
| celix_service_registry_service_listener_entry_t *listenerEntry = celix_arrayList_get(listeners, i); |
| info.context = celix_bundle_getContext(listenerEntry->bundle); |
| info.filter = celix_filter_getFilterString(listenerEntry->filter); |
| entry->hook->added(entry->hook->handle, infos); |
| celix_decreaseCountServiceListener(listenerEntry); |
| } |
| celix_decreaseCountHook(entry); |
| celix_arrayList_destroy(listeners); |
| celix_arrayList_destroy(infos); |
| |
| return status; |
| } |
| |
| static celix_status_t serviceRegistry_removeHook(service_registry_pt registry, service_registration_pt registration) { |
| celix_status_t status = CELIX_SUCCESS; |
| |
| long svcId = serviceRegistration_getServiceId(registration); |
| |
| struct listener_hook_info info = { .context = NULL, .filter = NULL, .removed = true }; |
| celix_array_list_t* infos = NULL; |
| celix_array_list_t* listeners = NULL; |
| celix_service_registry_listener_hook_entry_t *removedEntry = NULL; |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| for (int i = 0; i < celix_arrayList_size(registry->listenerHooks); ++i) { |
| celix_service_registry_listener_hook_entry_t *visit = celix_arrayList_get(registry->listenerHooks, i); |
| if (visit->svcId == svcId) { |
| removedEntry = visit; |
| celix_arrayList_removeAt(registry->listenerHooks, i); |
| celix_increaseCountHook(removedEntry); |
| break; |
| } |
| } |
| if (removedEntry != NULL) { |
| infos = celix_arrayList_create(); |
| celix_arrayList_add(infos, &info); |
| listeners = celix_arrayList_create(); |
| for (int i = 0; i < celix_arrayList_size(registry->serviceListeners); ++i) { |
| celix_service_registry_service_listener_entry_t *listenerEntry = celix_arrayList_get(registry->serviceListeners, i); |
| celix_increaseCountServiceListener(listenerEntry); |
| celix_arrayList_add(listeners, listenerEntry); |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| if (removedEntry != NULL) { |
| for (int i = 0; i < celix_arrayList_size(listeners); ++i) { |
| celix_service_registry_service_listener_entry_t *listenerEntry = celix_arrayList_get(listeners, i); |
| info.context = celix_bundle_getContext(listenerEntry->bundle); |
| info.filter = celix_filter_getFilterString(listenerEntry->filter); |
| removedEntry->hook->removed(removedEntry->hook->handle, infos); |
| celix_decreaseCountServiceListener(listenerEntry); |
| } |
| celix_decreaseCountHook(removedEntry); |
| celix_arrayList_destroy(listeners); |
| celix_arrayList_destroy(infos); |
| |
| celix_waitAndDestroyHookEntry(removedEntry); |
| } |
| |
| return status; |
| } |
| |
| static void serviceRegistry_callHooksForListenerFilter(service_registry_pt registry, celix_bundle_t *owner, const celix_filter_t *filter, bool removed) { |
| celix_bundle_context_t *ctx = celix_bundle_getContext(owner); |
| |
| struct listener_hook_info info; |
| info.context = ctx; |
| info.removed = removed; |
| info.filter = celix_filter_getFilterString(filter); |
| celix_array_list_t *infos = celix_arrayList_create(); |
| celix_arrayList_add(infos, &info); |
| |
| celix_array_list_t *hookRegistrations = celix_arrayList_create(); |
| |
| celixThreadRwlock_readLock(®istry->lock); |
| unsigned size = celix_arrayList_size(registry->listenerHooks); |
| for (int i = 0; i < size; ++i) { |
| celix_service_registry_listener_hook_entry_t* entry = celix_arrayList_get(registry->listenerHooks, i); |
| if (entry != NULL) { |
| celix_increaseCountHook(entry); //increate use count to ensure the hook cannot be removed, untill used |
| celix_arrayList_add(hookRegistrations, entry); |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| for (int i = 0; i < celix_arrayList_size(hookRegistrations); ++i) { |
| celix_service_registry_listener_hook_entry_t* entry = celix_arrayList_get(hookRegistrations, i); |
| if (removed) { |
| entry->hook->removed(entry->hook->handle, infos); |
| } else { |
| entry->hook->added(entry->hook->handle, infos); |
| } |
| celix_decreaseCountHook(entry); //done using hook. decrease count |
| } |
| celix_arrayList_destroy(hookRegistrations); |
| celix_arrayList_destroy(infos); |
| } |
| |
| size_t serviceRegistry_nrOfHooks(service_registry_pt registry) { |
| celixThreadRwlock_readLock(®istry->lock); |
| unsigned size = celix_arrayList_size(registry->listenerHooks); |
| celixThreadRwlock_unlock(®istry->lock); |
| return (size_t) size; |
| } |
| |
| static celix_status_t serviceRegistry_getUsingBundles(service_registry_pt registry, service_registration_pt registration, celix_array_list_t** out) { |
| celix_array_list_t* bundles = NULL; |
| hash_map_iterator_pt iter; |
| |
| bundles = celix_arrayList_create(); |
| if (bundles == NULL) { |
| return CELIX_ENOMEM; |
| } |
| |
| celixThreadRwlock_readLock(®istry->lock); |
| iter = hashMapIterator_create(registry->serviceReferences); |
| while (hashMapIterator_hasNext(iter)) { |
| hash_map_entry_pt entry = hashMapIterator_nextEntry(iter); |
| bundle_pt registrationUser = hashMapEntry_getKey(entry); |
| hash_map_pt regMap = hashMapEntry_getValue(entry); |
| if (hashMap_containsKey(regMap, (void*)registration->serviceId)) { |
| celix_arrayList_add(bundles, registrationUser); |
| } |
| } |
| hashMapIterator_destroy(iter); |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| *out = bundles; |
| |
| return CELIX_SUCCESS; |
| } |
| |
| celix_status_t |
| celix_serviceRegistry_registerServiceFactory( |
| celix_service_registry_t *reg, |
| const celix_bundle_t *bnd, |
| const char *serviceName, |
| celix_service_factory_t *factory, |
| celix_properties_t* props, |
| long reserveId, |
| service_registration_t **registration) { |
| return serviceRegistry_registerServiceInternal(reg, (celix_bundle_t*)bnd, serviceName, (const void *) factory, props, reserveId, CELIX_FACTORY_SERVICE, registration); |
| } |
| |
| celix_status_t |
| celix_serviceRegistry_registerService( |
| celix_service_registry_t *reg, |
| const celix_bundle_t *bnd, |
| const char *serviceName, |
| void* service, |
| celix_properties_t* props, |
| long reserveId, |
| service_registration_t **registration) { |
| return serviceRegistry_registerServiceInternal(reg, (celix_bundle_t*)bnd, serviceName, (const void *) service, props, reserveId, CELIX_PLAIN_SERVICE, registration); |
| } |
| |
| static celix_service_registry_listener_hook_entry_t* celix_createHookEntry(long svcId, celix_listener_hook_service_t *hook) { |
| celix_service_registry_listener_hook_entry_t* entry = calloc(1, sizeof(*entry)); |
| entry->svcId = svcId; |
| entry->hook = hook; |
| celixThreadMutex_create(&entry->mutex, NULL); |
| celixThreadCondition_init(&entry->cond, NULL); |
| return entry; |
| } |
| |
| static void celix_waitAndDestroyHookEntry(celix_service_registry_listener_hook_entry_t *entry) { |
| if (entry != NULL) { |
| celixThreadMutex_lock(&entry->mutex); |
| int waitCount = 0; |
| while (entry->useCount > 0) { |
| celixThreadCondition_timedwaitRelative(&entry->cond, &entry->mutex, 1, 0); //wait for 1 second |
| waitCount += 1; |
| if (waitCount >= 5) { |
| fw_log(celix_frameworkLogger_globalLogger(), CELIX_LOG_LEVEL_WARNING, |
| "Still waiting for service listener hook use count to become zero. Waiting for %i seconds. Use Count is %i, svc id is %li", waitCount, (int)entry->useCount, entry->svcId); |
| } |
| } |
| celixThreadMutex_unlock(&entry->mutex); |
| |
| //done waiting, use count is on 0 |
| celixThreadCondition_destroy(&entry->cond); |
| celixThreadMutex_destroy(&entry->mutex); |
| free(entry); |
| } |
| } |
| static void celix_increaseCountHook(celix_service_registry_listener_hook_entry_t *entry) { |
| if (entry != NULL) { |
| celixThreadMutex_lock(&entry->mutex); |
| entry->useCount += 1; |
| celixThreadCondition_broadcast(&entry->cond); |
| celixThreadMutex_unlock(&entry->mutex); |
| } |
| } |
| static void celix_decreaseCountHook(celix_service_registry_listener_hook_entry_t *entry) { |
| if (entry != NULL) { |
| celixThreadMutex_lock(&entry->mutex); |
| entry->useCount -= 1; |
| celixThreadCondition_broadcast(&entry->cond); |
| celixThreadMutex_unlock(&entry->mutex); |
| } |
| } |
| |
| static void celix_increaseCountServiceListener(celix_service_registry_service_listener_entry_t *entry) { |
| if (entry != NULL) { |
| celixThreadMutex_lock(&entry->mutex); |
| entry->useCount += 1; |
| celixThreadCondition_broadcast(&entry->cond); |
| celixThreadMutex_unlock(&entry->mutex); |
| } |
| } |
| |
| static void celix_decreaseCountServiceListener(celix_service_registry_service_listener_entry_t *entry) { |
| if (entry != NULL) { |
| celixThreadMutex_lock(&entry->mutex); |
| entry->useCount -= 1; |
| celixThreadCondition_broadcast(&entry->cond); |
| celixThreadMutex_unlock(&entry->mutex); |
| } |
| } |
| |
| static inline void celix_waitAndDestroyServiceListener(celix_service_registry_service_listener_entry_t *entry) { |
| celixThreadMutex_lock(&entry->mutex); |
| while (entry->useCount != 0) { |
| celixThreadCondition_wait(&entry->cond, &entry->mutex); |
| } |
| celixThreadMutex_unlock(&entry->mutex); |
| |
| //use count == 0 -> safe to destroy. |
| //destroy |
| celixThreadMutex_destroy(&entry->mutex); |
| celixThreadCondition_destroy(&entry->cond); |
| celix_filter_destroy(entry->filter); |
| free(entry); |
| } |
| |
| char* celix_serviceRegistry_createFilterFor(celix_service_registry_t* registry, const char* serviceName, const char* versionRangeStr, const char* additionalFilterIn) { |
| char* filter = NULL; |
| |
| if (serviceName == NULL) { |
| serviceName = "*"; |
| } |
| |
| celix_autofree char* versionRange = NULL; |
| if (versionRangeStr != NULL) { |
| celix_autoptr(celix_version_range_t) range = celix_versionRange_parse(versionRangeStr); |
| if(range == NULL) { |
| celix_framework_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, |
| "Error incorrect version range."); |
| return NULL; |
| } |
| versionRange = celix_versionRange_createLDAPFilter(range, CELIX_FRAMEWORK_SERVICE_VERSION); |
| if (versionRange == NULL) { |
| celix_framework_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, |
| "Error creating LDAP filter."); |
| return NULL; |
| } |
| } |
| |
| //setting filter |
| if (additionalFilterIn != NULL && versionRange != NULL) { |
| asprintf(&filter, "(&(%s=%s)%s%s)", CELIX_FRAMEWORK_SERVICE_NAME, serviceName, versionRange, additionalFilterIn); |
| } else if (versionRange != NULL) { |
| asprintf(&filter, "(&(%s=%s)%s)", CELIX_FRAMEWORK_SERVICE_NAME, serviceName, versionRange); |
| } else if (additionalFilterIn != NULL) { |
| asprintf(&filter, "(&(%s=%s)%s)", CELIX_FRAMEWORK_SERVICE_NAME, serviceName, additionalFilterIn); |
| } else { |
| asprintf(&filter, "(&(%s=%s))", CELIX_FRAMEWORK_SERVICE_NAME, serviceName); |
| } |
| |
| return filter; |
| } |
| |
| static int celix_serviceRegistry_compareRegistrations(celix_array_list_entry_t a, celix_array_list_entry_t b) { |
| const service_registration_t* regA = a.voidPtrVal; |
| const service_registration_t* regB = b.voidPtrVal; |
| |
| celix_properties_t* propsA = NULL; |
| celix_properties_t* propsB = NULL; |
| serviceRegistration_getProperties((service_registration_t*)regA, &propsA); |
| serviceRegistration_getProperties((service_registration_t*)regB, &propsB); |
| |
| long servIdA = celix_properties_getAsLong(propsA, CELIX_FRAMEWORK_SERVICE_ID, 0); |
| long servIdB = celix_properties_getAsLong(propsB, CELIX_FRAMEWORK_SERVICE_ID, 0); |
| |
| long servRankingA = celix_properties_getAsLong(propsA, CELIX_FRAMEWORK_SERVICE_RANKING, 0); |
| long servRankingB = celix_properties_getAsLong(propsB, CELIX_FRAMEWORK_SERVICE_RANKING, 0); |
| |
| return celix_utils_compareServiceIdsAndRanking(servIdA, servRankingA, servIdB, servRankingB); |
| } |
| |
| celix_array_list_t* celix_serviceRegistry_findServices( |
| celix_service_registry_t* registry, |
| const char* filterStr) { |
| |
| celix_autoptr(celix_filter_t) filter = celix_filter_create(filterStr); |
| if (filter == NULL) { |
| celix_framework_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, |
| "Error incorrect filter."); |
| return NULL; |
| } |
| |
| celix_array_list_t *result = celix_arrayList_create(); |
| celix_array_list_t* matchedRegistrations = celix_arrayList_create(); |
| |
| celixThreadRwlock_readLock(®istry->lock); |
| |
| hash_map_iterator_t iter = hashMapIterator_construct(registry->serviceRegistrations); |
| while (hashMapIterator_hasNext(&iter)) { |
| celix_array_list_t *regs = hashMapIterator_nextValue(&iter); |
| for (int i = 0; i < celix_arrayList_size(regs); ++i) { |
| service_registration_t *reg = celix_arrayList_get(regs, i); |
| celix_properties_t* svcProps = NULL; |
| serviceRegistration_getProperties(reg, &svcProps); |
| if (svcProps != NULL && celix_filter_match(filter, svcProps)) { |
| celix_arrayList_add(matchedRegistrations, reg); |
| } |
| } |
| } |
| |
| //sort matched registration and add the svc id to the result list. |
| if (celix_arrayList_size(matchedRegistrations) > 1) { |
| celix_arrayList_sortEntries(matchedRegistrations, celix_serviceRegistry_compareRegistrations); |
| } |
| for (int i = 0; i < celix_arrayList_size(matchedRegistrations); ++i) { |
| service_registration_t* reg = celix_arrayList_get(matchedRegistrations, i); |
| celix_arrayList_addLong(result, serviceRegistration_getServiceId(reg)); |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| celix_arrayList_destroy(matchedRegistrations); |
| return result; |
| } |
| |
| |
| celix_array_list_t* celix_serviceRegistry_listServiceIdsForOwner(celix_service_registry_t* registry, long bndId) { |
| celix_array_list_t *result = celix_arrayList_create(); |
| celixThreadRwlock_readLock(®istry->lock); |
| celix_bundle_t *bundle = framework_getBundleById(registry->framework, bndId); |
| celix_array_list_t *registrations = bundle != NULL ? hashMap_get(registry->serviceRegistrations, bundle) : NULL; |
| if (registrations != NULL) { |
| for (int i = 0; i < celix_arrayList_size(registrations); ++i) { |
| service_registration_t *reg = celix_arrayList_get(registrations, i); |
| long svcId = serviceRegistration_getServiceId(reg); |
| celix_arrayList_addLong(result, svcId); |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| return result; |
| } |
| |
| bool celix_serviceRegistry_getServiceInfo( |
| celix_service_registry_t* registry, |
| long svcId, |
| long bndId, |
| char **outServiceName, |
| celix_properties_t **outServiceProperties, |
| bool *outIsFactory) { |
| bool found = false; |
| |
| celixThreadRwlock_readLock(®istry->lock); |
| celix_bundle_t *bundle = framework_getBundleById(registry->framework, bndId); |
| celix_array_list_t *registrations = bundle != NULL ? hashMap_get(registry->serviceRegistrations, bundle) : NULL; |
| if (registrations != NULL) { |
| for (int i = 0; i < celix_arrayList_size(registrations); ++i) { |
| service_registration_t *reg = celix_arrayList_get(registrations, i); |
| if (svcId == serviceRegistration_getServiceId(reg)) { |
| found = true; |
| if (outServiceName != NULL) { |
| const char *s = NULL; |
| serviceRegistration_getServiceName(reg, &s); |
| *outServiceName = celix_utils_strdup(s); |
| } |
| if (outServiceProperties != NULL) { |
| celix_properties_t *p = NULL; |
| serviceRegistration_getProperties(reg, &p); |
| *outServiceProperties = celix_properties_copy(p); |
| } |
| if (outIsFactory != NULL) { |
| *outIsFactory = serviceRegistration_isFactoryService(reg); |
| } |
| break; |
| } |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| return found; |
| } |
| |
| celix_status_t celix_serviceRegistry_addServiceListener(celix_service_registry_t *registry, celix_bundle_t *bundle, const char *stringFilter, celix_service_listener_t *listener) { |
| |
| celix_filter_t *filter = NULL; |
| if (stringFilter != NULL) { |
| filter = celix_filter_create(stringFilter); |
| if (filter == NULL) { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot add service listener filter '%s' is invalid", stringFilter); |
| celix_framework_logTssErrors(registry->framework->logger, CELIX_LOG_LEVEL_ERROR); |
| return CELIX_ILLEGAL_ARGUMENT; |
| } |
| } |
| |
| celix_service_registry_service_listener_entry_t *entry = calloc(1, sizeof(*entry)); |
| entry->bundle = bundle; |
| entry->filter = filter; |
| entry->listener = listener; |
| entry->useCount = 1; //new entry -> count on 1 |
| celixThreadMutex_create(&entry->mutex, NULL); |
| celixThreadCondition_init(&entry->cond, NULL); |
| |
| celix_array_list_t *references = celix_arrayList_create(); |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| celix_arrayList_add(registry->serviceListeners, entry); //use count 1 |
| |
| //find already registered services |
| hash_map_iterator_t iter = hashMapIterator_construct(registry->serviceRegistrations); |
| while (hashMapIterator_hasNext(&iter)) { |
| celix_array_list_t *regs = (celix_array_list_t*) hashMapIterator_nextValue(&iter); |
| for (int regIdx = 0; (regs != NULL) && regIdx < celix_arrayList_size(regs); ++regIdx) { |
| service_registration_pt registration = celix_arrayList_get(regs, regIdx); |
| celix_properties_t* props = NULL; |
| serviceRegistration_getProperties(registration, &props); |
| if (celix_filter_match(filter, props)) { |
| long svcId = serviceRegistration_getServiceId(registration); |
| service_reference_pt ref = NULL; |
| serviceRegistry_getServiceReference_internal(registry, bundle, registration, &ref); |
| celix_arrayList_add(references, ref); |
| //update pending register event count |
| celix_increasePendingRegisteredEvent(registry, svcId); |
| } |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| //NOTE there is a race condition with serviceRegistry_registerServiceInternal, as result |
| //a REGISTERED event can be triggered twice instead of once. The service tracker can deal with this. |
| //The handling of pending registered events is to ensure that the UNREGISTERING event is always |
| //after the 1 or 2 REGISTERED events. |
| |
| for (int i = 0; i < celix_arrayList_size(references); ++i) { |
| service_reference_pt ref = celix_arrayList_get(references, i); |
| long svcId = serviceReference_getServiceId(ref); |
| celix_service_event_t event; |
| event.reference = ref; |
| event.type = OSGI_FRAMEWORK_SERVICE_EVENT_REGISTERED; |
| listener->serviceChanged(listener->handle, &event); |
| serviceReference_release(ref, NULL); |
| //update pending register event count |
| celix_decreasePendingRegisteredEvent(registry, svcId); |
| } |
| celix_arrayList_destroy(references); |
| |
| serviceRegistry_callHooksForListenerFilter(registry, bundle, entry->filter, false); |
| |
| celix_decreaseCountServiceListener(entry); //use count decreased, can be 0 |
| return CELIX_SUCCESS; |
| } |
| |
| celix_status_t celix_serviceRegistry_removeServiceListener(celix_service_registry_t *registry, celix_service_listener_t *listener) { |
| celix_service_registry_service_listener_entry_t *entry = NULL; |
| |
| celixThreadRwlock_writeLock(®istry->lock); |
| for (int i = 0; i < celix_arrayList_size(registry->serviceListeners); ++i) { |
| celix_service_registry_service_listener_entry_t *visit = celix_arrayList_get(registry->serviceListeners, i); |
| if (visit->listener == listener) { |
| entry = visit; |
| celix_arrayList_removeAt(registry->serviceListeners, i); |
| break; |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| if (entry != NULL) { |
| serviceRegistry_callHooksForListenerFilter(registry, entry->bundle, entry->filter, true); |
| celix_waitAndDestroyServiceListener(entry); |
| } else { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot remove service listener, listener not found"); |
| return CELIX_ILLEGAL_ARGUMENT; |
| } |
| return CELIX_SUCCESS; |
| } |
| |
| static void celix_serviceRegistry_serviceChanged(celix_service_registry_t *registry, celix_service_event_type_t eventType, service_registration_pt registration) { |
| celix_service_registry_service_listener_entry_t *entry; |
| |
| celix_array_list_t* retainedEntries = celix_arrayList_create(); |
| celix_array_list_t* matchedEntries = celix_arrayList_create(); |
| |
| celixThreadRwlock_readLock(®istry->lock); |
| for (int i = 0; i < celix_arrayList_size(registry->serviceListeners); ++i) { |
| entry = celix_arrayList_get(registry->serviceListeners, i); |
| celix_arrayList_add(retainedEntries, entry); |
| celix_increaseCountServiceListener(entry); //ensure that use count > 0, so that the listener cannot be destroyed until all pending event are handled. |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| for (int i = 0; i < celix_arrayList_size(retainedEntries); ++i) { |
| entry = celix_arrayList_get(retainedEntries, i); |
| int matched = 0; |
| celix_properties_t *props = NULL; |
| bool matchResult = false; |
| serviceRegistration_getProperties(registration, &props); |
| if (entry->filter != NULL) { |
| filter_match(entry->filter, props, &matchResult); |
| } |
| matched = (entry->filter == NULL) || matchResult; |
| if (matched) { |
| celix_arrayList_add(matchedEntries, entry); |
| } else { |
| celix_decreaseCountServiceListener(entry); //Not a match -> release entry |
| } |
| } |
| celix_arrayList_destroy(retainedEntries); |
| |
| /* |
| * TODO FIXME, A deadlock can happen when (e.g.) a service is deregistered, triggering this fw_serviceChanged and |
| * one of the matching service listener callbacks tries to remove an other matched service listener. |
| * The remove service listener will call the listener_waitForDestroy and the fw_serviceChanged part keeps the |
| * usageCount on > 0. |
| * |
| * Not sure how to prevent/handle this. |
| */ |
| |
| for (int i = 0; i < celix_arrayList_size(matchedEntries); ++i) { |
| entry = celix_arrayList_get(matchedEntries, i); |
| service_reference_pt reference = NULL; |
| celix_service_event_t event; |
| serviceRegistry_getServiceReference(registry, entry->bundle, registration, &reference); |
| event.type = eventType; |
| event.reference = reference; |
| entry->listener->serviceChanged(entry->listener->handle, &event); |
| serviceReference_release(reference, NULL); |
| celix_decreaseCountServiceListener(entry); //decrease usage, so that the listener can be destroyed (if use count is now 0) |
| } |
| celix_arrayList_destroy(matchedEntries); |
| } |
| |
| |
| static void celix_increasePendingRegisteredEvent(celix_service_registry_t *registry, long svcId) { |
| celixThreadMutex_lock(®istry->pendingRegisterEvents.mutex); |
| long count = (long)hashMap_get(registry->pendingRegisterEvents.map, (void*)svcId); |
| count += 1; |
| hashMap_put(registry->pendingRegisterEvents.map, (void*)svcId, (void*)count); |
| celixThreadMutex_unlock(®istry->pendingRegisterEvents.mutex); |
| } |
| |
| static void celix_decreasePendingRegisteredEvent(celix_service_registry_t *registry, long svcId) { |
| celixThreadMutex_lock(®istry->pendingRegisterEvents.mutex); |
| long count = (long)hashMap_get(registry->pendingRegisterEvents.map, (void*)svcId); |
| assert(count >= 1); |
| count -= 1; |
| if (count > 0) { |
| hashMap_put(registry->pendingRegisterEvents.map, (void *)svcId, (void *)count); |
| } else { |
| hashMap_remove(registry->pendingRegisterEvents.map, (void*)svcId); |
| } |
| celixThreadCondition_signal(®istry->pendingRegisterEvents.cond); |
| celixThreadMutex_unlock(®istry->pendingRegisterEvents.mutex); |
| } |
| |
| static void celix_waitForPendingRegisteredEvents(celix_service_registry_t *registry, long svcId) { |
| celixThreadMutex_lock(®istry->pendingRegisterEvents.mutex); |
| long count = (long)hashMap_get(registry->pendingRegisterEvents.map, (void*)svcId); |
| while (count > 0) { |
| celixThreadCondition_wait(®istry->pendingRegisterEvents.cond, ®istry->pendingRegisterEvents.mutex); |
| count = (long)hashMap_get(registry->pendingRegisterEvents.map, (void*)svcId); |
| } |
| celixThreadMutex_unlock(®istry->pendingRegisterEvents.mutex); |
| } |
| |
| long celix_serviceRegistry_nextSvcId(celix_service_registry_t* registry) { |
| long scvId = __atomic_fetch_add(®istry->nextServiceId, 1, __ATOMIC_RELAXED); |
| return scvId; |
| } |
| |
| bool celix_serviceRegistry_isServiceRegistered(celix_service_registry_t* reg, long serviceId) { |
| bool isRegistered = false; |
| if (serviceId >= 0) { |
| celixThreadRwlock_readLock(®->lock); |
| hash_map_iterator_t iter = hashMapIterator_construct(reg->serviceRegistrations); |
| while (!isRegistered && hashMapIterator_hasNext(&iter)) { |
| celix_array_list_t *regs = hashMapIterator_nextValue(&iter); |
| for (int i = 0; i < celix_arrayList_size(regs); ++i) { |
| service_registration_t* r = celix_arrayList_get(regs, i); |
| if (serviceId == serviceRegistration_getServiceId(r)) { |
| isRegistered = true; |
| break; |
| } |
| } |
| } |
| celixThreadRwlock_unlock(®->lock); |
| } |
| return isRegistered; |
| } |
| |
| void celix_serviceRegistry_unregisterService(celix_service_registry_t* registry, celix_bundle_t* bnd, long serviceId) { |
| service_registration_t *reg = NULL; |
| celixThreadRwlock_readLock(®istry->lock); |
| celix_array_list_t* registrations = hashMap_get(registry->serviceRegistrations, (void*)bnd); |
| if (registrations != NULL) { |
| for (int i = 0; i < celix_arrayList_size(registrations); ++i) { |
| service_registration_t *entry = celix_arrayList_get(registrations, i); |
| if (serviceRegistration_getServiceId(entry) == serviceId) { |
| reg = entry; |
| serviceRegistration_retain(reg); // protect against concurrently unregistering the same serviceId multiple times |
| break; |
| } |
| } |
| } |
| celixThreadRwlock_unlock(®istry->lock); |
| |
| if (reg != NULL) { |
| serviceRegistration_unregister(reg); |
| serviceRegistration_release(reg); |
| } else { |
| fw_log(registry->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot unregister service for service id %li. This id is not present or owned by the provided bundle (bnd id %li)", serviceId, celix_bundle_getId(bnd)); |
| } |
| } |