[asterisk-commits] dlee: branch dlee/stasis-http r380648 - in /team/dlee/stasis-http: include/as...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Jan 31 11:05:44 CST 2013


Author: dlee
Date: Thu Jan 31 11:05:40 2013
New Revision: 380648

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=380648
Log:
Application registration

Added:
    team/dlee/stasis-http/tests/test_stasis_core.c   (with props)
Modified:
    team/dlee/stasis-http/include/asterisk/stasis.h
    team/dlee/stasis-http/res/res_stasis_core.c

Modified: team/dlee/stasis-http/include/asterisk/stasis.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/include/asterisk/stasis.h?view=diff&rev=380648&r1=380647&r2=380648
==============================================================================
--- team/dlee/stasis-http/include/asterisk/stasis.h (original)
+++ team/dlee/stasis-http/include/asterisk/stasis.h Thu Jan 31 11:05:40 2013
@@ -34,12 +34,38 @@
  * \param chan Channel to invoke application upon
  * \param argc Number of arguments to pass into the application
  * \param argv Argument array.
- * \return 0 for success; -1 for error.
+ * \return 0 for success.
+ * \return -1 for error.
  */
 int stasis_app_invoke(const char *app_name, struct ast_channel *chan, int argc, char *argv[]);
 
+/*!
+ * \brief Callback for Stasis application handler.
+ *
+ * The message given to the handler is a borrowed copy. If you want to keep a
+ * reference to it, you should use \c ast_ref() to keep it around.
+ *
+ * \param data Data ptr given when registered
+ * \param message Message to handle. (borrowed copy)
+ */
 typedef void (*stasis_app_handler)(void *data, struct ast_json *message);
 
+/*!
+ * \brief Register a new Stasis application.
+ * If an application is already registered with the given name, the old
+ * application is sent a 'replaced' message and unregistered.
+ * \param app_name Name of this application.
+ * \param handler Callback for application messages.
+ * \param data Data blob to pass to the callback.
+ * \return 0 for success
+ * \return -1 for error.
+ */
 int stasis_app_register(const char *app_name, stasis_app_handler handler, void *data);
 
+/*!
+ * \brief Unregister a Stasis application.
+ * \param app_name Name of the application to unregister.
+ */
+void stasis_app_unregister(const char *app_name);
+
 #endif /* _ASTERISK_STASIS_H */

Modified: team/dlee/stasis-http/res/res_stasis_core.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/res_stasis_core.c?view=diff&rev=380648&r1=380647&r2=380648
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_core.c (original)
+++ team/dlee/stasis-http/res/res_stasis_core.c Thu Jan 31 11:05:40 2013
@@ -44,13 +44,20 @@
 
 /*! Stasis application ref */
 struct stasis_app {
-	const char *name;
+	char *name;
 	stasis_app_handler handler;
 	void *data;
 };
 
 /*! Stasis application container */
-struct ao2_container *stasis_apps;
+struct ao2_container *__stasis_apps;
+
+static struct ao2_container *stasis_apps(void)
+{
+	ao2_ref(__stasis_apps, 1);
+	return __stasis_apps;
+}
+
 
 /*! Hash function for stasis_app */
 static int hash_app(const void *obj, const int flags)
@@ -75,30 +82,33 @@
 	}
 }
 
-static void app_dtor(void *app)
+static void app_dtor(void *obj)
 {
-	/* No-op */
+	struct stasis_app *app = obj;
+	ast_free(app->name);
 }
 
 int stasis_app_invoke(const char *app_name, struct ast_channel *chan, int argc, char *argv[])
 {
-	RAII_VAR(struct ao2_container *, apps, stasis_apps, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, apps, stasis_apps(), ao2_cleanup);
 	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 	struct ast_json *json_args;
 	int i;
 
-	ao2_ref(apps, 1);
 	app = ao2_find(apps, app_name, OBJ_KEY);
 
 	if (!app) {
+		/* XXX We can do a better job handling late binding, queueing up the call for a few seconds
+		 * to wait for the app to register.
+		 */
 		ast_log(LOG_WARNING, "Stasis app '%s' not registered\n", app_name);
 		return -1;
 	}
 
-	msg = ast_json_pack("{s: s, s: s, s: []}",
+	msg = ast_json_pack("{s: s, s: o, s: []}",
 			    "command", "invoke",
-			    "channel", ast_channel_name(chan),
+			    "channel", chan ? ast_json_string_create(ast_channel_name(chan)) : ast_json_null(),
 			    "args");
 	if (!msg) {
 		ast_log(LOG_ERROR, "Couldn't create message for %s\n", app_name);
@@ -117,36 +127,46 @@
 
 int stasis_app_register(const char *app_name, stasis_app_handler handler, void *data)
 {
-	RAII_VAR(struct ao2_container *, apps, stasis_apps, ao2_cleanup);
+	RAII_VAR(struct ao2_container *, apps, stasis_apps(), ao2_cleanup);
+	RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
 
-	ao2_ref(apps, 1);
-	{
-		RAII_VAR(struct stasis_app *, app, ao2_find(apps, app_name, OBJ_KEY | OBJ_NOLOCK), ao2_cleanup);
-		SCOPED_LOCK(apps_lock, apps, ao2_lock, ao2_unlock);
+	SCOPED_LOCK(apps_lock, apps, ao2_lock, ao2_unlock);
 
+	app = ao2_find(apps, app_name, OBJ_KEY | OBJ_NOLOCK);
+
+	if (app) {
+		RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
+		msg = ast_json_pack("{s: s}", "event", "replaced");
+		app->handler(app->data, msg);
+	} else {
+		app = ao2_alloc(sizeof(*app), app_dtor);
+		app->name = ast_strdup(app_name);
 		if (app) {
-			RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
-			msg = ast_json_pack("{s: s}", "event", "replaced");
-			app->handler(app->data, msg);
+			ao2_link(apps, app);
 		} else {
-			app = ao2_alloc(sizeof(*app), app_dtor);
-		}
-
-		if (!app) {
 			ast_log(LOG_ERROR, "Failed to allocate stasis_app\n");
 			return -1;
 		}
+	}
 
-		app->handler = handler;
-		app->data = data;
-	}
+	app->handler = handler;
+	app->data = data;
+
 	return 0;
 }
 
+void stasis_app_unregister(const char *app_name)
+{
+	RAII_VAR(struct ao2_container *, apps, stasis_apps(), ao2_cleanup);
+
+	ao2_cleanup(ao2_find(apps, app_name, OBJ_KEY | OBJ_UNLINK));
+}
+
+
 static int load_module(void)
 {
-	stasis_apps = ao2_container_alloc(APPS_NUM_BUCKETS, hash_app, compare_app);
-	if (stasis_apps == NULL) {
+	__stasis_apps = ao2_container_alloc(APPS_NUM_BUCKETS, hash_app, compare_app);
+	if (__stasis_apps == NULL) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
 	return AST_MODULE_LOAD_SUCCESS;
@@ -154,8 +174,8 @@
 
 static int unload_module(void)
 {
-	ao2_ref(stasis_apps, -1);
-	stasis_apps = 0;
+	ao2_ref(__stasis_apps, -1);
+	__stasis_apps = NULL;
 	return 0;
 }
 

Added: team/dlee/stasis-http/tests/test_stasis_core.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/tests/test_stasis_core.c?view=auto&rev=380648
==============================================================================
--- team/dlee/stasis-http/tests/test_stasis_core.c (added)
+++ team/dlee/stasis-http/tests/test_stasis_core.c Thu Jan 31 11:05:40 2013
@@ -1,0 +1,144 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file \brief Test Stasis Core API.
+ * \author\verbatim David M. Lee, II <dlee at digium.com> \endverbatim
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<depend>res_stasis_core</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/stasis.h"
+
+AST_TEST_DEFINE(app_invoke_dne)
+{
+	int res;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/core/";
+		info->summary = "Test stasis app invocation.";
+		info->description = "Test stasis app invocation.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	res = stasis_app_invoke("i-am-not-an-app", NULL, 0, NULL);
+	ast_test_validate(test, -1 == res);
+
+	return AST_TEST_PASS;
+}
+
+struct app_data {
+	int invocations;
+	struct ast_json *messages;
+};
+
+static struct app_data *app_data_create(void)
+{
+	struct app_data *res = ast_calloc(1, sizeof(struct app_data));
+	res->messages = ast_json_array_create();
+	return res;
+}
+
+static void app_data_dtor(struct app_data *actual)
+{
+	if (actual) {
+		ast_json_unref(actual->messages);
+		actual->messages = NULL;
+		ast_free(actual);
+	}
+}
+
+static void test_handler(void *data, struct ast_json *message)
+{
+	struct app_data *actual = data;
+	int res;
+	++(actual->invocations);
+	res = ast_json_array_append(actual->messages, ast_json_copy(message));
+	ast_assert(res == 0);
+}
+
+AST_TEST_DEFINE(app_invoke_one)
+{
+	RAII_VAR(struct app_data *, app_data, NULL, app_data_dtor);
+	RAII_VAR(char *, app_name, "test-handler", stasis_app_unregister);
+	RAII_VAR(struct ast_json *, expected_message, NULL, ast_json_unref);
+	int res;
+
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/core/";
+		info->summary = "Test stasis app invocation.";
+		info->description = "Test stasis app invocation.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	app_data = app_data_create();
+
+	stasis_app_register(app_name, test_handler, app_data);
+	expected_message = ast_json_pack("[{s: s, s: o, s: []}]",
+					 "command", "invoke",
+					 "channel", ast_json_null(),
+					 "args");
+
+	res = stasis_app_invoke(app_name, NULL, 0, NULL);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, 1 == app_data->invocations);
+	ast_test_validate(test, ast_json_equal(expected_message, app_data->messages));
+
+	return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(app_invoke_dne);
+	AST_TEST_UNREGISTER(app_invoke_one);
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(app_invoke_one);
+	AST_TEST_REGISTER(app_invoke_dne);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Stasis Core testing",
+		.load = load_module,
+		.unload = unload_module,
+		.nonoptreq = "res_stasis_core"
+	);

Propchange: team/dlee/stasis-http/tests/test_stasis_core.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/stasis-http/tests/test_stasis_core.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Rev URL

Propchange: team/dlee/stasis-http/tests/test_stasis_core.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the asterisk-commits mailing list