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/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@381134 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
Joshua Colp 13 years ago
parent 0e442112ad
commit 27882b8599

@ -151,6 +151,29 @@ typedef struct ast_variable *(*sorcery_transform_handler)(struct ast_variable *s
*/ */
typedef void (*sorcery_apply_handler)(const struct ast_sorcery *sorcery, void *obj); 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 */ /*! \brief Interface for a sorcery wizard */
struct ast_sorcery_wizard { struct ast_sorcery_wizard {
/*! \brief Name of the wizard */ /*! \brief Name of the wizard */
@ -289,6 +312,24 @@ int ast_sorcery_apply_default(struct ast_sorcery *sorcery, const char *type, con
*/ */
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); 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 * \brief Register a field within an object
* *
@ -345,6 +386,14 @@ int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char
*/ */
void ast_sorcery_load(const struct ast_sorcery *sorcery); 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 * \brief Inform any wizards to reload persistent objects
* *
@ -352,6 +401,14 @@ void ast_sorcery_load(const struct ast_sorcery *sorcery);
*/ */
void ast_sorcery_reload(const struct ast_sorcery *sorcery); 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 * \brief Increase the reference count of a sorcery structure
* *

@ -63,6 +63,12 @@ struct ast_sorcery_object_type {
/*! \brief Optional object set apply callback */ /*! \brief Optional object set apply callback */
sorcery_apply_handler apply; 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 */ /*! \brief Wizard instances */
struct ao2_container *wizards; struct ao2_container *wizards;
@ -299,7 +305,7 @@ struct ast_sorcery *ast_sorcery_open(void)
return NULL; 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); ao2_ref(sorcery, -1);
sorcery = NULL; sorcery = NULL;
} }
@ -467,7 +473,7 @@ int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, a
{ {
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); 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; return -1;
} }
@ -487,6 +493,28 @@ int ast_sorcery_object_register(struct ast_sorcery *sorcery, const char *type, a
return 0; 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, 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,
aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...) aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...)
{ {
@ -573,6 +601,21 @@ void ast_sorcery_load(const struct ast_sorcery *sorcery)
ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details); 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) void ast_sorcery_reload(const struct ast_sorcery *sorcery)
{ {
struct sorcery_load_details details = { struct sorcery_load_details details = {
@ -583,6 +626,21 @@ void ast_sorcery_reload(const struct ast_sorcery *sorcery)
ao2_callback(sorcery->types, OBJ_NODATA, sorcery_object_load, &details); 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) void ast_sorcery_ref(struct ast_sorcery *sorcery)
{ {
ao2_ref(sorcery, +1); ao2_ref(sorcery, +1);
@ -753,14 +811,25 @@ void *ast_sorcery_alloc(const struct ast_sorcery *sorcery, const char *type, con
void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object) void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
{ {
const struct ast_sorcery_object_details *details = 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); struct ast_sorcery_object_details *copy = ast_sorcery_alloc(sorcery, details->type, details->id);
RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy); RAII_VAR(struct ast_variable *, objectset, NULL, ast_variables_destroy);
int res = 0;
if (!copy || if (!copy) {
!(objectset = ast_sorcery_objectset_create(sorcery, object)) ||
ast_sorcery_objectset_apply(sorcery, copy, objectset)) {
ao2_cleanup(copy);
return NULL; 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);
copy = NULL;
} }
return copy; return copy;
@ -768,8 +837,7 @@ void *ast_sorcery_copy(const struct ast_sorcery *sorcery, const void *object)
int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, const void *modified, struct ast_variable **changes) 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_sorcery_object_type *, object_type, ao2_find(sorcery->types, ast_sorcery_object_get_type(original), OBJ_KEY), ao2_cleanup);
RAII_VAR(struct ast_variable *, objectset2, NULL, ast_variables_destroy);
*changes = NULL; *changes = NULL;
@ -777,10 +845,19 @@ int ast_sorcery_diff(const struct ast_sorcery *sorcery, const void *original, co
return -1; return -1;
} }
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); objectset1 = ast_sorcery_objectset_create(sorcery, original);
objectset2 = ast_sorcery_objectset_create(sorcery, modified); objectset2 = ast_sorcery_objectset_create(sorcery, modified);
return ast_sorcery_changeset_create(objectset1, objectset2, changes); 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 */ /*! \brief Internal function used to create an object in caching wizards */

@ -77,6 +77,22 @@ static struct ast_variable *test_sorcery_transform(struct ast_variable *set)
return transformed; 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 */ /*! \brief Test structure for caching */
struct sorcery_test_caching { struct sorcery_test_caching {
/*! \brief Whether the object has been created in the cache or not */ /*! \brief Whether the object has been created in the cache or not */
@ -333,6 +349,11 @@ AST_TEST_DEFINE(object_register)
return AST_TEST_FAIL; 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;
}
return AST_TEST_PASS; return AST_TEST_PASS;
} }
@ -544,6 +565,57 @@ AST_TEST_DEFINE(object_copy)
return res; 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) AST_TEST_DEFINE(object_diff)
{ {
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
@ -608,6 +680,72 @@ AST_TEST_DEFINE(object_diff)
return res; 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;
}
} else {
ast_test_status_update(test, "Object diff produced unexpected field '%s'\n", field->name);
res = AST_TEST_FAIL;
}
}
return res;
}
AST_TEST_DEFINE(objectset_create) AST_TEST_DEFINE(objectset_create)
{ {
int res = AST_TEST_PASS; int res = AST_TEST_PASS;
@ -1946,7 +2084,9 @@ static int unload_module(void)
AST_TEST_UNREGISTER(object_alloc_with_id); AST_TEST_UNREGISTER(object_alloc_with_id);
AST_TEST_UNREGISTER(object_alloc_without_id); AST_TEST_UNREGISTER(object_alloc_without_id);
AST_TEST_UNREGISTER(object_copy); AST_TEST_UNREGISTER(object_copy);
AST_TEST_UNREGISTER(object_copy_native);
AST_TEST_UNREGISTER(object_diff); AST_TEST_UNREGISTER(object_diff);
AST_TEST_UNREGISTER(object_diff_native);
AST_TEST_UNREGISTER(objectset_create); AST_TEST_UNREGISTER(objectset_create);
AST_TEST_UNREGISTER(objectset_apply); AST_TEST_UNREGISTER(objectset_apply);
AST_TEST_UNREGISTER(objectset_apply_handler); AST_TEST_UNREGISTER(objectset_apply_handler);
@ -1985,7 +2125,9 @@ static int load_module(void)
AST_TEST_REGISTER(object_alloc_with_id); AST_TEST_REGISTER(object_alloc_with_id);
AST_TEST_REGISTER(object_alloc_without_id); AST_TEST_REGISTER(object_alloc_without_id);
AST_TEST_REGISTER(object_copy); AST_TEST_REGISTER(object_copy);
AST_TEST_REGISTER(object_copy_native);
AST_TEST_REGISTER(object_diff); AST_TEST_REGISTER(object_diff);
AST_TEST_REGISTER(object_diff_native);
AST_TEST_REGISTER(objectset_create); AST_TEST_REGISTER(objectset_create);
AST_TEST_REGISTER(objectset_apply); AST_TEST_REGISTER(objectset_apply);
AST_TEST_REGISTER(objectset_apply_handler); AST_TEST_REGISTER(objectset_apply_handler);

Loading…
Cancel
Save