This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/87-add-additional-svc-tracker-bundle-context-funtions in repository https://gitbox.apache.org/repos/asf/celix.git
commit b5ee724a43a4bfc7def24102f8648b4c58388d8e Author: Pepijn Noltes <pnol...@apache.org> AuthorDate: Sat Feb 10 17:15:29 2024 +0100 gh-509: Add bundle context functions to use underlying svc tracker --- .../src/CelixBundleContextServicesTestSuite.cc | 168 +++++++ libs/framework/include/celix_bundle_context.h | 555 +++++++++++++++------ .../framework/include_deprecated/service_tracker.h | 17 +- libs/framework/src/bundle.c | 5 +- libs/framework/src/bundle_context.c | 261 +++++++--- libs/framework/src/bundle_context_private.h | 29 +- libs/framework/src/service_tracker.c | 30 +- 7 files changed, 821 insertions(+), 244 deletions(-) diff --git a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc index d17b3892..02c49949 100644 --- a/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc +++ b/libs/framework/gtest/src/CelixBundleContextServicesTestSuite.cc @@ -52,6 +52,8 @@ public: fw = celix_frameworkFactory_createFramework(properties); ctx = framework_getContext(fw); + + celix_err_resetErrors(); } ~CelixBundleContextServicesTestSuite() override { @@ -1741,3 +1743,169 @@ TEST_F(CelixBundleContextServicesTestSuite, SetServicesWithTrackerWhenMultipleRe celix_bundleContext_unregisterService(ctx, svcId2); celix_bundleContext_unregisterService(ctx, svcId3); } + + +TEST_F(CelixBundleContextServicesTestSuite, InvalidArgumentsForUseTrackedServicesTest) { + EXPECT_FALSE(celix_bundleContext_useTrackedService(ctx, -1, nullptr, nullptr)); + EXPECT_FALSE(celix_bundleContext_useTrackedService(ctx, 1 /*non existing*/, nullptr, nullptr)); + + EXPECT_EQ(0, celix_bundleContext_useTrackedServices(ctx, -1, nullptr, nullptr)); + EXPECT_EQ(0, celix_bundleContext_useTrackedServices(ctx, 1 /*non existing*/, nullptr, nullptr)); + + celix_tracked_service_use_options_t useOpts{}; + EXPECT_FALSE(celix_bundleContext_useTrackedServiceWithOptions(ctx, -1, &useOpts)); + EXPECT_FALSE(celix_bundleContext_useTrackedServiceWithOptions(ctx, 1 /*non existing*/, &useOpts)); + + EXPECT_EQ(0, celix_bundleContext_useTrackedServicesWithOptions(ctx, -1, &useOpts)); + EXPECT_EQ(0, celix_bundleContext_useTrackedServicesWithOptions(ctx, 1 /*non existing*/, &useOpts)); + + EXPECT_EQ(0, celix_bundleContext_getTrackedServiceCount(ctx, -1)); + EXPECT_EQ(0, celix_bundleContext_getTrackedServiceCount(ctx, 1 /*non existing*/)); + + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceName(ctx, -1)); + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceName(ctx, 1 /*non existing*/)); + + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceFilter(ctx, -1)); + EXPECT_EQ(nullptr, celix_bundleContext_getTrackedServiceFilter(ctx, 1 /*non existing*/)); + + EXPECT_FALSE(celix_bundleContext_isValidTrackerId(ctx, -1)); +} + +TEST_F(CelixBundleContextServicesTestSuite, IsValidTrackerIdTest) { + long trkId = celix_bundleContext_trackServices(ctx, "test", nullptr, nullptr, nullptr); + EXPECT_TRUE(celix_bundleContext_isValidTrackerId(ctx, trkId)); + celix_bundleContext_stopTracker(ctx, trkId); + EXPECT_FALSE(celix_bundleContext_isValidTrackerId(ctx, trkId)); +} + +TEST_F(CelixBundleContextServicesTestSuite, UseTrackedServiceTest) { + // Given 3 foo services with different service properties + celix_properties_t* props1 = celix_properties_create(); + celix_properties_set(props1, "key", "1"); + long svcId1 = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props1); + celix_auto(celix_service_registration_guard_t) guard1 = celix_serviceRegistrationGuard_init(ctx, svcId1); + + celix_properties_t* props2 = celix_properties_create(); + celix_properties_set(props2, "key", "2"); + long svcId2 = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props2); + celix_auto(celix_service_registration_guard_t) guard2 = celix_serviceRegistrationGuard_init(ctx, svcId2); + + celix_properties_t* props3 = celix_properties_create(); + celix_properties_set(props3, "key", "3"); + long svcId3 = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props3); + celix_auto(celix_service_registration_guard_t) guard3 = celix_serviceRegistrationGuard_init(ctx, svcId3); + + // When tracking services for a service name + long trkId = celix_bundleContext_trackServices(ctx, "test", nullptr, nullptr, nullptr); + celix_auto(celix_tracker_guard_t) trkGuard = celix_trackerGuard_init(ctx, trkId); + + // Then the useTrackedService function should be called for each service + struct use_data { + long bndId{0}; + int count{0}; //note atomic not needed because the use function is called in the same thread. + }; + use_data data{}; + data.bndId = celix_bundleContext_getBundleId(ctx); + celix_tracked_service_use_options_t useOpts{}; + useOpts.callbackHandle = (void*)&data; + useOpts.use = [](void* handle, void* svc) { + EXPECT_EQ((void*)0x42, svc); + auto *d = static_cast<use_data*>(handle); + d->count++; + }; + useOpts.useWithProperties = [](void* handle, void* svc, const celix_properties_t* props) { + EXPECT_EQ((void*)0x42, svc); + auto* val = celix_properties_get(props, "key", nullptr); + EXPECT_TRUE(val != nullptr); + auto *d = static_cast<use_data*>(handle); + d->count++; + }; + useOpts.useWithOwner = [](void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* owner) { + EXPECT_EQ((void*)0x42, svc); + auto* val = celix_properties_get(props, "key", nullptr); + EXPECT_TRUE(val != nullptr); + auto *d = static_cast<use_data*>(handle); + d->count++; + EXPECT_EQ(celix_bundle_getId(owner), d->bndId); + }; + auto count = celix_bundleContext_useTrackedServicesWithOptions(ctx, trkId, &useOpts); + EXPECT_EQ(3, count); + EXPECT_EQ(9, data.count); // 3x use, 3x useWithProperties, 3x useWithOwner + + // And the useTrackedServiceWithOptions function should be called a single time + data.count = 0; + bool called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId, &useOpts); + EXPECT_TRUE(called); + EXPECT_EQ(3, data.count); // 1x use, 1x useWithProperties, 1x useWithOwner + + // And the useTrackedServices function should be called 3 times + data.count = 0; + count = celix_bundleContext_useTrackedServices(ctx, trkId, useOpts.callbackHandle, useOpts.use); + EXPECT_EQ(3, count); + EXPECT_EQ(3, data.count); // 3x use + + // And the useTrackedService function should be called a single time + data.count = 0; + called = celix_bundleContext_useTrackedService(ctx, trkId, useOpts.callbackHandle, useOpts.use); + EXPECT_TRUE(called); + EXPECT_EQ(1, data.count); // 1x use + + // When tracking a service with a filter + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "test"; + opts.filter.filter = "(key=1)"; + long trkId2 = celix_bundleContext_trackServicesWithOptions(ctx, &opts); + celix_auto(celix_tracker_guard_t) trkGuard2 = celix_trackerGuard_init(ctx, trkId2); + + // Then the useTrackedServiceWithOption function should be called for the service with the matching filter + useOpts.use = nullptr; + useOpts.useWithOwner = nullptr; + useOpts.useWithProperties = [](void* handle, void* svc, const celix_properties_t* props) { + EXPECT_EQ((void*)0x42, svc); + auto* val = celix_properties_get(props, "key", nullptr); + EXPECT_TRUE(val != nullptr); + EXPECT_STREQ("1", val); + auto *d = static_cast<use_data*>(handle); + d->count++; + }; + data.count = 0; + called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId2, &useOpts); + EXPECT_TRUE(called); + EXPECT_EQ(1, data.count); // 1x useWithProperties + + // And the useTrackedServicesWithOption function should be called a single time + data.count = 0; + count = celix_bundleContext_useTrackedServicesWithOptions(ctx, trkId2, &useOpts); + EXPECT_EQ(1, count); + EXPECT_EQ(1, data.count); // 1x useWithProperties +} + +TEST_F(CelixBundleContextServicesTestSuite, GetTrackedServicesInfoTest) { + //When a service tracker for a specific service name and with a filter + celix_service_tracking_options_t opts{}; + opts.filter.serviceName = "test"; + opts.filter.filter = "(key=1)"; + long trkId = celix_bundleContext_trackServicesWithOptions(ctx, &opts); + celix_auto(celix_tracker_guard_t) trkGuard = celix_trackerGuard_init(ctx, trkId); + + // And a service is registered with a matching service name and filter + celix_properties_t* props = celix_properties_create(); + celix_properties_set(props, "key", "1"); + long svcId = celix_bundleContext_registerService(ctx, (void*)0x42, "test", props); + celix_auto(celix_service_registration_guard_t) svcGuard = celix_serviceRegistrationGuard_init(ctx, svcId); + + // Then the tracked services info should be available + EXPECT_EQ(celix_bundleContext_getTrackedServiceCount(ctx, trkId), 1); + EXPECT_STREQ(celix_bundleContext_getTrackedServiceName(ctx, trkId), "test"); + EXPECT_STREQ(celix_bundleContext_getTrackedServiceFilter(ctx, trkId), "(&(objectClass=test)(key=1))"); + + + // When a tracker for all services is created + long trkId2 = celix_bundleContext_trackServices(ctx, nullptr, nullptr, nullptr, nullptr); + celix_auto(celix_tracker_guard_t) trkGuard2 = celix_trackerGuard_init(ctx, trkId2); + + // Then the tracked services info should be available + EXPECT_EQ(celix_bundleContext_getTrackedServiceCount(ctx, trkId2), 1); + EXPECT_STREQ(celix_bundleContext_getTrackedServiceName(ctx, trkId2), "*"); + EXPECT_TRUE(strstr(celix_bundleContext_getTrackedServiceFilter(ctx, trkId2), "(objectClass=*)") != nullptr); +} diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h index d582fabd..e18992f5 100644 --- a/libs/framework/include/celix_bundle_context.h +++ b/libs/framework/include/celix_bundle_context.h @@ -392,6 +392,197 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_findServiceWithOptions(celix_bun */ CELIX_FRAMEWORK_EXPORT celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts); +/** + * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that + * the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return false immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context + * @param serviceId the service id. + * @param serviceName the service name of the service. Should match with the registered service name of the provided service id (sanity check) + * @param callbackHandle The data pointer, which will be used in the callbacks + * @param use The callback, which will be called when service is retrieved. + * @param bool returns true if a service was found. + */ +CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithId( + celix_bundle_context_t *ctx, + long serviceId, + const char* serviceName /*sanity check*/, + void *callbackHandle, + void (*use)(void *handle, void* svc) +); + +/** + * @brief Use the highest ranking service with the provided service name using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return false immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context + * @param serviceName the required service name. + * @param callbackHandle The data pointer, which will be used in the callbacks + * @param use The callback, which will be called when service is retrieved. + * @return True if a service was found. + */ +CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( + celix_bundle_context_t *ctx, + const char* serviceName, + void *callbackHandle, + void (*use)(void *handle, void *svc) +); + +/** + * @brief Use the services with the provided service name using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return 0 immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context + * @param serviceName the required service name. + * @param callbackHandle The data pointer, which will be used in the callbacks + * @param use The callback, which will be called for every service found. + * @return The number of services found and called + */ +CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServices( + celix_bundle_context_t *ctx, + const char* serviceName, + void *callbackHandle, + void (*use)(void *handle, void *svc) +); + +/** + * @brief Service Use Options used to fine tune which services to use and which callbacks to use. + * + * If multiple use callbacks are set, all set callbacks will be called for every service found. + */ +typedef struct celix_service_use_options { + /** + * @brief The service filter options, used to setup the filter for the service to track. + */ + celix_service_filter_options_t filter CELIX_OPTS_INIT; + + /** + * @brief An optional timeout (in seconds), if > 0 the use service call will block until the timeout is expired or + * when at least one service is found. Note that it will be ignored when use service on the event loop. + * Default (0) + * + * Only applicable when using the celix_bundleContext_useService or + * celix_bundleContext_useServiceWithOptions (use single service calls). + */ + double waitTimeoutInSeconds CELIX_OPTS_INIT; + + /** + * @brief The optional callback pointer used in all the provided callback function (use, useWithProperties, and + * useWithOwner). + */ + void* callbackHandle CELIX_OPTS_INIT; + + /** + * @brief The optional use callback will be called when for every services found conform the service filter options + * - in case of findServices - or only for the highest ranking service found - in case of findService -. + * + * @param handle The callbackHandle pointer as provided in the service tracker options. + * @param svc The service pointer of the highest ranking service. + */ + void (*use)(void* handle, void* svc) CELIX_OPTS_INIT; + + /** + * @brief The optional useWithProperties callback is handled as the use callback, but with the addition that the + * service properties will also be provided to the callback. + */ + void (*useWithProperties)(void* handle, void* svc, const celix_properties_t* props) CELIX_OPTS_INIT; + + /** + * @brief The optional useWithOwner callback is handled as the yse callback, but with the addition that the service + * properties and the bundle owning the service will also be provided to the callback. + */ + void (*useWithOwner)(void* handle, void* svc, const celix_properties_t* props, const celix_bundle_t* svcOwner) + CELIX_OPTS_INIT; + /** + * @brief Call the provided callbacks from the caller thread directly if set, otherwise the callbacks will be called + * from the Celix event loop (most likely indirectly). Note that using blocking service in the Celix event loop is + * generally a bad idea, which should be avoided if possible. + */ +#define CELIX_SERVICE_USE_DIRECT (1) + /** + * @brief Whether "service on demand" pattern is supported when CELIX_SERVICE_USE_DIRECT is set. + * Note that it has no effect in indirect mode, in which case "service on demand" is supported. + */ +#define CELIX_SERVICE_USE_SOD (2) + int flags CELIX_OPTS_INIT; +} celix_service_use_options_t; + +#ifndef __cplusplus +/*! + * @brief C Macro to create a empty celix_service_use_options_t type. + */ +#define CELIX_EMPTY_SERVICE_USE_OPTIONS {.filter.serviceName = NULL, \ + .filter.versionRange = NULL, \ + .filter.filter = NULL, \ + .waitTimeoutInSeconds = 0.0F, \ + .callbackHandle = NULL, \ + .use = NULL, \ + .useWithProperties = NULL, \ + .useWithOwner = NULL, \ + .flags=0} +#endif + +/** + * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found the callback will not be invoked. In such cases, if a non-zero waitTimeoutInSeconds is specified in opts, + * this function will block until the timeout is expired or when at least one service is found, otherwise it will return false immediately. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context. + * @param opts The required options. Note that the serviceName is required. + * @return True if a service was found. + */ +CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( + celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts); + + +/** + * @brief Use the services with the provided service filter options using the provided callback. + * + * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * + * The svc is should only be considered valid during the callback. + * If no service is found, the callback will not be invoked and this function will return 0 immediately. + * Note that waitTimeoutInSeconds in opts has no effect. + * + * This function will block until the callback is finished. As result it is possible to provide callback data from the + * stack. + * + * @param ctx The bundle context. + * @param opts The required options. Note that the serviceName is required. + * @return The number of services found and called + */ +CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( + celix_bundle_context_t *ctx, + const celix_service_use_options_t *opts); + /** * @brief Track the highest ranking service with the provided serviceName. * @@ -615,7 +806,6 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptionsAsync(ce * The tracker options are only using during this call and can safely be freed/reused after this call returns. * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. * - * * @param ctx The bundle context. * @param opts The pointer to the tracker options. * @return the tracker id (>=0) or < 0 if unsuccessful. @@ -623,143 +813,71 @@ CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptionsAsync(ce CELIX_FRAMEWORK_EXPORT long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts); /** - * @brief Stop the tracker with the provided track id. - * - * Could be a service tracker, bundle tracker or service tracker tracker. - * Only works for the trackers owned by the bundle of the bundle context. + * @brief Use the highest ranking service, tracked by the provided tracker id, using the provided callback. * - * The service tracker will be destroyed async on the Celix event loop thread. This means that the function can return - * before the tracker is destroyed. + * If an service is found the use callback will be called on the thread that called this function and when function + * returns the callback is finished. * - * if the doneCallback is not NULL, this will be called when the destruction of the service tracker is done. - * (will be called on the event loop thread). + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return false. * - * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] use The callback, which will be called when service is retrieved. + * @return True if a service was found and the use callback was called. */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTrackerAsync( +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_useTrackedService( celix_bundle_context_t *ctx, long trackerId, - void *doneCallbackData, - void (*doneCallback)(void* doneCallbackData)); - -/** - * @brief Wait for (async) creation of tracker - */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t *ctx, long trackerId); - -/** - * @brief Wait for (async) stopping of tracking. - */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); - -/** - * @brief Stop the tracker with the provided track id. - * - * Could be a service tracker, bundle tracker or service tracker tracker. - * Only works for the trackers owned by the bundle of the bundle context. - * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. - * - * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. - */ -CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); - -/** - * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that - * the targeted service cannot be removed during the callback. - * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return false immediately. - * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. - * - * @param ctx The bundle context - * @param serviceId the service id. - * @param serviceName the service name of the service. Should match with the registered service name of the provided service id (sanity check) - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param use The callback, which will be called when service is retrieved. - * @param bool returns true if a service was found. - */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithId( - celix_bundle_context_t *ctx, - long serviceId, - const char* serviceName /*sanity check*/, void *callbackHandle, void (*use)(void *handle, void* svc) ); /** - * @brief Use the highest ranking service with the provided service name using the provided callback. + * @brief Use the services, tracked by the provided tracker id, using the provided callback. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. - * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return false immediately. + * If 1 or more services is found the use callback will be called for every service found on the thread that called this + * function and when function returns the callbacks are finished. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return 0. * - * @param ctx The bundle context - * @param serviceName the required service name. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param use The callback, which will be called when service is retrieved. - * @return True if a service was found. - */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useService( - celix_bundle_context_t *ctx, - const char* serviceName, - void *callbackHandle, - void (*use)(void *handle, void *svc) -); - -/** - * @brief Use the services with the provided service name using the provided callback. - * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. - * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return 0 immediately. - * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. - * - * @param ctx The bundle context - * @param serviceName the required service name. - * @param callbackHandle The data pointer, which will be used in the callbacks - * @param use The callback, which will be called for every service found. - * @return The number of services found and called + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] use The callback, which will be called for every service found. + * @return The number of services found and therefore the number of times the use callback was called. */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServices( - celix_bundle_context_t *ctx, - const char* serviceName, - void *callbackHandle, - void (*use)(void *handle, void *svc) -); +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)); /** - * @brief Service Use Options used to fine tune which services to use and which callbacks to use. + * @brief Use tracked service options used to configure which use callback to use on the tracked services. + * If multiple use callbacks are set, all set callbacks will be called for every service found. */ -typedef struct celix_service_use_options { - /** - * @brief The service filter options, used to setup the filter for the service to track. - */ - celix_service_filter_options_t filter CELIX_OPTS_INIT; - +typedef struct celix_tracked_service_use_options { /** - * @brief An optional timeout (in seconds), if > 0 the use service call will block until the timeout is expired or - * when at least one service is found. Note that it will be ignored when use service on the event loop. + * @brief An optional timeout (in seconds), if > 0 the use tracked service(s) call will block until the timeout is + * expired or when at least one service is found. Note that it will be ignored when use service on the event loop. * Default (0) + * + * Only applicable when using the celix_bundleContext_useTrackedService or + * celix_bundleContext_useTrackedServiceWithOptions (use single service calls). */ - double waitTimeoutInSeconds CELIX_OPTS_INIT; + double waitTimeoutInSeconds CELIX_OPTS_INIT; /** - * @brief The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc). + * @brief The optional callback pointer used in all the provided callback function (use, useWithProperties and useWithOwner). */ void *callbackHandle CELIX_OPTS_INIT; /** - * @brief The optional use callback will be called when for every services found conform the service filter options - * - in case of findServices - or only for the highest ranking service found - in case of findService -. + * @brief The optional use callback will be called when for every service tracked. * * @param handle The callbackHandle pointer as provided in the service tracker options. * @param svc The service pointer of the highest ranking service. @@ -767,84 +885,197 @@ typedef struct celix_service_use_options { void (*use)(void *handle, void *svc) CELIX_OPTS_INIT; /** - * @brief The optional useWithProperties callback is handled as the use callback, but with the addition that the service properties - * will also be provided to the callback. + * @brief The optional useWithProperties callback will be called when for every service tracked and the + * service properties will also be provided to the callback. */ void (*useWithProperties)(void *handle, void *svc, const celix_properties_t *props) CELIX_OPTS_INIT; /** - * @brief The optional useWithOwner callback is handled as the yse callback, but with the addition that the service properties - * and the bundle owning the service will also be provided to the callback. + * @brief The optional useWithOwner callback will be called when for every service tracked and the + * service properties and the bundle owning the service will also be provided to the callback. */ void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) CELIX_OPTS_INIT; - /** - * @brief Call the provided callbacks from the caller thread directly if set, otherwise the callbacks will be called from the Celix event loop (most likely indirectly). - * Note that using blocking service in the Celix event loop is generally a bad idea, which should be avoided if possible. - */ -#define CELIX_SERVICE_USE_DIRECT (1) - /** - * @brief Whether "service on demand" pattern is supported when CELIX_SERVICE_USE_DIRECT is set. - * Note that it has no effect in indirect mode, in which case "service on demand" is supported. - */ -#define CELIX_SERVICE_USE_SOD (2) - int flags CELIX_OPTS_INIT; -} celix_service_use_options_t; +} celix_tracked_service_use_options_t; #ifndef __cplusplus /*! - * @brief C Macro to create a empty celix_service_use_options_t type. + * @brief C Macro to create a empty celix_tracked_service_use_options_t type. */ -#define CELIX_EMPTY_SERVICE_USE_OPTIONS {.filter.serviceName = NULL, \ - .filter.versionRange = NULL, \ - .filter.filter = NULL, \ - .waitTimeoutInSeconds = 0.0F, \ - .callbackHandle = NULL, \ - .use = NULL, \ - .useWithProperties = NULL, \ - .useWithOwner = NULL, \ - .flags=0} +#define CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS \ + { \ + .waitTimeoutInSeconds = 0.0F, .callbackHandle = NULL, .use = NULL, .useWithProperties = NULL, \ + .useWithOwner = NULL \ + } #endif /** - * @brief Use the highest ranking service satisfying the provided service filter options using the provided callback. + * @brief Use the highest ranking service, tracked by the provided tracker id, using the callbacks in the provided + * options. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * If an service is found the use callbacks will be called on the thread that called this function and when function + * returns the callbacks are finished. * - * The svc is should only be considered valid during the callback. - * If no service is found the callback will not be invoked. In such cases, if a non-zero waitTimeoutInSeconds is specified in opts, - * this function will block until the timeout is expired or when at least one service is found, otherwise it will return false immediately. + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return false. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * @note the field USE_DIRECT in the provided options has no effect with this function. * - * @param ctx The bundle context. - * @param opts The required options. Note that the serviceName is required. - * @return True if a service was found. + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] opts The service use options. + * @return True if a service was found and the use callbacks where called. */ -CELIX_FRAMEWORK_EXPORT bool celix_bundleContext_useServiceWithOptions( - celix_bundle_context_t *ctx, - const celix_service_use_options_t *opts); +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts); +/** + * @brief Use the services, tracked by the provided tracker id, using the callbacks in the provided options. + * + * If 1 or more services is found the use callbacks will be called for every service found on the thread that called + * this function and when function returns the callbacks are finished. + * + * An tracker id < 0 will be silently ignored. + * An invalid (non existing) tracker id >= 0 will be logged and the function will return 0. + * + * @note the field USE_DIRECT in the provided options has no effect with this function. + * + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @param[in] callbackHandle The data pointer, which will be used in the callbacks. + * @param[in] opts The service use options. + * @return The number of services found and therefore the number of times the callbacks in the provided options where + * called. + */ +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_useTrackedServicesWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts); /** - * @brief Use the services with the provided service filter options using the provided callback. + * @brief Get the number of tracked services for the provided tracker id. * - * The Celix framework will ensure that the targeted service cannot be removed during the callback. + * Silently ignore tracker ids < 0 and invalid tracker ids. * - * The svc is should only be considered valid during the callback. - * If no service is found, the callback will not be invoked and this function will return 0 immediately. - * Note that waitTimeoutInSeconds in opts has no effect. + * @param[in] ctx The bundle context. + * @param[in] trackerId The tracker id. + * @return The number of tracked services or 0 if the tracker id is unknown or < 0. + */ +CELIX_FRAMEWORK_EXPORT +size_t celix_bundleContext_getTrackedServiceCount(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Get the service name of the tracked services for the provided tracker id. * - * This function will block until the callback is finished. As result it is possible to provide callback data from the - * stack. + * Silently ignore tracker ids < 0 and invalid tracker ids. * - * @param ctx The bundle context. - * @param opts The required options. Note that the serviceName is required. - * @return The number of services found and called + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return The service name of the tracked services or NULL if the tracker id is unknown or < 0. */ -CELIX_FRAMEWORK_EXPORT size_t celix_bundleContext_useServicesWithOptions( +CELIX_FRAMEWORK_EXPORT +const char* celix_bundleContext_getTrackedServiceName(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Get the service filter of the tracked services for the provided tracker id. + * + * The returned filter is the combination of the service name and the filter from the provided service filter options. + * For example serviceName="foo" and filter="(location=middle)" will result in a filter of + * "(&(objectClass=foo)(location=middle))" + * + * Silently ignore tracker ids < 0 and invalid tracker ids. + * + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return The service filter of the tracked services or NULL if the tracker id is unknown or < 0. + */ +CELIX_FRAMEWORK_EXPORT +const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* ctx, long trackerId); + +/** + * @brief Returns true if the provided tracker id is a tracker id for an existing tracker for the provided bundle + * context. + * @param ctx The bundle context. + * @param trackerId The tracker id. + * @return True if the tracker id is valid. + */ +CELIX_FRAMEWORK_EXPORT +bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId); + +/** + * @brief Stop the tracker with the provided track id. + * + * Could be a service tracker, bundle tracker or service tracker tracker. + * Only works for the trackers owned by the bundle of the bundle context. + * + * The service tracker will be destroyed async on the Celix event loop thread. This means that the function can return + * before the tracker is destroyed. + * + * if the doneCallback is not NULL, this will be called when the destruction of the service tracker is done. + * (will be called on the event loop thread). + * + * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTrackerAsync( celix_bundle_context_t *ctx, - const celix_service_use_options_t *opts); + long trackerId, + void *doneCallbackData, + void (*doneCallback)(void* doneCallbackData)); + +/** + * @brief Wait for (async) creation of tracker + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Wait for (async) stopping of tracking. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Stop the tracker with the provided track id. + * + * Could be a service tracker, bundle tracker or service tracker tracker. + * Only works for the trackers owned by the bundle of the bundle context. + * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead. + * + * Will log a error if the provided tracker id is unknown. Will silently ignore trackerId < 0. + */ +CELIX_FRAMEWORK_EXPORT void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId); + +/** + * @brief Tracker guard. + */ +typedef struct celix_tracker_guard { + celix_bundle_context_t* ctx; + long trackerId; +} celix_tracker_guard_t; + +/** + * @brief Initialize a scope guard for an existing bundle, service or meta tracker. + * @param [in] ctx The bundle context associated with the service registration. + * @param [in] trackerId The tracker id. + * @return An initialized service registration guard. + */ +static CELIX_UNUSED inline celix_tracker_guard_t +celix_trackerGuard_init(celix_bundle_context_t* ctx, long trackerId) { + return (celix_tracker_guard_t) { .ctx = ctx, .trackerId = trackerId }; +} + +/** + * @brief De-initialize a tracker guard. + * Will stop the tracker if the tracker id is >= 0. + * @param [in] trackerGuard A tracker guard + */ +static CELIX_UNUSED inline void celix_trackerGuard_deinit(celix_tracker_guard_t* trackerGuard) { + if (trackerGuard->trackerId >= 0) { + celix_bundleContext_stopTracker(trackerGuard->ctx, trackerGuard->trackerId); + } +} + +CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_tracker_guard_t, celix_trackerGuard_deinit) /** * @brief List the installed and started bundle ids. diff --git a/libs/framework/include_deprecated/service_tracker.h b/libs/framework/include_deprecated/service_tracker.h index 5f75c8ab..cba45a09 100644 --- a/libs/framework/include_deprecated/service_tracker.h +++ b/libs/framework/include_deprecated/service_tracker.h @@ -66,7 +66,7 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t serviceTracker_nrOfTrackedServices(serv /********************************************************************************************************************** ********************************************************************************************************************** - * Updated API + * Updated API. Note API is still deprecated for external usage, the api will become private in the future. ********************************************************************************************************************** **********************************************************************************************************************/ @@ -140,6 +140,21 @@ CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_useServices( void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *owner) ); +/** + * @brief Get the number of tracked services. + */ +CELIX_FRAMEWORK_DEPRECATED_EXPORT size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t *tracker); + +/** + * @brief Get the tracker service name. + */ +CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker); + +/** + * @brief Get the tracker filter. + */ +CELIX_FRAMEWORK_DEPRECATED_EXPORT const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker); + #ifdef __cplusplus } diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c index 6a687696..8eaf7112 100644 --- a/libs/framework/src/bundle.c +++ b/libs/framework/src/bundle.c @@ -583,9 +583,8 @@ void celix_bundle_destroyRegisteredServicesList(celix_array_list_t* list) { celix_array_list_t* celix_bundle_listServiceTrackers(const celix_bundle_t *bnd) { celix_array_list_t* result = celix_arrayList_create(); celixThreadMutex_lock(&bnd->context->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(bnd->context->serviceTrackers); - while (hashMapIterator_hasNext(&iter)) { - celix_bundle_context_service_tracker_entry_t *trkEntry = hashMapIterator_nextValue(&iter); + CELIX_LONG_HASH_MAP_ITERATE(bnd->context->serviceTrackers, iter) { + celix_bundle_context_service_tracker_entry_t *trkEntry = iter.value.ptrValue; if (trkEntry->tracker != NULL) { celix_bundle_service_tracker_list_entry_t *entry = calloc(1, sizeof(*entry)); entry->filter = celix_utils_strdup(trkEntry->tracker->filter); diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c index 0f0bc344..ccc49ddf 100644 --- a/libs/framework/src/bundle_context.c +++ b/libs/framework/src/bundle_context.c @@ -68,10 +68,10 @@ celix_status_t bundleContext_create(framework_pt framework, celix_framework_logg celixThreadMutex_create(&context->mutex, NULL); context->svcRegistrations = celix_arrayList_create(); - context->bundleTrackers = hashMap_create(NULL,NULL,NULL,NULL); - context->serviceTrackers = hashMap_create(NULL,NULL,NULL,NULL); - context->metaTrackers = hashMap_create(NULL,NULL,NULL,NULL); - context->stoppingTrackerEventIds = hashMap_create(NULL,NULL,NULL,NULL); + context->bundleTrackers = celix_longHashMap_create(); + context->serviceTrackers = celix_longHashMap_create(); + context->metaTrackers = celix_longHashMap_create(); + context->stoppingTrackerEventIds = celix_longHashMap_create(); context->nextTrackerId = 1L; *bundle_context = context; @@ -84,20 +84,19 @@ celix_status_t bundleContext_create(framework_pt framework, celix_framework_logg } celix_status_t bundleContext_destroy(bundle_context_pt context) { - celix_status_t status = CELIX_SUCCESS; - - if(context == NULL) { + if (context == NULL) { return CELIX_ILLEGAL_ARGUMENT; } - assert(hashMap_size(context->bundleTrackers) == 0); - hashMap_destroy(context->bundleTrackers, false, false); - assert(hashMap_size(context->serviceTrackers) == 0); - hashMap_destroy(context->serviceTrackers, false, false); - assert(hashMap_size(context->metaTrackers) == 0); - hashMap_destroy(context->metaTrackers, false, false); + + assert(celix_longHashMap_size(context->bundleTrackers) == 0); + celix_longHashMap_destroy(context->bundleTrackers); + assert(celix_longHashMap_size(context->serviceTrackers) == 0); + celix_longHashMap_destroy(context->serviceTrackers); + assert(celix_longHashMap_size(context->metaTrackers) == 0); + celix_longHashMap_destroy(context->metaTrackers); assert(celix_arrayList_size(context->svcRegistrations) == 0); celix_arrayList_destroy(context->svcRegistrations); - hashMap_destroy(context->stoppingTrackerEventIds, false, false); + celix_longHashMap_destroy(context->stoppingTrackerEventIds); celixThreadMutex_destroy(&context->mutex); @@ -108,7 +107,7 @@ celix_status_t bundleContext_destroy(bundle_context_pt context) { } free(context); - return status; + return CELIX_SUCCESS; } void celix_bundleContext_cleanup(celix_bundle_context_t* ctx) { @@ -593,7 +592,7 @@ static long celix_bundleContext_trackBundlesWithOptionsInternal( celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; - hashMap_put(ctx->bundleTrackers, (void*)(entry->trackerId), entry); + celix_longHashMap_put(ctx->bundleTrackers, entry->trackerId, entry); long trackerId = entry->trackerId; celixThreadMutex_unlock(&ctx->mutex); @@ -670,20 +669,24 @@ bool celix_bundleContext_useBundle( return celix_framework_useBundle(ctx->framework, false, bundleId, callbackHandle, use); } -static void bundleContext_cleanupBundleTrackers(bundle_context_t *ctx) { +static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) { module_pt module; - const char *symbolicName; + const char* symbolicName; bundle_getCurrentModule(ctx->bundle, &module); module_getSymbolicName(module, &symbolicName); celix_array_list_t* danglingTrkIds = NULL; celixThreadMutex_lock(&ctx->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(ctx->bundleTrackers); - while (hashMapIterator_hasNext(&iter)) { - long trkId = (long)hashMapIterator_nextKey(&iter); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling bundle tracker with id %li for bundle %s. Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName); - if (danglingTrkIds == NULL) { + CELIX_LONG_HASH_MAP_ITERATE(ctx->bundleTrackers, iter) { + long trkId = iter.key; + fw_log( + ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Dangling bundle tracker with id %li for bundle %s. Add missing 'celix_bundleContext_stopTracker' calls.", + trkId, + symbolicName); + if (!danglingTrkIds) { danglingTrkIds = celix_arrayList_create(); } celix_arrayList_addLong(danglingTrkIds, trkId); @@ -699,20 +702,25 @@ static void bundleContext_cleanupBundleTrackers(bundle_context_t *ctx) { } } -static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx) { +static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) { module_pt module; - const char *symbolicName; + const char* symbolicName; bundle_getCurrentModule(ctx->bundle, &module); module_getSymbolicName(module, &symbolicName); celix_array_list_t* danglingTrkIds = NULL; celixThreadMutex_lock(&ctx->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(ctx->serviceTrackers); - while (hashMapIterator_hasNext(&iter)) { - long trkId = (long)hashMapIterator_nextKey(&iter); - celix_bundle_context_service_tracker_entry_t* entry = hashMap_get(ctx->serviceTrackers, (void*)trkId); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling service tracker with trkId %li, for bundle %s and with filter %s. Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName, entry->tracker->filter); + CELIX_LONG_HASH_MAP_ITERATE(ctx->serviceTrackers, iter) { + long trkId = iter.key; + celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trkId); + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Dangling service tracker with trkId %li, for bundle %s and with filter %s. Add missing " + "'celix_bundleContext_stopTracker' calls.", + trkId, + symbolicName, + entry->tracker->filter); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } @@ -729,20 +737,25 @@ static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx) { } } -static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t *ctx) { +static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) { module_pt module; - const char *symbolicName; + const char* symbolicName; bundle_getCurrentModule(ctx->bundle, &module); module_getSymbolicName(module, &symbolicName); celix_array_list_t* danglingTrkIds = NULL; celixThreadMutex_lock(&ctx->mutex); - hash_map_iterator_t iter = hashMapIterator_construct(ctx->metaTrackers); - while (hashMapIterator_hasNext(&iter)) { - long trkId = (long)hashMapIterator_nextKey(&iter); - celix_bundle_context_service_tracker_tracker_entry_t *entry = hashMap_get(ctx->metaTrackers, (void*)trkId); - fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling meta tracker (service tracker tracker) with trkId %li, for bundle %s and for the services %s. Add missing 'celix_bundleContext_stopTracker' calls.", trkId, symbolicName, entry->serviceName); + CELIX_LONG_HASH_MAP_ITERATE(ctx->metaTrackers, iter) { + long trkId = iter.key; + celix_bundle_context_service_tracker_tracker_entry_t* entry = celix_longHashMap_get(ctx->metaTrackers, trkId); + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Dangling meta tracker (service tracker tracker) with trkId %li, for bundle %s and for the services %s. " + "Add missing 'celix_bundleContext_stopTracker' calls.", + trkId, + symbolicName, + entry->serviceName); if (danglingTrkIds == NULL) { danglingTrkIds = celix_arrayList_create(); } @@ -791,7 +804,7 @@ static void celix_bundleContext_removeBundleTracker(void *data) { celix_bundle_context_bundle_tracker_entry_t *tracker = data; fw_removeBundleListener(tracker->ctx->framework, tracker->ctx->bundle, &tracker->listener); celixThreadMutex_lock(&tracker->ctx->mutex); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); celixThreadMutex_unlock(&tracker->ctx->mutex); free(tracker); } @@ -800,7 +813,7 @@ static void celix_bundleContext_removeServiceTracker(void *data) { celix_bundle_context_service_tracker_entry_t *tracker = data; celix_serviceTracker_destroy(tracker->tracker); celixThreadMutex_lock(&tracker->ctx->mutex); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); celixThreadMutex_unlock(&tracker->ctx->mutex); if (tracker->isFreeFilterNeeded) { free((char*)tracker->opts.filter.serviceName); @@ -814,7 +827,7 @@ static void celix_bundleContext_removeServiceTrackerTracker(void *data) { celix_bundle_context_service_tracker_tracker_entry_t *tracker = data; celix_framework_unregister(tracker->ctx->framework, tracker->ctx->bundle, tracker->serviceId); celixThreadMutex_lock(&tracker->ctx->mutex); - hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId); + celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId); celixThreadMutex_unlock(&tracker->ctx->mutex); free(tracker->serviceName); free(tracker); @@ -833,25 +846,28 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) { + if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) { found = true; - bundleTracker = hashMap_remove(ctx->bundleTrackers, (void *) trackerId); + bundleTracker = celix_longHashMap_get(ctx->bundleTrackers, trackerId); + (void)celix_longHashMap_remove(ctx->bundleTrackers, trackerId); if (!bundleTracker->created && !async) { //note tracker not yet created, so cancel instead of removing bundleTracker->cancelled = true; cancelled = true; } - } else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId)) { found = true; - serviceTracker = hashMap_remove(ctx->serviceTrackers, (void *) trackerId); + serviceTracker = celix_longHashMap_get(ctx->serviceTrackers, trackerId); + (void)celix_longHashMap_remove(ctx->serviceTrackers, trackerId); if (serviceTracker->tracker == NULL && !async) { //note tracker not yet created, so cancel instead of removing serviceTracker->cancelled = true; cancelled = true; } - } else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->metaTrackers, trackerId)) { found = true; - svcTrackerTracker = hashMap_remove(ctx->metaTrackers, (void *) trackerId); + svcTrackerTracker = celix_longHashMap_get(ctx->metaTrackers, trackerId); + (void)celix_longHashMap_remove(ctx->metaTrackers, trackerId); //note because a meta tracker is a service listener hook under waiter, no additional cancel is needed (svc reg will be cancelled) } @@ -885,7 +901,7 @@ static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long } else if (found && async) { //NOTE: for async stopping of tracking we need to ensure we cant wait for the tracker destroy id event. long eventId = celix_framework_nextEventId(ctx->framework); - hashMap_put(ctx->stoppingTrackerEventIds, (void*)trackerId, (void*)eventId); + celix_longHashMap_put(ctx->stoppingTrackerEventIds, trackerId, (void*)eventId); if (bundleTracker != NULL) { celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop tracker", bundleTracker, celix_bundleContext_removeBundleTracker, doneData, doneCallback); @@ -937,25 +953,25 @@ static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* c if (waitForStart) { celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) { + if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) { found = true; - celix_bundle_context_bundle_tracker_entry_t* bundleTracker = hashMap_get(ctx->bundleTrackers, (void *) trackerId); + celix_bundle_context_bundle_tracker_entry_t* bundleTracker = celix_longHashMap_get(ctx->bundleTrackers, trackerId); eventId = bundleTracker->createEventId; - } else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId)) { found = true; - celix_bundle_context_service_tracker_entry_t* serviceTracker = hashMap_get(ctx->serviceTrackers, (void *) trackerId); + celix_bundle_context_service_tracker_entry_t* serviceTracker = celix_longHashMap_get(ctx->serviceTrackers, trackerId); eventId = serviceTracker->createEventId; - } else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) { + } else if (celix_longHashMap_hasKey(ctx->metaTrackers, trackerId)) { found = true; - celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = hashMap_get(ctx->metaTrackers, (void *) trackerId); + celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = celix_longHashMap_get(ctx->metaTrackers, trackerId); svcId = svcTrackerTracker->serviceId; } celixThreadMutex_unlock(&ctx->mutex); } else { celixThreadMutex_lock(&ctx->mutex); - if (hashMap_containsKey(ctx->stoppingTrackerEventIds, (void*)trackerId)) { + if (celix_longHashMap_hasKey(ctx->stoppingTrackerEventIds, trackerId)) { found = true; - eventId = (long)hashMap_get(ctx->stoppingTrackerEventIds, (void*)trackerId); + eventId = celix_longHashMap_getLong(ctx->stoppingTrackerEventIds, trackerId, -1); } celixThreadMutex_unlock(&ctx->mutex); } @@ -1310,7 +1326,7 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; trackerId = entry->trackerId; - hashMap_put(ctx->serviceTrackers, (void *) trackerId, entry); + celix_longHashMap_put(ctx->serviceTrackers, trackerId, entry); celixThreadMutex_unlock(&ctx->mutex); } return trackerId; @@ -1334,7 +1350,7 @@ static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_co celixThreadMutex_lock(&ctx->mutex); entry->trackerId = ctx->nextTrackerId++; long trackerId = entry->trackerId; - hashMap_put(ctx->serviceTrackers, (void *)entry->trackerId, entry); + celix_longHashMap_put(ctx->serviceTrackers, entry->trackerId, entry); celixThreadMutex_unlock(&ctx->mutex); long id = celix_framework_fireGenericEvent(ctx->framework, entry->createEventId, celix_bundle_getId(ctx->bundle), "create service tracker event", entry, celix_bundleContext_createTrackerOnEventLoop, entry, celix_bundleContext_doneCreatingTrackerOnEventLoop); @@ -1355,6 +1371,135 @@ long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *c return celix_bundleContext_trackServicesWithOptionsInternal(ctx, opts, true); } +bool celix_bundleContext_useTrackedService( + celix_bundle_context_t *ctx, + long trackerId, + void *callbackHandle, + void (*use)(void *handle, void* svc) +) { + celix_tracked_service_use_options_t opts = CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS; + opts.callbackHandle = callbackHandle; + opts.use = use; + return celix_bundleContext_useTrackedServiceWithOptions(ctx, trackerId, &opts); +} + +size_t celix_bundleContext_useTrackedServices(celix_bundle_context_t* ctx, + long trackerId, + void* callbackHandle, + void (*use)(void* handle, void* svc)) { + celix_tracked_service_use_options_t opts = CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS; + opts.callbackHandle = callbackHandle; + opts.use = use; + return celix_bundleContext_useTrackedServicesWithOptions(ctx, trackerId, &opts); +} + +/** + * @brief Find a service tracker with the given tracker id. ctx->mutex must be locked. + */ +static celix_service_tracker_t* celix_bundleContext_findServiceTracker(celix_bundle_context_t* ctx, long trackerId) { + if (trackerId < 0) { + return NULL; //silent ignore + } + + celix_bundle_context_service_tracker_entry_t* entry = celix_longHashMap_get(ctx->serviceTrackers, trackerId); + if (!entry) { + fw_log(ctx->framework->logger, + CELIX_LOG_LEVEL_ERROR, + "Cannot use tracked service with tracker id %li, because no tracker with that id is found", + trackerId); + return NULL; + } + return entry->tracker; +} + +static size_t celix_bundleContext_useTrackedServiceWithOptionsInternal(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts, + bool singleUse) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); + if (!trk) { + return 0; + } + + if (singleUse) { + bool called = celix_serviceTracker_useHighestRankingService(trk, + NULL, + opts->waitTimeoutInSeconds, + opts->callbackHandle, + opts->use, + opts->useWithProperties, + opts->useWithOwner); + return called ? 1 : 0; + } else { + return celix_serviceTracker_useServices( + trk, NULL, opts->callbackHandle, opts->use, opts->useWithProperties, opts->useWithOwner); + } +} + +bool celix_bundleContext_useTrackedServiceWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts) { + return celix_bundleContext_useTrackedServiceWithOptionsInternal(ctx, trackerId, opts, true) > 0; +} + +size_t celix_bundleContext_useTrackedServicesWithOptions(celix_bundle_context_t* ctx, + long trackerId, + const celix_tracked_service_use_options_t* opts) { + return celix_bundleContext_useTrackedServiceWithOptionsInternal(ctx, trackerId, opts, false); +} + +void celix_bundleContext_getTrackerInfo(celix_bundle_context_t *ctx, long trackerId, size_t *trackedServiceCount, const char **trackedServiceName, const char **trackedServiceFilter) { + if (trackedServiceCount) { + *trackedServiceCount = 0; + } + if (trackedServiceName) { + *trackedServiceName = NULL; + } + if (trackedServiceFilter) { + *trackedServiceFilter = NULL; + } + + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + celix_service_tracker_t* trk = celix_bundleContext_findServiceTracker(ctx, trackerId); + if (!trk) { + return; + } + + if (trackedServiceCount) { + *trackedServiceCount = celix_serviceTracker_getTrackedServiceCount(trk); + } + if (trackedServiceName) { + *trackedServiceName = celix_serviceTracker_getTrackedServiceName(trk); + } + if (trackedServiceFilter) { + *trackedServiceFilter = celix_serviceTracker_getTrackedServiceFilter(trk); + } +} + +size_t celix_bundleContext_getTrackedServiceCount(celix_bundle_context_t *ctx, long trackerId) { + size_t result = 0; + celix_bundleContext_getTrackerInfo(ctx, trackerId, &result, NULL, NULL); + return result; +} + +const char* celix_bundleContext_getTrackedServiceName(celix_bundle_context_t *ctx, long trackerId) { + const char* result = NULL; + celix_bundleContext_getTrackerInfo(ctx, trackerId, NULL, &result, NULL); + return result; +} + +const char* celix_bundleContext_getTrackedServiceFilter(celix_bundle_context_t* ctx, long trackerId) { + const char* result = NULL; + celix_bundleContext_getTrackerInfo(ctx, trackerId, NULL, NULL, &result); + return result; +} + +bool celix_bundleContext_isValidTrackerId(celix_bundle_context_t* ctx, long trackerId) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&ctx->mutex); + return celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId); +} + long celix_bundleContext_findService(celix_bundle_context_t *ctx, const char *serviceName) { celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS; opts.serviceName = serviceName; @@ -1471,7 +1616,7 @@ long celix_bundleContext_trackServiceTrackersInternal( if (entry->serviceId >= 0) { celixThreadMutex_lock(&ctx->mutex); - hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry); + celix_longHashMap_put(ctx->metaTrackers, entry->trackerId, entry); long trkId = entry->trackerId; celixThreadMutex_unlock(&ctx->mutex); return trkId; diff --git a/libs/framework/src/bundle_context_private.h b/libs/framework/src/bundle_context_private.h index 38482d1d..13c3f350 100644 --- a/libs/framework/src/bundle_context_private.h +++ b/libs/framework/src/bundle_context_private.h @@ -20,13 +20,15 @@ #ifndef BUNDLE_CONTEXT_PRIVATE_H_ #define BUNDLE_CONTEXT_PRIVATE_H_ +#include <stdbool.h> + #include "bundle_context.h" #include "bundle_listener.h" #include "celix_bundle_context.h" #include "celix_log.h" +#include "celix_long_hash_map.h" #include "listener_hook_service.h" #include "service_tracker.h" -#include <stdbool.h> #ifdef __cplusplus extern "C" { @@ -76,17 +78,20 @@ typedef struct celix_bundle_context_service_tracker_tracker_entry { } celix_bundle_context_service_tracker_tracker_entry_t; struct celix_bundle_context { - celix_framework_t *framework; - celix_bundle_t *bundle; - - celix_thread_mutex_t mutex; //protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage) - celix_array_list_t *svcRegistrations; //serviceIds - celix_dependency_manager_t *mng; - long nextTrackerId; - hash_map_t *bundleTrackers; //key = trackerId, value = celix_bundle_context_bundle_tracker_entry_t* - hash_map_t *serviceTrackers; //key = trackerId, value = celix_bundle_context_service_tracker_entry_t* - hash_map_t *metaTrackers; //key = trackerId, value = celix_bundle_context_service_tracker_tracker_entry_t* - hash_map_t *stoppingTrackerEventIds; //key = trackerId, value = eventId for stopping the tracker. Note id are only present if the stop tracking is queued. + celix_framework_t* framework; + celix_bundle_t* bundle; + + celix_thread_mutex_t + mutex; // protects fields below (NOTE/FIXME also used by bundle.c for listing service tracker usage) + celix_array_list_t* svcRegistrations; // serviceIds + celix_dependency_manager_t* mng; + long nextTrackerId; + celix_long_hash_map_t* bundleTrackers; // key = trackerId, value = celix_bundle_context_bundle_tracker_entry_t* + celix_long_hash_map_t* serviceTrackers; // key = trackerId, value = celix_bundle_context_service_tracker_entry_t* + celix_long_hash_map_t* + metaTrackers; // key = trackerId, value = celix_bundle_context_service_tracker_tracker_entry_t* + celix_long_hash_map_t* stoppingTrackerEventIds; // key = trackerId, value = eventId for stopping the tracker. Note + // id are only present if the stop tracking is queued. }; /** diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c index 481bcfce..d3bbbb21 100644 --- a/libs/framework/src/service_tracker.c +++ b/libs/framework/src/service_tracker.c @@ -712,20 +712,19 @@ void celix_serviceTracker_destroy(celix_service_tracker_t *tracker) { } } -static celix_tracked_entry_t* celix_serviceTracker_findHighestRankingService(service_tracker_t *tracker, const char* serviceName) { - //precondition tracker->mutex locked +static celix_tracked_entry_t* celix_serviceTracker_findHighestRankingService(service_tracker_t* tracker, + const char* serviceName) { + // precondition tracker->mutex locked celix_tracked_entry_t* highest = NULL; for (int i = 0; i < celix_arrayList_size(tracker->trackedServices); ++i) { celix_tracked_entry_t* tracked = celix_arrayList_get(tracker->trackedServices, i); - if (serviceName == NULL || (serviceName != NULL && tracked->serviceName != NULL && - celix_utils_stringEquals(tracked->serviceName, serviceName))) { + if (serviceName == NULL || + (tracked->serviceName != NULL && celix_utils_stringEquals(tracked->serviceName, serviceName))) { if (highest == NULL) { highest = tracked; } else { int compare = celix_utils_compareServiceIdsAndRanking( - tracked->serviceId, tracked->serviceRanking, - highest->serviceId, highest->serviceRanking - ); + tracked->serviceId, tracked->serviceRanking, highest->serviceId, highest->serviceRanking); if (compare < 0) { highest = tracked; } @@ -817,4 +816,19 @@ size_t celix_serviceTracker_useServices( tracked_release(entry); } return count; -} \ No newline at end of file +} + +size_t celix_serviceTracker_getTrackedServiceCount(celix_service_tracker_t *tracker) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); + return (size_t) celix_arrayList_size(tracker->trackedServices); +} + +const char* celix_serviceTracker_getTrackedServiceName(celix_service_tracker_t *tracker) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); + return tracker->serviceName; +} + +const char* celix_serviceTracker_getTrackedServiceFilter(celix_service_tracker_t *tracker) { + celix_auto(celix_mutex_lock_guard_t) lck = celixMutexLockGuard_init(&tracker->mutex); + return tracker->filter; +}