blob: 27db3a5780da970d1e9c354d611150577615154d [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.
*/
/*
* dm_component_impl.c
*
* \date 9 Oct 2014
* \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
* \copyright Apache License, Version 2.0
*/
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "constants.h"
#include "filter.h"
#include "dm_component_impl.h"
typedef struct dm_executor_struct * dm_executor_pt;
struct dm_component_struct {
char id[DM_COMPONENT_MAX_ID_LENGTH];
char name[DM_COMPONENT_MAX_NAME_LENGTH];
bundle_context_pt context;
array_list_pt dm_interfaces;
void* implementation;
init_fpt callbackInit;
start_fpt callbackStart;
stop_fpt callbackStop;
deinit_fpt callbackDeinit;
array_list_pt dependencies; //protected by mutex
pthread_mutex_t mutex;
dm_component_state_t state;
bool isStarted;
bool active;
bool setCLanguageProperty;
hash_map_pt dependencyEvents; //protected by mutex
dm_executor_pt executor;
};
typedef struct dm_interface_struct {
char* serviceName;
const void* service;
properties_pt properties;
service_registration_pt registration;
} dm_interface_t;
struct dm_executor_struct {
pthread_t runningThread;
bool runningThreadSet;
linked_list_pt workQueue;
pthread_mutex_t mutex;
};
typedef struct dm_executor_task_struct {
dm_component_pt component;
void (*command)(void *command_ptr, void *data);
void *data;
} dm_executor_task_t;
typedef struct dm_handle_event_type_struct {
dm_service_dependency_pt dependency;
dm_event_pt event;
dm_event_pt newEvent;
} *dm_handle_event_type_pt;
static celix_status_t executor_runTasks(dm_executor_pt executor, pthread_t currentThread __attribute__((unused)));
static celix_status_t executor_execute(dm_executor_pt executor);
static celix_status_t executor_executeTask(dm_executor_pt executor, dm_component_pt component, void (*command), void *data);
static celix_status_t executor_schedule(dm_executor_pt executor, dm_component_pt component, void (*command), void *data);
static celix_status_t executor_create(dm_component_pt component __attribute__((unused)), dm_executor_pt *executor);
static void executor_destroy(dm_executor_pt executor);
static celix_status_t component_invokeRemoveRequiredDependencies(dm_component_pt component);
static celix_status_t component_invokeRemoveInstanceBoundDependencies(dm_component_pt component);
static celix_status_t component_invokeRemoveOptionalDependencies(dm_component_pt component);
static celix_status_t component_registerServices(dm_component_pt component);
static celix_status_t component_unregisterServices(dm_component_pt component);
static celix_status_t component_invokeAddOptionalDependencies(dm_component_pt component);
static celix_status_t component_invokeAddRequiredInstanceBoundDependencies(dm_component_pt component);
static celix_status_t component_invokeAddRequiredDependencies(dm_component_pt component);
static celix_status_t component_invokeAutoConfigInstanceBoundDependencies(dm_component_pt component);
static celix_status_t component_invokeAutoConfigDependencies(dm_component_pt component);
static celix_status_t component_configureImplementation(dm_component_pt component, dm_service_dependency_pt dependency);
static celix_status_t component_allInstanceBoundAvailable(dm_component_pt component, bool *available);
static celix_status_t component_allRequiredAvailable(dm_component_pt component, bool *available);
static celix_status_t component_performTransition(dm_component_pt component, dm_component_state_t oldState, dm_component_state_t newState, bool *transition);
static celix_status_t component_calculateNewState(dm_component_pt component, dm_component_state_t currentState, dm_component_state_t *newState);
static celix_status_t component_handleChange(dm_component_pt component);
static celix_status_t component_startDependencies(dm_component_pt component __attribute__((unused)), array_list_pt dependencies);
static celix_status_t component_getDependencyEvent(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt *event_pptr);
static celix_status_t component_updateInstance(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event, bool update, bool add);
static celix_status_t component_addTask(dm_component_pt component, dm_service_dependency_pt dep);
static celix_status_t component_startTask(dm_component_pt component, void * data __attribute__((unused)));
static celix_status_t component_stopTask(dm_component_pt component, void * data __attribute__((unused)));
static celix_status_t component_removeTask(dm_component_pt component, dm_service_dependency_pt dependency);
static celix_status_t component_handleEventTask(dm_component_pt component, dm_handle_event_type_pt data);
static celix_status_t component_handleAdded(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event);
static celix_status_t component_handleChanged(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event);
static celix_status_t component_handleRemoved(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event);
static celix_status_t component_handleSwapped(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event, dm_event_pt newEvent);
static celix_status_t component_suspend(dm_component_pt component, dm_service_dependency_pt dependency);
static celix_status_t component_resume(dm_component_pt component, dm_service_dependency_pt dependency);
celix_status_t component_create(bundle_context_pt context, const char *name, dm_component_pt *out) {
celix_status_t status = CELIX_SUCCESS;
dm_component_pt component = calloc(1, sizeof(*component));
if (!component) {
status = CELIX_ENOMEM;
} else {
snprintf(component->id, DM_COMPONENT_MAX_ID_LENGTH, "%p", component);
snprintf(component->name, DM_COMPONENT_MAX_NAME_LENGTH, "%s", name == NULL ? "n/a" : name);
component->context = context;
arrayList_create(&component->dm_interfaces);
arrayList_create(&(component)->dependencies);
pthread_mutex_init(&(component)->mutex, NULL);
component->implementation = NULL;
component->callbackInit = NULL;
component->callbackStart = NULL;
component->callbackStop = NULL;
component->callbackDeinit = NULL;
component->state = DM_CMP_STATE_INACTIVE;
component->isStarted = false;
component->active = false;
component->setCLanguageProperty = false;
component->dependencyEvents = hashMap_create(NULL, NULL, NULL, NULL);
component->executor = NULL;
executor_create(component, &component->executor);
}
if (status == CELIX_SUCCESS) {
*out = component;
}
return status;
}
void component_destroy(dm_component_pt component) {
if (component) {
unsigned int i;
for (i = 0; i < arrayList_size(component->dm_interfaces); i++) {
dm_interface_t *interface = arrayList_get(component->dm_interfaces, i);
if(interface->properties!=NULL){
properties_destroy(interface->properties);
}
free (interface->serviceName);
free (interface);
}
arrayList_destroy(component->dm_interfaces);
executor_destroy(component->executor);
hash_map_iterator_pt iter = hashMapIterator_create(component->dependencyEvents);
while(hashMapIterator_hasNext(iter)){
hash_map_entry_pt entry = hashMapIterator_nextEntry(iter);
dm_service_dependency_pt sdep = (dm_service_dependency_pt)hashMapEntry_getKey(entry);
array_list_pt eventList = (array_list_pt)hashMapEntry_getValue(entry);
serviceDependency_destroy(&sdep);
arrayList_destroy(eventList);
}
hashMapIterator_destroy(iter);
hashMap_destroy(component->dependencyEvents, false, false);
arrayList_destroy(component->dependencies);
pthread_mutex_destroy(&component->mutex);
free(component);
}
}
celix_status_t component_addServiceDependency(dm_component_pt component, dm_service_dependency_pt dep) {
celix_status_t status = CELIX_SUCCESS;
executor_executeTask(component->executor, component, component_addTask, dep);
return status;
}
static celix_status_t component_addTask(dm_component_pt component, dm_service_dependency_pt dep) {
celix_status_t status = CELIX_SUCCESS;
array_list_pt bounds = NULL;
arrayList_create(&bounds);
array_list_pt events = NULL;
arrayList_createWithEquals(event_equals, &events);
pthread_mutex_lock(&component->mutex);
hashMap_put(component->dependencyEvents, dep, events);
arrayList_add(component->dependencies, dep);
pthread_mutex_unlock(&component->mutex);
serviceDependency_setComponent(dep, component);
if (component->state != DM_CMP_STATE_INACTIVE) {
serviceDependency_setInstanceBound(dep, true);
arrayList_add(bounds, dep);
}
component_startDependencies(component, bounds);
component_handleChange(component);
arrayList_destroy(bounds);
return status;
}
dm_component_state_t component_currentState(dm_component_pt cmp) {
return cmp->state;
}
void * component_getImplementation(dm_component_pt cmp) {
return cmp->implementation;
}
const char * component_getName(dm_component_pt cmp) {
return cmp->name;
}
celix_status_t component_removeServiceDependency(dm_component_pt component, dm_service_dependency_pt dependency) {
celix_status_t status = CELIX_SUCCESS;
executor_executeTask(component->executor, component, component_removeTask, dependency);
return status;
}
celix_status_t component_removeTask(dm_component_pt component, dm_service_dependency_pt dependency) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
arrayList_removeElement(component->dependencies, dependency);
pthread_mutex_unlock(&component->mutex);
if (component->state != DM_CMP_STATE_INACTIVE) {
serviceDependency_stop(dependency);
}
pthread_mutex_lock(&component->mutex);
array_list_pt events = hashMap_remove(component->dependencyEvents, dependency);
pthread_mutex_unlock(&component->mutex);
serviceDependency_destroy(&dependency);
while (!arrayList_isEmpty(events)) {
dm_event_pt event = arrayList_remove(events, 0);
event_destroy(&event);
}
arrayList_destroy(events);
component_handleChange(component);
return status;
}
celix_status_t component_start(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
component->active = true;
executor_executeTask(component->executor, component, component_startTask, NULL);
return status;
}
celix_status_t component_startTask(dm_component_pt component, void *data __attribute__((unused))) {
celix_status_t status = CELIX_SUCCESS;
component->isStarted = true;
component_handleChange(component);
return status;
}
celix_status_t component_stop(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
component->active = false;
executor_executeTask(component->executor, component, component_stopTask, NULL);
return status;
}
celix_status_t component_stopTask(dm_component_pt component, void *data __attribute__((unused))) {
celix_status_t status = CELIX_SUCCESS;
component->isStarted = false;
component_handleChange(component);
component->active = false;
return status;
}
celix_status_t setCLanguageProperty(dm_component_pt component, bool setCLangProp) {
component->setCLanguageProperty = setCLangProp;
return CELIX_SUCCESS;
}
celix_status_t component_addInterface(dm_component_pt component, const char* serviceName, const char* serviceVersion, const void* service, properties_pt properties) {
celix_status_t status = CELIX_SUCCESS;
if (component->active) {
return CELIX_ILLEGAL_STATE;
} else {
dm_interface_t *interface = (dm_interface_t *) calloc(1, sizeof(*interface));
char *name = strdup(serviceName);
if (properties == NULL) {
properties = properties_create();
}
if ((properties_get(properties, CELIX_FRAMEWORK_SERVICE_VERSION) == NULL) && (serviceVersion != NULL)) {
properties_set(properties, CELIX_FRAMEWORK_SERVICE_VERSION, serviceVersion);
}
if (component->setCLanguageProperty && properties_get(properties, CELIX_FRAMEWORK_SERVICE_LANGUAGE) == NULL) { //always set default lang to C
properties_set(properties, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
}
if (interface && name) {
interface->serviceName = name;
interface->service = service;
interface->properties = properties;
interface->registration = NULL;
arrayList_add(component->dm_interfaces, interface);
} else {
free(interface);
free(name);
status = CELIX_ENOMEM;
}
}
return status;
}
celix_status_t component_getInterfaces(dm_component_pt component, array_list_pt *out) {
celix_status_t status = CELIX_SUCCESS;
array_list_pt names = NULL;
arrayList_create(&names);
celixThreadMutex_lock(&component->mutex);
int size = arrayList_size(component->dm_interfaces);
int i;
for (i = 0; i < size; i += 1) {
dm_interface_info_pt info = calloc(1, sizeof(*info));
dm_interface_t *interface = arrayList_get(component->dm_interfaces, i);
info->name = strdup(interface->serviceName);
properties_copy(interface->properties, &info->properties);
arrayList_add(names, info);
}
celixThreadMutex_unlock(&component->mutex);
if (status == CELIX_SUCCESS) {
*out = names;
}
return status;
}
celix_status_t component_handleEvent(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event) {
celix_status_t status = CELIX_SUCCESS;
dm_handle_event_type_pt data = calloc(1, sizeof(*data));
data->dependency = dependency;
data->event = event;
data->newEvent = NULL;
status = executor_executeTask(component->executor, component, component_handleEventTask, data);
// component_handleEventTask(component, data);
return status;
}
celix_status_t component_handleEventTask(dm_component_pt component, dm_handle_event_type_pt data) {
celix_status_t status = CELIX_SUCCESS;
switch (data->event->event_type) {
case DM_EVENT_ADDED:
component_handleAdded(component,data->dependency, data->event);
break;
case DM_EVENT_CHANGED:
component_handleChanged(component,data->dependency, data->event);
break;
case DM_EVENT_REMOVED:
component_handleRemoved(component,data->dependency, data->event);
break;
case DM_EVENT_SWAPPED:
component_handleSwapped(component,data->dependency, data->event, data->newEvent);
break;
default:
break;
}
free(data);
return status;
}
static celix_status_t component_suspend(dm_component_pt component, dm_service_dependency_pt dependency) {
celix_status_t status = CELIX_SUCCESS;
dm_service_dependency_strategy_t strategy;
serviceDependency_getStrategy(dependency, &strategy);
if (strategy == DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND && component->callbackStop != NULL) {
status = component->callbackStop(component->implementation);
}
return status;
}
static celix_status_t component_resume(dm_component_pt component, dm_service_dependency_pt dependency) {
celix_status_t status = CELIX_SUCCESS;
dm_service_dependency_strategy_t strategy;
serviceDependency_getStrategy(dependency, &strategy);
if (strategy == DM_SERVICE_DEPENDENCY_STRATEGY_SUSPEND && component->callbackStop != NULL) {
status = component->callbackStart(component->implementation);
}
return status;
}
celix_status_t component_handleAdded(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
arrayList_add(events, event);
pthread_mutex_unlock(&component->mutex);
serviceDependency_setAvailable(dependency, true);
switch (component->state) {
case DM_CMP_STATE_WAITING_FOR_REQUIRED: {
serviceDependency_invokeSet(dependency, event);
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (required) {
component_handleChange(component);
}
break;
}
case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED: {
bool instanceBound = false;
serviceDependency_isInstanceBound(dependency, &instanceBound);
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (!instanceBound) {
if (required) {
serviceDependency_invokeSet(dependency, event);
serviceDependency_invokeAdd(dependency, event);
}
dm_event_pt event = NULL;
component_getDependencyEvent(component, dependency, &event);
component_updateInstance(component, dependency, event, false, true);
}
if (required) {
component_handleChange(component);
}
break;
}
case DM_CMP_STATE_TRACKING_OPTIONAL:
component_suspend(component,dependency);
serviceDependency_invokeSet(dependency, event);
serviceDependency_invokeAdd(dependency, event);
component_resume(component,dependency);
dm_event_pt event = NULL;
component_getDependencyEvent(component, dependency, &event);
component_updateInstance(component, dependency, event, false, true);
break;
default:
break;
}
return status;
}
celix_status_t component_handleChanged(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
int index = arrayList_indexOf(events, event);
if (index < 0) {
pthread_mutex_unlock(&component->mutex);
status = CELIX_BUNDLE_EXCEPTION;
} else {
dm_event_pt old = arrayList_remove(events, (unsigned int) index);
arrayList_add(events, event);
pthread_mutex_unlock(&component->mutex);
serviceDependency_invokeSet(dependency, event);
switch (component->state) {
case DM_CMP_STATE_TRACKING_OPTIONAL:
component_suspend(component,dependency);
serviceDependency_invokeChange(dependency, event);
component_resume(component,dependency);
dm_event_pt hevent = NULL;
component_getDependencyEvent(component, dependency, &hevent);
component_updateInstance(component, dependency, hevent, true, false);
break;
case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED: {
bool instanceBound = false;
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (!instanceBound) {
serviceDependency_invokeChange(dependency, event);
dm_event_pt hevent = NULL;
component_getDependencyEvent(component, dependency, &hevent);
component_updateInstance(component, dependency, hevent, true, false);
}
break;
}
default:
break;
}
event_destroy(&old);
}
return status;
}
celix_status_t component_handleRemoved(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
int size = arrayList_size(events);
if (arrayList_contains(events, event)) {
size--;
}
pthread_mutex_unlock(&component->mutex);
serviceDependency_setAvailable(dependency, size > 0);
component_handleChange(component);
pthread_mutex_lock(&component->mutex);
int index = arrayList_indexOf(events, event);
if (index < 0) {
pthread_mutex_unlock(&component->mutex);
status = CELIX_BUNDLE_EXCEPTION;
} else {
dm_event_pt old = arrayList_remove(events, (unsigned int) index);
pthread_mutex_unlock(&component->mutex);
switch (component->state) {
case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED: {
serviceDependency_invokeSet(dependency, event);
bool instanceBound = false;
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (!instanceBound) {
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (required) {
serviceDependency_invokeRemove(dependency, event);
}
dm_event_pt hevent = NULL;
component_getDependencyEvent(component, dependency, &hevent);
component_updateInstance(component, dependency, hevent, false, false);
}
break;
}
case DM_CMP_STATE_TRACKING_OPTIONAL:
component_suspend(component,dependency);
serviceDependency_invokeSet(dependency, event);
serviceDependency_invokeRemove(dependency, event);
component_resume(component,dependency);
dm_event_pt hevent = NULL;
component_getDependencyEvent(component, dependency, &hevent);
component_updateInstance(component, dependency, hevent, false, false);
break;
default:
break;
}
event_destroy(&event);
if (old) {
event_destroy(&old);
}
}
return status;
}
celix_status_t component_handleSwapped(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event, dm_event_pt newEvent) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
int index = arrayList_indexOf(events, event);
if (index < 0) {
pthread_mutex_unlock(&component->mutex);
status = CELIX_BUNDLE_EXCEPTION;
} else {
dm_event_pt old = arrayList_remove(events, (unsigned int) index);
arrayList_add(events, newEvent);
pthread_mutex_unlock(&component->mutex);
serviceDependency_invokeSet(dependency, event);
switch (component->state) {
case DM_CMP_STATE_WAITING_FOR_REQUIRED:
break;
case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED: {
bool instanceBound = false;
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (!instanceBound) {
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (required) {
serviceDependency_invokeSwap(dependency, event, newEvent);
}
}
break;
}
case DM_CMP_STATE_TRACKING_OPTIONAL:
component_suspend(component,dependency);
serviceDependency_invokeSwap(dependency, event, newEvent);
component_resume(component,dependency);
break;
default:
break;
}
event_destroy(&event);
if (old) {
event_destroy(&old);
}
}
return status;
}
celix_status_t component_updateInstance(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt event, bool update, bool add) {
celix_status_t status = CELIX_SUCCESS;
bool autoConfig = false;
serviceDependency_isAutoConfig(dependency, &autoConfig);
if (autoConfig) {
const void *service = NULL;
const void **field = NULL;
if (event != NULL) {
event_getService(event, &service);
}
serviceDependency_getAutoConfig(dependency, &field);
serviceDependency_lock(dependency);
*field = service;
serviceDependency_unlock(dependency);
}
return status;
}
celix_status_t component_startDependencies(dm_component_pt component __attribute__((unused)), array_list_pt dependencies) {
celix_status_t status = CELIX_SUCCESS;
array_list_pt required_dependencies = NULL;
arrayList_create(&required_dependencies);
for (unsigned int i = 0; i < arrayList_size(dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(dependencies, i);
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (required) {
arrayList_add(required_dependencies, dependency);
continue;
}
serviceDependency_start(dependency);
}
for (unsigned int i = 0; i < arrayList_size(required_dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(required_dependencies, i);
serviceDependency_start(dependency);
}
arrayList_destroy(required_dependencies);
return status;
}
celix_status_t component_stopDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
pthread_mutex_unlock(&component->mutex);
serviceDependency_stop(dependency);
pthread_mutex_lock(&component->mutex);
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_handleChange(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
dm_component_state_t oldState;
dm_component_state_t newState;
bool transition = false;
do {
oldState = component->state;
status = component_calculateNewState(component, oldState, &newState);
if (status == CELIX_SUCCESS) {
component->state = newState;
status = component_performTransition(component, oldState, newState, &transition);
}
if (status != CELIX_SUCCESS) {
break;
}
} while (transition);
return status;
}
celix_status_t component_calculateNewState(dm_component_pt component, dm_component_state_t currentState, dm_component_state_t *newState) {
celix_status_t status = CELIX_SUCCESS;
if (currentState == DM_CMP_STATE_INACTIVE) {
if (component->isStarted) {
*newState = DM_CMP_STATE_WAITING_FOR_REQUIRED;
} else {
*newState = currentState;
}
} else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
if (!component->isStarted) {
*newState = DM_CMP_STATE_INACTIVE;
} else {
bool available = false;
component_allRequiredAvailable(component, &available);
if (available) {
*newState = DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED;
} else {
*newState = currentState;
}
}
} else if (currentState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
if (!component->isStarted) {
*newState = DM_CMP_STATE_WAITING_FOR_REQUIRED;
} else {
bool available = false;
component_allRequiredAvailable(component, &available);
if (available) {
bool instanceBoundAvailable = false;
component_allInstanceBoundAvailable(component, &instanceBoundAvailable);
if (instanceBoundAvailable) {
*newState = DM_CMP_STATE_TRACKING_OPTIONAL;
} else {
*newState = currentState;
}
} else {
*newState = currentState;
}
}
} else if (currentState == DM_CMP_STATE_TRACKING_OPTIONAL) {
bool instanceBoundAvailable = false;
bool available = false;
component_allInstanceBoundAvailable(component, &instanceBoundAvailable);
component_allRequiredAvailable(component, &available);
if (component->isStarted && available && instanceBoundAvailable) {
*newState = currentState;
} else {
*newState = DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED;
}
} else {
//should not reach
*newState = DM_CMP_STATE_INACTIVE;
status = CELIX_BUNDLE_EXCEPTION;
}
return status;
}
celix_status_t component_performTransition(dm_component_pt component, dm_component_state_t oldState, dm_component_state_t newState, bool *transition) {
celix_status_t status = CELIX_SUCCESS;
//printf("performing transition for %s in thread %i from %i to %i\n", component->name, (int) pthread_self(), oldState, newState);
if (oldState == newState) {
*transition = false;
} else if (oldState == DM_CMP_STATE_INACTIVE && newState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
component_startDependencies(component, component->dependencies);
*transition = true;
} else if (oldState == DM_CMP_STATE_WAITING_FOR_REQUIRED && newState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
component_invokeAddRequiredDependencies(component);
component_invokeAutoConfigDependencies(component);
if (component->callbackInit) {
status = component->callbackInit(component->implementation);
}
*transition = true;
} else if (oldState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == DM_CMP_STATE_TRACKING_OPTIONAL) {
component_invokeAddRequiredInstanceBoundDependencies(component);
component_invokeAutoConfigInstanceBoundDependencies(component);
component_invokeAddOptionalDependencies(component);
if (component->callbackStart) {
status = component->callbackStart(component->implementation);
}
component_registerServices(component);
*transition = true;
} else if (oldState == DM_CMP_STATE_TRACKING_OPTIONAL && newState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
component_unregisterServices(component);
if (component->callbackStop) {
status = component->callbackStop(component->implementation);
}
component_invokeRemoveOptionalDependencies(component);
component_invokeRemoveInstanceBoundDependencies(component);
*transition = true;
} else if (oldState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED && newState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
if (component->callbackDeinit) {
status = component->callbackDeinit(component->implementation);
}
component_invokeRemoveRequiredDependencies(component);
*transition = true;
} else if (oldState == DM_CMP_STATE_WAITING_FOR_REQUIRED && newState == DM_CMP_STATE_INACTIVE) {
component_stopDependencies(component);
*transition = true;
}
return status;
}
celix_status_t component_allRequiredAvailable(dm_component_pt component, bool *available) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
*available = true;
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
bool instanceBound = false;
serviceDependency_isRequired(dependency, &required);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (required && !instanceBound) {
bool isAvailable = false;
serviceDependency_isAvailable(dependency, &isAvailable);
if (!isAvailable) {
*available = false;
break;
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_allInstanceBoundAvailable(dm_component_pt component, bool *available) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
*available = true;
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
bool instanceBound = false;
serviceDependency_isRequired(dependency, &required);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (required && instanceBound) {
bool isAvailable = false;
serviceDependency_isAvailable(dependency, &isAvailable);
if (!isAvailable) {
*available = false;
break;
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeAddRequiredDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
bool instanceBound = false;
serviceDependency_isRequired(dependency, &required);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (required && !instanceBound) {
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event = arrayList_get(events, j);
serviceDependency_invokeAdd(dependency, event);
}
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeAutoConfigDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool autoConfig = false;
bool instanceBound = false;
serviceDependency_isAutoConfig(dependency, &autoConfig);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (autoConfig && !instanceBound) {
component_configureImplementation(component, dependency);
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeAutoConfigInstanceBoundDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool autoConfig = false;
bool instanceBound = false;
serviceDependency_isAutoConfig(dependency, &autoConfig);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (autoConfig && instanceBound) {
component_configureImplementation(component, dependency);
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeAddRequiredInstanceBoundDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
bool instanceBound = false;
serviceDependency_isRequired(dependency, &required);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (instanceBound && required) {
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event = arrayList_get(events, j);
serviceDependency_invokeAdd(dependency, event);
}
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeAddOptionalDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (!required) {
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event = arrayList_get(events, j);
serviceDependency_invokeAdd(dependency, event);
}
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeRemoveOptionalDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
serviceDependency_isRequired(dependency, &required);
if (!required) {
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event = arrayList_get(events, j);
serviceDependency_invokeRemove(dependency, event);
}
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeRemoveInstanceBoundDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool instanceBound = false;
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (instanceBound) {
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event = arrayList_get(events, j);
serviceDependency_invokeRemove(dependency, event);
}
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_invokeRemoveRequiredDependencies(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
pthread_mutex_lock(&component->mutex);
for (unsigned int i = 0; i < arrayList_size(component->dependencies); i++) {
dm_service_dependency_pt dependency = arrayList_get(component->dependencies, i);
bool required = false;
bool instanceBound = false;
serviceDependency_isRequired(dependency, &required);
serviceDependency_isInstanceBound(dependency, &instanceBound);
if (!instanceBound && required) {
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event = arrayList_get(events, j);
serviceDependency_invokeRemove(dependency, event);
}
}
}
}
pthread_mutex_unlock(&component->mutex);
return status;
}
celix_status_t component_getDependencyEvent(dm_component_pt component, dm_service_dependency_pt dependency, dm_event_pt *event_pptr) {
celix_status_t status = CELIX_SUCCESS;
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
*event_pptr = NULL;
if (events) {
for (unsigned int j = 0; j < arrayList_size(events); j++) {
dm_event_pt event_ptr = arrayList_get(events, j);
if (*event_pptr != NULL) {
int compare = 0;
event_compareTo(event_ptr, *event_pptr, &compare);
if (compare > 0) {
*event_pptr = event_ptr;
}
} else {
*event_pptr = event_ptr;
}
}
}
return status;
}
celix_status_t component_configureImplementation(dm_component_pt component, dm_service_dependency_pt dependency) {
celix_status_t status = CELIX_SUCCESS;
const void **field = NULL;
array_list_pt events = hashMap_get(component->dependencyEvents, dependency);
if (events) {
const void *service = NULL;
dm_event_pt event = NULL;
component_getDependencyEvent(component, dependency, &event);
if (event != NULL) {
event_getService(event, &service);
serviceDependency_getAutoConfig(dependency, &field);
serviceDependency_lock(dependency);
*field = service;
serviceDependency_unlock(dependency);
}
}
return status;
}
celix_status_t component_registerServices(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
if (component->context != NULL) {
unsigned int i;
for (i = 0; i < arrayList_size(component->dm_interfaces); i++) {
dm_interface_t *interface = arrayList_get(component->dm_interfaces, i);
properties_pt regProps = NULL;
properties_copy(interface->properties, &regProps);
bundleContext_registerService(component->context, interface->serviceName, interface->service, regProps, &interface->registration);
}
}
return status;
}
celix_status_t component_unregisterServices(dm_component_pt component) {
celix_status_t status = CELIX_SUCCESS;
unsigned int i;
for (i = 0; i < arrayList_size(component->dm_interfaces); i++) {
dm_interface_t *interface = arrayList_get(component->dm_interfaces, i);
serviceRegistration_unregister(interface->registration);
interface->registration = NULL;
}
return status;
}
celix_status_t component_setCallbacks(dm_component_pt component, init_fpt init, start_fpt start, stop_fpt stop, deinit_fpt deinit) {
if (component->active) {
return CELIX_ILLEGAL_STATE;
}
component->callbackInit = init;
component->callbackStart = start;
component->callbackStop = stop;
component->callbackDeinit = deinit;
return CELIX_SUCCESS;
}
celix_status_t component_isAvailable(dm_component_pt component, bool *available) {
*available = component->state == DM_CMP_STATE_TRACKING_OPTIONAL;
return CELIX_SUCCESS;
}
celix_status_t component_setImplementation(dm_component_pt component, void *implementation) {
component->implementation = implementation;
return CELIX_SUCCESS;
}
celix_status_t component_getBundleContext(dm_component_pt component, bundle_context_pt *context) {
celix_status_t status = CELIX_SUCCESS;
if (!component) {
status = CELIX_ILLEGAL_ARGUMENT;
}
if (status == CELIX_SUCCESS) {
*context = component->context;
}
return status;
}
static celix_status_t executor_create(dm_component_pt component __attribute__((unused)), dm_executor_pt *executor) {
celix_status_t status = CELIX_SUCCESS;
*executor = malloc(sizeof(**executor));
if (!*executor) {
status = CELIX_ENOMEM;
} else {
linkedList_create(&(*executor)->workQueue);
pthread_mutex_init(&(*executor)->mutex, NULL);
(*executor)->runningThreadSet = false;
}
return status;
}
static void executor_destroy(dm_executor_pt executor) {
if (executor) {
pthread_mutex_destroy(&executor->mutex);
linkedList_destroy(executor->workQueue);
free(executor);
}
}
static celix_status_t executor_schedule(dm_executor_pt executor, dm_component_pt component, void (*command), void *data) {
celix_status_t status = CELIX_SUCCESS;
dm_executor_task_t *task = NULL;
task = malloc(sizeof(*task));
if (!task) {
status = CELIX_ENOMEM;
} else {
task->component = component;
task->command = command;
task->data = data;
pthread_mutex_lock(&executor->mutex);
linkedList_addLast(executor->workQueue, task);
pthread_mutex_unlock(&executor->mutex);
}
return status;
}
static celix_status_t executor_executeTask(dm_executor_pt executor, dm_component_pt component, void (*command), void *data) {
celix_status_t status = CELIX_SUCCESS;
// Check thread and executor thread, if the same, execute immediately.
// bool execute = false;
// pthread_mutex_lock(&executor->mutex);
// pthread_t currentThread = pthread_self();
// if (pthread_equal(executor->runningThread, currentThread)) {
// execute = true;
// }
// pthread_mutex_unlock(&executor->mutex);
// For now, just schedule.
executor_schedule(executor, component, command, data);
executor_execute(executor);
return status;
}
static celix_status_t executor_execute(dm_executor_pt executor) {
celix_status_t status = CELIX_SUCCESS;
pthread_t currentThread = pthread_self();
pthread_mutex_lock(&executor->mutex);
bool execute = false;
if (!executor->runningThreadSet) {
executor->runningThread = currentThread;
executor->runningThreadSet = true;
execute = true;
}
pthread_mutex_unlock(&executor->mutex);
if (execute) {
executor_runTasks(executor, currentThread);
}
return status;
}
static celix_status_t executor_runTasks(dm_executor_pt executor, pthread_t currentThread __attribute__((unused))) {
celix_status_t status = CELIX_SUCCESS;
// bool execute = false;
do {
dm_executor_task_t *entry = NULL;
pthread_mutex_lock(&executor->mutex);
while ((entry = linkedList_removeFirst(executor->workQueue)) != NULL) {
pthread_mutex_unlock(&executor->mutex);
entry->command(entry->component, entry->data);
pthread_mutex_lock(&executor->mutex);
free(entry);
}
executor->runningThreadSet = false;
pthread_mutex_unlock(&executor->mutex);
// pthread_mutex_lock(&executor->mutex);
// if (executor->runningThread == NULL) {
// executor->runningThread = currentThread;
// execute = true;
// }
// pthread_mutex_unlock(&executor->mutex);
} while (!linkedList_isEmpty(executor->workQueue)); // && execute
return status;
}
celix_status_t component_getComponentInfo(dm_component_pt component, dm_component_info_pt *out) {
celix_status_t status = CELIX_SUCCESS;
int i;
int size;
dm_component_info_pt info = NULL;
info = calloc(1, sizeof(*info));
if (info == NULL) {
return CELIX_ENOMEM;
}
arrayList_create(&info->dependency_list);
component_getInterfaces(component, &info->interfaces);
info->active = false;
memcpy(info->id, component->id, DM_COMPONENT_MAX_ID_LENGTH);
memcpy(info->name, component->name, DM_COMPONENT_MAX_NAME_LENGTH);
switch (component->state) {
case DM_CMP_STATE_INACTIVE :
info->state = strdup("INACTIVE");
break;
case DM_CMP_STATE_WAITING_FOR_REQUIRED :
info->state = strdup("WAITING_FOR_REQUIRED");
break;
case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED :
info->state = strdup("INSTANTIATED_AND_WAITING_FOR_REQUIRED");
break;
case DM_CMP_STATE_TRACKING_OPTIONAL :
info->state = strdup("TRACKING_OPTIONAL");
info->active = true;
break;
default :
info->state = strdup("UNKNOWN");
break;
}
celixThreadMutex_lock(&component->mutex);
size = arrayList_size(component->dependencies);
for (i = 0; i < size; i += 1) {
dm_service_dependency_pt dep = arrayList_get(component->dependencies, i);
dm_service_dependency_info_pt depInfo = NULL;
status = serviceDependency_getServiceDependencyInfo(dep, &depInfo);
if (status == CELIX_SUCCESS) {
arrayList_add(info->dependency_list, depInfo);
} else {
break;
}
}
celixThreadMutex_unlock(&component->mutex);
if (status == CELIX_SUCCESS) {
*out = info;
} else if (info != NULL) {
component_destroyComponentInfo(info);
}
return status;
}
void component_destroyComponentInfo(dm_component_info_pt info) {
int i;
int size;
if (info != NULL) {
free(info->state);
if (info->interfaces != NULL) {
size = arrayList_size(info->interfaces);
for (i = 0; i < size; i += 1) {
dm_interface_info_pt intfInfo = arrayList_get(info->interfaces, i);
free(intfInfo->name);
properties_destroy(intfInfo->properties);
free(intfInfo);
}
arrayList_destroy(info->interfaces);
}
if (info->dependency_list != NULL) {
size = arrayList_size(info->dependency_list);
for (i = 0; i < size; i += 1) {
dm_service_dependency_info_pt depInfo = arrayList_get(info->dependency_list, i);
dependency_destroyDependencyInfo(depInfo);
}
arrayList_destroy(info->dependency_list);
}
}
free(info);
}