pnoltes commented on code in PR #738: URL: https://github.com/apache/celix/pull/738#discussion_r1534513381
########## bundles/event_admin/event_admin/src/celix_event_admin.c: ########## @@ -0,0 +1,652 @@ +/* + * 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 "celix_event_admin.h" + +#include <string.h> +#include <stdbool.h> +#include <assert.h> +#include <errno.h> + +#include "celix_event_handler_service.h" +#include "celix_event_constants.h" +#include "celix_event.h" +#include "celix_log_helper.h" +#include "celix_string_hash_map.h" +#include "celix_long_hash_map.h" +#include "celix_filter.h" +#include "celix_constants.h" +#include "celix_threads.h" +#include "celix_utils.h" +#include "celix_stdlib_cleanup.h" + +//Belows parameters are not configurable, consider its configurability until a real need arises. +#define CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS 5 +#define CELIX_EVENT_ADMIN_MAX_PARALLEL_EVENTS_OF_HANDLER (CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS/3 + 1) //max parallel async event for a single handler +#define CELIX_EVENT_ADMIN_MAX_HANDLE_EVENT_TIME 60 //seconds +#define CELIX_EVENT_ADMIN_MAX_EVENT_QUEUE_SIZE 512 //events + +typedef struct celix_event_handler { + celix_event_handler_service_t* service; + long serviceId; + const char* serviceDescription; + bool asyncOrdered; + celix_filter_t* eventFilter; + bool blackListed;//Blacklisted handlers must not be notified of any events. + unsigned int handlingAsyncEventCnt; +}celix_event_handler_t; + +typedef struct celix_event_channel { + celix_array_list_t* eventHandlerSvcIdList; +}celix_event_channel_t; + +typedef struct celix_event_entry { + celix_event_t* event; + celix_long_hash_map_t* eventHandlers;//key: event handler service id, value: null +}celix_event_entry_t; + +struct celix_event_admin { + celix_bundle_context_t* ctx; + celix_log_helper_t* logHelper; + celix_thread_rwlock_t lock;//projects: channels,eventHandlers + celix_event_channel_t channelMatchingAllEvents; + celix_string_hash_map_t* channelsMatchingTopic; //key: topic, value: celix_event_channel_t * + celix_string_hash_map_t* channelsMatchingPrefixTopic;//key:prefix topic, value: celix_event_channel_t * + celix_long_hash_map_t* eventHandlers;//key: event handler service id, value: celix_event_handler_t* + celix_thread_mutex_t eventsMutex;// protect belows + celix_thread_cond_t eventsTriggerCond; + celix_array_list_t* asyncEventQueue;//array_list<celix_event_entry_t*> + bool threadsRunning; + celix_thread_t eventHandlerThreads[CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS]; +}; + +static void* celix_eventAdmin_deliverEventThread(void* data); +static void onAsyncEventQueueRemoved(void* value); + +celix_event_admin_t* celix_eventAdmin_create(celix_bundle_context_t* ctx) { + celix_autofree celix_event_admin_t* ea = calloc(1, sizeof(*ea)); + if (ea == NULL) { + errno = ENOMEM; + return NULL; + } + ea->ctx = ctx; + ea->threadsRunning = false; + + celix_autoptr(celix_log_helper_t) logHelper = ea->logHelper = celix_logHelper_create(ctx, "CelixEventAdmin"); + if (logHelper == NULL) { + errno = ENOMEM; + return NULL; + } + + celix_status_t status = celixThreadRwlock_create(&ea->lock, NULL); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(logHelper, "Failed to create event admin lock."); + errno = status;//The status be ensured to belong to CELIX_FACILITY_CERRNO + return NULL; + } + celix_autoptr(celix_thread_rwlock_t) lock = &ea->lock; + celix_autoptr(celix_array_list_t) channelMatchingAllEvents = ea->channelMatchingAllEvents.eventHandlerSvcIdList = celix_arrayList_create(); + if (channelMatchingAllEvents == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event channel matching all events."); + errno = ENOMEM; + return NULL; + } + celix_autoptr(celix_string_hash_map_t) channelsMatchingTopic = ea->channelsMatchingTopic = celix_stringHashMap_create(); + if (channelsMatchingTopic == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event channels matching topic."); + errno = ENOMEM; + return NULL; + } + celix_autoptr(celix_string_hash_map_t) channelsMatchingPrefixTopic = ea->channelsMatchingPrefixTopic = celix_stringHashMap_create(); + if (channelsMatchingPrefixTopic == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event channels matching prefix topic."); + errno = ENOMEM; + return NULL; + } + celix_autoptr(celix_long_hash_map_t) eventHandlers = ea->eventHandlers = celix_longHashMap_create(); + if (eventHandlers == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event handler map."); + errno = ENOMEM; + return NULL; + } + + status = celixThreadMutex_create(&ea->eventsMutex, NULL); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(logHelper, "Failed to create event admin events mutex."); + errno = status;//The status be ensured to belong to CELIX_FACILITY_CERRNO + return NULL; + } + celix_autoptr(celix_thread_mutex_t) mutex = &ea->eventsMutex; + status = celixThreadCondition_init(&ea->eventsTriggerCond, NULL); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(logHelper, "Failed to create event admin events not empty condition."); + errno = status;//The status be ensured to belong to CELIX_FACILITY_CERRNO + return NULL; + } + celix_autoptr(celix_thread_cond_t) cond = &ea->eventsTriggerCond; + + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.elementType = CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER; + opts.simpleRemovedCallback = onAsyncEventQueueRemoved; + celix_autoptr(celix_array_list_t) asyncEventQueue = ea->asyncEventQueue = celix_arrayList_createWithOptions(&opts); + if (asyncEventQueue == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create async event queue."); + errno = ENOMEM; + return NULL; + } + + celix_steal_ptr(asyncEventQueue); + celix_steal_ptr(cond); + celix_steal_ptr(mutex); + celix_steal_ptr(eventHandlers); + celix_steal_ptr(channelsMatchingPrefixTopic); + celix_steal_ptr(channelsMatchingTopic); + celix_steal_ptr(channelMatchingAllEvents); + celix_steal_ptr(lock); + celix_steal_ptr(logHelper); + + return celix_steal_ptr(ea); +} + +int celix_eventAdmin_start(celix_event_admin_t* ea) { + celix_status_t status = CELIX_SUCCESS; + ea->threadsRunning = true; + for (int thCnt = 0; thCnt < CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS; ++thCnt) { + status = celixThread_create(&ea->eventHandlerThreads[thCnt], NULL, celix_eventAdmin_deliverEventThread, ea); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(ea->logHelper, "Failed to create event handler thread."); + celixThreadMutex_lock(&ea->eventsMutex); + ea->threadsRunning = false; + celixThreadMutex_unlock(&ea->eventsMutex); + celixThreadCondition_broadcast(&ea->eventsTriggerCond); + for (int i = 0; i < thCnt; i++) { + celixThread_join(ea->eventHandlerThreads[i], NULL); + } + return status; + } + celixThread_setName(&ea->eventHandlerThreads[thCnt], "EventHandler"); Review Comment: Good that the threads have a name, The Celix Event is named "CelixEvent". Maybe this could be named "EventAdmin-1", "EventAdmin-2", etc. Still shorts (< 16 chars) but more specific for the event admin. ########## bundles/event_admin/event_admin_api/include/celix_event_admin_service.h: ########## @@ -0,0 +1,67 @@ +/* + * 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 CELIX_EVENT_ADMIN_SERVICE_H +#define CELIX_EVENT_ADMIN_SERVICE_H +#ifdef __cplusplus +extern "C" { +#endif +#include "celix_properties.h" +#include "celix_errno.h" + +/** + * @brief The Event Admin service name + */ +#define CELIX_EVENT_ADMIN_SERVICE_NAME "org.osgi.service.event.EventAdmin" Review Comment: Although Apache Celix is inspired by OSGi and we did use some OSGi named constants, enums etc. I currently think it is better not to use the "org.osgi" or "osgi" part and replace that with "celix". So in this case "celix.service.event.EventAdmin" ########## bundles/event_admin/event_admin/src/celix_event_admin.c: ########## @@ -0,0 +1,652 @@ +/* + * 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 "celix_event_admin.h" + +#include <string.h> +#include <stdbool.h> +#include <assert.h> +#include <errno.h> + +#include "celix_event_handler_service.h" +#include "celix_event_constants.h" +#include "celix_event.h" +#include "celix_log_helper.h" +#include "celix_string_hash_map.h" +#include "celix_long_hash_map.h" +#include "celix_filter.h" +#include "celix_constants.h" +#include "celix_threads.h" +#include "celix_utils.h" +#include "celix_stdlib_cleanup.h" + +//Belows parameters are not configurable, consider its configurability until a real need arises. +#define CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS 5 +#define CELIX_EVENT_ADMIN_MAX_PARALLEL_EVENTS_OF_HANDLER (CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS/3 + 1) //max parallel async event for a single handler +#define CELIX_EVENT_ADMIN_MAX_HANDLE_EVENT_TIME 60 //seconds +#define CELIX_EVENT_ADMIN_MAX_EVENT_QUEUE_SIZE 512 //events + +typedef struct celix_event_handler { + celix_event_handler_service_t* service; + long serviceId; + const char* serviceDescription; + bool asyncOrdered; + celix_filter_t* eventFilter; + bool blackListed;//Blacklisted handlers must not be notified of any events. + unsigned int handlingAsyncEventCnt; +}celix_event_handler_t; + +typedef struct celix_event_channel { + celix_array_list_t* eventHandlerSvcIdList; +}celix_event_channel_t; + +typedef struct celix_event_entry { + celix_event_t* event; + celix_long_hash_map_t* eventHandlers;//key: event handler service id, value: null +}celix_event_entry_t; + +struct celix_event_admin { + celix_bundle_context_t* ctx; + celix_log_helper_t* logHelper; + celix_thread_rwlock_t lock;//projects: channels,eventHandlers + celix_event_channel_t channelMatchingAllEvents; + celix_string_hash_map_t* channelsMatchingTopic; //key: topic, value: celix_event_channel_t * + celix_string_hash_map_t* channelsMatchingPrefixTopic;//key:prefix topic, value: celix_event_channel_t * + celix_long_hash_map_t* eventHandlers;//key: event handler service id, value: celix_event_handler_t* + celix_thread_mutex_t eventsMutex;// protect belows + celix_thread_cond_t eventsTriggerCond; + celix_array_list_t* asyncEventQueue;//array_list<celix_event_entry_t*> + bool threadsRunning; + celix_thread_t eventHandlerThreads[CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS]; +}; + +static void* celix_eventAdmin_deliverEventThread(void* data); +static void onAsyncEventQueueRemoved(void* value); + +celix_event_admin_t* celix_eventAdmin_create(celix_bundle_context_t* ctx) { + celix_autofree celix_event_admin_t* ea = calloc(1, sizeof(*ea)); + if (ea == NULL) { + errno = ENOMEM; + return NULL; + } + ea->ctx = ctx; + ea->threadsRunning = false; + + celix_autoptr(celix_log_helper_t) logHelper = ea->logHelper = celix_logHelper_create(ctx, "CelixEventAdmin"); + if (logHelper == NULL) { + errno = ENOMEM; + return NULL; + } + + celix_status_t status = celixThreadRwlock_create(&ea->lock, NULL); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(logHelper, "Failed to create event admin lock."); + errno = status;//The status be ensured to belong to CELIX_FACILITY_CERRNO + return NULL; + } + celix_autoptr(celix_thread_rwlock_t) lock = &ea->lock; + celix_autoptr(celix_array_list_t) channelMatchingAllEvents = ea->channelMatchingAllEvents.eventHandlerSvcIdList = celix_arrayList_create(); + if (channelMatchingAllEvents == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event channel matching all events."); + errno = ENOMEM; + return NULL; + } + celix_autoptr(celix_string_hash_map_t) channelsMatchingTopic = ea->channelsMatchingTopic = celix_stringHashMap_create(); + if (channelsMatchingTopic == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event channels matching topic."); + errno = ENOMEM; + return NULL; + } + celix_autoptr(celix_string_hash_map_t) channelsMatchingPrefixTopic = ea->channelsMatchingPrefixTopic = celix_stringHashMap_create(); + if (channelsMatchingPrefixTopic == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event channels matching prefix topic."); + errno = ENOMEM; + return NULL; + } + celix_autoptr(celix_long_hash_map_t) eventHandlers = ea->eventHandlers = celix_longHashMap_create(); + if (eventHandlers == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create event handler map."); + errno = ENOMEM; + return NULL; + } + + status = celixThreadMutex_create(&ea->eventsMutex, NULL); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(logHelper, "Failed to create event admin events mutex."); + errno = status;//The status be ensured to belong to CELIX_FACILITY_CERRNO + return NULL; + } + celix_autoptr(celix_thread_mutex_t) mutex = &ea->eventsMutex; + status = celixThreadCondition_init(&ea->eventsTriggerCond, NULL); + if (status != CELIX_SUCCESS) { + celix_logHelper_error(logHelper, "Failed to create event admin events not empty condition."); + errno = status;//The status be ensured to belong to CELIX_FACILITY_CERRNO + return NULL; + } + celix_autoptr(celix_thread_cond_t) cond = &ea->eventsTriggerCond; + + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.elementType = CELIX_ARRAY_LIST_ELEMENT_TYPE_POINTER; + opts.simpleRemovedCallback = onAsyncEventQueueRemoved; + celix_autoptr(celix_array_list_t) asyncEventQueue = ea->asyncEventQueue = celix_arrayList_createWithOptions(&opts); + if (asyncEventQueue == NULL) { + celix_logHelper_logTssErrors(logHelper, CELIX_LOG_LEVEL_ERROR); + celix_logHelper_error(logHelper, "Failed to create async event queue."); + errno = ENOMEM; + return NULL; + } + + celix_steal_ptr(asyncEventQueue); + celix_steal_ptr(cond); + celix_steal_ptr(mutex); + celix_steal_ptr(eventHandlers); + celix_steal_ptr(channelsMatchingPrefixTopic); + celix_steal_ptr(channelsMatchingTopic); + celix_steal_ptr(channelMatchingAllEvents); + celix_steal_ptr(lock); + celix_steal_ptr(logHelper); + + return celix_steal_ptr(ea); +} + +int celix_eventAdmin_start(celix_event_admin_t* ea) { + celix_status_t status = CELIX_SUCCESS; + ea->threadsRunning = true; + for (int thCnt = 0; thCnt < CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS; ++thCnt) { Review Comment: nitpick: The name of `CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS` implies that the event admin spins up a maximum of CELIX_EVENT_ADMIN_MAX_HANDLER_THREADS threads, but these threads are always created (in the happy flow). I think the name should imply this, something like: `CELIX_EVENT_ADMIN_HANDLER_THREADS`. I also think this is a good candidate for a configuration parameter, so that users can lower or increase the event handlers threads based on their usage. ########## bundles/event_admin/examples/event_publisher/src/celix_example_event_publisher_activator.c: ########## Review Comment: nice examples. Shows the simplicity (in usage) of the event admin. ########## bundles/event_admin/event_admin/README.md: ########## @@ -0,0 +1,107 @@ +--- +title: Event Admin Service +--- + +<!-- +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. +--> + +## Event Admin + +The Event Admin provides the pubsub mechanism for in-process communication. It has implemented two delivery mechanisms: +- Synchronous delivery +- Asynchronous delivery + +In addition, the Event Admin provides an adapter for the celix framework-level event. It maps the celix framework-level event to the +event admin pubsub model, and events are delivered asynchronously. + +### Cmake options + + BUILD_EVENT_ADMIN=OFF + +### Conan options + + build_event_admin=false + +### Software Design + +#### Synchronous Delivery + +In the synchronous delivery, the event handler will be notified in the caller's thread. And the `sendEvent` method will return after all the event handlers have been called. +The diagram of synchronous delivery is shown in the following figure: + + + +#### Asynchronous Delivery + +In the asynchronous delivery, the event handler will be notified in a separate event-delivery thread. And the `postEvent` method will return immediately. +The diagram of asynchronous delivery is shown in the following figure: + + + +In the above figure, if the event handler sets the "event.delivery" property to "async.ordered", each event handler can hold +at most one event-delivery thread at a time, so that events can be delivered in order. If the event handler sets the +"event.delivery" property to "async.unordered", the event handler can hold multiple event-delivery threads at the same +time, so that events can be delivered in parallel. + + +#### Event Adapter + +In order to present a consistent view of all the events occurring in the system, the Event Admin provides an adapter for the celix framework-level event. +The events are mapped as follows: + +- **Celix Framework Event** + +For celix framework Event, we map the celix framework condition id to the event admin event topic. The event admin event properties are none. +The details mappings are shown in the following table: + +| Celix Framework Condition ID | Event Admin Event topic | +|------------------------------------|---------------------------------------------| +| CELIX_CONDITION_ID_FRAMEWORK_READY | "org/osgi/framework/FrameworkEvent/STARTED" | +| CELIX_CONDITION_ID_FRAMEWORK_ERROR | "org/osgi/framework/FrameworkEvent/ERROR" | + +- **Celix Bundle Event** + +For celix bundle Event, we map the celix bundle event type to the event admin event topic. And The event admin event properties are as follows: +- bundle.id +- bundle.symbolicName +- bundle.version + +The details mappings are shown in the following table: + +| Celix Bundle Event Type | Event Admin Event topic | +|--------------------------------|----------------------------------------------| +| CELIX_BUNDLE_EVENT_INSTALLED | "org/osgi/framework/BundleEvent/INSTALLED" | Review Comment: Also mentioned for the service names, but I prefer that "org.osgi" or "osgi" is not used (inspired by, but not a implementation of). So I would prefer topics like "celix/framework/BundleEvent/INSTALLED ########## bundles/event_admin/event_admin/README.md: ########## @@ -0,0 +1,107 @@ +--- +title: Event Admin Service +--- + +<!-- +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. +--> + +## Event Admin + +The Event Admin provides the pubsub mechanism for in-process communication. It has implemented two delivery mechanisms: +- Synchronous delivery +- Asynchronous delivery + +In addition, the Event Admin provides an adapter for the celix framework-level event. It maps the celix framework-level event to the +event admin pubsub model, and events are delivered asynchronously. + +### Cmake options + + BUILD_EVENT_ADMIN=OFF + +### Conan options + + build_event_admin=false + +### Software Design + +#### Synchronous Delivery + +In the synchronous delivery, the event handler will be notified in the caller's thread. And the `sendEvent` method will return after all the event handlers have been called. +The diagram of synchronous delivery is shown in the following figure: + + + +#### Asynchronous Delivery + +In the asynchronous delivery, the event handler will be notified in a separate event-delivery thread. And the `postEvent` method will return immediately. +The diagram of asynchronous delivery is shown in the following figure: + + + +In the above figure, if the event handler sets the "event.delivery" property to "async.ordered", each event handler can hold +at most one event-delivery thread at a time, so that events can be delivered in order. If the event handler sets the +"event.delivery" property to "async.unordered", the event handler can hold multiple event-delivery threads at the same +time, so that events can be delivered in parallel. + + +#### Event Adapter + +In order to present a consistent view of all the events occurring in the system, the Event Admin provides an adapter for the celix framework-level event. +The events are mapped as follows: + +- **Celix Framework Event** + +For celix framework Event, we map the celix framework condition id to the event admin event topic. The event admin event properties are none. +The details mappings are shown in the following table: + +| Celix Framework Condition ID | Event Admin Event topic | +|------------------------------------|---------------------------------------------| +| CELIX_CONDITION_ID_FRAMEWORK_READY | "org/osgi/framework/FrameworkEvent/STARTED" | Review Comment: Good choice and prevents additional needs to exposing a additional framework header. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: dev-unsubscr...@celix.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org