summaryrefslogtreecommitdiff
path: root/main/sorcery.c
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2013-05-06 13:04:08 +0000
committerJoshua Colp <jcolp@digium.com>2013-05-06 13:04:08 +0000
commit40074542bf517a751061d7cf6f9ff83431e35ff8 (patch)
tree8e7d52a4c65363e0d4cbd8effd83995ac3bfa892 /main/sorcery.c
parent6e2fe0c9ab1ec9cdd0d45e48fd03217d06cc17ae (diff)
Add support for observers and JSON objectset creation to sorcery.
This change adds the ability for modules to add themselves as observers to sorcery object types. Observers can be notified when objects are created, updated, or deleted as well as when the object type is loaded or reloaded. Observer notifications are done using a thread pool in a serialized fashion so the caller of the sorcery API calls is minimally impacted. This also adds the ability to create JSON changesets of a sorcery object. Tests are also present to confirm all of the above functionality. Review: https://reviewboard.asterisk.org/r/2477/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@387662 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/sorcery.c')
-rw-r--r--main/sorcery.c315
1 files changed, 312 insertions, 3 deletions
diff --git a/main/sorcery.c b/main/sorcery.c
index d0aee4a92..aa48222dd 100644
--- a/main/sorcery.c
+++ b/main/sorcery.c
@@ -38,6 +38,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/config_options.h"
#include "asterisk/netsock2.h"
#include "asterisk/module.h"
+#include "asterisk/taskprocessor.h"
+#include "asterisk/threadpool.h"
+#include "asterisk/json.h"
/* To prevent DEBUG_FD_LEAKS from interfering with things we undef open and close */
#undef open
@@ -52,6 +55,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*! \brief Maximum length of an object field name */
#define MAX_OBJECT_FIELD 128
+/*! \brief Thread pool for observers */
+static struct ast_threadpool *threadpool;
+
/*! \brief Structure for registered object type */
struct ast_sorcery_object_type {
/*! \brief Unique name of the object type */
@@ -83,6 +89,27 @@ struct ast_sorcery_object_type {
/*! \brief Type details */
struct aco_type type;
+
+ /*! \brief Observers */
+ struct ao2_container *observers;
+
+ /*! \brief Serializer for observers */
+ struct ast_taskprocessor *serializer;
+};
+
+/*! \brief Structure for registered object type observer */
+struct ast_sorcery_object_type_observer {
+ /*! \brief Pointer to the observer implementation */
+ const struct ast_sorcery_observer *callbacks;
+};
+
+/*! \brief Structure used for observer invocations */
+struct sorcery_observer_invocation {
+ /*! \brief Pointer to the object type */
+ struct ast_sorcery_object_type *object_type;
+
+ /*! \brief Pointer to the object */
+ void *object;
};
/*! \brief Structure for registered object field */
@@ -213,9 +240,21 @@ static int sorcery_wizard_cmp(void *obj, void *arg, int flags)
int ast_sorcery_init(void)
{
+ struct ast_threadpool_options options = {
+ .version = AST_THREADPOOL_OPTIONS_VERSION,
+ .auto_increment = 1,
+ .max_size = 0,
+ .idle_timeout = 60,
+ .initial_size = 0,
+ };
ast_assert(wizards == NULL);
+ if (!(threadpool = ast_threadpool_create("Sorcery", NULL, &options))) {
+ return -1;
+ }
+
if (!(wizards = ao2_container_alloc(WIZARD_BUCKETS, sorcery_wizard_hash, sorcery_wizard_cmp))) {
+ ast_threadpool_shutdown(threadpool);
return -1;
}
@@ -317,6 +356,7 @@ static void sorcery_object_type_destructor(void *obj)
ao2_cleanup(object_type->wizards);
ao2_cleanup(object_type->fields);
+ ao2_cleanup(object_type->observers);
if (object_type->info) {
aco_info_destroy(object_type->info);
@@ -324,12 +364,15 @@ static void sorcery_object_type_destructor(void *obj)
}
ast_free(object_type->file);
+
+ ast_taskprocessor_unreference(object_type->serializer);
}
/*! \brief Internal function which allocates an object type structure */
static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *type, const char *module)
{
struct ast_sorcery_object_type *object_type;
+ char uuid[AST_UUID_STR_LEN];
if (!(object_type = ao2_alloc(sizeof(*object_type), sorcery_object_type_destructor))) {
return NULL;
@@ -346,6 +389,11 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
return NULL;
}
+ if (!(object_type->observers = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, 1, NULL, NULL))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
if (!(object_type->info = ast_calloc(1, sizeof(*object_type->info) + 2 * sizeof(object_type->info->files[0])))) {
ao2_ref(object_type, -1);
return NULL;
@@ -356,6 +404,16 @@ static struct ast_sorcery_object_type *sorcery_object_type_alloc(const char *typ
return NULL;
}
+ if (!ast_uuid_generate_str(uuid, sizeof(uuid))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
+ if (!(object_type->serializer = ast_threadpool_serializer(uuid, threadpool))) {
+ ao2_ref(object_type, -1);
+ return NULL;
+ }
+
object_type->info->files[0] = object_type->file;
object_type->info->files[1] = NULL;
object_type->info->module = module;
@@ -597,6 +655,58 @@ static int sorcery_wizard_load(void *obj, void *arg, int flags)
return 0;
}
+/*! \brief Destructor for observer invocation */
+static void sorcery_observer_invocation_destroy(void *obj)
+{
+ struct sorcery_observer_invocation *invocation = obj;
+
+ ao2_cleanup(invocation->object_type);
+ ao2_cleanup(invocation->object);
+}
+
+/*! \brief Allocator function for observer invocation */
+static struct sorcery_observer_invocation *sorcery_observer_invocation_alloc(struct ast_sorcery_object_type *object_type, void *object)
+{
+ struct sorcery_observer_invocation *invocation = ao2_alloc(sizeof(*invocation), sorcery_observer_invocation_destroy);
+
+ if (!invocation) {
+ return NULL;
+ }
+
+ ao2_ref(object_type, +1);
+ invocation->object_type = object_type;
+
+ if (object) {
+ ao2_ref(object, +1);
+ invocation->object = object;
+ }
+
+ return invocation;
+}
+
+/*! \brief Internal callback function which notifies an individual observer that an object type has been loaded */
+static int sorcery_observer_notify_loaded(void *obj, void *arg, int flags)
+{
+ const struct ast_sorcery_object_type_observer *observer = obj;
+
+ if (observer->callbacks->loaded) {
+ observer->callbacks->loaded(arg);
+ }
+
+ return 0;
+}
+
+/*! \brief Internal callback function which notifies observers that an object type has been loaded */
+static int sorcery_observers_notify_loaded(void *data)
+{
+ struct sorcery_observer_invocation *invocation = data;
+
+ ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_loaded, invocation->object_type->name);
+ ao2_cleanup(invocation);
+
+ return 0;
+}
+
static int sorcery_object_load(void *obj, void *arg, int flags)
{
struct ast_sorcery_object_type *type = obj;
@@ -605,6 +715,14 @@ static int sorcery_object_load(void *obj, void *arg, int flags)
details->type = type->name;
ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
+ if (ao2_container_count(type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(type, NULL);
+
+ if (invocation && ast_taskprocessor_push(type->serializer, sorcery_observers_notify_loaded, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
+
return 0;
}
@@ -715,6 +833,68 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc
return fields;
}
+struct ast_json *ast_sorcery_objectset_json_create(const struct ast_sorcery *sorcery, const void *object)
+{
+ const struct ast_sorcery_object_details *details = object;
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, details->type, OBJ_KEY), ao2_cleanup);
+ struct ao2_iterator i;
+ struct ast_sorcery_object_field *object_field;
+ struct ast_json *json = ast_json_object_create();
+ int res = 0;
+
+ if (!object_type || !json) {
+ return NULL;
+ }
+
+ i = ao2_iterator_init(object_type->fields, 0);
+
+ for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) {
+ if (object_field->multiple_handler) {
+ struct ast_variable *tmp = NULL;
+ struct ast_variable *field;
+
+ if ((res = object_field->multiple_handler(object, &tmp))) {
+ break;
+ }
+
+ for (field = tmp; field; field = field->next) {
+ struct ast_json *value = ast_json_string_create(field->value);
+
+ if (value && ast_json_object_set(json, field->name, value)) {
+ ast_json_unref(value);
+ res = -1;
+ }
+ }
+
+ ast_variables_destroy(tmp);
+ } else if (object_field->handler) {
+ char *buf = NULL;
+ struct ast_json *value = NULL;
+
+ if ((res = object_field->handler(object, object_field->args, &buf)) ||
+ !(value = ast_json_string_create(buf)) ||
+ ast_json_object_set(json, object_field->name, value)) {
+ ast_json_unref(value);
+ res = -1;
+ }
+
+ ast_free(buf);
+ } else {
+ continue;
+ }
+ }
+
+ ao2_iterator_destroy(&i);
+
+ /* If any error occurs we destroy the JSON object so a partial objectset is not returned */
+ if (res) {
+ ast_json_unref(json);
+ json = NULL;
+ }
+
+ return json;
+}
+
int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, struct ast_variable *objectset)
{
const struct ast_sorcery_object_details *details = object;
@@ -1016,6 +1196,29 @@ static int sorcery_wizard_create(void *obj, void *arg, int flags)
return (!object_wizard->caching && !object_wizard->wizard->create(details->sorcery, object_wizard->data, details->obj)) ? CMP_MATCH | CMP_STOP : 0;
}
+/*! \brief Internal callback function which notifies an individual observer that an object has been created */
+static int sorcery_observer_notify_create(void *obj, void *arg, int flags)
+{
+ const struct ast_sorcery_object_type_observer *observer = obj;
+
+ if (observer->callbacks->created) {
+ observer->callbacks->created(arg);
+ }
+
+ return 0;
+}
+
+/*! \brief Internal callback function which notifies observers that an object has been created */
+static int sorcery_observers_notify_create(void *data)
+{
+ struct sorcery_observer_invocation *invocation = data;
+
+ ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_create, invocation->object);
+ ao2_cleanup(invocation);
+
+ return 0;
+}
+
int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
{
const struct ast_sorcery_object_details *details = object;
@@ -1030,11 +1233,41 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
return -1;
}
- object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails);
+ if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_create, &sdetails)) &&
+ ao2_container_count(object_type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+
+ if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
return object_wizard ? 0 : -1;
}
+/*! \brief Internal callback function which notifies an individual observer that an object has been updated */
+static int sorcery_observer_notify_update(void *obj, void *arg, int flags)
+{
+ const struct ast_sorcery_object_type_observer *observer = obj;
+
+ if (observer->callbacks->updated) {
+ observer->callbacks->updated(arg);
+ }
+
+ return 0;
+}
+
+/*! \brief Internal callback function which notifies observers that an object has been updated */
+static int sorcery_observers_notify_update(void *data)
+{
+ struct sorcery_observer_invocation *invocation = data;
+
+ ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_update, invocation->object);
+ ao2_cleanup(invocation);
+
+ return 0;
+}
+
/*! \brief Internal function which returns if a wizard has updated the object */
static int sorcery_wizard_update(void *obj, void *arg, int flags)
{
@@ -1059,11 +1292,41 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
return -1;
}
- object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails);
+ if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_update, &sdetails)) &&
+ ao2_container_count(object_type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+
+ if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
return object_wizard ? 0 : -1;
}
+/*! \brief Internal callback function which notifies an individual observer that an object has been deleted */
+static int sorcery_observer_notify_delete(void *obj, void *arg, int flags)
+{
+ const struct ast_sorcery_object_type_observer *observer = obj;
+
+ if (observer->callbacks->deleted) {
+ observer->callbacks->deleted(arg);
+ }
+
+ return 0;
+}
+
+/*! \brief Internal callback function which notifies observers that an object has been deleted */
+static int sorcery_observers_notify_delete(void *data)
+{
+ struct sorcery_observer_invocation *invocation = data;
+
+ ao2_callback(invocation->object_type->observers, OBJ_NODATA, sorcery_observer_notify_delete, invocation->object);
+ ao2_cleanup(invocation);
+
+ return 0;
+}
+
/*! \brief Internal function which returns if a wizard has deleted the object */
static int sorcery_wizard_delete(void *obj, void *arg, int flags)
{
@@ -1088,7 +1351,14 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
return -1;
}
- object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails);
+ if ((object_wizard = ao2_callback(object_type->wizards, 0, sorcery_wizard_delete, &sdetails)) &&
+ ao2_container_count(object_type->observers)) {
+ struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+
+ if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
+ ao2_cleanup(invocation);
+ }
+ }
return object_wizard ? 0 : -1;
}
@@ -1109,3 +1379,42 @@ const char *ast_sorcery_object_get_type(const void *object)
const struct ast_sorcery_object_details *details = object;
return details->type;
}
+
+int ast_sorcery_observer_add(const struct ast_sorcery *sorcery, const char *type, const struct ast_sorcery_observer *callbacks)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+ struct ast_sorcery_object_type_observer *observer;
+
+ if (!object_type || !callbacks) {
+ return -1;
+ }
+
+ if (!(observer = ao2_alloc(sizeof(*observer), NULL))) {
+ return -1;
+ }
+
+ observer->callbacks = callbacks;
+ ao2_link(object_type->observers, observer);
+ ao2_ref(observer, -1);
+
+ return 0;
+}
+
+/*! \brief Internal callback function for removing an observer */
+static int sorcery_observer_remove(void *obj, void *arg, int flags)
+{
+ const struct ast_sorcery_object_type_observer *observer = obj;
+
+ return (observer->callbacks == arg) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+void ast_sorcery_observer_remove(const struct ast_sorcery *sorcery, const char *type, struct ast_sorcery_observer *callbacks)
+{
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+ if (!object_type) {
+ return;
+ }
+
+ ao2_callback(object_type->observers, OBJ_NODATA | OBJ_UNLINK, sorcery_observer_remove, callbacks);
+}