blob: 2afb709fb553bb6446a849dcfc7e754ba5464d48 [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 <string.h>
#include <uuid/uuid.h>
#include <assert.h>
#include "celix_utils.h"
#include "celix_bundle.h"
#include "celix_stdlib_cleanup.h"
#include "celix_constants.h"
#include "celix_filter.h"
#include "dm_component_impl.h"
#include "celix_framework.h"
#include "hash_map.h"
static const char * const CELIX_DM_PRINT_OK_COLOR = "\033[92m";
static const char * const CELIX_DM_PRINT_WARNING_COLOR = "\033[93m";
static const char * const CELIX_DM_PRINT_NOK_COLOR = "\033[91m";
static const char * const CELIX_DM_PRINT_END_COLOR = "\033[m";
struct celix_dm_component_struct {
char uuid[DM_COMPONENT_MAX_ID_LENGTH];
char name[DM_COMPONENT_MAX_NAME_LENGTH];
celix_bundle_context_t* context;
void* implementation;
celix_dm_cmp_impl_destroy_fpt implementationDestroyFn;
celix_dm_cmp_lifecycle_fpt callbackInit;
celix_dm_cmp_lifecycle_fpt callbackStart;
celix_dm_cmp_lifecycle_fpt callbackStop;
celix_dm_cmp_lifecycle_fpt callbackDeinit;
celix_dm_component_state_t state; //atomic
celix_thread_mutex_t mutex; //protects below
celix_array_list_t* providedInterfaces; //type = dm_interface_t*
celix_array_list_t* dependencies; //type = celix_dm_service_dependency_t*
/**
* Removed dependencies, which are potential still being stopped async
*/
celix_array_list_t* removedDependencies; //type = celix_dm_service_dependency_t*
size_t nrOfTimesStarted;
size_t nrOfTimesResumed;
bool isEnabled;
/**
* Whether the component is an a transition (active performTransition call).
* Should only be used inside the Celix event Thread -> no locking needed.
*/
bool inTransition;
};
typedef struct dm_interface_struct {
char* serviceName;
const void* service;
celix_properties_t *properties;
long svcId;
} dm_interface_t;
static celix_status_t celix_dmComponent_registerServices(celix_dm_component_t *component, bool needLock);
static celix_status_t celix_dmComponent_unregisterServices(celix_dm_component_t *component, bool needLock);
static bool celix_dmComponent_areAllRequiredServiceDependenciesResolved(celix_dm_component_t *component);
static bool celix_dmComponent_performTransition(celix_dm_component_t *component, celix_dm_component_state_t oldState, celix_dm_component_state_t newState);
static celix_status_t celix_dmComponent_calculateNewState(celix_dm_component_t *component, celix_dm_component_state_t currentState, celix_dm_component_state_t *newState);
static celix_status_t celix_dmComponent_handleChange(celix_dm_component_t *component);
static celix_status_t celix_dmComponent_handleAdd(celix_dm_component_t *component, const celix_dm_event_t* event);
static celix_status_t celix_dmComponent_handleRemove(celix_dm_component_t *component, const celix_dm_event_t* event);
static celix_status_t celix_dmComponent_handleSet(celix_dm_component_t *component, const celix_dm_event_t* event);
static celix_status_t celix_dmComponent_enableDependencies(celix_dm_component_t *component);
static celix_status_t celix_dmComponent_suspend(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency);
static celix_status_t celix_dmComponent_resume(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency);
static celix_status_t celix_dmComponent_disable(celix_dm_component_t *component);
static void celix_dmComponent_disableDirectly(celix_dm_component_t *component);
static celix_status_t celix_dmComponent_disableDependencies(celix_dm_component_t *component);
static bool celix_dmComponent_isDisabled(celix_dm_component_t *component);
static void celix_dmComponent_cleanupRemovedDependencies(celix_dm_component_t* component);
static bool celix_dmComponent_isActiveInternal(celix_dm_component_t *component);
static void celix_dmComponent_setCurrentState(celix_dm_component_t* cmp, celix_dm_component_state_t s);
static void celix_dmComponent_logTransition(celix_dm_component_t* cmp, celix_dm_component_state_t currentState, celix_dm_component_state_t desiredState);
celix_dm_component_t* celix_dmComponent_create(bundle_context_t *context, const char* name) {
return celix_dmComponent_createWithUUID(context, name, NULL);
}
celix_dm_component_t* celix_dmComponent_createWithUUID(bundle_context_t *context, const char* name, const char *uuidIn) {
celix_dm_component_t *component = calloc(1, sizeof(*component));
char uuidStr[DM_COMPONENT_MAX_ID_LENGTH];
bool genRandomUUID = true;
if (uuidIn != NULL) {
uuid_t uuid;
int rc = uuid_parse(uuidIn, uuid);
if (rc == 0) {
uuid_unparse(uuid, uuidStr);
genRandomUUID = false;
} else {
//parsing went wrong
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_WARNING,
"Cannot parse provided uuid '%s'. Not a valid UUID?. UUID will be generated", uuidIn);
}
}
if (genRandomUUID) {
//gen uuid
uuid_t uuid;
uuid_generate(uuid);
uuid_unparse(uuid, uuidStr);
}
snprintf(component->uuid, DM_COMPONENT_MAX_ID_LENGTH, "%s", uuidStr);
snprintf(component->name, DM_COMPONENT_MAX_NAME_LENGTH, "%s", name == NULL ? "n/a" : name);
component->context = context;
component->implementation = NULL;
component->callbackInit = NULL;
component->callbackStart = NULL;
component->callbackStop = NULL;
component->callbackDeinit = NULL;
component->implementation= NULL;
component->implementationDestroyFn = NULL;
component->state = CELIX_DM_CMP_STATE_INACTIVE;
component->providedInterfaces = celix_arrayList_create();
component->dependencies = celix_arrayList_create();
component->removedDependencies = celix_arrayList_create();
celixThreadMutex_create(&component->mutex, NULL);
component->isEnabled = false;
component->inTransition = false;
return component;
}
celix_status_t component_create(bundle_context_pt context, const char *name, celix_dm_component_t **out) {
celix_status_t status = CELIX_SUCCESS;
celix_dm_component_t *cmp = celix_dmComponent_create(context, name);
if (cmp == NULL) {
status = CELIX_BUNDLE_EXCEPTION;
} else {
*out = cmp;
}
return status;
}
void component_destroy(celix_dm_component_t *component) {
celix_dmComponent_destroy(component);
}
void celix_dmComponent_destroy(celix_dm_component_t *component) {
if (component != NULL) {
if (celix_framework_isCurrentThreadTheEventLoop(celix_bundleContext_getFramework(component->context))) {
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
"Cannot synchronized destroy dm component on Celix event thread. Use celix_dmComponent_destroyAsync instead!");
} else {
celix_bundle_context_t* context = component->context;
celix_dmComponent_destroyAsync(component, NULL, NULL);
celix_bundleContext_waitForEvents(context);
}
}
}
struct celix_dm_component_destroy_data {
celix_dm_component_t* cmp;
void* doneData;
void (*doneCallback)(void*);
};
static void celix_dmComponent_destroyCallback(void *voidData) {
struct celix_dm_component_destroy_data *data = voidData;
celix_dm_component_t *component = data->cmp;
celix_dmComponent_disable(component); //all service unregistered // all svc tracker stopped
if (celix_dmComponent_isDisabled(component)) {
if (component->implementationDestroyFn) {
if (component->implementation == NULL) {
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
"Component `%s` [uuid=%s] has a configured destroy component "
"implementation callback, but the implementation pointer is NULL. "
"Destroy callback will not be called!",
component->name,
component->uuid);
} else {
component->implementationDestroyFn(component->implementation);
}
}
for (int i = 0; i < celix_arrayList_size(component->providedInterfaces); ++i) {
dm_interface_t *interface = celix_arrayList_get(component->providedInterfaces, i);
if (interface->properties != NULL) {
celix_properties_destroy(interface->properties);
}
free(interface->serviceName);
free(interface);
}
celix_arrayList_destroy(component->providedInterfaces);
for (int i = 0; i < celix_arrayList_size(component->dependencies); ++i) {
celix_dm_service_dependency_t *dep = celix_arrayList_get(component->dependencies, i);
celix_dmServiceDependency_destroy(dep);
}
celix_arrayList_destroy(component->dependencies);
for (int i = 0; i < celix_arrayList_size(component->removedDependencies); ++i) {
celix_dm_service_dependency_t *dep = celix_arrayList_get(component->removedDependencies, i);
celix_dmServiceDependency_destroy(dep);
}
celix_arrayList_destroy(component->removedDependencies);
celixThreadMutex_destroy(&component->mutex);
free(component);
if (data->doneCallback) {
data->doneCallback(data->doneData);
}
free(data);
} else {
//not yet disabled, adding a new event on the event queue
celix_bundle_t* bnd = celix_bundleContext_getBundle(component->context);
celix_framework_t* fw = celix_bundleContext_getFramework(component->context);
celix_framework_fireGenericEvent(
fw, -1, celix_bundle_getId(bnd),
"destroy dm component",
data,
celix_dmComponent_destroyCallback,
NULL,
NULL);
}
}
void celix_dmComponent_destroyAsync(celix_dm_component_t *component, void *doneData, void (*doneCallback)(void*)) {
if (component != NULL) {
struct celix_dm_component_destroy_data* data = malloc(sizeof(*data));
data->cmp = component;
data->doneData = doneData;
data->doneCallback = doneCallback;
celix_bundle_t* bnd = celix_bundleContext_getBundle(component->context);
celix_framework_t* fw = celix_bundleContext_getFramework(component->context);
celix_framework_fireGenericEvent(
fw, -1, celix_bundle_getId(bnd),
"destroy dm component",
data,
celix_dmComponent_destroyCallback,
NULL,
NULL);
}
}
const char* celix_dmComponent_getUUID(celix_dm_component_t* cmp) {
return cmp->uuid;
}
celix_status_t component_addServiceDependency(celix_dm_component_t *component, celix_dm_service_dependency_t *dep) {
return celix_dmComponent_addServiceDependency(component, dep);
}
celix_status_t celix_dmComponent_addServiceDependency(celix_dm_component_t *component, celix_dm_service_dependency_t *dep) {
celix_status_t status = CELIX_SUCCESS;
celix_dmServiceDependency_setComponent(dep, component);
celixThreadMutex_lock(&component->mutex);
celix_arrayList_add(component->dependencies, dep);
bool startDep = celix_dmComponent_currentState(component) != CELIX_DM_CMP_STATE_INACTIVE;
if (startDep) {
celix_dmServiceDependency_enable(dep);
}
celix_dmComponent_cleanupRemovedDependencies(component);
celixThreadMutex_unlock(&component->mutex);
celix_dmComponent_handleChange(component);
return status;
}
celix_status_t celix_dmComponent_removeServiceDependency(celix_dm_component_t *component, celix_dm_service_dependency_t *dep) {
celixThreadMutex_lock(&component->mutex);
celix_arrayList_remove(component->dependencies, dep);
bool disableDependency = celix_dmComponent_currentState(component) != CELIX_DM_CMP_STATE_INACTIVE;
if (disableDependency) {
celix_dmServiceDependency_disable(dep);
}
celix_arrayList_add(component->removedDependencies, dep);
celix_dmComponent_cleanupRemovedDependencies(component);
celixThreadMutex_unlock(&component->mutex);
celix_dmComponent_handleChange(component);
return CELIX_SUCCESS;
}
celix_dm_component_state_t component_currentState(celix_dm_component_t *cmp) {
return celix_dmComponent_currentState(cmp);
}
static void celix_dmComponent_setCurrentState(celix_dm_component_t* cmp, celix_dm_component_state_t s) {
__atomic_store_n(&cmp->state, s, __ATOMIC_RELEASE);
}
celix_dm_component_state_t celix_dmComponent_currentState(celix_dm_component_t *cmp) {
celix_dm_component_state_t state = __atomic_load_n(&cmp->state, __ATOMIC_ACQUIRE);
return state;
}
void* component_getImplementation(celix_dm_component_t *cmp) {
return celix_dmComponent_getImplementation(cmp);
}
void* celix_dmComponent_getImplementation(celix_dm_component_t *cmp) {
return cmp->implementation;
}
const char* component_getName(celix_dm_component_t *cmp) {
return celix_dmComponent_getName(cmp);
}
const char * celix_dmComponent_getName(celix_dm_component_t *cmp) {
return cmp->name;
}
celix_status_t component_removeServiceDependency(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency) {
return celix_dmComponent_removeServiceDependency(component, dependency);
}
celix_status_t celix_private_dmComponent_enable(celix_dm_component_t *component) {
celixThreadMutex_lock(&component->mutex);
bool changed = false;
if (!component->isEnabled) {
changed = !component->isEnabled;
component->isEnabled = true;
}
celixThreadMutex_unlock(&component->mutex);
if (changed) {
celix_dmComponent_handleChange(component);
}
return CELIX_SUCCESS;
}
/**
* @brief disables component and effectualize this through normal component transitions.
*/
static celix_status_t celix_dmComponent_disable(celix_dm_component_t *component) {
celixThreadMutex_lock(&component->mutex);
bool changed = false;
if (component->isEnabled) {
changed = component->isEnabled;
component->isEnabled = false;
}
celixThreadMutex_unlock(&component->mutex);
if (changed) {
celix_dmComponent_handleChange(component);
}
return CELIX_SUCCESS;
}
/**
* Disables component directly, this is needed for an error flow.
*
* Should be called with component mutex locked.
*/
static void celix_dmComponent_disableDirectly(celix_dm_component_t *component) {
component->isEnabled = false;
celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_INACTIVE);
celix_dmComponent_unregisterServices(component, false);
celix_dmComponent_disableDependencies(component);
}
static void celix_dmComponent_cleanupRemovedDependencies(celix_dm_component_t* component) {
//note should be called with lock
bool removedDep = true;
while (removedDep) {
removedDep = false;
for (int i = 0 ; i < celix_arrayList_size(component->removedDependencies); ++i) {
celix_dm_service_dependency_t* dep = celix_arrayList_get(component->removedDependencies, i);
if (celix_dmServiceDependency_isDisabled(dep)) {
celix_arrayList_remove(component->removedDependencies, dep);
celix_dmServiceDependency_destroy(dep);
removedDep = true;
break;
}
}
}
}
static bool celix_dmComponent_areAllDependenciesDisabled(celix_dm_component_t* component) {
//note should be called with lock
bool allDisabled = true;
for (int i = 0 ; allDisabled && i < celix_arrayList_size(component->dependencies); ++i) {
celix_dm_service_dependency_t* dep = celix_arrayList_get(component->dependencies, i);
if (!celix_dmServiceDependency_isDisabled(dep)) {
allDisabled = false;
}
}
for (int i = 0 ; allDisabled && i < celix_arrayList_size(component->removedDependencies); ++i) {
celix_dm_service_dependency_t* dep = celix_arrayList_get(component->removedDependencies, i);
if (!celix_dmServiceDependency_isDisabled(dep)) {
allDisabled = false;
}
}
return allDisabled;
}
static bool celix_dmComponent_isDisabled(celix_dm_component_t *component) {
bool isStopped;
celixThreadMutex_lock(&component->mutex);
isStopped =
!component->isEnabled &&
celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_INACTIVE &&
celix_dmComponent_areAllDependenciesDisabled(component);
celixThreadMutex_unlock(&component->mutex);
return isStopped;
}
celix_status_t component_setCLanguageProperty(celix_dm_component_t *component, bool setCLangProp) {
return celix_dmComponent_setCLanguageProperty(component, setCLangProp);
}
celix_status_t celix_dmComponent_setCLanguageProperty(celix_dm_component_t *component, bool setCLangProp) {
//nop
return CELIX_SUCCESS;
}
celix_status_t component_addInterface(celix_dm_component_t *component, const char* serviceName, const char* serviceVersion, const void* service, celix_properties_t* properties) {
return celix_dmComponent_addInterface(component, serviceName, serviceVersion, service, properties);
}
celix_status_t celix_dmComponent_addInterface(celix_dm_component_t* component,
const char* serviceName,
const char* serviceVersion,
const void* service,
celix_properties_t* properties) {
if (serviceName == NULL || celix_utils_stringEquals(serviceName, "")) {
celix_bundleContext_log(
component->context, CELIX_LOG_LEVEL_ERROR, "Cannot add interface with a NULL or empty serviceName");
return CELIX_ILLEGAL_ARGUMENT;
}
celix_autofree dm_interface_t* interface = calloc(1, sizeof(*interface));
celix_autofree char* name = celix_utils_strdup(serviceName);
if (properties == NULL) {
properties = celix_properties_create();
}
if (serviceVersion != NULL) {
celix_autoptr(celix_version_t) version = celix_version_createVersionFromString(serviceVersion);
if (!version) {
celix_bundleContext_log(
component->context, CELIX_LOG_LEVEL_ERROR, "Cannot add interface with an invalid serviceVersion");
celix_properties_destroy(properties);
return CELIX_ILLEGAL_ARGUMENT;
}
celix_status_t rc =
celix_properties_assignVersion(properties, CELIX_FRAMEWORK_SERVICE_VERSION, celix_steal_ptr(version));
if (rc != CELIX_SUCCESS) {
celix_bundleContext_log(
component->context, CELIX_LOG_LEVEL_ERROR, "Cannot add interface with an invalid serviceVersion");
celix_properties_destroy(properties);
return CELIX_ILLEGAL_ARGUMENT;
}
}
celix_properties_set(properties, CELIX_DM_COMPONENT_UUID, (char*)component->uuid);
celixThreadMutex_lock(&component->mutex);
interface->serviceName = celix_steal_ptr(name);
interface->service = service;
interface->properties = properties;
interface->svcId = -1L;
celix_arrayList_add(component->providedInterfaces, celix_steal_ptr(interface));
if (celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
celix_dmComponent_registerServices(component, false);
}
celixThreadMutex_unlock(&component->mutex);
return CELIX_SUCCESS;
}
celix_status_t component_removeInterface(celix_dm_component_t *component, const void* service) {
return celix_dmComponent_removeInterface(component, service);
}
celix_status_t celix_dmComponent_removeInterface(celix_dm_component_t *component, const void* service) {
celix_status_t status = CELIX_ILLEGAL_ARGUMENT;
celixThreadMutex_lock(&component->mutex);
int nof_interfaces = celix_arrayList_size(component->providedInterfaces);
dm_interface_t* removedInterface = NULL;
for (int i = 0; i < nof_interfaces; ++i) {
dm_interface_t *interface = celix_arrayList_get(component->providedInterfaces, i);
if (interface->service == service) {
celix_arrayList_removeAt(component->providedInterfaces, i);
removedInterface = interface;
break;
}
}
celixThreadMutex_unlock(&component->mutex);
if (removedInterface != NULL) {
celix_bundleContext_unregisterService(component->context, removedInterface->svcId);
celix_properties_destroy(removedInterface->properties);
free(removedInterface->serviceName);
free(removedInterface);
}
return status;
}
celix_status_t component_getInterfaces(celix_dm_component_t *component, celix_array_list_t **out) {
return celix_dmComponent_getInterfaces(component, out);
}
celix_status_t celix_dmComponent_getInterfaces(celix_dm_component_t *component, celix_array_list_t **out) {
celix_array_list_t* names = celix_arrayList_create();
celixThreadMutex_lock(&component->mutex);
int size = celix_arrayList_size(component->providedInterfaces);
for (int i = 0; i < size; i += 1) {
dm_interface_info_t* info = calloc(1, sizeof(*info));
dm_interface_t *interface = celix_arrayList_get(component->providedInterfaces, i);
info->name = celix_utils_strdup(interface->serviceName);
info->properties = celix_properties_copy(interface->properties);
celix_arrayList_add(names, info);
}
celixThreadMutex_unlock(&component->mutex);
*out = names;
return CELIX_SUCCESS;
}
celix_status_t celix_private_dmComponent_handleEvent(celix_dm_component_t *component, const celix_dm_event_t* event) {
celix_status_t status = CELIX_SUCCESS;
switch (event->eventType) {
case CELIX_DM_EVENT_SVC_ADD:
celix_dmComponent_handleAdd(component, event);
break;
case CELIX_DM_EVENT_SVC_REM:
celix_dmComponent_handleRemove(component, event);
break;
case CELIX_DM_EVENT_SVC_SET:
celix_dmComponent_handleSet(component, event);
break;
default:
break;
}
return status;
}
static celix_status_t celix_dmComponent_suspend(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency) {
celix_status_t status = CELIX_SUCCESS;
celixThreadMutex_lock(&component->mutex);
celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component),
CELIX_DM_CMP_STATE_SUSPENDING);
celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_SUSPENDING);
celix_dmComponent_unregisterServices(component, false);
if (component->callbackStop != NULL ) {
status = component->callbackStop(component->implementation);
}
if (status == CELIX_SUCCESS) {
celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component),
CELIX_DM_CMP_STATE_SUSPENDED);
celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_SUSPENDED);
} else {
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
"Error stopping component %s (uuid=%s) using the stop callback. Disabling component.",
component->name,
component->uuid);
celix_dmComponent_disableDirectly(component);
}
celixThreadMutex_unlock(&component->mutex);
return status;
}
static celix_status_t celix_dmComponent_resume(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency) {
celix_status_t status = CELIX_SUCCESS;
if (celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_SUSPENDED) {
celixThreadMutex_lock(&component->mutex);
celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component),
CELIX_DM_CMP_STATE_RESUMING);
celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_RESUMING);
if (component->callbackStart != NULL) {
status = component->callbackStart(component->implementation);
}
if (status == CELIX_SUCCESS) {
celix_dmComponent_registerServices(component, false);
component->nrOfTimesResumed += 1;
celix_dmComponent_logTransition(component, celix_dmComponent_currentState(component),
CELIX_DM_CMP_STATE_TRACKING_OPTIONAL);
celix_dmComponent_setCurrentState(component, CELIX_DM_CMP_STATE_TRACKING_OPTIONAL);
} else {
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
"Error starting component %s (uuid=%s) using the start callback. Disabling component.",
component->name,
component->uuid);
celix_dmComponent_disableDirectly(component);
}
celixThreadMutex_unlock(&component->mutex);
}
return status;
}
static bool celix_dmComponent_needsSuspend(celix_dm_component_t *component, const celix_dm_event_t* event) {
if (celix_dmComponent_currentState(component) == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
bool callbackConfigured = event->eventType == CELIX_DM_EVENT_SVC_SET ?
celix_dmServiceDependency_isSetCallbackConfigured(event->dep) :
/*add or rem*/ celix_dmServiceDependency_isAddRemCallbacksConfigured(event->dep);
dm_service_dependency_strategy_t strategy = celix_dmServiceDependency_getStrategy(event->dep);
bool suspendStrategy = strategy == DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND;
return suspendStrategy && callbackConfigured;
}
return false;
}
static celix_status_t celix_dmComponent_handleEvent(celix_dm_component_t *component, const celix_dm_event_t* event, celix_status_t (*setAddOrRemFp)(celix_dm_service_dependency_t *dependency, void* svc, const celix_properties_t* props), const char *invokeName) {
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
"Calling %s service for component %s (uuid=%s) on service dependency with type %s",
invokeName,
component->name,
component->uuid,
event->dep->serviceName);
if (component->inTransition) {
/* Note if the component is already in transition (stopping, starting, etc) then only remove the svc
* function pointers. This can happen when with dependency loops or if a component also depends on a
* services it provides. (e.g. during stopping the component a handle event will be created to remove the
* service dependency).
*/
setAddOrRemFp(event->dep, event->svc, event->props);
return CELIX_SUCCESS;
}
bool eventHandled = false;
if (event->eventType == CELIX_DM_EVENT_SVC_ADD || (event->eventType == CELIX_DM_EVENT_SVC_SET && event->svc != NULL)) {
//note adding service or setting new service, so cmp will not be stopped in handleChange -> use suspend / resume now
eventHandled = true;
bool needSuspend = celix_dmComponent_needsSuspend(component, event);
if (needSuspend) {
celix_dmComponent_suspend(component, event->dep);
}
setAddOrRemFp(event->dep, event->svc, event->props);
if (needSuspend) {
celix_dmComponent_resume(component, event->dep);
}
};
celix_dmComponent_handleChange(component);
if (!eventHandled /*remove or set null*/) {
//removing svc or set svc to null -> if still active check if suspend is needed before invoking
bool needSuspend = celix_dmComponent_needsSuspend(component, event);
if (needSuspend) {
celix_dmComponent_suspend(component, event->dep);
}
setAddOrRemFp(event->dep, event->svc, event->props);
if (needSuspend) {
celix_dmComponent_resume(component, event->dep);
}
}
return CELIX_SUCCESS;
}
static celix_status_t celix_dmComponent_handleAdd(celix_dm_component_t *component, const celix_dm_event_t* event) {
return celix_dmComponent_handleEvent(component, event, celix_dmServiceDependency_invokeAdd, "add");
}
static celix_status_t celix_dmComponent_handleRemove(celix_dm_component_t *component, const celix_dm_event_t* event) {
return celix_dmComponent_handleEvent(component, event, celix_dmServiceDependency_invokeRemove, "remove");
}
static celix_status_t celix_dmComponent_handleSet(celix_dm_component_t *component, const celix_dm_event_t* event) {
return celix_dmComponent_handleEvent(component, event, celix_dmServiceDependency_invokeSet, "set");
}
/**
* perform state transition. This function should be called with the component->mutex locked.
*/
static celix_status_t celix_dmComponent_enableDependencies(celix_dm_component_t *component) {
for (int i = 0; i < celix_arrayList_size(component->dependencies); i++) {
celix_dm_service_dependency_t *dependency = celix_arrayList_get(component->dependencies, i);
celix_dmServiceDependency_enable(dependency);
}
return CELIX_SUCCESS;
}
/**
* perform state transition. This function should be called with the component->mutex locked.
*/
static celix_status_t celix_dmComponent_disableDependencies(celix_dm_component_t *component) {
for (int i = 0; i < celix_arrayList_size(component->dependencies); i++) {
celix_dm_service_dependency_t *dependency = celix_arrayList_get(component->dependencies, i);
celix_dmServiceDependency_disable(dependency);
}
return CELIX_SUCCESS;
}
/**
* Calculate and handle state change.
*/
static void celix_dmComponent_handleChangeOnEventThread(void *data) {
celix_dm_component_t* component = data;
assert(celix_framework_isCurrentThreadTheEventLoop(celix_bundleContext_getFramework(component->context)));
celixThreadMutex_lock(&component->mutex);
celix_dm_component_state_t oldState;
celix_dm_component_state_t newState;
bool transition = false;
do {
oldState = celix_dmComponent_currentState(component);
celix_status_t status = celix_dmComponent_calculateNewState(component, oldState, &newState);
if (status == CELIX_SUCCESS) {
transition = celix_dmComponent_performTransition(component, oldState, newState);
if (transition) {
celix_dmComponent_setCurrentState(component, newState);
}
}
if (status != CELIX_SUCCESS) {
break;
}
} while (transition);
celixThreadMutex_unlock(&component->mutex);
}
static celix_status_t celix_dmComponent_handleChange(celix_dm_component_t *component) {
celix_framework_t* fw = celix_bundleContext_getFramework(component->context);
if (celix_framework_isCurrentThreadTheEventLoop(fw)) {
celix_dmComponent_handleChangeOnEventThread(component);
} else {
celix_framework_fireGenericEvent(
fw,
-1,
celix_bundleContext_getBundleId(component->context),
"dm component handle change",
component,
celix_dmComponent_handleChangeOnEventThread,
NULL,
NULL);
}
return CELIX_SUCCESS;
}
/**
* Calculate possible state change. This function should be called with the component->mutex locked.
*/
static celix_status_t celix_dmComponent_calculateNewState(celix_dm_component_t *component, celix_dm_component_state_t currentState, celix_dm_component_state_t *newState) {
celix_status_t status = CELIX_SUCCESS;
bool allResolved = celix_dmComponent_areAllRequiredServiceDependenciesResolved(component);
if (currentState == CELIX_DM_CMP_STATE_INACTIVE) {
if (component->isEnabled) {
*newState = CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED;
} else {
*newState = currentState;
}
} else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) {
if (!component->isEnabled) {
*newState = CELIX_DM_CMP_STATE_INACTIVE;
} else {
if (allResolved) {
*newState = CELIX_DM_CMP_STATE_INITIALIZING;
} else {
*newState = currentState;
}
}
} else if (currentState == CELIX_DM_CMP_STATE_INITIALIZING) {
*newState = CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED;
} else if (currentState == CELIX_DM_CMP_STATE_DEINITIALIZING) {
*newState = CELIX_DM_CMP_STATE_INACTIVE;
} else if (currentState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
if (!component->isEnabled) {
*newState = CELIX_DM_CMP_STATE_DEINITIALIZING;
} else {
if (allResolved) {
*newState = CELIX_DM_CMP_STATE_STARTING;
} else {
*newState = currentState;
}
}
} else if (currentState == CELIX_DM_CMP_STATE_STARTING) {
*newState = CELIX_DM_CMP_STATE_TRACKING_OPTIONAL;
} else if (currentState == CELIX_DM_CMP_STATE_STOPPING) {
*newState = CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED;
} else if (currentState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
if (component->isEnabled && allResolved) {
*newState = currentState;
} else {
*newState = CELIX_DM_CMP_STATE_STOPPING;
}
} else {
//should not reach
*newState = CELIX_DM_CMP_STATE_INACTIVE;
status = CELIX_BUNDLE_EXCEPTION;
}
return status;
}
/**
* perform state transition. This function should be called with the component->mutex locked.
*
* Function can also indicate if the component needs to be disabled. This can be needed if a stop or deinit fails,
* because the component cannot be destroyed but apparently also not cleanly stopped
*/
static bool celix_dmComponent_performTransition(celix_dm_component_t *component, celix_dm_component_state_t currentState, celix_dm_component_state_t desiredState) {
if (currentState == desiredState) {
return false;
}
component->inTransition = true;
celix_dmComponent_logTransition(component, currentState, desiredState);
celix_status_t status = CELIX_SUCCESS;
if (currentState == CELIX_DM_CMP_STATE_INACTIVE && desiredState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) {
celix_dmComponent_enableDependencies(component);
} else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_INITIALIZING) {
//nop
} else if (currentState == CELIX_DM_CMP_STATE_INITIALIZING && desiredState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
if (component->callbackInit) {
status = component->callbackInit(component->implementation);
}
} else if (currentState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_DEINITIALIZING) {
//nop
} else if (currentState == CELIX_DM_CMP_STATE_DEINITIALIZING && desiredState == CELIX_DM_CMP_STATE_INACTIVE) {
if (component->callbackDeinit) {
status = component->callbackDeinit(component->implementation);
}
celix_dmComponent_disableDependencies(component);
} else if (currentState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_STARTING) {
//nop
} else if (currentState == CELIX_DM_CMP_STATE_STARTING && desiredState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
if (component->callbackStart) {
status = component->callbackStart(component->implementation);
}
if (status == CELIX_SUCCESS) {
celix_dmComponent_registerServices(component, false);
component->nrOfTimesStarted += 1;
}
} else if (currentState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL && desiredState == CELIX_DM_CMP_STATE_STOPPING) {
//nop
} else if (currentState == CELIX_DM_CMP_STATE_STOPPING && desiredState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
celix_dmComponent_unregisterServices(component, false);
if (component->callbackStop) {
status = component->callbackStop(component->implementation);
}
} else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_INACTIVE) {
celix_dmComponent_disableDependencies(component);
} else {
assert(false); //should not be reached.
}
bool transition = true;
if (status != CELIX_SUCCESS) {
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
"Error in component %s (uuid=%s) transition from %s to %s. Disabling component.",
component->name,
component->uuid,
celix_dmComponent_stateToString(currentState),
celix_dmComponent_stateToString(desiredState));
//If a lifecycle method fails, the component will disable itself (but no higher level transition is needed anymore)
transition = false;
celix_dmComponent_disableDirectly(component);
}
component->inTransition = false;
return transition;
}
/**
* Check if all required dependencies are resolved. This function should be called with the component->mutex locked.
*/
static bool celix_dmComponent_areAllRequiredServiceDependenciesResolved(celix_dm_component_t *component) {
bool allResolved = true;
for (int i = 0; i < celix_arrayList_size(component->dependencies); i++) {
celix_dm_service_dependency_t *dependency = celix_arrayList_get(component->dependencies, i);
bool started = celix_dmServiceDependency_isTrackerOpen(dependency);
bool required = celix_dmServiceDependency_isRequired(dependency);
bool available = celix_dmServiceDependency_isAvailable(dependency);
if (!started) {
allResolved = false;
break;
}
if (required && !available) {
allResolved = false;
break;
}
}
return allResolved;
}
/**
* Register component services (if not already registered).
*/
static celix_status_t celix_dmComponent_registerServices(celix_dm_component_t *component, bool needLock) {
if (needLock) {
celixThreadMutex_lock(&component->mutex);
}
for (int i = 0; i < celix_arrayList_size(component->providedInterfaces); i++) {
dm_interface_t *interface = celix_arrayList_get(component->providedInterfaces, i);
if (interface->svcId == -1L) {
celix_properties_t *regProps = celix_properties_copy(interface->properties);
celix_service_registration_options_t opts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS;
opts.properties = regProps;
opts.svc = (void*)interface->service;
opts.serviceName = interface->serviceName;
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
"Async registering service %s for component %s (uuid=%s)",
interface->serviceName,
component->name,
component->uuid);
long svcId = celix_bundleContext_registerServiceWithOptionsAsync(component->context, &opts);
interface->svcId = svcId;
}
}
if (needLock) {
celixThreadMutex_unlock(&component->mutex);
}
return CELIX_SUCCESS;
}
/**
* Unregister component services. This function should be called with the component->mutex locked.
* If needLock is false, this function should be called with the component->mutex locked.
*/
static celix_status_t celix_dmComponent_unregisterServices(celix_dm_component_t *component, bool needLock) {
celix_status_t status = CELIX_SUCCESS;
if (needLock) {
celixThreadMutex_lock(&component->mutex);
}
celix_array_list_t* ids = NULL;
for (int i = 0; i < celix_arrayList_size(component->providedInterfaces); ++i) {
dm_interface_t *interface = celix_arrayList_get(component->providedInterfaces, i);
if (interface->svcId == -1) {
continue;
}
if (ids == NULL) {
ids = celix_arrayList_create();
}
celix_arrayList_addLong(ids, interface->svcId);
interface->svcId = -1L;
celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
"Unregistering service %s for component %s (uuid=%s)",
interface->serviceName,
component->name,
component->uuid);
}
if (needLock) {
celixThreadMutex_unlock(&component->mutex);
}
if (ids != NULL) {
if (!needLock /*already locked when calling*/) {
celixThreadMutex_unlock(&component->mutex);
}
for (int i = 0; i < celix_arrayList_size(ids); ++i) {
long svcId = celix_arrayList_getLong(ids, i);
celix_bundleContext_unregisterService(component->context, svcId);
}
celix_arrayList_destroy(ids);
if (!needLock) {
celixThreadMutex_lock(&component->mutex);
}
}
return status;
}
celix_status_t component_setCallbacks(celix_dm_component_t *component, celix_dm_cmp_lifecycle_fpt init, celix_dm_cmp_lifecycle_fpt start, celix_dm_cmp_lifecycle_fpt stop, celix_dm_cmp_lifecycle_fpt deinit) {
return celix_dmComponent_setCallbacks(component, init, start, stop, deinit);
}
celix_status_t celix_dmComponent_setCallbacks(celix_dm_component_t *component, celix_dm_cmp_lifecycle_fpt init, celix_dm_cmp_lifecycle_fpt start, celix_dm_cmp_lifecycle_fpt stop, celix_dm_cmp_lifecycle_fpt deinit) {
component->callbackInit = init;
component->callbackStart = start;
component->callbackStop = stop;
component->callbackDeinit = deinit;
return CELIX_SUCCESS;
}
celix_status_t component_setImplementation(celix_dm_component_t *component, void *implementation) {
return celix_dmComponent_setImplementation(component, implementation);
}
celix_status_t celix_dmComponent_setImplementation(celix_dm_component_t *component, void *implementation) {
component->implementation = implementation;
return CELIX_SUCCESS;
}
void celix_dmComponent_setImplementationDestroyFunction(celix_dm_component_t* component, celix_dm_cmp_impl_destroy_fpt destroyFn) {
component->implementationDestroyFn = destroyFn;
}
celix_status_t component_getBundleContext(celix_dm_component_t *component, bundle_context_pt *context) {
*context = celix_dmComponent_getBundleContext(component);
return *context == NULL ? CELIX_BUNDLE_EXCEPTION : CELIX_SUCCESS;
}
celix_bundle_context_t* celix_dmComponent_getBundleContext(celix_dm_component_t *component) {
celix_bundle_context_t *result = NULL;
if (component) {
result = component->context;
}
return result;
}
celix_status_t component_getComponentInfo(celix_dm_component_t *component, celix_dm_component_info_t** out) {
return celix_dmComponent_getComponentInfo(component, out);
}
celix_status_t celix_dmComponent_getComponentInfo(celix_dm_component_t *component, celix_dm_component_info_t** out) {
celix_dm_component_info_t* info = calloc(1, sizeof(*info));
info->dependency_list = celix_arrayList_create();
celix_dmComponent_getInterfaces(component, &info->interfaces);
celixThreadMutex_lock(&component->mutex);
info->bundleId = celix_bundleContext_getBundleId(component->context);
info->bundleSymbolicName = celix_bundleContext_getBundleSymbolicName(component->context, info->bundleId);
info->active = false;
info->id = celix_utils_strdup(component->uuid);
info->name = celix_utils_strdup(component->name);
info->nrOfTimesStarted = component->nrOfTimesStarted;
info->nrOfTimesResumed = component->nrOfTimesResumed;
celix_dm_component_state_t state = celix_dmComponent_currentState(component);
info->state = celix_utils_strdup(celix_dmComponent_stateToString(state));
info->active = celix_dmComponent_isActiveInternal(component);
for (int i = 0; i < celix_arrayList_size(component->dependencies); i += 1) {
celix_dm_service_dependency_t *dep = celix_arrayList_get(component->dependencies, i);
dm_service_dependency_info_pt depInfo = celix_dmServiceDependency_createInfo(dep);
celix_arrayList_add(info->dependency_list, depInfo);
}
celixThreadMutex_unlock(&component->mutex);
*out = info;
return CELIX_SUCCESS;
}
static void celix_dmComponent_printFullInfo(FILE *out, bool colors, celix_dm_component_info_t* compInfo) {
const char *startColors = "";
const char *endColors = "";
if (colors) {
startColors = compInfo->active ? CELIX_DM_PRINT_OK_COLOR : CELIX_DM_PRINT_NOK_COLOR;
endColors = CELIX_DM_PRINT_END_COLOR;
}
fprintf(out, "%sComponent: Name=%s%s\n", startColors, compInfo->name, endColors);
fprintf(out, "|- UUID = %s\n", compInfo->id);
fprintf(out, "|- Active = %s\n", compInfo->active ? "true" : "false");
fprintf(out, "|- State = %s\n", compInfo->state);
fprintf(out, "|- Bundle = %li (%s)\n", compInfo->bundleId, compInfo->bundleSymbolicName);
fprintf(out, "|- Nr of times started = %i\n", (int)compInfo->nrOfTimesStarted);
fprintf(out, "|- Nr of times resumed = %i\n", (int)compInfo->nrOfTimesResumed);
fprintf(out, "|- Interfaces (%d):\n", celix_arrayList_size(compInfo->interfaces));
for (int interfCnt = 0; interfCnt < celix_arrayList_size(compInfo->interfaces); interfCnt++) {
dm_interface_info_pt intfInfo = celix_arrayList_get(compInfo->interfaces, interfCnt);
fprintf(out, " |- %sInterface %i: %s%s\n", startColors, (interfCnt+1), intfInfo->name, endColors);
hash_map_iterator_t iter = hashMapIterator_construct((hash_map_pt) intfInfo->properties);
char *key = NULL;
while ((key = hashMapIterator_nextKey(&iter)) != NULL) {
fprintf(out, " | %15s = %s\n", key, celix_properties_get(intfInfo->properties, key, "!ERROR!"));
}
}
fprintf(out, "|- Dependencies (%d):\n", celix_arrayList_size(compInfo->dependency_list));
for (int depCnt = 0; depCnt < celix_arrayList_size(compInfo->dependency_list); ++depCnt) {
dm_service_dependency_info_pt dependency;
dependency = celix_arrayList_get(compInfo->dependency_list, depCnt);
const char *depStartColors = "";
if (colors) {
if (dependency->required) {
depStartColors = dependency->available ? CELIX_DM_PRINT_OK_COLOR : CELIX_DM_PRINT_NOK_COLOR;
} else {
depStartColors = dependency->available ? CELIX_DM_PRINT_OK_COLOR : CELIX_DM_PRINT_WARNING_COLOR;
}
}
fprintf(out, " |- %sDependency %i: %s%s\n", depStartColors, (depCnt+1), dependency->serviceName == NULL ? "(any)" : dependency->serviceName, endColors);
fprintf(out, " | %15s = %s\n", "Available", dependency->available ? "true " : "false");
fprintf(out, " | %15s = %s\n", "Required", dependency->required ? "true " : "false");
fprintf(out, " | %15s = %s\n", "Version Range", dependency->versionRange == NULL ? "N/A" : dependency->versionRange);
fprintf(out, " | %15s = %s\n", "Filter", dependency->filter == NULL ? "N/A" : dependency->filter);
}
fprintf(out, "\n");
}
static void celix_dmComponent_printBasicInfo(FILE *out, bool colors, celix_dm_component_info_t* compInfo) {
const char *startColors = "";
const char *endColors = "";
if (colors) {
startColors = compInfo->active ? CELIX_DM_PRINT_OK_COLOR : CELIX_DM_PRINT_NOK_COLOR;
endColors = CELIX_DM_PRINT_END_COLOR;
}
fprintf(out, "Component: Name=%s, ID=%s, %sActive=%s%s, State=%s, Bundle=%li (%s)\n",
compInfo->name, compInfo->id, startColors, compInfo->active ? "true " : "false", endColors,
compInfo->state, compInfo->bundleId, compInfo->bundleSymbolicName);
}
void celix_dmComponent_printComponentInfo(celix_dm_component_info_t* info, bool printFullInfo, bool useAnsiColors, FILE* stream) {
if (printFullInfo) {
celix_dmComponent_printFullInfo(stream, useAnsiColors, info);
} else {
celix_dmComponent_printBasicInfo(stream, useAnsiColors, info);
}
}
void component_destroyComponentInfo(celix_dm_component_info_t* info) {
return celix_dmComponent_destroyComponentInfo(info);
}
void celix_dmComponent_destroyComponentInfo(celix_dm_component_info_t* info) {
int i;
int size;
if (info != NULL) {
free(info->bundleSymbolicName);
free(info->id);
free(info->name);
free(info->state);
if (info->interfaces != NULL) {
size = celix_arrayList_size(info->interfaces);
for (i = 0; i < size; i += 1) {
dm_interface_info_pt intfInfo = celix_arrayList_get(info->interfaces, i);
free(intfInfo->name);
celix_properties_destroy(intfInfo->properties);
free(intfInfo);
}
celix_arrayList_destroy(info->interfaces);
}
if (info->dependency_list != NULL) {
size = celix_arrayList_size(info->dependency_list);
for (i = 0; i < size; i += 1) {
dm_service_dependency_info_pt depInfo = celix_arrayList_get(info->dependency_list, i);
dependency_destroyDependencyInfo(depInfo);
}
celix_arrayList_destroy(info->dependency_list);
}
}
free(info);
}
static bool celix_dmComponent_isActiveInternal(celix_dm_component_t *component) {
//precondition: mutex component->mutex taken.
celix_dm_component_state_t state = celix_dmComponent_currentState(component);
return
state == CELIX_DM_CMP_STATE_STARTING ||
state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL ||
state == CELIX_DM_CMP_STATE_SUSPENDING ||
state == CELIX_DM_CMP_STATE_SUSPENDED ||
state == CELIX_DM_CMP_STATE_RESUMING ||
state == CELIX_DM_CMP_STATE_STOPPING;
}
bool celix_dmComponent_isActive(celix_dm_component_t *component) {
bool isActivate = celix_dmComponent_isActiveInternal(component);
return isActivate;
}
const char* celix_dmComponent_stateToString(celix_dm_component_state_t state) {
switch(state) {
case CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED:
return "WAITING_FOR_REQUIRED";
case CELIX_DM_CMP_STATE_INITIALIZING:
return "INITIALIZING";
case CELIX_DM_CMP_STATE_DEINITIALIZING:
return "DEINITIALIZING";
case CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED:
return "INITIALIZED_AND_WAITING_FOR_REQUIRED";
case CELIX_DM_CMP_STATE_STARTING:
return "STARTING";
case CELIX_DM_CMP_STATE_STOPPING:
return "STOPPING";
case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
return "TRACKING_OPTIONAL";
case CELIX_DM_CMP_STATE_SUSPENDING:
return "SUSPENDING";
case CELIX_DM_CMP_STATE_SUSPENDED:
return "SUSPENDED";
case CELIX_DM_CMP_STATE_RESUMING:
return "RESUMING";
default: //only CELIX_DM_CMP_STATE_INACTIVE left
return "INACTIVE";
}
}
static void celix_dmComponent_logTransition(celix_dm_component_t* cmp, celix_dm_component_state_t currentState, celix_dm_component_state_t desiredState) {
celix_bundleContext_log(cmp->context,
CELIX_LOG_LEVEL_TRACE,
"Component transition from %s -> %s for '%s' (uuid=%s).",
celix_dmComponent_stateToString(currentState),
celix_dmComponent_stateToString(desiredState),
cmp->name,
cmp->uuid);
}