[asterisk-commits] gtjoseph: branch 12 r428999 - in /branches/12: include/asterisk/ main/ tests/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Dec 5 11:05:21 CST 2014


Author: gtjoseph
Date: Fri Dec  5 11:05:09 2014
New Revision: 428999

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=428999
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/

Modified:
    branches/12/include/asterisk/sorcery.h
    branches/12/include/asterisk/test.h
    branches/12/main/sorcery.c
    branches/12/tests/test_sorcery.c

Modified: branches/12/include/asterisk/sorcery.h
URL: http://svnview.digium.com/svn/asterisk/branches/12/include/asterisk/sorcery.h?view=diff&rev=428999&r1=428998&r2=428999
==============================================================================
--- branches/12/include/asterisk/sorcery.h (original)
+++ branches/12/include/asterisk/sorcery.h Fri Dec  5 11:05:09 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/12/include/asterisk/test.h
URL: http://svnview.digium.com/svn/asterisk/branches/12/include/asterisk/test.h?view=diff&rev=428999&r1=428998&r2=428999
==============================================================================
--- branches/12/include/asterisk/test.h (original)
+++ branches/12/include/asterisk/test.h Fri Dec  5 11:05:09 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/12/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/main/sorcery.c?view=diff&rev=428999&r1=428998&r2=428999
==============================================================================
--- branches/12/main/sorcery.c (original)
+++ branches/12/main/sorcery.c Fri Dec  5 11:05:09 2014
@@ -61,6 +61,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;
 
@@ -154,10 +177,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;
@@ -170,6 +202,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];
 };
@@ -188,6 +224,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;
@@ -264,7 +324,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) {
@@ -273,7 +333,7 @@
 		break;
 	case OBJ_SEARCH_OBJECT:
 		object = obj;
-		key = object->name;
+		key = object->callbacks.name;
 		break;
 	default:
 		ast_assert(0);
@@ -285,20 +345,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;
@@ -370,6 +430,8 @@
 {
 	ao2_cleanup(wizards);
 	wizards = NULL;
+	ao2_cleanup(observers);
+	observers = NULL;
 	ao2_cleanup(instances);
 	instances = NULL;
 }
@@ -444,6 +506,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) {
@@ -458,9 +527,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));
@@ -473,17 +549,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);
@@ -494,9 +578,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 {
@@ -504,11 +592,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);
 }
 
@@ -582,6 +764,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) {
@@ -593,6 +781,8 @@
 
 	ao2_link_flags(instances, sorcery, OBJ_NOLOCK);
 
+	NOTIFY_GLOBAL_OBSERVERS(observers, instance_created, module_name, sorcery);
+
 done:
 	ao2_unlock(instances);
 	return sorcery;
@@ -685,22 +875,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;
 
@@ -721,17 +911,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;
@@ -741,6 +931,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;
 }
@@ -780,7 +973,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;
 		}
@@ -800,7 +993,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)
@@ -848,6 +1041,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;
 }
@@ -988,10 +1184,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;
@@ -1055,6 +1257,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)) {
@@ -1064,6 +1270,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;
 }
@@ -1075,7 +1284,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)
@@ -1100,7 +1315,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)
@@ -1304,20 +1526,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;
@@ -1331,9 +1539,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, ""))) {
@@ -1477,11 +1685,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;
 }
@@ -1500,8 +1708,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;
 		}
 
@@ -1542,12 +1750,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);
 			}
 		}
 
@@ -1583,11 +1791,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);
 
@@ -1600,13 +1808,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 */
@@ -1687,7 +1895,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;
 }
 
@@ -1746,7 +1954,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/12/tests/test_sorcery.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/tests/test_sorcery.c?view=diff&rev=428999&r1=428998&r2=428999
==============================================================================
--- branches/12/tests/test_sorcery.c (original)
+++ branches/12/tests/test_sorcery.c Fri Dec  5 11:05:09 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");
+
+	/* Setup for test loaded and reloaded */
+	if (!(sorcery = ast_sorcery_open())) {
+		ast_test_status_update(test, "Failed to open a sorcery instance\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_wizard_register(wizard);
+	ast_sorcery_apply_default(sorcery, "test_object_type", "test", NULL);

[... 52 lines stripped ...]



More information about the asterisk-commits mailing list