blob: 2f1473d7912f5ad30e95389e4b0eb08d0328cc05 [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 "celix_utils.h"
#include "celix_constants.h"
#include "bundle_context_private.h"
#include "framework_private.h"
#include "bundle.h"
#include "celix_bundle.h"
#include "celix_log.h"
#include "service_tracker.h"
#include "celix_dependency_manager.h"
#include "dm_dependency_manager_impl.h"
#include "celix_array_list.h"
#include "module.h"
#include "service_tracker_private.h"
#include "celix_array_list.h"
static celix_status_t bundleContext_bundleChanged(void *handle, 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;
celixThreadMutex_create(&context->mutex, NULL);
arrayList_create(&context->svcRegistrations);
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->nextTrackerId = 1L;
*bundle_context = context;
}
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to create context");
return status;
}
celix_status_t bundleContext_destroy(bundle_context_pt context) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL) {
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_arrayList_size(context->svcRegistrations) == 0);
celix_arrayList_destroy(context->svcRegistrations);
hashMap_destroy(context->stoppingTrackerEventIds, false, false);
celixThreadMutex_destroy(&context->mutex);
if (context->mng != NULL) {
celix_dependencyManager_removeAllComponents(context->mng);
celix_private_dependencyManager_destroy(context->mng);
context->mng = NULL;
}
free(context);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to destroy context");
return status;
}
void celix_bundleContext_cleanup(celix_bundle_context_t *ctx) {
//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) {
celix_status_t status = CELIX_SUCCESS;
celix_bundle_t *bnd = celix_bundleContext_getBundle(context);
if (context == NULL) {
status = CELIX_ILLEGAL_ARGUMENT;
}
if (out != NULL) {
*out = bnd;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get bundle");
return status;
}
celix_status_t bundleContext_getFramework(bundle_context_pt context, framework_pt *framework) {
celix_status_t status = CELIX_SUCCESS;
if (context == NULL) {
status = CELIX_ILLEGAL_ARGUMENT;
} else {
*framework = context->framework;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get framework");
return status;
}
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, bundle_pt *bundle) {
celix_status_t status = CELIX_SUCCESS;
bundle_pt b = NULL;
if (context != NULL) {
if (fw_installBundle(context->framework, &b, location, inputFile) != CELIX_SUCCESS) {
status = CELIX_FRAMEWORK_EXCEPTION;
} else {
*bundle = b;
}
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to install bundle");
return status;
}
celix_status_t bundleContext_registerService(bundle_context_pt context, const char * serviceName, const void * svcObj,
properties_pt properties, service_registration_pt *service_registration) {
service_registration_pt registration = NULL;
celix_status_t status = CELIX_SUCCESS;
if (context != NULL) {
long bndId = celix_bundle_getId(context->bundle);
fw_registerService(context->framework, &registration, bndId, serviceName, svcObj, properties);
*service_registration = registration;
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to register service. serviceName '%s'", serviceName);
return status;
}
celix_status_t bundleContext_registerServiceFactory(bundle_context_pt context, const char * serviceName, service_factory_pt factory,
properties_pt properties, service_registration_pt *service_registration) {
service_registration_pt registration = NULL;
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && *service_registration == NULL) {
long bndId = celix_bundle_getId(context->bundle);
fw_registerServiceFactory(context->framework, &registration, bndId, serviceName, factory, properties);
*service_registration = registration;
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to register service factory");
return status;
}
celix_status_t bundleContext_getServiceReferences(bundle_context_pt context, const char * serviceName, const char * filter, array_list_pt *service_references) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && *service_references == NULL) {
fw_getServiceReferences(context->framework, service_references, context->bundle, serviceName, filter);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get service references");
return status;
}
celix_status_t bundleContext_getServiceReference(bundle_context_pt context, const char * serviceName, service_reference_pt *service_reference) {
service_reference_pt reference = NULL;
array_list_pt services = NULL;
celix_status_t status = CELIX_SUCCESS;
if (serviceName != NULL) {
if (bundleContext_getServiceReferences(context, serviceName, NULL, &services) == CELIX_SUCCESS) {
reference = (arrayList_size(services) > 0) ? arrayList_get(services, 0) : NULL;
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;
}
FRAMEWORK_EXPORT celix_status_t bundleContext_retainServiceReference(bundle_context_pt context, service_reference_pt ref) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && ref != NULL) {
serviceRegistry_retainServiceReference(context->framework->registry, context->bundle, ref);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get service references");
return status;
}
celix_status_t bundleContext_ungetServiceReference(bundle_context_pt context, service_reference_pt reference) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && reference != NULL) {
status = framework_ungetServiceReference(context->framework, context->bundle, reference);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to unget service_reference");
return status;
}
celix_status_t bundleContext_getService(bundle_context_pt context, service_reference_pt reference, void** service_instance) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && reference != NULL && service_instance != NULL) {
/*NOTE argument service_instance should be considered a 'const void**'.
To ensure backwards compatibility a cast is made instead.
*/
status = fw_getService(context->framework, context->bundle, reference, (const void**) service_instance);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
if (status != CELIX_SUCCESS) {
fw_log(context->framework->logger, CELIX_LOG_LEVEL_ERROR, "Failed to get service");
}
return status;
}
celix_status_t bundleContext_ungetService(bundle_context_pt context, service_reference_pt reference, bool *result) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && reference != NULL) {
status = framework_ungetService(context->framework, context->bundle, reference, result);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to unget service");
return status;
}
celix_status_t bundleContext_getBundles(bundle_context_pt context, array_list_pt *bundles) {
celix_status_t status = CELIX_SUCCESS;
if (context == NULL || *bundles != NULL) {
status = CELIX_ILLEGAL_ARGUMENT;
} else {
*bundles = framework_getBundles(context->framework);
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to get bundles");
return status;
}
celix_status_t bundleContext_getBundleById(bundle_context_pt context, long id, bundle_pt *bundle) {
celix_status_t status = CELIX_SUCCESS;
if (context == NULL || *bundle != NULL) {
status = CELIX_ILLEGAL_ARGUMENT;
} else {
*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) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && listener != NULL) {
fw_addServiceListener(context->framework, context->bundle, listener, filter);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to add service listener");
return status;
}
celix_status_t bundleContext_removeServiceListener(bundle_context_pt context, celix_service_listener_t *listener) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && listener != NULL) {
fw_removeServiceListener(context->framework, context->bundle, listener);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to remove service listener");
return status;
}
celix_status_t bundleContext_addBundleListener(bundle_context_pt context, bundle_listener_pt listener) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && listener != NULL) {
fw_addBundleListener(context->framework, context->bundle, listener);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to add bundle listener");
return status;
}
celix_status_t bundleContext_removeBundleListener(bundle_context_pt context, bundle_listener_pt listener) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && listener != NULL) {
fw_removeBundleListener(context->framework, context->bundle, listener);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to remove bundle listener");
return status;
}
celix_status_t bundleContext_addFrameworkListener(bundle_context_pt context, framework_listener_pt listener) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && listener != NULL) {
fw_addFrameworkListener(context->framework, context->bundle, listener);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to add framework listener");
return status;
}
celix_status_t bundleContext_removeFrameworkListener(bundle_context_pt context, framework_listener_pt listener) {
celix_status_t status = CELIX_SUCCESS;
if (context != NULL && listener != NULL) {
fw_removeFrameworkListener(context->framework, context->bundle, listener);
} else {
status = CELIX_ILLEGAL_ARGUMENT;
}
framework_logIfError(context->framework->logger, status, NULL, "Failed to remove framework listener");
return status;
}
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);
}
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_properties_t *props = opts->properties;
if (props == NULL) {
props = celix_properties_create();
}
if (opts->serviceVersion != NULL && strncmp("", opts->serviceVersion, 1) != 0) {
celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_VERSION, opts->serviceVersion);
}
const char *lang = opts->serviceLanguage != NULL && strncmp("", opts->serviceLanguage, 1) != 0 ? opts->serviceLanguage : CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
long svcId = -1;
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, 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, 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) {
properties_destroy(props);
} else {
celixThreadMutex_lock(&ctx->mutex);
celix_arrayList_addLong(ctx->svcRegistrations, svcId);
celixThreadMutex_unlock(&ctx->mutex);
}
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) {
celixThreadMutex_lock(&ctx->mutex);
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;
}
}
celixThreadMutex_unlock(&ctx->mutex);
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) {
celix_dependency_manager_t* result = NULL;
if (ctx != NULL) {
celixThreadMutex_lock(&ctx->mutex);
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");
}
result = ctx->mng;
celixThreadMutex_unlock(&ctx->mutex);
}
return result;
}
static celix_status_t bundleContext_bundleChanged(void *listenerSvc, bundle_event_t *event) {
celix_status_t status = CELIX_SUCCESS;
bundle_listener_t *listener = listenerSvc;
celix_bundle_context_bundle_tracker_entry_t *tracker = NULL;
if (listener != NULL) {
tracker = listener->handle;
}
bool handleEvent = true;
long bndId = celix_bundle_getId(event->bnd);
if (bndId == 0 /*framework bundle*/) {
handleEvent = tracker->opts.includeFrameworkBundle;
}
if (tracker != NULL && 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 status;
}
void celix_bundleContext_trackBundlesWithOptionsCallback(void *data) {
celix_bundle_context_bundle_tracker_entry_t* entry = data;
assert(celix_framework_isCurrentThreadTheEventLoop(entry->ctx->framework));
fw_addBundleListener(entry->ctx->framework, entry->ctx->bundle, &entry->listener);
}
static long celix_bundleContext_trackBundlesWithOptionsInternal(
bundle_context_t* ctx,
const celix_bundle_tracking_options_t *opts,
bool async) {
long trackerId = -1;
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;
celixThreadMutex_lock(&ctx->mutex);
entry->trackerId = ctx->nextTrackerId++;
hashMap_put(ctx->bundleTrackers, (void*)(entry->trackerId), entry);
trackerId = entry->trackerId;
celixThreadMutex_unlock(&ctx->mutex);
void (*trackerCreatedCallback)(void *trackerCreatedCallbackData) = NULL;
if (async) { //note only using the async callback if this is a async call.
trackerCreatedCallback = opts->trackerCreatedCallback;
}
long id = celix_framework_fireGenericEvent(
ctx->framework,
entry->createEventId,
celix_bundle_getId(ctx->bundle),
"add bundle listener",
entry,
celix_bundleContext_trackBundlesWithOptionsCallback,
opts->trackerCreatedCallbackData,
trackerCreatedCallback);
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);
}
void celix_bundleContext_useBundles(
bundle_context_t *ctx,
void *callbackHandle,
void (*use)(void *handle, const bundle_t *bundle)) {
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, true, bundleId, callbackHandle, use);
}
static void bundleContext_cleanupBundleTrackers(bundle_context_t *ctx) {
module_pt module;
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) {
danglingTrkIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingTrkIds, trkId);
}
celixThreadMutex_unlock(&ctx->mutex);
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) {
module_pt module;
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);
if (danglingTrkIds == NULL) {
danglingTrkIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingTrkIds, trkId);
}
celixThreadMutex_unlock(&ctx->mutex);
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) {
module_pt module;
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);
if (danglingTrkIds == NULL) {
danglingTrkIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingTrkIds, trkId);
}
celixThreadMutex_unlock(&ctx->mutex);
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) {
module_pt module;
const char *symbolicName;
bundle_getCurrentModule(ctx->bundle, &module);
module_getSymbolicName(module, &symbolicName);
celix_array_list_t* danglingSvcIds = NULL;
celixThreadMutex_lock(&ctx->mutex);
for (int i = 0; i < celix_arrayList_size(ctx->svcRegistrations); ++i) {
service_registration_pt reg = celix_arrayList_get(ctx->svcRegistrations, i);
long svcId = serviceRegistration_getServiceId(reg);
const char* svcName = NULL;
serviceRegistration_getServiceName(reg, &svcName);
fw_log(ctx->framework->logger, CELIX_LOG_LEVEL_ERROR, "Dangling service registration with svcId %li, for bundle %s and with service name %s. Add missing 'celix_bundleContext_unregisterService' calls.", svcId, symbolicName, svcName);
if (danglingSvcIds == NULL) {
danglingSvcIds = celix_arrayList_create();
}
celix_arrayList_addLong(danglingSvcIds, svcId);
}
celixThreadMutex_unlock(&ctx->mutex);
if (danglingSvcIds != NULL) {
for (int i = 0; i < celix_arrayList_size(danglingSvcIds); ++i) {
long svcId = celix_arrayList_getLong(danglingSvcIds, i);
celix_bundleContext_unregisterService(ctx, 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);
celixThreadMutex_lock(&tracker->ctx->mutex);
hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId);
celixThreadMutex_unlock(&tracker->ctx->mutex);
free(tracker);
}
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);
celixThreadMutex_unlock(&tracker->ctx->mutex);
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);
celixThreadMutex_lock(&tracker->ctx->mutex);
hashMap_remove(tracker->ctx->stoppingTrackerEventIds, (void*)tracker->trackerId);
celixThreadMutex_unlock(&tracker->ctx->mutex);
free(tracker->serviceName);
free(tracker);
}
static void celix_bundleContext_stopTrackerInternal(bundle_context_t *ctx, long trackerId, bool async, void *doneData, void (*doneCallback)(void* doneData)) {
if (ctx == NULL || trackerId <= 0) {
return;
}
bool found = 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;
celixThreadMutex_lock(&ctx->mutex);
if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) {
found = true;
bundleTracker = hashMap_remove(ctx->bundleTrackers, (void *) trackerId);
} else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) {
found = true;
serviceTracker = hashMap_remove(ctx->serviceTrackers, (void *) trackerId);
} else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) {
found = true;
svcTrackerTracker = hashMap_remove(ctx->metaTrackers, (void *) trackerId);
}
if (found && !async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
//already on the event loop, stop tracker "traditionally" to keep old behavior
celixThreadMutex_unlock(&ctx->mutex); //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_serviceTracker_destroy(serviceTracker->tracker);
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);
hashMap_put(ctx->stoppingTrackerEventIds, (void*)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");
}
celixThreadMutex_unlock(&ctx->mutex);
} 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");
}
celixThreadMutex_unlock(&ctx->mutex);
celix_framework_waitForGenericEvent(ctx->framework, eventId);
} else {
celixThreadMutex_unlock(&ctx->mutex);
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;
}
bool found = false;
long eventId = -1;
long svcId = -1;
if (waitForStart) {
celixThreadMutex_lock(&ctx->mutex);
if (hashMap_containsKey(ctx->bundleTrackers, (void *) trackerId)) {
found = true;
celix_bundle_context_bundle_tracker_entry_t* bundleTracker = hashMap_get(ctx->bundleTrackers, (void *) trackerId);
eventId = bundleTracker->createEventId;
} else if (hashMap_containsKey(ctx->serviceTrackers, (void *) trackerId)) {
found = true;
celix_bundle_context_service_tracker_entry_t* serviceTracker = hashMap_get(ctx->serviceTrackers, (void *) trackerId);
eventId = serviceTracker->createEventId;
} else if (hashMap_containsKey(ctx->metaTrackers, (void *) trackerId)) {
found = true;
celix_bundle_context_service_tracker_tracker_entry_t* svcTrackerTracker = hashMap_get(ctx->metaTrackers, (void *) trackerId);
svcId = svcTrackerTracker->serviceId;
}
celixThreadMutex_unlock(&ctx->mutex);
} else {
celixThreadMutex_lock(&ctx->mutex);
if (hashMap_containsKey(ctx->stoppingTrackerEventIds, (void*)trackerId)) {
found = true;
eventId = (long)hashMap_get(ctx->stoppingTrackerEventIds, (void*)trackerId);
}
celixThreadMutex_unlock(&ctx->mutex);
}
if (found) {
if (svcId >= 0 && waitForStart) {
celix_framework_waitForAsyncRegistration(ctx->framework, svcId);
} else if (svcId >= 0) {
celix_framework_waitForAsyncUnregistration(ctx->framework, svcId);
} else {
celix_framework_waitForGenericEvent(ctx->framework, eventId);
}
}
}
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);
}
static void bundleContext_listBundlesCallback(void *handle, const bundle_t *c_bnd) {
celix_array_list_t* ids = handle;
long id = celix_bundle_getId(c_bnd);
if (id > 0) { //note skipping framework bundle id (0)
celix_arrayList_addLong(ids, id);
}
}
celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx) {
celix_array_list_t *result = celix_arrayList_create();
celix_bundleContext_useBundles(ctx, result, bundleContext_listBundlesCallback);
return result;
}
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 bundleId) {
return celix_framework_startBundle(ctx->framework, bundleId);
}
bool celix_bundleContext_stopBundle(celix_bundle_context_t *ctx, long bundleId) {
return celix_framework_stopBundle(ctx->framework, bundleId);
}
bool celix_bundleContext_uninstallBundle(bundle_context_t *ctx, long bundleId) {
return celix_framework_uninstallBundle(ctx->framework, bundleId);
}
bool celix_bundleContext_useServiceWithId(
bundle_context_t *ctx,
long serviceId,
const char *serviceName,
void *callbackHandle,
void (*use)(void *handle, void *svc)) {
celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS;
char filter[64];
snprintf(filter, 64, "(%s=%li)", OSGI_FRAMEWORK_SERVICE_ID, serviceId);
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);
}
typedef struct celix_bundle_context_use_service_data {
celix_bundle_context_t* ctx;
celix_service_use_options_t opts;
celix_thread_mutex_t mutex; //protects below
bool called;
size_t count;
celix_service_tracker_t * svcTracker;
} celix_bundle_context_use_service_data_t;
static void celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker(void *data) {
celix_bundle_context_use_service_data_t* d = data;
assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework));
celix_service_tracking_options_t trkOpts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
trkOpts.filter.serviceName = d->opts.filter.serviceName;
trkOpts.filter = d->opts.filter;
trkOpts.filter.versionRange = d->opts.filter.versionRange;
trkOpts.filter.serviceLanguage = d->opts.filter.serviceLanguage;
celixThreadMutex_lock(&d->mutex);
d->svcTracker = celix_serviceTracker_createWithOptions(d->ctx, &trkOpts);
celixThreadMutex_unlock(&d->mutex);
}
static void celix_bundleContext_useServiceWithOptions_2_UseService(void *data) {
celix_bundle_context_use_service_data_t* d = data;
assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework));
celixThreadMutex_lock(&d->mutex);
celix_service_tracker_t *tracker = d->svcTracker;
d->svcTracker = NULL;
celixThreadMutex_unlock(&d->mutex);
bool called = celix_serviceTracker_useHighestRankingService(tracker, d->opts.filter.serviceName, d->opts.callbackHandle, d->opts.use, d->opts.useWithProperties, d->opts.useWithOwner);
celix_serviceTracker_destroy(tracker);
celixThreadMutex_lock(&d->mutex);
d->called = called;
celixThreadMutex_unlock(&d->mutex);
}
bool celix_bundleContext_useServiceWithOptions(
celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts) {
if (opts == NULL) {
return false;
}
celix_bundle_context_use_service_data_t data = {0};
data.ctx = ctx;
data.opts = *opts;
celixThreadMutex_create(&data.mutex, NULL);
struct timespec startTime = celix_gettime(CLOCK_MONOTONIC);
bool useServiceIsDone = false;
do {
if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker(&data);
celix_bundleContext_useServiceWithOptions_2_UseService(&data);
} else {
long eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "create service tracker", &data, celix_bundleContext_useServiceWithOptions_1_CreateServiceTracker, NULL, NULL);
celix_framework_waitForGenericEvent(ctx->framework, eventId);
eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service", &data, celix_bundleContext_useServiceWithOptions_2_UseService, NULL, NULL);
celix_framework_waitForGenericEvent(ctx->framework, eventId);
}
bool timeoutNotUsed = opts->waitTimeoutInSeconds == 0;
bool timeoutExpired = celix_elapsedtime(CLOCK_MONOTONIC, startTime) > opts->waitTimeoutInSeconds;
celixThreadMutex_lock(&data.mutex);
bool called = data.called;
celixThreadMutex_unlock(&data.mutex);
useServiceIsDone = timeoutNotUsed || timeoutExpired || called;
if (!useServiceIsDone) {
usleep(10);
}
} while (!useServiceIsDone);
celixThreadMutex_lock(&data.mutex);
bool called = data.called;
celixThreadMutex_unlock(&data.mutex);
celixThreadMutex_destroy(&data.mutex);
return called;
}
static void celix_bundleContext_useServicesWithOptions_1_CreateServiceTracker(void *data) {
celix_bundle_context_use_service_data_t* d = data;
assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework));
celix_service_tracking_options_t trkOpts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
trkOpts.filter.serviceName = d->opts.filter.serviceName;
trkOpts.filter = d->opts.filter;
trkOpts.filter.versionRange = d->opts.filter.versionRange;
trkOpts.filter.serviceLanguage = d->opts.filter.serviceLanguage;
celixThreadMutex_lock(&d->mutex);
d->svcTracker = celix_serviceTracker_createWithOptions(d->ctx, &trkOpts);
celixThreadMutex_unlock(&d->mutex);
}
static void celix_bundleContext_useServicesWithOptions_2_UseServices(void *data) {
celix_bundle_context_use_service_data_t* d = data;
assert(celix_framework_isCurrentThreadTheEventLoop(d->ctx->framework));
celixThreadMutex_lock(&d->mutex);
celix_service_tracker_t *tracker = d->svcTracker;
d->svcTracker = NULL;
celixThreadMutex_unlock(&d->mutex);
size_t count = celix_serviceTracker_useServices(tracker, d->opts.filter.serviceName, d->opts.callbackHandle, d->opts.use, d->opts.useWithProperties, d->opts.useWithOwner);
celix_serviceTracker_destroy(tracker);
celixThreadMutex_lock(&d->mutex);
d->count = count;
celixThreadMutex_unlock(&d->mutex);
}
size_t celix_bundleContext_useServicesWithOptions(
celix_bundle_context_t *ctx,
const celix_service_use_options_t *opts) {
if (opts == NULL) {
return 0;
}
celix_bundle_context_use_service_data_t data = {0};
data.ctx = ctx;
data.opts = *opts;
if (celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
celix_bundleContext_useServicesWithOptions_1_CreateServiceTracker(&data);
celix_bundleContext_useServicesWithOptions_2_UseServices(&data);
} else {
long eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "create service tracker", &data, celix_bundleContext_useServicesWithOptions_1_CreateServiceTracker, NULL, NULL);
celix_framework_waitForGenericEvent(ctx->framework, eventId);
eventId = celix_framework_fireGenericEvent(ctx->framework, -1, celix_bundle_getId(ctx->bundle), "use service", &data, celix_bundleContext_useServicesWithOptions_2_UseServices, NULL, NULL);
celix_framework_waitForGenericEvent(ctx->framework, eventId);
}
celixThreadMutex_lock(&data.mutex);
size_t count = data.count;
celixThreadMutex_unlock(&data.mutex);
celixThreadMutex_destroy(&data.mutex);
return count;
}
long celix_bundleContext_trackService(
bundle_context_t* ctx,
const char* serviceName,
void* callbackHandle,
void (*set)(void* handle, void* svc)) {
celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.set = set;
return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, false);
}
long celix_bundleContext_trackServiceAsync(
bundle_context_t* ctx,
const char* serviceName,
void* callbackHandle,
void (*set)(void* handle, void* svc)) {
celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.set = set;
return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, true);
}
long celix_bundleContext_trackServices(
bundle_context_t* ctx,
const char* serviceName,
void* callbackHandle,
void (*add)(void* handle, void* svc),
void (*remove)(void* handle, void* svc)) {
celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.add = add;
opts.remove = remove;
return celix_bundleContext_trackServicesWithOptionsInternal(ctx, &opts, false);
}
long celix_bundleContext_trackServicesAsync(
celix_bundle_context_t* ctx,
const char* serviceName,
void* callbackHandle,
void (*add)(void* handle, void* svc),
void (*remove)(void* handle, void* svc)) {
celix_service_tracking_options_t opts = CELIX_EMPTY_SERVICE_TRACKING_OPTIONS;
opts.filter.serviceName = serviceName;
opts.callbackHandle = callbackHandle;
opts.add = add;
opts.remove = remove;
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));
celix_service_tracker_t *tracker = celix_serviceTracker_createWithOptions(entry->ctx, &entry->opts);
if (tracker != NULL) {
celixThreadMutex_lock(&entry->ctx->mutex);
entry->tracker = tracker;
celixThreadMutex_unlock(&entry->ctx->mutex);
} else {
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));
}
}
static void celix_bundleContext_doneCreatingTrackerOnEventLoop(void *data) {
celix_bundle_context_service_tracker_entry_t* entry = data;
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->createEventId = -1;
celixThreadMutex_lock(&ctx->mutex);
entry->trackerId = ctx->nextTrackerId++;
trackerId = entry->trackerId;
hashMap_put(ctx->serviceTrackers, (void *) trackerId, entry);
celixThreadMutex_unlock(&ctx->mutex);
}
return trackerId;
} else {
celix_bundle_context_service_tracker_entry_t* entry = calloc(1, sizeof(*entry));
entry->ctx = ctx;
entry->createEventId = celix_framework_nextEventId(ctx->framework);
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;
}
celixThreadMutex_lock(&ctx->mutex);
entry->trackerId = ctx->nextTrackerId++;
long trackerId = entry->trackerId;
hashMap_put(ctx->serviceTrackers, (void *)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);
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);
}
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, opts->serviceLanguage, opts->ignoreServiceLanguage);
if (filter != NULL) {
celix_array_list_t *svcIds = celix_serviceRegisrty_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, opts->serviceLanguage, opts->ignoreServiceLanguage);
if (filter != NULL) {
result = celix_serviceRegisrty_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) {
size_t size = celix_arrayList_size(listeners);
for (unsigned int i = 0; i < size; ++i) {
listener_hook_info_pt info = arrayList_get(listeners, i);
celix_bundle_t *bnd = NULL;
bundleContext_getBundle(info->context, &bnd);
celix_service_tracker_info_t trkInfo;
memset(&trkInfo, 0, sizeof(trkInfo));
trkInfo.bundleId = celix_bundle_getId(bnd);
trkInfo.filter = celix_filter_create(info->filter);
trkInfo.serviceName = celix_filter_findAttribute(trkInfo.filter, OSGI_FRAMEWORK_OBJECTCLASS);
trkInfo.serviceLanguage = celix_filter_findAttribute(trkInfo.filter, CELIX_FRAMEWORK_SERVICE_LANGUAGE);
const char *filterSvcName = celix_filter_findAttribute(trkInfo.filter, OSGI_FRAMEWORK_OBJECTCLASS);
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);
}
celix_filter_destroy(trkInfo.filter);
}
}
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", __FUNCTION__);
}
celix_bundle_context_service_tracker_tracker_entry_t *entry = calloc(1, sizeof(*entry));
celixThreadMutex_lock(&ctx->mutex);
entry->trackerId = ctx->nextTrackerId++;
celixThreadMutex_unlock(&ctx->mutex);
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) {
celixThreadMutex_lock(&ctx->mutex);
hashMap_put(ctx->metaTrackers, (void*)entry->trackerId, entry);
long trkId = entry->trackerId;
celixThreadMutex_unlock(&ctx->mutex);
return trkId;
} else {
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));
}
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;
}
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 *defaultVal) {
const char *val = NULL;
if (ctx != NULL) {
fw_getProperty(ctx->framework, key, defaultVal, &val);
}
return val;
}
long celix_bundleContext_getPropertyAsLong(celix_bundle_context_t *ctx, const char *key, long defaultValue) {
long result = defaultValue;
const char *val = celix_bundleContext_getProperty(ctx, key, NULL);
if (val != NULL) {
char *enptr = NULL;
errno = 0;
long r = strtol(val, &enptr, 10);
if (enptr != val && errno == 0) {
result = r;
}
}
return result;
}
double celix_bundleContext_getPropertyAsDouble(celix_bundle_context_t *ctx, const char *key, double defaultValue) {
double result = defaultValue;
const char *val = celix_bundleContext_getProperty(ctx, key, NULL);
if (val != NULL) {
char *enptr = NULL;
errno = 0;
double r = strtod(val, &enptr);
if (enptr != val && errno == 0) {
result = r;
}
}
return result;
}
bool celix_bundleContext_getPropertyAsBool(celix_bundle_context_t *ctx, const char *key, bool defaultValue) {
bool result = defaultValue;
const char *val = celix_bundleContext_getProperty(ctx, key, NULL);
if (val != NULL) {
char buf[32];
snprintf(buf, 32, "%s", val);
char *trimmed = utils_stringTrim(buf);
if (strncasecmp("true", trimmed, strlen("true")) == 0) {
result = true;
} else if (strncasecmp("false", trimmed, strlen("false")) == 0) {
result = false;
}
}
return result;
}
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;
}