blob: ede216dafe5429ef1301e0f499ae34ced4057dc1 [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.
*/
#ifndef FRAMEWORK_PRIVATE_H_
#define FRAMEWORK_PRIVATE_H_
#include <stdbool.h>
#include "celix_framework.h"
#include "framework.h"
#include "celix_bundle_manifest.h"
#include "hash_map.h"
#include "celix_array_list.h"
#include "celix_errno.h"
#include "service_factory.h"
#include "celix_bundle_archive.h"
#include "celix_service_listener.h"
#include "bundle_listener.h"
#include "framework_listener.h"
#include "service_registration.h"
#include "bundle_context.h"
#include "celix_bundle_cache.h"
#include "celix_log.h"
#include "celix_threads.h"
#include "service_registry.h"
#include "celix_long_hash_map.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CELIX_FRAMEWORK_DEFAULT_STATIC_EVENT_QUEUE_SIZE
#define CELIX_FRAMEWORK_DEFAULT_STATIC_EVENT_QUEUE_SIZE 1024
#endif
#define CELIX_FRAMEWORK_DEFAULT_MAX_TIMEDWAIT_EVENT_HANDLER_IN_SECONDS 1
#define CELIX_FRAMEWORK_CLEAN_CACHE_DIR_ON_CREATE_DEFAULT false
#define CELIX_FRAMEWORK_CACHE_USE_TMP_DIR_DEFAULT false
#define CELIX_FRAMEWORK_CACHE_DIR_DEFAULT ".cache"
typedef struct celix_bundle_entry {
celix_bundle_t *bnd;
celix_thread_rwlock_t fsmMutex; //protects bundle state transition
long bndId;
celix_thread_mutex_t useMutex; //protects useCount
celix_thread_cond_t useCond;
size_t useCount;
} celix_bundle_entry_t;
enum celix_framework_event_type {
CELIX_FRAMEWORK_EVENT_TYPE = 0x01,
CELIX_BUNDLE_EVENT_TYPE = 0x11,
CELIX_REGISTER_SERVICE_EVENT = 0x21,
CELIX_UNREGISTER_SERVICE_EVENT = 0x22,
CELIX_GENERIC_EVENT = 0x30
};
typedef enum celix_framework_event_type celix_framework_event_type_e;
struct celix_framework_event {
celix_framework_event_type_e type;
celix_bundle_entry_t* bndEntry;
void *doneData;
void (*doneCallback)(void*);
//for framework event
framework_event_type_e fwEvent;
celix_status_t errorCode;
const char *error;
//for bundle event
bundle_event_type_e bundleEvent;
//for register event
long registerServiceId;
bool cancelled;
char *serviceName;
void *svc;
celix_service_factory_t* factory;
celix_properties_t* properties;
void* registerData;
void (*registerCallback)(void *data, long serviceId);
//for unregister event
long unregisterServiceId;
//for the generic event
long genericEventId;
const char* genericEventName;
void *genericProcessData;
void (*genericProcess)(void*);
};
typedef struct celix_framework_event celix_framework_event_t;
enum celix_bundle_lifecycle_command {
CELIX_BUNDLE_LIFECYCLE_START,
CELIX_BUNDLE_LIFECYCLE_STOP,
CELIX_BUNDLE_LIFECYCLE_UNINSTALL,
CELIX_BUNDLE_LIFECYCLE_UPDATE,
CELIX_BUNDLE_LIFECYCLE_UNLOAD
};
typedef struct celix_framework_bundle_lifecycle_handler {
celix_framework_t* framework;
celix_bundle_entry_t* bndEntry;
long bndId;
char* updatedBundleUrl; //only relevant and present for update command
enum celix_bundle_lifecycle_command command;
} celix_framework_bundle_lifecycle_handler_t;
struct celix_framework {
celix_bundle_t *bundle;
long bundleId; //the bundle id of the framework (normally 0)
hash_map_pt installRequestMap;
celix_array_list_t* frameworkListeners;
celix_thread_mutex_t frameworkListenersLock;
celix_array_list_t* bundleListeners;
celix_thread_mutex_t bundleListenerLock;
long currentBundleId; //atomic
celix_service_registry_t *registry;
celix_bundle_cache_t* cache;
struct {
celix_thread_mutex_t mutex;
celix_thread_cond_t cond;
bool done; //true is shutdown is done
bool joined; //true if shutdown thread is joined
bool initialized; //true is a shutdown is initialized
celix_thread_t thread;
} shutdown;
celix_thread_mutex_t installLock; // serialize install/uninstall
struct {
celix_array_list_t *entries; //value = celix_framework_bundle_entry_t*. Note ordered by installed bundle time
//i.e. later installed bundle are last
celix_thread_mutex_t mutex;
} installedBundles;
celix_properties_t* configurationMap;
struct {
long nextEventId; //atomic
long nextScheduledEventId; //atomic
celix_thread_cond_t cond;
celix_thread_t thread;
celix_thread_mutex_t mutex; //protects below
bool active;
//normal event queue
celix_framework_event_t* eventQueue; //ring buffer
int eventQueueCap;
int eventQueueSize;
int eventQueueFirstEntry;
celix_array_list_t *dynamicEventQueue; //entry = celix_framework_event_t*. Used when the eventQueue is full
struct {
int nbFramework; // number of pending framework events
int nbBundle; // number of pending bundle events
int nbRegister; // number of pending registration
int nbUnregister; // number of pending async de-registration
int nbEvent; // number of pending generic events
} stats;
celix_long_hash_map_t *scheduledEvents; //key = scheduled event id, entry = celix_framework_scheduled_event_t*. Used for scheduled events
double genericEventTimeoutInSeconds; // Timeout for printing an warning on unfinished generic events
} dispatcher;
celix_framework_logger_t* logger;
struct {
celix_thread_cond_t cond;
celix_thread_mutex_t mutex; //protects below
celix_array_list_t* bundleLifecycleHandlers; //entry = celix_framework_bundle_lifecycle_handler_t*
} bundleLifecycleHandling;
};
/**
* @brief Get the config property for the given key.
*
* The config property is a property from the framework configuration or a system property.
* If a system property is found, the system property is returned.
* Otherwise the framework configuration property - if found - is returned.
*
* @param framework The framework.
* @param name The name of the property.
* @param defaultValue The default value if the property is not found.
* @param found If not NULL, the found flag is set to true if the property is found, otherwise false.
* @return The property value or the default value if the property is not found.
*/
const char* celix_framework_getConfigProperty(celix_framework_t* framework, const char* name, const char* defaultValue, bool* found);
/**
* @brief Get the config property for the given key converted as long value.
*
* The config property is a property from the framework configuration or a system property.
* If a system property is found, the system property is returned.
* Otherwise the framework configuration property - if found - is returned.
*
* @param framework The framework.
* @param name The name of the property.
* @param defaultValue The default value if the property is not found.
* @param found If not NULL, the found flag is set to true if the property is found and converted, otherwise false.
* @return The property value or the default value if the property is not found or the property value cannot be converted
* to a long value.
*/
long celix_framework_getConfigPropertyAsLong(celix_framework_t* framework, const char* name, long defaultValue, bool* found);
/**
* @brief Get the config property for the given key converted as double value.
*
* The config property is a property from the framework configuration or a system property.
* If a system property is found, the system property is returned.
* Otherwise the framework configuration property - if found - is returned.
*
* @param framework The framework.
* @param name The name of the property.
* @param defaultValue The default value if the property is not found.
* @param found If not NULL, the found flag is set to true if the property is found and converted, otherwise false.
* @return The property value or the default value if the property is not found or the property value cannot be converted
* to a double value.
*/
double celix_framework_getConfigPropertyAsDouble(celix_framework_t* framework, const char* name, double defaultValue, bool* found);
/**
* @brief Get the config property for the given key converted as bool value.
*
* The config property is a property from the framework configuration or a system property.
* If a system property is found, the system property is returned.
* Otherwise the framework configuration property - if found - is returned.
*
* @param framework The framework.
* @param name The name of the property.
* @param defaultValue The default value if the property is not found.
* @param found If not NULL, the found flag is set to true if the property is found and converted, otherwise false.
* @return The property value or the default value if the property is not found or the property value cannot be converted
* to a bool value.
*/
bool celix_framework_getConfigPropertyAsBool(celix_framework_t* framework, const char* name, bool defaultValue, bool* found);
celix_status_t celix_framework_installBundleInternal(celix_framework_t* framework, const char* bndLoc, long* bndId);
celix_status_t fw_registerService(framework_pt framework, service_registration_pt * registration, long bundleId, const char* serviceName, const void* svcObj, celix_properties_t* properties);
celix_status_t fw_registerServiceFactory(framework_pt framework, service_registration_pt * registration, long bundleId, const char* serviceName, service_factory_pt factory, celix_properties_t* properties);
celix_status_t fw_getServiceReferences(framework_pt framework, celix_array_list_t** references, bundle_pt bundle, const char* serviceName, const char* filter);
celix_status_t fw_getBundleRegisteredServices(framework_pt framework, bundle_pt bundle, celix_array_list_t** services);
celix_status_t fw_getBundleServicesInUse(framework_pt framework, bundle_pt bundle, celix_array_list_t** services);
void fw_addServiceListener(framework_pt framework, bundle_pt bundle, celix_service_listener_t *listener, const char* filter);
void fw_removeServiceListener(framework_pt framework, bundle_pt bundle, celix_service_listener_t *listener);
celix_status_t fw_addBundleListener(framework_pt framework, bundle_pt bundle, bundle_listener_pt listener);
celix_status_t fw_removeBundleListener(framework_pt framework, bundle_pt bundle, bundle_listener_pt listener);
celix_status_t fw_addFrameworkListener(framework_pt framework, bundle_pt bundle, framework_listener_pt listener);
celix_status_t fw_removeFrameworkListener(framework_pt framework, bundle_pt bundle, framework_listener_pt listener);
celix_array_list_t* framework_getBundles(framework_pt framework) __attribute__((deprecated("not thread safe, use celix_framework_useBundles instead")));
long framework_getBundle(framework_pt framework, const char* location);
bundle_pt framework_getBundleById(framework_pt framework, long id);
/**********************************************************************************************************************
**********************************************************************************************************************
* Updated API
**********************************************************************************************************************
**********************************************************************************************************************/
/**
* register service or service factory. Will return a svc id directly and return a service registration in a callback.
* callback is called on the fw event loop thread
*/
long celix_framework_registerService(framework_t *fw, celix_bundle_t *bnd, const char* serviceName, void* svc, celix_service_factory_t *factory, celix_properties_t *properties);
/**
* register service or service factory async. Will return a svc id directly and return a service registration in a callback.
* callback is called on the fw event loop thread
*/
long celix_framework_registerServiceAsync(
framework_t *fw,
celix_bundle_t *bnd,
const char* serviceName,
void* svc,
celix_service_factory_t* factory,
celix_properties_t *properties,
void* registerDoneData,
void(*registerDoneCallback)(void *registerDoneData, long serviceId),
void* eventDoneData,
void (*eventDoneCallback)(void* eventDoneData));
/**
* Unregister service async on the event loop thread.
*/
void celix_framework_unregisterAsync(celix_framework_t* fw, celix_bundle_t* bnd, long serviceId, void *doneData, void (*doneCallback)(void*));
/**
* Unregister service
*/
void celix_framework_unregister(celix_framework_t* fw, celix_bundle_t* bnd, long serviceId);
/**
* Wait til all service registration or unregistration events for a specific bundle are no longer present in the event queue.
*/
void celix_framework_waitForAsyncRegistrations(celix_framework_t *fw, long bndId);
/**
* Wait til the async service registration for the provided service id is no longer present in the event queue.
*/
void celix_framework_waitForAsyncRegistration(celix_framework_t *fw, long svcId);
/**
* Wait til the async service unregistration for the provided service id is no longer present in the event queue.
*/
void celix_framework_waitForAsyncUnregistration(celix_framework_t *fw, long svcId);
/**
* Increase the use count of a bundle and ensure that a bundle cannot be uninstalled.
*/
void celix_bundleEntry_increaseUseCount(celix_bundle_entry_t*entry);
/**
* Decrease the use count of a bundle.
*/
void celix_bundleEntry_decreaseUseCount(celix_bundle_entry_t*entry);
/**
* @brief Use guard for bundle entry usage.
*/
typedef struct celix_bundle_entry_use_guard {
celix_bundle_entry_t* entry;
} celix_bundle_entry_use_guard_t;
/**
* @brief Initialize a bundle entry use guard.
*/
static CELIX_UNUSED inline celix_bundle_entry_use_guard_t celix_bundleEntryUseGuard_init(celix_bundle_entry_t* entry) {
celix_bundle_entry_use_guard_t guard;
guard.entry = entry;
return guard;
}
/**
* @brief De-initialize a bundle entry use guard.
*/
static CELIX_UNUSED inline void celix_bundleEntryUseGuard_deinit(celix_bundle_entry_use_guard_t* guard) {
if (guard->entry) {
celix_bundleEntry_decreaseUseCount(guard->entry);
guard->entry = NULL;
}
}
CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_bundle_entry_use_guard_t, celix_bundleEntryUseGuard_deinit)
/**
* Find the bundle entry for the bnd id and increase use count
*/
celix_bundle_entry_t* celix_framework_bundleEntry_getBundleEntryAndIncreaseUseCount(celix_framework_t *fw, long bndId);
/**
* @brief Check if the bundle id is already in use.
* Note that this can happen if bundles have been created through already existing bundle archives.
* @param fw The framework.
* @param bndId The bundle id.
* @return True if the bundle id is already in use, false otherwise.
*/
bool celix_framework_isBundleIdAlreadyUsed(celix_framework_t *fw, long bndId);
/**
* @brief Check if a bundle with the provided bundle symbolic name is already installed.
*/
bool celix_framework_isBundleAlreadyInstalled(celix_framework_t* fw, const char* bundleSymbolicName);
/**
* Start a bundle and ensure that this is not done on the Celix event thread.
* Will spawn a thread if needed.
* @param fw The Celix framework
* @param bndEntry A bnd entry
* @param forceSpawnThread If the true, the start bundle will always be done on a spawn thread
* @return CELIX_SUCCESS of the call went alright.
*/
celix_status_t celix_framework_startBundleOnANonCelixEventThread(celix_framework_t* fw,
celix_bundle_entry_t* bndEntry, bool forceSpawnThread);
/**
* Stop a bundle and ensure that this is not done on the Celix event thread.
* Will spawn a thread if needed.
* @param fw The Celix framework
* @param bndEntry A bnd entry
* @param forceSpawnThread If the true, the start bundle will always be done on a spawn thread
* @return CELIX_SUCCESS of the call went alright.
*/
celix_status_t celix_framework_stopBundleOnANonCelixEventThread(celix_framework_t* fw,
celix_bundle_entry_t* bndEntry, bool forceSpawnThread);
/**
* Uninstall (and if needed stop) a bundle and ensure that this is not done on the Celix event thread.
* Will spawn a thread if needed.
* @param fw The Celix framework
* @param bndEntry A bnd entry
* @param forceSpawnThread If the true, the start bundle will always be done on a spawn thread
* @param permanent If true, the bundle will be permanently uninstalled (e.g. the bundle archive will be removed).
* @return CELIX_SUCCESS of the call went alright.
*/
celix_status_t celix_framework_uninstallBundleOnANonCelixEventThread(celix_framework_t* fw,
celix_bundle_entry_t* bndEntry, bool forceSpawnThread, bool permanent);
/**
* Update (and if needed stop and start) a bundle and ensure that this is not done on the Celix event thread.
* Will spawn a thread if needed.
* @param bndEntry A bnd entry
* @param forceSpawnThread If the true, the start bundle will always be done on a spawn thread
* @return CELIX_SUCCESS of the call went alright.
*/
celix_status_t celix_framework_updateBundleOnANonCelixEventThread(celix_framework_t* fw,
celix_bundle_entry_t* bndEntry, const char* updatedBundleUrl, bool forceSpawnThread);
/**
* Wait for all bundle lifecycle handlers finishing their jobs.
* @param fw The Celix framework
*/
void celix_framework_waitForBundleLifecycleHandlers(celix_framework_t* fw);
/**
* Start a bundle. Cannot be called on the Celix event thread.
*/
celix_status_t celix_framework_startBundleEntry(celix_framework_t* fw, celix_bundle_entry_t* bndEntry);
/**
* Stop a bundle. Cannot be called on the Celix event thread.
*/
celix_status_t celix_framework_stopBundleEntry(celix_framework_t* fw, celix_bundle_entry_t* bndEntry);
/**
* Uninstall a bundle. Cannot be called on the Celix event thread.
*/
celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* fw, celix_bundle_entry_t* bndEntry, bool permanent);
/**
* Update a bundle. Cannot be called on the Celix event thread.
*/
celix_status_t celix_framework_updateBundleEntry(celix_framework_t* fw, celix_bundle_entry_t* bndEntry, const char* updatedBundleUrl);
/** @brief Return the next scheduled event id.
* @param[in] fw The Celix framework
* @return The next scheduled event id.
*/
long celix_framework_nextScheduledEventId(framework_t *fw);
/**
* @brief Add a scheduled event to the Celix framework.
*
*
* @param[in] fw The Celix framework
* @param[in] bndId The bundle id to add the scheduled event for.
* @param[in] eventName The event name to use for the scheduled event. If NULL, a default event name is used.
* @param[in] initialDelayInSeconds The initial delay in seconds before the first event callback is called.
* @param[in] intervalInSeconds The interval in seconds between event callbacks.
* @param[in] callbackData The event data to pass to the event callback.
* @param[in] callback The event callback to call when the scheduled event is triggered.
* @param[in] removeCallbackData The removed callback data.
* @param[in] removeCallback The removed callback.
* @return The scheduled event id of the scheduled event. Can be used to cancel the event.
* @retval <0 If the event could not be added.
*/
long celix_framework_scheduleEvent(celix_framework_t* fw,
long bndId,
const char* eventName,
double initialDelayInSeconds,
double intervalInSeconds,
void* callbackData,
void (*callback)(void*),
void* removeCallbackData,
void (*removeCallback)(void*));
/**
* @brief Wakeup a scheduled event and returns immediately, not waiting for the scheduled event callback to be
* called.
*
* Silently ignored if the scheduled event ids < 0.
*
* @param[in] fw The Celix framework
* @param[in] scheduledEventId The scheduled event id to wakeup.
* @return CELIX_SUCCESS if the scheduled event is woken up, CELIX_ILLEGAL_ARGUMENT if the scheduled event id is not known.
*/
celix_status_t celix_framework_wakeupScheduledEvent(celix_framework_t* fw, long scheduledEventId);
/**
* @brief Wait for the next scheduled event to be processed.
*
* Silently ignored if the scheduled event ids < 0.
*
* @param[in] fw The Celix framework
* @param[in] scheduledEventId The scheduled event id to wait for.
* @param[in] waitTimeInSeconds The maximum time to wait for the next scheduled event. If <= 0 the function will return
* immediately.
* @return CELIX_SUCCESS if the scheduled event is done with processing, CELIX_ILLEGAL_ARGUMENT if the scheduled event id is not
* known and ETIMEDOUT if the waitTimeInSeconds is reached.
*/
celix_status_t celix_framework_waitForScheduledEvent(celix_framework_t* fw,
long scheduledEventId,
double waitTimeInSeconds);
/**
* @brief Cancel a scheduled event.
*
* When this function returns, no more scheduled event callbacks will be called.
*
* Silently ignored if the scheduled event ids < 0.
*
* @param[in] fw The Celix framework
* @param[in] async If true, the scheduled event will be cancelled asynchronously and the function will not block.
* @param[in] errorIfNotFound If true, removal of a non existing scheduled event id will be logged.
* @param[in] scheduledEventId The scheduled event id to cancel.
* @return true if a scheduled event is cancelled, false if the scheduled event id is not known.
*/
bool celix_framework_removeScheduledEvent(celix_framework_t* fw, bool async, bool errorIfNotFound, long scheduledEventId);
/**
* Remove all scheduled events for the provided bundle id and logs warning if there are still un-removed scheduled
* events that are not a one time event.
* @param[in] fw The Celix framework.
* @param[in] bndId The bundle id to remove the scheduled events for.
*/
void celix_framework_cleanupScheduledEvents(celix_framework_t* fw, long bndId);
/**
* @brief Start the celix framework shutdown sequence on a separate thread and return immediately.
*/
void celix_framework_shutdownAsync(celix_framework_t* framework);
#ifdef __cplusplus
}
#endif
#endif /* FRAMEWORK_PRIVATE_H_ */