blob: 0cd0d621e7ef7adc49c141f4f83ba8ba919ce6ff [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <utils.h>
#include <assert.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdbool.h>
#include "bundle_context_private.h"
#include "celix_bundle_private.h"
#include "celix_array_list.h"
#include "celix_bundle.h"
#include "celix_constants.h"
#include "celix_convert_utils.h"
#include "celix_dependency_manager.h"
#include "celix_log.h"
#include "celix_module.h"
#include "celix_utils.h"
#include "dm_dependency_manager_impl.h"
#include "framework_private.h"
#include "service_reference_private.h"
#include "service_tracker.h"
#include "service_tracker_private.h"
#define TRACKER_WARN_THRESHOLD_SEC 5
static celix_status_t bundleContext_bundleChanged(void* listenerSvc, bundle_event_t* event);
static void bundleContext_cleanupBundleTrackers(bundle_context_t *ct);
static void bundleContext_cleanupServiceTrackers(bundle_context_t *ctx);
static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t *ctx);
static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx);
static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts, bool async);
celix_status_t bundleContext_create(framework_pt framework, celix_framework_logger_t* logger, bundle_pt bundle, bundle_context_pt *bundle_context) {
celix_status_t status = CELIX_SUCCESS;
bundle_context_pt context = NULL;
if (*bundle_context != NULL && framework == NULL && bundle == NULL) {
status = CELIX_ILLEGAL_ARGUMENT;
} else {
context = malloc(sizeof(*context));
if (!context) {
status = CELIX_ENOMEM;
} else {
context->framework = framework;
context->bundle = bundle;
context->mng = NULL;
celixThreadRwlock_create(&context->lock, NULL);
context->svcRegistrations = celix_arrayList_create();
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;
}
}
framework_logIfError(logger, status, NULL, "Failed to create context");
return status;
}
celix_status_t bundleContext_destroy(bundle_context_pt context) {
if (context == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
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);
celix_longHashMap_destroy(context->stoppingTrackerEventIds);
celixThreadRwlock_destroy(&context->lock);
if (context->mng != NULL) {
celix_dependencyManager_removeAllComponents(context->mng);
celix_private_dependencyManager_destroy(context->mng);
context->mng = NULL;
}
free(context);
return CELIX_SUCCESS;
}
void celix_bundleContext_cleanup(celix_bundle_context_t* ctx) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_TRACE,
"Cleaning up bundle context `%s` (id=%li)",
celix_bundle_getSymbolicName(ctx->bundle),
celix_bundle_getId(ctx->bundle));
celix_framework_cleanupScheduledEvents(ctx->framework, celix_bundle_getId(ctx->bundle));
// NOTE not perfect, because stopping of registrations/tracker when the activator is destroyed can lead to
// segfault. but at least we can try to warn the bundle implementer that some cleanup is missing.
bundleContext_cleanupBundleTrackers(ctx);
bundleContext_cleanupServiceTrackers(ctx);
bundleContext_cleanupServiceTrackerTrackers(ctx);
bundleContext_cleanupServiceRegistration(ctx);
}
celix_status_t bundleContext_getBundle(bundle_context_pt context, bundle_pt *out) {
if (context == NULL || out == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
*out = celix_bundleContext_getBundle(context);
return CELIX_SUCCESS;
}
celix_status_t bundleContext_getFramework(bundle_context_pt context, framework_pt *framework) {
if (context == NULL || framework == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
*framework = context->framework;
return CELIX_SUCCESS;
}
celix_status_t bundleContext_installBundle(bundle_context_pt context, const char * location, bundle_pt *bundle) {
return bundleContext_installBundle2(context, location, NULL, bundle);
}
celix_status_t bundleContext_installBundle2(bundle_context_pt context,
const char* location,
const char* inputFile __attribute__((unused)),
bundle_pt* bundle) {
celix_status_t status = CELIX_SUCCESS;
long id = -1L;
if (context == NULL || location == NULL || bundle == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
status = celix_framework_installBundleInternal(context->framework, location, &id);
if (status == CELIX_SUCCESS) {
*bundle = framework_getBundleById(context->framework, id);
}
return status;
}
celix_status_t bundleContext_registerService(bundle_context_pt context, const char * serviceName, const void * svcObj,
celix_properties_t* properties, service_registration_pt *service_registration) {
if (context == NULL || service_registration == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
long bndId = celix_bundle_getId(context->bundle);
return fw_registerService(context->framework, service_registration, bndId, serviceName, svcObj, properties);
}
celix_status_t bundleContext_registerServiceFactory(bundle_context_pt context, const char * serviceName, service_factory_pt factory,
celix_properties_t* properties, service_registration_pt *service_registration) {
if (context == NULL || service_registration == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
long bndId = celix_bundle_getId(context->bundle);
return fw_registerServiceFactory(context->framework, service_registration, bndId, serviceName, factory, properties);
}
celix_status_t bundleContext_getServiceReferences(bundle_context_pt context, const char * serviceName, const char * filter, celix_array_list_t** service_references) {
if (context == NULL || service_references == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
return fw_getServiceReferences(context->framework, service_references, context->bundle, serviceName, filter);
}
celix_status_t bundleContext_getServiceReference(bundle_context_pt context, const char * serviceName, service_reference_pt *service_reference) {
service_reference_pt reference = NULL;
celix_array_list_t* services = NULL;
celix_status_t status = CELIX_SUCCESS;
if (serviceName != NULL) {
if (bundleContext_getServiceReferences(context, serviceName, NULL, &services) == CELIX_SUCCESS) {
unsigned int size = celix_arrayList_size(services);
for(unsigned int i = 0; i < size; i++) {
if(i == 0) {
reference = celix_arrayList_get(services, 0);
} else {
bundleContext_ungetServiceReference(context, celix_arrayList_get(services, i));
}
}
celix_arrayList_destroy(services);
*service_reference = reference;
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get service reference");
return status;
}
static bool bundleContext_IsServiceReferenceValid(bundle_context_pt context, service_reference_pt ref) {
bundle_pt refBundle = NULL;
serviceReference_getOwner(ref, &refBundle);
return context->bundle == refBundle;
}
celix_status_t bundleContext_retainServiceReference(bundle_context_pt context, service_reference_pt ref) {
if (context == NULL || ref == NULL || !bundleContext_IsServiceReferenceValid(context, ref)) {
return CELIX_ILLEGAL_ARGUMENT;
}
return serviceReference_retain(ref);
}
celix_status_t bundleContext_ungetServiceReference(bundle_context_pt context, service_reference_pt reference) {
if (context == NULL || reference == NULL || !bundleContext_IsServiceReferenceValid(context, reference)) {
return CELIX_ILLEGAL_ARGUMENT;
}
return serviceReference_release(reference, NULL);
}
celix_status_t bundleContext_getService(bundle_context_pt context, service_reference_pt reference, void** service_instance) {
if (context == NULL || reference == NULL || service_instance == NULL || !bundleContext_IsServiceReferenceValid(context, reference)) {
return CELIX_ILLEGAL_ARGUMENT;
}
/*NOTE argument service_instance should be considered a 'const void**'.
To ensure backwards compatibility a cast is made instead.
*/
return serviceReference_getService(reference, (const void**) service_instance);
}
celix_status_t bundleContext_ungetService(bundle_context_pt context, service_reference_pt reference, bool *result) {
if (context == NULL || reference == NULL || !bundleContext_IsServiceReferenceValid(context, reference)) {
return CELIX_ILLEGAL_ARGUMENT;
}
return serviceReference_ungetService(reference, result);
}
celix_status_t bundleContext_getBundles(bundle_context_pt context, celix_array_list_t** bundles) {
if (context == NULL || bundles == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
*bundles = framework_getBundles(context->framework);
return CELIX_SUCCESS;
}
celix_status_t bundleContext_getBundleById(bundle_context_pt context, long id, bundle_pt *bundle) {
if (context == NULL || bundle == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
celix_status_t status = CELIX_SUCCESS;
*bundle = framework_getBundleById(context->framework, id);
if (*bundle == NULL) {
status = CELIX_BUNDLE_EXCEPTION;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get bundle [id=%ld]", id);
return status;
}
celix_status_t bundleContext_addServiceListener(bundle_context_pt context, celix_service_listener_t *listener, const char* filter) {
if (context == NULL || listener == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
fw_addServiceListener(context->framework, context->bundle, listener, filter);
return CELIX_SUCCESS;
}
celix_status_t bundleContext_removeServiceListener(bundle_context_pt context, celix_service_listener_t *listener) {
if (context == NULL || listener == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
fw_removeServiceListener(context->framework, context->bundle, listener);
return CELIX_SUCCESS;
}
celix_status_t bundleContext_addBundleListener(bundle_context_pt context, bundle_listener_pt listener) {
if (context == NULL || listener == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
return fw_addBundleListener(context->framework, context->bundle, listener);
}
celix_status_t bundleContext_removeBundleListener(bundle_context_pt context, bundle_listener_pt listener) {
if (context == NULL || listener == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
return fw_removeBundleListener(context->framework, context->bundle, listener);
}
celix_status_t bundleContext_addFrameworkListener(bundle_context_pt context, framework_listener_pt listener) {
if (context == NULL || listener == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
return fw_addFrameworkListener(context->framework, context->bundle, listener);
}
celix_status_t bundleContext_removeFrameworkListener(bundle_context_pt context, framework_listener_pt listener) {
if (context == NULL || listener == NULL) {
return CELIX_ILLEGAL_ARGUMENT;
}
return fw_removeFrameworkListener(context->framework, context->bundle, listener);
}
celix_status_t bundleContext_getProperty(bundle_context_pt context, const char *name, const char** value) {
return bundleContext_getPropertyWithDefault(context, name, NULL, value);
}
celix_status_t bundleContext_getPropertyWithDefault(bundle_context_pt context, const char* name, const char* defaultValue, const char** out) {
const char *val = celix_bundleContext_getProperty(context, name, defaultValue);
if (out != NULL) {
*out = val;
}
return CELIX_SUCCESS;
}
/**********************************************************************************************************************
**********************************************************************************************************************
* Updated API
**********************************************************************************************************************
**********************************************************************************************************************/
long celix_bundleContext_registerServiceAsync(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties) {
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
opts.svc = svc;
opts.serviceName = serviceName;
opts.properties = properties;
return celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
}
long celix_bundleContext_registerService(bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties) {
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
opts.svc = svc;
opts.serviceName = serviceName;
opts.properties = properties;
return celix_bundleContext_registerServiceWithOptions(ctx, &opts);
}
long celix_bundleContext_registerServiceFactoryAsync(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props) {
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
opts.factory = factory;
opts.serviceName = serviceName;
opts.properties = props;
return celix_bundleContext_registerServiceWithOptionsAsync(ctx, &opts);
}
long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props) {
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
opts.factory = factory;
opts.serviceName = serviceName;
opts.properties = props;
return celix_bundleContext_registerServiceWithOptions(ctx, &opts);
}
/**
* Corrects the service properties value types for the service ranking and service version to ensure
* that these properties are of the correct type.
* Print a warning log message if the service ranking or service version was not of the correct type.
*/
static celix_status_t celix_bundleContext_correctServicePropertiesValueTypes(celix_bundle_context_t* ctx,
celix_properties_t* props) {
celix_status_t status = CELIX_SUCCESS;
const celix_properties_entry_t* entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_RANKING);
if (entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
fw_log(
ctx->framework->logger, CELIX_LOG_LEVEL_WARNING, "Service ranking is of type string, converting to long");
bool converted;
long ranking = celix_utils_convertStringToLong(entry->value, 0, &converted);
if (!converted) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_WARNING,
"Cannot convert service ranking %s to long. Keeping %s value as string",
entry->value,
CELIX_FRAMEWORK_SERVICE_RANKING);
} else {
status = celix_properties_setLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, ranking);
}
}
entry = celix_properties_getEntry(props, CELIX_FRAMEWORK_SERVICE_VERSION);
if (status == CELIX_SUCCESS && entry && entry->valueType == CELIX_PROPERTIES_VALUE_TYPE_STRING) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_WARNING,
"Service version is of type string, converting to version");
celix_autoptr(celix_version_t) version = NULL;
status = celix_version_parse(entry->value, &version);
if (status == CELIX_ILLEGAL_ARGUMENT) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_WARNING,
"Cannot parse service version %s. Keeping %s value as string",
entry->value,
CELIX_FRAMEWORK_SERVICE_VERSION);
status = CELIX_SUCCESS; //after warning, ignore parse error.
} else if (status == CELIX_SUCCESS) {
status = celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
}
}
return status;
}
static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_context_t *ctx, const celix_service_registration_options_t *opts, bool async) {
bool valid = opts->serviceName != NULL && strncmp("", opts->serviceName, 1) != 0;
if (!valid) {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Required serviceName argument is NULL or empty");
return -1;
}
valid = opts->svc != NULL || opts->factory != NULL;
if (!valid) {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Required svc or factory argument is NULL");
return -1;
}
//set properties
celix_autoptr(celix_properties_t) props = opts->properties;
if (props == NULL) {
props = celix_properties_create();
}
if (opts->serviceVersion != NULL && strncmp("", opts->serviceVersion, 1) != 0) {
celix_autoptr(celix_version_t) version = celix_version_createVersionFromString(opts->serviceVersion);
if (!version) {
celix_framework_logTssErrors(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR);
fw_log(
ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot parse service version %s", opts->serviceVersion);
return -1;
}
celix_status_t rc =
celix_properties_assignVersion(props, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
if (rc != CELIX_SUCCESS) {
celix_framework_logTssErrors(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR);
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot set service version %s", opts->serviceVersion);
return -1;
}
}
celix_status_t correctionStatus = celix_bundleContext_correctServicePropertiesValueTypes(ctx, props);
if (correctionStatus != CELIX_SUCCESS) {
celix_framework_logTssErrors(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR);
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot correct service properties value types");
return -1;
}
long svcId;
if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
/*
* Note already on event loop, cannot register the service async, because we cannot wait a future event (the
* service registration) the event loop.
*
* So in this case we handle the service registration the "traditional way" and call the sync fw service
* registrations versions on the event loop thread
*/
svcId = celix_framework_registerService(ctx->framework, ctx->bundle, opts->serviceName, opts->svc, opts->factory, celix_steal_ptr(props));
} else {
void (*asyncCallback)(void *data, long serviceId) = async ? opts->asyncCallback : NULL; //NOTE for not async call do not use the callback.
svcId = celix_framework_registerServiceAsync(ctx->framework, ctx->bundle, opts->serviceName, opts->svc, opts->factory, celix_steal_ptr(props), opts->asyncData, asyncCallback, NULL, NULL);
if (!async && svcId >= 0) {
//note on event loop thread, but in a sync call, so waiting till service registration is concluded
celix_bundleContext_waitForAsyncRegistration(ctx, svcId);
}
}
if (svcId >= 0) {
celixThreadRwlock_writeLock(&ctx->lock);
celix_arrayList_addLong(ctx->svcRegistrations, svcId);
celixThreadRwlock_unlock(&ctx->lock);
}
return svcId;
}
long celix_bundleContext_registerServiceWithOptions(bundle_context_t *ctx, const celix_service_registration_options_t *opts) {
return celix_bundleContext_registerServiceWithOptionsInternal(ctx, opts, false);
}
long celix_bundleContext_registerServiceWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts) {
return celix_bundleContext_registerServiceWithOptionsInternal(ctx, opts, true);
}
void celix_bundleContext_waitForAsyncRegistration(celix_bundle_context_t* ctx, long serviceId) {
if (serviceId >= 0) {
celix_framework_waitForAsyncRegistration(ctx->framework, serviceId);
}
}
bool celix_bundleContext_isServiceRegistered(celix_bundle_context_t* ctx, long serviceId) {
return celix_serviceRegistry_isServiceRegistered(ctx->framework->registry, serviceId);
}
static void celix_bundleContext_unregisterServiceInternal(celix_bundle_context_t *ctx, long serviceId, bool async, void *data, void (*done)(void*)) {
long found = -1L;
if (ctx != NULL && serviceId >= 0) {
celixThreadRwlock_writeLock(&ctx->lock);
int size = celix_arrayList_size(ctx->svcRegistrations);
for (int i = 0; i < size; ++i) {
long entryId = celix_arrayList_getLong(ctx->svcRegistrations, i);
if (entryId == serviceId) {
celix_arrayList_removeAt(ctx->svcRegistrations, i);
found = entryId;
break;
}
}
celixThreadRwlock_unlock(&ctx->lock);
if (found >= 0) {
if (async) {
celix_framework_unregisterAsync(ctx->framework, ctx->bundle, found, data, done);
} else if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
/*
* sync unregistration.
* Note already on event loop, cannot unregister the service async, because we cannot wait a future event (the
* service unregistration) the event loop.
*
* So in this case we handle the service unregistration the "traditional way" and call the sync fw service
* unregistrations versions
*/
celix_framework_unregister(ctx->framework, ctx->bundle, found);
if (done != NULL) {
done(data);
}
} else {
celix_framework_unregisterAsync(ctx->framework, ctx->bundle, found, data, done);
celix_bundleContext_waitForAsyncUnregistration(ctx, serviceId);
}
} else {
framework_logIfError(ctx->framework->logger, CELIX_ILLEGAL_ARGUMENT, NULL,
"No service registered with svc id %li for bundle %s (bundle id: %li)!", serviceId,
celix_bundle_getSymbolicName(ctx->bundle), celix_bundle_getId(ctx->bundle));
}
}
}
void celix_bundleContext_unregisterServiceAsync(celix_bundle_context_t *ctx, long serviceId, void *data, void (*done)(void*)) {
return celix_bundleContext_unregisterServiceInternal(ctx, serviceId, true, data, done);
}
void celix_bundleContext_unregisterService(bundle_context_t *ctx, long serviceId) {
return celix_bundleContext_unregisterServiceInternal(ctx, serviceId, false, NULL, NULL);
}
void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx, long serviceId) {
if (serviceId >= 0) {
celix_framework_waitForAsyncUnregistration(ctx->framework, serviceId);
}
}
celix_dependency_manager_t* celix_bundleContext_getDependencyManager(bundle_context_t *ctx) {
if (ctx == NULL) {
return NULL;
}
celix_auto(celix_rwlock_rlock_guard_t) rlockGuard = celixRwlockRlockGuard_init(&ctx->lock);
if (ctx->mng) {
return ctx->mng;
}
celixRwlockRlockGuard_deinit(&rlockGuard);
celix_auto(celix_rwlock_wlock_guard_t) wlockGuard = celixRwlockWlockGuard_init(&ctx->lock);
if (ctx->mng == NULL) {
ctx->mng = celix_private_dependencyManager_create(ctx);
}
if (ctx->mng == NULL) {
framework_logIfError(ctx->framework->logger, CELIX_BUNDLE_EXCEPTION, NULL, "Cannot create dependency manager");
}
return ctx->mng;
}
static celix_status_t bundleContext_bundleChanged(void* listenerSvc, bundle_event_t* event) {
bundle_listener_t* listener = listenerSvc;
celix_bundle_context_bundle_tracker_entry_t* tracker = listener->handle;
bool handleEvent = true;
long bndId = celix_bundle_getId(event->bnd);
if (bndId == 0 /*framework bundle*/) {
handleEvent = tracker->opts.includeFrameworkBundle;
}
if (handleEvent) {
void* callbackHandle = tracker->opts.callbackHandle;
if (event->type == OSGI_FRAMEWORK_BUNDLE_EVENT_INSTALLED && tracker->opts.onInstalled != NULL) {
tracker->opts.onInstalled(callbackHandle, event->bnd);
} else if (event->type == OSGI_FRAMEWORK_BUNDLE_EVENT_STARTED && tracker->opts.onStarted != NULL) {
tracker->opts.onStarted(callbackHandle, event->bnd);
} else if (event->type == OSGI_FRAMEWORK_BUNDLE_EVENT_STOPPING && tracker->opts.onStopped != NULL) {
tracker->opts.onStopped(callbackHandle, event->bnd);
}
if (tracker->opts.onBundleEvent != NULL) {
tracker->opts.onBundleEvent(callbackHandle, event);
}
}
return CELIX_SUCCESS;
}
void celix_bundleContext_trackBundlesWithOptionsCallback(void *data) {
celix_bundle_context_bundle_tracker_entry_t* entry = data;
assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework));
celixThreadRwlock_writeLock(&entry->ctx->lock);
bool cancelled = entry->cancelled;
entry->created = true;
celixThreadRwlock_unlock(&entry->ctx->lock);
if (cancelled) {
fw_log(entry->ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Creation of bundle tracker cancelled. trk id = %li", entry->trackerId);
free(entry);
} else {
fw_addBundleListener(entry->ctx->framework, entry->ctx->bundle, &entry->listener);
if (entry->opts.trackerCreatedCallback) {
entry->opts.trackerCreatedCallback(entry->opts.trackerCreatedCallbackData);
}
}
}
static long celix_bundleContext_trackBundlesWithOptionsInternal(
bundle_context_t* ctx,
const celix_bundle_tracking_options_t *opts,
bool async) {
celix_bundle_context_bundle_tracker_entry_t *entry = calloc(1, sizeof(*entry));
memcpy(&entry->opts, opts, sizeof(*opts));
entry->ctx = ctx;
entry->createEventId = celix_framework_nextEventId(ctx->framework);
entry->listener.handle = entry;
entry->listener.bundleChanged = bundleContext_bundleChanged;
celixThreadRwlock_writeLock(&ctx->lock);
entry->trackerId = ctx->nextTrackerId++;
celix_longHashMap_put(ctx->bundleTrackers, entry->trackerId, entry);
long trackerId = entry->trackerId;
celixThreadRwlock_unlock(&ctx->lock);
if (!async) { //note only using the async callback if this is a async call.
entry->opts.trackerCreatedCallback = NULL;
}
long id = celix_framework_fireGenericEvent(
ctx->framework,
entry->createEventId,
celix_bundle_getId(ctx->bundle),
"add bundle listener",
entry,
celix_bundleContext_trackBundlesWithOptionsCallback,
NULL,
NULL);
if (!async) {
celix_framework_waitForGenericEvent(ctx->framework, id);
}
return trackerId;
}
long celix_bundleContext_trackBundlesWithOptions(
bundle_context_t* ctx,
const celix_bundle_tracking_options_t *opts) {
return celix_bundleContext_trackBundlesWithOptionsInternal(ctx, opts, false);
}
long celix_bundleContext_trackBundlesWithOptionsAsync(
celix_bundle_context_t* ctx,
const celix_bundle_tracking_options_t *opts) {
return celix_bundleContext_trackBundlesWithOptionsInternal(ctx, opts, true);
}
long celix_bundleContext_trackBundles(
bundle_context_t* ctx,
void* callbackHandle,
void (*onStarted)(void* handle, const bundle_t *bundle),
void (*onStopped)(void *handle, const bundle_t *bundle)) {
celix_bundle_tracking_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.callbackHandle = callbackHandle;
opts.onStarted = onStarted;
opts.onStopped = onStopped;
return celix_bundleContext_trackBundlesWithOptions(ctx, &opts);
}
long celix_bundleContext_trackBundlesAsync(
celix_bundle_context_t* ctx,
void* callbackHandle,
void (*onStarted)(void* handle, const celix_bundle_t *bundle),
void (*onStopped)(void *handle, const celix_bundle_t *bundle)) {
celix_bundle_tracking_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.callbackHandle = callbackHandle;
opts.onStarted = onStarted;
opts.onStopped = onStopped;
return celix_bundleContext_trackBundlesWithOptionsAsync(ctx, &opts);
}
size_t celix_bundleContext_useBundles(
bundle_context_t *ctx,
void *callbackHandle,
void (*use)(void *handle, const bundle_t *bundle)) {
return celix_framework_useBundles(ctx->framework, false, callbackHandle, use);
}
bool celix_bundleContext_useBundle(
bundle_context_t *ctx,
long bundleId,
void *callbackHandle,
void (*use)(void *handle, const bundle_t *bundle)) {
return celix_framework_useBundle(ctx->framework, false, bundleId, callbackHandle, use);
}
static void bundleContext_cleanupBundleTrackers(bundle_context_t* ctx) {
celix_module_t* module;
const char* symbolicName;
bundle_getCurrentModule(ctx->bundle, &module);
module_getSymbolicName(module, &symbolicName);
celix_array_list_t* danglingTrkIds = NULL;
celixThreadRwlock_readLock(&ctx->lock);
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);
}
celixThreadRwlock_unlock(&ctx->lock);
if (danglingTrkIds != NULL) {
for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) {
long trkId = celix_arrayList_getLong(danglingTrkIds, i);
celix_bundleContext_stopTracker(ctx, trkId);
}
celix_arrayList_destroy(danglingTrkIds);
}
}
static void bundleContext_cleanupServiceTrackers(bundle_context_t* ctx) {
celix_module_t* module;
const char* symbolicName;
bundle_getCurrentModule(ctx->bundle, &module);
module_getSymbolicName(module, &symbolicName);
celix_array_list_t* danglingTrkIds = NULL;
celixThreadRwlock_readLock(&ctx->lock);
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 ? entry->tracker->filter : "unknown");
if (danglingTrkIds == NULL) {
danglingTrkIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingTrkIds, trkId);
}
celixThreadRwlock_unlock(&ctx->lock);
if (danglingTrkIds != NULL) {
for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) {
long trkId = celix_arrayList_getLong(danglingTrkIds, i);
celix_bundleContext_stopTracker(ctx, trkId);
}
celix_arrayList_destroy(danglingTrkIds);
}
}
static void bundleContext_cleanupServiceTrackerTrackers(bundle_context_t* ctx) {
celix_module_t* module;
const char* symbolicName;
bundle_getCurrentModule(ctx->bundle, &module);
module_getSymbolicName(module, &symbolicName);
celix_array_list_t* danglingTrkIds = NULL;
celixThreadRwlock_readLock(&ctx->lock);
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 ? entry->serviceName : "all");
if (danglingTrkIds == NULL) {
danglingTrkIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingTrkIds, trkId);
}
celixThreadRwlock_unlock(&ctx->lock);
if (danglingTrkIds != NULL) {
for (int i = 0; i < celix_arrayList_size(danglingTrkIds); ++i) {
long trkId = celix_arrayList_getLong(danglingTrkIds, i);
celix_bundleContext_stopTracker(ctx, trkId);
}
celix_arrayList_destroy(danglingTrkIds);
}
}
static void bundleContext_cleanupServiceRegistration(bundle_context_t* ctx) {
celix_module_t* module;
const char *symbolicName;
bundle_getCurrentModule(ctx->bundle, &module);
module_getSymbolicName(module, &symbolicName);
celix_array_list_t* danglingSvcIds = NULL;
celixThreadRwlock_readLock(&ctx->lock);
for (int i = 0; i < celix_arrayList_size(ctx->svcRegistrations); ++i) {
long svcId = celix_arrayList_getLong(ctx->svcRegistrations, i);
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR,
"Dangling service registration with svcId %li, for bundle %s. "
"Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName);
if (danglingSvcIds == NULL) {
danglingSvcIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingSvcIds, svcId);
}
celixThreadRwlock_unlock(&ctx->lock);
if (danglingSvcIds != NULL) {
for (int i = 0; i < celix_arrayList_size(danglingSvcIds); ++i) {
long svcId = celix_arrayList_getLong(danglingSvcIds, i);
celix_framework_unregister(ctx->framework, ctx->bundle, svcId);
}
celix_arrayList_destroy(danglingSvcIds);
}
}
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);
celixThreadRwlock_writeLock(&tracker->ctx->lock);
celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId);
celixThreadRwlock_unlock(&tracker->ctx->lock);
free(tracker);
}
static void celix_bundleContext_removeServiceTracker(void *data) {
celix_bundle_context_service_tracker_entry_t *tracker = data;
celix_serviceTracker_destroy(tracker->tracker);
celixThreadRwlock_writeLock(&tracker->ctx->lock);
celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId);
celixThreadRwlock_unlock(&tracker->ctx->lock);
if (tracker->isFreeFilterNeeded) {
free((char*)tracker->opts.filter.serviceName);
free((char*)tracker->opts.filter.versionRange);
free((char*)tracker->opts.filter.filter);
}
free(tracker);
}
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);
celixThreadRwlock_writeLock(&tracker->ctx->lock);
celix_longHashMap_remove(tracker->ctx->stoppingTrackerEventIds, tracker->trackerId);
celixThreadRwlock_unlock(&tracker->ctx->lock);
free(tracker->serviceName);
free(tracker);
}
static void celix_bundleContext_waitForUnusedServiceTracker(celix_bundle_context_t* ctx,
celix_bundle_context_service_tracker_entry_t* trkEntry) {
// busy wait till the tracker is not used anymore
// note that the use count cannot be increased anymore, because the tracker is removed from the map
struct timespec start = celix_gettime(CLOCK_MONOTONIC);
int logCount = 0;
while (__atomic_load_n(&trkEntry->useCount, __ATOMIC_ACQUIRE) > 0) {
if (celix_elapsedtime(CLOCK_MONOTONIC, start) > TRACKER_WARN_THRESHOLD_SEC) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_WARNING,
"Service tracker with trk id %li is still in use after %i seconds. "
"This might indicate a programming error.",
trkEntry->trackerId,
logCount * TRACKER_WARN_THRESHOLD_SEC);
start = celix_gettime(CLOCK_MONOTONIC);
logCount++;
}
usleep(1000);
}
}
static void celix_bundleContext_stopTrackerInternal(celix_bundle_context_t* ctx, long trackerId, bool async, void *doneData, void (*doneCallback)(void* doneData)) {
if (ctx == NULL || trackerId <= 0) {
return;
}
bool found = false;
bool cancelled = false;
celix_bundle_context_bundle_tracker_entry_t *bundleTracker = NULL;
celix_bundle_context_service_tracker_entry_t *serviceTracker = NULL;
celix_bundle_context_service_tracker_tracker_entry_t *svcTrackerTracker = NULL;
celixThreadRwlock_writeLock(&ctx->lock);
if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) {
found = true;
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 (celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId)) {
found = true;
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 (celix_longHashMap_hasKey(ctx->metaTrackers, trackerId)) {
found = true;
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)
}
if (found && cancelled) {
//nop
celixThreadRwlock_unlock(&ctx->lock);
} else if (found && !async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
//already on the event loop, stop tracker "traditionally" to keep old behavior
celixThreadRwlock_unlock(&ctx->lock); //note calling remove/stops/unregister out side of locks
if (bundleTracker != NULL) {
fw_removeBundleListener(ctx->framework, ctx->bundle, &bundleTracker->listener);
free(bundleTracker);
} else if (serviceTracker != NULL) {
celix_bundleContext_waitForUnusedServiceTracker(ctx, serviceTracker);
celix_serviceTracker_destroy(serviceTracker->tracker);
if (serviceTracker->isFreeFilterNeeded) {
free((char*)serviceTracker->opts.filter.serviceName);
free((char*)serviceTracker->opts.filter.versionRange);
free((char*)serviceTracker->opts.filter.filter);
}
free(serviceTracker);
} else if (svcTrackerTracker != NULL) {
celix_framework_unregister(ctx->framework, ctx->bundle, svcTrackerTracker->serviceId);
free(svcTrackerTracker->serviceName);
free(svcTrackerTracker);
}
if (doneCallback != NULL) {
doneCallback(doneData);
}
} 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);
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);
} else if (serviceTracker != NULL) {
celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop tracker", serviceTracker, celix_bundleContext_removeServiceTracker, doneData, doneCallback);
} else if (svcTrackerTracker != NULL) {
celix_framework_fireGenericEvent(ctx->framework, eventId, celix_bundle_getId(ctx->bundle), "stop meta tracker", svcTrackerTracker, celix_bundleContext_removeServiceTrackerTracker, doneData, doneCallback);
} else {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected else branch");
}
celixThreadRwlock_unlock(&ctx->lock);
} else if (found) { /*sync, so waiting for events*/
long eventId = -1L;
if (bundleTracker != NULL) {
eventId = celix_framework_fireGenericEvent(ctx->framework, -1L, celix_bundle_getId(ctx->bundle), "stop tracker", bundleTracker, celix_bundleContext_removeBundleTracker, doneData, doneCallback);
} else if (serviceTracker != NULL) {
eventId = celix_framework_fireGenericEvent(ctx->framework, -1L, celix_bundle_getId(ctx->bundle), "stop tracker", serviceTracker, celix_bundleContext_removeServiceTracker, doneData, doneCallback);
} else if (svcTrackerTracker != NULL) {
eventId = celix_framework_fireGenericEvent(ctx->framework, -1L, celix_bundle_getId(ctx->bundle), "stop meta tracker", svcTrackerTracker, celix_bundleContext_removeServiceTrackerTracker, doneData, doneCallback);
} else {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Unexpected else branch");
}
celixThreadRwlock_unlock(&ctx->lock);
celix_framework_waitForGenericEvent(ctx->framework, eventId);
} else {
celixThreadRwlock_unlock(&ctx->lock);
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "No tracker with id %li found'", trackerId);
}
}
void celix_bundleContext_stopTracker(bundle_context_t *ctx, long trackerId) {
return celix_bundleContext_stopTrackerInternal(ctx, trackerId, false, NULL, NULL);
}
void celix_bundleContext_stopTrackerAsync(celix_bundle_context_t *ctx, long trackerId, void *doneData, void (*doneCallback)(void* doneData)) {
return celix_bundleContext_stopTrackerInternal(ctx, trackerId, true, doneData, doneCallback);
}
static void celix_bundleContext_waitForTrackerInternal(celix_bundle_context_t* ctx, long trackerId, bool waitForStart) {
if (ctx == NULL || trackerId < 0) {
//silent ignore
return;
}
if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_WARNING,
"Cannot wait for tracker on the event loop thread. This can cause a deadlock. "
"Ignoring call.");
return;
}
bool found = false;
long eventId = -1;
long svcId = -1;
if (waitForStart) {
celixThreadRwlock_readLock(&ctx->lock);
if (celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId)) {
found = true;
celix_bundle_context_bundle_tracker_entry_t* bundleTracker = celix_longHashMap_get(ctx->bundleTrackers, trackerId);
eventId = bundleTracker->createEventId;
} else if (celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId)) {
found = true;
celix_bundle_context_service_tracker_entry_t* serviceTracker = celix_longHashMap_get(ctx->serviceTrackers, trackerId);
eventId = serviceTracker->createEventId;
} else if (celix_longHashMap_hasKey(ctx->metaTrackers, trackerId)) {
found = true;
celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = celix_longHashMap_get(ctx->metaTrackers, trackerId);
svcId = svcTrackerTracker->serviceId;
}
celixThreadRwlock_unlock(&ctx->lock);
} else {
celixThreadRwlock_readLock(&ctx->lock);
if (celix_longHashMap_hasKey(ctx->stoppingTrackerEventIds, trackerId)) {
found = true;
eventId = celix_longHashMap_getLong(ctx->stoppingTrackerEventIds, trackerId, -1);
}
celixThreadRwlock_unlock(&ctx->lock);
}
if (found) {
if (svcId >= 0) {
celix_framework_waitForAsyncRegistration(ctx->framework, svcId);
} else {
celix_framework_waitForGenericEvent(ctx->framework, eventId);
}
} else if (waitForStart) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_ERROR,
"Cannot wait for tracker, no tracker with id %li found'",
trackerId);
}
}
void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t* ctx, long trackerId) {
return celix_bundleContext_waitForTrackerInternal(ctx, trackerId, true);
}
void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t* ctx, long trackerId) {
return celix_bundleContext_waitForTrackerInternal(ctx, trackerId, false);
}
long celix_bundleContext_installBundle(bundle_context_t *ctx, const char *bundleLoc, bool autoStart) {
return celix_framework_installBundle(ctx->framework, bundleLoc, autoStart);
}
celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx) {
return celix_framework_listBundles(ctx->framework);
}
celix_array_list_t* celix_bundleContext_listInstalledBundles(celix_bundle_context_t *ctx) {
return celix_framework_listInstalledBundles(ctx->framework);
}
bool celix_bundleContext_isBundleInstalled(celix_bundle_context_t *ctx, long bndId) {
return celix_framework_isBundleInstalled(ctx->framework, bndId);
}
bool celix_bundleContext_isBundleActive(celix_bundle_context_t *ctx, long bndId) {
return celix_framework_isBundleActive(ctx->framework, bndId);
}
bool celix_bundleContext_startBundle(celix_bundle_context_t *ctx, long bndId) {
return celix_framework_startBundle(ctx->framework, bndId);
}
bool celix_bundleContext_updateBundle(celix_bundle_context_t *ctx, long bndId, const char* updatedBundleUrl) {
return celix_framework_updateBundle(ctx->framework, bndId, updatedBundleUrl);
}
bool celix_bundleContext_stopBundle(celix_bundle_context_t *ctx, long bndId) {
return celix_framework_stopBundle(ctx->framework, bndId);
}
bool celix_bundleContext_uninstallBundle(bundle_context_t *ctx, long bndId) {
return celix_framework_uninstallBundle(ctx->framework, bndId);
}
bool celix_bundleContext_unloadBundle(celix_bundle_context_t *ctx, long bndId) {
return celix_framework_unloadBundle(ctx->framework, bndId);
}
bool celix_bundleContext_useServiceWithId(
bundle_context_t *ctx,
long serviceId,
const char *serviceName,
void *callbackHandle,
void (*use)(void *handle, void *svc)) {
char filter[1+/*(*/sizeof(CELIX_FRAMEWORK_SERVICE_ID)-1+1/*=*/+20/*INT64_MIN*/+1/*)*/+1/*'\0'*/];
(void)snprintf(filter, sizeof(filter), "(%s=%li)", CELIX_FRAMEWORK_SERVICE_ID, serviceId);
celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
opts.filter.serviceName = serviceName;
opts.filter.filter = filter;
opts.callbackHandle = callbackHandle;
opts.use = use;
return celix_bundleContext_useServiceWithOptions(ctx, &opts);
}
bool celix_bundleContext_useService(
bundle_context_t *ctx,
const char* serviceName,
void *callbackHandle,
void (*use)(void *handle, void *svc)) {
celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.use = use;
return celix_bundleContext_useServiceWithOptions(ctx, &opts);
}
size_t celix_bundleContext_useServices(
bundle_context_t *ctx,
const char* serviceName,
void *callbackHandle,
void (*use)(void *handle, void *svc)) {
celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.use = use;
return celix_bundleContext_useServicesWithOptions(ctx, &opts);
}
static size_t celix_bundleContext_useServicesInternal(celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts, bool singular) {
if (opts == NULL || opts->filter.serviceName == NULL) {
return 0;
}
if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
fw_log(ctx->framework->logger,
CELIX_LOG_LEVEL_ERROR,
"Cannot use services on the event loop thread. Ignoring call.");
return 0;
}
celix_service_tracking_options_t trackingOpts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
memcpy(&trackingOpts.filter, &opts->filter, sizeof(opts->filter));
long trkId = celix_bundleContext_trackServicesWithOptions(ctx, &trackingOpts);
if (trkId < 0) {
return 0;
}
//Note because useService* is primary an API used in tests, waiting for events to that service-on-demand is
//working and useService* calls work after an async service (un)registration.
celix_framework_waitForEmptyEventQueue(ctx->framework);
celix_tracked_service_use_options_t useOpts = CELIX_EMPTY_TRACKER_SERVICE_USE_OPTIONS;
useOpts.callbackHandle = opts->callbackHandle;
useOpts.use = opts->use;
useOpts.useWithProperties = opts->useWithProperties;
useOpts.useWithOwner = opts->useWithOwner;
size_t count;
if (singular) {
struct timespec start = celix_gettime(CLOCK_MONOTONIC);
bool called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId, &useOpts);
if (!called && opts->waitTimeoutInSeconds > 0) {
while (!called && celix_elapsedtime(CLOCK_MONOTONIC, start) < opts->waitTimeoutInSeconds) {
usleep(1000);
called = celix_bundleContext_useTrackedServiceWithOptions(ctx, trkId, &useOpts);
}
}
count = called ? 1 : 0;
} else {
count = celix_bundleContext_useTrackedServicesWithOptions(ctx, trkId, &useOpts);
}
celix_bundleContext_stopTracker(ctx, trkId);
return count;
}
bool celix_bundleContext_useServiceWithOptions(
celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts) {
return celix_bundleContext_useServicesInternal(ctx, opts, true) > 0;
}
size_t celix_bundleContext_useServicesWithOptions(
celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts) {
return celix_bundleContext_useServicesInternal(ctx, opts, false);
}
long celix_bundleContext_trackServices(bundle_context_t* ctx, const char* serviceName) {
celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
opts.filter.serviceName = serviceName;
return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, false);
}
long celix_bundleContext_trackServicesAsync(celix_bundle_context_t* ctx, const char* serviceName) {
celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
opts.filter.serviceName = serviceName;
return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, true);
}
static void celix_bundleContext_createTrackerOnEventLoop(void *data) {
celix_bundle_context_service_tracker_entry_t* entry = data;
assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework));
celixThreadRwlock_writeLock(&entry->ctx->lock);
bool cancelled = entry->cancelled;
if (cancelled) {
fw_log(entry->ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Creating of service tracker was cancelled. trk id = %li, svc name tracked = %s", entry->trackerId, entry->opts.filter.serviceName);
celixThreadRwlock_unlock(&entry->ctx->lock);
return;
}
celix_service_tracker_t *tracker = celix_serviceTracker_createClosedWithOptions(entry->ctx, &entry->opts);
if (tracker == NULL) {
fw_log(entry->ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot create tracker for bnd %s (%li)", celix_bundle_getSymbolicName(entry->ctx->bundle), celix_bundle_getId(entry->ctx->bundle));
} else {
entry->tracker = tracker;
}
celixThreadRwlock_unlock(&entry->ctx->lock);
if (tracker) {
serviceTracker_open(tracker);
}
}
static void celix_bundleContext_doneCreatingTrackerOnEventLoop(void *data) {
celix_bundle_context_service_tracker_entry_t* entry = data;
celixThreadRwlock_readLock(&entry->ctx->lock);
bool cancelled = entry->cancelled;
celixThreadRwlock_unlock(&entry->ctx->lock);
if (cancelled) {
//tracker creation cancelled -> entry already removed from map, but memory needs to be freed.
if (entry->isFreeFilterNeeded) {
free((char*)entry->opts.filter.serviceName);
free((char*)entry->opts.filter.versionRange);
free((char*)entry->opts.filter.filter);
}
free(entry);
} else if (entry->trackerCreatedCallback != NULL) {
entry->trackerCreatedCallback(entry->trackerCreatedCallbackData);
}
}
static long celix_bundleContext_trackServicesWithOptionsInternal(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts, bool async) {
if (ctx == NULL) {
return -1L;
} else if (opts == NULL) {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Cannot track services with a NULL service tracking options argument");
return -1L;
}
if (opts->filter.serviceName == NULL) {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Starting a tracker for any services");
}
if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
//already in event loop thread. To keep the old behavior just create the tracker traditionally (chained in the current thread).
celix_service_tracker_t *tracker = celix_serviceTracker_createWithOptions(ctx, opts);
long trackerId = -1L;
if (tracker != NULL) {
celix_bundle_context_service_tracker_entry_t* entry = calloc(1, sizeof(*entry));
entry->ctx = ctx;
entry->tracker = tracker;
entry->opts = *opts;
entry->isFreeFilterNeeded = false;
entry->createEventId = -1;
celixThreadRwlock_writeLock(&ctx->lock);
entry->trackerId = ctx->nextTrackerId++;
trackerId = entry->trackerId;
celix_longHashMap_put(ctx->serviceTrackers, trackerId, entry);
celixThreadRwlock_unlock(&ctx->lock);
}
return trackerId;
} else {
long createEventId = celix_framework_nextEventId(ctx->framework);
celix_bundle_context_service_tracker_entry_t* entry = calloc(1, sizeof(*entry));
entry->ctx = ctx;
entry->createEventId = createEventId;
entry->tracker = NULL; //will be set async
entry->opts = *opts;
if (async) { //note only setting the async callback if this is a async call
entry->trackerCreatedCallbackData = opts->trackerCreatedCallbackData;
entry->trackerCreatedCallback = opts->trackerCreatedCallback;
//for async copying the const char* inputs
entry->isFreeFilterNeeded = true;
entry->opts.filter.serviceName = celix_utils_strdup(opts->filter.serviceName);
entry->opts.filter.versionRange = celix_utils_strdup(opts->filter.versionRange);
entry->opts.filter.filter = celix_utils_strdup(opts->filter.filter);
}
celixThreadRwlock_writeLock(&ctx->lock);
entry->trackerId = ctx->nextTrackerId++;
long trackerId = entry->trackerId;
celix_longHashMap_put(ctx->serviceTrackers, entry->trackerId, entry);
celixThreadRwlock_unlock(&ctx->lock);
long id = celix_framework_fireGenericEvent(ctx->framework,
createEventId,
celix_bundle_getId(ctx->bundle),
"create service tracker event",
entry,
celix_bundleContext_createTrackerOnEventLoop,
entry,
celix_bundleContext_doneCreatingTrackerOnEventLoop);
if (!async) {
celix_framework_waitForGenericEvent(ctx->framework, id);
}
return trackerId;
}
}
long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts) {
return celix_bundleContext_trackServicesWithOptionsInternal(ctx, opts, false);
}
long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts) {
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->lock must be taken.
*/
static celix_bundle_context_service_tracker_entry_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;
}
if (!entry->tracker && entry->createEventId >= 0) { // tracker not yet created (async creation)
fw_log(
ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Tracker with id %li is not yet created.", entry->trackerId);
return NULL;
}
return entry;
}
static size_t celix_bundleContext_useTrackedServiceWithOptionsInternal(celix_bundle_context_t* ctx,
long trackerId,
const celix_tracked_service_use_options_t* opts,
bool singleUse) {
celixThreadRwlock_readLock(&ctx->lock);
celix_bundle_context_service_tracker_entry_t* trkEntry = celix_bundleContext_findServiceTracker(ctx, trackerId);
if (trkEntry) {
// note use count is only increased inside a read (shared) lock and ensures that the trkEntry is not freed and
// the trkEntry->tracker is not destroyed until the use count drops back to 0.
(void)__atomic_fetch_add(&trkEntry->useCount, 1, __ATOMIC_RELAXED);
}
celixThreadRwlock_unlock(&ctx->lock);
if (!trkEntry) {
return 0;
}
size_t callCount;
if (singleUse) {
callCount = celix_serviceTracker_useHighestRankingService(trkEntry->tracker,
NULL,
0,
opts->callbackHandle,
opts->use,
opts->useWithProperties,
opts->useWithOwner);
} else {
callCount = celix_serviceTracker_useServices(
trkEntry->tracker, NULL, opts->callbackHandle, opts->use, opts->useWithProperties, opts->useWithOwner);
}
(void)__atomic_fetch_sub(&trkEntry->useCount, 1, __ATOMIC_RELEASE);
return callCount;
}
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);
}
static 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_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock);
celix_bundle_context_service_tracker_entry_t* trkEntry = celix_bundleContext_findServiceTracker(ctx, trackerId);
if (!trkEntry) {
return;
}
if (trackedServiceCount) {
*trackedServiceCount = celix_serviceTracker_getTrackedServiceCount(trkEntry->tracker);
}
if (trackedServiceName) {
*trackedServiceName = celix_serviceTracker_getTrackedServiceName(trkEntry->tracker);
}
if (trackedServiceFilter) {
*trackedServiceFilter = celix_serviceTracker_getTrackedServiceFilter(trkEntry->tracker);
}
}
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_rwlock_rlock_guard_t) lck = celixRwlockRlockGuard_init(&ctx->lock);
return celix_longHashMap_hasKey(ctx->serviceTrackers, trackerId) ||
celix_longHashMap_hasKey(ctx->bundleTrackers, trackerId) ||
celix_longHashMap_hasKey(ctx->metaTrackers, 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;
return celix_bundleContext_findServiceWithOptions(ctx, &opts);
}
long celix_bundleContext_findServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts) {
long result = -1L;
char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter);
if (filter != NULL) {
celix_array_list_t *svcIds = celix_serviceRegistry_findServices(ctx->framework->registry, filter);
if (svcIds != NULL && celix_arrayList_size(svcIds) > 0) {
result = celix_arrayList_getLong(svcIds, 0);
}
if (svcIds != NULL) {
celix_arrayList_destroy(svcIds);
}
free(filter);
}
return result;
}
celix_array_list_t* celix_bundleContext_findServices(celix_bundle_context_t *ctx, const char *serviceName) {
celix_service_filter_options_t opts = CELIX_EMPTY_SERVICE_FILTER_OPTIONS;
opts.serviceName = serviceName;
return celix_bundleContext_findServicesWithOptions(ctx, &opts);
}
celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts) {
celix_array_list_t* result = NULL;
char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter);
if (filter != NULL) {
result = celix_serviceRegistry_findServices(ctx->framework->registry, filter);
free(filter);
}
return result;
}
static celix_status_t bundleContext_callServicedTrackerTrackerCallback(void *handle, celix_array_list_t *listeners, bool add) {
celix_bundle_context_service_tracker_tracker_entry_t *entry = handle;
if (entry != NULL) {
int size = celix_arrayList_size(listeners);
for (int i = 0; i < size; ++i) {
listener_hook_info_pt info = celix_arrayList_get(listeners, i);
celix_bundle_t *bnd = NULL;
bundleContext_getBundle(info->context, &bnd);
celix_autoptr(celix_filter_t) filter = celix_filter_create(info->filter);
celix_service_tracker_info_t trkInfo;
memset(&trkInfo, 0, sizeof(trkInfo));
trkInfo.bundleId = celix_bundle_getId(bnd);
trkInfo.filter = filter;
trkInfo.serviceName = celix_filter_findAttribute(filter, CELIX_FRAMEWORK_SERVICE_NAME);
const char *filterSvcName = celix_filter_findAttribute(filter, CELIX_FRAMEWORK_SERVICE_NAME);
bool match = entry->serviceName == NULL || (filterSvcName != NULL && strncmp(filterSvcName, entry->serviceName, 1024*1024) == 0);
if (add && entry->add != NULL && match) {
entry->add(entry->callbackHandle, &trkInfo);
} else if (!add && entry->remove != NULL && match) {
entry->remove(entry->callbackHandle, &trkInfo);
}
}
}
return CELIX_SUCCESS;
}
static celix_status_t bundleContext_callServicedTrackerTrackerAdd(void *handle, celix_array_list_t *listeners) {
return bundleContext_callServicedTrackerTrackerCallback(handle, listeners, true);
}
static celix_status_t bundleContext_callServicedTrackerTrackerRemove(void *handle, celix_array_list_t *listeners) {
return bundleContext_callServicedTrackerTrackerCallback(handle, listeners, false);
}
long celix_bundleContext_trackServiceTrackersInternal(
celix_bundle_context_t *ctx,
const char *serviceName,
void *callbackHandle,
void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info),
bool async,
void *doneData,
void (*doneCallback)(void*)) {
if (serviceName == NULL) {
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_DEBUG, "Note starting a meta tracker for all services");
}
celix_bundle_context_service_tracker_tracker_entry_t *entry = calloc(1, sizeof(*entry));
celixThreadRwlock_writeLock(&ctx->lock);
entry->trackerId = ctx->nextTrackerId++;
celixThreadRwlock_unlock(&ctx->lock);
entry->ctx = ctx;
entry->callbackHandle = callbackHandle;
entry->add = trackerAdd;
entry->remove = trackerRemove;
entry->serviceName = celix_utils_strdup(serviceName);
entry->hook.handle = entry;
entry->hook.added = bundleContext_callServicedTrackerTrackerAdd;
entry->hook.removed = bundleContext_callServicedTrackerTrackerRemove;
if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
//already on event loop, registering the "traditional way" i.e. chaining on the current thread
service_registration_t* reg = NULL;
bundleContext_registerService(ctx, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, &reg);
entry->serviceId = serviceRegistration_getServiceId(reg);
} else if (async) {
entry->serviceId = celix_framework_registerServiceAsync(ctx->framework, ctx->bundle, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, NULL, NULL, NULL, doneData, doneCallback);
} else {
entry->serviceId = celix_framework_registerServiceAsync(ctx->framework, ctx->bundle, OSGI_FRAMEWORK_LISTENER_HOOK_SERVICE_NAME, &entry->hook, NULL, NULL, NULL, NULL, doneData, doneCallback);
celix_framework_waitForAsyncRegistration(ctx->framework, entry->serviceId);
}
if (entry->serviceId >= 0) {
celixThreadRwlock_writeLock(&ctx->lock);
celix_longHashMap_put(ctx->metaTrackers, entry->trackerId, entry);
long trkId = entry->trackerId;
celixThreadRwlock_unlock(&ctx->lock);
return trkId;
} else {
celix_framework_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__, "Error registering service listener hook for service tracker tracker\n");
free(entry);
return -1L;
}
}
long celix_bundleContext_trackServiceTrackers(
celix_bundle_context_t *ctx,
const char *serviceName,
void *callbackHandle,
void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info)) {
return celix_bundleContext_trackServiceTrackersInternal(ctx, serviceName, callbackHandle, trackerAdd, trackerRemove, false, NULL, NULL);
}
long celix_bundleContext_trackServiceTrackersAsync(
celix_bundle_context_t *ctx,
const char *serviceName,
void *callbackHandle,
void (*trackerAdd)(void *handle, const celix_service_tracker_info_t *info),
void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info),
void *doneCallbackData,
void (*doneCallback)(void* doneCallbackData)) {
return celix_bundleContext_trackServiceTrackersInternal(ctx, serviceName, callbackHandle, trackerAdd, trackerRemove, true, doneCallbackData, doneCallback);
}
void celix_bundleContext_waitForEvents(celix_bundle_context_t* ctx) {
celix_framework_waitUntilNoEventsForBnd(ctx->framework, celix_bundle_getId(ctx->bundle));
}
long celix_bundleContext_scheduleEvent(celix_bundle_context_t* ctx,
const celix_scheduled_event_options_t* options) {
return celix_framework_scheduleEvent(ctx->framework,
celix_bundle_getId(ctx->bundle),
options->name,
options->initialDelayInSeconds,
options->intervalInSeconds,
options->callbackData,
options->callback,
options->removeCallbackData,
options->removeCallback);
}
celix_status_t celix_bundleContext_wakeupScheduledEvent(celix_bundle_context_t* ctx, long scheduledEventId) {
return celix_framework_wakeupScheduledEvent(ctx->framework, scheduledEventId);
}
celix_status_t celix_bundleContext_waitForScheduledEvent(celix_bundle_context_t* ctx,
long scheduledEventId,
double waitTimeInSeconds) {
return celix_framework_waitForScheduledEvent(ctx->framework, scheduledEventId, waitTimeInSeconds);
}
bool celix_bundleContext_removeScheduledEvent(celix_bundle_context_t* ctx, long scheduledEventId) {
return celix_framework_removeScheduledEvent(ctx->framework, false, true, scheduledEventId);
}
bool celix_bundleContext_removeScheduledEventAsync(celix_bundle_context_t* ctx, long scheduledEventId) {
return celix_framework_removeScheduledEvent(ctx->framework, true, true, scheduledEventId);
}
bool celix_bundleContext_tryRemoveScheduledEventAsync(celix_bundle_context_t* ctx, long scheduledEventId) {
return celix_framework_removeScheduledEvent(ctx->framework, true, false, scheduledEventId);
}
celix_bundle_t* celix_bundleContext_getBundle(const celix_bundle_context_t *ctx) {
celix_bundle_t *bnd = NULL;
if (ctx != NULL) {
bnd = ctx->bundle;
}
return bnd;
}
long celix_bundleContext_getBundleId(const celix_bundle_context_t *ctx) {
celix_bundle_t *bnd = celix_bundleContext_getBundle(ctx);
return bnd == NULL ? -1L : celix_bundle_getId(bnd);
}
celix_framework_t* celix_bundleContext_getFramework(const celix_bundle_context_t *ctx) {
celix_framework_t *fw = NULL;
if (ctx != NULL) {
fw = ctx->framework;
}
return fw;
}
const char* celix_bundleContext_getProperty(celix_bundle_context_t *ctx, const char *key, const char* defaultValue) {
const char* val = defaultValue;
if (ctx != NULL) {
val = celix_framework_getConfigProperty(ctx->framework, key, defaultValue, NULL);
}
return val;
}
long celix_bundleContext_getPropertyAsLong(celix_bundle_context_t *ctx, const char *key, long defaultValue) {
long val = defaultValue;
if (ctx != NULL) {
val = celix_framework_getConfigPropertyAsLong(ctx->framework, key, defaultValue, NULL);
}
return val;
}
double celix_bundleContext_getPropertyAsDouble(celix_bundle_context_t *ctx, const char *key, double defaultValue) {
double val = defaultValue;
if (ctx != NULL) {
val = celix_framework_getConfigPropertyAsDouble(ctx->framework, key, defaultValue, NULL);
}
return val;
}
bool celix_bundleContext_getPropertyAsBool(celix_bundle_context_t *ctx, const char *key, bool defaultValue) {
bool val = defaultValue;
if (ctx != NULL) {
val = celix_framework_getConfigPropertyAsBool(ctx->framework, key, defaultValue, NULL);
}
return val;
}
static void celix_bundleContext_getBundleSymbolicNameCallback(void *data, const celix_bundle_t *bnd) {
char **out = data;
*out = celix_utils_strdup(celix_bundle_getSymbolicName(bnd));
}
char* celix_bundleContext_getBundleSymbolicName(celix_bundle_context_t *ctx, long bndId) {
char *name = NULL;
celix_framework_useBundle(ctx->framework, false, bndId, &name, celix_bundleContext_getBundleSymbolicNameCallback);
return name;
}
void celix_bundleContext_log(const celix_bundle_context_t *ctx, celix_log_level_e level, const char* format, ...) {
va_list args;
va_start(args, format);
celix_bundleContext_vlog(ctx, level, format, args);
va_end(args);
}
void celix_bundleContext_vlog(const celix_bundle_context_t *ctx, celix_log_level_e level, const char *format, va_list formatArgs) {
celix_framework_vlog(ctx->framework->logger, level, NULL, NULL, 0, format, formatArgs);
}
void celix_bundleContext_logTssErrors(const celix_bundle_context_t *ctx, celix_log_level_e level) {
celix_framework_logTssErrors(ctx->framework->logger, level);
}