[asterisk-commits] file: trunk r381134 - in /trunk: include/asterisk/ main/ tests/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun Feb 10 08:58:42 CST 2013


Author: file
Date: Sun Feb 10 08:58:37 2013
New Revision: 381134

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=381134
Log:
Add additional functionality to the Sorcery API.

This commit adds native implementation support for copying and diffing objects,
as well as the ability to load or reload on a per-object type level.

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

Modified:
    trunk/include/asterisk/sorcery.h
    trunk/main/sorcery.c
    trunk/tests/test_sorcery.c

Modified: trunk/include/asterisk/sorcery.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/sorcery.h?view=diff&rev=381134&r1=381133&r2=381134
==============================================================================
--- trunk/include/asterisk/sorcery.h (original)
+++ trunk/include/asterisk/sorcery.h Sun Feb 10 08:58:37 2013
@@ -112,8 +112,8 @@
 	/*! \brief Return all matching objects */
 	AST_RETRIEVE_FLAG_MULTIPLE = (1 << 0),
 
-        /*! \brief Perform no matching, return all objects */
-        AST_RETRIEVE_FLAG_ALL = (1 << 1),
+	/*! \brief Perform no matching, return all objects */
+	AST_RETRIEVE_FLAG_ALL = (1 << 1),
 };
 
 /*! \brief Forward declaration for the sorcery main structure */
@@ -150,6 +150,29 @@
  * \param obj The object itself
  */
 typedef void (*sorcery_apply_handler)(const struct ast_sorcery *sorcery, void *obj);
+
+/*!
+ * \brief A callback function for copying the contents of one object to another
+ *
+ * \param src The source object
+ * \param dst The destination object
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+typedef int (*sorcery_copy_handler)(const void *src, void *dst);
+
+/*!
+ * \brief A callback function for generating a changeset between two objects
+ *
+ * \param original The original object
+ * \param modified The modified object
+ * \param changes The changeset
+ *
+ * \param 0 success
+ * \param -1 failure
+ */
+typedef int (*sorcery_diff_handler)(const void *original, const void *modified, struct ast_variable **changes);
 
 /*! \brief Interface for a sorcery wizard */
 struct ast_sorcery_wizard {
@@ -288,6 +311,24 @@
  * \retval -1 failure
  */
 int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, aco_type_item_alloc alloc, sorcery_transform_handler transform, sorcery_apply_handler apply);
+
+/*!
+ * \brief Set the copy handler for an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param copy Copy handler
+ */
+void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char *type, sorcery_copy_handler copy);
+
+/*!
+ * \brief Set the diff handler for an object type
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Type of object
+ * \param diff Diff handler
+ */
+void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff);
 
 /*!
  * \brief Register a field within an object
@@ -346,11 +387,27 @@
 void ast_sorcery_load(const struct ast_sorcery *sorcery);
 
 /*!
+ * \brief Inform any wizards of a specific object type to load persistent objects
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Name of the object type to load
+ */
+void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char *type);
+
+/*!
  * \brief Inform any wizards to reload persistent objects
  *
  * \param sorcery Pointer to a sorcery structure
  */
 void ast_sorcery_reload(const struct ast_sorcery *sorcery);
+
+/*!
+ * \brief Inform any wizards of a specific object type to reload persistent objects
+ *
+ * \param sorcery Pointer to a sorcery structure
+ * \param type Name of the object type to reload
+ */
+void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type);
 
 /*!
  * \brief Increase the reference count of a sorcery structure

Modified: trunk/main/sorcery.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/sorcery.c?view=diff&rev=381134&r1=381133&r2=381134
==============================================================================
--- trunk/main/sorcery.c (original)
+++ trunk/main/sorcery.c Sun Feb 10 08:58:37 2013
@@ -63,6 +63,12 @@
 	/*! \brief Optional object set apply callback */
 	sorcery_apply_handler apply;
 
+	/*! \brief Optional object copy callback */
+	sorcery_copy_handler copy;
+
+	/*! \brief Optional object diff callback */
+	sorcery_diff_handler diff;
+
 	/*! \brief Wizard instances */
 	struct ao2_container *wizards;
 
@@ -299,7 +305,7 @@
 		return NULL;
 	}
 
-	if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) {
+	if (!(sorcery->types = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, TYPE_BUCKETS, sorcery_type_hash, sorcery_type_cmp))) {
 		ao2_ref(sorcery, -1);
 		sorcery = NULL;
 	}
@@ -467,7 +473,7 @@
 {
 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
 
-	if (!object_type) {
+	if (!object_type || object_type->type.item_alloc) {
 		return -1;
 	}
 
@@ -485,6 +491,28 @@
 	}
 
 	return 0;
+}
+
+void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char *type, sorcery_copy_handler copy)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+	if (!object_type) {
+		return;
+	}
+
+	object_type->copy = copy;
+}
+
+void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+
+	if (!object_type) {
+		return;
+	}
+
+	object_type->diff = diff;
 }
 
 int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type,
@@ -573,6 +601,21 @@
 	ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details);
 }
 
+void ast_sorcery_load_object(const struct ast_sorcery *sorcery, const char *type)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+	struct sorcery_load_details details = {
+		.sorcery = sorcery,
+		.reload = 0,
+	};
+
+	if (!object_type) {
+		return;
+	}
+
+	sorcery_object_load(object_type, &details, 0);
+}
+
 void ast_sorcery_reload(const struct ast_sorcery *sorcery)
 {
 	struct sorcery_load_details details = {
@@ -581,6 +624,21 @@
 	};
 
 	ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details);
+}
+
+void ast_sorcery_reload_object(const struct ast_sorcery *sorcery, const char *type)
+{
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
+	struct sorcery_load_details details = {
+		.sorcery = sorcery,
+		.reload = 1,
+	};
+
+	if (!object_type) {
+		return;
+	}
+
+	sorcery_object_load(object_type, &details, 0);
 }
 
 void ast_sorcery_ref(struct ast_sorcery *sorcery)
@@ -753,14 +811,25 @@
 void *ast_sorcery_copy(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 ast_sorcery_object_details *copy = ast_sorcery_alloc(sorcery, details->type, details->id);
 	RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
-
-	if (!copy ||
-	    !(objectset = ast_sorcery_objectset_create(sorcery, object)) ||
-	    ast_sorcery_objectset_apply(sorcery, copy, objectset)) {
+	int res = 0;
+
+	if (!copy) {
+		return NULL;
+	} else if (object_type->copy) {
+		res = object_type->copy(object, copy);
+	} else if ((objectset = ast_sorcery_objectset_create(sorcery, object))) {
+		res = ast_sorcery_objectset_apply(sorcery, copy, objectset);
+	} else {
+		/* No native copy available and could not create an objectset, this copy has failed */
+		res = -1;
+	}
+
+	if (res) {
 		ao2_cleanup(copy);
-		return NULL;
+		copy = NULL;
 	}
 
 	return copy;
@@ -768,8 +837,7 @@
 
 int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified, struct ast_variable **changes)
 {
-	RAII_VAR(struct ast_variable *, objectset1, NULL, ast_variables_destroy);
-	RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy);
+	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, ast_sorcery_object_get_type(original), OBJ_KEY), ao2_cleanup);
 
 	*changes = NULL;
 
@@ -777,10 +845,19 @@
 		return -1;
 	}
 
-	objectset1 = ast_sorcery_objectset_create(sorcery, original);
-	objectset2 = ast_sorcery_objectset_create(sorcery, modified);
-
-	return ast_sorcery_changeset_create(objectset1, objectset2, changes);
+	if (original == modified) {
+		return 0;
+	} else if (!object_type->diff) {
+		RAII_VAR(struct ast_variable *, objectset1, NULL, ast_variables_destroy);
+		RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy);
+
+		objectset1 = ast_sorcery_objectset_create(sorcery, original);
+		objectset2 = ast_sorcery_objectset_create(sorcery, modified);
+
+		return ast_sorcery_changeset_create(objectset1, objectset2, changes);
+	} else {
+		return object_type->diff(original, modified, changes);
+	}
 }
 
 /*! \brief Internal function used to create an object in caching wizards */

Modified: trunk/tests/test_sorcery.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_sorcery.c?view=diff&rev=381134&r1=381133&r2=381134
==============================================================================
--- trunk/tests/test_sorcery.c (original)
+++ trunk/tests/test_sorcery.c Sun Feb 10 08:58:37 2013
@@ -77,6 +77,22 @@
 	return transformed;
 }
 
+/*! \brief Internal function which copies pre-defined data into an object, natively */
+static int test_sorcery_copy(const void *src, void *dst)
+{
+	struct test_sorcery_object *obj = dst;
+	obj->bob = 10;
+	obj->joe = 20;
+	return 0;
+}
+
+/*! \brief Internal function which creates a pre-defined diff natively */
+static int test_sorcery_diff(const void *original, const void *modified, struct ast_variable **changes)
+{
+	*changes = ast_variable_new("yes", "itworks", "");
+	return 0;
+}
+
 /*! \brief Test structure for caching */
 struct sorcery_test_caching {
 	/*! \brief Whether the object has been created in the cache or not */
@@ -142,18 +158,18 @@
 {
 	struct ast_sorcery *sorcery;
 
-        if (!(sorcery = ast_sorcery_open())) {
+	if (!(sorcery = ast_sorcery_open())) {
 		return NULL;
-        }
-
-        if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
-	    ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
+	}
+
+	if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
+		ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
 		ast_sorcery_unref(sorcery);
 		return NULL;
-        }
-
-        ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
-        ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
+	}
+
+	ast_sorcery_object_field_register(sorcery, "test", "bob", "5", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, bob));
+	ast_sorcery_object_field_register(sorcery, "test", "joe", "10", OPT_UINT_T, 0, FLDSET(struct test_sorcery_object, joe));
 
 	return sorcery;
 }
@@ -330,6 +346,11 @@
 
 	if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
 		ast_test_status_update(test, "Failed to register object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (!ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
+		ast_test_status_update(test, "Registered object type a second time, despite it being registered already\n");
 		return AST_TEST_FAIL;
 	}
 
@@ -544,6 +565,57 @@
 	return res;
 }
 
+AST_TEST_DEFINE(object_copy_native)
+{
+	int res = AST_TEST_PASS;
+	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
+	RAII_VAR(struct test_sorcery_object *, copy, NULL, ao2_cleanup);
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "object_copy_native";
+		info->category = "/main/sorcery/";
+		info->summary = "sorcery object native copy unit test";
+		info->description =
+			"Test object native copy in sorcery";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (!(sorcery = alloc_and_initialize_sorcery())) {
+		ast_test_status_update(test, "Failed to open sorcery structure\n");
+		return AST_TEST_FAIL;
+	}
+
+	ast_sorcery_object_set_copy_handler(sorcery, "test", test_sorcery_copy);
+
+	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+		ast_test_status_update(test, "Failed to allocate a known object type\n");
+		return AST_TEST_FAIL;
+	}
+
+	obj->bob = 50;
+	obj->joe = 100;
+
+	if (!(copy = ast_sorcery_copy(sorcery, obj))) {
+		ast_test_status_update(test, "Failed to create a copy of a known valid object\n");
+		res = AST_TEST_FAIL;
+	} else if (copy == obj) {
+		ast_test_status_update(test, "Created copy is actually the original object\n");
+		res = AST_TEST_FAIL;
+	} else if (copy->bob != 10) {
+		ast_test_status_update(test, "Value of 'bob' on newly created copy is not the predefined native copy value\n");
+		res = AST_TEST_FAIL;
+	} else if (copy->joe != 20) {
+		ast_test_status_update(test, "Value of 'joe' on newly created copy is not the predefined native copy value\n");
+		res = AST_TEST_FAIL;
+	}
+
+	return res;
+}
+
 AST_TEST_DEFINE(object_diff)
 {
        RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
@@ -596,6 +668,72 @@
        for (field = changes; field; field = field->next) {
 	       if (!strcmp(field->name, "joe")) {
 		       if (strcmp(field->value, "42")) {
+			       ast_test_status_update(test, "Object diff produced unexpected value '%s' for joe\n", field->value);
+			       res = AST_TEST_FAIL;
+		       }
+	       } else {
+		       ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", field->name);
+		       res = AST_TEST_FAIL;
+	       }
+       }
+
+       return res;
+}
+
+AST_TEST_DEFINE(object_diff_native)
+{
+       RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+       RAII_VAR(struct test_sorcery_object *, obj1, NULL, ao2_cleanup);
+       RAII_VAR(struct test_sorcery_object *, obj2, NULL, ao2_cleanup);
+       RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
+       struct ast_variable *field;
+       int res = AST_TEST_PASS;
+
+       switch (cmd) {
+       case TEST_INIT:
+	       info->name = "object_diff_native";
+	       info->category = "/main/sorcery/";
+	       info->summary = "sorcery object native diff unit test";
+	       info->description =
+		       "Test native object diffing in sorcery";
+	       return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+	       break;
+       }
+
+       if (!(sorcery = alloc_and_initialize_sorcery())) {
+	       ast_test_status_update(test, "Failed to open sorcery structure\n");
+	       return AST_TEST_FAIL;
+       }
+
+       ast_sorcery_object_set_diff_handler(sorcery, "test", test_sorcery_diff);
+
+       if (!(obj1 = ast_sorcery_alloc(sorcery, "test", "blah"))) {
+	       ast_test_status_update(test, "Failed to allocate a known object type\n");
+	       return AST_TEST_FAIL;
+       }
+
+       obj1->bob = 99;
+       obj1->joe = 55;
+
+       if (!(obj2 = ast_sorcery_alloc(sorcery, "test", "blah2"))) {
+	       ast_test_status_update(test, "Failed to allocate a second known object type\n");
+	       return AST_TEST_FAIL;
+       }
+
+       obj2->bob = 99;
+       obj2->joe = 42;
+
+       if (ast_sorcery_diff(sorcery, obj1, obj2, &changes)) {
+	       ast_test_status_update(test, "Failed to diff obj1 and obj2\n");
+       } else if (!changes) {
+	       ast_test_status_update(test, "Failed to produce a diff of two objects, despite there being differences\n");
+	       return AST_TEST_FAIL;
+       }
+
+       for (field = changes; field; field = field->next) {
+	       if (!strcmp(field->name, "yes")) {
+		       if (strcmp(field->value, "itworks")) {
 			       ast_test_status_update(test, "Object diff produced unexpected value '%s' for joe\n", field->value);
 			       res = AST_TEST_FAIL;
 		       }
@@ -1946,7 +2084,9 @@
 	AST_TEST_UNREGISTER(object_alloc_with_id);
 	AST_TEST_UNREGISTER(object_alloc_without_id);
 	AST_TEST_UNREGISTER(object_copy);
+	AST_TEST_UNREGISTER(object_copy_native);
 	AST_TEST_UNREGISTER(object_diff);
+	AST_TEST_UNREGISTER(object_diff_native);
 	AST_TEST_UNREGISTER(objectset_create);
 	AST_TEST_UNREGISTER(objectset_apply);
 	AST_TEST_UNREGISTER(objectset_apply_handler);
@@ -1985,7 +2125,9 @@
 	AST_TEST_REGISTER(object_alloc_with_id);
 	AST_TEST_REGISTER(object_alloc_without_id);
 	AST_TEST_REGISTER(object_copy);
+	AST_TEST_REGISTER(object_copy_native);
 	AST_TEST_REGISTER(object_diff);
+	AST_TEST_REGISTER(object_diff_native);
 	AST_TEST_REGISTER(objectset_create);
 	AST_TEST_REGISTER(objectset_apply);
 	AST_TEST_REGISTER(objectset_apply_handler);




More information about the asterisk-commits mailing list