Author: gtjoseph
Date: Fri Dec  5 11:06:42 2014
New Revision: 429000

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=429000
Log:
sorcery: Add additional observer capabilities.

Add new global, instance and wizard observers.
instance_created
wizard_registered
wizard_unregistered
instance_destroying
instance_loading
instance_loaded
wizard_mapped
object_type_registered
object_type_loading
object_type_loaded
wizard_loading
wizard_loaded

Tested-by: George Joseph

Review: https://reviewboard.asterisk.org/r/4215/
........

Merged revisions 428999 from http://svn.asterisk.org/svn/asterisk/branches/12

Modified:
    branches/13/   (props changed)
    branches/13/include/asterisk/sorcery.h
    branches/13/include/asterisk/test.h
    branches/13/main/sorcery.c
    branches/13/tests/test_sorcery.c

Propchange: branches/13/
------------------------------------------------------------------------------
Binary property 'branch-12-merged' - no diff available.

Modified: branches/13/include/asterisk/sorcery.h
URL: 
http://svnview.digium.com/svn/asterisk/branches/13/include/asterisk/sorcery.h?view=diff&rev=429000&r1=428999&r2=429000
==============================================================================
--- branches/13/include/asterisk/sorcery.h (original)
+++ branches/13/include/asterisk/sorcery.h Fri Dec  5 11:06:42 2014
@@ -141,8 +141,9 @@
 };
 
 
-/*! \brief Forward declaration for the sorcery main structure */
+/*! \brief Forward declaration for the sorcery main structure and wizard 
structure */
 struct ast_sorcery;
+struct ast_sorcery_wizard;
 
 /*!
  * \brief A callback function for translating a value into a string
@@ -214,6 +215,62 @@
  * \param -1 failure
  */
 typedef int (*sorcery_diff_handler)(const void *original, const void 
*modified, struct ast_variable **changes);
+
+/*! \brief Interface for the global sorcery observer */
+struct ast_sorcery_global_observer {
+       /*! \brief Callback after an instance is created */
+       void (*instance_created)(const char *name, struct ast_sorcery *sorcery);
+
+       /*! \brief Callback after an wizard is registered */
+       void (*wizard_registered)(const char *name,
+               const struct ast_sorcery_wizard *wizard);
+
+       /*! \brief Callback before an instance is destroyed */
+       void (*instance_destroying)(const char *name, struct ast_sorcery 
*sorcery);
+
+       /*! \brief Callback before a wizard is unregistered */
+       void (*wizard_unregistering)(const char *name,
+               const struct ast_sorcery_wizard *wizard);
+};
+
+/*! \brief Interface for the sorcery instance observer */
+struct ast_sorcery_instance_observer {
+       /*! \brief Callback before instance is loaded/reloaded */
+       void (*instance_loading)(const char *name, const struct ast_sorcery 
*sorcery,
+               int reloaded);
+
+       /*! \brief Callback after instance is loaded/reloaded */
+       void (*instance_loaded)(const char *name, const struct ast_sorcery 
*sorcery,
+               int reloaded);
+
+       /*! \brief Callback after a wizard is mapped to an object_type */
+       void (*wizard_mapped)(const char *name, struct ast_sorcery *sorcery,
+               const char *object_type, struct ast_sorcery_wizard *wizard,
+               const char *wizard_args, void *wizard_data);
+
+       /*! \brief Callback after any object_type is registered */
+       void (*object_type_registered)(const char *name, struct ast_sorcery 
*sorcery,
+               const char *object_type);
+
+       /*! \brief Callback before any object_type is loaded/reloaded */
+       void (*object_type_loading)(const char *name, const struct ast_sorcery 
*sorcery,
+               const char *object_type, int reloaded);
+
+       /*! \brief Callback after any object_type is loaded/reloaded */
+       void (*object_type_loaded)(const char *name, const struct ast_sorcery 
*sorcery,
+               const char *object_type, int reloaded);
+};
+
+/*! \brief Interface for the sorcery wizard observer */
+struct ast_sorcery_wizard_observer {
+       /*! \brief Callback before a wizard is loaded/reloaded for any type */
+       void (*wizard_loading)(const char *name, const struct 
ast_sorcery_wizard *wizard,
+               const char *object_type, int reloaded);
+
+       /*! \brief Callback after a wizard is loaded/reloaded for any type */
+       void (*wizard_loaded)(const char *name, const struct ast_sorcery_wizard 
*wizard,
+               const char *object_type, int reloaded);
+};
 
 /*! \brief Interface for a sorcery wizard */
 struct ast_sorcery_wizard {
@@ -406,6 +463,40 @@
 #define ast_sorcery_apply_default(sorcery, type, name, data) \
        __ast_sorcery_apply_default((sorcery), (type), AST_MODULE, (name), 
(data))
 
+
+/*!
+ * \brief Apply additional object wizard mappings
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ * \param caching Wizard should cache
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ */
+enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct 
ast_sorcery *sorcery,
+               const char *type, const char *module, const char *name, const 
char *data, unsigned int caching);
+
+/*!
+ * \brief Apply additional object wizard mappings
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object to apply to
+ * \param module The name of the module, typically AST_MODULE
+ * \param name Name of the wizard to use
+ * \param data Data to be passed to wizard
+ *
+ * \return What occurred when applying the mapping
+ *
+ * \note This should be called *after* applying default mappings
+ */
+#define ast_sorcery_apply_wizard_mapping(sorcery, type, name, data, caching) \
+       __ast_sorcery_apply_wizard_mapping((sorcery), (type), AST_MODULE, 
(name), (data), (caching));
+
 /*!
  * \brief Register an object type
  *
@@ -794,6 +885,89 @@
 int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, 
const void *modified, struct ast_variable **changes);
 
 /*!
+ * \brief Add a global observer to sorcery
+ *
+ * \param callbacks Implementation of the global observer interface
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note You must be ready to accept observer invocations before this function 
is called
+ */
+int ast_sorcery_global_observer_add(const struct ast_sorcery_global_observer 
*callbacks);
+
+/*!
+ * \brief Remove a global observer from sorcery.
+ *
+ * A global observer is notified...
+ * After a new wizard is registered.
+ * After a new sorcery instance is opened.
+ * Before an instance is destroyed.
+ * Before a wizard is unregistered.
+ *
+ * \param callbacks Implementation of the global observer interface
+ */
+void ast_sorcery_global_observer_remove(const struct 
ast_sorcery_global_observer *callbacks);
+
+/*!
+ * \brief Add an observer to a sorcery instance
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param callbacks Implementation of the instance observer interface
+ *
+ * An instance observer is notified...
+ * Before an instance is loaded or reloaded.
+ * After an instance is loaded or reloaded.
+ * After a wizard is mapped to an object type.
+ * After an object type is registered.
+ * Before an object type is loaded or reloaded.
+ * After an object type is loaded or reloaded.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note You must be ready to accept observer invocations before this function 
is called
+ */
+int ast_sorcery_instance_observer_add(struct ast_sorcery *sorcery,
+       const struct ast_sorcery_instance_observer *callbacks);
+
+/*!
+ * \brief Remove an observer from a sorcery instance
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param callbacks Implementation of the instance observer interface
+ */
+void ast_sorcery_instance_observer_remove(struct ast_sorcery *sorcery,
+       const struct ast_sorcery_instance_observer *callbacks);
+
+/*!
+ * \brief Add an observer to a sorcery wizard
+ *
+ * \param sorcery Pointer to a previously registered wizard structure
+ * \param callbacks Implementation of the wizard observer interface
+ *
+ * A wizard observer is notified...
+ * Before a wizard is loaded or reloaded.
+ * After a wizard is loaded or reloaded.
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note You must be ready to accept observer invocations before this function 
is called
+ */
+int ast_sorcery_wizard_observer_add(struct ast_sorcery_wizard *wizard,
+       const struct ast_sorcery_wizard_observer *callbacks);
+
+/*!
+ * \brief Remove an observer from a sorcery wizard.
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param callbacks Implementation of the wizard observer interface
+ */
+void ast_sorcery_wizard_observer_remove(struct ast_sorcery_wizard *wizard,
+       const struct ast_sorcery_wizard_observer *callbacks);
+
+/*!
  * \brief Add an observer to a specific object type
  *
  * \param sorcery Pointer to a sorcery structure

Modified: branches/13/include/asterisk/test.h
URL: 
http://svnview.digium.com/svn/asterisk/branches/13/include/asterisk/test.h?view=diff&rev=429000&r1=428999&r2=429000
==============================================================================
--- branches/13/include/asterisk/test.h (original)
+++ branches/13/include/asterisk/test.h Fri Dec  5 11:06:42 2014
@@ -381,10 +381,10 @@
  * \param test Currently executing test
  * \param condition Boolean condition to check.
  */
-#define ast_test_validate(test, condition)                             \
+#define ast_test_validate(test, condition, ...)                                
\
        do {                                                            \
                if (!(condition)) {                                     \
-                       __ast_test_status_update(__FILE__, __PRETTY_FUNCTION__, 
__LINE__, (test), "Condition failed: %s\n", #condition); \
+                       __ast_test_status_update(__FILE__, __PRETTY_FUNCTION__, 
__LINE__, (test), "%s: %s\n", strlen(#__VA_ARGS__) ? #__VA_ARGS__ : "Condition 
failed", #condition); \
                        return AST_TEST_FAIL;                           \
                }                                                       \
        } while(0)

Modified: branches/13/main/sorcery.c
URL: 
http://svnview.digium.com/svn/asterisk/branches/13/main/sorcery.c?view=diff&rev=429000&r1=428999&r2=429000
==============================================================================
--- branches/13/main/sorcery.c (original)
+++ branches/13/main/sorcery.c Fri Dec  5 11:06:42 2014
@@ -60,6 +60,29 @@
 /*! \brief Number of buckets for object fields (should be prime for 
performance reasons) */
 #define OBJECT_FIELD_BUCKETS 29
 
+#define NOTIFY_GENERIC_OBSERVERS(container, type, callback, ...) ({ \
+       struct ao2_iterator i = ao2_iterator_init(container, 0); \
+       struct type *observer; \
+       ao2_rdlock(container); \
+       while ((observer = ao2_iterator_next(&i))) { \
+               if (observer->callbacks->callback) { \
+                       observer->callbacks->callback(__VA_ARGS__); \
+               } \
+               ao2_cleanup(observer); \
+       } \
+       ao2_unlock(container); \
+       ao2_iterator_cleanup(&i); \
+})
+
+#define NOTIFY_GLOBAL_OBSERVERS(container, callback, ...) \
+       NOTIFY_GENERIC_OBSERVERS(container, sorcery_global_observer, callback, 
__VA_ARGS__)
+
+#define NOTIFY_INSTANCE_OBSERVERS(container, callback, ...) \
+       NOTIFY_GENERIC_OBSERVERS(container, sorcery_instance_observer, 
callback, __VA_ARGS__)
+
+#define NOTIFY_WIZARD_OBSERVERS(container, callback, ...) \
+       NOTIFY_GENERIC_OBSERVERS(container, sorcery_wizard_observer, callback, 
__VA_ARGS__)
+
 /*! \brief Thread pool for observers */
 static struct ast_threadpool *threadpool;
 
@@ -153,10 +176,19 @@
        intptr_t args[];
 };
 
+/*! \brief Structure for an internal wizard instance */
+struct ast_sorcery_internal_wizard {
+       /*! \brief Wizard interface itself */
+       struct ast_sorcery_wizard callbacks;
+
+       /*! \brief Observers */
+       struct ao2_container *observers;
+};
+
 /*! \brief Structure for a wizard instance which operates on objects */
 struct ast_sorcery_object_wizard {
        /*! \brief Wizard interface itself */
-       struct ast_sorcery_wizard *wizard;
+       struct ast_sorcery_internal_wizard *wizard;
 
        /*! \brief Unique data for the wizard */
        void *data;
@@ -169,6 +201,10 @@
 struct ast_sorcery {
        /*! \brief Container for known object types */
        struct ao2_container *types;
+
+       /*! \brief Observers */
+       struct ao2_container *observers;
+
        /*! \brief The name of the module owning this sorcery instance */
        char module_name[0];
 };
@@ -187,6 +223,30 @@
 
 /*! \brief Registered sorcery wizards */
 static struct ao2_container *wizards;
+
+/* The following 3 observer wrappers must name their
+ * external observer 'callbacks' and it must be
+ * the first member of the structure.  Common macros
+ * and container callbacks depend on it.
+ */
+
+/*! \brief A global observer wrapper */
+struct sorcery_global_observer {
+       const struct ast_sorcery_global_observer *callbacks;
+};
+
+/*! \brief An instance observer wrapper */
+struct sorcery_instance_observer {
+       const struct ast_sorcery_instance_observer *callbacks;
+};
+
+/*! \brief A wizard observer wrapper */
+struct sorcery_wizard_observer {
+       const struct ast_sorcery_wizard_observer *callbacks;
+};
+
+/*! \brief Registered global observers */
+struct ao2_container *observers;
 
 /*! \brief Registered sorcery instances */
 static struct ao2_container *instances;
@@ -262,7 +322,7 @@
 /*! \brief Hashing function for sorcery wizards */
 static int sorcery_wizard_hash(const void *obj, const int flags)
 {
-       const struct ast_sorcery_wizard *object;
+       const struct ast_sorcery_internal_wizard *object;
        const char *key;
 
        switch (flags & OBJ_SEARCH_MASK) {
@@ -271,7 +331,7 @@
                break;
        case OBJ_SEARCH_OBJECT:
                object = obj;
-               key = object->name;
+               key = object->callbacks.name;
                break;
        default:
                ast_assert(0);
@@ -283,20 +343,20 @@
 /*! \brief Comparator function for sorcery wizards */
 static int sorcery_wizard_cmp(void *obj, void *arg, int flags)
 {
-       const struct ast_sorcery_wizard *object_left = obj;
-       const struct ast_sorcery_wizard *object_right = arg;
+       const struct ast_sorcery_internal_wizard *object_left = obj;
+       const struct ast_sorcery_internal_wizard *object_right = arg;
        const char *right_key = arg;
        int cmp;
 
        switch (flags & OBJ_SEARCH_MASK) {
        case OBJ_SEARCH_OBJECT:
-               right_key = object_right->name;
+               right_key = object_right->callbacks.name;
                /* Fall through */
        case OBJ_SEARCH_KEY:
-               cmp = strcmp(object_left->name, right_key);
+               cmp = strcmp(object_left->callbacks.name, right_key);
                break;
        case OBJ_SEARCH_PARTIAL_KEY:
-               cmp = strncmp(object_left->name, right_key, strlen(right_key));
+               cmp = strncmp(object_left->callbacks.name, right_key, 
strlen(right_key));
                break;
        default:
                cmp = 0;
@@ -368,6 +428,8 @@
 {
        ao2_cleanup(wizards);
        wizards = NULL;
+       ao2_cleanup(observers);
+       observers = NULL;
        ao2_cleanup(instances);
        instances = NULL;
 }
@@ -442,6 +504,13 @@
                return -1;
        }
 
+       observers = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, 
NULL, NULL);
+       if (!observers) {
+               sorcery_cleanup();
+               sorcery_exit();
+               return -1;
+       }
+
        instances = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, 
INSTANCE_BUCKETS,
                sorcery_instance_hash, sorcery_instance_cmp);
        if (!instances) {
@@ -456,9 +525,16 @@
        return 0;
 }
 
+static void sorcery_internal_wizard_destructor(void *obj)
+{
+       struct ast_sorcery_internal_wizard *wizard = obj;
+
+       ao2_cleanup(wizard->observers);
+}
+
 int __ast_sorcery_wizard_register(const struct ast_sorcery_wizard *interface, 
struct ast_module *module)
 {
-       struct ast_sorcery_wizard *wizard;
+       struct ast_sorcery_internal_wizard *wizard;
        int res = -1;
 
        ast_assert(!ast_strlen_zero(interface->name));
@@ -471,17 +547,25 @@
                goto done;
        }
 
-       if (!(wizard = ao2_alloc(sizeof(*wizard), NULL))) {
+       if (!(wizard = ao2_alloc(sizeof(*wizard), 
sorcery_internal_wizard_destructor))) {
                goto done;
        }
 
-       *wizard = *interface;
-       wizard->module = module;
+       wizard->callbacks = *interface;
+       wizard->callbacks.module = module;
+
+       wizard->observers = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK, 
0, NULL, NULL);
+       if (!wizard->observers) {
+               goto done;
+       }
 
        ao2_link_flags(wizards, wizard, OBJ_NOLOCK);
        res = 0;
 
        ast_verb(2, "Sorcery registered wizard '%s'\n", interface->name);
+
+       NOTIFY_GLOBAL_OBSERVERS(observers, wizard_registered,
+               interface->name, interface);
 
 done:
        ao2_cleanup(wizard);
@@ -492,9 +576,13 @@
 
 int ast_sorcery_wizard_unregister(const struct ast_sorcery_wizard *interface)
 {
-       RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, 
interface->name, OBJ_KEY | OBJ_UNLINK), ao2_cleanup);
+       struct ast_sorcery_internal_wizard *wizard =
+               interface ? ao2_find(wizards, interface->name, OBJ_SEARCH_KEY) 
: NULL;
 
        if (wizard) {
+               NOTIFY_GLOBAL_OBSERVERS(observers, wizard_unregistering, 
wizard->callbacks.name, &wizard->callbacks);
+               ao2_unlink(wizards, wizard);
+               ao2_ref(wizard, -1);
                ast_verb(2, "Sorcery unregistered wizard '%s'\n", 
interface->name);
                return 0;
        } else {
@@ -502,11 +590,105 @@
        }
 }
 
+/*! \brief Internal callback function for removing a generic observer */
+static int sorcery_generic_observer_remove(void *obj, void *arg, int flags)
+{
+       const struct sorcery_global_observer *observer = obj;
+
+       return (observer->callbacks == arg) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+int ast_sorcery_global_observer_add(const struct ast_sorcery_global_observer 
*callbacks)
+{
+       struct sorcery_global_observer *cb;
+
+       cb = ao2_alloc(sizeof(*cb), NULL);
+       if (!cb) {
+               return -1;
+       }
+
+       cb->callbacks = callbacks;
+       ao2_link(observers, cb);
+       ao2_ref(cb, -1);
+
+       return 0;
+}
+
+void ast_sorcery_global_observer_remove(
+       const struct ast_sorcery_global_observer *callbacks)
+{
+       ao2_callback(observers, OBJ_NODATA | OBJ_UNLINK, 
sorcery_generic_observer_remove, (void *)callbacks);
+}
+
+int ast_sorcery_instance_observer_add(struct ast_sorcery *sorcery,
+       const struct ast_sorcery_instance_observer *callbacks)
+{
+       struct sorcery_instance_observer *cb;
+
+       cb = ao2_alloc(sizeof(*cb), NULL);
+       if (!cb) {
+               return -1;
+       }
+
+       cb->callbacks = callbacks;
+       ao2_link(sorcery->observers, cb);
+       ao2_ref(cb, -1);
+
+       return 0;
+}
+
+void ast_sorcery_instance_observer_remove(struct ast_sorcery *sorcery,
+       const struct ast_sorcery_instance_observer *callbacks)
+{
+       ao2_callback(sorcery->observers, OBJ_NODATA | OBJ_UNLINK, 
sorcery_generic_observer_remove, (void *)callbacks);
+}
+
+int ast_sorcery_wizard_observer_add(struct ast_sorcery_wizard *interface,
+       const struct ast_sorcery_wizard_observer *callbacks)
+{
+       RAII_VAR(struct ast_sorcery_internal_wizard *, wizard,
+               interface ? ao2_find(wizards, interface->name, OBJ_SEARCH_KEY) 
: NULL,
+                       ao2_cleanup);
+
+       if (wizard) {
+               struct sorcery_wizard_observer *cb;
+
+               cb = ao2_alloc(sizeof(*cb), NULL);
+               if (!cb) {
+                       return -1;
+               }
+
+               cb->callbacks = callbacks;
+               ao2_link(wizard->observers, cb);
+               ao2_ref(cb, -1);
+
+               return 0;
+       }
+
+       return -1;
+}
+
+void ast_sorcery_wizard_observer_remove(struct ast_sorcery_wizard *interface,
+       const struct ast_sorcery_wizard_observer *callbacks)
+{
+       RAII_VAR(struct ast_sorcery_internal_wizard *, wizard,
+               interface ? ao2_find(wizards, interface->name, OBJ_SEARCH_KEY) 
: NULL,
+                       ao2_cleanup);
+
+       if (wizard) {
+               ao2_callback(wizard->observers, OBJ_NODATA | OBJ_UNLINK, 
sorcery_generic_observer_remove, (void *)callbacks);
+       }
+}
+
 /*! \brief Destructor called when sorcery structure is destroyed */
 static void sorcery_destructor(void *obj)
 {
        struct ast_sorcery *sorcery = obj;
 
+       if (sorcery->observers) {
+               NOTIFY_GLOBAL_OBSERVERS(observers, instance_destroying, 
sorcery->module_name, sorcery);
+       }
+       ao2_cleanup(sorcery->observers);
        ao2_cleanup(sorcery->types);
 }
 
@@ -580,6 +762,12 @@
                goto done;
        }
 
+       if (!(sorcery->observers = 
ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_RWLOCK, 0, NULL, NULL))) {
+               ao2_ref(sorcery, -1);
+               sorcery = NULL;
+               goto done;
+       }
+
        strcpy(sorcery->module_name, module_name); /* Safe */
 
        if (__ast_sorcery_apply_config(sorcery, module_name, module_name) == 
AST_SORCERY_APPLY_FAIL) {
@@ -591,6 +779,8 @@
 
        ao2_link_flags(instances, sorcery, OBJ_NOLOCK);
 
+       NOTIFY_GLOBAL_OBSERVERS(observers, instance_created, module_name, 
sorcery);
+
 done:
        ao2_unlock(instances);
        return sorcery;
@@ -683,22 +873,22 @@
        struct ast_sorcery_object_wizard *object_wizard = obj;
 
        if (object_wizard->data) {
-               object_wizard->wizard->close(object_wizard->data);
+               object_wizard->wizard->callbacks.close(object_wizard->data);
        }
 
        if (object_wizard->wizard) {
-               ast_module_unref(object_wizard->wizard->module);
+               ast_module_unref(object_wizard->wizard->callbacks.module);
        }
 
        ao2_cleanup(object_wizard->wizard);
 }
 
 /*! \brief Internal function which creates an object type and adds a wizard 
mapping */
-static enum ast_sorcery_apply_result sorcery_apply_wizard_mapping(struct 
ast_sorcery *sorcery,
+enum ast_sorcery_apply_result __ast_sorcery_apply_wizard_mapping(struct 
ast_sorcery *sorcery,
                const char *type, const char *module, const char *name, const 
char *data, unsigned int caching)
 {
        RAII_VAR(struct ast_sorcery_object_type *, object_type, 
ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
-       RAII_VAR(struct ast_sorcery_wizard *, wizard, ao2_find(wizards, name, 
OBJ_KEY), ao2_cleanup);
+       RAII_VAR(struct ast_sorcery_internal_wizard *, wizard, 
ao2_find(wizards, name, OBJ_KEY), ao2_cleanup);
        RAII_VAR(struct ast_sorcery_object_wizard *, object_wizard, 
ao2_alloc(sizeof(*object_wizard), sorcery_object_wizard_destructor), 
ao2_cleanup);
        int created = 0;
 
@@ -719,17 +909,17 @@
                found = ao2_find(object_type->wizards, wizard, 
OBJ_SEARCH_OBJECT);
                if (found) {
                        ast_debug(1, "Wizard %s already applied to object type 
%s\n",
-                                       wizard->name, object_type->name);
+                                       wizard->callbacks.name, 
object_type->name);
                        ao2_cleanup(found);
                        return AST_SORCERY_APPLY_DUPLICATE;
                }
        }
 
-       if (wizard->open && !(object_wizard->data = wizard->open(data))) {
+       if (wizard->callbacks.open && !(object_wizard->data = 
wizard->callbacks.open(data))) {
                return AST_SORCERY_APPLY_FAIL;
        }
 
-       ast_module_ref(wizard->module);
+       ast_module_ref(wizard->callbacks.module);
 
        object_wizard->wizard = ao2_bump(wizard);
        object_wizard->caching = caching;
@@ -739,6 +929,9 @@
        if (created) {
                ao2_link(sorcery->types, object_type);
        }
+
+       NOTIFY_INSTANCE_OBSERVERS(sorcery->observers, wizard_mapped,
+               sorcery->module_name, sorcery, type, &wizard->callbacks, data, 
object_wizard->data);
 
        return AST_SORCERY_APPLY_SUCCESS;
 }
@@ -778,7 +971,7 @@
                }
 
                /* Any error immediately causes us to stop */
-               if (sorcery_apply_wizard_mapping(sorcery, type, module, wizard, 
data, caching) == AST_SORCERY_APPLY_FAIL) {
+               if (__ast_sorcery_apply_wizard_mapping(sorcery, type, module, 
wizard, data, caching) == AST_SORCERY_APPLY_FAIL) {
                        res = AST_SORCERY_APPLY_FAIL;
                        break;
                }
@@ -798,7 +991,7 @@
                return AST_SORCERY_APPLY_DEFAULT_UNNECESSARY;
        }
 
-       return sorcery_apply_wizard_mapping(sorcery, type, module, name, data, 
0);
+       return __ast_sorcery_apply_wizard_mapping(sorcery, type, module, name, 
data, 0);
 }
 
 static int sorcery_extended_config_handler(const struct aco_option *opt, 
struct ast_variable *var, void *obj)
@@ -846,6 +1039,9 @@
        if (ast_sorcery_object_fields_register(sorcery, type, "^@", 
sorcery_extended_config_handler, sorcery_extended_fields_handler)) {
                return -1;
        }
+
+       NOTIFY_INSTANCE_OBSERVERS(sorcery->observers, object_type_registered,
+               sorcery->module_name, sorcery, type);
 
        return 0;
 }
@@ -986,10 +1182,16 @@
                return 0;
        }
 
-       load = !details->reload ? wizard->wizard->load : wizard->wizard->reload;
+       load = !details->reload ? wizard->wizard->callbacks.load : 
wizard->wizard->callbacks.reload;
 
        if (load) {
+               NOTIFY_WIZARD_OBSERVERS(wizard->wizard->observers, 
wizard_loading,
+                       wizard->wizard->callbacks.name, 
&wizard->wizard->callbacks, details->type, details->reload);
+
                load(wizard->data, details->sorcery, details->type);
+
+               NOTIFY_WIZARD_OBSERVERS(wizard->wizard->observers, 
wizard_loaded,
+                       wizard->wizard->callbacks.name, 
&wizard->wizard->callbacks, details->type, details->reload);
        }
 
        return 0;
@@ -1053,6 +1255,10 @@
        struct sorcery_load_details *details = arg;
 
        details->type = type->name;
+
+       NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, 
object_type_loading,
+               details->sorcery->module_name, details->sorcery, type->name, 
details->reload);
+
        ao2_callback(type->wizards, OBJ_NODATA, sorcery_wizard_load, details);
 
        if (ao2_container_count(type->observers)) {
@@ -1062,6 +1268,9 @@
                        ao2_cleanup(invocation);
                }
        }
+
+       NOTIFY_INSTANCE_OBSERVERS(details->sorcery->observers, 
object_type_loaded,
+               details->sorcery->module_name, details->sorcery, type->name, 
details->reload);
 
        return 0;
 }
@@ -1073,7 +1282,13 @@
                .reload = 0,
        };
 
+       NOTIFY_INSTANCE_OBSERVERS(sorcery->observers, instance_loading,
+               sorcery->module_name, sorcery, 0);
+
        ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details);
+
+       NOTIFY_INSTANCE_OBSERVERS(sorcery->observers, instance_loaded,
+               sorcery->module_name, sorcery, 0);
 }
 
 void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char 
*type)
@@ -1098,7 +1313,14 @@
                .reload = 1,
        };
 
+       NOTIFY_INSTANCE_OBSERVERS(sorcery->observers, instance_loading,
+               sorcery->module_name, sorcery, 1);
+
        ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details);
+
+       NOTIFY_INSTANCE_OBSERVERS(sorcery->observers, instance_loaded,
+               sorcery->module_name, sorcery, 1);
+
 }
 
 void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char 
*type)
@@ -1302,20 +1524,6 @@
        return res;
 }
 
-static const struct ast_variable *sorcery_find_field(const struct ast_variable 
*fields, const char *name)
-{
-       const struct ast_variable *field;
-
-       /* Search the linked list of fields to find the correct one */
-       for (field = fields; field; field = field->next) {
-               if (!strcmp(field->name, name)) {
-                       return field;
-               }
-       }
-
-       return NULL;
-}
-
 int ast_sorcery_changeset_create(const struct ast_variable *original, const 
struct ast_variable *modified, struct ast_variable **changes)
 {
        const struct ast_variable *field;
@@ -1329,9 +1537,9 @@
        }
 
        for (field = modified; field; field = field->next) {
-               const struct ast_variable *old = sorcery_find_field(original, 
field->name);
-
-               if (!old || strcmp(old->value, field->value)) {
+               const char *old_value = ast_variable_find_in_list(original, 
field->name);
+
+               if (!old_value || strcmp(old_value, field->value)) {
                        struct ast_variable *tmp;
 
                        if (!(tmp = ast_variable_new(field->name, field->value, 
""))) {
@@ -1475,11 +1683,11 @@
        const struct ast_sorcery_object_wizard *object_wizard = obj;
        const struct sorcery_details *details = arg;
 
-       if (!object_wizard->caching || !object_wizard->wizard->create) {
+       if (!object_wizard->caching || 
!object_wizard->wizard->callbacks.create) {
                return 0;
        }
 
-       object_wizard->wizard->create(details->sorcery, object_wizard->data, 
details->obj);
+       object_wizard->wizard->callbacks.create(details->sorcery, 
object_wizard->data, details->obj);
 
        return 0;
 }
@@ -1498,8 +1706,8 @@
 
        i = ao2_iterator_init(object_type->wizards, 0);
        for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
-               if (wizard->wizard->retrieve_id &&
-                       !(object = wizard->wizard->retrieve_id(sorcery, 
wizard->data, object_type->name, id))) {
+               if (wizard->wizard->callbacks.retrieve_id &&
+                       !(object = 
wizard->wizard->callbacks.retrieve_id(sorcery, wizard->data, object_type->name, 
id))) {
                        continue;
                }
 
@@ -1540,12 +1748,12 @@
        i = ao2_iterator_init(object_type->wizards, 0);
        for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
                if ((flags & AST_RETRIEVE_FLAG_MULTIPLE)) {
-                       if (wizard->wizard->retrieve_multiple) {
-                               wizard->wizard->retrieve_multiple(sorcery, 
wizard->data, object_type->name, object, fields);
+                       if (wizard->wizard->callbacks.retrieve_multiple) {
+                               
wizard->wizard->callbacks.retrieve_multiple(sorcery, wizard->data, 
object_type->name, object, fields);
                        }
-               } else if (fields && wizard->wizard->retrieve_fields) {
-                       if (wizard->wizard->retrieve_fields) {
-                               object = 
wizard->wizard->retrieve_fields(sorcery, wizard->data, object_type->name, 
fields);
+               } else if (fields && wizard->wizard->callbacks.retrieve_fields) 
{
+                       if (wizard->wizard->callbacks.retrieve_fields) {
+                               object = 
wizard->wizard->callbacks.retrieve_fields(sorcery, wizard->data, 
object_type->name, fields);
                        }
                }
 
@@ -1581,11 +1789,11 @@
 
        i = ao2_iterator_init(object_type->wizards, 0);
        for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) {
-               if (!wizard->wizard->retrieve_regex) {
+               if (!wizard->wizard->callbacks.retrieve_regex) {
                        continue;
                }
 
-               wizard->wizard->retrieve_regex(sorcery, wizard->data, 
object_type->name, objects, regex);
+               wizard->wizard->callbacks.retrieve_regex(sorcery, wizard->data, 
object_type->name, objects, regex);
        }
        ao2_iterator_destroy(&i);
 
@@ -1598,13 +1806,13 @@
        const struct ast_sorcery_object_wizard *object_wizard = obj;
        const struct sorcery_details *details = arg;
 
-       if (!object_wizard->wizard->create) {
+       if (!object_wizard->wizard->callbacks.create) {
                ast_assert(0);
                ast_log(LOG_ERROR, "Sorcery wizard '%s' doesn't contain a 
'create' virtual function.\n",
-                       object_wizard->wizard->name);
+                       object_wizard->wizard->callbacks.name);
                return 0;
        }
-       return (!object_wizard->caching && 
!object_wizard->wizard->create(details->sorcery, object_wizard->data, 
details->obj)) ? CMP_MATCH | CMP_STOP : 0;
+       return (!object_wizard->caching && 
!object_wizard->wizard->callbacks.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 */
@@ -1685,7 +1893,7 @@
        const struct ast_sorcery_object_wizard *object_wizard = obj;
        const struct sorcery_details *details = arg;
 
-       return (object_wizard->wizard->update && 
!object_wizard->wizard->update(details->sorcery, object_wizard->data, 
details->obj) &&
+       return (object_wizard->wizard->callbacks.update && 
!object_wizard->wizard->callbacks.update(details->sorcery, object_wizard->data, 
details->obj) &&
                !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
 }
 
@@ -1744,7 +1952,7 @@
        const struct ast_sorcery_object_wizard *object_wizard = obj;
        const struct sorcery_details *details = arg;
 
-       return (object_wizard->wizard->delete && 
!object_wizard->wizard->delete(details->sorcery, object_wizard->data, 
details->obj) &&
+       return (object_wizard->wizard->callbacks.delete && 
!object_wizard->wizard->callbacks.delete(details->sorcery, object_wizard->data, 
details->obj) &&
                !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
 }
 

Modified: branches/13/tests/test_sorcery.c
URL: 
http://svnview.digium.com/svn/asterisk/branches/13/tests/test_sorcery.c?view=diff&rev=429000&r1=428999&r2=429000
==============================================================================
--- branches/13/tests/test_sorcery.c (original)
+++ branches/13/tests/test_sorcery.c Fri Dec  5 11:06:42 2014
@@ -3033,6 +3033,309 @@
        ast_sorcery_object_fields_register(sorcery, "test", "^", 
test_sorcery_regex_handler, test_sorcery_regex_fields);
 
        ast_test_validate(test, 
ast_sorcery_is_object_field_registered(object_type, "goober"));
+
+       return AST_TEST_PASS;
+}
+
+static int event_observed;
+
+static void wizard_observer(const char *name, const struct ast_sorcery_wizard 
*wizard)
+{
+       if (!strcmp(wizard->name, "test")) {
+               event_observed = 1;
+       }
+}
+
+static void instance_observer(const char *name, struct ast_sorcery *sorcery)
+{
+       if (!strcmp(name, "test_sorcery")) {
+               event_observed = 1;
+       }
+}
+
+AST_TEST_DEFINE(global_observation)
+{
+       RAII_VAR(struct ast_sorcery_wizard *, wizard, &test_wizard, 
ast_sorcery_wizard_unregister);
+       RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+       const struct ast_sorcery_global_observer observer = {
+               .wizard_registered = wizard_observer,
+               .instance_created = instance_observer,
+               .wizard_unregistering = wizard_observer,
+               .instance_destroying = instance_observer,
+       };
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "global_observation";
+               info->category = "/main/sorcery/";
+               info->summary = "global sorcery observation test";
+               info->description =
+                       "Test observation of sorcery (global)";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       ast_sorcery_global_observer_add(&observer);
+
+       event_observed = 0;
+       ast_sorcery_wizard_register(wizard);
+       ast_test_validate(test, (event_observed == 1), "Wizard registered 
failed");
+
+       event_observed = 0;
+       ast_sorcery_wizard_unregister(wizard);
+       ast_test_validate(test, (event_observed == 1), "Wizard unregistered 
failed");
+
+       event_observed = 0;
+       sorcery = ast_sorcery_open();
+       ast_test_validate(test, (event_observed == 1), "Instance created 
failed");
+
+       event_observed = 0;
+       ast_sorcery_unref(sorcery);
+       sorcery = NULL;
+       ast_test_validate(test, (event_observed == 1), "Instance destroyed 
failed");
+
+       ast_sorcery_global_observer_remove(&observer);
+       event_observed = 0;
+       ast_sorcery_wizard_register(&test_wizard);
+       ast_test_validate(test, (event_observed == 0), "Observer removed 
failed");
+
+       return AST_TEST_PASS;
+}
+
+static void instance_loaded_observer(const char *name, const struct 
ast_sorcery *sorcery,
+       int reloaded)
+{
+       if (!strcmp(name, "test_sorcery") && !reloaded) {
+               event_observed++;
+       }
+}
+
+static void instance_reloaded_observer(const char *name,
+       const struct ast_sorcery *sorcery, int reloaded)
+{
+       if (!strcmp(name, "test_sorcery") && reloaded) {
+               event_observed++;
+       }
+}
+
+static void wizard_mapped_observer(const char *name, struct ast_sorcery 
*sorcery,
+       const char *object_type, struct ast_sorcery_wizard *wizard,
+       const char *wizard_args, void *wizard_data)
+{
+       if (!strcmp(name, "test_sorcery") && !strcmp(object_type, 
"test_object_type")
+               && !strcmp(wizard->name, "memory") && !strcmp(wizard_args, 
"memwiz")) {
+               event_observed++;
+       }
+}
+
+static void object_type_registered_observer(const char *name,
+       struct ast_sorcery *sorcery, const char *object_type)
+{
+       if (!strcmp(name, "test_sorcery") && !strcmp(object_type, 
"test_object_type")) {
+               event_observed++;
+       }
+}
+
+static void object_type_loaded_observer(const char *name,
+       const struct ast_sorcery *sorcery, const char *object_type, int 
reloaded)
+{
+       if (!strcmp(name, "test_sorcery") && !strcmp(object_type, 
"test_object_type")
+               && !reloaded) {
+               event_observed++;
+       }
+}
+
+static void object_type_reloaded_observer(const char *name,
+       const struct ast_sorcery *sorcery, const char *object_type, int 
reloaded)
+{
+       if (!strcmp(name, "test_sorcery") && !strcmp(object_type, 
"test_object_type")
+               && reloaded) {
+               event_observed++;
+       }
+}
+
+AST_TEST_DEFINE(instance_observation)
+{
+       RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+       struct ast_sorcery_instance_observer observer = {
+               .wizard_mapped = wizard_mapped_observer,
+               .object_type_registered = object_type_registered_observer,
+       };
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "instance_observation";
+               info->category = "/main/sorcery/";
+               info->summary = "sorcery instance observation test";
+               info->description =
+                       "Test observation of sorcery (instance)";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       /* Test instance load */
+       if (!(sorcery = ast_sorcery_open())) {
+               ast_test_status_update(test, "Failed to open a sorcery 
instance\n");
+               return AST_TEST_FAIL;
+       }
+       observer.instance_loading = instance_loaded_observer;
+       observer.instance_loaded = instance_loaded_observer;
+       ast_sorcery_instance_observer_add(sorcery, &observer);
+       event_observed = 0;
+       ast_sorcery_load(sorcery);
+       ast_test_validate(test, (event_observed == 2), "Instance loaded 
failed");
+       event_observed = 0;
+       ast_sorcery_reload(sorcery);
+       ast_test_validate(test, (event_observed == 0), "Instance reloaded 
failed");
+
+       /* Test instance reload */
+       ast_sorcery_instance_observer_remove(sorcery, &observer);
+       observer.instance_loading = instance_reloaded_observer;
+       observer.instance_loaded = instance_reloaded_observer;
+       ast_sorcery_instance_observer_add(sorcery, &observer);
+       event_observed = 0;
+       ast_sorcery_load(sorcery);
+       ast_test_validate(test, (event_observed == 0), "Instance loaded 
failed");
+       event_observed = 0;
+       ast_sorcery_reload(sorcery);
+       ast_test_validate(test, (event_observed == 2), "Instance reloaded 
failed");
+
+       /* Test wizard mapping */
+       event_observed = 0;
+       ast_sorcery_apply_default(sorcery, "test_object_type", "memory", 
"memwiz");
+       ast_test_validate(test, (event_observed == 1), "Wizard mapping failed");
+
+       /* Test object type register */
+       event_observed = 0;
+       ast_sorcery_internal_object_register(sorcery, "test_object_type",
+               test_sorcery_object_alloc, NULL, NULL);
+       ast_test_validate(test, (event_observed == 1), "Object type registered 
failed");
+
+       /* Test object type load */
+       ast_sorcery_instance_observer_remove(sorcery, &observer);
+       observer.object_type_loading = object_type_loaded_observer;
+       observer.object_type_loaded = object_type_loaded_observer;
+       ast_sorcery_instance_observer_add(sorcery, &observer);
+       event_observed = 0;
+       ast_sorcery_load_object(sorcery, "test_object_type");
+       ast_test_validate(test, (event_observed == 2), "Object type loaded 
failed");
+       event_observed = 0;
+       ast_sorcery_reload_object(sorcery, "test_object_type");
+       ast_test_validate(test, (event_observed == 0), "Object type reloaded 
failed");
+
+       /* Test object type reload */
+       ast_sorcery_instance_observer_remove(sorcery, &observer);
+       observer.object_type_loading = object_type_reloaded_observer;
+       observer.object_type_loaded = object_type_reloaded_observer;
+       ast_sorcery_instance_observer_add(sorcery, &observer);
+       event_observed = 0;
+       ast_sorcery_load_object(sorcery, "test_object_type");
+       ast_test_validate(test, (event_observed == 0), "Object type loaded 
failed");
+       event_observed = 0;
+       ast_sorcery_reload_object(sorcery, "test_object_type");
+       ast_test_validate(test, (event_observed == 2), "Object type reloaded 
failed");
+
+       ast_sorcery_instance_observer_remove(sorcery, &observer);
+       event_observed = 0;
+       ast_sorcery_apply_default(sorcery, "test_object_type", "memory", 
"memwiz");
+       ast_test_validate(test, (event_observed == 0), "Observer remove 
failed");
+
+       return AST_TEST_PASS;
+}
+
+static void wizard_loaded_observer(const char *name,
+       const struct ast_sorcery_wizard *wizard, const char *object_type, int 
reloaded)
+{
+       if (!strcmp(name, "test") && !strcmp(object_type, "test_object_type")
+               && !reloaded) {
+               event_observed++;
+       }
+}
+
+static void sorcery_test_load(void *data, const struct ast_sorcery *sorcery, 
const char *type)
+{
+       return;
+}
+
+static void wizard_reloaded_observer(const char *name,
+       const struct ast_sorcery_wizard *wizard, const char *object_type, int 
reloaded)
+{
+       if (!strcmp(name, "test") && !strcmp(object_type, "test_object_type")
+               && reloaded) {
+               event_observed++;
+       }
+}
+
+AST_TEST_DEFINE(wizard_observation)
+{
+       RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+       RAII_VAR(struct ast_sorcery_wizard *, wizard, &test_wizard, 
ast_sorcery_wizard_unregister);
+       struct ast_sorcery_wizard_observer observer = {
+               .wizard_loading = wizard_loaded_observer,
+               .wizard_loaded = wizard_loaded_observer,
+       };
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "wizard_observation";
+               info->category = "/main/sorcery/";
+               info->summary = "sorcery wizard observation test";
+               info->description =
+                       "Test observation of sorcery (wizard)";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       wizard->load = sorcery_test_load;
+       wizard->reload = sorcery_test_load;
+
+       /* Test wizard observer remove and wizard unregister */
+       ast_sorcery_wizard_register(wizard);
+       ast_sorcery_wizard_observer_add(wizard, &observer);
+       ast_sorcery_wizard_observer_remove(wizard, &observer);
+       event_observed = 0;
+       ast_sorcery_wizard_unregister(wizard);
+       ast_test_validate(test, (event_observed == 0), "Wizard observer removed 
failed");
+

[... 60 lines stripped ...]

-- 
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

svn-commits mailing list
To UNSUBSCRIBE or update options visit:
   http://lists.digium.com/mailman/listinfo/svn-commits

Reply via email to