diff --git a/c/Makefile b/c/Makefile index bd98f996..4ab3e7d7 100644 --- a/c/Makefile +++ b/c/Makefile @@ -1,174 +1,258 @@ -# The following two variables must be set. -# -# Directory containing the C client include files. -# DIFFUSION_C_CLIENT_INCDIR = -# -# Directory containing libdiffusion.a -# DIFFUSION_C_CLIENT_LIBDIR = - -ifndef DIFFUSION_C_CLIENT_INCDIR -$(error DIFFUSION_C_CLIENT_INCDIR is not set) -endif - -ifndef DIFFUSION_C_CLIENT_LIBDIR -$(error DIFFUSION_C_CLIENT_LIBDIR is not set) -endif - -CC = gcc - -# Extra definitions from parent directory, if they exist. --include ../makefile.defs - -CFLAGS += -g -Wall -Werror -Wno-error=deprecated-declarations -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=700 -I$(DIFFUSION_C_CLIENT_INCDIR) -LDFLAGS += $(DIFFUSION_C_CLIENT_LIBDIR)/libdiffusion.a -lpthread -lpcre -lz $(LIBS) -# If you have openssl installed then you can uncomment these. -ifdef HAVE_OPEN_SSL -LDFLAGS += -lssl -lcrypto -endif - -ARFLAGS += -SOURCES = subscribe.c fetch-request.c \ - auth-service.c \ - change-principal.c add-topics.c \ - subscribe-multiple.c update-record.c connect.c \ - connect-async.c system-auth-control.c \ - session-properties-listener.c \ - get-session-properties.c \ - missing-topic-notification.c \ - subscription-control.c reconnect.c \ - json/json-publishing-example.c \ - json/json-subscribing-example.c \ - recordv2-topics.c binary-topics.c \ - int64-topics.c double-topics.c string-topics.c \ - send-request-to-session.c send-request-to-path.c \ - send-request-to-filter.c topic-update.c \ - topic-update-with-constraint.c topic-update-stream.c \ - topic-update-add-and-set.c time-series-append.c \ - topic-views.c \ - -TARGETDIR = target -OBJDIR = $(TARGETDIR)/objs -BINDIR = $(TARGETDIR)/bin -OBJECTS = $(SOURCES:.c=.o) -TARGETS = subscribe fetch-request \ - auth-service change-principal add-topics \ - subscribe-multiple update-record \ - connect connect-async system-auth-control \ - session-properties-listener \ - get-session-properties \ - missing-topic-notification \ - subscription-control reconnect \ - json-publishing-example json-subscribing-example \ - recordv2-topics binary-topics \ - int64-topics double-topics string-topics \ - send-request-to-session send-request-to-path \ - send-request-to-filter topic-update \ - topic-update-with-constraint topic-update-stream \ - topic-update-add-and-set time-series-append \ - topic-views \ - -all: prepare $(TARGETS) -.PHONY: all - -prepare: - mkdir -p $(OBJDIR) $(BINDIR) - -$(OBJDIR)/%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< - -subscribe: $(OBJDIR)/subscribe.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -fetch-request: $(OBJDIR)/fetch-request.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -send-request-to-session: $(OBJDIR)/send-request-to-session.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -send-request-to-path: $(OBJDIR)/send-request-to-path.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -send-request-to-filter: $(OBJDIR)/send-request-to-filter.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -auth-service: $(OBJDIR)/auth-service.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -change-principal: $(OBJDIR)/change-principal.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -add-topics: $(OBJDIR)/add-topics.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -update-record: $(OBJDIR)/update-record.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -subscribe-multiple: $(OBJDIR)/subscribe-multiple.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -connect: $(OBJDIR)/connect.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -connect-async: $(OBJDIR)/connect-async.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -system-auth-control: $(OBJDIR)/system-auth-control.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -session-properties-listener: $(OBJDIR)/session-properties-listener.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -get-session-properties: $(OBJDIR)/get-session-properties.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -missing-topic-notification: $(OBJDIR)/missing-topic-notification.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -subscription-control: $(OBJDIR)/subscription-control.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -reconnect: $(OBJDIR)/reconnect.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -json-publishing-example: json/json-publishing-example.c - $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BINDIR)/$@ - -json-subscribing-example: json/json-subscribing-example.c - $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BINDIR)/$@ - -recordv2-topics: $(OBJDIR)/recordv2-topics.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -binary-topics: $(OBJDIR)/binary-topics.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -int64-topics: $(OBJDIR)/int64-topics.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -double-topics: $(OBJDIR)/double-topics.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -string-topics: $(OBJDIR)/string-topics.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -topic-update: $(OBJDIR)/topic-update.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -topic-update-with-constraint: $(OBJDIR)/topic-update-with-constraint.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -topic-update-stream: $(OBJDIR)/topic-update-stream.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -topic-update-add-and-set: $(OBJDIR)/topic-update-add-and-set.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -time-series-append: $(OBJDIR)/time-series-append.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - -topic-views: $(OBJDIR)/topic-views.o - $(CC) $< $(LDFLAGS) -o $(BINDIR)/$@ - - -clean: - rm -rf $(TARGETS) $(OBJECTS) $(TARGETDIR) core a.out +# The following two variables must be set. +# +# Directory containing the C client include files. +# DIFFUSION_C_CLIENT_INCDIR = +# +# Directory containing libdiffusion.a +# DIFFUSION_C_CLIENT_LIBDIR = +# +# Directory containing the various external Diffusion dependencies +# DIFFUSION_C_CLIENT_DEPENDENCIES_DIR = +# +# Directory that will contain the executables and generated objects +# TARGET_FOLDER = + +ifndef DIFFUSION_C_CLIENT_INCDIR +$(error DIFFUSION_C_CLIENT_INCDIR is not set) +endif + +ifndef DIFFUSION_C_CLIENT_LIBDIR +$(error DIFFUSION_C_CLIENT_LIBDIR is not set) +endif + +ifndef TARGET_FOLDER +$(error TARGET_FOLDER is not set) +endif + +# Extra definitions from current directory, if they exist. +-include makefile.defs + +CFLAGS += $(INCLUDES) \ + -g -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=700 \ + -Wall -Werror -Wno-error=deprecated-declarations \ + -I$(DIFFUSION_C_CLIENT_INCDIR) + +LDFLAGS += -lpthread -lpcre -lcurl -lz \ + $(DIFFUSION_C_CLIENT_LIBDIR)/$(ARCH)/libdiffusion.a \ + $(LIBS) + +# Detect the platform the Diffusion Client will be running on +PLATFORM = $(shell uname -s | tr '[A-Z]' '[a-z]' | sed -e 's/darwin/osx/') + +ifeq ($(PLATFORM),osx) + CC = clang +else + # If not MacOS, add '-lrt' to the linker flags + CC = gcc + LDFLAGS += -lrt +endif + +ARFLAGS += +SOURCES = connect-async.c \ + connect.c \ + reconnect.c \ + session-factory.c \ + features/authentication_control/auth-service.c \ + features/client-control-change-roles-with-filter.c \ + features/client-control-change-roles-with-session.c \ + features/client-control-close-with-filter.c \ + features/client_control/get-session-properties.c \ + features/client_control/session-properties-listener.c \ + features/messaging/send-request-to-filter.c \ + features/messaging/send-request-to-path.c \ + features/messaging/send-request-to-session.c \ + features/remote_servers/remote-servers.c \ + features/security/change-principal.c \ + features/subscription_control/subscription-control.c \ + features/system_authentication_control/system-auth-control.c \ + features/time_series/time-series-range-query.c \ + features/time_series/time-series-append.c \ + features/time_series/time-series-edit.c \ + features/topic_control/missing-topic-notification.c \ + features/topic_control/add-topics.c \ + features/topic_update/update-record.c \ + features/topic_update/topic-update.c \ + features/topic_update/topic-update-stream.c \ + features/topic_update/topic-update-with-constraint.c \ + features/topic_update/topic-update-add-and-set.c \ + features/topic_views/topic-views.c \ + features/topic_views/topic-views-remove.c \ + features/topic_views/topic-views-list.c \ + features/topics/subscribe.c \ + features/topics/subscribe-multiple.c \ + features/topics/recordv2-topics.c \ + features/topics/string-topics.c \ + features/topics/double-topics.c \ + features/topics/fetch-request.c \ + features/topics/int64-topics.c \ + features/topics/binary-topics.c + +OBJ_DIR = $(TARGET_FOLDER)/objs/$(ARCH) +BIN_DIR = $(TARGET_FOLDER)/bin/$(ARCH) +OBJECTS = $(SOURCES:.c=.o) + +TARGETS = connect-async \ + connect \ + reconnect \ + session-factory \ + authentication_control \ + client-control-change-roles-with-filter \ + client-control-change-roles-with-session \ + client-control-close-with-filter \ + client-control-get-session-properties \ + client-control-session-properties-listener \ + messaging-send-request-to-filter \ + messaging-send-request-to-path \ + messaging-send-request-to-session \ + remote-servers \ + security-change-principal \ + subscription-control \ + system-authentication-control \ + time-series-range-query \ + time-series-append \ + time-series-edit \ + topic-control-missing-topic-notification \ + topic-control-add-topics \ + topic-update-record \ + topic-update \ + topic-update-stream \ + topic-update-with-constraint \ + topic-update-add-and-set \ + topic-views \ + topic-views-remove \ + topic-views-list \ + topics-subscribe \ + topics-subscribe-multiple \ + topics-recordv2 \ + topics-string \ + topics-double \ + topics-fetch \ + topics-int64 \ + topics-binary + +.PHONY: all + +all: prepare $(TARGETS) + +prepare: + mkdir -p $(OBJ_DIR) $(BIN_DIR) + +$(OBJ_DIR)/%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +connect-async: $(OBJ_DIR)/connect-async.o + $(CC) $< $(LDFLAGS) -o $(BIN_DIR)/$@ + +connect: $(OBJ_DIR)/connect.o + $(CC) $< $(LDFLAGS) -o $(BIN_DIR)/$@ + +reconnect: $(OBJ_DIR)/reconnect.o + $(CC) $< $(LDFLAGS) -o $(BIN_DIR)/$@ + +session-factory: $(OBJ_DIR)/session-factory.o + $(CC) $< $(LDFLAGS) -o $(BIN_DIR)/$@ + +authentication_control: features/authentication_control/auth-service.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +client-control-change-roles-with-filter: features/client_control/change-roles-with-filter.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +client-control-change-roles-with-session: features/client_control/change-roles-with-session.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +client-control-close-with-filter: features/client_control/close-with-filter.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +client-control-get-session-properties: features/client_control/get-session-properties.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +client-control-session-properties-listener: features/client_control/session-properties-listener.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +messaging-send-request-to-filter: features/messaging/send-request-to-filter.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +messaging-send-request-to-path: features/messaging/send-request-to-path.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +messaging-send-request-to-session: features/messaging/send-request-to-session.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +remote-servers: features/remote_servers/remote-servers.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +security-change-principal: features/security/change-principal.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +subscription-control: features/subscription_control/subscription-control.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +system-authentication-control: features/system_authentication_control/system-auth-control.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +time-series-range-query: features/time_series/time-series-range-query.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +time-series-append: features/time_series/time-series-append.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +time-series-edit: features/time_series/time-series-edit.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-control-missing-topic-notification: features/topic_control/missing-topic-notification.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-control-add-topics: features/topic_control/add-topics.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-update-record: features/topic_update/update-record.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-update: features/topic_update/topic-update.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-update-stream: features/topic_update/topic-update-stream.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-update-with-constraint: features/topic_update/topic-update-with-constraint.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-update-add-and-set: features/topic_update/topic-update-add-and-set.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-views: features/topic_views/topic-views.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-views-remove: features/topic_views/topic-views-remove.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topic-views-list: features/topic_views/topic-views-list.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-subscribe: features/topics/subscribe.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-subscribe-multiple: features/topics/subscribe-multiple.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-recordv2: features/topics/recordv2-topics.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-string: features/topics/string-topics.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-double: features/topics/double-topics.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-fetch: features/topics/fetch-request.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-int64: features/topics/int64-topics.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +topics-binary: features/topics/binary-topics.c + $(CC) $^ $(CFLAGS) $(LDFLAGS) -lm -o $(BIN_DIR)/$@ + +clean: + rm -rf $(TARGETS) $(OBJECTS) $(TARGET_FOLDER) core a.out diff --git a/c/connect-async.c b/c/connect-async.c index 11f50b32..0fb1d33f 100644 --- a/c/connect-async.c +++ b/c/connect-async.c @@ -1,196 +1,157 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This examples shows how to make an asynchronous connection to Diffusion. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" - -/* - * Used to synchronise connection callbacks with the main flow of control, - * so we can close the program in a timely fashion. - */ -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -SESSION_T *g_session = NULL; - -/* - * This callback is used when the session state changes, e.g. when a session - * moves from a "connecting" to a "connected" state, or from "connected" to - * "closed". - */ -static void -on_session_state_changed(SESSION_T *session, - const SESSION_STATE_T old_state, - const SESSION_STATE_T new_state) -{ - printf("Session state changed from %s (%d) to %s (%d)\n", - session_state_as_string(old_state), old_state, - session_state_as_string(new_state), new_state); -} - -/* - * This is the callback that is invoked if the client can successfully - * connect to Diffusion, and a session instance is ready for use. - */ -static int -on_connected(SESSION_T *session) -{ - char *sid = session_id_to_string(session->id); - printf("on_connected(), state=%d, session id=%s\n", - session_state_get(session), - sid); - free(sid); - - apr_thread_mutex_lock(mutex); - - g_session = session; - - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -/* - * This is the callback that is invoked if there is an error connection - * to the Diffusion instance. - */ -static int -on_error(SESSION_T *session, DIFFUSION_ERROR_T *error) -{ - g_session = session; - - char *sid = session_id_to_string(session->id); - printf("on_error(), session_id=%s, error=%s\n", - sid, - error->message); - free(sid); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -/* - * Entry point for the example. - */ -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup synchronisation variables. - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - DIFFUSION_ERROR_T error = { 0 }; - - SESSION_LISTENER_T session_listener = { 0 }; - session_listener.on_state_changed = &on_session_state_changed; - - /* - * Asynchronous connections have callbacks for notifying that - * a connection has been made, or that an error occurred. - */ - SESSION_CREATE_CALLBACK_T *callbacks = calloc(1, sizeof(SESSION_CREATE_CALLBACK_T)); - callbacks->on_connected = &on_connected; - callbacks->on_error = &on_error; - - RECONNECTION_STRATEGY_T reconnection_strategy = { - .retry_count = 3, - .retry_delay = 1000 - }; - - /* - * Although we're connecting asynchronously, we are using a - * mutex and a condition variable to signal when a - * session_create_async callback has been invoked, and we can - * then close & free the session. - */ - apr_thread_mutex_lock(mutex); - session_create_async(url, principal, credentials, &session_listener, &reconnection_strategy, callbacks, &error); - - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - // keep it open for 5 seconds - sleep( 5 ); - - /* - * Close/free session (if we have one) and release resources - * and memory. - */ - if(g_session != NULL) { - session_close(g_session, NULL); - session_free(g_session); - } - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - credentials_free(credentials); - hash_free(options, NULL, free); - free(callbacks); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This examples shows how to make an asynchronous connection to Diffusion. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + +SESSION_T *g_session = NULL; + +/* + * This callback is used when the session state changes, e.g. when a session + * moves from a "connecting" to a "connected" state, or from "connected" to + * "closed". + */ +static void on_session_state_changed( + SESSION_T *session, + const SESSION_STATE_T old_state, + const SESSION_STATE_T new_state) +{ + printf("Session state changed from %s (%d) to %s (%d)\n", + session_state_as_string(old_state), old_state, + session_state_as_string(new_state), new_state); +} + +/* + * This is the callback that is invoked if the client can successfully + * connect to Diffusion, and a session instance is ready for use. + */ +static int on_connected(SESSION_T *session) +{ + char *sid = session_id_to_string(session->id); + printf("on_connected(), state=%d, session id=%s\n", + session_state_get(session), + sid); + free(sid); + + g_session = session; + return HANDLER_SUCCESS; +} + +/* + * This is the callback that is invoked if there is an error connection + * to the Diffusion instance. + */ +static int on_error(SESSION_T *session, DIFFUSION_ERROR_T *error) +{ + g_session = session; + + char *sid = session_id_to_string(session->id); + printf("on_error(), session_id=%s, error=%s\n", + sid, + error->message); + free(sid); + return HANDLER_SUCCESS; +} + +/* + * Entry point for the example. + */ +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + DIFFUSION_ERROR_T error = { 0 }; + SESSION_LISTENER_T session_listener = { 0 }; + session_listener.on_state_changed = &on_session_state_changed; + + /* + * Asynchronous connections have callbacks for notifying that + * a connection has been made, or that an error occurred. + */ + SESSION_CREATE_CALLBACK_T *callbacks = calloc(1, sizeof(SESSION_CREATE_CALLBACK_T)); + callbacks->on_connected = &on_connected; + callbacks->on_error = &on_error; + + RECONNECTION_STRATEGY_T reconnection_strategy = { + .retry_count = 3, + .retry_delay = 1000 + }; + + /* + * Although we're connecting asynchronously, we are using a + * mutex and a condition variable to signal when a + * session_create_async callback has been invoked, and we can + * then close & free the session. + */ + session_create_async(url, principal, credentials, &session_listener, &reconnection_strategy, callbacks, &error); + + // sleep for 10 seconds + sleep(10); + + /* + * Close/free session (if we have one) and release resources + * and memory. + */ + if(g_session != NULL) { + session_close(g_session, NULL); + session_free(g_session); + } + credentials_free(credentials); + hash_free(options, NULL, free); + free(callbacks); + + return EXIT_SUCCESS; +} diff --git a/c/connect-with-user-context.c b/c/connect-with-user-context.c new file mode 100644 index 00000000..eddd939e --- /dev/null +++ b/c/connect-with-user-context.c @@ -0,0 +1,161 @@ +/** + * Copyright © 2022 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.9 + */ + +/* + * This example shows how to make a synchronous connection to Diffusion, passing a user_context to it. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'d', "delay", "Delay between reconnection attempts, in ms", ARG_OPTIONAL, ARG_HAS_VALUE, "2000" }, + {'r', "retries", "Reconnection retry attempts", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, + {'t', "timeout", "Reconnection timeout for a disconnected session", ARG_OPTIONAL, ARG_HAS_VALUE, NULL }, + {'s', "sleep", "Time to sleep before disconnecting (in seconds).", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, + END_OF_ARG_OPTS +}; + +/* + * This callback is used when the session state changes, e.g. when a session + * moves from a "connecting" to a "connected" state, or from "connected" to + * "closed". + */ +static void on_session_state_changed( + SESSION_T *session, + const SESSION_STATE_T old_state, + const SESSION_STATE_T new_state) +{ + printf("Session state changed from %s (%d) to %s (%d)\n", + session_state_as_string(old_state), old_state, + session_state_as_string(new_state), new_state); + + printf("The user context for the session is: %s\n", session->user_context); +} + +/* + * Entry point for the example. + */ +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + long retry_delay = atol(hash_get(options, "delay")); + long retry_count = atol(hash_get(options, "retries")); + long reconnect_timeout; + if(hash_get(options, "timeout") != NULL) { + reconnect_timeout = atol(hash_get(options, "timeout")); + } + else { + reconnect_timeout = -1; + } + + const unsigned int sleep_time = atol(hash_get(options, "sleep")); + + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + + SESSION_LISTENER_T session_listener = { 0 }; + session_listener.on_state_changed = &on_session_state_changed; + + /* + * Specify how we might want to failover or retry, and + * how long to keep the session alive on the server before + * it's discarded. + */ + RECONNECTION_STRATEGY_T *reconnection_strategy = + make_reconnection_strategy_repeating_attempt(retry_count, retry_delay); + + if(reconnect_timeout > 0) { + reconnection_strategy_set_timeout(reconnection_strategy, reconnect_timeout); + } + + /* + * Set the user context for the session. + * This can be of any type. + */ + char *user_context = "This is the user context for the session to be created."; + + /* + * Create a session, synchronously. + */ + session = session_create_with_user_context( + url, + principal, + credentials, + &session_listener, + reconnection_strategy, + user_context, + &error); + if(session != NULL) { + char *sid_str = session_id_to_string(session->id); + printf("Session created (state=%d, id=%s)\n", session_state_get(session), sid_str); + free(sid_str); + } + else { + printf("Failed to create session: %s\n", error.message); + free(error.message); + } + + /* + * Sleep for a while. + */ + sleep(sleep_time); + + /* + * Close the session, and release resources and memory. + */ + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + free_reconnection_strategy(reconnection_strategy); + + return EXIT_SUCCESS; +} diff --git a/c/connect.c b/c/connect.c index 951e4a9e..29a99157 100644 --- a/c/connect.c +++ b/c/connect.c @@ -1,148 +1,155 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This examples shows how to make a synchronous connection to Diffusion. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'d', "delay", "Delay between reconnection attempts, in ms", ARG_OPTIONAL, ARG_HAS_VALUE, "2000" }, - {'r', "retries", "Reconnection retry attempts", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, - {'t', "timeout", "Reconnection timeout for a disconnected session", ARG_OPTIONAL, ARG_HAS_VALUE, NULL }, - {'s', "sleep", "Time to sleep before disconnecting (in seconds).", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, - END_OF_ARG_OPTS -}; - -/* - * This callback is used when the session state changes, e.g. when a session - * moves from a "connecting" to a "connected" state, or from "connected" to - * "closed". - */ -static void -on_session_state_changed(SESSION_T *session, - const SESSION_STATE_T old_state, - const SESSION_STATE_T new_state) -{ - printf("Session state changed from %s (%d) to %s (%d)\n", - session_state_as_string(old_state), old_state, - session_state_as_string(new_state), new_state); -} - -/* - * Entry point for the example. - */ -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - long retry_delay = atol(hash_get(options, "delay")); - long retry_count = atol(hash_get(options, "retries")); - long reconnect_timeout; - if(hash_get(options, "timeout") != NULL) { - reconnect_timeout = atol(hash_get(options, "timeout")); - } - else { - reconnect_timeout = -1; - } - - const unsigned int sleep_time = atol(hash_get(options, "sleep")); - - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - - SESSION_LISTENER_T session_listener = { 0 }; - session_listener.on_state_changed = &on_session_state_changed; - - /* - * Specify how we might want to failover or retry, and - * how long to keep the session alive on the server before - * it's discarded. - */ - RECONNECTION_STRATEGY_T *reconnection_strategy = - make_reconnection_strategy_repeating_attempt(retry_count, retry_delay); - - if(reconnect_timeout > 0) { - reconnection_strategy_set_timeout(reconnection_strategy, reconnect_timeout); - } - - /* - * Create a session, synchronously. - */ - session = session_create(url, principal, credentials, &session_listener, reconnection_strategy, &error); - if(session != NULL) { - char *sid_str = session_id_to_string(session->id); - printf("Session created (state=%d, id=%s)\n", - session_state_get(session), - sid_str); - free(sid_str); - } - else { - printf("Failed to create session: %s\n", error.message); - free(error.message); - } - - /* - * Sleep for a while. - */ - sleep(sleep_time); - - /* - * Close the session, and release resources and memory. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - free_reconnection_strategy(reconnection_strategy); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This example shows how to make a synchronous connection to Diffusion. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'d', "delay", "Delay between reconnection attempts, in ms", ARG_OPTIONAL, ARG_HAS_VALUE, "2000" }, + {'r', "retries", "Reconnection retry attempts", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, + {'t', "timeout", "Reconnection timeout for a disconnected session", ARG_OPTIONAL, ARG_HAS_VALUE, NULL }, + {'s', "sleep", "Time to sleep before disconnecting (in seconds).", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, + END_OF_ARG_OPTS +}; + + +/* + * This callback is used when the session state changes, e.g. when a session + * moves from a "connecting" to a "connected" state, or from "connected" to + * "closed". + */ +static void on_session_state_changed( + SESSION_T *session, + const SESSION_STATE_T old_state, + const SESSION_STATE_T new_state) +{ + printf("Session state changed from %s (%d) to %s (%d)\n", + session_state_as_string(old_state), old_state, + session_state_as_string(new_state), new_state); +} + + +/* + * Entry point for the example. + */ +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + long retry_delay = atol(hash_get(options, "delay")); + long retry_count = atol(hash_get(options, "retries")); + long reconnect_timeout; + if(hash_get(options, "timeout") != NULL) { + reconnect_timeout = atol(hash_get(options, "timeout")); + } + else { + reconnect_timeout = -1; + } + + const unsigned int sleep_time = atol(hash_get(options, "sleep")); + + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + + SESSION_LISTENER_T session_listener = { 0 }; + session_listener.on_state_changed = &on_session_state_changed; + + /* + * Specify how we might want to failover or retry, and + * how long to keep the session alive on the server before + * it's discarded. + */ + RECONNECTION_STRATEGY_T *reconnection_strategy = + make_reconnection_strategy_repeating_attempt(retry_count, retry_delay); + + if(reconnect_timeout > 0) { + reconnection_strategy_set_timeout(reconnection_strategy, reconnect_timeout); + } + + /* + * Create a session, synchronously. + */ + session = session_create( + url, + principal, + credentials, + &session_listener, + reconnection_strategy, + &error); + if(session != NULL) { + char *sid_str = session_id_to_string(session->id); + printf("Session created (state=%d, id=%s)\n", session_state_get(session), sid_str); + free(sid_str); + } + else { + printf("Failed to create session: %s\n", error.message); + free(error.message); + } + + /* + * Sleep for a while. + */ + sleep(sleep_time); + + /* + * Close the session, and release resources and memory. + */ + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + free_reconnection_strategy(reconnection_strategy); + + return EXIT_SUCCESS; +} diff --git a/c/auth-service.c b/c/features/authentication_control/auth-service.c similarity index 54% rename from c/auth-service.c rename to c/features/authentication_control/auth-service.c index bd9be564..17924d91 100644 --- a/c/auth-service.c +++ b/c/features/authentication_control/auth-service.c @@ -1,268 +1,253 @@ -/* - * Copyright © 2014, 2017 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * Diffusion can be configured to delegate authentication requests to - * an external handler. This program provides an authentication - * handler to demonstrate this feature. A detailed description of - * security and authentication handlers can be found in the Diffusion - * user manual. - * - * Authentication handlers are registered with a name, which is typically specified in - * Server.xml - * - * Two handler names are provided by Diffusion and Diffusion Cloud by default; - * before-system-handler and after-system-handler, and additional - * handlers may be specified for Diffusion through the Server.xml file - * and an accompanying Java class that implements the - * AuthenticationHandler interface. - * - * This control authentication handler connects to Diffusion and attempts - * to register itself with a user-supplied name, which should match the name - * configured in Server.xml. - * - * The default behavior is to install as the "before-system-handler", - * which means that it will intercept authentication requests before - * Diffusion has a chance to act on them. - * - * It will: - *
    - *
  • Deny all anonymous connections
  • - *
  • Allow connections where the principal and credentials (i.e., username and password) match some hardcoded values
  • - *
  • Abstain from all other decisions, thereby letting Diffusion and other authentication handlers decide what to do.
  • - *
- */ - -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -struct user_credentials_s { - const char *username; - const char *password; -}; - -/* - * Username/password pairs that this handler accepts. - */ -static const struct user_credentials_s USERS[] = { - { "fish", "chips" }, - { "ham", "eggs" }, - { NULL, NULL } -}; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'n', "name", "Name under which to register the authorisation handler", ARG_OPTIONAL, ARG_HAS_VALUE, "before-system-handler"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - END_OF_ARG_OPTS -}; - -/* - * When the authentication service has been registered, this function will be - * called. - */ -static int -on_registration(SESSION_T *session, void *context) -{ - printf("Registered authentication handler\n"); - return HANDLER_SUCCESS; -} - -/* - * When the authentication service has be deregistered, this function will be - * called. - */ -static int -on_deregistration(SESSION_T *session, void *context) -{ - printf("Deregistered authentication handler\n"); - return HANDLER_SUCCESS; -} - -/* - * This is the function that is called when authentication has been delegated - * from Diffusion. - * - * The response may return one of three values via the response parameter: - * ALLOW: The user is authenticated. - * ALLOW_WITH_RESULT: The user is authenticated, and additional roles are - * to be applied to the user. - * DENY: The user is NOT authenticated. - * ABSTAIN: Allow another handler to make the decision. - * - * The handler should return HANDLER_SUCCESS in all cases, unless an actual - * error occurs during the authentication process (in which case, - * HANDLER_FAILURE is appropriate). - */ -static int -on_authentication(SESSION_T *session, - const SVC_AUTHENTICATION_REQUEST_T *request, - SVC_AUTHENTICATION_RESPONSE_T *response, - void *context) -{ - // No credentials, or not password type. We're not an authority for - // this type of authentication so abstain in case some other registered - // authentication handler can deal with the request. - if(request->credentials == NULL) { - printf("No credentials specified, abstaining\n"); - response->value = AUTHENTICATION_ABSTAIN; - return HANDLER_SUCCESS; - } - if(request->credentials->type != PLAIN_PASSWORD) { - printf("Credentials are not PLAIN_PASSWORD, abstaining\n"); - response->value = AUTHENTICATION_ABSTAIN; - return HANDLER_SUCCESS; - } - - printf("principal = %s\n", request->principal); - printf("credentials = %*s\n", - (int)request->credentials->data->len, - request->credentials->data->data); - - if(request->principal == NULL || strlen(request->principal) == 0) { - printf("Denying anonymous connection (no principal)\n"); - response->value = AUTHENTICATION_DENY; // Deny anon connections - return HANDLER_SUCCESS; - } - - char *password = malloc(request->credentials->data->len + 1); - memmove(password, request->credentials->data->data, request->credentials->data->len); - password[request->credentials->data->len] = '\0'; - - int auth_decided = 0; - int i = 0; - while(USERS[i].username != NULL) { - - printf("Checking username %s vs %s\n", request->principal, USERS[i].username); - printf(" and password %s vs %s\n", password, USERS[i].password); - - if(strcmp(USERS[i].username, request->principal) == 0 && - strcmp(USERS[i].password, password) == 0) { - - puts("Allowed"); - response->value = AUTHENTICATION_ALLOW; - auth_decided = 1; - break; - } - i++; - - } - - free(password); - - if(auth_decided == 0) { - puts("Abstained"); - response->value = AUTHENTICATION_ABSTAIN; - } - - return HANDLER_SUCCESS; -} - -int -main(int argc, char** argv) -{ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if (options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - char *name = hash_get(options, "name"); - char *principal = hash_get(options, "principal"); - char *credentials = hash_get(options, "credentials"); - - /* - * Create a session with Diffusion. - */ - puts("Creating session"); - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, - principal, - credentials != NULL ? credentials_create_password(credentials) : NULL, - NULL, NULL, - &error); - if (session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Provide a set (via a hash map containing keys and NULL - * values) to indicate what information about the connecting - * client that we'd like Diffusion to send us. - */ - HASH_T *detail_set = hash_new(5); - char buf[2]; - sprintf(buf, "%d", SESSION_DETAIL_SUMMARY); - hash_add(detail_set, strdup(buf), NULL); - sprintf(buf, "%d", SESSION_DETAIL_LOCATION); - hash_add(detail_set, strdup(buf), NULL); - sprintf(buf, "%d", SESSION_DETAIL_CONNECTOR_NAME); - hash_add(detail_set, strdup(buf), NULL); - - /* - * Register the authentication handler. - */ - AUTHENTICATION_REGISTRATION_PARAMS_T auth_registration_params = { - .name = name, - .detail_set = detail_set, - .on_registration = on_registration, - .authentication_handlers.on_authentication = on_authentication - }; - - puts("Sending registration request"); - SVC_AUTHENTICATION_REGISTER_REQUEST_T *reg_request = - authentication_register(session, auth_registration_params); - - /* - * Wait a while before moving on to deregistration. - */ - sleep(30); - - AUTHENTICATION_DEREGISTRATION_PARAMS_T auth_deregistration_params = { - .on_deregistration = on_deregistration, - .original_request = reg_request - }; - - /* - * Deregister the authentication handler. - */ - printf("Deregistering authentication handler\n"); - authentication_deregister(session, auth_deregistration_params); - - session_close(session, NULL); - session_free(session); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/* + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * Diffusion can be configured to delegate authentication requests to + * an external handler. This program provides an authentication + * handler to demonstrate this feature. A detailed description of + * security and authentication handlers can be found in the Diffusion + * user manual. + * + * Authentication handlers are registered with a name, which is typically specified in + * Server.xml + * + * Two handler names are provided by Diffusion and Diffusion Cloud by default; + * before-system-handler and after-system-handler, and additional + * handlers may be specified for Diffusion through the Server.xml file + * and an accompanying Java class that implements the + * AuthenticationHandler interface. + * + * This control authentication handler connects to Diffusion and attempts + * to register itself with a user-supplied name, which should match the name + * configured in Server.xml. + * + * The default behavior is to install as the "before-system-handler", + * which means that it will intercept authentication requests before + * Diffusion has a chance to act on them. + * + * It will: + *
    + *
  • Deny all anonymous connections
  • + *
  • Allow connections where the principal and credentials (i.e., username and password) match some hardcoded values
  • + *
  • Abstain from all other decisions, thereby letting Diffusion and other authentication handlers decide what to do.
  • + *
+ */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +struct user_credentials_s { + const char *username; + const char *password; +}; + +// Username/password pairs that this handler accepts. +static const struct user_credentials_s USERS[] = { + { "fish", "chips" }, + { "ham", "eggs" }, + { NULL, NULL } +}; + + +DIFFUSION_REGISTRATION_T *g_registration = NULL; + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'n', "name", "Name under which to register the authorisation handler", ARG_OPTIONAL, ARG_HAS_VALUE, "before-system-handler"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + +// When the authenticator handler is active, this function will be called. +static int on_authenticator_active( + SESSION_T *session, + const DIFFUSION_REGISTRATION_T *registration) +{ + g_registration = diffusion_registration_dup(registration); + + printf("Registered authentication handler\n"); + return HANDLER_SUCCESS; +} + + +// When the authenticator handler is closed, this function will be called. +static void on_authenticator_close() +{ + printf("Closed authentication handler\n"); +} + +/* + * This is the function that is called when authentication has been delegated + * from Diffusion. + * + * The available methods for an authentication response are: + * diffusion_authenticator_allow: The user is authenticated without user-defined + * properties + * diffusion_authenticator_allow_with_properties: The user is authenticated with + * modifications to the session properties. + * diffusion_authenticator_abstain: Allow another handler to make the decision + * diffusion_authenticator_deny: The user is NOT authenticated + * The response may return one of three values via the response parameter: + * + * The handler should return HANDLER_SUCCESS in all cases, unless an actual + * error occurs during the authentication process (in which case, + * HANDLER_FAILURE is appropriate). + */ +static int on_authenticator_authenticate( + SESSION_T *session, + const char *principal, + const CREDENTIALS_T *credentials, + const HASH_T *session_properties, + const HASH_T *proposed_session_properties, + const DIFFUSION_AUTHENTICATOR_T *authenticator) +{ + // No credentials, or not password type. We're not an authority for + // this type of authentication so abstain in case some other registered + // authentication handler can deal with the request. + if(credentials == NULL) { + printf("No credentials specified, abstaining\n"); + diffusion_authenticator_abstain(session, authenticator, NULL); + return HANDLER_SUCCESS; + } + if(credentials->type != PLAIN_PASSWORD) { + printf("Credentials are not PLAIN_PASSWORD, abstaining\n"); + diffusion_authenticator_abstain(session, authenticator, NULL); + return HANDLER_SUCCESS; + } + + printf("principal = %s\n", principal); + printf("credentials = %*s\n", + (int)credentials->data->len, + credentials->data->data); + + if(principal == NULL || strlen(principal) == 0) { + printf("Denying anonymous connection (no principal)\n"); + // Deny anonymous connections + diffusion_authenticator_deny(session, authenticator, NULL); + return HANDLER_SUCCESS; + } + + char *password = malloc(credentials->data->len + 1); + memmove(password, credentials->data->data, credentials->data->len); + password[credentials->data->len] = '\0'; + + int auth_decided = 0; + int i = 0; + while(USERS[i].username != NULL) { + + printf("Checking username %s vs %s\n", principal, USERS[i].username); + printf(" and password %s vs %s\n", password, USERS[i].password); + + if(strcmp(USERS[i].username, principal) == 0 && + strcmp(USERS[i].password, password) == 0) { + + puts("Allowed"); + diffusion_authenticator_allow(session, authenticator, NULL); + auth_decided = 1; + break; + } + i++; + } + + free(password); + + if(auth_decided == 0) { + puts("Abstained"); + diffusion_authenticator_abstain(session, authenticator, NULL); + } + return HANDLER_SUCCESS; +} + + +int main(int argc, char** argv) +{ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if (options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *name = hash_get(options, "name"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if (password != NULL) { + credentials = credentials_create_password(password); + } + + /* + * Create a session with Diffusion. + */ + puts("Creating session"); + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if (session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Register the authentication handler. + DIFFUSION_AUTHENTICATION_HANDLER_T handler = { + .handler_name = (char *) name, + .on_active = on_authenticator_active, + .on_authenticate = on_authenticator_authenticate, + .on_close = on_authenticator_close + }; + + DIFFUSION_AUTHENTICATION_HANDLER_PARAMS_T params = { + .handler = &handler + }; + + puts("Setting authentication handler"); + diffusion_set_authentication_handler(session, params); + + // Wait a while before moving on to deregistration. + sleep(30); + + // Deregister the authentication handler. + printf("Closing authentication handler\n"); + diffusion_registration_close(session, g_registration); + + session_close(session, NULL); + session_free(session); + hash_free(options, NULL, free); + credentials_free(credentials); + diffusion_registration_free(g_registration); + g_registration = NULL; + + return EXIT_SUCCESS; +} diff --git a/c/features/client_control/change-roles-with-filter.c b/c/features/client_control/change-roles-with-filter.c new file mode 100644 index 00000000..dcd5c4c6 --- /dev/null +++ b/c/features/client_control/change-roles-with-filter.c @@ -0,0 +1,150 @@ +/** + * Copyright © 2021 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.7 + */ + +/* + * This example shows how roles can be changed during an active session. + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Callback to display that the roles has been successfully changed. +static int on_roles_changed_with_filter( + int number_of_matching_sessions, + void *context) +{ + printf("Successfully changed roles, affecting %d session(s).\n", number_of_matching_sessions); + return HANDLER_SUCCESS; +} + + +// Callback to display an error when attempting to change roles. +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("Failed to change roles: [%d] %s\n", error->code, diffusion_error_str(error->code)); + return HANDLER_SUCCESS; +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + // Create a control session with Diffusion. + CREDENTIALS_T *control_credentials = + credentials_create_password("password"); + + DIFFUSION_SESSION_FACTORY_T *session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(session_factory, "control"); + diffusion_session_factory_credentials(session_factory, control_credentials); + + SESSION_T *control_session = session_create_with_session_factory(session_factory, url); + if (control_session == NULL) { + fprintf(stderr, "Failed to create control session\n"); + return EXIT_FAILURE; + } + + // Create multiple normal sessions with Diffusion, using `client` as Principal + CREDENTIALS_T *credentials = credentials_create_password(password); + + DIFFUSION_SESSION_FACTORY_T *client_session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(client_session_factory, principal); + diffusion_session_factory_credentials(client_session_factory, credentials); + + int totalSessions = 3; + SESSION_T **sessions = calloc(totalSessions, sizeof(SESSION_T*)); + + for (int i = 0; i < totalSessions; i++) { + sessions[i] = session_create_with_session_factory(client_session_factory, url); + if (sessions[i] == NULL) { + fprintf(stderr, "Failed to create normal session %d\n", i); + return EXIT_FAILURE; + } + } + + SET_T *roles_to_add = set_new_string(1); + set_add(roles_to_add, "NEW_CLIENT_ROLE"); + + DIFFUSION_CHANGE_ROLES_WITH_FILTER_PARAMS_T params = { + .filter = "$Principal EQ 'client'", + .roles_to_remove = NULL, + .roles_to_add = roles_to_add, + .on_roles_changed = on_roles_changed_with_filter, + .on_error = on_error + }; + + DIFFUSION_API_ERROR api_error; + diffusion_change_roles_with_filter(control_session, params, &api_error); + + // Wait for a couple of seconds. + sleep(2); + + puts("Closing sessions"); + + // Close the connections and free resources + session_close(control_session, NULL); + session_free(control_session); + + for(int i = 0; i < totalSessions; i++) { + session_close(sessions[i], NULL); + session_free(sessions[i]); + } + free(sessions); + + set_free(roles_to_add); + credentials_free(control_credentials); + credentials_free(credentials); + hash_free(options, NULL, free); + + diffusion_session_factory_free(client_session_factory); + diffusion_session_factory_free(session_factory); + + return EXIT_SUCCESS; +} diff --git a/c/features/client_control/change-roles-with-session.c b/c/features/client_control/change-roles-with-session.c new file mode 100644 index 00000000..b8ff55eb --- /dev/null +++ b/c/features/client_control/change-roles-with-session.c @@ -0,0 +1,146 @@ +/** + * Copyright © 2021 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.7 + */ + +/* + * This example shows how roles can be changed during an active session. + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +/* + * Callback to display that the roles have been successfully changed. + */ +static int on_roles_changed(void *context) +{ + printf("Successfully changed roles.\n"); + return HANDLER_SUCCESS; +} + + +/* + * Callback to display an error when attempting to change roles. + */ +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("Failed to change roles: [%d] %s\n", error->code, diffusion_error_str(error->code)); + return HANDLER_SUCCESS; +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + // Create a control session with Diffusion. + CREDENTIALS_T *control_credentials = + credentials_create_password("password"); + + DIFFUSION_SESSION_FACTORY_T *session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(session_factory, "control"); + diffusion_session_factory_credentials(session_factory, control_credentials); + + SESSION_T *control_session = session_create_with_session_factory(session_factory, url); + if (control_session == NULL) { + fprintf(stderr, "Failed to create control session\n"); + return EXIT_FAILURE; + } + + // Create normal session with Diffusion, using `client` as Principal + CREDENTIALS_T *credentials = credentials_create_password(password); + + DIFFUSION_SESSION_FACTORY_T *client_session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(client_session_factory, principal); + diffusion_session_factory_credentials(client_session_factory, credentials); + + SESSION_T *normal_session = session_create_with_session_factory(client_session_factory, url); + if (normal_session == NULL) { + fprintf(stderr, "Failed to create normal session\n"); + return EXIT_FAILURE; + } + + SET_T *roles_to_add = set_new_string(1); + set_add(roles_to_add, "AUTHENTICATION_HANDLER"); + + DIFFUSION_CHANGE_ROLES_WITH_SESSION_ID_PARAMS_T params = { + .session_id = normal_session->id, + .roles_to_remove = NULL, + .roles_to_add = roles_to_add, + .on_roles_changed = on_roles_changed, + .on_error = on_error + }; + + DIFFUSION_API_ERROR api_error; + diffusion_change_roles_with_session_id(control_session, params, &api_error); + + // Wait for a couple of seconds. + sleep(5); + + puts("Closing session"); + + // Close the connections and free resources + session_close(normal_session, NULL); + session_free(normal_session); + + session_close(control_session, NULL); + session_free(control_session); + + set_free(roles_to_add); + + credentials_free(control_credentials); + credentials_free(credentials); + + hash_free(options, NULL, free); + + diffusion_session_factory_free(client_session_factory); + diffusion_session_factory_free(session_factory); + + return EXIT_SUCCESS; +} diff --git a/c/features/client_control/close-with-filter.c b/c/features/client_control/close-with-filter.c new file mode 100644 index 00000000..14b3af6e --- /dev/null +++ b/c/features/client_control/close-with-filter.c @@ -0,0 +1,144 @@ +/** + * Copyright © 2021 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.7 + */ + +/* + * This example shows how to close a session via a control session using + * a session properties filter. + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Callback to indicate that the affected sessions by the filter have been closed. +static int on_clients_closed( + int selected, + void *context) +{ + printf("%d session(s) closed\n", selected); + return HANDLER_SUCCESS; +} + + +// Callback to display an error when attempting to close a session. +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("Failed to close session(s): [%d] %s\n", error->code, diffusion_error_str(error->code)); + return HANDLER_SUCCESS; +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + // Create a control session with Diffusion. + CREDENTIALS_T *control_credentials = + credentials_create_password("password"); + + DIFFUSION_SESSION_FACTORY_T *session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(session_factory, "control"); + diffusion_session_factory_credentials(session_factory, control_credentials); + + SESSION_T *control_session = session_create_with_session_factory(session_factory, url); + if (control_session == NULL) { + fprintf(stderr, "Failed to create control session\n"); + return EXIT_FAILURE; + } + + // Create multiple normal sessions with Diffusion, using `client` as Principal + CREDENTIALS_T *credentials = credentials_create_password(password); + + DIFFUSION_SESSION_FACTORY_T *client_session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(client_session_factory, principal); + diffusion_session_factory_credentials(client_session_factory, credentials); + + int totalSessions = 3; + SESSION_T **sessions = calloc(totalSessions, sizeof(SESSION_T*)); + + for (int i = 0; i < totalSessions; i++) { + sessions[i] = session_create_with_session_factory(client_session_factory, url); + if (sessions[i] == NULL) { + fprintf(stderr, "Failed to create normal session %d\n", i); + return EXIT_FAILURE; + } + } + + // Close normal sessions using control session and diffusion_client_close_with_filter + DIFFUSION_CLIENT_CLOSE_WITH_FILTER_PARAMS_T params = { + .filter = "$Principal EQ 'client'", + .on_clients_closed = on_clients_closed, + .on_error = on_error + }; + + DIFFUSION_API_ERROR api_error; + diffusion_client_close_with_filter(control_session, params, &api_error); + + // Wait for a couple of seconds. + sleep(2); + + puts("Closing sessions"); + + // Close the connections and free resources. + session_close(control_session, NULL); + session_free(control_session); + + for(int i = 0; i < totalSessions; i++) { + session_free(sessions[i]); + } + free(sessions); + + diffusion_session_factory_free(client_session_factory); + diffusion_session_factory_free(session_factory); + credentials_free(control_credentials); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/get-session-properties.c b/c/features/client_control/get-session-properties.c similarity index 80% rename from c/get-session-properties.c rename to c/features/client_control/get-session-properties.c index 6bf77c59..2aa8e709 100644 --- a/c/get-session-properties.c +++ b/c/features/client_control/get-session-properties.c @@ -1,163 +1,142 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.7 - */ - -/* - * In this example, we attempt to receive session properties for a client - * with the specified client ID. - * - * In normal use, this could be used in conjunction with a session - * properties listener that can track connecting client sessions and - * their associated client IDs. - */ - -#include -#ifndef WIN32 -#include -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'i', "sessionid", "Session ID of the client. If not specified, get properties for this session.", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'r', "properties", "Comma separated list of properties to be requested.", ARG_OPTIONAL, ARG_HAS_VALUE, PROPERTIES_SELECTOR_ALL_FIXED_PROPERTIES}, - END_OF_ARG_OPTS -}; - -/* - * Callback invoked when session properties are received. - */ -int -on_session_properties(SESSION_T *session, const SVC_GET_SESSION_PROPERTIES_RESPONSE_T *response, void *context) -{ - char **keys = hash_keys(response->properties); - for(char **k = keys; *k != NULL; k++) { - char *v = hash_get(response->properties, *k); - printf("%s=%s\n", *k, v); - } - free(keys); - - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with Diffusion. - */ - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "Failed to create session: %s\n", error.message); - return EXIT_FAILURE; - } - - SESSION_ID_T *sid; - char *sid_str = hash_get(options, "sessionid"); - if(sid_str != NULL) { - sid = session_id_create_from_string(sid_str); - } - else { - sid = session->id; - } - - SET_T *properties = set_new_string(10); - char *props_str = strdup(hash_get(options, "properties")); - char *str = props_str; - char *tok = NULL; - while((tok = strtok(str, ",")) != NULL) { - str = NULL; - set_add(properties, tok); - } - free(props_str); - - GET_SESSION_PROPERTIES_PARAMS_T params = { - .session_id = sid, - .required_properties = properties, - .on_session_properties = on_session_properties - }; - - /* - * Request the session properties, and wait for the response. - */ - apr_thread_mutex_lock(mutex); - get_session_properties(session, params); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - /* - * Close the session and clean up. - */ - session_close(session, NULL); - session_free(session); - - set_free(properties); - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.6 + */ + +/* + * In this example, we attempt to receive session properties for a client + * with the specified client ID. + * + * In normal use, this could be used in conjunction with a session + * properties listener that can track connecting client sessions and + * their associated client IDs. + */ + +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'i', "sessionid", "Session ID of the client. If not specified, get properties for this session.", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, + {'r', "properties", "Comma separated list of properties to be requested.", ARG_OPTIONAL, ARG_HAS_VALUE, PROPERTIES_SELECTOR_ALL_FIXED_PROPERTIES}, + END_OF_ARG_OPTS +}; + +/* + * Callback invoked when session properties are received. + */ +int on_session_properties( + SESSION_T *session, + const SVC_GET_SESSION_PROPERTIES_RESPONSE_T *response, + void *context) +{ + char **keys = hash_keys(response->properties); + for(char **k = keys; *k != NULL; k++) { + char *v = hash_get(response->properties, *k); + printf("%s=%s\n", *k, v); + } + free(keys); + return HANDLER_SUCCESS; +} + + +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + /* + * Create a session with Diffusion. + */ + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "Failed to create session: %s\n", error.message); + return EXIT_FAILURE; + } + + SESSION_ID_T *sid; + char *sid_str = hash_get(options, "sessionid"); + if(sid_str != NULL) { + sid = session_id_create_from_string(sid_str); + } + else { + sid = session->id; + } + + SET_T *properties = set_new_string(10); + char *props_str = strdup(hash_get(options, "properties")); + char *str = props_str; + char *tok = NULL; + while((tok = strtok(str, ",")) != NULL) { + str = NULL; + set_add(properties, tok); + } + free(props_str); + + GET_SESSION_PROPERTIES_PARAMS_T params = { + .session_id = sid, + .required_properties = properties, + .on_session_properties = on_session_properties + }; + + /* + * Request the session properties, and wait for the response. + */ + get_session_properties(session, params); + + // Sleep for a while + sleep(5); + + /* + * Close the session and clean up. + */ + session_close(session, NULL); + session_free(session); + set_free(properties); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/session-properties-listener.c b/c/features/client_control/session-properties-listener.c similarity index 82% rename from c/session-properties-listener.c rename to c/features/client_control/session-properties-listener.c index c110ba3c..5f7edd6e 100644 --- a/c/session-properties-listener.c +++ b/c/features/client_control/session-properties-listener.c @@ -1,207 +1,219 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.7 - */ - -/* - * This example demonstrates how to register a listener that receives - * notification of new client connections, clients closing and client - * properties being updated. - */ - -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "set.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Helper function to print the properties hash key/value pairs. - */ -static void -print_properties(HASH_T *properties) -{ - char **keys = hash_keys(properties); - for(char **k = keys; *k != NULL; k++) { - char *v = hash_get(properties, *k); - printf("%s=%s\n", *k, v); - } - free(keys); -} - -/* - * Callback invoked when our listener registers successfully. - */ -static int -on_registered(SESSION_T *session, void *context) -{ - printf("on_registered\n"); - return HANDLER_SUCCESS; -} - -/* - * Callback invoked if our listener fails to register. - */ -static int -on_registration_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("on_registration_error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Callback invoked when we receive notification of a new client - * session. - */ -static int -on_session_open(SESSION_T *session, const SESSION_PROPERTIES_EVENT_T *request, void *context) -{ - char *sid_str = session_id_to_string(&request->session_id); - printf("on_session_open: %s\n", sid_str); - free(sid_str); - print_properties(request->properties); - return HANDLER_SUCCESS; -} - -/* - * Callback invoked when an existing client session undergoes a change - * of properties. - */ -static int -on_session_update(SESSION_T *session, const SESSION_PROPERTIES_EVENT_T *request, void *context) -{ - printf("on_session_update\n"); - char *sid_str = session_id_to_string(&request->session_id); - printf("on_session_close: %s\n", sid_str); - free(sid_str); - printf("update type: %d\n", request->update_type); - return HANDLER_SUCCESS; -} - -/* - * Callback invoked when a client session closes. - */ -static int -on_session_close(SESSION_T *session, const SESSION_PROPERTIES_EVENT_T *request, void *context) -{ - char *sid_str = session_id_to_string(&request->session_id); - printf("on_session_close: %s\n", sid_str); - free(sid_str); - printf("reason: %d\n", request->close_reason); - print_properties(request->properties); - return HANDLER_SUCCESS; -} - -/* - * Callback invoked if an error occurs while procssing a session - * property event from the server. - */ -static int -on_session_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("on_session_error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - /* - * Create a session with Diffusion. - */ - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "Failed to create session: %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Register a session properties listener. - * - * Requests all "fixed" properties, i.e. those defined by - * Diffusion rather than user-defined properties. - */ - SET_T *required_properties = set_new_string(5); - set_add(required_properties, PROPERTIES_SELECTOR_ALL_FIXED_PROPERTIES); - - SESSION_PROPERTIES_REGISTRATION_PARAMS_T params = { - .on_registered = on_registered, - .on_registration_error = on_registration_error, - .on_session_open = on_session_open, - .on_session_close = on_session_close, - .on_session_update = on_session_update, - .on_session_error = on_session_error, - .required_properties = required_properties - }; - session_properties_listener_register(session, params); - - /* - * Wait for session events for 2 minutes. - */ - sleep(120); - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - set_free(required_properties); - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.6 + */ + +/* + * This example demonstrates how to register a listener that receives + * notification of new client connections, clients closing and client + * properties being updated. + */ + +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "set.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +/* + * Helper function to print the properties hash key/value pairs. + */ +static void print_properties(HASH_T *properties) +{ + char **keys = hash_keys(properties); + for(char **k = keys; *k != NULL; k++) { + char *v = hash_get(properties, *k); + printf("%s=%s\n", *k, v); + } + free(keys); +} + + +/* + * Callback invoked when our listener registers successfully. + */ +static int on_registered( + SESSION_T *session, + void *context) +{ + printf("on_registered\n"); + return HANDLER_SUCCESS; +} + + +/* + * Callback invoked if our listener fails to register. + */ +static int on_registration_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("on_registration_error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +/* + * Callback invoked when we receive notification of a new client + * session. + */ +static int on_session_open( + SESSION_T *session, + const SESSION_PROPERTIES_EVENT_T *request, + void *context) +{ + char *sid_str = session_id_to_string(&request->session_id); + printf("on_session_open: %s\n", sid_str); + free(sid_str); + print_properties(request->properties); + return HANDLER_SUCCESS; +} + + +/* + * Callback invoked when an existing client session undergoes a change + * of properties. + */ +static int on_session_update( + SESSION_T *session, + const SESSION_PROPERTIES_EVENT_T *request, + void *context) +{ + printf("on_session_update\n"); + char *sid_str = session_id_to_string(&request->session_id); + printf("on_session_close: %s\n", sid_str); + free(sid_str); + printf("update type: %d\n", request->update_type); + return HANDLER_SUCCESS; +} + + +/* + * Callback invoked when a client session closes. + */ +static int on_session_close( + SESSION_T *session, + const SESSION_PROPERTIES_EVENT_T *request, + void *context) +{ + char *sid_str = session_id_to_string(&request->session_id); + printf("on_session_close: %s\n", sid_str); + free(sid_str); + printf("reason: %d\n", request->close_reason); + print_properties(request->properties); + return HANDLER_SUCCESS; +} + + +/* + * Callback invoked if an error occurs while procssing a session + * property event from the server. + */ +static int on_session_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("on_session_error: %s\n", error->message); + return HANDLER_SUCCESS; +} + +/* + * Program entry point. + */ +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + /* + * Create a session with Diffusion. + */ + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "Failed to create session: %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Register a session properties listener. + * + * Requests all "fixed" properties, i.e. those defined by + * Diffusion rather than user-defined properties. + */ + SET_T *required_properties = set_new_string(5); + set_add(required_properties, PROPERTIES_SELECTOR_ALL_FIXED_PROPERTIES); + + SESSION_PROPERTIES_REGISTRATION_PARAMS_T params = { + .on_registered = on_registered, + .on_registration_error = on_registration_error, + .on_session_open = on_session_open, + .on_session_close = on_session_close, + .on_session_update = on_session_update, + .on_session_error = on_session_error, + .required_properties = required_properties + }; + session_properties_listener_register(session, params); + + /* + * Wait for session events for 2 minutes. + */ + sleep(120); + + /* + * Close session and free resources. + */ + session_close(session, NULL); + session_free(session); + + set_free(required_properties); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/send-request-to-filter.c b/c/features/messaging/send-request-to-filter.c similarity index 95% rename from c/send-request-to-filter.c rename to c/features/messaging/send-request-to-filter.c index e336a9e1..1f8be9d3 100644 --- a/c/send-request-to-filter.c +++ b/c/features/messaging/send-request-to-filter.c @@ -1,181 +1,181 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example shows how a request can be sent through a filter to distribute to - * all clients matching the filter. - */ - -#include - -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -char *response; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'t', "request_path", "Request path", ARG_REQUIRED, ARG_HAS_VALUE, "echo"}, - {'d', "request", "Request to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client request!"}, - {'r', "response", "Response to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client response!"}, - END_OF_ARG_OPTS -}; - -static int on_number_sent(int number_sent, void *context) -{ - printf("Requests sent: %d\n", number_sent); - return HANDLER_SUCCESS; -} - -static int -on_request(SESSION_T *session, const char *request_path, DIFFUSION_DATATYPE request_datatype, - const DIFFUSION_VALUE_T *request, const DIFFUSION_RESPONDER_HANDLE_T *handle, void *context) -{ - - char *request_val; - read_diffusion_string_value(request, &request_val, NULL); - - printf("Request received: %s\n", request_val); - free(request_val); - - BUF_T *response_buf = buf_create(); - write_diffusion_string_value(response, response_buf); - diffusion_respond_to_request(session, handle, response_buf, NULL); - - buf_free(response_buf); - - return HANDLER_SUCCESS; -} - -static int -on_response(DIFFUSION_DATATYPE response_datatype, const DIFFUSION_VALUE_T *response, void *context) -{ - char *response_val; - read_diffusion_string_value(response, &response_val, NULL); - printf("Response received: %s\n\n", response_val); - free(response_val); - - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - char *request_path = hash_get(options, "request_path"); - - /* - * Create 2 sessions with Diffusion. - */ - SESSION_T *client = NULL; - SESSION_T *sender = NULL; - - DIFFUSION_ERROR_T error = { 0 }; - client = session_create(url, principal, credentials, NULL, NULL, &error); - if(client == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - sender = session_create(url, "admin", credentials, NULL, NULL, &error); - if(sender == NULL) { - fprintf(stderr, "TEST: Failed to create sender session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Create a payload. - */ - char *request_data = hash_get(options, "request"); - response = hash_get(options, "response"); - - BUF_T *request = buf_create(); - - write_diffusion_string_value(request_data, request); - - DIFFUSION_REQUEST_STREAM_T request_stream = { - .on_request = on_request - }; - - set_request_stream(client, request_path, DATATYPE_STRING, DATATYPE_STRING, &request_stream); - - /* - * Send to all non admin principal clients. - */ - SEND_REQUEST_TO_FILTER_PARAMS_T params = { - .path = request_path, - .filter = "$Principal NE 'admin'", - .request_datatype = DATATYPE_STRING, - .response_datatype = DATATYPE_STRING, - .on_response = on_response, - .on_number_sent = on_number_sent, - .request = request, - }; - - int counter = 1; - - while (counter <= 120) { - printf("Sending filter request to path {%s}.. #%d\n", request_path, counter); - send_request_to_filter(sender, params); - sleep(1); - ++counter; - } - - session_close(client, NULL); - session_free(client); - - session_close(sender, NULL); - session_free(sender); - - buf_free(request); - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} \ No newline at end of file +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example shows how a request can be sent through a filter to distribute to + * all clients matching the filter. + */ + +#include + +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +char *response; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "request_path", "Request path", ARG_REQUIRED, ARG_HAS_VALUE, "echo"}, + {'d', "request", "Request to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client request!"}, + {'r', "response", "Response to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client response!"}, + END_OF_ARG_OPTS +}; + +static int on_number_sent(int number_sent, void *context) +{ + printf("Requests sent: %d\n", number_sent); + return HANDLER_SUCCESS; +} + +static int +on_request(SESSION_T *session, const char *request_path, DIFFUSION_DATATYPE request_datatype, + const DIFFUSION_VALUE_T *request, const DIFFUSION_RESPONDER_HANDLE_T *handle, void *context) +{ + + char *request_val; + read_diffusion_string_value(request, &request_val, NULL); + + printf("Request received: %s\n", request_val); + free(request_val); + + BUF_T *response_buf = buf_create(); + write_diffusion_string_value(response, response_buf); + diffusion_respond_to_request(session, handle, response_buf, NULL); + + buf_free(response_buf); + + return HANDLER_SUCCESS; +} + +static int +on_response(DIFFUSION_DATATYPE response_datatype, const DIFFUSION_VALUE_T *response, void *context) +{ + char *response_val; + read_diffusion_string_value(response, &response_val, NULL); + printf("Response received: %s\n\n", response_val); + free(response_val); + + return HANDLER_SUCCESS; +} + +int +main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + char *url = hash_get(options, "url"); + + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + char *request_path = hash_get(options, "request_path"); + + /* + * Create 2 sessions with Diffusion. + */ + SESSION_T *client = NULL; + SESSION_T *sender = NULL; + + DIFFUSION_ERROR_T error = { 0 }; + client = session_create(url, principal, credentials, NULL, NULL, &error); + if(client == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + sender = session_create(url, "admin", credentials, NULL, NULL, &error); + if(sender == NULL) { + fprintf(stderr, "TEST: Failed to create sender session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Create a payload. + */ + char *request_data = hash_get(options, "request"); + response = hash_get(options, "response"); + + BUF_T *request = buf_create(); + + write_diffusion_string_value(request_data, request); + + DIFFUSION_REQUEST_STREAM_T request_stream = { + .on_request = on_request + }; + + set_request_stream(client, request_path, DATATYPE_STRING, DATATYPE_STRING, &request_stream); + + /* + * Send to all non admin principal clients. + */ + SEND_REQUEST_TO_FILTER_PARAMS_T params = { + .path = request_path, + .filter = "$Principal NE 'admin'", + .request_datatype = DATATYPE_STRING, + .response_datatype = DATATYPE_STRING, + .on_response = on_response, + .on_number_sent = on_number_sent, + .request = request, + }; + + int counter = 1; + + while (counter <= 120) { + printf("Sending filter request to path {%s}.. #%d\n", request_path, counter); + send_request_to_filter(sender, params); + sleep(1); + ++counter; + } + + session_close(client, NULL); + session_free(client); + + session_close(sender, NULL); + session_free(sender); + + buf_free(request); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/send-request-to-path.c b/c/features/messaging/send-request-to-path.c similarity index 95% rename from c/send-request-to-path.c rename to c/features/messaging/send-request-to-path.c index 4503b79f..b0fef6e8 100644 --- a/c/send-request-to-path.c +++ b/c/features/messaging/send-request-to-path.c @@ -1,190 +1,190 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example shows how a request can be sent to a request handler via - * a request path endpoint. - */ - -#include - -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -char *response; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'t', "request_path", "Request path", ARG_REQUIRED, ARG_HAS_VALUE, "echo"}, - {'d', "request", "Request to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client request!"}, - {'r', "response", "Response to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client response!"}, - END_OF_ARG_OPTS -}; - -static int -on_active(SESSION_T *session, const char *path, const DIFFUSION_REGISTRATION_T *registered_handler) -{ - printf("Request handler active\n"); - return HANDLER_SUCCESS; -} - -static int -on_request(SESSION_T *session, DIFFUSION_DATATYPE request_datatype, const DIFFUSION_VALUE_T *request, - const DIFFUSION_REQUEST_CONTEXT_T *request_context, const DIFFUSION_RESPONDER_HANDLE_T *handle, void *context) -{ - - char *request_val; - read_diffusion_string_value(request, &request_val, NULL); - - SESSION_ID_T *session_id = diffusion_request_context_get_session_id(request_context); - char *session_id_str = session_id_to_string(session_id); - - printf("Request received from %s: %s\n", session_id_str, request_val); - free(request_val); - - BUF_T *response_buf = buf_create(); - write_diffusion_string_value(response, response_buf); - diffusion_respond_to_request(session, handle, response_buf, NULL); - - buf_free(response_buf); - session_id_free(session_id); - free(session_id_str); - - return HANDLER_SUCCESS; -} - -static int -on_response(DIFFUSION_DATATYPE response_datatype, const DIFFUSION_VALUE_T *response, void *context) -{ - char *response_val; - read_diffusion_string_value(response, &response_val, NULL); - printf("Response received: %s\n\n", response_val); - free(response_val); - - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - char *request_path = hash_get(options, "request_path"); - - /* - * Create 2 sessions with Diffusion. - */ - SESSION_T *session = NULL; - SESSION_T *handler = NULL; - - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - handler = session_create(url, "admin", credentials, NULL, NULL, &error); - if(handler == NULL) { - fprintf(stderr, "TEST: Failed to create handler session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Create a payload. - */ - char *request_data = hash_get(options, "request"); - response = hash_get(options, "response"); - - BUF_T *request = buf_create(); - - write_diffusion_string_value(request_data, request); - - DIFFUSION_REQUEST_HANDLER_T request_handler = { - .request_datatype = DATATYPE_STRING, - .response_datatype = DATATYPE_STRING, - .on_active = on_active, - .on_request = on_request - }; - - ADD_REQUEST_HANDLER_PARAMS_T request_handler_params = { - .path = request_path, - .request_handler = &request_handler - }; - - add_request_handler(handler, request_handler_params); - - SEND_REQUEST_PARAMS_T send_request_params = { - .path = request_path, - .request = request, - .on_response = on_response, - .request_datatype = DATATYPE_STRING, - .response_datatype = DATATYPE_STRING - }; - - int counter = 1; - - while (counter <= 120) { - printf("Sending request to path {%s}.. #%d\n", request_path, counter); - send_request(session, send_request_params); - sleep(1); - ++counter; - } - - session_close(session, NULL); - session_free(session); - - session_close(handler, NULL); - session_free(handler); - - buf_free(request); - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} \ No newline at end of file +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example shows how a request can be sent to a request handler via + * a request path endpoint. + */ + +#include + +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +char *response; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "request_path", "Request path", ARG_REQUIRED, ARG_HAS_VALUE, "echo"}, + {'d', "request", "Request to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client request!"}, + {'r', "response", "Response to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client response!"}, + END_OF_ARG_OPTS +}; + +static int +on_active(SESSION_T *session, const char *path, const DIFFUSION_REGISTRATION_T *registered_handler) +{ + printf("Request handler active\n"); + return HANDLER_SUCCESS; +} + +static int +on_request(SESSION_T *session, DIFFUSION_DATATYPE request_datatype, const DIFFUSION_VALUE_T *request, + const DIFFUSION_REQUEST_CONTEXT_T *request_context, const DIFFUSION_RESPONDER_HANDLE_T *handle, void *context) +{ + + char *request_val; + read_diffusion_string_value(request, &request_val, NULL); + + SESSION_ID_T *session_id = diffusion_request_context_get_session_id(request_context); + char *session_id_str = session_id_to_string(session_id); + + printf("Request received from %s: %s\n", session_id_str, request_val); + free(request_val); + + BUF_T *response_buf = buf_create(); + write_diffusion_string_value(response, response_buf); + diffusion_respond_to_request(session, handle, response_buf, NULL); + + buf_free(response_buf); + session_id_free(session_id); + free(session_id_str); + + return HANDLER_SUCCESS; +} + +static int +on_response(DIFFUSION_DATATYPE response_datatype, const DIFFUSION_VALUE_T *response, void *context) +{ + char *response_val; + read_diffusion_string_value(response, &response_val, NULL); + printf("Response received: %s\n\n", response_val); + free(response_val); + + return HANDLER_SUCCESS; +} + +int +main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + char *url = hash_get(options, "url"); + + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + char *request_path = hash_get(options, "request_path"); + + /* + * Create 2 sessions with Diffusion. + */ + SESSION_T *session = NULL; + SESSION_T *handler = NULL; + + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + handler = session_create(url, "admin", credentials, NULL, NULL, &error); + if(handler == NULL) { + fprintf(stderr, "TEST: Failed to create handler session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Create a payload. + */ + char *request_data = hash_get(options, "request"); + response = hash_get(options, "response"); + + BUF_T *request = buf_create(); + + write_diffusion_string_value(request_data, request); + + DIFFUSION_REQUEST_HANDLER_T request_handler = { + .request_datatype = DATATYPE_STRING, + .response_datatype = DATATYPE_STRING, + .on_active = on_active, + .on_request = on_request + }; + + ADD_REQUEST_HANDLER_PARAMS_T request_handler_params = { + .path = request_path, + .request_handler = &request_handler + }; + + add_request_handler(handler, request_handler_params); + + SEND_REQUEST_PARAMS_T send_request_params = { + .path = request_path, + .request = request, + .on_response = on_response, + .request_datatype = DATATYPE_STRING, + .response_datatype = DATATYPE_STRING + }; + + int counter = 1; + + while (counter <= 120) { + printf("Sending request to path {%s}.. #%d\n", request_path, counter); + send_request(session, send_request_params); + sleep(1); + ++counter; + } + + session_close(session, NULL); + session_free(session); + + session_close(handler, NULL); + session_free(handler); + + buf_free(request); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/send-request-to-session.c b/c/features/messaging/send-request-to-session.c similarity index 95% rename from c/send-request-to-session.c rename to c/features/messaging/send-request-to-session.c index a5c6084d..5d73881e 100644 --- a/c/send-request-to-session.c +++ b/c/features/messaging/send-request-to-session.c @@ -1,157 +1,157 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example shows how a request can be sent to another client via - * a request path endpoint. The session ID of the target client must be - * known. - */ - -#include - -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -char *response; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'t', "request_path", "Request path", ARG_REQUIRED, ARG_HAS_VALUE, "echo"}, - {'d', "request", "Request to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client request!"}, - {'r', "response", "Response to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client response!"}, - END_OF_ARG_OPTS -}; - -static int -on_request(SESSION_T *session, const char *request_path, DIFFUSION_DATATYPE request_datatype, - const DIFFUSION_VALUE_T *request, const DIFFUSION_RESPONDER_HANDLE_T *handle, void *context) -{ - - char *request_val; - read_diffusion_string_value(request, &request_val, NULL); - printf("Request received: %s\n", request_val); - free(request_val); - - BUF_T *response_buf = buf_create(); - write_diffusion_string_value(response, response_buf); - diffusion_respond_to_request(session, handle, response_buf, NULL); - buf_free(response_buf); - - return HANDLER_SUCCESS; -} - -static int -on_response(DIFFUSION_DATATYPE response_datatype, const DIFFUSION_VALUE_T *response, void *context) -{ - char *response_val; - read_diffusion_string_value(response, &response_val, NULL); - printf("Response received: %s\n\n", response_val); - free(response_val); - - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - char *request_path = hash_get(options, "request_path"); - - /* - * Create a session with Diffusion. - */ - SESSION_T *session = NULL; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Create a payload. - */ - char *request_data = hash_get(options, "request"); - response = hash_get(options, "response"); - - BUF_T *request = buf_create(); - - write_diffusion_string_value(request_data, request); - - DIFFUSION_REQUEST_STREAM_T request_stream = { - .on_request = on_request - }; - - set_request_stream(session, request_path, DATATYPE_STRING, DATATYPE_STRING, &request_stream); - - SEND_REQUEST_TO_SESSION_PARAMS_T params = { - .recipient_session = session->id, - .path = request_path, - .request = request, - .request_datatype = DATATYPE_STRING, - .response_datatype = DATATYPE_STRING, - .on_response = on_response - }; - - int counter = 1; - - while (counter <= 120) { - printf("Sending request.. #%d\n", counter); - send_request_to_session(session, params); - sleep(1); - ++counter; - } - - session_close(session, NULL); - session_free(session); - buf_free(request); - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} \ No newline at end of file +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example shows how a request can be sent to another client via + * a request path endpoint. The session ID of the target client must be + * known. + */ + +#include + +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +char *response; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "request_path", "Request path", ARG_REQUIRED, ARG_HAS_VALUE, "echo"}, + {'d', "request", "Request to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client request!"}, + {'r', "response", "Response to send", ARG_REQUIRED, ARG_HAS_VALUE, "hello client response!"}, + END_OF_ARG_OPTS +}; + +static int +on_request(SESSION_T *session, const char *request_path, DIFFUSION_DATATYPE request_datatype, + const DIFFUSION_VALUE_T *request, const DIFFUSION_RESPONDER_HANDLE_T *handle, void *context) +{ + + char *request_val; + read_diffusion_string_value(request, &request_val, NULL); + printf("Request received: %s\n", request_val); + free(request_val); + + BUF_T *response_buf = buf_create(); + write_diffusion_string_value(response, response_buf); + diffusion_respond_to_request(session, handle, response_buf, NULL); + buf_free(response_buf); + + return HANDLER_SUCCESS; +} + +static int +on_response(DIFFUSION_DATATYPE response_datatype, const DIFFUSION_VALUE_T *response, void *context) +{ + char *response_val; + read_diffusion_string_value(response, &response_val, NULL); + printf("Response received: %s\n\n", response_val); + free(response_val); + + return HANDLER_SUCCESS; +} + +int +main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + char *url = hash_get(options, "url"); + + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + char *request_path = hash_get(options, "request_path"); + + /* + * Create a session with Diffusion. + */ + SESSION_T *session = NULL; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Create a payload. + */ + char *request_data = hash_get(options, "request"); + response = hash_get(options, "response"); + + BUF_T *request = buf_create(); + + write_diffusion_string_value(request_data, request); + + DIFFUSION_REQUEST_STREAM_T request_stream = { + .on_request = on_request + }; + + set_request_stream(session, request_path, DATATYPE_STRING, DATATYPE_STRING, &request_stream); + + SEND_REQUEST_TO_SESSION_PARAMS_T params = { + .recipient_session = session->id, + .path = request_path, + .request = request, + .request_datatype = DATATYPE_STRING, + .response_datatype = DATATYPE_STRING, + .on_response = on_response + }; + + int counter = 1; + + while (counter <= 120) { + printf("Sending request.. #%d\n", counter); + send_request_to_session(session, params); + sleep(1); + ++counter; + } + + session_close(session, NULL); + session_free(session); + buf_free(request); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/features/remote_servers/remote-servers.c b/c/features/remote_servers/remote-servers.c new file mode 100644 index 00000000..4b751a73 --- /dev/null +++ b/c/features/remote_servers/remote-servers.c @@ -0,0 +1,276 @@ +/** + * Copyright © 2021 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.7 + */ + + +// This example creates, lists, checks and removes a remote server. +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "admin"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +static char *get_connection_option_string( + DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_T option) +{ + switch(option) { + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RECONNECTION_TIMEOUT: + return "reconnection_timeout"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RETRY_DELAY: + return "retry_delay"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RECOVERY_BUFFER_SIZE: + return "recovery_buffer_size"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_INPUT_BUFFER_SIZE: + return "input_buffer_size"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_OUTPUT_BUFFER_SIZE: + return "output_buffer_size"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_MAXIMUM_QUEUE_SIZE: + return "maximum_queue_size"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_CONNECTION_TIMEOUT: + return "connection_timeout"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_WRITE_TIMEOUT: + return "write_timeout"; + default: + return "unknown"; + } +} + + +static void print_connection_options( + HASH_NUM_T *connection_options) +{ + unsigned long *keys = hash_num_keys(connection_options); + for (unsigned long i = 0; i < connection_options->size; i++) { + char *val = (char *)hash_num_get(connection_options, keys[i]); + printf("\t%s: %s\n", get_connection_option_string(keys[i]), val); + } + free(keys); +} + + +static void print_remote_server( + DIFFUSION_REMOTE_SERVER_T *remote_server) +{ + printf("Name: %s\n", remote_server->name); + printf("URL: %s\n", remote_server->url); + printf("Principal: %s\n", remote_server->principal); + + printf("Connection Options:\n"); + print_connection_options(remote_server->connection_options); +} + + +static int on_remote_server_created( + DIFFUSION_REMOTE_SERVER_T *remote_server, + LIST_T *errors, + void *context) +{ + if (remote_server == NULL) { + printf("The following errors occurred while creating the remote server:\n"); + for (int i = 0; i < list_get_size(errors); i++) { + ERROR_REPORT_T *report = list_get_data_indexed(errors, i); + printf("\t[%d, %d] %s\n", report->line, report->column, report->message); + } + } + else { + printf("Remote Server successfully created\n"); + print_remote_server(remote_server); + } + return HANDLER_SUCCESS; +} + + +static int on_remote_servers_listed( + LIST_T *remote_servers, + void *context) +{ + int list_size = list_get_size(remote_servers); + printf("Remote Servers found: %d\n", list_size); + for (int i = 0; i < list_size; i++) { + DIFFUSION_REMOTE_SERVER_T *remote_server = list_get_data_indexed(remote_servers, i); + print_remote_server(remote_server); + printf("\n"); + } + return HANDLER_SUCCESS; +} + + +static char *get_server_state_string( + DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_T state) +{ + switch(state) { + case DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_INACTIVE: + return "inactive"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_CONNECTED: + return "connected"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_RETRYING: + return "retrying"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_FAILED: + return "failed"; + case DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_MISSING: + return "missing"; + default: + return "unknown"; + } +} + + +static int on_remote_server_checked( + DIFFUSION_CHECK_REMOTE_SERVER_RESPONSE_T *response, + void *context) +{ + DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_T state = + diffusion_check_remote_server_response_get_state(response); + + printf("Received remote server status: %s.\n", get_server_state_string(state)); + if (state == DIFFUSION_REMOTE_SERVER_CONNECTION_STATE_FAILED) { + char *failure_message = + diffusion_check_remote_server_response_get_failure_message(response); + printf("Failure message: %s\n", failure_message); + free(failure_message); + } + return HANDLER_SUCCESS; +} + + +static int on_remote_server_removed(void *context) +{ + printf("Remote server has been successfully removed.\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("Error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Create a remote server + HASH_NUM_T *connection_options = hash_num_new(10); + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RECONNECTION_TIMEOUT, "120000"); // milliseconds + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RETRY_DELAY, "2000"); // milliseconds + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_RECOVERY_BUFFER_SIZE, "5000"); + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_INPUT_BUFFER_SIZE, "1024"); // kilobytes + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_OUTPUT_BUFFER_SIZE, "2048"); // kilobytes + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_MAXIMUM_QUEUE_SIZE, "7500"); // milliseconds + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_CONNECTION_TIMEOUT, "120000"); // milliseconds + hash_num_add(connection_options, DIFFUSION_REMOTE_SERVER_CONNECTION_OPTION_WRITE_TIMEOUT, "300000"); // milliseconds + + CREDENTIALS_T *remote_server_credentials = credentials_create_password("password"); + + + // Create the remote server definition in the Diffusion server + DIFFUSION_CREATE_REMOTE_SERVER_PARAMS_T create_remote_server_params = { + .name = "remote server 1", + .url = "ws://localhost:9091", + .principal = "admin", + .credentials = remote_server_credentials, + .connection_options = connection_options, + .on_remote_server_created = on_remote_server_created, + .on_error = on_error + }; + diffusion_create_remote_server(session, create_remote_server_params, NULL); + sleep(2); + + // List all remote servers defined in the Diffusion server + DIFFUSION_LIST_REMOTE_SERVERS_PARAMS_T list_remote_servers_params = { + .on_remote_servers_listed = on_remote_servers_listed, + .on_error = on_error + }; + diffusion_list_remote_servers(session, list_remote_servers_params, NULL); + sleep(2); + + // Check remote server we created. + DIFFUSION_CHECK_REMOTE_SERVER_PARAMS_T check_remote_server_params = { + .name = "remote server 1", + .on_remote_server_checked = on_remote_server_checked, + .on_error = on_error + }; + diffusion_check_remote_server(session, check_remote_server_params, NULL); + sleep(2); + + // Remove remote server we created. + DIFFUSION_REMOVE_REMOTE_SERVER_PARAMS_T remove_remote_server_params = { + .name = "remote server 1", + .on_remote_server_removed = on_remote_server_removed, + .on_error = on_error + }; + diffusion_remove_remote_server(session, remove_remote_server_params, NULL); + sleep(2); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + credentials_free(remote_server_credentials); + hash_free(options, NULL, free); + hash_num_free(connection_options, NULL); + + return EXIT_SUCCESS; +} diff --git a/c/change-principal.c b/c/features/security/change-principal.c similarity index 94% rename from c/change-principal.c rename to c/features/security/change-principal.c index 6caa4b3b..c552cc7c 100644 --- a/c/change-principal.c +++ b/c/features/security/change-principal.c @@ -1,121 +1,121 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This client shows how the principal (e.g. username) can be changed during - * an active session. - */ - -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Callback to display that the principal has been successfully - * changed. - */ -static int -on_change_principal(SESSION_T *session, void *context) -{ - printf("Successfully changed the principal.\n"); - return HANDLER_SUCCESS; -} - -/* - * Callback to display an error when attempting to change the - * principal. - */ -static int -on_change_principal_failure(SESSION_T *session, void *context) -{ - printf("Failed to change the principal\n"); - return HANDLER_SUCCESS; -} - -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - - // Create a session with Diffusion, with no principal or credentials. - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, NULL, NULL, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "Failed to create session: %s\n", error.message); - return EXIT_FAILURE; - } - - // Wait for a couple of seconds. - sleep(2); - - puts("Changing credentials"); - - CREDENTIALS_T *credentials = credentials_create_password(hash_get(options, "credentials")); - - // Specify callbacks for the change_principal request. - CHANGE_PRINCIPAL_PARAMS_T params = { - .principal = hash_get(options, "principal"), - .credentials = credentials, - .on_change_principal = on_change_principal, - .on_change_principal_failure = on_change_principal_failure - }; - - // Do the change. - change_principal(session, params); - - // Wait for a couple more seconds. - sleep(2); - - puts("Closing session"); - - // Gracefully close the connection. - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This client shows how the principal (e.g. username) can be changed during + * an active session. + */ + +#include +#include +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + +/* + * Callback to display that the principal has been successfully + * changed. + */ +static int +on_change_principal(SESSION_T *session, void *context) +{ + printf("Successfully changed the principal.\n"); + return HANDLER_SUCCESS; +} + +/* + * Callback to display an error when attempting to change the + * principal. + */ +static int +on_change_principal_failure(SESSION_T *session, void *context) +{ + printf("Failed to change the principal\n"); + return HANDLER_SUCCESS; +} + +int +main(int argc, char** argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + char *url = hash_get(options, "url"); + + // Create a session with Diffusion, with no principal or credentials. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, NULL, NULL, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "Failed to create session: %s\n", error.message); + return EXIT_FAILURE; + } + + // Wait for a couple of seconds. + sleep(2); + + puts("Changing credentials"); + + CREDENTIALS_T *credentials = credentials_create_password(hash_get(options, "credentials")); + + // Specify callbacks for the change_principal request. + CHANGE_PRINCIPAL_PARAMS_T params = { + .principal = hash_get(options, "principal"), + .credentials = credentials, + .on_change_principal = on_change_principal, + .on_change_principal_failure = on_change_principal_failure + }; + + // Do the change. + change_principal(session, params); + + // Wait for a couple more seconds. + sleep(2); + + puts("Closing session"); + + // Gracefully close the connection. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/subscription-control.c b/c/features/subscription_control/subscription-control.c similarity index 81% rename from c/subscription-control.c rename to c/features/subscription_control/subscription-control.c index 16637bc3..144d2f64 100644 --- a/c/subscription-control.c +++ b/c/features/subscription_control/subscription-control.c @@ -1,151 +1,140 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.7 - */ - -/* - * This example waits to be notified of a client connection, and then - * subscribes that client to a named topic. - */ - -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'t', "topic_selector", "Topic selector to subscribe/unsubscribe clients from", ARG_OPTIONAL, ARG_HAS_VALUE, ">foo"}, - END_OF_ARG_OPTS -}; -HASH_T *options = NULL; - -/* - * Callback invoked when a client has been successfully subscribed to - * a topic. - */ -static int -on_subscription_complete(SESSION_T *session, void *context) -{ - printf("Subscription complete\n"); - return HANDLER_SUCCESS; -} - -/* - * Callback invoked when a client session has been opened. - */ -static int -on_session_open(SESSION_T *session, const SESSION_PROPERTIES_EVENT_T *request, void *context) -{ - if(session_id_cmp(*session->id, request->session_id) == 0) { - // It's our own session, ignore. - return HANDLER_SUCCESS; - } - - char *topic_selector = hash_get(options, "topic_selector"); - - char *sid_str = session_id_to_string(&request->session_id); - printf("Subscribing session %s to topic selector %s\n", sid_str, topic_selector); - free(sid_str); - - /* - * Subscribe the client session to the topic. - */ - SUBSCRIPTION_CONTROL_PARAMS_T subscribe_params = { - .session_id = request->session_id, - .topic_selector = topic_selector, - .on_complete = on_subscription_complete - }; - subscribe_client(session, subscribe_params); - - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - /* - * Create a session with Diffusion. - */ - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "Failed to create session: %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Register a session properties listener, so we are notified - * of new client connections. - * In the callback, we will subscribe the client to topics - * according to the topic_selector argument. - */ - SET_T *required_properties = set_new_string(1); - set_add(required_properties, PROPERTIES_SELECTOR_ALL_FIXED_PROPERTIES); - SESSION_PROPERTIES_REGISTRATION_PARAMS_T params = { - .on_session_open = on_session_open, - .required_properties = required_properties - }; - session_properties_listener_register(session, params); - set_free(required_properties); - - /* - * Pretend to do some work. - */ - sleep(10); - - /* - * Close session and tidy up. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.7 + */ + +/* + * This example waits to be notified of a client connection, and then + * subscribes that client to a named topic. + */ + +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic_selector", "Topic selector to subscribe/unsubscribe clients from", ARG_OPTIONAL, ARG_HAS_VALUE, ">foo"}, + END_OF_ARG_OPTS +}; + +HASH_T *options = NULL; + + +// Callback invoked when a client has been successfully subscribed to a topic. +static int on_subscription_complete( + SESSION_T *session, + void *context) +{ + printf("Subscription complete\n"); + return HANDLER_SUCCESS; +} + + +// Callback invoked when a client session has been opened. +static int on_session_open( + SESSION_T *session, + const SESSION_PROPERTIES_EVENT_T *request, + void *context) +{ + if(session_id_cmp(*session->id, request->session_id) == 0) { + // It's our own session, ignore. + return HANDLER_SUCCESS; + } + + char *topic_selector = hash_get(options, "topic_selector"); + + char *sid_str = session_id_to_string(&request->session_id); + printf("Subscribing session %s to topic selector %s\n", sid_str, topic_selector); + free(sid_str); + + // Subscribe the client session to the topic. + SUBSCRIPTION_CONTROL_PARAMS_T subscribe_params = { + .session_id = request->session_id, + .topic_selector = topic_selector, + .on_complete = on_subscription_complete + }; + subscribe_client(session, subscribe_params); + + return HANDLER_SUCCESS; +} + + +int main(int argc, char **argv) +{ + // Standard command-line parsing. + options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with Diffusion. + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "Failed to create session: %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Register a session properties listener, so we are notified + * of new client connections. + * In the callback, we will subscribe the client to topics + * according to the topic_selector argument. + */ + SET_T *required_properties = set_new_string(1); + set_add(required_properties, PROPERTIES_SELECTOR_ALL_FIXED_PROPERTIES); + SESSION_PROPERTIES_REGISTRATION_PARAMS_T params = { + .on_session_open = on_session_open, + .required_properties = required_properties + }; + session_properties_listener_register(session, params); + set_free(required_properties); + + // Pretend to do some work. + sleep(10); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/system-auth-control.c b/c/features/system_authentication_control/system-auth-control.c similarity index 72% rename from c/system-auth-control.c rename to c/features/system_authentication_control/system-auth-control.c index bd2849e3..23c7592e 100644 --- a/c/system-auth-control.c +++ b/c/features/system_authentication_control/system-auth-control.c @@ -1,165 +1,144 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.5 - */ - -/* - * This examples demonstrates how to interact with the system - * authentication store. - */ - -#include - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * This callback is invoked when the system authentication store is - * received, and prints the contents of the store. - */ -int -on_get_system_authentication_store(SESSION_T *session, - const SYSTEM_AUTHENTICATION_STORE_T store, - void *context) -{ - puts("on_get_system_authentication_store()"); - - printf("Got %ld principals\n", store.system_principals->size); - - char **names = get_principal_names(store); - for(char **name = names; *name != NULL; name++) { - printf("Principal: %s\n", *name); - - char **roles = get_roles_for_principal(store, *name); - for(char **role = roles; *role != NULL; role++) { - printf(" |- Role: %s\n", *role); - } - free(roles); - } - free(names); - - switch(store.anonymous_connection_action) { - case ANONYMOUS_CONNECTION_ACTION_ALLOW: - puts("Allow anonymous connections"); - break; - case ANONYMOUS_CONNECTION_ACTION_DENY: - puts("Deny anonymous connections"); - break; - case ANONYMOUS_CONNECTION_ACTION_ABSTAIN: - puts("Abstain from making anonymous connection decision"); - break; - } - - puts("Anonymous connection roles:"); - char **roles = get_anonymous_roles(store); - for(char **role = roles; *role != NULL; role++) { - printf(" |- Role: %s\n", *role); - } - free(roles); - - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - /* - * Setup for condition variable - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with Diffusion. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Request the system authentication store. - */ - const GET_SYSTEM_AUTHENTICATION_STORE_PARAMS_T params = { - .on_get = on_get_system_authentication_store - }; - - apr_thread_mutex_lock(mutex); - - get_system_authentication_store(session, params); - - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - /* - * Close the session and tidy up. - */ - session_close(session, NULL); - session_free(session); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.5 + */ + +/* + * This examples demonstrates how to interact with the system + * authentication store. + */ + +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "admin"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +/* + * This callback is invoked when the system authentication store is + * received, and prints the contents of the store. + */ +int on_get_system_authentication_store( + SESSION_T *session, + const SYSTEM_AUTHENTICATION_STORE_T store, + void *context) +{ + puts("Received System Authentication Store"); + + printf("Got %ld principals\n", store.system_principals->size); + + char **names = get_principal_names(store); + for(char **name = names; *name != NULL; name++) { + printf("Principal: %s\n", *name); + + char **roles = get_roles_for_principal(store, *name); + for(char **role = roles; *role != NULL; role++) { + printf(" |- Role: %s\n", *role); + } + free(roles); + } + free(names); + + switch(store.anonymous_connection_action) { + case ANONYMOUS_CONNECTION_ACTION_ALLOW: + puts("Allow anonymous connections"); + break; + case ANONYMOUS_CONNECTION_ACTION_DENY: + puts("Deny anonymous connections"); + break; + case ANONYMOUS_CONNECTION_ACTION_ABSTAIN: + puts("Abstain from making anonymous connection decision"); + break; + } + + puts("Anonymous connection roles:"); + char **roles = get_anonymous_roles(store); + for(char **role = roles; *role != NULL; role++) { + printf(" |- Role: %s\n", *role); + } + free(roles); + + return HANDLER_SUCCESS; +} + + +int main(int argc, char **argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with Diffusion. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Request the system authentication store. + const GET_SYSTEM_AUTHENTICATION_STORE_PARAMS_T params = { + .on_get = on_get_system_authentication_store + }; + + puts("Requesting System Authentication Store"); + get_system_authentication_store(session, params); + + // Sleep for a while + sleep(5); + + // Close the session and free resources. + puts("Closing session"); + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/time-series-append.c b/c/features/time_series/time-series-append.c similarity index 66% rename from c/time-series-append.c rename to c/features/time_series/time-series-append.c index cb798774..0df2bb28 100644 --- a/c/time-series-append.c +++ b/c/features/time_series/time-series-append.c @@ -1,224 +1,195 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.3 - */ - -/* - * This example creates a Time series topic (of String datatype) and periodically appends - * data to it. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time-series-append"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add topic feature. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("Added topic \"%s\"\n", (const char *)context); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback(const char *topic_name) -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard, - .context = (char *)topic_name - }; - - return callback; -} - -static int on_append(const DIFFUSION_TIME_SERIES_EVENT_METADATA_T *event_metadata, void *context) -{ - printf("time series append success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("time series append error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); - - HASH_T *properties = hash_new(2); - hash_add(properties, DIFFUSION_TIME_SERIES_EVENT_VALUE_TYPE, "string"); - - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_TIME_SERIES); - topic_specification_set_properties(spec, properties); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, spec, callback); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(spec); - hash_free(properties, NULL, NULL); - - time_t end_time = time(NULL) + seconds; - - while(time(NULL) < end_time) { - - /* - * Compose the update content - */ - const time_t time_now = time(NULL); - const char *time_str = ctime(&time_now); - - /* - * Create a BUF_T and write the string datatype value - * into it. - */ - BUF_T *value = buf_create(); - write_diffusion_string_value(time_str, value); - - DIFFUSION_TIME_SERIES_APPEND_PARAMS_T params = { - .on_append = on_append, - .on_error = on_error, - .topic_path = topic_name, - .datatype = DATATYPE_STRING, - .value = value - }; - - /* - * Append to the time series topic - */ - diffusion_time_series_append(session, params, NULL); - buf_free(value); - - sleep(1); - } - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.3 + */ + +/* + * This example creates a Time series topic (of String datatype) and periodically appends + * data to it. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time-series-append"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + +/* + * Handlers for add topic feature. + */ +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_append( + const DIFFUSION_TIME_SERIES_EVENT_METADATA_T *event_metadata, + void *context) +{ + printf("time series append success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("time series append error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + + HASH_T *properties = hash_new(2); + hash_add(properties, DIFFUSION_TIME_SERIES_EVENT_VALUE_TYPE, "string"); + + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_TIME_SERIES); + topic_specification_set_properties(spec, properties); + + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + hash_free(properties, NULL, NULL); + + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + + // Compose the update content + const time_t time_now = time(NULL); + const char *time_str = ctime(&time_now); + + // Create a BUF_T and write the string datatype value into it. + BUF_T *value = buf_create(); + write_diffusion_string_value(time_str, value); + + DIFFUSION_TIME_SERIES_APPEND_PARAMS_T params = { + .on_append = on_append, + .on_error = on_error, + .topic_path = topic_name, + .datatype = DATATYPE_STRING, + .value = value + }; + + // Append to the time series topic + diffusion_time_series_append(session, params, NULL); + buf_free(value); + + sleep(1); + } + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/features/time_series/time-series-edit.c b/c/features/time_series/time-series-edit.c new file mode 100644 index 00000000..7968c6e1 --- /dev/null +++ b/c/features/time_series/time-series-edit.c @@ -0,0 +1,244 @@ +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.6 + */ + +/* + * This example creates a Time series topic (of String datatype), appends a sequence of + * values to it and edits the first value. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time-series-edit"}, + END_OF_ARG_OPTS +}; + +/* + * Handlers for add topic feature. + */ +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_append( + const DIFFUSION_TIME_SERIES_EVENT_METADATA_T *event_metadata, + void *context) +{ + printf("time series append success\n"); + return HANDLER_SUCCESS; +} + + +static int on_edit( + const DIFFUSION_TIME_SERIES_EVENT_METADATA_T *event_metadata, + void *context) +{ + printf("time series edit success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("time series append error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_error_edit( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("time series edit error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static void append_value_to_time_series_topic( + SESSION_T *session, + char *topic_path, + char *value) +{ + BUF_T *buf = buf_create(); + write_diffusion_string_value(value, buf); + + DIFFUSION_TIME_SERIES_APPEND_PARAMS_T params = { + .on_append = on_append, + .on_error = on_error, + .topic_path = topic_path, + .datatype = DATATYPE_STRING, + .value = buf + }; + + /* + * Append to the time series topic + */ + diffusion_time_series_append(session, params, NULL); + + // Sleep for a while + sleep(1); + + buf_free(buf); +} + +/* + * Program entry point. + */ +int main(int argc, char** argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + const char *topic_name = hash_get(options, "topic"); + + /* + * Create a session with the Diffusion server. + */ + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + + HASH_T *properties = hash_new(2); + hash_add(properties, DIFFUSION_TIME_SERIES_EVENT_VALUE_TYPE, "string"); + + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_TIME_SERIES); + topic_specification_set_properties(spec, properties); + + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + hash_free(properties, NULL, NULL); + + /* + * Append 3 values to the time series topic + */ + append_value_to_time_series_topic(session, (char *)topic_name, "hello world!"); + append_value_to_time_series_topic(session, (char *)topic_name, "Diffusion"); + append_value_to_time_series_topic(session, (char *)topic_name, "DiffusionData"); + + /* + * Edit the first event in the times series topic + */ + BUF_T *buf = buf_create(); + write_diffusion_string_value("edited hello world!", buf); + + DIFFUSION_TIME_SERIES_EDIT_PARAMS_T edit_params = { + .on_edit = on_edit, + .on_error = on_error_edit, + .topic_path = topic_name, + .original_sequence = 0, + .datatype = DATATYPE_STRING, + .value = buf + }; + diffusion_time_series_edit(session, edit_params, NULL); + + // Sleep for a while + sleep(5); + + buf_free(buf); + + /* + * Close session and free resources. + */ + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/features/time_series/time-series-range-query.c b/c/features/time_series/time-series-range-query.c new file mode 100644 index 00000000..17568855 --- /dev/null +++ b/c/features/time_series/time-series-range-query.c @@ -0,0 +1,249 @@ +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.6 + */ + +/* + * This example creates a Time series topic (of String datatype), appends a sequence of + * values to it and performs a range query on it. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time-series-range-query"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d) (%d - %s)\n", + (const char *)context, result_code, error->code, error->message); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +// Handlers for appending value to time series topics +static int on_append( + const DIFFUSION_TIME_SERIES_EVENT_METADATA_T *event_metadata, + void *context) +{ + printf("time series append success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("time series append error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Handlers for range query of a time series topic +static int on_query_result( + const DIFFUSION_TIME_SERIES_QUERY_RESULT_T *query_result, + void *context) +{ + LIST_T *events = diffusion_time_series_query_result_get_events(query_result); + int size = diffusion_time_series_query_result_get_selected_count(query_result); + printf("Range query: total results = %d\n", size); + + for(int i = 0; i < size; i++) { + DIFFUSION_TIME_SERIES_EVENT_T *event = list_get_data_indexed(events, i); + + char *author = diffusion_time_series_event_get_author(event); + + char *val; + DIFFUSION_VALUE_T *value = diffusion_time_series_event_get_value(event); + read_diffusion_string_value(value, &val, NULL); + + printf("Range query: [%d] --> [%s] appended the value [%s]\n", i, author, val); + + free(author); + diffusion_value_free(value); + free(val); + } + + list_free(events, (void (*)(void *))diffusion_time_series_event_free); + return HANDLER_SUCCESS; +} + + +// Helper function to append a value to a time series topic +static void append_value_to_time_series_topic( + SESSION_T *session, + char *topic_path, + char *value) +{ + BUF_T *buf = buf_create(); + write_diffusion_string_value(value, buf); + + DIFFUSION_TIME_SERIES_APPEND_PARAMS_T params = { + .on_append = on_append, + .on_error = on_error, + .topic_path = topic_path, + .datatype = DATATYPE_STRING, + .value = buf + }; + + // Append to the time series topic + diffusion_time_series_append(session, params, NULL); + + // Sleep for a while + sleep(1); + + buf_free(buf); +} + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + + HASH_T *properties = hash_new(2); + hash_add(properties, DIFFUSION_TIME_SERIES_EVENT_VALUE_TYPE, "string"); + // increase the retained range for the topic by up to 50 values, default is 10. + hash_add(properties, DIFFUSION_TIME_SERIES_RETAINED_RANGE, "limit 50"); + + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_TIME_SERIES); + topic_specification_set_properties(spec, properties); + + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + hash_free(properties, NULL, NULL); + + // Append an incremental value to the time series topic 20 times + for (int i = 0; i < 20; i++) { + char *value = calloc(20, sizeof(char)); + sprintf(value, "value %0d", i); + append_value_to_time_series_topic(session, (char *)topic_name, value); + free(value); + } + + // Range query from the 6th update for the next 10 updates + // NOTE: the sequence numbers are zero-based. + DIFFUSION_TIME_SERIES_RANGE_QUERY_T *range_query = diffusion_time_series_range_query(); + diffusion_time_series_range_query_from(range_query, 5, NULL); + diffusion_time_series_range_query_next(range_query, 10, NULL); + + DIFFUSION_TIME_SERIES_RANGE_QUERY_PARAMS_T params_range_query = { + .topic_path = topic_name, + .range_query = range_query, + .on_query_result = on_query_result + }; + + diffusion_time_series_select_from(session, params_range_query, NULL); + + // Sleep for a while + sleep(5); + + diffusion_time_series_range_query_free(range_query); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + return EXIT_SUCCESS; +} diff --git a/c/add-topics.c b/c/features/topic_control/add-topics.c similarity index 73% rename from c/add-topics.c rename to c/features/topic_control/add-topics.c index e8673a30..f9222921 100644 --- a/c/add-topics.c +++ b/c/features/topic_control/add-topics.c @@ -1,317 +1,263 @@ -/** - * Copyright © 2014, 2020 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This example shows how to connect to Diffusion as a control client and - * create various topics on the server. - */ - -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "utils.h" - -// Topic selector, selector set delimiter -#define DELIM "////" - -/* - * We use a mutex and a condition variable to help synchronize the - * flow so that it becomes linear and easier to follow the core logic. - */ -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -// Various handlers which are common to all add_topic() functions. -static int -on_topic_added(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("on_topic_added: %s\n", (const char *)context); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("on_topic_add_failed: %s -> %d\n", (const char *)context, result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - puts("on_topic_add_discard"); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_removed(SESSION_T *session, const SVC_TOPIC_REMOVAL_RESPONSE_T *response, void *context) -{ - puts("on_topic_removed"); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_remove_discard(SESSION_T *session, void *context) -{ - puts("on_topic_remove_discard"); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_view_created(const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context) -{ - char *view_name = diffusion_topic_view_get_name(topic_view); - char *spec = diffusion_topic_view_get_specification(topic_view); - - printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); - free(view_name); - free(spec); - - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("Error: %s\n", error->message); - - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback(char *topic_name) -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added, - .on_topic_add_failed_with_specification = on_topic_add_failed, - .on_discard = on_topic_add_discard, - .context = topic_name - }; - - return callback; -} - -/* - * - */ -int main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup for condition variable - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - // Setup for session - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Create a JSON topic. - */ - { - char *json_topic_name = "json"; - TOPIC_SPECIFICATION_T *json_specification = topic_specification_init(TOPIC_TYPE_JSON); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, json_topic_name, json_specification, create_topic_callback(json_topic_name)); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(json_specification); - } - - /* - * Create a topic view which is an alias for the "master" - * topic (its master topic). This is the preferred method of - * creating slave topics. - */ - { - char *master_topic_name = "master"; - TOPIC_SPECIFICATION_T *string_specification = topic_specification_init(TOPIC_TYPE_STRING); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, master_topic_name, string_specification, create_topic_callback(master_topic_name)); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { - .view = "view0", - .specification = "map master to slave", - .on_topic_view_created = on_topic_view_created, - .on_error = on_error - }; - - apr_thread_mutex_lock(mutex); - diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(string_specification); - } - - /* - * This adds a topic with a record containing multiple fields - * of different types. - */ - { - DIFFUSION_RECORDV2_SCHEMA_BUILDER_T *schema_builder = diffusion_recordv2_schema_builder_init(); - diffusion_recordv2_schema_builder_record(schema_builder, "Record1", NULL); - diffusion_recordv2_schema_builder_string(schema_builder, "Field1", NULL); - diffusion_recordv2_schema_builder_integer(schema_builder, "Field2", NULL); - diffusion_recordv2_schema_builder_decimal(schema_builder, "Field3", 2, NULL); - - DIFFUSION_RECORDV2_SCHEMA_T *schema = diffusion_recordv2_schema_builder_build(schema_builder, NULL); - char *schema_string = diffusion_recordv2_schema_as_json_string(schema); - - HASH_T *properties = hash_new(2); - hash_add(properties, DIFFUSION_VALIDATE_VALUES, "true"); - hash_add(properties, DIFFUSION_SCHEMA, schema_string); - - char *recordv2_topic_name = "recordv2"; - TOPIC_SPECIFICATION_T *recordv2_specification = topic_specification_init_with_properties(TOPIC_TYPE_RECORDV2, properties); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, recordv2_topic_name, recordv2_specification, create_topic_callback(recordv2_topic_name)); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - diffusion_recordv2_schema_builder_free(schema_builder); - diffusion_recordv2_schema_free(schema); - free(schema_string); - - topic_specification_free(recordv2_specification); - hash_free(properties, NULL, NULL); - } - - /* - * Create a binary topic - */ - { - char *binary_topic_name = "binary"; - TOPIC_SPECIFICATION_T *binary_specification = topic_specification_init(TOPIC_TYPE_BINARY); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, binary_topic_name, binary_specification, create_topic_callback(binary_topic_name)); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(binary_specification); - } - - /* - * We can also remove topics. - */ - { - puts("Removing topics in 5 seconds..."); - sleep(5); - - TOPIC_REMOVAL_PARAMS_T remove_params = { - .on_removed = on_topic_removed, - .on_discard = on_topic_remove_discard, - .topic_selector = "#json" DELIM "slave" DELIM "recordv2" DELIM "binary" - }; - - apr_thread_mutex_lock(mutex); - topic_removal(session, remove_params); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - } - - /* - * Close our session, and release resources and memory. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This example shows how to connect to Diffusion as a control client and + * create various topics on the server. + */ + +#include +#include +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "utils.h" + +// Topic selector, selector set delimiter +#define DELIM "////" + +static int default_sleep_time = 3; + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + +// Various handlers which are common to all add_topic() functions. +static int +on_topic_added(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) +{ + printf("on_topic_added: %s\n", (const char *)context); + return HANDLER_SUCCESS; +} + +static int +on_topic_add_failed(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) +{ + printf("on_topic_add_failed: %s -> %d\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + +static int +on_topic_add_discard(SESSION_T *session, void *context) +{ + puts("on_topic_add_discard"); + return HANDLER_SUCCESS; +} + +static int +on_topic_removed(SESSION_T *session, const SVC_TOPIC_REMOVAL_RESPONSE_T *response, void *context) +{ + printf("on_topic_removed: removed topic(s).\n"); + return HANDLER_SUCCESS; +} + +static int +on_topic_remove_discard(SESSION_T *session, void *context) +{ + puts("on_topic_remove_discard"); + return HANDLER_SUCCESS; +} + +static int +on_topic_view_created(const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context) +{ + char *view_name = diffusion_topic_view_get_name(topic_view); + char *spec = diffusion_topic_view_get_specification(topic_view); + + printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); + free(view_name); + free(spec); + + return HANDLER_SUCCESS; +} + +static int +on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) +{ + printf("Error: %s\n", error->message); + + return HANDLER_SUCCESS; +} + +static ADD_TOPIC_CALLBACK_T +create_topic_callback(char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added, + .on_topic_add_failed_with_specification = on_topic_add_failed, + .on_discard = on_topic_add_discard, + .context = topic_name + }; + + return callback; +} + +/* + * + */ +int main(int argc, char** argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Setup for session + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Create a JSON topic. + */ + { + char *json_topic_name = "json"; + TOPIC_SPECIFICATION_T *json_specification = topic_specification_init(TOPIC_TYPE_JSON); + + add_topic_from_specification(session, json_topic_name, json_specification, create_topic_callback(json_topic_name)); + sleep(default_sleep_time); + + topic_specification_free(json_specification); + } + + /* + * Create a topic view which is an alias for the "source" + * topic. + */ + { + char *source_topic_name = "source_topic"; + TOPIC_SPECIFICATION_T *string_specification = topic_specification_init(TOPIC_TYPE_STRING); + + add_topic_from_specification(session, source_topic_name, string_specification, create_topic_callback(source_topic_name)); + sleep(default_sleep_time); + + DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { + .view = "view0", + .specification = "map source_topic to topic_view", + .on_topic_view_created = on_topic_view_created, + .on_error = on_error + }; + + diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); + sleep(default_sleep_time); + + topic_specification_free(string_specification); + } + + /* + * This adds a topic with a record containing multiple fields + * of different types. + */ + { + DIFFUSION_RECORDV2_SCHEMA_BUILDER_T *schema_builder = diffusion_recordv2_schema_builder_init(); + diffusion_recordv2_schema_builder_record(schema_builder, "Record1", NULL); + diffusion_recordv2_schema_builder_string(schema_builder, "Field1", NULL); + diffusion_recordv2_schema_builder_integer(schema_builder, "Field2", NULL); + diffusion_recordv2_schema_builder_decimal(schema_builder, "Field3", 2, NULL); + + DIFFUSION_RECORDV2_SCHEMA_T *schema = diffusion_recordv2_schema_builder_build(schema_builder, NULL); + char *schema_string = diffusion_recordv2_schema_as_json_string(schema); + + HASH_T *properties = hash_new(2); + hash_add(properties, DIFFUSION_VALIDATE_VALUES, "true"); + hash_add(properties, DIFFUSION_SCHEMA, schema_string); + + char *recordv2_topic_name = "recordv2"; + TOPIC_SPECIFICATION_T *recordv2_specification = topic_specification_init_with_properties(TOPIC_TYPE_RECORDV2, properties); + + add_topic_from_specification(session, recordv2_topic_name, recordv2_specification, create_topic_callback(recordv2_topic_name)); + sleep(default_sleep_time); + + diffusion_recordv2_schema_builder_free(schema_builder); + diffusion_recordv2_schema_free(schema); + free(schema_string); + + topic_specification_free(recordv2_specification); + hash_free(properties, NULL, NULL); + } + + /* + * Create a binary topic + */ + { + char *binary_topic_name = "binary"; + TOPIC_SPECIFICATION_T *binary_specification = topic_specification_init(TOPIC_TYPE_BINARY); + + add_topic_from_specification(session, binary_topic_name, binary_specification, create_topic_callback(binary_topic_name)); + sleep(default_sleep_time); + + topic_specification_free(binary_specification); + } + + /* + * We can also remove topics. + */ + { + puts("Removing topics in 5 seconds..."); + sleep(5); + + TOPIC_REMOVAL_PARAMS_T remove_params = { + .on_removed = on_topic_removed, + .on_discard = on_topic_remove_discard, + .topic_selector = "#json" DELIM "topic_view" DELIM "recordv2" DELIM "binary" + }; + + topic_removal(session, remove_params); + sleep(default_sleep_time); + } + + /* + * Close our session, and release resources and memory. + */ + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/missing-topic-notification.c b/c/features/topic_control/missing-topic-notification.c similarity index 72% rename from c/missing-topic-notification.c rename to c/features/topic_control/missing-topic-notification.c index ff8c0472..223cdd60 100644 --- a/c/missing-topic-notification.c +++ b/c/features/topic_control/missing-topic-notification.c @@ -1,177 +1,169 @@ -/** - * Copyright © 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.7 - */ - -/* - * This example shows how to register a missing topic notification - * handler and return a missing topic notification response - calling - * missing_topic_proceed() once we've created the topic. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'r', "topic_root", "Topic root to process missing topic notifications on", ARG_OPTIONAL, ARG_HAS_VALUE, "foo"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add_topic_from_specification() function. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - puts("Topic added"); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - puts("Topic add failed"); - printf("Reason code: %d\n", result_code); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - puts("Topic add discarded"); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -/* - * A request has been made for a topic that doesn't exist; create it - * and inform Diffusion that the client's subcription request can - * proceed. - */ -static int -on_missing_topic(SESSION_T *session, const SVC_MISSING_TOPIC_REQUEST_T *request, void *context) -{ - printf("Missing topic: %s\n", request->topic_selector); - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(); - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_JSON); - - add_topic_from_specification(session, request->topic_selector+1, spec, callback); - topic_specification_free(spec); - - // Proceed with the client's subscription to the topic - missing_topic_proceed(session, (SVC_MISSING_TOPIC_REQUEST_T *) request); - - return HANDLER_SUCCESS; -} - -/* - * Entry point for the example. - */ -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - const char *topic_root = hash_get(options, "topic_root"); - - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session != NULL) { - char *session_id = session_id_to_string(session->id); - printf("Session created (state=%d, id=%s)\n", - session_state_get(session), session_id); - free(session_id); - } - else { - printf("Failed to create session: %s\n", error.message); - free(error.message); - return EXIT_FAILURE; - } - - /* - * Register the missing topic handler - */ - MISSING_TOPIC_PARAMS_T handler = { - .on_missing_topic = on_missing_topic, - .topic_path = topic_root, - .context = NULL - }; - - missing_topic_register_handler(session, handler); - - /* - * Run for 5 minutes. - */ - sleep(5 * 60); - - /* - * Close session and clean up. - */ - session_close(session, NULL); - session_free(session); - - hash_free(options, NULL, free); - credentials_free(credentials); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2016 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.7 + */ + +/* + * This example shows how to register a missing topic notification handler. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'r', "topic_root", "Topic root to process missing topic notifications on", ARG_OPTIONAL, ARG_HAS_VALUE, "foo"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add_topic_from_specification() function. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + puts("Topic added"); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + puts("Topic add failed"); + printf("Reason code: %d\n", result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + puts("Topic add discarded"); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback() +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard + }; + + return callback; +} + + +/* + * A request has been made for a topic that doesn't exist. + * This handler will create the missing topic. + */ +static int on_missing_topic( + SESSION_T *session, + const SVC_MISSING_TOPIC_REQUEST_T *request, + void *context) +{ + printf("Missing topic: %s\n", request->topic_selector); + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_JSON); + + add_topic_from_specification(session, request->topic_selector + 1, spec, callback); + topic_specification_free(spec); + + return HANDLER_SUCCESS; +} + + +// Entry point for the example. +int main(int argc, char **argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *topic_root = hash_get(options, "topic_root"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session != NULL) { + char *session_id = session_id_to_string(session->id); + printf("Session created (state=%d, id=%s)\n", + session_state_get(session), session_id); + free(session_id); + } + else { + printf("Failed to create session: %s\n", error.message); + free(error.message); + return EXIT_FAILURE; + } + + // Register the missing topic handler + MISSING_TOPIC_PARAMS_T handler = { + .on_missing_topic = on_missing_topic, + .topic_path = topic_root, + .context = NULL + }; + + missing_topic_register_handler(session, handler); + + // Run for 5 minutes. + sleep(5 * 60); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + hash_free(options, NULL, free); + credentials_free(credentials); + + return EXIT_SUCCESS; +} diff --git a/c/topic-update-add-and-set.c b/c/features/topic_update/topic-update-add-and-set.c similarity index 72% rename from c/topic-update-add-and-set.c rename to c/features/topic_update/topic-update-add-and-set.c index 6e82a40f..477779f8 100644 --- a/c/topic-update-add-and-set.c +++ b/c/features/topic_update/topic-update-add-and-set.c @@ -1,173 +1,142 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example uses the topic update API to add and set a topic. - * The topic is then periodically updated with data. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -static int -on_topic_update_add_and_set(DIFFUSION_TOPIC_CREATION_RESULT_T result, void *context) -{ - if(result == TOPIC_CREATED) { - printf("topic update success: TOPIC_CREATED\n"); - } - else { - printf("topic update success: TOPIC_EXISTS\n"); - } - - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); - time_t end_time = time(NULL) + seconds; - - while(time(NULL) < end_time) { - - /* - * Compose the update content - */ - const time_t time_now = time(NULL); - const char *time_str = ctime(&time_now); - - /* - * Create a BUF_T and write the string datatype value - * into it. - */ - BUF_T *update_buf = buf_create(); - write_diffusion_string_value(time_str, update_buf); - - DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T topic_update_params = { - .topic_path = topic_name, - .specification = spec, - .datatype = DATATYPE_STRING, - .update = update_buf, - .on_topic_update_add_and_set = on_topic_update_add_and_set, - .on_error = on_error - }; - - /* - * Update the topic. - */ - diffusion_topic_update_add_and_set(session, topic_update_params); - buf_free(update_buf); - - sleep(1); - } - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - topic_specification_free(spec); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example uses the topic update API to add and set a topic. + * The topic is then periodically updated with data. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + + +static int on_topic_update_add_and_set( + DIFFUSION_TOPIC_CREATION_RESULT_T result, + void *context) +{ + if(result == TOPIC_CREATED) { + printf("topic update success: TOPIC_CREATED\n"); + } + else { + printf("topic update success: TOPIC_EXISTS\n"); + } + + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + // Compose the update content + const time_t time_now = time(NULL); + const char *time_str = ctime(&time_now); + + // Create a BUF_T and write the string datatype value into it. + BUF_T *update_buf = buf_create(); + write_diffusion_string_value(time_str, update_buf); + + DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T topic_update_params = { + .topic_path = topic_name, + .specification = spec, + .datatype = DATATYPE_STRING, + .update = update_buf, + .on_topic_update_add_and_set = on_topic_update_add_and_set, + .on_error = on_error + }; + + // Update the topic. + diffusion_topic_update_add_and_set(session, topic_update_params); + buf_free(update_buf); + + sleep(1); + } + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + topic_specification_free(spec); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/topic-update-stream.c b/c/features/topic_update/topic-update-stream.c similarity index 66% rename from c/topic-update-stream.c rename to c/features/topic_update/topic-update-stream.c index dc5aa4c5..936b78b5 100644 --- a/c/topic-update-stream.c +++ b/c/features/topic_update/topic-update-stream.c @@ -1,235 +1,199 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example creates a String topic and periodically updates - * the data it contains. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add topic feature. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("Added topic \"%s\"\n", (const char *)context); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback(const char *topic_name) -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard, - .context = (char *)topic_name - }; - - return callback; -} - -static int -on_topic_creation_result(DIFFUSION_TOPIC_CREATION_RESULT_T result, void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, spec, callback); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(spec); - - /* - * Create a new update stream for the topic. - */ - DIFFUSION_TOPIC_UPDATE_STREAM_T *update_stream = - diffusion_topic_update_create_update_stream(session, topic_name, DATATYPE_STRING); - - time_t end_time = time(NULL) + seconds; - - while(time(NULL) < end_time) { - - /* - * Compose the update content. - */ - const time_t time_now = time(NULL); - const char *time_str = ctime(&time_now); - - /* - * Get the update stream's current value. - */ - DIFFUSION_VALUE_T *current_value = diffusion_topic_update_stream_get(update_stream); - if(current_value != NULL) { - char *value; - read_diffusion_string_value(current_value, &value, NULL); - printf("current topic value: %s", value); - diffusion_value_free(current_value); - free(value); - } - - /* - * Create a BUF_T and write the string datatype value - * into it. - */ - BUF_T *update_buf = buf_create(); - write_diffusion_string_value(time_str, update_buf); - - DIFFUSION_TOPIC_UPDATE_STREAM_PARAMS_T update_stream_params = { - .on_topic_creation_result = on_topic_creation_result, - .on_error = on_error - }; - - /* - * Update the topic with the update stream. - */ - diffusion_topic_update_stream_set(session, update_stream, update_buf, update_stream_params); - buf_free(update_buf); - - sleep(1); - } - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - diffusion_topic_update_stream_free(update_stream); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example creates a String topic and periodically updates + * the data it contains. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_creation_result( + DIFFUSION_TOPIC_CREATION_RESULT_T result, + void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + + // Create a new update stream for the topic. + DIFFUSION_TOPIC_UPDATE_STREAM_T *update_stream = + diffusion_topic_update_create_update_stream(session, topic_name, DATATYPE_STRING); + + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + // Compose the update content. + const time_t time_now = time(NULL); + const char *time_str = ctime(&time_now); + + // Get the update stream's current value. + DIFFUSION_VALUE_T *current_value = diffusion_topic_update_stream_get(update_stream); + if(current_value != NULL) { + char *value; + read_diffusion_string_value(current_value, &value, NULL); + printf("current topic value: %s", value); + diffusion_value_free(current_value); + free(value); + } + + // Create a BUF_T and write the string datatype value into it. + BUF_T *update_buf = buf_create(); + write_diffusion_string_value(time_str, update_buf); + + DIFFUSION_TOPIC_UPDATE_STREAM_PARAMS_T update_stream_params = { + .on_topic_creation_result = on_topic_creation_result, + .on_error = on_error + }; + + // Update the topic with the update stream. + diffusion_topic_update_stream_set(session, update_stream, update_buf, update_stream_params); + buf_free(update_buf); + + sleep(1); + } + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + diffusion_topic_update_stream_free(update_stream); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/topic-update-with-constraint.c b/c/features/topic_update/topic-update-with-constraint.c similarity index 63% rename from c/topic-update-with-constraint.c rename to c/features/topic_update/topic-update-with-constraint.c index 2072b730..da5be437 100644 --- a/c/topic-update-with-constraint.c +++ b/c/features/topic_update/topic-update-with-constraint.c @@ -1,258 +1,218 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example creates a String topic and periodically updates - * the data it contains. - * - * In order to perform the update, a constraint is evaluated - in this - * example, the constraint is an acquired session lock. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -DIFFUSION_SESSION_LOCK_T *g_session_lock = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add topic feature. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("Added topic \"%s\"\n", (const char *)context); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback(const char *topic_name) -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard, - .context = (char *)topic_name - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static int -on_lock_acquired(const DIFFUSION_SESSION_LOCK_T *session_lock, void *context) -{ - char *lock_name = diffusion_session_lock_get_name(session_lock); - printf("session lock acquired: %s\n", lock_name); - free(lock_name); - - g_session_lock = diffusion_session_lock_dup(session_lock); - - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, spec, callback); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(spec); - - DIFFUSION_SESSION_LOCK_PARAMS_T lock_params = { - .on_lock_acquired = on_lock_acquired - }; - - /* - * Acquire the session lock. - */ - apr_thread_mutex_lock(mutex); - diffusion_session_lock(session, "topic-update-lock", lock_params); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - /* - * Create the session lock topic update constraint. - */ - DIFFUSION_TOPIC_UPDATE_CONSTRAINT_T *session_lock_constraint = diffusion_topic_update_constraint_locked(g_session_lock); - - time_t end_time = time(NULL) + seconds; - - while(time(NULL) < end_time) { - - /* - * Compose the update content. - */ - const time_t time_now = time(NULL); - const char *time_str = ctime(&time_now); - - /* - * Create a BUF_T and write the string datatype value - * into it. - */ - BUF_T *update_buf = buf_create(); - write_diffusion_string_value(time_str, update_buf); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_name, - .datatype = DATATYPE_STRING, - .update = update_buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - /* - * Update the topic with the constraint to be evaluated. - */ - diffusion_topic_update_set_with_constraint(session, session_lock_constraint, topic_update_params); - buf_free(update_buf); - - sleep(1); - } - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - diffusion_topic_update_constraint_free(session_lock_constraint); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example creates a String topic and periodically updates + * the data it contains. + * + * In order to perform the update, a constraint is evaluated - in this + * example, the constraint is an acquired session lock. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +DIFFUSION_SESSION_LOCK_T *g_session_lock = NULL; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_lock_acquired( + const DIFFUSION_SESSION_LOCK_T *session_lock, + void *context) +{ + char *lock_name = diffusion_session_lock_get_name(session_lock); + printf("session lock acquired: %s\n", lock_name); + free(lock_name); + + g_session_lock = diffusion_session_lock_dup(session_lock); + + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + + DIFFUSION_SESSION_LOCK_PARAMS_T lock_params = { + .on_lock_acquired = on_lock_acquired + }; + + // Acquire the session lock. + diffusion_session_lock(session, "topic-update-lock", lock_params); + + // Sleep for a while + sleep(5); + + // Create the session lock topic update constraint. + DIFFUSION_TOPIC_UPDATE_CONSTRAINT_T *session_lock_constraint = + diffusion_topic_update_constraint_locked(g_session_lock); + + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + // Compose the update content. + const time_t time_now = time(NULL); + const char *time_str = ctime(&time_now); + + // Create a BUF_T and write the string datatype value into it. + BUF_T *update_buf = buf_create(); + write_diffusion_string_value(time_str, update_buf); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_name, + .datatype = DATATYPE_STRING, + .update = update_buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + // Update the topic with the constraint to be evaluated. + diffusion_topic_update_set_with_constraint( + session, + session_lock_constraint, + topic_update_params); + buf_free(update_buf); + + sleep(1); + } + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + diffusion_topic_update_constraint_free(session_lock_constraint); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/features/topic_update/topic-update-with-value-comparison-constraint.c b/c/features/topic_update/topic-update-with-value-comparison-constraint.c new file mode 100644 index 00000000..02f47979 --- /dev/null +++ b/c/features/topic_update/topic-update-with-value-comparison-constraint.c @@ -0,0 +1,256 @@ +/** + * Copyright © 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.10 + */ + +/* + * This example creates a String topic and periodically updates it. + * + * In order to perform the update, a constraint is evaluated - in this + * example, the string topic value is compared with a monotonically increasing Int64 value. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +char *g_topic_value; + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "5"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add and set topic. +static int on_topic_update_add_and_set( + DIFFUSION_TOPIC_CREATION_RESULT_T result, + void *context) +{ + char *topic_path = (char *) context; + printf("Topic %s has been updated.\n\n", topic_path); + + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + char *topic_path = (char *) error->context; + printf("Error while attempting to update topic %s: %s\n\n", topic_path, error->message); + + return HANDLER_SUCCESS; +} + + +// Handler for value stream +static int on_value( + const char* topic_path, + const TOPIC_SPECIFICATION_T *const specification, + const DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + DIFFUSION_API_ERROR api_error; + bool success = read_diffusion_string_value(new_value, &g_topic_value, &api_error); + + if(success) { + printf("[%s] --> %s\n", topic_path, g_topic_value); + } + else { + printf("Error during diffusion value read: %s\n", get_diffusion_api_error_description(api_error)); + diffusion_api_error_free(api_error); + } + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char* topic_path, + const TOPIC_SPECIFICATION_T *specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_unsubscription( + const char* topic_path, + const TOPIC_SPECIFICATION_T *specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + CREDENTIALS_T *credentials = (password != NULL) ? + credentials_create_password(password) : + NULL; + + // Create a session with the Diffusion server. + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // create value stream and subscribe to topic + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_STRING, + .on_value = on_value, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription + }; + add_stream(session, topic_name, &value_stream); + + SUBSCRIPTION_PARAMS_T subscribe_params = { + .topic_selector = topic_name + }; + subscribe(session, subscribe_params); + + // Sleep for a while + sleep(2); + + // create the topic and set its initial value + TOPIC_SPECIFICATION_T *topic_specification = + topic_specification_init(TOPIC_TYPE_STRING); + + BUF_T *value_buf = buf_create(); + write_diffusion_string_value("0", value_buf); + + DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T initial_params = { + .topic_path = (char *) topic_name, + .update = value_buf, + .specification = topic_specification, + .datatype = DATATYPE_STRING, + .on_topic_update_add_and_set = on_topic_update_add_and_set, + .on_error = on_error, + .context = (char *) topic_name + }; + diffusion_topic_update_add_and_set(session, initial_params); + buf_free(value_buf); + + // Sleep for a while + sleep(2); + + // Begin loop, updating the string topic, using a value comparison update constraint + time_t end_time = time(NULL) + seconds; + int64_t comparison_value = 1; + + printf("Loop has started.\n"); + while(time(NULL) < end_time) { + DIFFUSION_UPDATE_CONSTRAINT_VALUE_T *constraint_value = + diffusion_update_constraint_value_from_int64(comparison_value); + + DIFFUSION_TOPIC_UPDATE_CONSTRAINT_T *update_constraint = + diffusion_topic_update_constraint_value_comparison( + DIFFUSION_TOPIC_UPDATE_CONSTRAINT_OPERATOR_LT, + constraint_value + ); + printf( + "Update Constraint --> current topic value (%s) < constraint_value (%ld)\n", + g_topic_value, + (long) comparison_value + ); + + // Write the [comparison_value] in a string to update the topic + char string_value[21]; + sprintf(string_value, "%ld", (long) comparison_value); + + BUF_T *value_update_buf = buf_create(); + write_diffusion_string_value(string_value, value_update_buf); + + DIFFUSION_TOPIC_UPDATE_ADD_AND_SET_PARAMS_T update_params = { + .topic_path = (char *) topic_name, + .update = value_update_buf, + .specification = topic_specification, + .datatype = DATATYPE_STRING, + .on_topic_update_add_and_set = on_topic_update_add_and_set, + .on_error = on_error, + .context = (char *) topic_name + }; + + printf("Updating Topic '%s' with value '%ld'\n", topic_name, (long) comparison_value); + diffusion_topic_update_add_and_set_with_constraint( + session, + update_constraint, + update_params + ); + buf_free(value_update_buf); + diffusion_update_constraint_value_free(constraint_value); + diffusion_topic_update_constraint_free(update_constraint); + + sleep(1); + + comparison_value -= 1; + } + printf("Loop has terminated. Closing session.\n"); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + topic_specification_free(topic_specification); + hash_free(options, NULL, free); + free(g_topic_value); + + return EXIT_SUCCESS; +} diff --git a/c/topic-update.c b/c/features/topic_update/topic-update.c similarity index 65% rename from c/topic-update.c rename to c/features/topic_update/topic-update.c index e34bf057..41219fe2 100644 --- a/c/topic-update.c +++ b/c/features/topic_update/topic-update.c @@ -1,219 +1,185 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.2 - */ - -/* - * This example creates a String topic and periodically updates - * the data it contains. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add topic feature. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("Added topic \"%s\"\n", (const char *)context); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback(const char *topic_name) -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard, - .context = (char *)topic_name - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, spec, callback); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(spec); - - time_t end_time = time(NULL) + seconds; - - while(time(NULL) < end_time) { - - /* - * Compose the update content - */ - const time_t time_now = time(NULL); - const char *time_str = ctime(&time_now); - - /* - * Create a BUF_T and write the string datatype value - * into it. - */ - BUF_T *update_buf = buf_create(); - write_diffusion_string_value(time_str, update_buf); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_name, - .datatype = DATATYPE_STRING, - .update = update_buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - /* - * Update the topic. - */ - diffusion_topic_update_set(session, topic_update_params); - buf_free(update_buf); - - sleep(1); - } - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.2 + */ + +/* + * This example creates a String topic and periodically updates + * the data it contains. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + // Compose the update content + const time_t time_now = time(NULL); + const char *time_str = ctime(&time_now); + + // Create a BUF_T and write the string datatype value into it. + BUF_T *update_buf = buf_create(); + write_diffusion_string_value(time_str, update_buf); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_name, + .datatype = DATATYPE_STRING, + .update = update_buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + // Update the topic. + diffusion_topic_update_set(session, topic_update_params); + buf_free(update_buf); + + sleep(1); + } + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/update-record.c b/c/features/topic_update/update-record.c similarity index 76% rename from c/update-record.c rename to c/features/topic_update/update-record.c index b3a7240a..fd25fc67 100644 --- a/c/update-record.c +++ b/c/features/topic_update/update-record.c @@ -1,260 +1,255 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This example creates simple RecordTopicData with a single Record - * containing two Fields, and updates it every second. - * - * When running this example, it's possible to choose whether - * subscribing clients see a entire contents of the topic with every - * update, or just the fields that have changed (ie, a delta). - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -int active = 0; - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "foo"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -static const char *EMPTY_FIELD_MARKER = "-EMPTY-"; - -/* - * Handlers for adding topics. - */ -static int -on_topic_added(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("Added topic\n"); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("Failed to add topic (%d)\n", result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Connect to the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Add a topic with a simple record topic data structure, - * containing two fields. - */ - DIFFUSION_RECORDV2_SCHEMA_BUILDER_T *schema_builder = diffusion_recordv2_schema_builder_init(); - diffusion_recordv2_schema_builder_record(schema_builder, "SimpleRecord", NULL); - diffusion_recordv2_schema_builder_string(schema_builder, "first", NULL); - diffusion_recordv2_schema_builder_string(schema_builder, "second", NULL); - - DIFFUSION_RECORDV2_SCHEMA_T *schema = diffusion_recordv2_schema_builder_build(schema_builder, NULL); - char *schema_as_string = diffusion_recordv2_schema_as_json_string(schema); - - HASH_T *properties = hash_new(2); - hash_add(properties, DIFFUSION_VALIDATE_VALUES, "true"); - hash_add(properties, DIFFUSION_SCHEMA, schema_as_string); - - TOPIC_SPECIFICATION_T *recordv2_specification = topic_specification_init_with_properties(TOPIC_TYPE_RECORDV2, properties); - - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added, - .on_topic_add_failed_with_specification = on_topic_add_failed, - .on_discard = NULL, - .context = (void *)topic_name - }; - - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, recordv2_specification, callback); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - diffusion_recordv2_schema_builder_free(schema_builder); - diffusion_recordv2_schema_free(schema); - free(schema_as_string); - - topic_specification_free(recordv2_specification); - hash_free(properties, NULL, NULL); - - /* - * Alternately update one field or both every second. - */ - int count1 = 0; - int count2 = 0; - - DIFFUSION_RECORDV2_BUILDER_T *value_builder = diffusion_recordv2_builder_init(); - char **fields = calloc(3, sizeof(char *)); - - time_t end_time = time(NULL) + seconds; - while(time(NULL) < end_time) { - - BUF_T *buf = buf_create(); - - if(count1 % 2 == 0) { - count2++; - } - count1++; - - if(count1 == 5 || count1 == 6) { - fields[0] = (char *)EMPTY_FIELD_MARKER; - fields[1] = (char *)EMPTY_FIELD_MARKER; - } - else { - char count1_string[20]; - char count2_string[20]; - snprintf(count1_string, 20, "%d", count1); - snprintf(count2_string, 20, "%d", count2); - fields[0] = count1_string; - fields[1] = count2_string; - } - - diffusion_recordv2_builder_add_record(value_builder, fields); - void *record_bytes = diffusion_recordv2_builder_build(value_builder); - - if(!write_diffusion_recordv2_value(record_bytes, buf)) { - fprintf(stderr, "Unable to write the recordv2 update\n"); - diffusion_recordv2_builder_free(value_builder); - free(fields); - buf_free(buf); - return EXIT_FAILURE; - } - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_name, - .datatype = DATATYPE_RECORDV2, - .update = buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - /* - * Update the topic. - */ - diffusion_topic_update_set(session, topic_update_params); - - diffusion_recordv2_builder_clear(value_builder); - free(record_bytes); - buf_free(buf); - - sleep(1); - } - - diffusion_recordv2_builder_free(value_builder); - free(fields); - - /* - * Close session and tidy up. - */ - session_close(session, NULL); - session_free(session); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - puts("Done."); - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This example creates simple RecordTopicData with a single Record + * containing two Fields, and updates it every second. + * + * When running this example, it's possible to choose whether + * subscribing clients see a entire contents of the topic with every + * update, or just the fields that have changed (ie, a delta). + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +int active = 0; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "foo"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + +static const char *EMPTY_FIELD_MARKER = "-EMPTY-"; + +// Handlers for adding topics. +static int on_topic_added( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic\n"); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic (%d)\n", result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + const long seconds = atol(hash_get(options, "seconds")); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Connect to the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Add a topic with a simple record topic data structure, containing two fields. + DIFFUSION_RECORDV2_SCHEMA_BUILDER_T *schema_builder = diffusion_recordv2_schema_builder_init(); + diffusion_recordv2_schema_builder_record(schema_builder, "SimpleRecord", NULL); + diffusion_recordv2_schema_builder_string(schema_builder, "first", NULL); + diffusion_recordv2_schema_builder_string(schema_builder, "second", NULL); + + DIFFUSION_RECORDV2_SCHEMA_T *schema = diffusion_recordv2_schema_builder_build(schema_builder, NULL); + char *schema_as_string = diffusion_recordv2_schema_as_json_string(schema); + + HASH_T *properties = hash_new(2); + hash_add(properties, DIFFUSION_VALIDATE_VALUES, "true"); + hash_add(properties, DIFFUSION_SCHEMA, schema_as_string); + + TOPIC_SPECIFICATION_T *recordv2_specification = topic_specification_init_with_properties(TOPIC_TYPE_RECORDV2, properties); + + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added, + .on_topic_add_failed_with_specification = on_topic_add_failed, + .on_discard = NULL, + .context = (void *)topic_name + }; + + add_topic_from_specification(session, topic_name, recordv2_specification, callback); + + // Sleep for a while + sleep(5); + + diffusion_recordv2_schema_builder_free(schema_builder); + diffusion_recordv2_schema_free(schema); + free(schema_as_string); + + topic_specification_free(recordv2_specification); + hash_free(properties, NULL, NULL); + + // Alternately update one field or both every second. + int count1 = 0; + int count2 = 0; + + DIFFUSION_RECORDV2_BUILDER_T *value_builder = diffusion_recordv2_builder_init(); + char **fields = calloc(3, sizeof(char *)); + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + BUF_T *buf = buf_create(); + + if(count1 % 2 == 0) { + count2++; + } + count1++; + + char *count1_string = NULL; + char *count2_string = NULL; + + if(count1 == 5 || count1 == 6) { + fields[0] = (char *)EMPTY_FIELD_MARKER; + fields[1] = (char *)EMPTY_FIELD_MARKER; + } + else { + count1_string = calloc(20, sizeof(char)); + count2_string = calloc(20, sizeof(char)); + snprintf(count1_string, 20, "%d", count1); + snprintf(count2_string, 20, "%d", count2); + fields[0] = count1_string; + fields[1] = count2_string; + } + + diffusion_recordv2_builder_add_record(value_builder, fields); + void *record_bytes = diffusion_recordv2_builder_build(value_builder); + + if(!write_diffusion_recordv2_value(record_bytes, buf)) { + fprintf(stderr, "Unable to write the recordv2 update\n"); + + // free resources + diffusion_recordv2_builder_free(value_builder); + + free(fields); + + buf_free(buf); + + if (count1_string != NULL) { + free(count1_string); + } + + if (count2_string != NULL) { + free(count2_string); + } + + return EXIT_FAILURE; + } + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_name, + .datatype = DATATYPE_RECORDV2, + .update = buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + // Update the topic. + diffusion_topic_update_set(session, topic_update_params); + + // Sleep for a while. + sleep(3); + + diffusion_recordv2_builder_clear(value_builder); + free(record_bytes); + buf_free(buf); + + if (count1_string != NULL) { + free(count1_string); + } + + if (count2_string != NULL) { + free(count2_string); + } + + sleep(1); + } + + diffusion_recordv2_builder_free(value_builder); + free(fields); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + hash_free(options, NULL, free); + + puts("Done."); + return EXIT_SUCCESS; +} diff --git a/c/features/topic_views/topic-views-get.c b/c/features/topic_views/topic-views-get.c new file mode 100644 index 00000000..a95848f4 --- /dev/null +++ b/c/features/topic_views/topic-views-get.c @@ -0,0 +1,319 @@ +/** + * Copyright © 2022 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.8 + */ + +/* + * This example creates multiple topics and corresponding topic views. + * The topic views are listed and we retrieve a topic view. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "source"}, + END_OF_ARG_OPTS +}; + +// Forward declaration +static void print_topic_view(DIFFUSION_TOPIC_VIEW_T *topic_view); + + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + printf("Topic add discarded\n"); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_view_created( + const DIFFUSION_TOPIC_VIEW_T *topic_view, + void *context) +{ + char *view_name = diffusion_topic_view_get_name(topic_view); + char *spec = diffusion_topic_view_get_specification(topic_view); + + printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); + + free(view_name); + free(spec); + + return HANDLER_SUCCESS; +} + + +static int on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) +{ + printf("Error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Handlers for listing Topic views +static int on_topic_views_list( + const LIST_T *topic_views, + void *context) +{ + int size = list_get_size(topic_views); + + printf("Total topic views: %d\n", size); + for (int i = 0; i < size; i++) { + DIFFUSION_TOPIC_VIEW_T *topic_view = list_get_data_indexed(topic_views, i); + print_topic_view(topic_view); + } + return HANDLER_SUCCESS; +} + + +static int on_error_list( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("An error has occured while listing Topic Views: (%d) %s\n", error->code, error->message); + + return HANDLER_SUCCESS; +} + + +// Handlers for retrieving the topic view information +static int on_topic_view_get (const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context) +{ + printf("Received a topic view.\n"); + print_topic_view((DIFFUSION_TOPIC_VIEW_T *) topic_view); + + return HANDLER_SUCCESS; +} + + +static int on_error_get( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("An error has occured while retrieving a Topic View: (%d) %s\n", error->code, error->message); + + return HANDLER_SUCCESS; +} + + +// Helper functions to create topics and topic views, list, get and print topic views +static void print_topic_view(DIFFUSION_TOPIC_VIEW_T *topic_view) +{ + char *view_name = diffusion_topic_view_get_name(topic_view); + char *view_specification = diffusion_topic_view_get_specification(topic_view); + SET_T *view_roles = diffusion_topic_view_get_roles(topic_view); + + printf("%s: [%s] [", view_name, view_specification); + char **values = (char **) set_values(view_roles); + for(char **value = values; *value != NULL; value++) { + printf("%s ", *value); + } + printf("]\n"); + + free(values); + set_free(view_roles); + free(view_specification); + free(view_name); +} + + +static void create_topic_and_topic_view( + SESSION_T *session, + char *root_topic_path, + char *topic_name, + char *view_name) +{ + char *topic_path = calloc(strlen(root_topic_path) + strlen(topic_name) + 2, sizeof(char)); + sprintf(topic_path, "%s/%s", root_topic_path, topic_name); + + char *topic_view_path = calloc(strlen(view_name) + 7, sizeof(char)); + sprintf(topic_view_path, "views/%s", view_name); + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_path); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + // Create the source topic. + add_topic_from_specification(session, topic_path, spec, callback); + + // Sleep for a while + sleep(5); + + BUF_T *buf = buf_create(); + buf_sprintf(buf, "map %s to %s", topic_path, topic_view_path); + + char *topic_view_spec = buf_as_string(buf); + + DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { + .view = view_name, + .specification = topic_view_spec, + .on_topic_view_created = on_topic_view_created, + .on_error = on_error + }; + + // Send the request to create the topic view. + diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); + + // Sleep for a while + sleep(5); + + // Free resources. + free(topic_view_spec); + buf_free(buf); + topic_specification_free(spec); + free(topic_view_path); + free(topic_path); +} + + +static void list_topic_views(SESSION_T *session) +{ + DIFFUSION_TOPIC_VIEWS_LIST_PARAMS_T params_list = { + .on_topic_views_list = on_topic_views_list, + .on_error = on_error_list + }; + diffusion_topic_views_list_topic_views(session, params_list, NULL); + + // Sleep for a while + sleep(5); +} + + +static void get_topic_view( + SESSION_T *session, + char *view_name) +{ + DIFFUSION_GET_TOPIC_VIEW_PARAMS_T params = { + .name = view_name, + .on_topic_view = on_topic_view_get, + .on_error = on_error_get, + }; + + // Send the request to retrieve the topic view. + diffusion_topic_views_get_topic_view(session, params, NULL); + + // Sleep for a while + sleep(5); +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "Failed to create session\n"); + fprintf(stderr, "%s\n", error.message); + return EXIT_FAILURE; + } + + // Create multiple topics and corresponding topic views + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_1", "view_1"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_2", "view_2"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_3", "view_3"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_4", "view_4"); + + // List the topic views before removal + list_topic_views(session); + + // Get topic view details + get_topic_view(session, "view_1"); + get_topic_view(session, "view_2"); + get_topic_view(session, "view_3"); + get_topic_view(session, "view_4"); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/features/topic_views/topic-views-list.c b/c/features/topic_views/topic-views-list.c new file mode 100644 index 00000000..925a7500 --- /dev/null +++ b/c/features/topic_views/topic-views-list.c @@ -0,0 +1,260 @@ +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.6 + */ + +/* + * This example creates multiple topics and corresponding topic views. + * The method `diffusion_topic_views_list_topic_views` is used to list all + * available topic views. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "source"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + printf("Topic add discarded\n"); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_view_created(const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context) +{ + char *view_name = diffusion_topic_view_get_name(topic_view); + char *spec = diffusion_topic_view_get_specification(topic_view); + + printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); + + free(view_name); + free(spec); + + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("Error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Handlers for listing Topic views +static int on_topic_views_list( + const LIST_T *topic_views, + void *context) +{ + int size = list_get_size(topic_views); + + printf("Total topic views: %d\n", size); + for (int i = 0; i < size; i++) { + DIFFUSION_TOPIC_VIEW_T *topic_view = list_get_data_indexed(topic_views, i); + + char *view_name = diffusion_topic_view_get_name(topic_view); + char *view_specification = diffusion_topic_view_get_specification(topic_view); + SET_T *view_roles = diffusion_topic_view_get_roles(topic_view); + + printf("%s: [%s] [", view_name, view_specification); + char **values = (char **) set_values(view_roles); + for(char **value = values; *value != NULL; value++) { + printf("%s ", *value); + } + printf("]\n"); + + free(values); + set_free(view_roles); + free(view_specification); + free(view_name); + } + return HANDLER_SUCCESS; +} + + +static int on_error_list( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("An error has occured while listing Topic Views: (%d) %s\n", error->code, error->message); + return HANDLER_SUCCESS; +} + + +// Helper functions to create topics and topic views +static void create_topic_and_topic_view( + SESSION_T *session, + char *root_topic_path, + char *topic_name, + char *view_name) { + + char *topic_path = calloc(strlen(root_topic_path) + strlen(topic_name) + 2, sizeof(char)); + sprintf(topic_path, "%s/%s", root_topic_path, topic_name); + + char *topic_view_path = calloc(strlen(view_name) + 7, sizeof(char)); + sprintf(topic_view_path, "views/%s", view_name); + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_path); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + // Create the source topic. + add_topic_from_specification(session, topic_path, spec, callback); + + // Sleep for a while + sleep(5); + + // Create the topic view specification string. + // Maps topic to reference topic. + BUF_T *buf = buf_create(); + buf_sprintf(buf, "map %s to %s", topic_path, topic_view_path); + + char *topic_view_spec = buf_as_string(buf); + + DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { + .view = view_name, + .specification = topic_view_spec, + .on_topic_view_created = on_topic_view_created, + .on_error = on_error + }; + + // Send the request to create the topic view. + diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); + + // Sleep for a while + sleep(5); + + // Free resources. + free(topic_view_spec); + buf_free(buf); + topic_specification_free(spec); + free(topic_view_path); + free(topic_path); +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + const char *topic_name = hash_get(options, "topic"); + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Create multiple topics and corresponding topic views + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_1", "view_1"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_2", "view_2"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_3", "view_3"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_4", "view_4"); + + // List the topic views + DIFFUSION_TOPIC_VIEWS_LIST_PARAMS_T params_list = { + .on_topic_views_list = on_topic_views_list, + .on_error = on_error_list + }; + diffusion_topic_views_list_topic_views(session, params_list, NULL); + + // Sleep for a while + sleep(5); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/features/topic_views/topic-views-remove.c b/c/features/topic_views/topic-views-remove.c new file mode 100644 index 00000000..ae59ff47 --- /dev/null +++ b/c/features/topic_views/topic-views-remove.c @@ -0,0 +1,295 @@ +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.6 + */ + +/* + * This example creates multiple topics and corresponding topic views. + * The topic views are listed before and after removing a topic view. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "source"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + printf("Topic add discarded\n"); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_view_created( + const DIFFUSION_TOPIC_VIEW_T *topic_view, + void *context) +{ + char *view_name = diffusion_topic_view_get_name(topic_view); + char *spec = diffusion_topic_view_get_specification(topic_view); + + printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); + + free(view_name); + free(spec); + + return HANDLER_SUCCESS; +} + + +static int on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) +{ + printf("Error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Handlers for listing Topic views +static int on_topic_views_list(const LIST_T *topic_views, void *context) +{ + int size = list_get_size(topic_views); + + printf("Total topic views: %d\n", size); + for (int i = 0; i < size; i++) { + DIFFUSION_TOPIC_VIEW_T *topic_view = list_get_data_indexed(topic_views, i); + + char *view_name = diffusion_topic_view_get_name(topic_view); + char *view_specification = diffusion_topic_view_get_specification(topic_view); + SET_T *view_roles = diffusion_topic_view_get_roles(topic_view); + + printf("%s: [%s] [", view_name, view_specification); + char **values = (char **) set_values(view_roles); + for(char **value = values; *value != NULL; value++) { + printf("%s ", *value); + } + printf("]\n"); + + free(values); + set_free(view_roles); + free(view_specification); + free(view_name); + } + return HANDLER_SUCCESS; +} + + +static int on_error_list( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("An error has occured while listing Topic Views: (%d) %s\n", error->code, error->message); + return HANDLER_SUCCESS; +} + + +// Handlers for topic view removal +static int on_topic_view_removed(void *context) +{ + printf("Topic view has been removed.\n"); + return HANDLER_SUCCESS; +} + + +static int on_error_remove( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("An error has occured while removing a topic view: (%d) %s\n", error->code, error->message); + return HANDLER_SUCCESS; +} + + +// Helper functions to create topics and topic views and list topic views +static void create_topic_and_topic_view( + SESSION_T *session, + char *root_topic_path, + char *topic_name, + char *view_name) { + + char *topic_path = calloc(strlen(root_topic_path) + strlen(topic_name) + 2, sizeof(char)); + sprintf(topic_path, "%s/%s", root_topic_path, topic_name); + + char *topic_view_path = calloc(strlen(view_name) + 7, sizeof(char)); + sprintf(topic_view_path, "views/%s", view_name); + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_path); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + // Create the source topic. + add_topic_from_specification(session, topic_path, spec, callback); + + // Sleep for a while + sleep(5); + + BUF_T *buf = buf_create(); + buf_sprintf(buf, "map %s to %s", topic_path, topic_view_path); + + char *topic_view_spec = buf_as_string(buf); + + DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { + .view = view_name, + .specification = topic_view_spec, + .on_topic_view_created = on_topic_view_created, + .on_error = on_error + }; + + // Send the request to create the topic view. + diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); + + // Sleep for a while + sleep(5); + + // Free resources. + free(topic_view_spec); + buf_free(buf); + topic_specification_free(spec); + free(topic_view_path); + free(topic_path); +} + + +static void list_topic_views(SESSION_T *session) +{ + DIFFUSION_TOPIC_VIEWS_LIST_PARAMS_T params_list = { + .on_topic_views_list = on_topic_views_list, + .on_error = on_error_list + }; + + diffusion_topic_views_list_topic_views(session, params_list, NULL); + + // Sleep for a while + sleep(5); +} + +// Program entry point +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + const char *topic_name = hash_get(options, "topic"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Create multiple topics and corresponding topic views + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_1", "view_1"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_2", "view_2"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_3", "view_3"); + create_topic_and_topic_view(session, (char *) topic_name, "topic_path_example_4", "view_4"); + + // List the topic views before removal + list_topic_views(session); + + // Remove topic view + DIFFUSION_REMOVE_TOPIC_VIEW_PARAMS_T params_remove = { + .view = "view_2", + .on_topic_view_removed = on_topic_view_removed, + .on_error = on_error_remove + }; + diffusion_topic_views_remove_topic_view(session, params_remove, NULL); + + // Sleep for a while + sleep(5); + + // List the topic views after removal + list_topic_views(session); + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/topic-views.c b/c/features/topic_views/topic-views.c similarity index 68% rename from c/topic-views.c rename to c/features/topic_views/topic-views.c index 19543e41..9346f711 100644 --- a/c/topic-views.c +++ b/c/features/topic_views/topic-views.c @@ -1,305 +1,263 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.3 - */ - -/* - * This example creates a topic view. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "conversation.h" - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "source"}, - {'r', "reference-topic", "Reference topic name to be mapped", ARG_OPTIONAL, ARG_HAS_VALUE, "reference"}, - {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add topic feature. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - printf("Added topic \"%s\"\n", (const char *)context); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - printf("Topic add discarded\n"); - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback(const char *topic_name) -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard, - .context = (char *)topic_name - }; - - return callback; -} - -static int -on_topic_view_created(const DIFFUSION_TOPIC_VIEW_T *topic_view, void *context) -{ - char *view_name = diffusion_topic_view_get_name(topic_view); - char *spec = diffusion_topic_view_get_specification(topic_view); - - printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); - - free(view_name); - free(spec); - return HANDLER_SUCCESS; -} - -static int -on_topic_update(void *context) -{ - printf("Topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_subscription(const char *const topic_path, const TOPIC_SPECIFICATION_T *const specification, void *context) -{ - printf("Subscribed to \"%s\"\n", topic_path); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - char *result; - read_diffusion_string_value(new_value, &result, NULL); - - printf("Value from \"%s\" topic: %s\n", topic_path, result); - - free(result); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("Error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -/* - * Program entry point. - */ -int -main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - const char *reference_topic_name = hash_get(options, "reference-topic"); - const long seconds = atol(hash_get(options, "seconds")); - - /* - * Setup for condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); - TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); - - /* - * Create the source topic. - */ - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, spec, callback); - apr_thread_cond_wait(cond, mutex); - apr_thread_mutex_unlock(mutex); - - topic_specification_free(spec); - - /* - * Create the topic view specification string. - * Maps topic to reference topic. - */ - BUF_T *buf = buf_create(); - buf_sprintf(buf, "map %s to %s", topic_name, reference_topic_name); - - char *topic_view_spec = buf_as_string(buf); - buf_free(buf); - - DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { - .view = "example-view", - .specification = topic_view_spec, - .on_topic_view_created = on_topic_view_created, - .on_error = on_error - }; - - /* - * Send the request to create the topic view. - */ - diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); - free(topic_view_spec); - - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_STRING, - .on_subscription = on_subscription, - .on_value = on_value - }; - - /* - * Add a value stream for the reference topic to - * receive update values. - */ - add_stream(session, reference_topic_name, &value_stream); - - SUBSCRIPTION_PARAMS_T subscribe_params = { - .topic_selector = reference_topic_name, - .on_topic_message = NULL - }; - - /* - * Subscribe to the reference topic. - */ - subscribe(session, subscribe_params); - - time_t end_time = time(NULL) + seconds; - while(time(NULL) < end_time) { - - /* - * Compose the update content - */ - const time_t time_now = time(NULL); - const char *time_str = ctime(&time_now); - - /* - * Create a BUF_T and write the string datatype value - * into it. - */ - BUF_T *value = buf_create(); - write_diffusion_string_value(time_str, value); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_name, - .datatype = DATATYPE_STRING, - .update = value, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - /* - * Update the source topic. - */ - diffusion_topic_update_set(session, topic_update_params); - buf_free(value); - - sleep(1); - } - - /* - * Close session and free resources. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - apr_thread_mutex_destroy(mutex); - apr_thread_cond_destroy(cond); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.3 + */ + +/* + * This example creates a topic view. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "conversation.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "source"}, + {'r', "reference-topic", "Reference topic name to be mapped", ARG_OPTIONAL, ARG_HAS_VALUE, "reference"}, + {'s', "seconds", "Number of seconds to run for before exiting", ARG_OPTIONAL, ARG_HAS_VALUE, "30"}, + END_OF_ARG_OPTS +}; + +// Handlers for add topic feature. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + printf("Added topic \"%s\"\n", (const char *)context); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + printf("Failed to add topic \"%s\" (%d)\n", (const char *)context, result_code); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + printf("Topic add discarded\n"); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback(const char *topic_name) +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard, + .context = (char *)topic_name + }; + + return callback; +} + + +static int on_topic_view_created( + const DIFFUSION_TOPIC_VIEW_T *topic_view, + void *context) +{ + char *view_name = diffusion_topic_view_get_name(topic_view); + char *spec = diffusion_topic_view_get_specification(topic_view); + + printf("Topic view \"%s\" created with specification \"%s\"\n", view_name, spec); + + free(view_name); + free(spec); + return HANDLER_SUCCESS; +} + + +static int on_topic_update(void *context) +{ + printf("Topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + void *context) +{ + printf("Subscribed to \"%s\"\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_value( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + char *result; + read_diffusion_string_value(new_value, &result, NULL); + + printf("Value from \"%s\" topic: %s\n", topic_path, result); + + free(result); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("Error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +// Program entry point. +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + const char *topic_name = hash_get(options, "topic"); + const char *reference_topic_name = hash_get(options, "reference-topic"); + const long seconds = atol(hash_get(options, "seconds")); + + // Create a session with the Diffusion server. + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + ADD_TOPIC_CALLBACK_T callback = create_topic_callback(topic_name); + TOPIC_SPECIFICATION_T *spec = topic_specification_init(TOPIC_TYPE_STRING); + + // Create the source topic. + add_topic_from_specification(session, topic_name, spec, callback); + + // Sleep for a while + sleep(5); + + topic_specification_free(spec); + + // Create the topic view specification string. + // Maps topic to reference topic. + BUF_T *buf = buf_create(); + buf_sprintf(buf, "map %s to %s", topic_name, reference_topic_name); + + char *topic_view_spec = buf_as_string(buf); + buf_free(buf); + + DIFFUSION_CREATE_TOPIC_VIEW_PARAMS_T topic_view_params = { + .view = "example-view", + .specification = topic_view_spec, + .on_topic_view_created = on_topic_view_created, + .on_error = on_error + }; + + // Send the request to create the topic view. + diffusion_topic_views_create_topic_view(session, topic_view_params, NULL); + free(topic_view_spec); + + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_STRING, + .on_subscription = on_subscription, + .on_value = on_value + }; + + // Add a value stream for the reference topic to receive update values. + add_stream(session, reference_topic_name, &value_stream); + + SUBSCRIPTION_PARAMS_T subscribe_params = { + .topic_selector = reference_topic_name, + .on_topic_message = NULL + }; + + // Subscribe to the reference topic. + subscribe(session, subscribe_params); + + time_t end_time = time(NULL) + seconds; + + while(time(NULL) < end_time) { + // Compose the update content + const time_t time_now = time(NULL); + const char *time_str = ctime(&time_now); + + // Create a BUF_T and write the string datatype value into it. + BUF_T *value = buf_create(); + write_diffusion_string_value(time_str, value); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_name, + .datatype = DATATYPE_STRING, + .update = value, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + // Update the source topic. + diffusion_topic_update_set(session, topic_update_params); + buf_free(value); + + sleep(1); + } + + // Close session and free resources. + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/binary-topics.c b/c/features/topics/binary-topics.c similarity index 62% rename from c/binary-topics.c rename to c/features/topics/binary-topics.c index 4d3cbd0a..da16a2f6 100644 --- a/c/binary-topics.c +++ b/c/features/topics/binary-topics.c @@ -1,318 +1,283 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.1 - */ - -/** - * This example shows how to add, subscribe and update a Binary topic - */ - -#include -#include -#include - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "utils.h" - -static const long timeout = 5000; -static const long sleep_timeout = 1000 * 1000; - -char random_bytes[51]; - -apr_pool_t *pool = NULL; - -apr_thread_mutex_t *mutex_add_topic = NULL; -apr_thread_cond_t *cond_add_topic = NULL; - -apr_thread_mutex_t *mutex_value_stream = NULL; -apr_thread_cond_t *cond_value_stream = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add_topic_from_specification() function. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - apr_thread_mutex_lock(mutex_add_topic); - apr_thread_cond_broadcast(cond_add_topic); - apr_thread_mutex_unlock(mutex_add_topic); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - fprintf(stderr, "Failed to add topic: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - fprintf(stderr, "Topic add discarded."); - return HANDLER_SUCCESS; -} - -static int -on_subscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - void *context) -{ - printf("Subscribed to topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_unsubscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - printf("Unsubscribed from topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - if(old_value) { - DIFFUSION_API_ERROR old_value_error; - void *old_binary_value; - if(!read_diffusion_binary_value(old_value, &old_binary_value, &old_value_error)) { - fprintf(stderr, "Error parsing binary old value: %s\n", get_diffusion_api_error_description(old_value_error)); - diffusion_api_error_free(old_value_error); - return HANDLER_SUCCESS; - } - - printf("Old binary value: %s\n", (char *)old_binary_value); - free(old_binary_value); - } - - DIFFUSION_API_ERROR new_value_error; - void *new_binary_value; - if(!read_diffusion_binary_value(new_value, &new_binary_value, &new_value_error)) { - fprintf(stderr, "Error parsing binary new value: %s\n", get_diffusion_api_error_description(new_value_error)); - diffusion_api_error_free(new_value_error); - return HANDLER_SUCCESS; - } - - printf("New binary value: %s\n\n", (char *)new_binary_value); - free(new_binary_value); - - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static void -dispatch_binary_update(SESSION_T *session, const char *topic_path) -{ - // Generate 50 bytes of random values - srand(time(NULL)); - for(unsigned long i = 0; i < sizeof(random_bytes) - 1; i++) { - random_bytes[i] = (char)(33 + (rand() % 94)); - } - - random_bytes[50] = '\0'; - - BUF_T *buf = buf_create(); - if(!write_diffusion_binary_value(random_bytes, buf, sizeof(random_bytes))) { - fprintf(stderr, "Unable to write the binary update\n"); - buf_free(buf); - return; - } - - char *topic_path_dup = strdup(topic_path); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_path_dup, - .datatype = DATATYPE_BINARY, - .update = buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - apr_thread_mutex_lock(mutex_value_stream); - diffusion_topic_update_set(session, topic_update_params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting for value stream on_value callback\n"); - } - apr_thread_mutex_unlock(mutex_value_stream); - - buf_free(buf); - free(topic_path_dup); -} - -static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) -{ - session_close(session, NULL); - session_free(session); - - topic_specification_free(specification); - - apr_thread_mutex_destroy(mutex_add_topic); - apr_thread_cond_destroy(cond_add_topic); - - apr_thread_mutex_destroy(mutex_value_stream); - apr_thread_cond_destroy(cond_value_stream); - - apr_pool_destroy(pool); - apr_terminate(); -} - -int main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *topic_path = "binary-example"; - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup for session - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - apr_initialize(); - apr_pool_create(&pool, NULL); - - apr_thread_mutex_create(&mutex_add_topic, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_add_topic, pool); - - apr_thread_mutex_create(&mutex_value_stream, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_value_stream, pool); - - // Add the binary topic - TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_BINARY); - ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); - - apr_thread_mutex_lock(mutex_add_topic); - add_topic_from_specification(session, topic_path, specification, add_topic_callback); - if(apr_thread_cond_timedwait(cond_add_topic, mutex_add_topic, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to add topic\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_add_topic); - - // Set up and add the value stream to receive binary topic updates - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_BINARY, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value - }; - - add_stream(session, topic_path, &value_stream); - - // Subscribe to the topic path - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = topic_path, - .on_topic_message = NULL - }; - - apr_thread_mutex_lock(mutex_value_stream); - subscribe(session, params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to receive value stream on_subscription callback\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_value_stream); - - // Dispatch 120 binary topic updates at 1 second intervals. - for(int i = 1; i <= 120; i++) { - dispatch_binary_update(session, topic_path); - apr_sleep(sleep_timeout); - } - - // Close our session, and release resources and memory. - tear_down(session, specification); - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.1 + */ + +/** + * This example shows how to add, subscribe and update a Binary topic + */ + +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "utils.h" + + +static const long sleep_timeout = 1; + +char random_bytes[51]; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add_topic_from_specification() function. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + fprintf(stderr, "Failed to add topic: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + fprintf(stderr, "Topic add discarded."); + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_unsubscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_value( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + if(old_value) { + DIFFUSION_API_ERROR old_value_error; + void *old_binary_value; + if(!read_diffusion_binary_value(old_value, &old_binary_value, &old_value_error)) { + fprintf(stderr, "Error parsing binary old value: %s\n", get_diffusion_api_error_description(old_value_error)); + diffusion_api_error_free(old_value_error); + return HANDLER_SUCCESS; + } + + printf("Old binary value: %s\n", (char *)old_binary_value); + free(old_binary_value); + } + + DIFFUSION_API_ERROR new_value_error; + void *new_binary_value; + if(!read_diffusion_binary_value(new_value, &new_binary_value, &new_value_error)) { + fprintf(stderr, "Error parsing binary new value: %s\n", get_diffusion_api_error_description(new_value_error)); + diffusion_api_error_free(new_value_error); + return HANDLER_SUCCESS; + } + + printf("New binary value: %s\n\n", (char *)new_binary_value); + free(new_binary_value); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback() +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static void dispatch_binary_update( + SESSION_T *session, + const char *topic_path) +{ + // Generate 50 bytes of random values + srand(time(NULL)); + for(unsigned long i = 0; i < sizeof(random_bytes) - 1; i++) { + random_bytes[i] = (char)(33 + (rand() % 94)); + } + + random_bytes[50] = '\0'; + + BUF_T *buf = buf_create(); + if(!write_diffusion_binary_value(random_bytes, buf, sizeof(random_bytes))) { + fprintf(stderr, "Unable to write the binary update\n"); + buf_free(buf); + return; + } + + char *topic_path_dup = strdup(topic_path); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_path_dup, + .datatype = DATATYPE_BINARY, + .update = buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + diffusion_topic_update_set(session, topic_update_params); + + // Sleep for a while + sleep(1); + + buf_free(buf); + free(topic_path_dup); +} + + +static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) +{ + session_close(session, NULL); + session_free(session); + + topic_specification_free(specification); +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *topic_path = "binary-example"; + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Setup for session + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Add the binary topic + TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_BINARY); + ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); + + add_topic_from_specification(session, topic_path, specification, add_topic_callback); + + // Sleep for a while + sleep(5); + + // Set up and add the value stream to receive binary topic updates + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_BINARY, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription, + .on_value = on_value + }; + + add_stream(session, topic_path, &value_stream); + + // Subscribe to the topic path + SUBSCRIPTION_PARAMS_T params = { + .topic_selector = topic_path, + .on_topic_message = NULL + }; + + subscribe(session, params); + + // Sleep for a while + sleep(5); + + // Dispatch 120 binary topic updates at 1 second intervals. + for(int i = 1; i <= 120; i++) { + dispatch_binary_update(session, topic_path); + sleep(sleep_timeout); + } + + // Close our session, and release resources and memory. + tear_down(session, specification); + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/double-topics.c b/c/features/topics/double-topics.c similarity index 57% rename from c/double-topics.c rename to c/features/topics/double-topics.c index af26382b..bd75866f 100644 --- a/c/double-topics.c +++ b/c/features/topics/double-topics.c @@ -1,308 +1,283 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.1 - */ - -/** - * This example shows how to add, subscribe and update a Double topic - */ - -#include -#include - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "utils.h" - -static const long timeout = 5000; -static const long sleep_timeout = 1000 * 1000; - -apr_pool_t *pool = NULL; - -apr_thread_mutex_t *mutex_add_topic = NULL; -apr_thread_cond_t *cond_add_topic = NULL; - -apr_thread_mutex_t *mutex_value_stream = NULL; -apr_thread_cond_t *cond_value_stream = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add_topic_from_specification() function. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - apr_thread_mutex_lock(mutex_add_topic); - apr_thread_cond_broadcast(cond_add_topic); - apr_thread_mutex_unlock(mutex_add_topic); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - fprintf(stderr, "Failed to add topic: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - fprintf(stderr, "Topic add discarded."); - return HANDLER_SUCCESS; -} - -static int -on_subscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - void *context) -{ - printf("Subscribed to topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_unsubscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - printf("Unsubscribed from topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - if(old_value) { - DIFFUSION_API_ERROR old_value_error; - double old_double_value; - if(!read_diffusion_double_value(old_value, &old_double_value, &old_value_error)) { - fprintf(stderr, "Error parsing double old value: %s\n", get_diffusion_api_error_description(old_value_error)); - diffusion_api_error_free(old_value_error); - return HANDLER_SUCCESS; - } - - printf("Old double value: %f\n", old_double_value); - } - - DIFFUSION_API_ERROR new_value_error; - double new_double_value; - if(!read_diffusion_double_value(new_value, &new_double_value, &new_value_error)) { - fprintf(stderr, "Error parsing double new value: %s\n", get_diffusion_api_error_description(new_value_error)); - diffusion_api_error_free(new_value_error); - return HANDLER_SUCCESS; - } - - printf("New double value: %f\n\n", new_double_value); - - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static void -dispatch_double_update(SESSION_T *session, const char *topic_path) -{ - double value = (double)rand()/(double)(RAND_MAX/100); - - BUF_T *buf = buf_create(); - if(!write_diffusion_double_value(value, buf)) { - fprintf(stderr, "Unable to write the double update\n"); - buf_free(buf); - return; - } - - char *topic_path_dup = strdup(topic_path); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_path_dup, - .datatype = DATATYPE_DOUBLE, - .update = buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - apr_thread_mutex_lock(mutex_value_stream); - diffusion_topic_update_set(session, topic_update_params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting for value stream on_value callback\n"); - } - apr_thread_mutex_unlock(mutex_value_stream); - - buf_free(buf); - free(topic_path_dup); -} - -static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) -{ - session_close(session, NULL); - session_free(session); - - topic_specification_free(specification); - - apr_thread_mutex_destroy(mutex_add_topic); - apr_thread_cond_destroy(cond_add_topic); - - apr_thread_mutex_destroy(mutex_value_stream); - apr_thread_cond_destroy(cond_value_stream); - - apr_pool_destroy(pool); - apr_terminate(); -} - -int main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *topic_path = "double-example"; - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup for session - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - apr_initialize(); - apr_pool_create(&pool, NULL); - - apr_thread_mutex_create(&mutex_add_topic, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_add_topic, pool); - - apr_thread_mutex_create(&mutex_value_stream, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_value_stream, pool); - - // Add the double topic - TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_DOUBLE); - ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); - - apr_thread_mutex_lock(mutex_add_topic); - add_topic_from_specification(session, topic_path, specification, add_topic_callback); - if(apr_thread_cond_timedwait(cond_add_topic, mutex_add_topic, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to add topic\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_add_topic); - - // Set up and add the value stream to receive double topic updates - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_DOUBLE, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value - }; - - add_stream(session, topic_path, &value_stream); - - // Subscribe to the topic path - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = topic_path, - .on_topic_message = NULL - }; - - apr_thread_mutex_lock(mutex_value_stream); - subscribe(session, params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to receive value stream on_subscription callback\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_value_stream); - - // Dispatch 120 double topic updates at 1 second intervals. - for(int i = 1; i <= 120; i++) { - dispatch_double_update(session, topic_path); - apr_sleep(sleep_timeout); - } - - // Close our session, and release resources and memory. - tear_down(session, specification); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.1 + */ + +/** + * This example shows how to add, subscribe and update a Double topic + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "utils.h" + + +static const long sleep_timeout = 1; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add_topic_from_specification() function. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + fprintf(stderr, "Failed to add topic: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + fprintf(stderr, "Topic add discarded."); + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_unsubscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_value( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + if(old_value) { + DIFFUSION_API_ERROR old_value_error; + double old_double_value; + if(!read_diffusion_double_value(old_value, &old_double_value, &old_value_error)) { + fprintf( + stderr, + "Error parsing double old value: %s\n", + get_diffusion_api_error_description(old_value_error)); + diffusion_api_error_free(old_value_error); + return HANDLER_SUCCESS; + } + + printf("Old double value: %f\n", old_double_value); + } + + DIFFUSION_API_ERROR new_value_error; + double new_double_value; + if(!read_diffusion_double_value(new_value, &new_double_value, &new_value_error)) { + fprintf( + stderr, + "Error parsing double new value: %s\n", + get_diffusion_api_error_description(new_value_error)); + diffusion_api_error_free(new_value_error); + return HANDLER_SUCCESS; + } + + printf("New double value: %f\n\n", new_double_value); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback() +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static void dispatch_double_update( + SESSION_T *session, + const char *topic_path) +{ + double value = (double)rand()/(double)(RAND_MAX/100); + + BUF_T *buf = buf_create(); + if(!write_diffusion_double_value(value, buf)) { + fprintf(stderr, "Unable to write the double update\n"); + buf_free(buf); + return; + } + + char *topic_path_dup = strdup(topic_path); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_path_dup, + .datatype = DATATYPE_DOUBLE, + .update = buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + diffusion_topic_update_set(session, topic_update_params); + + // Sleep for a while + sleep(1); + + buf_free(buf); + free(topic_path_dup); +} + + +static void tear_down( + SESSION_T *session, + TOPIC_SPECIFICATION_T *specification) +{ + session_close(session, NULL); + session_free(session); + + topic_specification_free(specification); +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *topic_path = "double-example"; + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Setup for session + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Add the double topic + TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_DOUBLE); + ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); + + add_topic_from_specification(session, topic_path, specification, add_topic_callback); + + // Sleep for a while + sleep(5); + + // Set up and add the value stream to receive double topic updates + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_DOUBLE, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription, + .on_value = on_value + }; + + add_stream(session, topic_path, &value_stream); + + // Subscribe to the topic path + SUBSCRIPTION_PARAMS_T params = { + .topic_selector = topic_path, + .on_topic_message = NULL + }; + + subscribe(session, params); + + // Sleep for a while + sleep(5); + + // Dispatch 120 double topic updates at 1 second intervals. + for(int i = 1; i <= 120; i++) { + dispatch_double_update(session, topic_path); + sleep(sleep_timeout); + } + + // Close our session, and release resources and memory. + tear_down(session, specification); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/fetch-request.c b/c/features/topics/fetch-request.c similarity index 96% rename from c/fetch-request.c rename to c/features/topics/fetch-request.c index ee1bdbc5..fb1a8ed0 100644 --- a/c/fetch-request.c +++ b/c/features/topics/fetch-request.c @@ -1,209 +1,209 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.4 - */ - -/* - * This is a sample client which connects to Diffusion and demonstrates - * the following features: - * - * 1. Connect to Diffusion with a username and password. - * 2. Fetch topic state using a user-specified topic path. - */ - -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'t', "topic_path", "Topic path", ARG_REQUIRED, ARG_HAS_VALUE, NULL}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * This callback is used when the session state changes, e.g. when a session - * moves from a "connecting" to a "connected" state, or from "connected" to - * "closed". - */ -static void -on_session_state_changed(SESSION_T *session, const SESSION_STATE_T old_state, const SESSION_STATE_T new_state) -{ - printf("Session state changed from %s (%d) to %s (%d)\n", - session_state_as_string(old_state), old_state, - session_state_as_string(new_state), new_state); - if(new_state == CONNECTED_ACTIVE) { - char *session_id = session_id_to_string(session->id); - printf("Session ID=%s\n", session_id); - free(session_id); - } -} - -static int -on_fetch_result(const DIFFUSION_FETCH_RESULT_T *fetch_result, void *context) -{ - LIST_T *results = diffusion_fetch_result_get_topic_results(fetch_result); - - DIFFUSION_TOPIC_RESULT_T *topic_result = list_get_data_indexed(results, 0); - DIFFUSION_VALUE_T *value = diffusion_topic_result_get_value(topic_result); - - char *topic_path = diffusion_topic_result_get_path(topic_result); - printf("Fetching value from \"%s\"\n", topic_path); - - if(value != NULL) { - switch(diffusion_topic_result_get_topic_type(topic_result)) { - - char *json_value; - int64_t int64_value; - void *binary_value; - double double_value; - char *string_value; - char *recordv2_value; - - case TOPIC_TYPE_JSON: - to_diffusion_json_string(value, &json_value, NULL); - printf("JSON topic type, fetch value: %s\n", json_value); - free(json_value); - break; - case TOPIC_TYPE_INT64: - read_diffusion_int64_value(value, &int64_value, NULL); - printf("Int64 topic type, fetch value: " "%"PRId64 "\n", int64_value); - break; - case TOPIC_TYPE_BINARY: - read_diffusion_binary_value(value, &binary_value, NULL); - printf("Binary topic type, fetch value: %s\n", (char *)binary_value); - free(binary_value); - break; - case TOPIC_TYPE_DOUBLE: - read_diffusion_double_value(value, &double_value, NULL); - printf("Double topic type, fetch value: %f\n", double_value); - break; - case TOPIC_TYPE_STRING: - read_diffusion_string_value(value, &string_value, NULL); - printf("String topic type, fetch value: %s\n", string_value); - free(string_value); - break; - case TOPIC_TYPE_RECORDV2: - diffusion_recordv2_to_string(value, &recordv2_value, NULL); - printf("RecordV2 topic type, fetch value: %s\n", recordv2_value); - free(recordv2_value); - break; - default: - break; - } - } - else { - printf("No fetch value\n"); - } - - list_free(results, (void (*)(void *))diffusion_topic_result_free); - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - char *topic = hash_get(options, "topic_path"); - const char *principal = hash_get(options, "principal"); - - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - /* - * A SESSION_LISTENER_T holds callbacks to inform the client - * about changes to the state. Used here for informational - * purposes only. - */ - SESSION_LISTENER_T session_listener = { - .on_state_changed = &on_session_state_changed - }; - - /* - * Creating a session requires at least a URL. Creating a session - * initiates a connection with Diffusion. - */ - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - session = session_create(url, principal, credentials, &session_listener, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * Create the fetch request - */ - DIFFUSION_FETCH_REQUEST_T *fetch_request = diffusion_fetch_request_init(session); - diffusion_fetch_request_with_values(fetch_request, NULL, NULL); - diffusion_fetch_request_from(fetch_request, topic, NULL); - diffusion_fetch_request_to(fetch_request, topic, NULL); - diffusion_fetch_request_first(fetch_request, 1, NULL); - diffusion_fetch_request_maximum_result_size(fetch_request, 1000, NULL); - diffusion_fetch_request_limit_deep_branches(fetch_request, 3 ,3, NULL); // this limits results to a max depth of 3, with each result having maximum 3 results. - - DIFFUSION_FETCH_REQUEST_PARAMS_T params = { - .topic_selector = topic, - .fetch_request = fetch_request, - .on_fetch_result = on_fetch_result - }; - - /* - * Issue the fetch request. - */ - diffusion_fetch_request_fetch(session, params); - - /* - * Wait for 5 seconds for the results to come in. - */ - sleep(5); - - /* - * Clean up. - */ - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - diffusion_fetch_request_free(fetch_request); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2019 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.4 + */ + +/* + * This is a sample client which connects to Diffusion and demonstrates + * the following features: + * + * 1. Connect to Diffusion with a username and password. + * 2. Fetch topic state using a user-specified topic path. + */ + +#include +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'t', "topic_path", "Topic path", ARG_REQUIRED, ARG_HAS_VALUE, NULL}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + +/* + * This callback is used when the session state changes, e.g. when a session + * moves from a "connecting" to a "connected" state, or from "connected" to + * "closed". + */ +static void +on_session_state_changed(SESSION_T *session, const SESSION_STATE_T old_state, const SESSION_STATE_T new_state) +{ + printf("Session state changed from %s (%d) to %s (%d)\n", + session_state_as_string(old_state), old_state, + session_state_as_string(new_state), new_state); + if(new_state == CONNECTED_ACTIVE) { + char *session_id = session_id_to_string(session->id); + printf("Session ID=%s\n", session_id); + free(session_id); + } +} + +static int +on_fetch_result(const DIFFUSION_FETCH_RESULT_T *fetch_result, void *context) +{ + LIST_T *results = diffusion_fetch_result_get_topic_results(fetch_result); + + DIFFUSION_TOPIC_RESULT_T *topic_result = list_get_data_indexed(results, 0); + DIFFUSION_VALUE_T *value = diffusion_topic_result_get_value(topic_result); + + char *topic_path = diffusion_topic_result_get_path(topic_result); + printf("Fetching value from \"%s\"\n", topic_path); + + if(value != NULL) { + switch(diffusion_topic_result_get_topic_type(topic_result)) { + + char *json_value; + int64_t int64_value; + void *binary_value; + double double_value; + char *string_value; + char *recordv2_value; + + case TOPIC_TYPE_JSON: + to_diffusion_json_string(value, &json_value, NULL); + printf("JSON topic type, fetch value: %s\n", json_value); + free(json_value); + break; + case TOPIC_TYPE_INT64: + read_diffusion_int64_value(value, &int64_value, NULL); + printf("Int64 topic type, fetch value: " "%"PRId64 "\n", int64_value); + break; + case TOPIC_TYPE_BINARY: + read_diffusion_binary_value(value, &binary_value, NULL); + printf("Binary topic type, fetch value: %s\n", (char *)binary_value); + free(binary_value); + break; + case TOPIC_TYPE_DOUBLE: + read_diffusion_double_value(value, &double_value, NULL); + printf("Double topic type, fetch value: %f\n", double_value); + break; + case TOPIC_TYPE_STRING: + read_diffusion_string_value(value, &string_value, NULL); + printf("String topic type, fetch value: %s\n", string_value); + free(string_value); + break; + case TOPIC_TYPE_RECORDV2: + diffusion_recordv2_to_string(value, &recordv2_value, NULL); + printf("RecordV2 topic type, fetch value: %s\n", recordv2_value); + free(recordv2_value); + break; + default: + break; + } + } + else { + printf("No fetch value\n"); + } + + list_free(results, (void (*)(void *))diffusion_topic_result_free); + return HANDLER_SUCCESS; +} + +int +main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + char *url = hash_get(options, "url"); + char *topic = hash_get(options, "topic_path"); + const char *principal = hash_get(options, "principal"); + + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + /* + * A SESSION_LISTENER_T holds callbacks to inform the client + * about changes to the state. Used here for informational + * purposes only. + */ + SESSION_LISTENER_T session_listener = { + .on_state_changed = &on_session_state_changed + }; + + /* + * Creating a session requires at least a URL. Creating a session + * initiates a connection with Diffusion. + */ + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + session = session_create(url, principal, credentials, &session_listener, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * Create the fetch request + */ + DIFFUSION_FETCH_REQUEST_T *fetch_request = diffusion_fetch_request_init(session); + diffusion_fetch_request_with_values(fetch_request, NULL, NULL); + diffusion_fetch_request_from(fetch_request, topic, NULL); + diffusion_fetch_request_to(fetch_request, topic, NULL); + diffusion_fetch_request_first(fetch_request, 1, NULL); + diffusion_fetch_request_maximum_result_size(fetch_request, 1000, NULL); + diffusion_fetch_request_limit_deep_branches(fetch_request, 3 ,3, NULL); // this limits results to a max depth of 3, with each result having maximum 3 results. + + DIFFUSION_FETCH_REQUEST_PARAMS_T params = { + .topic_selector = topic, + .fetch_request = fetch_request, + .on_fetch_result = on_fetch_result + }; + + /* + * Issue the fetch request. + */ + diffusion_fetch_request_fetch(session, params); + + /* + * Wait for 5 seconds for the results to come in. + */ + sleep(5); + + /* + * Clean up. + */ + session_close(session, NULL); + session_free(session); + + credentials_free(credentials); + hash_free(options, NULL, free); + diffusion_fetch_request_free(fetch_request); + + return EXIT_SUCCESS; +} diff --git a/c/int64-topics.c b/c/features/topics/int64-topics.c similarity index 60% rename from c/int64-topics.c rename to c/features/topics/int64-topics.c index f18dc2cf..021f6b73 100644 --- a/c/int64-topics.c +++ b/c/features/topics/int64-topics.c @@ -1,308 +1,276 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.1 - */ - -/** - * This example shows how to add, subscribe and update an Int64 topic - */ - -#include -#include - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "utils.h" - -static const long timeout = 5000; -static const long sleep_timeout = 1000 * 1000; - -apr_pool_t *pool = NULL; - -apr_thread_mutex_t *mutex_add_topic = NULL; -apr_thread_cond_t *cond_add_topic = NULL; - -apr_thread_mutex_t *mutex_value_stream = NULL; -apr_thread_cond_t *cond_value_stream = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add_topic_from_specification() function. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - apr_thread_mutex_lock(mutex_add_topic); - apr_thread_cond_broadcast(cond_add_topic); - apr_thread_mutex_unlock(mutex_add_topic); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - fprintf(stderr, "Failed to add topic: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - fprintf(stderr, "Topic add discarded."); - return HANDLER_SUCCESS; -} - -static int -on_subscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - void *context) -{ - printf("Subscribed to topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_unsubscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - printf("Unsubscribed from topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - if(old_value) { - DIFFUSION_API_ERROR old_value_error; - int64_t old_int64_value; - if(!read_diffusion_int64_value(old_value, &old_int64_value, &old_value_error)) { - fprintf(stderr, "Error parsing int64 old value: %s\n", get_diffusion_api_error_description(old_value_error)); - diffusion_api_error_free(old_value_error); - return HANDLER_SUCCESS; - } - - printf("Old int64 value: " "%"PRId64"" "\n", old_int64_value); - } - - DIFFUSION_API_ERROR new_value_error; - int64_t new_int64_value; - if(!read_diffusion_int64_value(new_value, &new_int64_value, &new_value_error)) { - fprintf(stderr, "Error parsing int64 new value: %s\n", get_diffusion_api_error_description(new_value_error)); - diffusion_api_error_free(new_value_error); - return HANDLER_SUCCESS; - } - - printf("New int64 value: " "%"PRId64"" "\n\n", new_int64_value); - - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static void -dispatch_int64_update(SESSION_T *session, const char *topic_path) -{ - int64_t value = rand(); - - BUF_T *buf = buf_create(); - if(!write_diffusion_int64_value(value, buf)) { - fprintf(stderr, "Unable to write the int64 update\n"); - buf_free(buf); - return; - } - - char *topic_path_dup = strdup(topic_path); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_path_dup, - .datatype = DATATYPE_INT64, - .update = buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - apr_thread_mutex_lock(mutex_value_stream); - diffusion_topic_update_set(session, topic_update_params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting for value stream on_value callback\n"); - } - apr_thread_mutex_unlock(mutex_value_stream); - - buf_free(buf); - free(topic_path_dup); -} - -static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) -{ - session_close(session, NULL); - session_free(session); - - topic_specification_free(specification); - - apr_thread_mutex_destroy(mutex_add_topic); - apr_thread_cond_destroy(cond_add_topic); - - apr_thread_mutex_destroy(mutex_value_stream); - apr_thread_cond_destroy(cond_value_stream); - - apr_pool_destroy(pool); - apr_terminate(); -} - -int main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *topic_path = "int64-example"; - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup for session - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - apr_initialize(); - apr_pool_create(&pool, NULL); - - apr_thread_mutex_create(&mutex_add_topic, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_add_topic, pool); - - apr_thread_mutex_create(&mutex_value_stream, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_value_stream, pool); - - // Add the int64 topic - TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_INT64); - ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); - - apr_thread_mutex_lock(mutex_add_topic); - add_topic_from_specification(session, topic_path, specification, add_topic_callback); - if(apr_thread_cond_timedwait(cond_add_topic, mutex_add_topic, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to add topic\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_add_topic); - - // Set up and add the value stream to receive int64 topic updates - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_INT64, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value - }; - - add_stream(session, topic_path, &value_stream); - - // Subscribe to the topic path - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = topic_path, - .on_topic_message = NULL - }; - - apr_thread_mutex_lock(mutex_value_stream); - subscribe(session, params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to receive value stream on_subscription callback\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_value_stream); - - // Dispatch 120 int64 topic updates at 1 second intervals. - for(int i = 1; i <= 120; i++) { - dispatch_int64_update(session, topic_path); - apr_sleep(sleep_timeout); - } - - // Close our session, and release resources and memory. - tear_down(session, specification); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.1 + */ + +/** + * This example shows how to add, subscribe and update an Int64 topic + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "utils.h" + + +static const long sleep_timeout = 1; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add_topic_from_specification() function. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + fprintf(stderr, "Failed to add topic: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard(SESSION_T *session, void *context) +{ + fprintf(stderr, "Topic add discarded."); + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_unsubscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_value( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + if(old_value) { + DIFFUSION_API_ERROR old_value_error; + int64_t old_int64_value; + if(!read_diffusion_int64_value(old_value, &old_int64_value, &old_value_error)) { + fprintf(stderr, "Error parsing int64 old value: %s\n", get_diffusion_api_error_description(old_value_error)); + diffusion_api_error_free(old_value_error); + return HANDLER_SUCCESS; + } + + printf("Old int64 value: " "%"PRId64"" "\n", old_int64_value); + } + + DIFFUSION_API_ERROR new_value_error; + int64_t new_int64_value; + if(!read_diffusion_int64_value(new_value, &new_int64_value, &new_value_error)) { + fprintf(stderr, "Error parsing int64 new value: %s\n", get_diffusion_api_error_description(new_value_error)); + diffusion_api_error_free(new_value_error); + return HANDLER_SUCCESS; + } + + printf("New int64 value: " "%"PRId64"" "\n\n", new_int64_value); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback() +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static void dispatch_int64_update( + SESSION_T *session, + const char *topic_path) +{ + int64_t value = rand(); + + BUF_T *buf = buf_create(); + if(!write_diffusion_int64_value(value, buf)) { + fprintf(stderr, "Unable to write the int64 update\n"); + buf_free(buf); + return; + } + + char *topic_path_dup = strdup(topic_path); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_path_dup, + .datatype = DATATYPE_INT64, + .update = buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + diffusion_topic_update_set(session, topic_update_params); + + // Sleep for a while + sleep(1); + + buf_free(buf); + free(topic_path_dup); +} + + +static void tear_down( + SESSION_T *session, + TOPIC_SPECIFICATION_T *specification) +{ + session_close(session, NULL); + session_free(session); + + topic_specification_free(specification); +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *topic_path = "int64-example"; + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Setup for session + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Add the int64 topic + TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_INT64); + ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); + + add_topic_from_specification(session, topic_path, specification, add_topic_callback); + + // Sleep for a while + sleep(5); + + // Set up and add the value stream to receive int64 topic updates + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_INT64, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription, + .on_value = on_value + }; + + add_stream(session, topic_path, &value_stream); + + // Subscribe to the topic path + SUBSCRIPTION_PARAMS_T params = { + .topic_selector = topic_path, + .on_topic_message = NULL + }; + + subscribe(session, params); + + + // Sleep for a while + sleep(5); + + // Dispatch 120 int64 topic updates at 1 second intervals. + for(int i = 1; i <= 120; i++) { + dispatch_int64_update(session, topic_path); + sleep(sleep_timeout); + } + + // Close our session, and release resources and memory. + tear_down(session, specification); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/recordv2-topics.c b/c/features/topics/recordv2-topics.c similarity index 63% rename from c/recordv2-topics.c rename to c/features/topics/recordv2-topics.c index 3a836c76..b50f1b18 100644 --- a/c/recordv2-topics.c +++ b/c/features/topics/recordv2-topics.c @@ -1,326 +1,294 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.1 - */ - -/** - * This example shows how to add, subscribe and update a RecordV2 topic. - */ - -#include -#include - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "utils.h" - -static const long timeout = 5000; -static const long sleep_timeout = 1000 * 1000; - -apr_pool_t *pool = NULL; - -apr_thread_mutex_t *mutex_add_topic = NULL; -apr_thread_cond_t *cond_add_topic = NULL; - -apr_thread_mutex_t *mutex_value_stream = NULL; -apr_thread_cond_t *cond_value_stream = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add_topic_from_specification() function. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - apr_thread_mutex_lock(mutex_add_topic); - apr_thread_cond_broadcast(cond_add_topic); - apr_thread_mutex_unlock(mutex_add_topic); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - fprintf(stderr, "Failed to add topic: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - fprintf(stderr, "Topic add discarded."); - return HANDLER_SUCCESS; -} - -static int -on_subscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - void *context) -{ - printf("Subscribed to topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_unsubscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - printf("Unsubscribed from topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - if(old_value) { - DIFFUSION_API_ERROR old_value_error; - char *old_value_string; - if(!diffusion_recordv2_to_string(old_value, &old_value_string, &old_value_error)) { - fprintf(stderr, "Error parsing recordv2 old value to string: %s\n", get_diffusion_api_error_description(old_value_error)); - diffusion_api_error_free(old_value_error); - return HANDLER_SUCCESS; - } - - printf("Old recordv2 value: %s\n", old_value_string); - free(old_value_string); - } - - DIFFUSION_API_ERROR new_value_error; - char *new_value_string; - if(!diffusion_recordv2_to_string(new_value, &new_value_string, &new_value_error)) { - fprintf(stderr, "Error parsing recordv2 new value to string: %s\n", get_diffusion_api_error_description(new_value_error)); - diffusion_api_error_free(new_value_error); - return HANDLER_SUCCESS; - } - - printf("New recordv2 value: %s\n\n", new_value_string); - free(new_value_string); - - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static void -dispatch_recordv2_update(SESSION_T *session, const char *topic_path, int update_number) -{ - // convert the update number into a string - char update_number_string[20]; - snprintf(update_number_string, 20, "%d", update_number); - - DIFFUSION_RECORDV2_BUILDER_T *value_builder = diffusion_recordv2_builder_init(); - char **fields = calloc(5, sizeof(char *)); - fields[0] = update_number_string; - fields[1] = "foo"; - fields[2] = "bar"; - fields[3] = "baz"; - - diffusion_recordv2_builder_add_record(value_builder, fields); - void *record_bytes = diffusion_recordv2_builder_build(value_builder); - - BUF_T *buf = buf_create(); - if(!write_diffusion_recordv2_value(record_bytes, buf)) { - fprintf(stderr, "Unable to write the recordv2 update\n"); - diffusion_recordv2_builder_free(value_builder); - buf_free(buf); - return; - } - - char *topic_path_dup = strdup(topic_path); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_path_dup, - .datatype = DATATYPE_RECORDV2, - .update = buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - apr_thread_mutex_lock(mutex_value_stream); - diffusion_topic_update_set(session, topic_update_params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting for value stream on_value callback\n"); - } - apr_thread_mutex_unlock(mutex_value_stream); - - diffusion_recordv2_builder_free(value_builder); - buf_free(buf); - free(fields); - free(record_bytes); - free(topic_path_dup); -} - -static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) -{ - session_close(session, NULL); - session_free(session); - - topic_specification_free(specification); - - apr_thread_mutex_destroy(mutex_add_topic); - apr_thread_cond_destroy(cond_add_topic); - - apr_thread_mutex_destroy(mutex_value_stream); - apr_thread_cond_destroy(cond_value_stream); - - apr_pool_destroy(pool); - apr_terminate(); -} - -int main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *topic_path = "recordv2-example"; - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup for session - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - apr_initialize(); - apr_pool_create(&pool, NULL); - - apr_thread_mutex_create(&mutex_add_topic, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_add_topic, pool); - - apr_thread_mutex_create(&mutex_value_stream, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_value_stream, pool); - - // Add the recordv2 topic - TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_RECORDV2); - ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); - - apr_thread_mutex_lock(mutex_add_topic); - add_topic_from_specification(session, topic_path, specification, add_topic_callback); - if(apr_thread_cond_timedwait(cond_add_topic, mutex_add_topic, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to add topic\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_add_topic); - - // Set up and add the value stream to receive recordv2 topic updates - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_RECORDV2, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value - }; - - add_stream(session, topic_path, &value_stream); - - // Subscribe to the topic path - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = topic_path, - .on_topic_message = NULL - }; - - apr_thread_mutex_lock(mutex_value_stream); - subscribe(session, params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to receive value stream on_subscription callback\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_value_stream); - - // Dispatch 120 recordv2 topic updates at 1 second intervals. - for(int i = 1; i <= 120; i++) { - dispatch_recordv2_update(session, topic_path, i); - apr_sleep(sleep_timeout); - } - - // Close our session, and release resources and memory. - tear_down(session, specification); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.1 + */ + +/** + * This example shows how to add, subscribe and update a RecordV2 topic. + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "utils.h" + + +static const long sleep_timeout = 1; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add_topic_from_specification() function. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + fprintf(stderr, "Failed to add topic: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + fprintf(stderr, "Topic add discarded."); + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_unsubscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_value( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + if(old_value) { + DIFFUSION_API_ERROR old_value_error; + char *old_value_string; + if(!diffusion_recordv2_to_string(old_value, &old_value_string, &old_value_error)) { + fprintf(stderr, "Error parsing recordv2 old value to string: %s\n", get_diffusion_api_error_description(old_value_error)); + diffusion_api_error_free(old_value_error); + return HANDLER_SUCCESS; + } + + printf("Old recordv2 value: %s\n", old_value_string); + free(old_value_string); + } + + DIFFUSION_API_ERROR new_value_error; + char *new_value_string; + if(!diffusion_recordv2_to_string(new_value, &new_value_string, &new_value_error)) { + fprintf(stderr, "Error parsing recordv2 new value to string: %s\n", get_diffusion_api_error_description(new_value_error)); + diffusion_api_error_free(new_value_error); + return HANDLER_SUCCESS; + } + + printf("New recordv2 value: %s\n\n", new_value_string); + free(new_value_string); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback() +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static void dispatch_recordv2_update( + SESSION_T *session, + const char *topic_path, + int update_number) +{ + // convert the update number into a string + char update_number_string[20]; + snprintf(update_number_string, 20, "%d", update_number); + + DIFFUSION_RECORDV2_BUILDER_T *value_builder = diffusion_recordv2_builder_init(); + char **fields = calloc(5, sizeof(char *)); + fields[0] = update_number_string; + fields[1] = "foo"; + fields[2] = "bar"; + fields[3] = "baz"; + + diffusion_recordv2_builder_add_record(value_builder, fields); + void *record_bytes = diffusion_recordv2_builder_build(value_builder); + + BUF_T *buf = buf_create(); + if(!write_diffusion_recordv2_value(record_bytes, buf)) { + fprintf(stderr, "Unable to write the recordv2 update\n"); + diffusion_recordv2_builder_free(value_builder); + buf_free(buf); + return; + } + + char *topic_path_dup = strdup(topic_path); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_path_dup, + .datatype = DATATYPE_RECORDV2, + .update = buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + diffusion_topic_update_set(session, topic_update_params); + + // Sleep for a while + sleep(1); + + diffusion_recordv2_builder_free(value_builder); + buf_free(buf); + free(fields); + free(record_bytes); + free(topic_path_dup); +} + + +static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) +{ + session_close(session, NULL); + session_free(session); + + topic_specification_free(specification); +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *topic_path = "recordv2-example"; + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Setup for session + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Add the recordv2 topic + TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_RECORDV2); + ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); + + add_topic_from_specification(session, topic_path, specification, add_topic_callback); + + // Sleep for a while + sleep(5); + + // Set up and add the value stream to receive recordv2 topic updates + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_RECORDV2, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription, + .on_value = on_value + }; + + add_stream(session, topic_path, &value_stream); + + // Subscribe to the topic path + SUBSCRIPTION_PARAMS_T params = { + .topic_selector = topic_path, + .on_topic_message = NULL + }; + + subscribe(session, params); + + // Sleep for a while + sleep(5); + + // Dispatch 120 recordv2 topic updates at 1 second intervals. + for(int i = 1; i <= 120; i++) { + dispatch_recordv2_update(session, topic_path, i); + sleep(sleep_timeout); + } + + // Close our session, and release resources and memory. + tear_down(session, specification); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/string-topics.c b/c/features/topics/string-topics.c similarity index 60% rename from c/string-topics.c rename to c/features/topics/string-topics.c index 44e3346a..f616c039 100644 --- a/c/string-topics.c +++ b/c/features/topics/string-topics.c @@ -1,312 +1,282 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.1 - */ - -/** - * This example shows how to add, subscribe and update a String topic. - */ - -#include -#include - -#include -#include -#include - -#include "diffusion.h" -#include "args.h" -#include "utils.h" - -static const long timeout = 5000; -static const long sleep_timeout = 1000 * 1000; - -apr_pool_t *pool = NULL; - -apr_thread_mutex_t *mutex_add_topic = NULL; -apr_thread_cond_t *cond_add_topic = NULL; - -apr_thread_mutex_t *mutex_value_stream = NULL; -apr_thread_cond_t *cond_value_stream = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - END_OF_ARG_OPTS -}; - -/* - * Handlers for add_topic_from_specification() function. - */ -static int -on_topic_added_with_specification(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - apr_thread_mutex_lock(mutex_add_topic); - apr_thread_cond_broadcast(cond_add_topic); - apr_thread_mutex_unlock(mutex_add_topic); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed_with_specification(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - fprintf(stderr, "Failed to add topic: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - fprintf(stderr, "Topic add discarded."); - return HANDLER_SUCCESS; -} - -static int -on_subscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - void *context) -{ - printf("Subscribed to topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_unsubscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - printf("Unsubscribed from topic: %s\n", topic_path); - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - if(old_value) { - DIFFUSION_API_ERROR old_value_error; - char *old_value_string; - if(!read_diffusion_string_value(old_value, &old_value_string, &old_value_error)) { - fprintf(stderr, "Error parsing string old value to string: %s\n", get_diffusion_api_error_description(old_value_error)); - diffusion_api_error_free(old_value_error); - return HANDLER_SUCCESS; - } - - printf("Old string value: %s\n", old_value_string); - free(old_value_string); - } - - DIFFUSION_API_ERROR new_value_error; - char *new_value_string; - if(!read_diffusion_string_value(new_value, &new_value_string, &new_value_error)) { - fprintf(stderr, "Error parsing string new value to string: %s\n", get_diffusion_api_error_description(new_value_error)); - diffusion_api_error_free(new_value_error); - return HANDLER_SUCCESS; - } - - printf("New string value: %s\n\n", new_value_string); - free(new_value_string); - - apr_thread_mutex_lock(mutex_value_stream); - apr_thread_cond_broadcast(cond_value_stream); - apr_thread_mutex_unlock(mutex_value_stream); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added_with_specification, - .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -static int -on_topic_update(void *context) -{ - printf("topic update success\n"); - return HANDLER_SUCCESS; -} - -static int -on_error(SESSION_T *session, const DIFFUSION_ERROR_T *error) -{ - printf("topic update error: %s\n", error->message); - return HANDLER_SUCCESS; -} - -static void -dispatch_string_update(SESSION_T *session, const char *topic_path, int update_number) -{ - // convert the update number into a string - char update_number_string[20]; - snprintf(update_number_string, 20, "%d", update_number); - - BUF_T *buf = buf_create(); - if(!write_diffusion_string_value(update_number_string, buf)) { - fprintf(stderr, "Unable to write the string update\n"); - buf_free(buf); - return; - } - - char *topic_path_dup = strdup(topic_path); - - DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { - .topic_path = topic_path_dup, - .datatype = DATATYPE_STRING, - .update = buf, - .on_topic_update = on_topic_update, - .on_error = on_error - }; - - apr_thread_mutex_lock(mutex_value_stream); - diffusion_topic_update_set(session, topic_update_params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting for value stream on_value callback\n"); - } - apr_thread_mutex_unlock(mutex_value_stream); - - buf_free(buf); - free(topic_path_dup); -} - -static void tear_down(SESSION_T *session, TOPIC_SPECIFICATION_T *specification) -{ - session_close(session, NULL); - session_free(session); - - topic_specification_free(specification); - - apr_thread_mutex_destroy(mutex_add_topic); - apr_thread_cond_destroy(cond_add_topic); - - apr_thread_mutex_destroy(mutex_value_stream); - apr_thread_cond_destroy(cond_value_stream); - - apr_pool_destroy(pool); - apr_terminate(); -} - -int main(int argc, char** argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *topic_path = "string-example"; - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - // Setup for session - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - apr_initialize(); - apr_pool_create(&pool, NULL); - - apr_thread_mutex_create(&mutex_add_topic, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_add_topic, pool); - - apr_thread_mutex_create(&mutex_value_stream, APR_THREAD_MUTEX_DEFAULT, pool); - apr_thread_cond_create(&cond_value_stream, pool); - - // Add the string topic - TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_STRING); - ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); - - apr_thread_mutex_lock(mutex_add_topic); - add_topic_from_specification(session, topic_path, specification, add_topic_callback); - if(apr_thread_cond_timedwait(cond_add_topic, mutex_add_topic, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to add topic\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_add_topic); - - // Set up and add the value stream to receive string topic updates - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_STRING, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value - }; - - add_stream(session, topic_path, &value_stream); - - // Subscribe to the topic path - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = topic_path, - .on_topic_message = NULL - }; - - apr_thread_mutex_lock(mutex_value_stream); - subscribe(session, params); - if(apr_thread_cond_timedwait(cond_value_stream, mutex_value_stream, timeout * 1000) != APR_SUCCESS) { - fprintf(stderr, "Failed to receive value stream on_subscription callback\n"); - tear_down(session, specification); - return EXIT_FAILURE; - } - apr_thread_mutex_unlock(mutex_value_stream); - - // Dispatch 120 string topic updates at 1 second intervals. - for(int i = 1; i <= 120; i++) { - dispatch_string_update(session, topic_path, i); - apr_sleep(sleep_timeout); - } - - // Close our session, and release resources and memory. - tear_down(session, specification); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2018 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.1 + */ + +/** + * This example shows how to add, subscribe and update a String topic. + */ + +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" +#include "utils.h" + + +static const long sleep_timeout = 1; + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + END_OF_ARG_OPTS +}; + + +// Handlers for add_topic_from_specification() function. +static int on_topic_added_with_specification( + SESSION_T *session, + TOPIC_ADD_RESULT_CODE result_code, + void *context) +{ + return HANDLER_SUCCESS; +} + + +static int on_topic_add_failed_with_specification( + SESSION_T *session, + TOPIC_ADD_FAIL_RESULT_CODE result_code, + const DIFFUSION_ERROR_T *error, + void *context) +{ + fprintf(stderr, "Failed to add topic: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static int on_topic_add_discard( + SESSION_T *session, + void *context) +{ + fprintf(stderr, "Topic add discarded."); + return HANDLER_SUCCESS; +} + + +static int on_subscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_unsubscription( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + + +static int on_value( + const char *const topic_path, + const TOPIC_SPECIFICATION_T *const specification, + DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + if(old_value) { + DIFFUSION_API_ERROR old_value_error; + char *old_value_string; + if(!read_diffusion_string_value(old_value, &old_value_string, &old_value_error)) { + fprintf(stderr, "Error parsing string old value to string: %s\n", get_diffusion_api_error_description(old_value_error)); + diffusion_api_error_free(old_value_error); + return HANDLER_SUCCESS; + } + + printf("Old string value: %s\n", old_value_string); + free(old_value_string); + } + + DIFFUSION_API_ERROR new_value_error; + char *new_value_string; + if(!read_diffusion_string_value(new_value, &new_value_string, &new_value_error)) { + fprintf(stderr, "Error parsing string new value to string: %s\n", get_diffusion_api_error_description(new_value_error)); + diffusion_api_error_free(new_value_error); + return HANDLER_SUCCESS; + } + + printf("New string value: %s\n\n", new_value_string); + free(new_value_string); + return HANDLER_SUCCESS; +} + + +static ADD_TOPIC_CALLBACK_T create_topic_callback() +{ + ADD_TOPIC_CALLBACK_T callback = { + .on_topic_added_with_specification = on_topic_added_with_specification, + .on_topic_add_failed_with_specification = on_topic_add_failed_with_specification, + .on_discard = on_topic_add_discard + }; + + return callback; +} + + +static int on_topic_update(void *context) +{ + printf("topic update success\n"); + return HANDLER_SUCCESS; +} + + +static int on_error( + SESSION_T *session, + const DIFFUSION_ERROR_T *error) +{ + printf("topic update error: %s\n", error->message); + return HANDLER_SUCCESS; +} + + +static void dispatch_string_update( + SESSION_T *session, + const char *topic_path, + int update_number) +{ + // convert the update number into a string + char update_number_string[20]; + snprintf(update_number_string, 20, "%d", update_number); + + BUF_T *buf = buf_create(); + if(!write_diffusion_string_value(update_number_string, buf)) { + fprintf(stderr, "Unable to write the string update\n"); + buf_free(buf); + return; + } + + char *topic_path_dup = strdup(topic_path); + + DIFFUSION_TOPIC_UPDATE_SET_PARAMS_T topic_update_params = { + .topic_path = topic_path_dup, + .datatype = DATATYPE_STRING, + .update = buf, + .on_topic_update = on_topic_update, + .on_error = on_error + }; + + diffusion_topic_update_set(session, topic_update_params); + + // Sleep for a while + sleep(1); + + buf_free(buf); + free(topic_path_dup); +} + + +static void tear_down( + SESSION_T *session, + TOPIC_SPECIFICATION_T *specification) +{ + session_close(session, NULL); + session_free(session); + + topic_specification_free(specification); +} + + +int main(int argc, char** argv) +{ + // Standard command-line parsing. + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *topic_path = "string-example"; + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + const char *password = hash_get(options, "credentials"); + + CREDENTIALS_T *credentials = NULL; + if(password != NULL) { + credentials = credentials_create_password(password); + } + + // Setup for session + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + // Add the string topic + TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_STRING); + ADD_TOPIC_CALLBACK_T add_topic_callback = create_topic_callback(); + + add_topic_from_specification(session, topic_path, specification, add_topic_callback); + + // Sleep for a while + sleep(5); + + // Set up and add the value stream to receive string topic updates + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_STRING, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription, + .on_value = on_value + }; + + add_stream(session, topic_path, &value_stream); + + // Subscribe to the topic path + SUBSCRIPTION_PARAMS_T params = { + .topic_selector = topic_path, + .on_topic_message = NULL + }; + + subscribe(session, params); + + // Sleep for a while + sleep(5); + + // Dispatch 120 string topic updates at 1 second intervals. + for(int i = 1; i <= 120; i++) { + dispatch_string_update(session, topic_path, i); + sleep(sleep_timeout); + } + + // Close our session, and release resources and memory. + tear_down(session, specification); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/subscribe-multiple.c b/c/features/topics/subscribe-multiple.c similarity index 93% rename from c/subscribe-multiple.c rename to c/features/topics/subscribe-multiple.c index 6a1eecf9..ab2eeb01 100644 --- a/c/subscribe-multiple.c +++ b/c/features/topics/subscribe-multiple.c @@ -1,141 +1,142 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This example is similar to that in subscribe.c but subscribes to the - * specified topic twice. This is to show that multiple listeners can be - * registered for a topic, or more likely, overlapping topic selectors cause - * multiple callbacks to be issued. - * - * For clarity, many of the callbacks for handling session state have been - * omitted; see subscribe.c for more detailed examples. - */ - -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'t', "topic_selector", "Topic selector", ARG_REQUIRED, ARG_HAS_VALUE, NULL}, - END_OF_ARG_OPTS -}; - -/* - * When a subscribed message is received by the first listener, this - * callback is invoked. - */ -static int -on_topic_message_1(SESSION_T *session, const TOPIC_MESSAGE_T *msg) -{ - printf("First handler: Received message for topic %s\n", msg->name); - printf("Payload: %.*s\n", (int)msg->payload->len, msg->payload->data); - return HANDLER_SUCCESS; -} - -/* - * When a subscribed message is received by the second listener, this - * callback is invoked. - */ -static int -on_topic_message_2(SESSION_T *session, const TOPIC_MESSAGE_T *msg) -{ - printf("Second handler: Received message for topic %s\n", msg->name); - printf("Payload: %.*s\n", (int)msg->payload->len, msg->payload->data); - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - char *url = hash_get(options, "url"); - char *topic = hash_get(options, "topic_selector"); - - /* - * Create a session with Diffusion. - */ - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = NULL; - session = session_create(url, NULL, NULL, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "TEST: Failed to create session\n"); - fprintf(stderr, "ERR : %s\n", error.message); - return EXIT_FAILURE; - } - - /* - * When issuing commands to Diffusion (in this case, subscribe to - * a topic), it's typical that more than one message may be - * received in response and a handler can be installed for each - * message type. - */ - SUBSCRIPTION_PARAMS_T sub_params_1 = { - .topic_selector = topic, - .on_topic_message = on_topic_message_1 - }; - - SUBSCRIPTION_PARAMS_T sub_params_2 = { - .topic_selector = topic, - .on_topic_message = on_topic_message_2 - }; - - /* - * Register two subscription handlers for the same topic. The - * first handler is replaced by the second, so we will only - * see on_topic_message_2() invoked. - */ - TOPIC_HANDLER_T old_handlers = NULL; - if((old_handlers = subscribe(session, sub_params_1)) != NULL) { - puts("Replacing existing handlers for topic selector"); - } - if((old_handlers = subscribe(session, sub_params_2)) != NULL) { - puts("Replacing existing handlers for topic selector"); - } - - /* - * Keep receiving messages for 10 seconds. - */ - sleep(10); - - /* - * Politely close the connection. - */ - session_close(session, NULL); - session_free(session); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This example is similar to that in subscribe.c but subscribes to the + * specified topic twice. This is to show that multiple listeners can be + * registered for a topic, or more likely, overlapping topic selectors cause + * multiple callbacks to be issued. + * + * For clarity, many of the callbacks for handling session state have been + * omitted; see subscribe.c for more detailed examples. + */ + +#include +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'t', "topic_selector", "Topic selector", ARG_REQUIRED, ARG_HAS_VALUE, NULL}, + END_OF_ARG_OPTS +}; + +/* + * When a subscribed message is received by the first listener, this + * callback is invoked. + */ +static int on_topic_message_1( + SESSION_T *session, + const TOPIC_MESSAGE_T *msg) +{ + printf("First handler: Received message for topic %s\n", msg->name); + printf("Payload: %.*s\n", (int)msg->payload->len, msg->payload->data); + return HANDLER_SUCCESS; +} + +/* + * When a subscribed message is received by the second listener, this + * callback is invoked. + */ +static int +on_topic_message_2(SESSION_T *session, const TOPIC_MESSAGE_T *msg) +{ + printf("Second handler: Received message for topic %s\n", msg->name); + printf("Payload: %.*s\n", (int)msg->payload->len, msg->payload->data); + return HANDLER_SUCCESS; +} + +int +main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + char *url = hash_get(options, "url"); + char *topic = hash_get(options, "topic_selector"); + + /* + * Create a session with Diffusion. + */ + DIFFUSION_ERROR_T error = { 0 }; + SESSION_T *session = NULL; + session = session_create(url, NULL, NULL, NULL, NULL, &error); + if(session == NULL) { + fprintf(stderr, "TEST: Failed to create session\n"); + fprintf(stderr, "ERR : %s\n", error.message); + return EXIT_FAILURE; + } + + /* + * When issuing commands to Diffusion (in this case, subscribe to + * a topic), it's typical that more than one message may be + * received in response and a handler can be installed for each + * message type. + */ + SUBSCRIPTION_PARAMS_T sub_params_1 = { + .topic_selector = topic, + .on_topic_message = on_topic_message_1 + }; + + SUBSCRIPTION_PARAMS_T sub_params_2 = { + .topic_selector = topic, + .on_topic_message = on_topic_message_2 + }; + + /* + * Register two subscription handlers for the same topic. The + * first handler is replaced by the second, so we will only + * see on_topic_message_2() invoked. + */ + TOPIC_HANDLER_T old_handlers = NULL; + if((old_handlers = subscribe(session, sub_params_1)) != NULL) { + puts("Replacing existing handlers for topic selector"); + } + if((old_handlers = subscribe(session, sub_params_2)) != NULL) { + puts("Replacing existing handlers for topic selector"); + } + + /* + * Keep receiving messages for 10 seconds. + */ + sleep(10); + + /* + * Politely close the connection. + */ + session_close(session, NULL); + session_free(session); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/subscribe.c b/c/features/topics/subscribe.c similarity index 95% rename from c/subscribe.c rename to c/features/topics/subscribe.c index 7c9d6229..a1003390 100644 --- a/c/subscribe.c +++ b/c/features/topics/subscribe.c @@ -1,179 +1,179 @@ -/** - * Copyright © 2020 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.1 - */ - -/* - * This example shows how to add a JSON value stream and subscribe to a selector. - */ -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'t', "topic", "Topic selector to subscribe to", ARG_REQUIRED, ARG_HAS_VALUE, "time"}, - END_OF_ARG_OPTS -}; - -static int on_subscription(const char* topic_path, - const TOPIC_SPECIFICATION_T *specification, - void *context) -{ - printf("Subscribed to topic: %s\n", topic_path); - return HANDLER_SUCCESS; -} - -static int on_unsubscription(const char* topic_path, - const TOPIC_SPECIFICATION_T *specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - printf("Unsubscribed from topic: %s\n", topic_path); - return HANDLER_SUCCESS; -} - -static int on_value(const char* topic_path, - const TOPIC_SPECIFICATION_T *const specification, - const DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - DIFFUSION_API_ERROR api_error; - char *result; - bool success = to_diffusion_json_string(new_value, &result, &api_error); - - if(success) { - printf("Received value: %s\n", result); - free(result); - return HANDLER_SUCCESS; - } - - printf("Error during diffusion value read: %s\n", - get_diffusion_api_error_description(api_error)); - diffusion_api_error_free(api_error); - return HANDLER_SUCCESS; -} - -static void on_close() -{ - printf("Value stream closed\n"); -} - -/* - * Entry point for the example. - */ -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *selector = hash_get(options, "topic"); - - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - - /* - * Create a session, synchronously. - */ - session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session != NULL) { - char *sid_str = session_id_to_string(session->id); - printf("Session created (state=%d, id=%s)\n", - session_state_get(session), - sid_str); - free(sid_str); - } - else { - printf("Failed to create session: %s\n", error.message); - free(error.message); - credentials_free(credentials); - return EXIT_FAILURE; - } - - /* - * Create a value stream - */ - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_JSON, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value, - .on_close = on_close - }; - - add_stream(session, selector, &value_stream); - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = selector - }; - - /* - * Subscribe to topics matching the selector - */ - subscribe(session, params); - - UNSUBSCRIPTION_PARAMS_T unsub_params = { - .topic_selector = selector - }; - - /* - * Unsubscribe to topics matching the selector - */ - unsubscribe(session, unsub_params); - - /* - * Sleep for 2 seconds. - */ - sleep(2); - - /* - * Close the session, and release resources and memory. - */ - session_close(session, NULL); - session_free(session); - hash_free(options, NULL, free); - - credentials_free(credentials); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2020 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.1 + */ + +/* + * This example shows how to add a JSON value stream and subscribe to a selector. + */ +#include +#include +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'t', "topic", "Topic selector to subscribe to", ARG_REQUIRED, ARG_HAS_VALUE, "time"}, + END_OF_ARG_OPTS +}; + +static int on_subscription(const char* topic_path, + const TOPIC_SPECIFICATION_T *specification, + void *context) +{ + printf("Subscribed to topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + +static int on_unsubscription(const char* topic_path, + const TOPIC_SPECIFICATION_T *specification, + NOTIFY_UNSUBSCRIPTION_REASON_T reason, + void *context) +{ + printf("Unsubscribed from topic: %s\n", topic_path); + return HANDLER_SUCCESS; +} + +static int on_value(const char* topic_path, + const TOPIC_SPECIFICATION_T *const specification, + const DIFFUSION_DATATYPE datatype, + const DIFFUSION_VALUE_T *const old_value, + const DIFFUSION_VALUE_T *const new_value, + void *context) +{ + DIFFUSION_API_ERROR api_error; + char *result; + bool success = to_diffusion_json_string(new_value, &result, &api_error); + + if(success) { + printf("Received value: %s\n", result); + free(result); + return HANDLER_SUCCESS; + } + + printf("Error during diffusion value read: %s\n", + get_diffusion_api_error_description(api_error)); + diffusion_api_error_free(api_error); + return HANDLER_SUCCESS; +} + +static void on_close() +{ + printf("Value stream closed\n"); +} + +/* + * Entry point for the example. + */ +int +main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + const char *selector = hash_get(options, "topic"); + + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + + /* + * Create a session, synchronously. + */ + session = session_create(url, principal, credentials, NULL, NULL, &error); + if(session != NULL) { + char *sid_str = session_id_to_string(session->id); + printf("Session created (state=%d, id=%s)\n", + session_state_get(session), + sid_str); + free(sid_str); + } + else { + printf("Failed to create session: %s\n", error.message); + free(error.message); + credentials_free(credentials); + return EXIT_FAILURE; + } + + /* + * Create a value stream + */ + VALUE_STREAM_T value_stream = { + .datatype = DATATYPE_JSON, + .on_subscription = on_subscription, + .on_unsubscription = on_unsubscription, + .on_value = on_value, + .on_close = on_close + }; + + add_stream(session, selector, &value_stream); + SUBSCRIPTION_PARAMS_T params = { + .topic_selector = selector + }; + + /* + * Subscribe to topics matching the selector + */ + subscribe(session, params); + + UNSUBSCRIPTION_PARAMS_T unsub_params = { + .topic_selector = selector + }; + + /* + * Unsubscribe to topics matching the selector + */ + unsubscribe(session, unsub_params); + + /* + * Sleep for 2 seconds. + */ + sleep(2); + + /* + * Close the session, and release resources and memory. + */ + session_close(session, NULL); + session_free(session); + hash_free(options, NULL, free); + + credentials_free(credentials); + + return EXIT_SUCCESS; +} diff --git a/c/json/json-publishing-example.c b/c/json/json-publishing-example.c deleted file mode 100644 index a16d42ee..00000000 --- a/c/json/json-publishing-example.c +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Copyright © 2017 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.0 - */ - -/* - * The purpose of this example is to show how to update a JSON topic. - */ - -#include -#include -#include -#include -#include - -#ifdef WIN32 -#define sleep(x) Sleep(1000 * x) -#else -#include -#endif - -#include - -#include "diffusion.h" -#include "args.h" - -// 5 seconds -#define SYNC_DEFAULT_TIMEOUT 5000 * 1000 - -int volatile g_active = 0; -CONVERSATION_ID_T volatile *g_updater_id; - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "control" }, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password" }, - {'t', "topic", "Topic name to create and update", ARG_OPTIONAL, ARG_HAS_VALUE, "time" }, - END_OF_ARG_OPTS -}; - -/* - * Handler for processing updater registration callbacks. - */ -static int -register_updater_callback(SESSION_T *session, - const CONVERSATION_ID_T *updater_id, - const SVC_UPDATE_REGISTRATION_RESPONSE_T *response, - void *context) -{ - apr_thread_mutex_lock(mutex); - - switch(response->state) { - case UPDATE_SOURCE_STATE_ACTIVE: - g_active = 1; - break; - default: - g_active = 0; - break; - } - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -/* - * Handlers for update of data. - */ -static int -on_update_success(SESSION_T *session, - const CONVERSATION_ID_T *updater_id, - const SVC_UPDATE_RESPONSE_T *response, - void *context) -{ - char *id_str = conversation_id_to_string(*updater_id); - printf("on_update_success for updater \"%s\"\n", id_str); - free(id_str); - return HANDLER_SUCCESS; -} - -static int -on_update_failure(SESSION_T *session, - const CONVERSATION_ID_T *updater_id, - const SVC_UPDATE_RESPONSE_T *response, - void *context) -{ - char *id_str = conversation_id_to_string(*updater_id); - printf("on_update_failure for updater \"%s\"\n", id_str); - free(id_str); - return HANDLER_SUCCESS; -} - -static int -on_topic_added(SESSION_T *session, TOPIC_ADD_RESULT_CODE result_code, void *context) -{ - apr_thread_mutex_lock(mutex); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_failed(SESSION_T *session, TOPIC_ADD_FAIL_RESULT_CODE result_code, const DIFFUSION_ERROR_T *error, void *context) -{ - printf("on_topic_add_failed, code: %d\n", result_code); - return HANDLER_SUCCESS; -} - -static int -on_topic_add_discard(SESSION_T *session, void *context) -{ - printf("on_topic_add_discard\n"); - return HANDLER_SUCCESS; -} - -static ADD_TOPIC_CALLBACK_T -create_topic_callback() -{ - ADD_TOPIC_CALLBACK_T callback = { - .on_topic_added_with_specification = on_topic_added, - .on_topic_add_failed_with_specification = on_topic_add_failed, - .on_discard = on_topic_add_discard - }; - - return callback; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - - /* - * Setup mutex and condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "Failed to create session: %s\n", error.message); - return EXIT_FAILURE; - } - - ADD_TOPIC_CALLBACK_T callback = create_topic_callback(); - TOPIC_SPECIFICATION_T *specification = topic_specification_init(TOPIC_TYPE_JSON); - - /* - * Synchronously create a topic holding JSON content. - */ - apr_thread_mutex_lock(mutex); - add_topic_from_specification(session, topic_name, specification, callback); - apr_status_t rc = apr_thread_cond_timedwait(cond, mutex, SYNC_DEFAULT_TIMEOUT); - apr_thread_mutex_unlock(mutex); - topic_specification_free(specification); - if(rc != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting for topic to be created\n"); - return EXIT_FAILURE; - } - - /* - * Register an updater for the topic. - */ - const UPDATE_SOURCE_REGISTRATION_PARAMS_T update_reg_params = { - .topic_path = topic_name, - .on_active = register_updater_callback, - .on_standby = register_updater_callback, - .on_close = register_updater_callback - }; - - apr_thread_mutex_lock(mutex); - g_updater_id = register_update_source(session, update_reg_params); - rc = apr_thread_cond_timedwait(cond, mutex, SYNC_DEFAULT_TIMEOUT); - apr_thread_mutex_unlock(mutex); - if(rc != APR_SUCCESS) { - fprintf(stderr, "Timed out while waiting to register an updater\n"); - return EXIT_FAILURE; - } - - /* - * Define default parameters for an update source. - */ - UPDATE_VALUE_PARAMS_T update_value_params_base = { - .updater_id = (CONVERSATION_ID_T *)g_updater_id, - .topic_path = (char *)topic_name, - .on_success = on_update_success, - .on_failure = on_update_failure - }; - - time_t current_time; - struct tm *time_info; - char json[255]; - char format_string[] = "{\"day\":\"%d\",\"month\":\"%m\",\"year\":\"%Y\",\"hour\":\"%H\",\"minute\":\"%M\",\"second\":\"%S\"}"; - - /* - * Forever, until deactivated. - */ - while(g_active) { - // Get current time - current_time = time(NULL); - if(current_time == ((time_t)-1)) { - fprintf(stderr, "Failure to obtain the current time\n"); - return EXIT_FAILURE; - } - - // Get UTC time info - time_info = gmtime( ¤t_time ); - if(time_info == NULL) { - fprintf(stderr, "Failure to obtain UTC time info\n"); - return EXIT_FAILURE; - } - - // Construct JSON string based on current time - if(strftime(json, sizeof(json), format_string, time_info) == 0) { - fprintf(stderr, "Failure to construct JSON value\n"); - return EXIT_FAILURE; - } - - printf("Updated value: %s\n", json); - - // Extract the CBOR-encoded data and wrap it in a BUF_T structure. - BUF_T *buf = buf_create(); - write_diffusion_json_value(json, buf); - - // Issue an update request to Diffusion. Under the covers, - // this transmits a binary delta of changes, assuming those - // changes are smaller than sending the entire value. - UPDATE_VALUE_PARAMS_T update_value_params = update_value_params_base; - update_value_params.data = buf; - - update_value_with_datatype(session, DATATYPE_JSON, update_value_params); - buf_free(buf); - - // Sleep for a second - sleep(1); - } - - puts("Updater not active, exiting."); - - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - apr_thread_cond_destroy(cond); - apr_thread_mutex_destroy(mutex); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} diff --git a/c/json/json-subscribing-example.c b/c/json/json-subscribing-example.c deleted file mode 100644 index f7d3c05f..00000000 --- a/c/json/json-subscribing-example.c +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright © 2017 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 6.0 - */ - -/* - * The purpose of this example is to show how to subscribe to a JSON topic. - */ - -#include -#include - -#ifdef WIN32 -#define sleep(x) Sleep(1000 * x) -#else -#include -#endif - -#include "diffusion.h" -#include "args.h" - -// 5 seconds -#define SYNC_DEFAULT_TIMEOUT 5000 * 1000 - -apr_pool_t *pool = NULL; -apr_thread_mutex_t *mutex = NULL; -apr_thread_cond_t *cond = NULL; - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, - {'t', "topic", "Topic name to subscribe", ARG_OPTIONAL, ARG_HAS_VALUE, "time"}, - END_OF_ARG_OPTS -}; - -static int -on_subscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - void *context) -{ - apr_thread_mutex_lock(mutex); - printf("on_subscription, topic_path: %s\n", topic_path); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_unsubscription(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - NOTIFY_UNSUBSCRIPTION_REASON_T reason, - void *context) -{ - apr_thread_mutex_lock(mutex); - printf("Unsubscribed from topic: %s\n", topic_path); - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -static int -on_value(const char *const topic_path, - const TOPIC_SPECIFICATION_T *const specification, - DIFFUSION_DATATYPE datatype, - const DIFFUSION_VALUE_T *const old_value, - const DIFFUSION_VALUE_T *const new_value, - void *context) -{ - - apr_thread_mutex_lock(mutex); - - DIFFUSION_API_ERROR error; - char *json_value; - if(!to_diffusion_json_string(new_value, &json_value, &error)) { - printf("Error: %s\n", get_diffusion_api_error_description(error)); - diffusion_api_error_free(error); - return HANDLER_SUCCESS; - } - - printf("Received message for topic %s (%ld bytes)\n", topic_path, strlen(json_value)); - printf("As JSON: %s\n", json_value); - free(json_value); - - apr_thread_cond_broadcast(cond); - apr_thread_mutex_unlock(mutex); - return HANDLER_SUCCESS; -} - -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - const char *topic_name = hash_get(options, "topic"); - - /* - * Setup mutex and condition variable. - */ - apr_initialize(); - apr_pool_create(&pool, NULL); - apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool); - apr_thread_cond_create(&cond, pool); - - /* - * Create a session with the Diffusion server. - */ - DIFFUSION_ERROR_T error = { 0 }; - SESSION_T *session = session_create(url, principal, credentials, NULL, NULL, &error); - if(session == NULL) { - fprintf(stderr, "Failed to create session: %s\n", error.message); - return EXIT_FAILURE; - } - - // Set up and add the value stream to receive JSON updates - VALUE_STREAM_T value_stream = { - .datatype = DATATYPE_JSON, - .on_subscription = on_subscription, - .on_unsubscription = on_unsubscription, - .on_value = on_value - }; - - // Add value stream - add_stream(session, topic_name, &value_stream); - - SUBSCRIPTION_PARAMS_T params = { - .topic_selector = topic_name, - .on_topic_message = NULL - }; - - subscribe(session, params); - - // Receive updates for 2 minutes - sleep(120); - - session_close(session, NULL); - session_free(session); - - credentials_free(credentials); - hash_free(options, NULL, free); - - apr_thread_cond_destroy(cond); - apr_thread_mutex_destroy(mutex); - apr_pool_destroy(pool); - apr_terminate(); - - return EXIT_SUCCESS; -} diff --git a/c/reconnect.c b/c/reconnect.c index 724eb495..8a65d82b 100644 --- a/c/reconnect.c +++ b/c/reconnect.c @@ -1,197 +1,201 @@ -/** - * Copyright © 2014, 2016 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This example is written in C99. Please use an appropriate C99 capable compiler - * - * @author Push Technology Limited - * @since 5.0 - */ - -/* - * This example shows how to make a synchronous connection to - * Diffusion, with user-provided reconnection logic. - */ -#include -#include -#include -#ifndef WIN32 -#include -#else -#define sleep(x) Sleep(1000 * x) -#endif - -#include - -#include "diffusion.h" -#include "args.h" - -ARG_OPTS_T arg_opts[] = { - ARG_OPTS_HELP, - {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, - {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, - {'s', "sleep", "Time to sleep before disconnecting (in seconds).", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, - END_OF_ARG_OPTS -}; - -/* - * This callback is used when the session state changes, e.g. when a session - * moves from a "connecting" to a "connected" state, or from "connected" to - * "closed". - */ -static void -on_session_state_changed(SESSION_T *session, - const SESSION_STATE_T old_state, - const SESSION_STATE_T new_state) -{ - printf("Session state changed from %s (%d) to %s (%d)\n", - session_state_as_string(old_state), old_state, - session_state_as_string(new_state), new_state); -} - -typedef struct { - long current_wait; - long max_wait; -} BACKOFF_STRATEGY_ARGS_T; - -static RECONNECTION_ATTEMPT_ACTION_T -backoff_reconnection_strategy(SESSION_T *session, void *args) -{ - BACKOFF_STRATEGY_ARGS_T *backoff_args = args; - - printf("Waiting for %ld ms\n", backoff_args->current_wait); - - apr_sleep(backoff_args->current_wait * 1000); // µs -> ms - - // But only up to some maximum time. - if(backoff_args->current_wait > backoff_args->max_wait) { - backoff_args->current_wait = backoff_args->max_wait; - } - - return RECONNECTION_ATTEMPT_ACTION_START; -} - -static void -backoff_success(SESSION_T *session, void *args) -{ - printf("Reconnection successful\n"); - - BACKOFF_STRATEGY_ARGS_T *backoff_args = args; - backoff_args->current_wait = 0; // Reset wait. -} - -static void -backoff_failure(SESSION_T *session, void *args) -{ - printf("Reconnection failed (%s)\n", session_state_as_string(session->state)); - - BACKOFF_STRATEGY_ARGS_T *backoff_args = args; - - // Exponential backoff. - if(backoff_args->current_wait == 0) { - backoff_args->current_wait = 1; - } - else { - backoff_args->current_wait *= 2; - } -} - -/* - * Entry point for the example. - */ -int -main(int argc, char **argv) -{ - /* - * Standard command-line parsing. - */ - HASH_T *options = parse_cmdline(argc, argv, arg_opts); - if(options == NULL || hash_get(options, "help") != NULL) { - show_usage(argc, argv, arg_opts); - return EXIT_FAILURE; - } - - const char *url = hash_get(options, "url"); - const char *principal = hash_get(options, "principal"); - CREDENTIALS_T *credentials = NULL; - const char *password = hash_get(options, "credentials"); - if(password != NULL) { - credentials = credentials_create_password(password); - } - - const unsigned int sleep_time = atol(hash_get(options, "sleep")); - - SESSION_T *session; - DIFFUSION_ERROR_T error = { 0 }; - - SESSION_LISTENER_T session_listener = { 0 }; - session_listener.on_state_changed = &on_session_state_changed; - - /* - * Set the arguments to our exponential backoff strategy. - */ - BACKOFF_STRATEGY_ARGS_T *backoff_args = calloc(1, sizeof(BACKOFF_STRATEGY_ARGS_T)); - backoff_args->current_wait = 0; - backoff_args->max_wait = 5000; - - /* - * Create the backoff strategy. - */ - RECONNECTION_STRATEGY_T *reconnection_strategy = - make_reconnection_strategy_user_function(backoff_reconnection_strategy, - backoff_args, - backoff_success, - backoff_failure); - - /* - * Only ever retry for 30 seconds. - */ - reconnection_strategy_set_timeout(reconnection_strategy, 30 * 1000); - - /* - * Create a session, synchronously. - */ - session = session_create(url, principal, credentials, &session_listener, reconnection_strategy, &error); - if(session != NULL) { - char *sid_str = session_id_to_string(session->id); - printf("Session created (state=%d, id=%s)\n", session_state_get(session), sid_str); - free(sid_str); - } - else { - printf("Failed to create session: %s\n", error.message); - free(error.message); - } - - // With the exception of backoff_args, the reconnection strategy is - // copied withing session_create() and may be freed now. - free(reconnection_strategy); - - /* - * Sleep for a while. - */ - sleep(sleep_time); - - /* - * Close the session, and release resources and memory. - */ - session_close(session, NULL); - session_free(session); - - free(backoff_args); - - credentials_free(credentials); - hash_free(options, NULL, free); - - return EXIT_SUCCESS; -} +/** + * Copyright © 2014 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 5.0 + */ + +/* + * This example shows how to make a synchronous connection to + * Diffusion, with user-provided reconnection logic. + */ +#include +#include +#include + +#ifndef WIN32 + #include +#else + #define sleep(x) Sleep(1000 * x) +#endif + + +#include "diffusion.h" +#include "args.h" + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, NULL}, + {'s', "sleep", "Time to sleep before disconnecting (in seconds).", ARG_OPTIONAL, ARG_HAS_VALUE, "20" }, + END_OF_ARG_OPTS +}; + +/* + * This callback is used when the session state changes, e.g. when a session + * moves from a "connecting" to a "connected" state, or from "connected" to + * "closed". + */ +static void on_session_state_changed( + SESSION_T *session, + const SESSION_STATE_T old_state, + const SESSION_STATE_T new_state) +{ + printf("Session state changed from %s (%d) to %s (%d)\n", + session_state_as_string(old_state), old_state, + session_state_as_string(new_state), new_state); +} + + +typedef struct { + double current_wait; + double max_wait; +} BACKOFF_STRATEGY_ARGS_T; + + +static RECONNECTION_ATTEMPT_ACTION_T backoff_reconnection_strategy( + SESSION_T *session, + void *args) +{ + BACKOFF_STRATEGY_ARGS_T *backoff_args = args; + + printf("Waiting for %f ms\n", backoff_args->current_wait); + + sleep(backoff_args->current_wait); + + // But only up to some maximum time. + if(backoff_args->current_wait > backoff_args->max_wait) { + backoff_args->current_wait = backoff_args->max_wait; + } + + return RECONNECTION_ATTEMPT_ACTION_START; +} + + +static void backoff_success(SESSION_T *session, void *args) +{ + printf("Reconnection successful\n"); + + BACKOFF_STRATEGY_ARGS_T *backoff_args = args; + backoff_args->current_wait = 0; // Reset wait. +} + + +static void backoff_failure(SESSION_T *session, void *args) +{ + printf("Reconnection failed (%s)\n", session_state_as_string(session->state)); + + BACKOFF_STRATEGY_ARGS_T *backoff_args = args; + + // Exponential backoff. + if(backoff_args->current_wait == 0) { + backoff_args->current_wait = 0.01; + } + else { + backoff_args->current_wait *= 2; + } +} + + +/* + * Entry point for the example. + */ +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + const unsigned int sleep_time = atol(hash_get(options, "sleep")); + + SESSION_T *session; + DIFFUSION_ERROR_T error = { 0 }; + + SESSION_LISTENER_T session_listener = { 0 }; + session_listener.on_state_changed = &on_session_state_changed; + + /* + * Set the arguments to our exponential backoff strategy. + */ + BACKOFF_STRATEGY_ARGS_T *backoff_args = calloc(1, sizeof(BACKOFF_STRATEGY_ARGS_T)); + backoff_args->current_wait = 0; + backoff_args->max_wait = 5; + + /* + * Create the backoff strategy. + */ + RECONNECTION_STRATEGY_T *reconnection_strategy = + make_reconnection_strategy_user_function( + backoff_reconnection_strategy, + backoff_args, + backoff_success, + backoff_failure); + + /* + * Only ever retry for 30 seconds. + */ + reconnection_strategy_set_timeout(reconnection_strategy, 30 * 1000); + + /* + * Create a session, synchronously. + */ + session = session_create(url, principal, credentials, &session_listener, reconnection_strategy, &error); + if(session != NULL) { + char *sid_str = session_id_to_string(session->id); + printf("Session created (state=%d, id=%s)\n", session_state_get(session), sid_str); + free(sid_str); + } + else { + printf("Failed to create session: %s\n", error.message); + free(error.message); + } + + // With the exception of backoff_args, the reconnection strategy is + // copied withing session_create() and may be freed now. + free(reconnection_strategy); + + /* + * Sleep for a while. + */ + sleep(sleep_time); + + /* + * Close the session, and release resources and memory. + */ + session_close(session, NULL); + session_free(session); + + free(backoff_args); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/c/session-factory.c b/c/session-factory.c new file mode 100644 index 00000000..544c226f --- /dev/null +++ b/c/session-factory.c @@ -0,0 +1,107 @@ +/** + * Copyright © 2022 - 2023 DiffusionData Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This example is written in C99. Please use an appropriate C99 capable compiler + * + * @author DiffusionData Limited + * @since 6.9 + */ + +/* + * This examples shows how to connect to Diffusion via a session factory. + */ +#include +#include +#include +#ifndef WIN32 +#include +#else +#define sleep(x) Sleep(1000 * x) +#endif + +#include "diffusion.h" +#include "args.h" + + +ARG_OPTS_T arg_opts[] = { + ARG_OPTS_HELP, + {'u', "url", "Diffusion server URL", ARG_OPTIONAL, ARG_HAS_VALUE, "ws://localhost:8080"}, + {'p', "principal", "Principal (username) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "client"}, + {'c', "credentials", "Credentials (password) for the connection", ARG_OPTIONAL, ARG_HAS_VALUE, "password"}, + {'s', "sleep", "Time to sleep before disconnecting (in seconds).", ARG_OPTIONAL, ARG_HAS_VALUE, "5" }, + END_OF_ARG_OPTS +}; + + +/* + * Entry point for the example. + */ +int main(int argc, char **argv) +{ + /* + * Standard command-line parsing. + */ + HASH_T *options = parse_cmdline(argc, argv, arg_opts); + if(options == NULL || hash_get(options, "help") != NULL) { + show_usage(argc, argv, arg_opts); + return EXIT_FAILURE; + } + + const char *url = hash_get(options, "url"); + const char *principal = hash_get(options, "principal"); + CREDENTIALS_T *credentials = NULL; + const char *password = hash_get(options, "credentials"); + if(password != NULL) { + credentials = credentials_create_password(password); + } + + const unsigned int sleep_time = atol(hash_get(options, "sleep")); + + DIFFUSION_SESSION_FACTORY_T *session_factory = diffusion_session_factory_init(); + diffusion_session_factory_principal(session_factory, principal); + diffusion_session_factory_credentials(session_factory, credentials); + + /* + * Create a session, synchronously. + */ + SESSION_T *session = session_create_with_session_factory(session_factory, url); + if(session != NULL) { + char *sid_str = session_id_to_string(session->id); + printf("Session created (state=%d, id=%s)\n", + session_state_get(session), + sid_str); + free(sid_str); + } + else { + printf("Failed to create session\n"); + } + + /* + * Sleep for a while. + */ + sleep(sleep_time); + + /* + * Close the session, and release resources and memory. + */ + session_close(session, NULL); + session_free(session); + + diffusion_session_factory_free(session_factory); + + credentials_free(credentials); + hash_free(options, NULL, free); + + return EXIT_SUCCESS; +} diff --git a/dotnet/README.md b/dotnet/README.md index f859b391..b0ec714f 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -1,18 +1,18 @@ -# Diffusion .NET Client Examples - -This directory contains examples showing the use of the Diffusion .NET Client Library. - -The `getting-started` directory contains a simple publishing and -subscribing client. The `examples` directory contains more in-depth -examples that can be run via the `Runner.csproj` project. - -## Client libraries - -In order for all examples to work you have to pull in -`Diffusion.Client` via NuGet: `dotnet add package Diffusion.Client`. - -You can also download the client library from the following locations: - -* Download from [our website](http://download.pushtechnology.com/cloud/latest/sdks.html#dotnet) - -* The client library is also available in the `clients` directory of the Diffusion server installation. +# Diffusion .NET Client Examples + +This directory contains examples showing the use of the Diffusion .NET Client Library. + +The `getting-started` directory contains a simple publishing and +subscribing client. The `examples` directory contains more in-depth +examples that can be run via the `Runner.csproj` project. + +## Client libraries + +In order for all examples to work you have to pull in +`Diffusion.Client` via NuGet: `dotnet add package Diffusion.Client`. + +You can also download the client library from the following locations: + +* Download from [our website](http://download.pushtechnology.com/cloud/latest/sdks.html#dotnet) + +* The client library is also available in the `clients` directory of the Diffusion server installation. diff --git a/dotnet/client-examples.sln b/dotnet/client-examples.sln index efd86324..b993ef75 100644 --- a/dotnet/client-examples.sln +++ b/dotnet/client-examples.sln @@ -1,72 +1,72 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30011.22 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Getting Started", "Getting Started", "{37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{A7A3F917-51B9-409E-910A-F1B728E71373}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Publishing", "source\getting-started\Publishing\Publishing.csproj", "{67EA612D-71CC-4353-9A47-CEF86B911917}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Subscribing", "source\getting-started\Subscribing\Subscribing.csproj", "{9109F6B4-3AF3-4699-8757-B03ADE15ADD3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner", "source\examples\Runner\Runner.csproj", "{1EFBF419-9BEE-4E12-9C91-FB2C7738B524}" -EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Publishing", "source\examples\Publishing\Publishing.shproj", "{960603FE-DE40-42FE-A1E7-1284E3AF1A15}" -EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Consuming", "source\examples\Consuming\Consuming.shproj", "{6E978DC0-D636-4883-B0D8-4BA93667865C}" -EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Features", "source\examples\Features\Features.shproj", "{CBF43C7F-76A7-4476-8F01-AB2F4C89F90E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7C879BE2-668F-4149-876F-F891E8E0E448}" - ProjectSection(SolutionItems) = preProject - README.md = README.md - EndProjectSection -EndProject -Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - source\examples\Consuming\Consuming.projitems*{1efbf419-9bee-4e12-9c91-fb2c7738b524}*SharedItemsImports = 5 - source\examples\Features\Features.projitems*{1efbf419-9bee-4e12-9c91-fb2c7738b524}*SharedItemsImports = 5 - source\examples\Publishing\Publishing.projitems*{1efbf419-9bee-4e12-9c91-fb2c7738b524}*SharedItemsImports = 5 - source\examples\Consuming\Consuming.projitems*{6e978dc0-d636-4883-b0d8-4ba93667865c}*SharedItemsImports = 13 - source\examples\Publishing\Publishing.projitems*{960603fe-de40-42fe-a1e7-1284e3af1a15}*SharedItemsImports = 13 - source\examples\Features\Features.projitems*{cbf43c7f-76a7-4476-8f01-ab2f4c89f90e}*SharedItemsImports = 13 - EndGlobalSection - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {67EA612D-71CC-4353-9A47-CEF86B911917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {67EA612D-71CC-4353-9A47-CEF86B911917}.Debug|Any CPU.Build.0 = Debug|Any CPU - {67EA612D-71CC-4353-9A47-CEF86B911917}.Release|Any CPU.ActiveCfg = Release|Any CPU - {67EA612D-71CC-4353-9A47-CEF86B911917}.Release|Any CPU.Build.0 = Release|Any CPU - {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Release|Any CPU.Build.0 = Release|Any CPU - {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} = {A7A3F917-51B9-409E-910A-F1B728E71373} - {37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227} = {A7A3F917-51B9-409E-910A-F1B728E71373} - {67EA612D-71CC-4353-9A47-CEF86B911917} = {37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227} - {9109F6B4-3AF3-4699-8757-B03ADE15ADD3} = {37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227} - {1EFBF419-9BEE-4E12-9C91-FB2C7738B524} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} - {960603FE-DE40-42FE-A1E7-1284E3AF1A15} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} - {6E978DC0-D636-4883-B0D8-4BA93667865C} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} - {CBF43C7F-76A7-4476-8F01-AB2F4C89F90E} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {5978CBA1-E70C-47D3-AE8E-057A43482B63} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30011.22 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Getting Started", "Getting Started", "{37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{A7A3F917-51B9-409E-910A-F1B728E71373}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Publishing", "source\getting-started\Publishing\Publishing.csproj", "{67EA612D-71CC-4353-9A47-CEF86B911917}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Subscribing", "source\getting-started\Subscribing\Subscribing.csproj", "{9109F6B4-3AF3-4699-8757-B03ADE15ADD3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner", "source\examples\Runner\Runner.csproj", "{1EFBF419-9BEE-4E12-9C91-FB2C7738B524}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Publishing", "source\examples\Publishing\Publishing.shproj", "{960603FE-DE40-42FE-A1E7-1284E3AF1A15}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Consuming", "source\examples\Consuming\Consuming.shproj", "{6E978DC0-D636-4883-B0D8-4BA93667865C}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Features", "source\examples\Features\Features.shproj", "{CBF43C7F-76A7-4476-8F01-AB2F4C89F90E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7C879BE2-668F-4149-876F-F891E8E0E448}" + ProjectSection(SolutionItems) = preProject + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + source\examples\Consuming\Consuming.projitems*{1efbf419-9bee-4e12-9c91-fb2c7738b524}*SharedItemsImports = 5 + source\examples\Features\Features.projitems*{1efbf419-9bee-4e12-9c91-fb2c7738b524}*SharedItemsImports = 5 + source\examples\Publishing\Publishing.projitems*{1efbf419-9bee-4e12-9c91-fb2c7738b524}*SharedItemsImports = 5 + source\examples\Consuming\Consuming.projitems*{6e978dc0-d636-4883-b0d8-4ba93667865c}*SharedItemsImports = 13 + source\examples\Publishing\Publishing.projitems*{960603fe-de40-42fe-a1e7-1284e3af1a15}*SharedItemsImports = 13 + source\examples\Features\Features.projitems*{cbf43c7f-76a7-4476-8f01-ab2f4c89f90e}*SharedItemsImports = 13 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {67EA612D-71CC-4353-9A47-CEF86B911917}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67EA612D-71CC-4353-9A47-CEF86B911917}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67EA612D-71CC-4353-9A47-CEF86B911917}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67EA612D-71CC-4353-9A47-CEF86B911917}.Release|Any CPU.Build.0 = Release|Any CPU + {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9109F6B4-3AF3-4699-8757-B03ADE15ADD3}.Release|Any CPU.Build.0 = Release|Any CPU + {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EFBF419-9BEE-4E12-9C91-FB2C7738B524}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} = {A7A3F917-51B9-409E-910A-F1B728E71373} + {37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227} = {A7A3F917-51B9-409E-910A-F1B728E71373} + {67EA612D-71CC-4353-9A47-CEF86B911917} = {37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227} + {9109F6B4-3AF3-4699-8757-B03ADE15ADD3} = {37D0C7B4-D0DD-46CB-BAAF-8CB5B43E4227} + {1EFBF419-9BEE-4E12-9C91-FB2C7738B524} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} + {960603FE-DE40-42FE-A1E7-1284E3AF1A15} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} + {6E978DC0-D636-4883-B0D8-4BA93667865C} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} + {CBF43C7F-76A7-4476-8F01-AB2F4C89F90E} = {1F4E79D7-6CBD-4E2C-8001-56C6DD086FB9} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5978CBA1-E70C-47D3-AE8E-057A43482B63} + EndGlobalSection +EndGlobal diff --git a/dotnet/source/examples/Consuming/BinaryTopics.cs b/dotnet/source/examples/Consuming/BinaryTopics.cs index 56e15f38..e7943b81 100644 --- a/dotnet/source/examples/Consuming/BinaryTopics.cs +++ b/dotnet/source/examples/Consuming/BinaryTopics.cs @@ -1,118 +1,118 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using PushTechnology.ClientInterface.Data.Binary; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Implementation of a client which subscribes to a binary topic and consumes the data it receives. - /// - public sealed class ConsumingBinaryTopics : IExample { - /// - /// Runs the binary topic consuming client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = "random/Binary"; - - // Add a topic stream for 'random/Binary' - var binaryStream = new BinaryStream(); - topics.AddStream( topic, binaryStream ); - - try { - // Subscribe to 'random/Binary' topic - await topics.SubscribeAsync( topic, cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch ( TaskCanceledException ) { - //Task was canceled; close stream and unsubscribe - topics.RemoveStream( binaryStream ); - await topics.UnsubscribeAsync( topic ); - } finally { - // Note that closing the session, will automatically unsubscribe from all topics the client is - // subscribed to. - session.Close(); - } - } - - /// - /// Basic implementation of the IValueStream for binary topics. - /// - private sealed class BinaryStream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => WriteLine( "The subscrption stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => WriteLine( $"An error has occured : {errorReason}." ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => WriteLine( $"Client subscribed to {topicPath}." ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => WriteLine( $"Client unsubscribed from {topicPath} : {reason}." ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, IBinary oldValue, IBinary newValue ) - => WriteLine( $"New value of {topicPath} is {Encoding.UTF8.GetString( newValue.ToByteArray() )}." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using PushTechnology.ClientInterface.Data.Binary; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Implementation of a client which subscribes to a binary topic and consumes the data it receives. + /// + public sealed class ConsumingBinaryTopics : IExample { + /// + /// Runs the binary topic consuming client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[ 0 ]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + string topic = "random/Binary"; + + // Add a topic stream for 'random/Binary' + var binaryStream = new BinaryStream(); + topics.AddStream( topic, binaryStream ); + + try { + // Subscribe to 'random/Binary' topic + await topics.SubscribeAsync( topic, cancellationToken ); + + // Run until user requests ending of example + await Task.Delay( Timeout.Infinite, cancellationToken ); + } catch ( TaskCanceledException ) { + //Task was canceled; close stream and unsubscribe + topics.RemoveStream( binaryStream ); + await topics.UnsubscribeAsync( topic ); + } finally { + // Note that closing the session, will automatically unsubscribe from all topics the client is + // subscribed to. + session.Close(); + } + } + + /// + /// Basic implementation of the IValueStream for binary topics. + /// + private sealed class BinaryStream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => WriteLine( "The subscrption stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => WriteLine( $"An error has occured : {errorReason}." ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => WriteLine( $"Client subscribed to topic '{topicPath}'." ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => WriteLine( $"Client unsubscribed from topic '{topicPath}' with reason '{reason}'." ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, IBinary oldValue, IBinary newValue ) + => WriteLine( $"New value of topic '{topicPath}' is {Encoding.UTF8.GetString(newValue.ToByteArray())}." ); + } + } +} diff --git a/dotnet/source/examples/Consuming/Consuming.projitems b/dotnet/source/examples/Consuming/Consuming.projitems index fe1af884..5429fdfa 100644 --- a/dotnet/source/examples/Consuming/Consuming.projitems +++ b/dotnet/source/examples/Consuming/Consuming.projitems @@ -1,22 +1,22 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - 6e978dc0-d636-4883-b0d8-4ba93667865c - - - PushTechnology.ClientInterface.Example.Consuming - - - - - - - - - - - - + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 6e978dc0-d636-4883-b0d8-4ba93667865c + + + PushTechnology.ClientInterface.Example.Consuming + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/source/examples/Consuming/Consuming.shproj b/dotnet/source/examples/Consuming/Consuming.shproj index c76700a3..47dfcd3a 100644 --- a/dotnet/source/examples/Consuming/Consuming.shproj +++ b/dotnet/source/examples/Consuming/Consuming.shproj @@ -1,13 +1,13 @@ - - - - 6e978dc0-d636-4883-b0d8-4ba93667865c - 14.0 - - - - - - - - + + + + 6e978dc0-d636-4883-b0d8-4ba93667865c + 14.0 + + + + + + + + diff --git a/dotnet/source/examples/Consuming/DoubleTopics.cs b/dotnet/source/examples/Consuming/DoubleTopics.cs index 0fb9fe94..82428131 100644 --- a/dotnet/source/examples/Consuming/DoubleTopics.cs +++ b/dotnet/source/examples/Consuming/DoubleTopics.cs @@ -1,116 +1,116 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using static System.Console; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Implementation of a client which subscribes to a double topic and consumes the data it receives. - /// - public sealed class ConsumingDoubleTopics : IExample { - /// - /// Runs the double topic client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = "random/Double"; - - // Add a topic stream for 'random/Double' - var doubleStream = new DoubleStream(); - topics.AddStream( topic, doubleStream ); - - try { - // Subscribe to 'random/Double' topic - await topics.SubscribeAsync( topic, cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch ( TaskCanceledException ) { - //Task was canceled; close stream and unsubscribe - topics.RemoveStream( doubleStream ); - await topics.UnsubscribeAsync( topic ); - } finally { - // Note that closing the session, will automatically unsubscribe from all topics the client is - // subscribed to. - session.Close(); - } - } - - /// - /// Basic implementation of the IValueStream for Double topics. - /// - private sealed class DoubleStream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => WriteLine( "The subscrption stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => WriteLine( $"An error has occured : {errorReason}." ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => WriteLine( $"Client subscribed to {topicPath}." ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => WriteLine( $"Client unsubscribed from {topicPath} : {reason}." ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, double? oldValue, double? newValue ) - => WriteLine( $"New value of {topicPath} is {newValue}." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using static System.Console; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Implementation of a client which subscribes to a double topic and consumes the data it receives. + /// + public sealed class ConsumingDoubleTopics : IExample { + /// + /// Runs the double topic client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + string topic = "random/Double"; + + // Add a topic stream for 'random/Double' + var doubleStream = new DoubleStream(); + topics.AddStream( topic, doubleStream ); + + try { + // Subscribe to 'random/Double' topic + await topics.SubscribeAsync( topic, cancellationToken ); + + // Run until user requests ending of example + await Task.Delay( Timeout.Infinite, cancellationToken ); + } catch ( TaskCanceledException ) { + //Task was canceled; close stream and unsubscribe + topics.RemoveStream( doubleStream ); + await topics.UnsubscribeAsync( topic ); + } finally { + // Note that closing the session, will automatically unsubscribe from all topics the client is + // subscribed to. + session.Close(); + } + } + + /// + /// Basic implementation of the IValueStream for Double topics. + /// + private sealed class DoubleStream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => WriteLine( "The subscrption stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => WriteLine( $"An error has occured : {errorReason}." ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => WriteLine( $"Client subscribed to topic '{topicPath}'." ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => WriteLine( $"Client unsubscribed from topic '{topicPath}' with reason '{reason}'." ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, double? oldValue, double? newValue ) + => WriteLine( $"New value of topic '{topicPath}' is {newValue}." ); + } + } +} diff --git a/dotnet/source/examples/Consuming/FilterRequestMessages.cs b/dotnet/source/examples/Consuming/FilterRequestMessages.cs index 4f10a15e..9466f18d 100644 --- a/dotnet/source/examples/Consuming/FilterRequestMessages.cs +++ b/dotnet/source/examples/Consuming/FilterRequestMessages.cs @@ -1,79 +1,79 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Client implementation that listens for messages on a topic path. - /// - public sealed class ReceivingFilterRequestMessages : IExample { - /// - /// Runs the client receiving messages example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "client" ).Password( "password" ).Open( serverUrl ); - var messaging = session.Messaging; - var messagingPath = ">random/requestResponse"; - - var requestStream = new SimpleRequestStream(); - messaging.SetRequestStream( messagingPath, requestStream ); - - try { - await Task.Delay( Timeout.InfiniteTimeSpan, cancellationToken ); - } finally { - // Close session - messaging.RemoveRequestStream( messagingPath ); - session.Close(); - } - } - - /// - /// A simple IRequestStream implementation that prints confirmation of the actions completed. - /// - private class SimpleRequestStream : IRequestStream { - /// - /// Indicates that the request stream was closed. - /// - public void OnClose() - => WriteLine( "A request handler was closed." ); - - /// - /// Indicates that the request stream has received error. - /// - public void OnError( ErrorReason errorReason ) - => WriteLine( $"A request handler has received error: {errorReason}." ); - - /// - /// Indicates that a request was received and responds to it. - /// - /// On invalid request you would call: . - public void OnRequest( string path, string request, IResponder responder ) { - WriteLine( $"Received request: '{request}'." ); - responder.Respond( DateTime.UtcNow.ToLongTimeString() ); - } - } - } -} +/** + * Copyright © 2018 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Client implementation that listens for messages on a topic path. + /// + public sealed class ReceivingFilterRequestMessages : IExample { + /// + /// Runs the client receiving messages example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + var serverUrl = args[ 0 ]; + var session = Diffusion.Sessions.Principal( "client" ).Password( "password" ).Open( serverUrl ); + var messaging = session.Messaging; + var messagingPath = ">random/requestResponse"; + + var requestStream = new SimpleRequestStream(); + messaging.SetRequestStream( messagingPath, requestStream ); + + try { + await Task.Delay( Timeout.InfiniteTimeSpan, cancellationToken ); + } finally { + // Close session + messaging.RemoveRequestStream( messagingPath ); + session.Close(); + } + } + + /// + /// A simple IRequestStream implementation that prints confirmation of the actions completed. + /// + private class SimpleRequestStream : IRequestStream { + /// + /// Indicates that the request stream was closed. + /// + public void OnClose() + => WriteLine( "A request handler was closed." ); + + /// + /// Indicates that the request stream has received error. + /// + public void OnError( ErrorReason errorReason ) + => WriteLine( $"A request handler has received error: {errorReason}." ); + + /// + /// Indicates that a request was received and responds to it. + /// + /// On invalid request you would call: . + public void OnRequest( string path, string request, IResponder responder ) { + WriteLine( $"Received request: '{request}'." ); + responder.Respond( DateTime.UtcNow.ToLongTimeString() ); + } + } + } +} diff --git a/dotnet/source/examples/Consuming/IntegerTopics.cs b/dotnet/source/examples/Consuming/IntegerTopics.cs index 10b99762..540ad628 100644 --- a/dotnet/source/examples/Consuming/IntegerTopics.cs +++ b/dotnet/source/examples/Consuming/IntegerTopics.cs @@ -1,116 +1,116 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using static System.Console; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Implementation of a client which subscribes to a integer topic and consumes the data it receives. - /// - public sealed class ConsumingIntegerTopics : IExample { - /// - /// Runs the integer topic client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = "random/Integer"; - - // Add a topic stream for 'random/Integer' - var integerStream = new IntegerStream(); - topics.AddStream( topic, integerStream ); - - try { - // Subscribe to 'random/Integer' topic - await topics.SubscribeAsync( topic, cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch ( TaskCanceledException ) { - //Task was canceled; close stream and unsubscribe - topics.RemoveStream( integerStream ); - await topics.UnsubscribeAsync( topic ); - } finally { - // Note that closing the session, will automatically unsubscribe from all topics the client is - // subscribed to. - session.Close(); - } - } - - /// - /// Basic implementation of the IValueStream for Integer topics. - /// - private sealed class IntegerStream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => WriteLine( "The subscrption stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => WriteLine( $"An error has occured : {errorReason}." ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => WriteLine( $"Client subscribed to {topicPath}." ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => WriteLine( $"Client unsubscribed from {topicPath} : {reason}." ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, long? oldValue, long? newValue ) - => WriteLine( $"New value of {topicPath} is {newValue}." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using static System.Console; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Implementation of a client which subscribes to a integer topic and consumes the data it receives. + /// + public sealed class ConsumingIntegerTopics : IExample { + /// + /// Runs the integer topic client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + string topic = "random/Integer"; + + // Add a topic stream for 'random/Integer' + var integerStream = new IntegerStream(); + topics.AddStream( topic, integerStream ); + + try { + // Subscribe to 'random/Integer' topic + await topics.SubscribeAsync( topic, cancellationToken ); + + // Run until user requests ending of example + await Task.Delay( Timeout.Infinite, cancellationToken ); + } catch ( TaskCanceledException ) { + //Task was canceled; close stream and unsubscribe + topics.RemoveStream( integerStream ); + await topics.UnsubscribeAsync( topic ); + } finally { + // Note that closing the session, will automatically unsubscribe from all topics the client is + // subscribed to. + session.Close(); + } + } + + /// + /// Basic implementation of the IValueStream for Integer topics. + /// + private sealed class IntegerStream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => WriteLine( "The subscrption stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => WriteLine( $"An error has occured : {errorReason}." ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => WriteLine( $"Client subscribed to topic '{topicPath}'." ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => WriteLine( $"Client unsubscribed from topic '{topicPath}' with reason '{reason}'." ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, long? oldValue, long? newValue ) + => WriteLine( $"New value of topic '{topicPath}' is {newValue}." ); + } + } +} diff --git a/dotnet/source/examples/Consuming/JSONTopics.cs b/dotnet/source/examples/Consuming/JSONTopics.cs index c7d906c1..ca0204ca 100644 --- a/dotnet/source/examples/Consuming/JSONTopics.cs +++ b/dotnet/source/examples/Consuming/JSONTopics.cs @@ -1,118 +1,118 @@ -/** - * Copyright © 2016, 2017 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using PushTechnology.ClientInterface.Data.JSON; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using static System.Console; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Implementation of a client which subscribes to a JSON topic and consumes the data it receives. - /// - public sealed class ConsumingJSONTopics : IExample { - /// - /// Runs the JSON topic client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = "random/JSON"; - - // Add a topic stream for 'random/JSON' - var jsonStream = new JSONStream(); - topics.AddStream( topic, jsonStream ); - - try { - // Subscribe to 'random/JSON' topic - await topics.SubscribeAsync( topic, cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch ( TaskCanceledException ) { - //Task was canceled; close stream and unsubscribe - topics.RemoveStream( jsonStream ); - await topics.UnsubscribeAsync( topic ); - - } finally { - // Note that closing the session, will automatically unsubscribe from all topics the client is - // subscribed to. - session.Close(); - } - } - - /// - /// Basic implementation of the IValueStream for JSON topics. - /// - private sealed class JSONStream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => WriteLine( "The subscrption stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => WriteLine( $"An error has occured : {errorReason}." ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => WriteLine( $"Client subscribed to {topicPath}." ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => WriteLine( $"Client unsubscribed from {topicPath} : {reason}." ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, IJSON oldValue, IJSON newValue ) - => WriteLine( $"New value of {topicPath} is {newValue.ToJSONString()}." ); - } - } -} +/** + * Copyright © 2016, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using PushTechnology.ClientInterface.Data.JSON; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using static System.Console; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Implementation of a client which subscribes to a JSON topic and consumes the data it receives. + /// + public sealed class ConsumingJSONTopics : IExample { + /// + /// Runs the JSON topic client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + string topic = "random/JSON"; + + // Add a topic stream for 'random/JSON' + var jsonStream = new JSONStream(); + topics.AddStream( topic, jsonStream ); + + try { + // Subscribe to 'random/JSON' topic + await topics.SubscribeAsync( topic, cancellationToken ); + + // Run until user requests ending of example + await Task.Delay( Timeout.Infinite, cancellationToken ); + } catch ( TaskCanceledException ) { + //Task was canceled; close stream and unsubscribe + topics.RemoveStream( jsonStream ); + await topics.UnsubscribeAsync( topic ); + + } finally { + // Note that closing the session, will automatically unsubscribe from all topics the client is + // subscribed to. + session.Close(); + } + } + + /// + /// Basic implementation of the IValueStream for JSON topics. + /// + private sealed class JSONStream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => WriteLine( "The subscrption stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => WriteLine( $"An error has occured : {errorReason}." ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => WriteLine( $"Client subscribed to topic '{topicPath}'." ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => WriteLine( $"Client unsubscribed from topic '{topicPath}' with reason '{reason}'." ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, IJSON oldValue, IJSON newValue ) + => WriteLine( $"New value of topic '{topicPath}' is {newValue.ToJSONString()}." ); + } + } +} diff --git a/dotnet/source/examples/Consuming/PathRequestMessages.cs b/dotnet/source/examples/Consuming/PathRequestMessages.cs index 156613a7..502f35c2 100644 --- a/dotnet/source/examples/Consuming/PathRequestMessages.cs +++ b/dotnet/source/examples/Consuming/PathRequestMessages.cs @@ -1,81 +1,85 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Client implementation that listens for messages on a topic path. - /// - public sealed class ReceivingPathRequestMessages : IExample { - /// - /// Runs the client receiving messages example. - /// - /// A token used to end the client example. - /// A single string should be used for the server URL. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var messaging = session.Messaging; - var messagingPath = ">random/requestResponse"; - - var requestHandler = new SimpleRequestHandler(); - var requestHandlerRegistration = await messaging.AddRequestHandlerAsync( - messagingPath, requestHandler, cancellationToken ); - - try { - await Task.Delay( Timeout.InfiniteTimeSpan, cancellationToken ); - } finally { - // Close session - await requestHandlerRegistration.CloseAsync(); - session.Close(); - } - } - - /// - /// A simple IRequestHandler implementation that prints confirmation of the actions completed. - /// - private class SimpleRequestHandler : IRequestHandler { - /// - /// Indicates that the request handler was closed. - /// - public void OnClose() - => WriteLine( "A request handler was closed." ); - - /// - /// Indicates that the request handler has received error. - /// - public void OnError( ErrorReason errorReason ) - => WriteLine( $"A request handler has received error: '{errorReason}'." ); - - /// - /// Indicates that a request was received and responds to it. - /// - /// On invalid request you would call: . - public void OnRequest( string request, IRequestContext context, IResponder responder ) { - WriteLine( $"Received request: '{request}'." ); - responder.Respond( DateTime.UtcNow.ToLongTimeString() ); - } - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Control.Topics; +using PushTechnology.ClientInterface.Client.Session; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Client implementation that listens for messages on a topic path. + /// + public sealed class ReceivingPathRequestMessages : IExample { + /// + /// Runs the client receiving messages example. + /// + /// A token used to end the client example. + /// A single string should be used for the server URL. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var messaging = session.Messaging; + string messagingPath = ">random/requestResponse"; + + var requestHandler = new SimpleRequestHandler(); + var requestHandlerRegistration = await messaging.AddRequestHandlerAsync( + messagingPath, requestHandler, cancellationToken ); + + try { + await Task.Delay( Timeout.InfiniteTimeSpan, cancellationToken ); + } finally { + // Close session + await requestHandlerRegistration.CloseAsync(); + session.Close(); + } + } + + /// + /// A simple IRequestHandler implementation that prints confirmation of the actions completed. + /// + private class SimpleRequestHandler : IRequestHandler { + /// + /// Indicates that the request handler was closed. + /// + public void OnClose() + => WriteLine( "A request handler was closed." ); + + /// + /// Indicates that the request handler has received error. + /// + public void OnError( ErrorReason errorReason ) + => WriteLine( $"A request handler has received error: '{errorReason}'." ); + + /// + /// Indicates that a request was received and responds to it. + /// + /// On invalid request you would call: . + public void OnRequest( string request, IRequestContext context, IResponder responder ) { + WriteLine( $"Received request: '{request}'." ); + responder.Respond( DateTime.UtcNow.ToLongTimeString() ); + } + } + } +} diff --git a/dotnet/source/examples/Consuming/RecordV2Topics.cs b/dotnet/source/examples/Consuming/RecordV2Topics.cs index 18ad1b2e..4a5c9626 100644 --- a/dotnet/source/examples/Consuming/RecordV2Topics.cs +++ b/dotnet/source/examples/Consuming/RecordV2Topics.cs @@ -1,117 +1,117 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using PushTechnology.ClientInterface.Data.Record; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Implementation of a client which subscribes to a RecordV2 topic and consumes the data it receives. - /// - public sealed class ConsumingRecordV2Topics : IExample { - /// - /// Runs the RecordV2 topic consuming client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server URL. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = "random/RecordV2"; - - // Add a topic stream for 'random/RecordV2' - var recordV2Stream = new RecordV2Stream(); - topics.AddStream( topic, recordV2Stream ); - - try { - // Subscribe to 'random/RecordV2' topic - await topics.SubscribeAsync( topic, cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch ( TaskCanceledException ) { - //Task was canceled; close stream and unsubscribe - topics.RemoveStream( recordV2Stream ); - await topics.UnsubscribeAsync( topic ); - } finally { - // Note that closing the session, will automatically unsubscribe from all topics the client is - // subscribed to. - session.Close(); - } - } - - /// - /// Basic implementation of the IValueStream for RecordV2 topics. - /// - private sealed class RecordV2Stream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => WriteLine( "The subscription stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => WriteLine( $"An error has occurred : {errorReason}." ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => WriteLine( $"Client subscribed to {topicPath}." ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => WriteLine( $"Client unsubscribed from {topicPath} : {reason}." ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, IRecordV2 oldValue, IRecordV2 newValue ) - => WriteLine( $"New value of {topicPath} is [{string.Join( ", ", newValue.AsFields() )}]." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using PushTechnology.ClientInterface.Data.Record; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Implementation of a client which subscribes to a RecordV2 topic and consumes the data it receives. + /// + public sealed class ConsumingRecordV2Topics : IExample { + /// + /// Runs the RecordV2 topic consuming client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server URL. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + string topic = "random/RecordV2"; + + // Add a topic stream for 'random/RecordV2' + var recordV2Stream = new RecordV2Stream(); + topics.AddStream( topic, recordV2Stream ); + + try { + // Subscribe to 'random/RecordV2' topic + await topics.SubscribeAsync( topic, cancellationToken ); + + // Run until user requests ending of example + await Task.Delay( Timeout.Infinite, cancellationToken ); + } catch ( TaskCanceledException ) { + //Task was canceled; close stream and unsubscribe + topics.RemoveStream( recordV2Stream ); + await topics.UnsubscribeAsync( topic ); + } finally { + // Note that closing the session, will automatically unsubscribe from all topics the client is + // subscribed to. + session.Close(); + } + } + + /// + /// Basic implementation of the IValueStream for RecordV2 topics. + /// + private sealed class RecordV2Stream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => WriteLine( "The subscription stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => WriteLine( $"An error has occurred : {errorReason}." ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => WriteLine( $"Client subscribed to topic '{topicPath}'." ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => WriteLine( $"Client unsubscribed from topic '{topicPath}' with reason '{reason}'." ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, IRecordV2 oldValue, IRecordV2 newValue ) + => WriteLine( $"New value of topic '{topicPath}' is [{string.Join( ", ", newValue.AsFields() )}]." ); + } + } +} diff --git a/dotnet/source/examples/Consuming/SessionRequestMessages.cs b/dotnet/source/examples/Consuming/SessionRequestMessages.cs index c88a2f12..b04a6ab3 100644 --- a/dotnet/source/examples/Consuming/SessionRequestMessages.cs +++ b/dotnet/source/examples/Consuming/SessionRequestMessages.cs @@ -1,83 +1,83 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Client implementation that listens for messages on a topic path. - /// - public sealed class ReceivingSessionRequestMessages : IExample { - /// - /// Runs the client receiving messages example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "client" ).Password( "password" ).Open( serverUrl ); - var messaging = session.Messaging; - var messagingPath = ">random/requestResponse"; - - var requestStream = new SimpleRequestStream(); - messaging.SetRequestStream( messagingPath, requestStream ); - - try { - await Task.Delay( Timeout.InfiniteTimeSpan, cancellationToken ); - } finally { - // Close session - messaging.RemoveRequestStream( messagingPath ); - session.Close(); - } - } - - /// - /// A simple IRequestStream implementation that prints confirmation of the actions completed. - /// - private class SimpleRequestStream : IRequestStream { - /// - /// Indicates that the request stream was closed. - /// - public void OnClose() - => WriteLine( "A request handler was closed." ); - - /// - /// Indicates that the request stream has received error. - /// - public void OnError( ErrorReason errorReason ) - => WriteLine( $"A request handler has received error: {errorReason}." ); - - /// - /// Indicates that a request was received and responds to it. - /// - /// On invalid request you would call: . - public void OnRequest( string path, string request, IResponder responder ) { - if ( request == "Hello?" ) { // message to the filter to obtain the session ID - responder.Respond( "Yes" ); - } else { - WriteLine( $"Received request: '{request}'." ); - responder.Respond( DateTime.UtcNow.ToLongTimeString() ); - } - } - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Client implementation that listens for messages on a topic path. + /// + public sealed class ReceivingSessionRequestMessages : IExample { + /// + /// Runs the client receiving messages example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "client" ).Password( "password" ).Open( serverUrl ); + var messaging = session.Messaging; + string messagingPath = ">random/requestResponse"; + + var requestStream = new SimpleRequestStream(); + messaging.SetRequestStream( messagingPath, requestStream ); + + try { + await Task.Delay( Timeout.InfiniteTimeSpan, cancellationToken ); + } finally { + // Close session + messaging.RemoveRequestStream( messagingPath ); + session.Close(); + } + } + + /// + /// A simple IRequestStream implementation that prints confirmation of the actions completed. + /// + private class SimpleRequestStream : IRequestStream { + /// + /// Indicates that the request stream was closed. + /// + public void OnClose() + => WriteLine( "A request handler was closed." ); + + /// + /// Indicates that the request stream has received error. + /// + public void OnError( ErrorReason errorReason ) + => WriteLine( $"A request handler has received error: {errorReason}." ); + + /// + /// Indicates that a request was received and responds to it. + /// + /// On invalid request you would call: . + public void OnRequest( string path, string request, IResponder responder ) { + if ( request == "Hello?" ) { // message to the filter to obtain the session ID + responder.Respond( "Yes" ); + } else { + WriteLine( $"Received request: '{request}'." ); + responder.Respond( DateTime.UtcNow.ToLongTimeString() ); + } + } + } + } +} diff --git a/dotnet/source/examples/Consuming/StringTopics.cs b/dotnet/source/examples/Consuming/StringTopics.cs index 8f23793b..db75be5a 100644 --- a/dotnet/source/examples/Consuming/StringTopics.cs +++ b/dotnet/source/examples/Consuming/StringTopics.cs @@ -1,116 +1,116 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using static System.Console; - -namespace PushTechnology.ClientInterface.Example.Consuming { - /// - /// Implementation of a client which subscribes to a string topic and consumes the data it receives. - /// - public sealed class ConsumingStringTopics : IExample { - /// - /// Runs the string topic client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = "random/String"; - - // Add a topic stream for 'random/String' - var stringStream = new StringStream(); - topics.AddStream( topic, stringStream ); - - try { - // Subscribe to 'random/String' topic - await topics.SubscribeAsync( topic, cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch(TaskCanceledException) { - //Task was canceled; close stream and unsubscribe - topics.RemoveStream( stringStream ); - await topics.UnsubscribeAsync( topic ); - } finally { - // Note that closing the session, will automatically unsubscribe from all topics the client is - // subscribed to. - session.Close(); - } - } - - /// - /// Basic implementation of the IValueStream for String topics. - /// - private sealed class StringStream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => WriteLine( "The subscrption stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => WriteLine( $"An error has occured : {errorReason}." ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => WriteLine( $"Client subscribed to {topicPath}." ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => WriteLine( $"Client unsubscribed from {topicPath} : {reason}." ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, string oldValue, string newValue ) - => WriteLine( $"New value of {topicPath} is {newValue}." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using static System.Console; + +namespace PushTechnology.ClientInterface.Example.Consuming { + /// + /// Implementation of a client which subscribes to a string topic and consumes the data it receives. + /// + public sealed class ConsumingStringTopics : IExample { + /// + /// Runs the string topic client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + string topic = "random/String"; + + // Add a topic stream for 'random/String' + var stringStream = new StringStream(); + topics.AddStream( topic, stringStream ); + + try { + // Subscribe to 'random/String' topic + await topics.SubscribeAsync( topic, cancellationToken ); + + // Run until user requests ending of example + await Task.Delay( Timeout.Infinite, cancellationToken ); + } catch(TaskCanceledException) { + //Task was canceled; close stream and unsubscribe + topics.RemoveStream( stringStream ); + await topics.UnsubscribeAsync( topic ); + } finally { + // Note that closing the session, will automatically unsubscribe from all topics the client is + // subscribed to. + session.Close(); + } + } + + /// + /// Basic implementation of the IValueStream for String topics. + /// + private sealed class StringStream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => WriteLine( "The subscrption stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => WriteLine( $"An error has occured : {errorReason}." ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => WriteLine( $"Client subscribed to topic '{topicPath}'." ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => WriteLine( $"Client unsubscribed from topic '{topicPath}' with reason '{reason}'." ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, string oldValue, string newValue ) + => WriteLine( $"New value of topic '{topicPath}' is '{newValue}'." ); + } + } +} diff --git a/dotnet/source/examples/Features/Authentication.cs b/dotnet/source/examples/Features/Authentication.cs deleted file mode 100644 index 1d65fc52..00000000 --- a/dotnet/source/examples/Features/Authentication.cs +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright © 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Clients; -using PushTechnology.ClientInterface.Client.Security.Authentication; -using PushTechnology.DiffusionCore.Client.Types; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using static System.Console; - -namespace PushTechnology.ClientInterface.Example.Features { - /// - /// Implementation of a client which authenticates other sessions. - /// - public sealed class Authentication : IExample { - /// - /// Runs the authenticator client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect as a control session - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - - try { - await session.AuthenticationControl.SetAuthenticationHandlerAsync( - "before-system-handler", new Authenticator(), cancellationToken ); - - // Run until user requests ending of example - await Task.Delay( Timeout.Infinite, cancellationToken ); - } catch ( TaskCanceledException ) { - //Task was canceled; - } finally { - session.Close(); - } - } - - /// - /// Basic implementation of . - /// - private sealed class Authenticator : IControlAuthenticator { - /// - /// Method which decides whether a connection attempt should be allowed, denied or - /// if another authenticator should evaluate this request. - /// - /// The session principal. - /// The credentials. - /// The session properties. - /// The client proposed properties. - /// The callback. - public void Authenticate( - string principal, - ICredentials credentials, - IReadOnlyDictionary sessionProperties, - IReadOnlyDictionary proposedProperties, - IAuthenticatorCallback callback ) { - - switch ( principal ) { - case "admin": { - WriteLine( "Authenticator allowing connection with proposed properties properties." ); - callback.Allow( proposedProperties ); - break; - } - case "client": { - WriteLine( "Authenticator allowing connection with no properties." ); - callback.Allow(); - break; - } - case "block": { - WriteLine( "Authenticator denying connection." ); - callback.Deny(); - break; - } - default: { - WriteLine( "Authenticator abstaining." ); - callback.Abstain(); - break; - } - } - } - /// - /// Notification of authenticator closure. - /// - public void OnClose() => WriteLine( "Authenticator closed." ); - - /// - /// Notification of error. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) => WriteLine( $"Received and error: {errorReason}" ); - } - } -} diff --git a/dotnet/source/examples/Features/AuthenticationControl.cs b/dotnet/source/examples/Features/AuthenticationControl.cs new file mode 100644 index 00000000..3e294e8a --- /dev/null +++ b/dotnet/source/examples/Features/AuthenticationControl.cs @@ -0,0 +1,157 @@ +/** + * Copyright © 2019, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features.Control.Clients; +using PushTechnology.ClientInterface.Client.Security.Authentication; +using PushTechnology.DiffusionCore.Client.Types; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using static System.Console; +using PushTechnology.ClientInterface.Client.Session; +using System; + +namespace PushTechnology.ClientInterface.Example.Features +{ + /// + /// Implementation of a client which authenticates other sessions. + /// + public sealed class AuthenticationControl : IExample + { + /// + /// Runs the authenticator client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run(CancellationToken cancellationToken, string[] args) + { + string serverUrl = args[0]; + + // Connect as a control session + var session = Diffusion.Sessions.Principal("control").Password("password") + .CertificateValidation((cert, chain, errors) => CertificateValidationResult.ACCEPT) + .Open(serverUrl); + + WriteLine("Opening control session."); + + IRegistration registration = null; + + try + { + registration = await session.AuthenticationControl.SetAuthenticationHandlerAsync( + "before-system-handler", new Authenticator(), cancellationToken); + + WriteLine("Authentication handler registered. Authenticator created."); + + Diffusion.Sessions.Principal("client") + .Credentials(Diffusion.Credentials.Password("password")) + .CertificateValidation((cert, chain, errors) => CertificateValidationResult.ACCEPT) + .Open(serverUrl, new SessionOpenCallback()); + + await Task.Delay(TimeSpan.FromMilliseconds(2000), cancellationToken); + } + catch (TaskCanceledException) + { + //Task was cancelled; + } + finally + { + WriteLine("Closing control session."); + + await registration.CloseAsync(); + session.Close(); + } + } + + private sealed class SessionOpenCallback : ISessionOpenCallback + { + public void OnError(ErrorReason errorReason) => WriteLine($"An error occurred: {errorReason}"); + + public void OnOpened(ISession session) + { + WriteLine("Other session opened."); + + session.Close(); + + WriteLine("Other session closed."); + } + } + + /// + /// Basic implementation of . + /// + private sealed class Authenticator : IControlAuthenticator + { + /// + /// Method which decides whether a connection attempt should be allowed, denied or + /// if another authenticator should evaluate this request. + /// + /// The session principal. + /// The credentials. + /// The session properties. + /// The client proposed properties. + /// The callback. + public void Authenticate( + string principal, + ICredentials credentials, + IReadOnlyDictionary sessionProperties, + IReadOnlyDictionary proposedProperties, + IAuthenticatorCallback callback) + { + + switch (principal) + { + case "admin": + { + WriteLine("Authenticator allowing connection with proposed properties properties."); + callback.Allow(proposedProperties); + break; + } + case "client": + { + WriteLine("Authenticator allowing connection with no properties."); + callback.Allow(); + break; + } + case "block": + { + WriteLine("Authenticator denying connection."); + callback.Deny(); + break; + } + default: + { + WriteLine("Authenticator abstaining."); + callback.Abstain(); + break; + } + } + } + /// + /// Notification of authenticator closure. + /// + public void OnClose() => WriteLine("Authenticator closed."); + + /// + /// Notification of error. + /// + /// Error reason. + public void OnError(ErrorReason errorReason) => WriteLine($"Authenticator received an error: {errorReason}"); + } + } +} diff --git a/dotnet/source/examples/Features/AutomaticRemoval.cs b/dotnet/source/examples/Features/AutomaticRemoval.cs index 4a52c25a..283cb7a7 100644 --- a/dotnet/source/examples/Features/AutomaticRemoval.cs +++ b/dotnet/source/examples/Features/AutomaticRemoval.cs @@ -1,149 +1,153 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using System; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using static System.Console; - -namespace PushTechnology.ClientInterface.Example.Features { - public class Removal : IExample { - /// Client implementation that removes topics based on different topic specifications. - /// The cancellation token to cancel the current example run. - /// A single string which should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - // Connect with control principal. - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - ITopicSpecification topicSpecification; - - // Topic to be removed after a set time has passed. - var topicPath = "removal/RemovalOnTime"; - - try { - // Get the time 30 seconds from now as a RFC1123 String. - var time = DateTime - .UtcNow - .AddSeconds( 30 ) - .ToString( DateTimeFormatInfo.InvariantInfo.RFC1123Pattern ); - - // Creating a topic specification with the required topic type and property. - topicSpecification = topicControl - .NewSpecification( TopicType.INT64 ) - .WithProperty( TopicSpecificationProperty.Removal, $"when time after \"{time}\"" ); - - // After adding this topic, the removal property will see to its deletion after the set time and date - // have passed. - await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); - WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", - topicPath, - TopicSpecificationProperty.Removal, - topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); - } catch ( Exception ex ) { - WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); - } - - // Topic to be removed after the session that created it is closed. - topicPath = "removal/RemovalOnSessionClosure"; - - try { - // Creating a topic specification with the required topic type and property. - topicSpecification = topicControl - .NewSpecification( TopicType.INT64 ) - .WithProperty( TopicSpecificationProperty.Removal, "when this session closes" ); - - // After adding this topic, the removal property will see to its deletion after the session closes. - await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); - WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", - topicPath, - TopicSpecificationProperty.Removal, - topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); - } catch ( Exception ex ) { - WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); - } - - // Topic to be removed after a set time has passed without any updates. - topicPath = "removal/RemovalOnNoUpdates"; - - try { - // Creating a topic specification with the required topic type and property. - topicSpecification = topicControl - .NewSpecification( TopicType.INT64 ) - .WithProperty( TopicSpecificationProperty.Removal, "when no updates for 10s" ); - - // After adding this topic, the removal property will see to its deletion when there have been - // no updates for 10 seconds. - await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); - WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", - topicPath, - TopicSpecificationProperty.Removal, - topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); - } catch ( Exception ex ) { - WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); - } - - // Topic to be removed if specified criteria is met. - topicPath = "removal/RemovalOnCriteria"; - - try { - // Creating a topic specification with the required topic type and property. - topicSpecification = topicControl - .NewSpecification( TopicType.INT64 ) - .WithProperty( TopicSpecificationProperty.Removal, - "when no session has \"$Principal is 'yourprincipal'\" after 5s" ); - - // After adding this topic, the removal property will see to its deletion when the criteria are met. - // In this case, it means that the 'yourprincipal' principal is not authenticated with any session. - await topicControl.AddTopicAsync( - topicPath, - topicSpecification, - cancellationToken ); - WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", - topicPath, - TopicSpecificationProperty.Removal, - topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); - } catch ( Exception ex ) { - WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); - } - - // Topic to be removed when the number of subscriptions falls below a specified value. - topicPath = "removal/RemovalOnLessSubscriptions"; - - try { - // Creating a topic specification with the required topic type and property. - topicSpecification = topicControl - .NewSpecification( TopicType.INT64 ) - .WithProperty( TopicSpecificationProperty.Removal, "when subscriptions < 1 for 10s" ); - - // After adding this topic, the removal property will see to its deletion when there are no subscribers. - await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); - WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", - topicPath, - TopicSpecificationProperty.Removal, - topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); - } catch ( Exception ex ) { - WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); - } - - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using System; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using static System.Console; + +namespace PushTechnology.ClientInterface.Example.Features { + public class Removal : IExample { + /// Client implementation that removes topics based on different topic specifications. + /// The cancellation token to cancel the current example run. + /// A single string which should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + var serverUrl = args[ 0 ]; + // Connect with control principal. + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + ITopicSpecification topicSpecification; + + // Topic to be removed after a set time has passed. + var topicPath = "removal/RemovalOnTime"; + + try { + // Get the time 30 seconds from now as a RFC1123 String. + var time = DateTime + .UtcNow + .AddSeconds( 30 ) + .ToString( DateTimeFormatInfo.InvariantInfo.RFC1123Pattern ); + + // Creating a topic specification with the required topic type and property. + topicSpecification = topicControl + .NewSpecification( TopicType.INT64 ) + .WithProperty( TopicSpecificationProperty.Removal, $"when time after \"{time}\"" ); + + // After adding this topic, the removal property will see to its deletion after the set time and date + // have passed. + await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); + WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", + topicPath, + TopicSpecificationProperty.Removal, + topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); + } catch ( Exception ex ) { + WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); + } + + // Topic to be removed after the session that created it is closed. + topicPath = "removal/RemovalOnSessionClosure"; + + try { + // Creating a topic specification with the required topic type and property. + topicSpecification = topicControl + .NewSpecification( TopicType.INT64 ) + .WithProperty( TopicSpecificationProperty.Removal, "when this session closes" ); + + // After adding this topic, the removal property will see to its deletion after the session closes. + await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); + WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", + topicPath, + TopicSpecificationProperty.Removal, + topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); + } catch ( Exception ex ) { + WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); + } + + // Topic to be removed after a set time has passed without any updates. + topicPath = "removal/RemovalOnNoUpdates"; + + try { + // Creating a topic specification with the required topic type and property. + topicSpecification = topicControl + .NewSpecification( TopicType.INT64 ) + .WithProperty( TopicSpecificationProperty.Removal, "when no updates for 10s" ); + + // After adding this topic, the removal property will see to its deletion when there have been + // no updates for 10 seconds. + await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); + WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", + topicPath, + TopicSpecificationProperty.Removal, + topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); + } catch ( Exception ex ) { + WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); + } + + // Topic to be removed if specified criteria is met. + topicPath = "removal/RemovalOnCriteria"; + + try { + // Creating a topic specification with the required topic type and property. + topicSpecification = topicControl + .NewSpecification( TopicType.INT64 ) + .WithProperty( TopicSpecificationProperty.Removal, + "when no session has \"$Principal is 'yourprincipal'\" after 5s" ); + + // After adding this topic, the removal property will see to its deletion when the criteria are met. + // In this case, it means that the 'yourprincipal' principal is not authenticated with any session. + await topicControl.AddTopicAsync( + topicPath, + topicSpecification, + cancellationToken ); + WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", + topicPath, + TopicSpecificationProperty.Removal, + topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); + } catch ( Exception ex ) { + WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); + } + + // Topic to be removed when the number of subscriptions falls below a specified value. + topicPath = "removal/RemovalOnLessSubscriptions"; + + try { + // Creating a topic specification with the required topic type and property. + topicSpecification = topicControl + .NewSpecification( TopicType.INT64 ) + .WithProperty( TopicSpecificationProperty.Removal, "when subscriptions < 1 for 10s" ); + + // After adding this topic, the removal property will see to its deletion when there are no subscribers. + await topicControl.AddTopicAsync( topicPath, topicSpecification, cancellationToken ); + WriteLine( "Topic \"{0}\" with the specification \"{1} : {2}\" has been added.", + topicPath, + TopicSpecificationProperty.Removal, + topicSpecification.Properties[ TopicSpecificationProperty.Removal ] ); + } catch ( Exception ex ) { + WriteLine( "Failed to add topic {0} : {1}.", topicPath, ex ); + } + + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Features/ClientUsingFetch.cs b/dotnet/source/examples/Features/ClientUsingFetch.cs index 3510f51e..f398001b 100644 --- a/dotnet/source/examples/Features/ClientUsingFetch.cs +++ b/dotnet/source/examples/Features/ClientUsingFetch.cs @@ -1,213 +1,213 @@ - -/******************************************************************************* - * Copyright (C) 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *******************************************************************************/ -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Data.JSON; -using static PushTechnology.ClientInterface.Examples.Runner.Program; -using System; -using PushTechnology.ClientInterface.Data; -using PushTechnology.ClientInterface.Client.Topics; -using PushTechnology.ClientInterface.Client.Session; - -namespace PushTechnology.ClientInterface.Example.Features { - /// - /// Implementation of a client which utlises the Fetch API. - /// - - - public class IFetch : IExample { - ITopics topics; - ISession session; - - - /// - /// Runs the authenticator client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - - var serverUrl = args[ 0 ]; - - session = Diffusion.Sessions - .Principal( "client" ) - .Password( "password" ) - .Open( serverUrl ); - - topics = session.Topics; - - var topicCtl = session.TopicControl; - - for(char a = 'a'; a <= 'z'; ++a ) { - - if (a % 2 == 0) { - await topicCtl.AddTopicAsync( "?a//" + a, TopicType.JSON ); - } else if (a % 3 == 0) { - await topicCtl.AddTopicAsync( "?a//" + a, TopicType.STRING ); - } - } - - - var res = fetchAll(); - Console.WriteLine( "All results" ); - foreach (var topic in res.Results) { - Console.WriteLine(topic.Path); - } - - - res = fetchAllStringTopics("*.*"); - Console.WriteLine( "All string results" ); - foreach ( var topic in res.Results ) { - Console.WriteLine( topic.Path ); - } - - - var resStringTopic = fetchStringTopic( "a/b" ); - Console.WriteLine( "\n" + resStringTopic.Path ); - Console.WriteLine( resStringTopic.Type ); - Console.WriteLine( resStringTopic.Value ); - Console.WriteLine( resStringTopic.Specification + "\n"); - - - Console.WriteLine( getStringTopicValue( "a/b" ) + "\n" ); - - - var resJSON = fetchJSONTopics( "*.*" ); - Console.WriteLine( "\nAll JSON results" ); - foreach ( var topic in resJSON.Results ) { - Console.WriteLine( topic.Path ); - } - - - var resBytes = fetchRange( "?a//a", "?a//c" ); - Console.WriteLine("\nAll topics between a/a and a/c inclusive" ); - foreach ( var topic in resBytes.Results ) { - Console.WriteLine( topic.Path ); - } - - - var resNext = next( "?a//a", 2 ); - Console.WriteLine( "\nThe 2 next lexographic topics after a/a" ); - foreach ( var topic in resNext.Results ) { - Console.WriteLine( topic.Path ); - } - - - var resPrior = prior( "?a//e", 3 ); - Console.WriteLine( "\nThe 3 prior lexographic topics before a/e" ); - foreach ( var topic in resPrior.Results ) { - Console.WriteLine( topic.Path ); - } - - var resLimit = limitDeep(3, 3); - Console.WriteLine("\nProduce results that are at most 3 parts deep, with a maximum of 3 results per deep branch."); - foreach (var topic in resLimit.Results) - { - Console.WriteLine(topic.Path); - } - } - - - /// - /// This shows an example of retrieving all topics - only topic path and type - /// are returned in each result. - /// - public IFetchResult fetchAll() => topics.FetchRequest.FetchAsync("*.*").Result; - - - /// - /// This shows an example of retrieving all string topics that satisfy a - /// specified topic selector with values. - /// - /// The topic selector to be used - public IFetchResult fetchAllStringTopics(string topicSelector) => topics.FetchRequest.WithValues().FetchAsync(topicSelector).Result; - - - /// - ///This shows an example of retrieving a single string topic with value and - ///properties. - /// - /// The topic path to be used - public ITopicResult fetchStringTopic(string topicPath) => topics.FetchRequest.WithValues().WithProperties().FetchAsync(topicPath).Result.Results.GetEnumerator().Current; - - - /// - ///This shows how to obtain the value of a specified string topic. - ///

- ///

- /// The topic path to be used - public string getStringTopicValue(string topicPath) => fetchStringTopic(topicPath)?.Value; - - /// - ///This shows an example of retrieving all JSON topics that match a - ///specified selector with values. - /// - ///This would return results only for JSON topics and not type compatible - ///subtypes. - /// - /// The topic selector to be used - public IFetchResult fetchJSONTopics(string topicSelector) => topics.FetchRequest.WithValues().TopicTypes(new [] {TopicType.JSON}).FetchAsync(topicSelector).Result; - - - /// - ///Shows how to obtain an inclusive range of topics, with values. - /// - /// The topic selector to be used as a starting point - /// The topic selector to be used as an ending point - public IFetchResult fetchRange( string from, string to ) => topics.FetchRequest.From(from).To(to).WithValues().FetchAsync("*.*").Result; - - - /// - ///Shows how to obtain the next group of topics, with values, from a - ///specified start point. - /// - ///This demonstrates paging and could be used repeatedly specifying the - ///after value as the path of the last topic retrieved from the previous - ///call of the next method. The {@link FetchResult#hasMore() hasMore} method - ///on the result can be used to determine whether there may be more results. - /// - ///Bytes is used as the value type so that all topic types are selected. - /// - /// The topic selector to be used as a starting point - /// The number of topics to fetch after the starting point - public IFetchResult next( string after, int limit) => topics.FetchRequest.After(after).WithValues().First(limit).FetchAsync("*.*").Result; - - - /// - ///Shows how to obtain the prior group of topics, with values, from a - ///specified end point. - ///This demonstrates paging and could be used to retrieve the set of topics - ///prior to the first topic from a previous call of prior or next. - /// - /// The topic selector to be used as a starting point - /// The number of topics to fetch before the starting point - public IFetchResult prior( string before, int limit) => topics.FetchRequest.Before(before).WithValues().Last(limit).FetchAsync("*.*").Result; - - /// - ///Shows how to specify a limit on the number of results returned for each deep branch. - ///This demonstrates a method that could be particularly useful for incrementally - ///exploring a topic tree from the root, allowing a breadth-first search strategy. - /// - /// The number of parts in the root path of a branch for it to be considered deep - /// The maximum number of results to return for each deep branch - public IFetchResult limitDeep(int deepBranchDepth, int deepBranchLimit) => topics.FetchRequest.LimitDeepBranches(deepBranchDepth, deepBranchLimit).WithValues().FetchAsync("*.*").Result; - - } - -} - + +/******************************************************************************* + * Copyright (C) 2019 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Data.JSON; +using static PushTechnology.ClientInterface.Examples.Runner.Program; +using System; +using PushTechnology.ClientInterface.Data; +using PushTechnology.ClientInterface.Client.Topics; +using PushTechnology.ClientInterface.Client.Session; + +namespace PushTechnology.ClientInterface.Example.Features { + /// + /// Implementation of a client which utlises the Fetch API. + /// + + + public class IFetch : IExample { + ITopics topics; + ISession session; + + + /// + /// Runs the authenticator client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + + var serverUrl = args[ 0 ]; + + session = Diffusion.Sessions + .Principal( "client" ) + .Password( "password" ) + .Open( serverUrl ); + + topics = session.Topics; + + var topicCtl = session.TopicControl; + + for(char a = 'a'; a <= 'z'; ++a ) { + + if (a % 2 == 0) { + await topicCtl.AddTopicAsync( "?a//" + a, TopicType.JSON ); + } else if (a % 3 == 0) { + await topicCtl.AddTopicAsync( "?a//" + a, TopicType.STRING ); + } + } + + + var res = fetchAll(); + Console.WriteLine( "All results" ); + foreach (var topic in res.Results) { + Console.WriteLine(topic.Path); + } + + + res = fetchAllStringTopics("*.*"); + Console.WriteLine( "All string results" ); + foreach ( var topic in res.Results ) { + Console.WriteLine( topic.Path ); + } + + + var resStringTopic = fetchStringTopic( "a/b" ); + Console.WriteLine( "\n" + resStringTopic.Path ); + Console.WriteLine( resStringTopic.Type ); + Console.WriteLine( resStringTopic.Value ); + Console.WriteLine( resStringTopic.Specification + "\n"); + + + Console.WriteLine( getStringTopicValue( "a/b" ) + "\n" ); + + + var resJSON = fetchJSONTopics( "*.*" ); + Console.WriteLine( "\nAll JSON results" ); + foreach ( var topic in resJSON.Results ) { + Console.WriteLine( topic.Path ); + } + + + var resBytes = fetchRange( "?a//a", "?a//c" ); + Console.WriteLine("\nAll topics between a/a and a/c inclusive" ); + foreach ( var topic in resBytes.Results ) { + Console.WriteLine( topic.Path ); + } + + + var resNext = next( "?a//a", 2 ); + Console.WriteLine( "\nThe 2 next lexographic topics after a/a" ); + foreach ( var topic in resNext.Results ) { + Console.WriteLine( topic.Path ); + } + + + var resPrior = prior( "?a//e", 3 ); + Console.WriteLine( "\nThe 3 prior lexographic topics before a/e" ); + foreach ( var topic in resPrior.Results ) { + Console.WriteLine( topic.Path ); + } + + var resLimit = limitDeep(3, 3); + Console.WriteLine("\nProduce results that are at most 3 parts deep, with a maximum of 3 results per deep branch."); + foreach (var topic in resLimit.Results) + { + Console.WriteLine(topic.Path); + } + } + + + /// + /// This shows an example of retrieving all topics - only topic path and type + /// are returned in each result. + /// + public IFetchResult fetchAll() => topics.FetchRequest.FetchAsync("*.*").Result; + + + /// + /// This shows an example of retrieving all string topics that satisfy a + /// specified topic selector with values. + /// + /// The topic selector to be used + public IFetchResult fetchAllStringTopics(string topicSelector) => topics.FetchRequest.WithValues().FetchAsync(topicSelector).Result; + + + /// + ///This shows an example of retrieving a single string topic with value and + ///properties. + /// + /// The topic path to be used + public ITopicResult fetchStringTopic(string topicPath) => topics.FetchRequest.WithValues().WithProperties().FetchAsync(topicPath).Result.Results.GetEnumerator().Current; + + + /// + ///This shows how to obtain the value of a specified string topic. + ///

+ ///

+ /// The topic path to be used + public string getStringTopicValue(string topicPath) => fetchStringTopic(topicPath)?.Value; + + /// + ///This shows an example of retrieving all JSON topics that match a + ///specified selector with values. + /// + ///This would return results only for JSON topics and not type compatible + ///subtypes. + /// + /// The topic selector to be used + public IFetchResult fetchJSONTopics(string topicSelector) => topics.FetchRequest.WithValues().TopicTypes(new [] {TopicType.JSON}).FetchAsync(topicSelector).Result; + + + /// + ///Shows how to obtain an inclusive range of topics, with values. + /// + /// The topic selector to be used as a starting point + /// The topic selector to be used as an ending point + public IFetchResult fetchRange( string from, string to ) => topics.FetchRequest.From(from).To(to).WithValues().FetchAsync("*.*").Result; + + + /// + ///Shows how to obtain the next group of topics, with values, from a + ///specified start point. + /// + ///This demonstrates paging and could be used repeatedly specifying the + ///after value as the path of the last topic retrieved from the previous + ///call of the next method. The {@link FetchResult#hasMore() hasMore} method + ///on the result can be used to determine whether there may be more results. + /// + ///Bytes is used as the value type so that all topic types are selected. + /// + /// The topic selector to be used as a starting point + /// The number of topics to fetch after the starting point + public IFetchResult next( string after, int limit) => topics.FetchRequest.After(after).WithValues().First(limit).FetchAsync("*.*").Result; + + + /// + ///Shows how to obtain the prior group of topics, with values, from a + ///specified end point. + ///This demonstrates paging and could be used to retrieve the set of topics + ///prior to the first topic from a previous call of prior or next. + /// + /// The topic selector to be used as a starting point + /// The number of topics to fetch before the starting point + public IFetchResult prior( string before, int limit) => topics.FetchRequest.Before(before).WithValues().Last(limit).FetchAsync("*.*").Result; + + /// + ///Shows how to specify a limit on the number of results returned for each deep branch. + ///This demonstrates a method that could be particularly useful for incrementally + ///exploring a topic tree from the root, allowing a breadth-first search strategy. + /// + /// The number of parts in the root path of a branch for it to be considered deep + /// The maximum number of results to return for each deep branch + public IFetchResult limitDeep(int deepBranchDepth, int deepBranchLimit) => topics.FetchRequest.LimitDeepBranches(deepBranchDepth, deepBranchLimit).WithValues().FetchAsync("*.*").Result; + + } + +} + diff --git a/dotnet/source/examples/Features/Features.projitems b/dotnet/source/examples/Features/Features.projitems index 5be00e31..ec829a1e 100644 --- a/dotnet/source/examples/Features/Features.projitems +++ b/dotnet/source/examples/Features/Features.projitems @@ -1,17 +1,17 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - cbf43c7f-76a7-4476-8f01-ab2f4c89f90e - - - PushTechnology.ClientInterface.Example.Features - - - - - - - + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + cbf43c7f-76a7-4476-8f01-ab2f4c89f90e + + + PushTechnology.ClientInterface.Example.Features + + + + + + + \ No newline at end of file diff --git a/dotnet/source/examples/Features/Features.shproj b/dotnet/source/examples/Features/Features.shproj index 58d0757b..77ef0ab9 100644 --- a/dotnet/source/examples/Features/Features.shproj +++ b/dotnet/source/examples/Features/Features.shproj @@ -1,13 +1,13 @@ - - - - cbf43c7f-76a7-4476-8f01-ab2f4c89f90e - 14.0 - - - - - - - - + + + + cbf43c7f-76a7-4476-8f01-ab2f4c89f90e + 14.0 + + + + + + + + diff --git a/dotnet/source/examples/Features/PingServer.cs b/dotnet/source/examples/Features/PingServer.cs index 7d3f80a3..d1841268 100644 --- a/dotnet/source/examples/Features/PingServer.cs +++ b/dotnet/source/examples/Features/PingServer.cs @@ -1,70 +1,70 @@ -/** - * Copyright © 2016, 2017 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Session; - -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Features { - /// - /// This example shows how a client session is able to ping the server. - /// - public sealed class PingServer : IExample { - /// - /// Runs the server ping example. - /// - /// The cancellation token to cancel the current example run. - /// The optional example arguments. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - - // Connect anonymously - var session = Diffusion.Sessions.Open( serverUrl ); - - // Access the pings feature - var pings = session.Ping; - - // Loop until cancelled - while ( !cancellationToken.IsCancellationRequested ) { - // Ping server roughly every second - await Task.Delay( TimeSpan.FromSeconds( 1 ) ); - - try { - // Ping the server. A overload without a cancellation token is available. - var details = await pings.PingServerAsync( cancellationToken ); - - // Print out details - WriteLine( $"Pinged server at {details.Timestamp}. Received answer after {details.RoundTripTimeSpan}." ); - - } catch ( SessionClosedException ) { - WriteLine( "Ping failed due to a closed session." ); - - } catch ( SessionException ) { - WriteLine( "Ping failed due to a communication failure." ); - - } catch ( TaskCanceledException ) { - WriteLine( "Ping failed due to a manual cancellation." ); - } - } - - session.Close(); - } - } -} +/** + * Copyright © 2016, 2017 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; + +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Features { + /// + /// This example shows how a client session is able to ping the server. + /// + public sealed class PingServer : IExample { + /// + /// Runs the server ping example. + /// + /// The cancellation token to cancel the current example run. + /// The optional example arguments. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + var serverUrl = args[ 0 ]; + + // Connect anonymously + var session = Diffusion.Sessions.Open( serverUrl ); + + // Access the pings feature + var pings = session.Ping; + + // Loop until cancelled + while ( !cancellationToken.IsCancellationRequested ) { + // Ping server roughly every second + await Task.Delay( TimeSpan.FromSeconds( 1 ) ); + + try { + // Ping the server. A overload without a cancellation token is available. + var details = await pings.PingServerAsync( cancellationToken ); + + // Print out details + WriteLine( $"Pinged server at {details.Timestamp}. Received answer after {details.RoundTripTimeSpan}." ); + + } catch ( SessionClosedException ) { + WriteLine( "Ping failed due to a closed session." ); + + } catch ( SessionException ) { + WriteLine( "Ping failed due to a communication failure." ); + + } catch ( TaskCanceledException ) { + WriteLine( "Ping failed due to a manual cancellation." ); + } + } + + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/AddAndSetTopic.cs b/dotnet/source/examples/Publishing/AddAndSetTopic.cs index 2439980e..42a25d98 100644 --- a/dotnet/source/examples/Publishing/AddAndSetTopic.cs +++ b/dotnet/source/examples/Publishing/AddAndSetTopic.cs @@ -1,63 +1,76 @@ -/** - * Copyright © 2020 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Topics; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that adds a topic if it doesn't exist, or updates the same topic if it does exist. - /// - public sealed class AddAndSetTopic : IExample { - /// - /// Runs the AddAndSet topic update client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancel, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicUpdate = session.TopicUpdate; - - try { - // Attempt to add a topic, set its value to 0, and await the response from the server. - // If there was no topic previously bound to "test/topic", the method will return and create the topic, set the value, and return false. - // If there is an existing INT64 topic with default topic properties bound to "test/topic" the method will set its value to 0. - // If an incompatible topic is bound to "test/topic", the method will throw ExistingTopicException. - var doesExist = await topicUpdate.AddAndSetAsync( "test/topic", session.TopicControl.NewSpecification( TopicType.INT64 ), 0 ); - - WriteLine( "Topic test/topic exists:" + doesExist + "with value: " + 0 ); - - doesExist = await topicUpdate.AddAndSetAsync( "test/topic", session.TopicControl.NewSpecification( TopicType.INT64 ), 1 ); - - WriteLine( "Topic test/topic exists:" + doesExist + "with value: " + 1 ); - - } catch ( Exception ex ) { - WriteLine( $"Failed to add topic : {ex}." ); - return; - } finally { - session.Close(); - } - } - } -} - - +/** + * Copyright © 2020, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that adds a topic if it doesn't exist, or updates the same topic if it does exist. + /// + public sealed class AddAndSetTopic : IExample { + /// + /// Runs the AddAndSet topic update client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancel, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicUpdate = session.TopicUpdate; + + try { + // Attempt to add a topic, set its value to 0, and await the response from the server. + // If there was no topic previously bound to "test/topic", the method will return and create the topic and set its value to 0. + // If there is an existing INT64 topic with default topic properties bound to "test/topic" the method will set its value to 1. + // If an incompatible topic is bound to "test/topic", the method will throw ExistingTopicException. + var state = await topicUpdate.AddAndSetAsync( "test/topic", session.TopicControl.NewSpecification( TopicType.INT64 ), 0L ); + + WriteLine( $"Topic 'test/topic' {state} with value: 0." ); + + state = await topicUpdate.AddAndSetAsync( "test/topic", session.TopicControl.NewSpecification( TopicType.INT64 ), 1L ); + + WriteLine( $"Topic 'test/topic' {state} with value: 1." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to add and set topic 'test/topic': {ex}." ); + session.Close(); + + return; + } + + try { + await session.TopicControl.RemoveTopicsAsync( "test/topic" ); + + WriteLine( $"Removed topic 'test/topic'." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to remove topic 'test/topic': {ex}." ); + } finally { + session.Close(); + } + } + } +} + + diff --git a/dotnet/source/examples/Publishing/AddMissingTopicHandler.cs b/dotnet/source/examples/Publishing/AddMissingTopicHandler.cs new file mode 100644 index 00000000..489e995a --- /dev/null +++ b/dotnet/source/examples/Publishing/AddMissingTopicHandler.cs @@ -0,0 +1,189 @@ +/** + * Copyright © 2022 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features.Control.Topics; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing +{ + /// + /// Control client implementation of a missing topic handler. + /// + public sealed class AddMissingTopicHandler : IExample + { + private const string TOPIC_PREFIX = "Example"; + private ISession session; + + /// + /// Runs the client missing topic handler example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run(CancellationToken cancellationToken, string[] args) + { + var selector = $"?{TOPIC_PREFIX}//"; + var topicPath = $"{TOPIC_PREFIX}"; + + var serverUrl = args[0]; + session = Diffusion.Sessions.Principal("control").Password("password") + .CertificateValidation((cert, chain, errors) => CertificateValidationResult.ACCEPT) + .Open(serverUrl); + + var clientSession = Diffusion.Sessions.Principal("client").Password("password") + .CertificateValidation((cert, chain, errors) => CertificateValidationResult.ACCEPT) + .Open(serverUrl); + + IRegistration registration = null; + + try + { + WriteLine($"Adding missing topic handler for topic '{topicPath}'."); + + registration = await session.TopicControl.AddMissingTopicHandlerAsync(topicPath, new MissingTopicNotificationStream(session)); + } + catch (Exception ex) + { + WriteLine($"Failed to add missing topic handler : {ex}."); + + clientSession.Close(); + session.Close(); + return; + } + + try + { + WriteLine($"Subscribing to topic '{topicPath}'."); + + await clientSession.Topics.SubscribeAsync(selector, cancellationToken); + + await Task.Delay(TimeSpan.FromSeconds(1)); + } + catch (Exception ex) + { + WriteLine($"Subscribing to topic '{topicPath}' failed : {ex}."); + + await registration.CloseAsync(); + + clientSession.Close(); + session.Close(); + return; + } + + try//Clear up + { + await session.TopicControl.RemoveTopicsAsync(topicPath); + + WriteLine($"Topic '{topicPath}' removed."); + + await clientSession.Topics.UnsubscribeAsync(selector, cancellationToken); + + WriteLine($"Unsubscribing to topic '{topicPath}'."); + + await registration.CloseAsync(); + } + catch(Exception ex) + { + WriteLine($"Clear up failed : {ex}."); + } + + clientSession.Close(); + session.Close(); + } + + /// + /// Basic implementation of the stream that will be called when a session subscribes using a topic selector + /// that matches no topics. + /// + private sealed class MissingTopicNotificationStream : IMissingTopicNotificationStream + { + private ISession session; + + public MissingTopicNotificationStream(ISession session) => this.session = session; + + /// + /// Notification of the stream being closed normally. + /// + public void OnClose() + => WriteLine("Handler is removed."); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError(ErrorReason errorReason) + => WriteLine($"An error has occured : {errorReason}."); + + /// + /// Called when a session requests a topic that does not exist, and the topic path + /// belongs to part of the topic tree for which this stream was registered. + /// + /// The missing topic notification. + public void OnMissingTopic(IMissingTopicNotification notification) + { + WriteLine($"Topic '{notification.TopicPath}' does not exist."); + + session.TopicControl.AddTopic(notification.TopicPath, session.TopicControl.NewSpecification(TopicType.STRING), new TopicControlAddCallback(notification)); + } + } + + /// + /// Implementation of a callback interface for adding topics. + /// + private sealed class TopicControlAddCallback : ITopicControlAddCallback + { + IMissingTopicNotification notification; + + public TopicControlAddCallback(IMissingTopicNotification notification) => this.notification = notification; + + /// + /// Called to notify that the session is closed. + /// + public void OnDiscard() => WriteLine("The stream is now closed."); + + /// + /// Called to indicate that the topic has been successfully added. + /// + /// The path to the topic. + public void OnTopicAdded(string topicPath) + { + WriteLine($"Topic '{topicPath}' added."); + notification.Proceed(); + } + + /// + /// Called to indicate that an attempt to add a topic has failed. + /// + /// The path to the topic. + /// The reason why the topic could not be added. + public void OnTopicAddFailed(string topicPath, TopicAddFailReason reason) + { + WriteLine($"The topic could not be added with reason: {reason}."); + notification.Cancel(); + } + } + } +} diff --git a/dotnet/source/examples/Publishing/BinaryTopics.cs b/dotnet/source/examples/Publishing/BinaryTopics.cs index 277117aa..eb0efc32 100644 --- a/dotnet/source/examples/Publishing/BinaryTopics.cs +++ b/dotnet/source/examples/Publishing/BinaryTopics.cs @@ -1,83 +1,87 @@ -/** - * Copyright © 2018, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using PushTechnology.ClientInterface.Data.Binary; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that adds and updates a binary topic. - /// - public sealed class PublishingBinaryTopics : IExample { - /// - /// Runs the binary topic control client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - var topicUpdate = session.TopicUpdate; - - var random = new Random(); - - // Create a binary topic 'random/Binary' - var topic = "random/Binary"; - - try { - await topicControl.AddTopicAsync( topic, TopicType.BINARY, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to add topic '{topic}' : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms until user requests cancellation of example - while ( !cancellationToken.IsCancellationRequested ) { - var newValue = Diffusion.DataTypes.Binary.ReadValue( - Encoding.UTF8.GetBytes( DateTime.Now.ToLongTimeString() ) ); - - try { - await topicUpdate.SetAsync( topic, newValue, cancellationToken ); - Console.WriteLine( $"Topic {topic} updated successfully." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); - } catch ( Exception ex ) { - Console.WriteLine( $"Topic {topic} could not be updated : {ex}." ); - } - } - - // Remove the binary topic 'random/Binary' - try { - await topicControl.RemoveTopicsAsync( topic, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that adds and updates a binary topic. + /// + public sealed class PublishingBinaryTopics : IExample { + /// + /// Runs the binary topic control client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + var topicUpdate = session.TopicUpdate; + + var random = new Random(); + + // Create a binary topic 'random/Binary' + string topic = "random/Binary"; + + try { + await topicControl.AddTopicAsync( topic, TopicType.BINARY, cancellationToken ); + + WriteLine( $"Topic '{topic}' added successfully." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to add topic '{topic}' : {ex}." ); + session.Close(); + return; + } + + WriteLine( $"Updating topic '{topic}' with new values:" ); + + // Update topic every 300 ms until user requests cancellation of example + while ( !cancellationToken.IsCancellationRequested ) { + var newValue = Diffusion.DataTypes.Binary.ReadValue( + Encoding.UTF8.GetBytes( DateTime.Now.ToLongTimeString() ) ); + + try { + await topicUpdate.SetAsync( topic, newValue, cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); + } catch ( Exception ex ) { + WriteLine( $"Topic {topic} could not be updated : {ex}." ); + } + } + + // Remove the binary topic 'random/Binary' + try { + await topicControl.RemoveTopicsAsync( topic, cancellationToken ); + } catch ( Exception ex ) { + WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/DoubleTopics.cs b/dotnet/source/examples/Publishing/DoubleTopics.cs index 238be8d2..ee9937c7 100644 --- a/dotnet/source/examples/Publishing/DoubleTopics.cs +++ b/dotnet/source/examples/Publishing/DoubleTopics.cs @@ -1,80 +1,85 @@ -/** - * Copyright © 2018, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that adds and updates a double topic. - /// - public sealed class PublishingDoubleTopics : IExample { - /// - /// Runs the double topic control client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - var topicUpdate = session.TopicUpdate; - - var random = new Random(); - - // Create a double topic 'random/Double' - var topic = "random/Double"; - - try { - await topicControl.AddTopicAsync( topic, TopicType.DOUBLE, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to add topic '{topic}' : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms until user requests cancellation of example - while ( !cancellationToken.IsCancellationRequested ) { - var newValue = random.NextDouble(); - - try { - await topicUpdate.SetAsync( topic, newValue, cancellationToken ); - Console.WriteLine( $"Topic {topic} updated successfully." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); - } catch ( Exception ex ) { - Console.WriteLine( $"Topic {topic} could not be updated : {ex}." ); - } - } - - // Remove the double topic 'random/Double' - try { - await topicControl.RemoveTopicsAsync( topic, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that adds and updates a double topic. + /// + public sealed class PublishingDoubleTopics : IExample { + /// + /// Runs the double topic control client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + var topicUpdate = session.TopicUpdate; + + var random = new Random(); + + // Create a double topic 'random/Double' + string topic = "random/Double"; + + try { + await topicControl.AddTopicAsync( topic, TopicType.DOUBLE, cancellationToken ); + + WriteLine( $"Topic '{topic}' added successfully." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to add topic '{topic}' : {ex}." ); + session.Close(); + return; + } + + WriteLine( $"Updating topic '{topic}' with new values:" ); + + // Update topic every 300 ms until user requests cancellation of example + while ( !cancellationToken.IsCancellationRequested ) { + double newValue = random.NextDouble(); + + try { + await topicUpdate.SetAsync( topic, newValue, cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); + } catch ( Exception ex ) { + WriteLine( $"Topic {topic} could not be updated : {ex}." ); + } + } + + // Remove the double topic 'random/Double' + try { + await topicControl.RemoveTopicsAsync( topic, cancellationToken ); + } catch ( Exception ex ) { + WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/FilterRequestMessages.cs b/dotnet/source/examples/Publishing/FilterRequestMessages.cs index 2445213a..7df58226 100644 --- a/dotnet/source/examples/Publishing/FilterRequestMessages.cs +++ b/dotnet/source/examples/Publishing/FilterRequestMessages.cs @@ -1,90 +1,93 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Session; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that sends request messages to a filter and if it gets response displays it on the - /// system console, and sends another request directly to the session. - /// - public sealed class SendingFilterRequestMessages : IExample { - private readonly string messagingPath = ">random/requestResponse"; - - /// - /// Runs the client sending request/response messages example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var messaging = session.Messaging; - var requestCallback = new RequestCallback(); - - while ( !cancellationToken.IsCancellationRequested ) { - int requestsSent = await messaging.SendRequestToFilterAsync( - "$Principal EQ 'client'", - messagingPath, - "Time", - requestCallback, - cancellationToken ); - WriteLine( $"Sent request to {requestsSent} session(s) matching the filter." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); - } - - // Close the session - session.Close(); - } - - /// - /// A simple IFilteredRequestCallback implementation that prints confirmation of the actions completed. - /// - private class RequestCallback : IFilteredRequestCallback { - /// - /// Indicates that the stream was closed. - /// - public void OnClose() - => WriteLine( "A request handler was closed." ); - - /// - /// Indicates error received by the callback. - /// - public void OnError( ErrorReason errorReason ) - => WriteLine( $"A request handler has received error: '{errorReason}'." ); - - /// - /// Indicates that a response message was received. - /// - public void OnResponse( ISessionId sessionId, string response ) - => WriteLine( $"Received response: '{response}'." ); - - /// - /// Indicates that a error response message was received. - /// - public void OnResponseError( ISessionId sessionId, Exception exception ) - => WriteLine( $"Response error received from session {sessionId}: '{exception}'." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Session; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that sends request messages to a filter and if it gets response displays it on the + /// system console, and sends another request directly to the session. + /// + public sealed class SendingFilterRequestMessages : IExample { + private readonly string messagingPath = ">random/requestResponse"; + + /// + /// Runs the client sending request/response messages example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var messaging = session.Messaging; + var requestCallback = new RequestCallback(); + + while ( !cancellationToken.IsCancellationRequested ) { + int requestsSent = await messaging.SendRequestToFilterAsync( + "$Principal EQ 'client'", + messagingPath, + "Time", + requestCallback, + cancellationToken ); + WriteLine( $"Sent request to {requestsSent} session(s) matching the filter." ); + + await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); + } + + // Close the session + session.Close(); + } + + /// + /// A simple IFilteredRequestCallback implementation that prints confirmation of the actions completed. + /// + private class RequestCallback : IFilteredRequestCallback { + /// + /// Indicates that the stream was closed. + /// + public void OnClose() + => WriteLine( "A request handler was closed." ); + + /// + /// Indicates error received by the callback. + /// + public void OnError( ErrorReason errorReason ) + => WriteLine( $"A request handler has received error: '{errorReason}'." ); + + /// + /// Indicates that a response message was received. + /// + public void OnResponse( ISessionId sessionId, string response ) + => WriteLine( $"Received response: '{response}'." ); + + /// + /// Indicates that a error response message was received. + /// + public void OnResponseError( ISessionId sessionId, Exception exception ) + => WriteLine( $"Response error received from session {sessionId}: '{exception}'." ); + } + } +} diff --git a/dotnet/source/examples/Publishing/IntegerTopics.cs b/dotnet/source/examples/Publishing/IntegerTopics.cs index a43c9c6f..f0bfbaba 100644 --- a/dotnet/source/examples/Publishing/IntegerTopics.cs +++ b/dotnet/source/examples/Publishing/IntegerTopics.cs @@ -1,80 +1,85 @@ -/** - * Copyright © 2018, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that adds and updates an integer topic. - /// - public sealed class PublishingIntegerTopics : IExample { - /// - /// Runs the integer topic control client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - var topicUpdate = session.TopicUpdate; - - var random = new Random(); - - // Create a Integer topic 'random/Integer' - var topic = "random/Integer"; - - try { - await topicControl.AddTopicAsync( topic, TopicType.INT64, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to add topic '{topic}' : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms until user requests cancellation of example - while ( !cancellationToken.IsCancellationRequested ) { - var newValue = (long) random.Next(); - - try { - await topicUpdate.SetAsync( topic, newValue, cancellationToken ); - Console.WriteLine( $"Topic {topic} updated successfully." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); - } catch ( Exception ex ) { - Console.WriteLine( $"Topic {topic} could not be updated : {ex}." ); - } - } - - // Remove the Integer topic 'random/Integer' - try { - await topicControl.RemoveTopicsAsync( topic, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that adds and updates an integer topic. + /// + public sealed class PublishingIntegerTopics : IExample { + /// + /// Runs the integer topic control client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + var topicUpdate = session.TopicUpdate; + + var random = new Random(); + + // Create a Integer topic 'random/Integer' + string topic = "random/Integer"; + + try { + await topicControl.AddTopicAsync( topic, TopicType.INT64, cancellationToken ); + + WriteLine( $"Topic '{topic}' added successfully." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to add topic '{topic}' : {ex}." ); + session.Close(); + return; + } + + WriteLine( $"Updating topic '{topic}' with new values:" ); + + // Update topic every 300 ms until user requests cancellation of example + while ( !cancellationToken.IsCancellationRequested ) { + long newValue = (long) random.Next(); + + try { + await topicUpdate.SetAsync( topic, newValue, cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); + } catch ( Exception ex ) { + WriteLine( $"Topic {topic} could not be updated : {ex}." ); + } + } + + // Remove the Integer topic 'random/Integer' + try { + await topicControl.RemoveTopicsAsync( topic, cancellationToken ); + } catch ( Exception ex ) { + WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/JSONTopics.cs b/dotnet/source/examples/Publishing/JSONTopics.cs index 43c8e006..6663d71d 100644 --- a/dotnet/source/examples/Publishing/JSONTopics.cs +++ b/dotnet/source/examples/Publishing/JSONTopics.cs @@ -1,83 +1,86 @@ -/** - * Copyright © 2016, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using PushTechnology.ClientInterface.Data.JSON; - -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing -{ - /// - /// Control client implementation that adds and updates a JSON topic. - /// - public sealed class PublishingJSONTopics : IExample { - /// - /// Runs the JSON topic control client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - var topicUpdate = session.TopicUpdate; - - // Create a JSON topic 'random/JSON' - var topic = "random/JSON"; - - try { - await topicControl.AddTopicAsync( topic, TopicType.JSON, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to add topic '{topic}' : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms until user requests cancellation of example - while ( !cancellationToken.IsCancellationRequested ) { - var newValue = Diffusion.DataTypes.JSON.FromJSONString( - "{\"date\":\"" + DateTime.Today.Date.ToString( "D" ) + "\"," + - "\"time\":\"" + DateTime.Now.TimeOfDay.ToString( "g" ) + "\"}" ); - - try { - await topicUpdate.SetAsync( topic, newValue, cancellationToken ); - Console.WriteLine( $"Topic {topic} updated successfully." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); - } catch ( Exception ex ) { - Console.WriteLine( $"Topic {topic} could not be updated : {ex}." ); - } - } - - // Remove the JSON topic 'random/JSON' - try { - await topicControl.RemoveTopicsAsync( topic, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2016, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; + +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing +{ + /// + /// Control client implementation that adds and updates a JSON topic. + /// + public sealed class PublishingJSONTopics : IExample { + /// + /// Runs the JSON topic control client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + var topicUpdate = session.TopicUpdate; + + // Create a JSON topic 'random/JSON' + string topic = "random/JSON"; + + try { + await topicControl.AddTopicAsync( topic, TopicType.JSON, cancellationToken ); + WriteLine( $"Topic '{topic}' added successfully." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to add topic '{topic}' : {ex}." ); + session.Close(); + return; + } + + WriteLine( $"Updating topic '{topic}' with new values:" ); + + // Update topic every 300 ms until user requests cancellation of example + while ( !cancellationToken.IsCancellationRequested ) { + var newValue = Diffusion.DataTypes.JSON.FromJSONString( + "{\"date\":\"" + DateTime.Today.Date.ToString( "D" ) + "\"," + + "\"time\":\"" + DateTime.Now.TimeOfDay.ToString( "g" ) + "\"}" ); + + try { + await topicUpdate.SetAsync( topic, newValue, cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); + } catch ( Exception ex ) { + WriteLine( $"Topic {topic} could not be updated : {ex}." ); + } + } + + // Remove the JSON topic 'random/JSON' + try { + await topicControl.RemoveTopicsAsync( topic, cancellationToken ); + } catch ( Exception ex ) { + WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/PathRequestMessages.cs b/dotnet/source/examples/Publishing/PathRequestMessages.cs index b647d7f7..248531ea 100644 --- a/dotnet/source/examples/Publishing/PathRequestMessages.cs +++ b/dotnet/source/examples/Publishing/PathRequestMessages.cs @@ -1,56 +1,60 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Factories; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that sends request messages to a path and if it gets a response it displays it on the - /// system console. - /// - public sealed class SendingPathRequestMessages : IExample { - /// - /// Runs the client sending request/response messages example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var messaging = session.Messaging; - var messagingPath = ">random/requestResponse"; - - while ( !cancellationToken.IsCancellationRequested ) { - try { - string response = await messaging.SendRequestAsync( - messagingPath, "Time", cancellationToken ); - WriteLine( $"Received response: '{response}'." ); - } catch ( Exception e ) { - WriteLine( $"Got exception: '{e.Message}'." ); - } - - await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that sends request messages to a path and if it gets a response it displays it on the + /// system console. + /// + public sealed class SendingPathRequestMessages : IExample { + /// + /// Runs the client sending request/response messages example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var messaging = session.Messaging; + string messagingPath = ">random/requestResponse"; + + while ( !cancellationToken.IsCancellationRequested ) { + try { + string response = await messaging.SendRequestAsync( + messagingPath, "Time", cancellationToken ); + WriteLine( $"Received response: '{response}'." ); + } catch ( Exception e ) { + WriteLine( $"Got exception: '{e.Message}'." ); + } + + await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/Publishing.projitems b/dotnet/source/examples/Publishing/Publishing.projitems index 2c0f12cd..8859b47f 100644 --- a/dotnet/source/examples/Publishing/Publishing.projitems +++ b/dotnet/source/examples/Publishing/Publishing.projitems @@ -1,23 +1,24 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - 960603fe-de40-42fe-a1e7-1284e3af1a15 - - - PushTechnology.ClientInterface.Example.Publishing - - - - - - - - - - - - - + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 960603fe-de40-42fe-a1e7-1284e3af1a15 + + + PushTechnology.ClientInterface.Example.Publishing + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dotnet/source/examples/Publishing/Publishing.shproj b/dotnet/source/examples/Publishing/Publishing.shproj index f70c49f0..cfe620cc 100644 --- a/dotnet/source/examples/Publishing/Publishing.shproj +++ b/dotnet/source/examples/Publishing/Publishing.shproj @@ -1,13 +1,13 @@ - - - - 960603fe-de40-42fe-a1e7-1284e3af1a15 - 14.0 - - - - - - - - + + + + 960603fe-de40-42fe-a1e7-1284e3af1a15 + 14.0 + + + + + + + + diff --git a/dotnet/source/examples/Publishing/RecordV2Topics.cs b/dotnet/source/examples/Publishing/RecordV2Topics.cs index 68f5e264..74567465 100644 --- a/dotnet/source/examples/Publishing/RecordV2Topics.cs +++ b/dotnet/source/examples/Publishing/RecordV2Topics.cs @@ -1,85 +1,89 @@ -/** - * Copyright © 2018, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using PushTechnology.ClientInterface.Data.Record; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that adds and updates a RecordV2 topic. - /// - public sealed class PublishingRecordV2Topics : IExample { - /// - /// Runs the RecordV2 topic control client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server URL. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - var topicUpdate = session.TopicUpdate; - - var random = new Random(); - - // Create a RecordV2 topic 'random/RecordV2' - var topic = "random/RecordV2"; - - try { - await topicControl.AddTopicAsync( topic, TopicType.RECORD_V2, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to add topic '{topic}' : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms until user requests cancellation of example - while ( !cancellationToken.IsCancellationRequested ) { - var builder = Diffusion.DataTypes.RecordV2.CreateValueBuilder(); - var fields = new List() { DateTime.Now.ToLongTimeString(), "this", "is", "an", "example" }; - builder.AddFields( fields ); - var newValue = builder.Build(); - - try { - await topicUpdate.SetAsync( topic, newValue, cancellationToken ); - Console.WriteLine( $"Topic {topic} updated successfully." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); - } catch ( Exception ex ) { - Console.WriteLine( $"Topic {topic} could not be updated : {ex}." ); - } - } - - // Remove the RecordV2 topic 'random/RecordV2' - try { - await topicControl.RemoveTopicsAsync( topic, cancellationToken ); - } catch ( Exception ex ) { - WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that adds and updates a RecordV2 topic. + /// + public sealed class PublishingRecordV2Topics : IExample { + /// + /// Runs the RecordV2 topic control client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server URL. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + var topicUpdate = session.TopicUpdate; + + var random = new Random(); + + // Create a RecordV2 topic 'random/RecordV2' + string topic = "random/RecordV2"; + + try { + await topicControl.AddTopicAsync( topic, TopicType.RECORD_V2, cancellationToken ); + + WriteLine( $"Topic '{topic}' added successfully." ); + } catch ( Exception ex ) { + WriteLine( $"Failed to add topic '{topic}' : {ex}." ); + session.Close(); + return; + } + + WriteLine( $"Updating topic '{topic}' with new values:" ); + + // Update topic every 300 ms until user requests cancellation of example + while ( !cancellationToken.IsCancellationRequested ) { + var builder = Diffusion.DataTypes.RecordV2.CreateValueBuilder(); + var fields = new List() { DateTime.Now.ToLongTimeString(), "this", "is", "an", "example" }; + builder.AddFields( fields ); + var newValue = builder.Build(); + + try { + await topicUpdate.SetAsync( topic, newValue, cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); + } catch ( Exception ex ) { + WriteLine( $"Topic {topic} could not be updated : {ex}." ); + } + } + + // Remove the RecordV2 topic 'random/RecordV2' + try { + await topicControl.RemoveTopicsAsync( topic, cancellationToken ); + } catch ( Exception ex ) { + WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Publishing/SessionRequestMessages.cs b/dotnet/source/examples/Publishing/SessionRequestMessages.cs index 8215a830..8db4d656 100644 --- a/dotnet/source/examples/Publishing/SessionRequestMessages.cs +++ b/dotnet/source/examples/Publishing/SessionRequestMessages.cs @@ -1,106 +1,101 @@ -/** - * Copyright © 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Session; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that sends request messages to a filter and if it gets response displays it on the - /// system console, and sends another request directly to the session. - /// - public sealed class SendingSessionRequestMessages : IExample { - private readonly string messagingPath = ">random/requestResponse"; - - /// - /// Runs the client sending request/response messages example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var messaging = session.Messaging; - var requestCallback = new RequestCallback(); - - while ( !cancellationToken.IsCancellationRequested ) { - // To obtain session IDs we will use in this example request to filter messaging - int requestsSent = await messaging.SendRequestToFilterAsync( - "$Principal EQ 'client'", - messagingPath, - "Hello?", - requestCallback, - cancellationToken ); - - if ( requestsSent > 0 ) { - requestCallback.ResponseEvent.WaitOne(); - - // Send message to a session using obtained session ID - var response = await messaging.SendRequestAsync( - requestCallback.SessionId, messagingPath, "Time", cancellationToken ); - WriteLine( $"Received response: '{response}'." ); - } - - await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); - } - - // Close the session - session.Close(); - } - - /// - /// A simple IFilteredRequestCallback implementation that prints confirmation of the actions completed. - /// - private class RequestCallback : IFilteredRequestCallback { - public string Response { get; private set; } - public ISessionId SessionId { get; private set; } - public AutoResetEvent ResponseEvent { get; private set; } = new AutoResetEvent( false ); - - /// - /// Indicates that the stream was closed. - /// - public void OnClose() - => WriteLine( "A request handler was closed." ); - - /// - /// Indicates error received by the callback. - /// - public void OnError( ErrorReason errorReason ) - => WriteLine( $"A request handler has received error: '{errorReason}'." ); - - /// - /// Indicates that a response message was received. - /// - public void OnResponse( ISessionId sessionId, string response ) { - Response = response; - SessionId = sessionId; - ResponseEvent.Set(); - } - - /// - /// Indicates that a error response message was received. - /// - public void OnResponseError( ISessionId sessionId, Exception exception ) - => WriteLine( $"Response error received from session {sessionId}: '{exception}'." ); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Session; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that sends request messages to a filter and if it gets response displays it on the + /// system console, and sends another request directly to the session. + /// + public sealed class SendingSessionRequestMessages : IExample { + private readonly string messagingPath = ">random/requestResponse"; + + /// + /// Runs the client sending request/response messages example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var messaging = session.Messaging; + var requestCallback = new RequestCallback(); + + // Filter messaging is used to get the session ID for this example + int requestsSent = await messaging.SendRequestToFilterAsync( + "$Principal EQ 'client'", + messagingPath, + "Hello?", + requestCallback, + cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); + + while ( !cancellationToken.IsCancellationRequested ) { + // Send message to a session using obtained session ID + string response = await messaging.SendRequestAsync( + requestCallback.SessionId, messagingPath, "Time", cancellationToken ); + WriteLine( $"Received response: '{response}'." ); + + await Task.Delay( TimeSpan.FromMilliseconds( 1000 ) ); + } + + // Close the session + session.Close(); + } + + /// + /// A simple IFilteredRequestCallback implementation that prints confirmation of the actions completed. + /// + private class RequestCallback : IFilteredRequestCallback { + public ISessionId SessionId { get; private set; } + + /// + /// Indicates that the stream was closed. + /// + public void OnClose() + => WriteLine( "A request handler was closed." ); + + /// + /// Indicates error received by the callback. + /// + public void OnError( ErrorReason errorReason ) + => WriteLine( $"A request handler has received error: '{errorReason}'." ); + + /// + /// Indicates that a response message was received. + /// + public void OnResponse( ISessionId sessionId, string response ) => SessionId = sessionId; + + /// + /// Indicates that a error response message was received. + /// + public void OnResponseError( ISessionId sessionId, Exception exception ) + => WriteLine( $"Response error received from session {sessionId}: '{exception}'." ); + } + } +} diff --git a/dotnet/source/examples/Publishing/StringTopics.cs b/dotnet/source/examples/Publishing/StringTopics.cs index 685c46fe..b5bd2b2e 100644 --- a/dotnet/source/examples/Publishing/StringTopics.cs +++ b/dotnet/source/examples/Publishing/StringTopics.cs @@ -1,79 +1,84 @@ -/** - * Copyright © 2018, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using static System.Console; -using static PushTechnology.ClientInterface.Examples.Runner.Program; - -namespace PushTechnology.ClientInterface.Example.Publishing { - /// - /// Control client implementation that adds and updates a string topic. - /// - public sealed class PublishingStringTopics : IExample { - /// - /// Runs the string topic control client example. - /// - /// A token used to end the client example. - /// A single string should be used for the server url. - public async Task Run( CancellationToken cancellationToken, string[] args ) { - var serverUrl = args[ 0 ]; - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( serverUrl ); - var topicControl = session.TopicControl; - var topicUpdate = session.TopicUpdate; - - // Create a string topic 'random/String' - var topic = "random/String"; - - try { - await topicControl.AddTopicAsync( topic, TopicType.STRING, cancellationToken ); - } catch(Exception ex) { - WriteLine( $"Failed to add topic '{topic}' : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms until user requests cancellation of example - while(!cancellationToken.IsCancellationRequested) { - var newValue = DateTime.Today.Date.ToString( "D" ) + " " + - DateTime.Now.TimeOfDay.ToString( "g" ); - - try { - await topicUpdate.SetAsync( topic, newValue, cancellationToken ); - Console.WriteLine( $"Topic {topic} updated successfully." ); - - await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); - } catch ( Exception ex ) { - Console.WriteLine( $"Topic {topic} could not be updated : {ex}." ); - } - } - - // Remove the string topic 'random/String' - try { - await topicControl.RemoveTopicsAsync( topic, cancellationToken ); - } catch(Exception ex) { - WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); - } - - // Close the session - session.Close(); - } - } -} +/** + * Copyright © 2018, 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Session; +using PushTechnology.ClientInterface.Client.Topics; +using static System.Console; +using static PushTechnology.ClientInterface.Examples.Runner.Program; + +namespace PushTechnology.ClientInterface.Example.Publishing { + /// + /// Control client implementation that adds and updates a string topic. + /// + public sealed class PublishingStringTopics : IExample { + /// + /// Runs the string topic control client example. + /// + /// A token used to end the client example. + /// A single string should be used for the server url. + public async Task Run( CancellationToken cancellationToken, string[] args ) { + string serverUrl = args[0]; + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ) + .CertificateValidation( ( cert, chain, errors ) => CertificateValidationResult.ACCEPT ) + .Open( serverUrl ); + + var topicControl = session.TopicControl; + var topicUpdate = session.TopicUpdate; + + // Create a string topic 'random/String' + string topic = "random/String"; + + try { + await topicControl.AddTopicAsync( topic, TopicType.STRING, cancellationToken ); + + WriteLine( $"Topic '{topic}' added successfully." ); + } catch(Exception ex) { + WriteLine( $"Failed to add topic '{topic}' : {ex}." ); + session.Close(); + return; + } + + WriteLine( $"Updating topic '{topic}' with new values:" ); + + // Update topic every 300 ms until user requests cancellation of example + while(!cancellationToken.IsCancellationRequested) { + string newValue = DateTime.Today.Date.ToString( "D" ) + " " + + DateTime.Now.TimeOfDay.ToString( "g" ); + + try { + await topicUpdate.SetAsync( topic, newValue, cancellationToken ); + + await Task.Delay( TimeSpan.FromMilliseconds( 300 ) ); + } catch ( Exception ex ) { + WriteLine( $"Topic {topic} could not be updated : {ex}." ); + } + } + + // Remove the string topic 'random/String' + try { + await topicControl.RemoveTopicsAsync( topic, cancellationToken ); + } catch(Exception ex) { + WriteLine( $"Failed to remove topic '{topic}' : {ex}." ); + } + + // Close the session + session.Close(); + } + } +} diff --git a/dotnet/source/examples/Runner/Program.cs b/dotnet/source/examples/Runner/Program.cs index b7d9d0da..18d7208e 100644 --- a/dotnet/source/examples/Runner/Program.cs +++ b/dotnet/source/examples/Runner/Program.cs @@ -1,146 +1,146 @@ -/** - * Copyright © 2016, 2018 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using PushTechnology.ClientInterface.Example.Consuming; -using PushTechnology.ClientInterface.Example.Features; -using PushTechnology.ClientInterface.Example.Publishing; - -namespace PushTechnology.ClientInterface.Examples.Runner { - /// - /// This is used to run the examples. - /// - public static class Program { - /// - /// To run an example set, uncomment the corresponding block of code. - /// - /// The program arguments. - public static void Main( string[] args ) { - var url = "ws://localhost:8080"; - var secureUrl = "wss://localhost:8080"; - - using ( var runner = new ExampleRunner() ) { - /// Start JSON topic examples - //runner.Start( new PublishingJSONTopics(), secureUrl ); - //runner.Start( new ConsumingJSONTopics(), url ); - - /// Start string topic examples - //runner.Start( new PublishingStringTopics(), secureUrl ); - //runner.Start( new ConsumingStringTopics(), url ); - - /// Start integer topic examples - //runner.Start( new PublishingIntegerTopics(), secureUrl ); - //runner.Start( new ConsumingIntegerTopics(), url ); - - /// Start double topic examples - //runner.Start( new PublishingDoubleTopics(), secureUrl ); - //runner.Start( new ConsumingDoubleTopics(), url ); - - /// Start binary topic examples - //runner.Start( new PublishingBinaryTopics(), secureUrl ); - //runner.Start( new ConsumingBinaryTopics(), url ); - - /// Start RecordV2 topic examples - //runner.Start( new PublishingRecordV2Topics(), secureUrl ); - //runner.Start( new ConsumingRecordV2Topics(), url ); - - /// Start messaging examples - //runner.Start( new ReceivingMessages(), secureUrl ); - //runner.Start( new SendingMessages(), secureUrl ); - - /// Start Request/Response to path examples - //runner.Start( new ReceivingPathRequestMessages(), secureUrl ); - //runner.Start( new SendingPathRequestMessages(), secureUrl ); - - /// Start Request/Response to session filter examples - //runner.Start( new ReceivingFilterRequestMessages(), secureUrl ); - //runner.Start( new SendingFilterRequestMessages(), secureUrl ); - - /// Start Request/Response to specified session examples - //runner.Start( new ReceivingSessionRequestMessages(), secureUrl ); - //runner.Start( new SendingSessionRequestMessages(), secureUrl ); - - /// Start ping example - //runner.Start( new Example.Features.PingServer(), secureUrl ); - - /// Start automatic topic removal - //runner.Start( new Removal(), secureUrl ); - - /// Start authentication example - //runner.Start( new Authentication(), secureUrl ); - - /// Start AddAndSet example - //runner.Start( new AddAndSetTopic(), secureUrl ); - } - } - - /// - /// Interface to be used by all examples. - /// - public interface IExample { - /// - /// Runs the current example. - /// - /// - /// This acts as the main method for examples. - /// - /// The cancellation token to cancel the current example run. - /// The optional example arguments. - Task Run( CancellationToken cancel, string[] args ); - } - - /// - /// Class used by the Main method in to start a new cancelable task - /// for each implementation. - /// - private sealed class ExampleRunner : IDisposable { - private readonly List runningExamples = new List(); - private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - - /// - /// Starts a new task for an implementation. - /// - /// The example to run. - /// An array of arguments. Depending on what is necessary for an example, - /// it may contain multiple variables, such as serverUrl, topic paths etc. Check the example class - /// for the description of what is required for this array. - public void Start( IExample example, params string[] args ) { - var task = Task.Run( async () => { - var run = example?.Run( cancellationTokenSource.Token, args ); - if ( run != null ) { - await run; - } - } ); - runningExamples.Add( task ); - } - - /// - /// Method used to wait for examples to be canceled by the user. - /// - /// - /// Pressing any key will stop the examples. - /// - public void Dispose() { - // Wait for key press to cancel - Console.ReadKey( true ); - cancellationTokenSource.Cancel(); - Task.WaitAll( runningExamples.ToArray() ); - } - } - } -} +/** + * Copyright © 2016, 2022 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using PushTechnology.ClientInterface.Example.Consuming; +using PushTechnology.ClientInterface.Example.Features; +using PushTechnology.ClientInterface.Example.Publishing; + +namespace PushTechnology.ClientInterface.Examples.Runner { + /// + /// This is used to run the examples. + /// + public static class Program { + /// + /// To run an example set, uncomment the corresponding block of code. + /// + /// The program arguments. + public static void Main( string[] args ) { + var url = "ws://localhost:8080"; + var secureUrl = "wss://localhost:8080"; + + using ( var runner = new ExampleRunner() ) { + /// Start JSON topic examples + //runner.Start( new PublishingJSONTopics(), secureUrl ); + //runner.Start( new ConsumingJSONTopics(), url ); + + /// Start string topic examples + //runner.Start( new PublishingStringTopics(), secureUrl ); + //runner.Start( new ConsumingStringTopics(), url ); + + /// Start integer topic examples + //runner.Start( new PublishingIntegerTopics(), secureUrl ); + //runner.Start( new ConsumingIntegerTopics(), url ); + + /// Start double topic examples + //runner.Start( new PublishingDoubleTopics(), secureUrl ); + //runner.Start( new ConsumingDoubleTopics(), url ); + + /// Start binary topic examples + //runner.Start( new PublishingBinaryTopics(), secureUrl ); + //runner.Start( new ConsumingBinaryTopics(), url ); + + /// Start RecordV2 topic examples + //runner.Start( new PublishingRecordV2Topics(), secureUrl ); + //runner.Start( new ConsumingRecordV2Topics(), url ); + + /// Start Request/Response to path examples + //runner.Start( new ReceivingPathRequestMessages(), secureUrl ); + //runner.Start( new SendingPathRequestMessages(), secureUrl ); + + /// Start Request/Response to session filter examples + //runner.Start( new ReceivingFilterRequestMessages(), url ); + //runner.Start( new SendingFilterRequestMessages(), secureUrl ); + + /// Start Request/Response to specified session examples + //runner.Start( new ReceivingSessionRequestMessages(), url ); + //runner.Start( new SendingSessionRequestMessages(), secureUrl ); + + /// Start ping example + //runner.Start( new PingServer(), url); + + /// Start automatic topic removal + //runner.Start( new Removal(), secureUrl ); + + /// Start authentication example + //runner.Start( new AuthenticationControl(), secureUrl ); + + /// Start AddAndSet example + //runner.Start( new AddAndSetTopic(), secureUrl ); + + /// Start AddMissingTopicHandler example + //runner.Start( new AddMissingTopicHandler(), secureUrl ); + + } + } + + /// + /// Interface to be used by all examples. + /// + public interface IExample { + /// + /// Runs the current example. + /// + /// + /// This acts as the main method for examples. + /// + /// The cancellation token to cancel the current example run. + /// The optional example arguments. + Task Run( CancellationToken cancel, string[] args ); + } + + /// + /// Class used by the Main method in to start a new cancelable task + /// for each implementation. + /// + private sealed class ExampleRunner : IDisposable { + private readonly List runningExamples = new List(); + private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + + /// + /// Starts a new task for an implementation. + /// + /// The example to run. + /// An array of arguments. Depending on what is necessary for an example, + /// it may contain multiple variables, such as serverUrl, topic paths etc. Check the example class + /// for the description of what is required for this array. + public void Start( IExample example, params string[] args ) { + var task = Task.Run( async () => { + var run = example?.Run( cancellationTokenSource.Token, args ); + if ( run != null ) { + await run; + } + } ); + runningExamples.Add( task ); + } + + /// + /// Method used to wait for examples to be canceled by the user. + /// + /// + /// Pressing any key will stop the examples. + /// + public void Dispose() { + // Wait for key press to cancel + Console.ReadKey( true ); + cancellationTokenSource.Cancel(); + Task.WaitAll( runningExamples.ToArray() ); + } + } + } +} diff --git a/dotnet/source/examples/Runner/Runner.csproj b/dotnet/source/examples/Runner/Runner.csproj index 1de266dc..592aeb69 100644 --- a/dotnet/source/examples/Runner/Runner.csproj +++ b/dotnet/source/examples/Runner/Runner.csproj @@ -1,31 +1,31 @@ - - - Exe - net461;netcoreapp2.0 - PushTechnology.ClientInterface.Example - - - - ..\..\..\target - 7.2 - - - - ..\..\..\target - 7.2 - - - - - False - ..\..\..\target\Diffusion.Client.dll - - - - - - - - - - + + + Exe + net461;netcoreapp2.0 + PushTechnology.ClientInterface.Example + + + + ..\..\..\target + 7.2 + + + + ..\..\..\target + 7.2 + + + + + False + ..\..\..\target\Diffusion.Client.dll + + + + + + + + + + diff --git a/dotnet/source/getting-started/Publishing/Program.cs b/dotnet/source/getting-started/Publishing/Program.cs index d49bb548..4a0389fd 100644 --- a/dotnet/source/getting-started/Publishing/Program.cs +++ b/dotnet/source/getting-started/Publishing/Program.cs @@ -1,62 +1,62 @@ -/** - * Copyright © 2016, 2019 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features.Control.Topics; -using PushTechnology.ClientInterface.Client.Topics; -using PushTechnology.ClientInterface.Data.JSON; - -namespace PushTechnology.ClientInterface.Example { - /// - /// A client that publishes an incrementing count to the JSON topic "foo/counter". - /// - class Program { - static void Main( string[] args ) { - // Connect using a principal with 'modify_topic' and 'update_topic' permissions - var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( "ws://localhost:8080" ); - - // Get the TopicControl and TopicUpdateControl features - var topicControl = session.TopicControl; - var updateControl = session.TopicUpdateControl; - var topicUpdate = session.TopicUpdate; - - // Create a JSON topic 'foo/counter' - var topic = "foo/counter"; - try { - topicControl.AddTopicAsync( topic, TopicType.JSON ).Wait(); - } catch ( Exception ex ) { - Console.WriteLine( $"Failed to add topic {topic} : {ex}." ); - session.Close(); - return; - } - - // Update topic every 300 ms for 30 minutes - for ( var i = 0; i < 3600; ++i ) { - var newValue = Diffusion.DataTypes.JSON.FromJSONString( - "{\"date\":\"" + DateTime.Today.Date.ToString( "D" ) + "\"," + - "\"time\":\"" + DateTime.Now.TimeOfDay.ToString( "g" ) + "\"}" ); - topicUpdate.SetAsync( topic, newValue ); - - Thread.Sleep( 300 ); - } - - // Close session - session.Close(); - } - } -} +/** + * Copyright © 2016, 2019 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features.Control.Topics; +using PushTechnology.ClientInterface.Client.Topics; +using PushTechnology.ClientInterface.Data.JSON; + +namespace PushTechnology.ClientInterface.Example { + /// + /// A client that publishes an incrementing count to the JSON topic "foo/counter". + /// + class Program { + static void Main( string[] args ) { + // Connect using a principal with 'modify_topic' and 'update_topic' permissions + var session = Diffusion.Sessions.Principal( "control" ).Password( "password" ).Open( "ws://localhost:8080" ); + + // Get the TopicControl and TopicUpdateControl features + var topicControl = session.TopicControl; + var updateControl = session.TopicUpdateControl; + var topicUpdate = session.TopicUpdate; + + // Create a JSON topic 'foo/counter' + var topic = "foo/counter"; + try { + topicControl.AddTopicAsync( topic, TopicType.JSON ).Wait(); + } catch ( Exception ex ) { + Console.WriteLine( $"Failed to add topic {topic} : {ex}." ); + session.Close(); + return; + } + + // Update topic every 300 ms for 30 minutes + for ( var i = 0; i < 3600; ++i ) { + var newValue = Diffusion.DataTypes.JSON.FromJSONString( + "{\"date\":\"" + DateTime.Today.Date.ToString( "D" ) + "\"," + + "\"time\":\"" + DateTime.Now.TimeOfDay.ToString( "g" ) + "\"}" ); + topicUpdate.SetAsync( topic, newValue ); + + Thread.Sleep( 300 ); + } + + // Close session + session.Close(); + } + } +} diff --git a/dotnet/source/getting-started/Publishing/Publishing.csproj b/dotnet/source/getting-started/Publishing/Publishing.csproj index 2e871782..9dad534e 100644 --- a/dotnet/source/getting-started/Publishing/Publishing.csproj +++ b/dotnet/source/getting-started/Publishing/Publishing.csproj @@ -1,25 +1,25 @@ - - - Exe - net461;netcoreapp2.0 - PushTechnology.ClientInterface.Example - - - - ..\..\..\target - 7.2 - - - - ..\..\..\target - 7.2 - - - - - False - ..\..\..\target\Diffusion.Client.dll - - - - + + + Exe + net461;netcoreapp2.0 + PushTechnology.ClientInterface.Example + + + + ..\..\..\target + 7.2 + + + + ..\..\..\target + 7.2 + + + + + False + ..\..\..\target\Diffusion.Client.dll + + + + diff --git a/dotnet/source/getting-started/Subscribing/Program.cs b/dotnet/source/getting-started/Subscribing/Program.cs index d09b1dd2..ade57db9 100644 --- a/dotnet/source/getting-started/Subscribing/Program.cs +++ b/dotnet/source/getting-started/Subscribing/Program.cs @@ -1,95 +1,95 @@ -/** - * Copyright © 2016, 2017 Push Technology Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -using System; -using System.Threading; -using PushTechnology.ClientInterface.Client.Callbacks; -using PushTechnology.ClientInterface.Client.Factories; -using PushTechnology.ClientInterface.Client.Features; -using PushTechnology.ClientInterface.Client.Features.Topics; -using PushTechnology.ClientInterface.Client.Topics.Details; -using PushTechnology.ClientInterface.Data.JSON; - -namespace PushTechnology.ClientInterface.Example { - class Program { - static void Main( string[] args ) { - // Connect anonymously - var session = Diffusion.Sessions.Open( "ws://localhost:8080" ); - - // Get the Topics feature to subscribe to topics - var topics = session.Topics; - var topic = ">foo/counter"; - - // Add a topic stream for 'random/JSON' and request subscription - var jsonStream = new JSONStream(); - topics.AddStream( topic, jsonStream ); - topics.SubscribeAsync( topic ).Wait(); - - //Stay connected for 10 minutes - Thread.Sleep( TimeSpan.FromMinutes( 10 ) ); - - session.Close(); - } - - /// - /// Basic implementation of the IValueStream for JSON topics. - /// - internal sealed class JSONStream : IValueStream { - /// - /// Notification of stream being closed normally. - /// - public void OnClose() - => Console.WriteLine( "The subscription stream is now closed." ); - - /// - /// Notification of a contextual error related to this callback. - /// - /// - /// Situations in which OnError is called include the session being closed, a communication - /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. - /// - /// Error reason. - public void OnError( ErrorReason errorReason ) - => Console.WriteLine( $"An error has occured : {errorReason}" ); - - /// - /// Notification of a successful subscription. - /// - /// Topic path. - /// Topic specification. - public void OnSubscription( string topicPath, ITopicSpecification specification ) - => Console.WriteLine( $"Client subscribed to {topicPath}" ); - - /// - /// Notification of a successful unsubscription. - /// - /// Topic path. - /// Topic specification. - /// Error reason. - public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) - => Console.WriteLine( $"Client unsubscribed from {topicPath} : {reason}" ); - - /// - /// Topic update received. - /// - /// Topic path. - /// Topic specification. - /// Value prior to update. - /// Value after update. - public void OnValue( string topicPath, ITopicSpecification specification, IJSON oldValue, IJSON newValue ) - => Console.WriteLine( $"New value of {topicPath} is {newValue.ToJSONString()}" ); - } - } -} +/** + * Copyright © 2016, 2017 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; +using System.Threading; +using PushTechnology.ClientInterface.Client.Callbacks; +using PushTechnology.ClientInterface.Client.Factories; +using PushTechnology.ClientInterface.Client.Features; +using PushTechnology.ClientInterface.Client.Features.Topics; +using PushTechnology.ClientInterface.Client.Topics.Details; +using PushTechnology.ClientInterface.Data.JSON; + +namespace PushTechnology.ClientInterface.Example { + class Program { + static void Main( string[] args ) { + // Connect anonymously + var session = Diffusion.Sessions.Open( "ws://localhost:8080" ); + + // Get the Topics feature to subscribe to topics + var topics = session.Topics; + var topic = ">foo/counter"; + + // Add a topic stream for 'random/JSON' and request subscription + var jsonStream = new JSONStream(); + topics.AddStream( topic, jsonStream ); + topics.SubscribeAsync( topic ).Wait(); + + //Stay connected for 10 minutes + Thread.Sleep( TimeSpan.FromMinutes( 10 ) ); + + session.Close(); + } + + /// + /// Basic implementation of the IValueStream for JSON topics. + /// + internal sealed class JSONStream : IValueStream { + /// + /// Notification of stream being closed normally. + /// + public void OnClose() + => Console.WriteLine( "The subscription stream is now closed." ); + + /// + /// Notification of a contextual error related to this callback. + /// + /// + /// Situations in which OnError is called include the session being closed, a communication + /// timeout, or a problem with the provided parameters. No further calls will be made to this callback. + /// + /// Error reason. + public void OnError( ErrorReason errorReason ) + => Console.WriteLine( $"An error has occured : {errorReason}" ); + + /// + /// Notification of a successful subscription. + /// + /// Topic path. + /// Topic specification. + public void OnSubscription( string topicPath, ITopicSpecification specification ) + => Console.WriteLine( $"Client subscribed to {topicPath}" ); + + /// + /// Notification of a successful unsubscription. + /// + /// Topic path. + /// Topic specification. + /// Error reason. + public void OnUnsubscription( string topicPath, ITopicSpecification specification, TopicUnsubscribeReason reason ) + => Console.WriteLine( $"Client unsubscribed from {topicPath} : {reason}" ); + + /// + /// Topic update received. + /// + /// Topic path. + /// Topic specification. + /// Value prior to update. + /// Value after update. + public void OnValue( string topicPath, ITopicSpecification specification, IJSON oldValue, IJSON newValue ) + => Console.WriteLine( $"New value of {topicPath} is {newValue.ToJSONString()}" ); + } + } +} diff --git a/dotnet/source/getting-started/Subscribing/Subscribing.csproj b/dotnet/source/getting-started/Subscribing/Subscribing.csproj index 2e871782..9dad534e 100644 --- a/dotnet/source/getting-started/Subscribing/Subscribing.csproj +++ b/dotnet/source/getting-started/Subscribing/Subscribing.csproj @@ -1,25 +1,25 @@ - - - Exe - net461;netcoreapp2.0 - PushTechnology.ClientInterface.Example - - - - ..\..\..\target - 7.2 - - - - ..\..\..\target - 7.2 - - - - - False - ..\..\..\target\Diffusion.Client.dll - - - - + + + Exe + net461;netcoreapp2.0 + PushTechnology.ClientInterface.Example + + + + ..\..\..\target + 7.2 + + + + ..\..\..\target + 7.2 + + + + + False + ..\..\..\target\Diffusion.Client.dll + + + + diff --git a/java/pom.xml b/java/pom.xml index 537fee2f..8d05e001 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -11,7 +11,7 @@ com.pushtechnology.diffusion diffusion-api-java-examples - 6.5.2 + 6.5.18 jar Diffusion public API examples. @@ -54,7 +54,7 @@ com.pushtechnology.diffusion diffusion-client - 6.5.2 + 6.5.18 runtime diff --git a/java/src/main/java/com/pushtechnology/diffusion/examples/CreateRemoteTopicView.java b/java/src/main/java/com/pushtechnology/diffusion/examples/CreateRemoteTopicView.java new file mode 100644 index 00000000..2bc0889e --- /dev/null +++ b/java/src/main/java/com/pushtechnology/diffusion/examples/CreateRemoteTopicView.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (C) 2021 Push Technology Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +package com.pushtechnology.diffusion.examples; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.Arrays; + +import com.pushtechnology.diffusion.client.Diffusion; +import com.pushtechnology.diffusion.client.features.TopicUpdate; +import com.pushtechnology.diffusion.client.features.control.RemoteServers; +import com.pushtechnology.diffusion.client.features.control.topics.views.TopicViews; +import com.pushtechnology.diffusion.client.session.Session; +import com.pushtechnology.diffusion.client.topics.details.TopicType; +import com.pushtechnology.diffusion.datatype.json.JSON; +import com.pushtechnology.diffusion.datatype.json.JSONDataType; + +/** + * An example of creating a remote topic view. + *

+ * Two sessions are established and a remote topic + * view is created to map part of the topic tree + * from the primary server to the secondary server + * + * @author Push Technology Limited + * @since 6.5 + */ +public final class CreateRemoteTopicView { + + private static final String ROOT_TOPIC = "Accounts"; + private final String primaryServerUrl; + private final String secondaryServerUrl; + + /** + * Constructor. + * @param primaryServerUrl url for primary server + * @param secondaryServerUrl url for secondary server + */ + public CreateRemoteTopicView(String primaryServerUrl, String secondaryServerUrl) { + this.primaryServerUrl = primaryServerUrl; + this.secondaryServerUrl = secondaryServerUrl; + } + + /** + * Creates a topic tree on primary server. + *

+ * Populates topic tree with JSON data + */ + private void addTopics(Session session) + throws Exception { + final JSONDataType jsonDataType = Diffusion.dataTypes().json(); + final JSON value = jsonDataType.fromJsonString("{\"foo\" : \"bar\" }"); + + for (String type : Arrays.asList("Free", "Premium")) { + for (int i = 0; i < 10; i++) { + final String topicPath = String.format("%s/%s/%s-Account-%d", ROOT_TOPIC, type, type, i); + session.feature(TopicUpdate.class) + .addAndSet(topicPath, Diffusion + .newTopicSpecification(TopicType.JSON), JSON.class, value) + .get(5, SECONDS); + } + } + } + + /** + * Establish remote server. + *

+ * @param name name of the remote server + */ + private void createRemoteServer(Session session, String name) + throws Exception { + session.feature(RemoteServers.class).createRemoteServer( + name, + primaryServerUrl, + "admin", + Diffusion.credentials().password("password")).get(5, SECONDS); + } + /** + * Create the remote topic view. + *

+ * @param serverName name of the remote server + * @param viewName name of the remote view + */ + private void createRemoteTopicView(Session session, String serverName, String viewName) + throws Exception { + final String specification = String + .format("map ?%s/%s// from %s to %s/", + ROOT_TOPIC, "Premium", serverName, viewName); + + session.feature(TopicViews.class) + .createTopicView(viewName, specification).get(5, SECONDS); + } + + /** + * Run the example. + */ + public void run() throws Exception { + final Session primarySession = Diffusion.sessions().principal("admin").password("password") + .open(primaryServerUrl); + final Session secondarySession = Diffusion.sessions().principal("admin").password("password") + .open(secondaryServerUrl); + + addTopics(primarySession); + createRemoteServer(secondarySession, "foo"); + createRemoteTopicView(secondarySession, "foo", "bar"); + + primarySession.close(); + secondarySession.close(); + } +} diff --git a/js/examples/fetchRequest.js b/js/examples/fetchRequest.js index cdad8555..2b25604b 100644 --- a/js/examples/fetchRequest.js +++ b/js/examples/fetchRequest.js @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2018, 2020 Push Technology Ltd. + * Copyright (C) 2018, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,7 +62,7 @@ diffusion.connect({ .withProperties() // get the topic properties .topicTypes([TopicType.STRING, TopicType.INT64]) // limit to string and integer topic types .withValues(jsonDataType) // fetch values return them as JSON objects - .fetch("?.//") // permform the fetch using a topic selector + .fetch("?.//") // perform the fetch using a topic selector .then(function(fetchResult) { var results = fetchResult.results() diff --git a/js/examples/messages.js b/js/examples/messages.js index eeceae43..8280b517 100644 --- a/js/examples/messages.js +++ b/js/examples/messages.js @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2018 Push Technology Ltd. + * Copyright (C) 2018, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ diffusion.connect({ // Send a message to another session. It is the application's responsibility // to find the SessionID of the intended recipient. - session.messages.sendRequest('foo/bar', 'Hello World', 'another-session-id') + session.messages.sendRequest('foo/bar', 'Hello World', 'another-session-id', diffusion.datatypes.string(), diffusion.datatypes.string()) .then(function(response) { console.log("Received response " + response); }); @@ -63,6 +63,21 @@ diffusion.connect({ // message will only be dispatched to one handler. // Send a message at a lower path, without an explicit recipient - this will be received by the Handler. + session.messages.sendRequest('foo/bar', 'Hello World', diffusion.datatypes.string(), diffusion.datatypes.string()) + .then(function(response) { + console.log("Received response " + response); + }); + + // 4. The datatype of the message and the response can be specified using topic types or omitted altogether. + // In the latter case, the datatype is inferred from the value passed to sendRequest() + + // Send a message using topic types to specify the datatype + session.messages.sendRequest('foo/bar', 'Hello World', diffusion.topics.TopicType.STRING, diffusion.topics.TopicType.STRING) + .then(function(response) { + console.log("Received response " + response); + }); + + // Send a message leaving out the datatype session.messages.sendRequest('foo/bar', 'Hello World') .then(function(response) { console.log("Received response " + response); diff --git a/js/examples/timeseries.js b/js/examples/timeseries.js index 0ae21c73..a682931e 100644 --- a/js/examples/timeseries.js +++ b/js/examples/timeseries.js @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2018 Push Technology Ltd. + * Copyright (C) 2018, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ var diffusion = require('diffusion'); var TopicSpecification = diffusion.topics.TopicSpecification; var TopicType = diffusion.topics.TopicType; -var dataType = diffusion.datatypes.int64(); +var dataType = diffusion.datatypes.double(); // Connect to the server. Change these options to suit your own environment. // Node.js does not accept self-signed certificates by default. If you have @@ -31,9 +31,9 @@ diffusion.connect({ credentials : 'password' }).then(function(session) { // 1. Create a time series topic specification with events - // of type int64 + // of type double var specification = new TopicSpecification(TopicType.TIME_SERIES, { - TIME_SERIES_EVENT_VALUE_TYPE : "int64" + TIME_SERIES_EVENT_VALUE_TYPE : "double" }); // 2. Create a time series topic @@ -55,12 +55,16 @@ diffusion.connect({ for (var i = 0; i < 10; i++) { // 4. Append values 0-9 to the topic - session.timeseries.append("topic/timeseries", i, dataType.Int64); + session.timeseries.append("topic/timeseries", i, dataType); } + for (var i = 10; i < 20; i++) { + // 5. Append values 10 - 19 to the topic using a value constructor to specify the datatype + session.timeseries.append("topic/timeseries", i, Number); + } // 5. Retrieve the last time series event and edit it session.timeseries.rangeQuery().as(dataType).fromLast(1).selectFrom("topic/timeseries").then(function(result) { - session.timeseries.edit("topic/timeseries", result.events[0].sequence, 999, dataType.Int64); + session.timeseries.edit("topic/timeseries", result.events[0].sequence, 999, dataType); }); }); -}); \ No newline at end of file +}); diff --git a/js/examples/topicUpdate.js b/js/examples/topicUpdate.js index 7b5eb1e3..22f0a9a5 100644 --- a/js/examples/topicUpdate.js +++ b/js/examples/topicUpdate.js @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2018 Push Technology Ltd. + * Copyright (C) 2018, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,7 +83,14 @@ function createUpdateStreamWithValueConstraint() { function createUpdateStreamThatAddsTopic() { var topicSpec = new diffusion.topics.TopicSpecification(TopicType.STRING); var stream = session.topicUpdate.createUpdateStream('quux_topic', stringDataType, {specification: topicSpec}); - stream.validate(); + // the first call to validate() or set() resolves in a TopicCreationResult + stream.validate().then((result) => { + if (result === diffusion.topicUpdate.TopicCreationResult.CREATED) { + console.log('A new topic has been created!'); + } else { + console.log('The topic already existed.'); + } + }); stream.set('hello'); var cachedValue = stream.get(); return stream.set('world'); @@ -93,7 +100,14 @@ function createUpdateStreamThatAddsTopicWithNoTopicConstraint() { var topicSpec = new diffusion.topics.TopicSpecification(TopicType.STRING); var constraint = diffusion.updateConstraints().noTopic(); var stream = session.topicUpdate.createUpdateStream('quuz_topic', stringDataType, {specification: topicSpec, constraint}); - stream.validate(); + // the first call to validate() or set() resolves in a TopicCreationResult + stream.validate().then((result) => { + if (result === diffusion.topicUpdate.TopicCreationResult.CREATED) { + console.log('A new topic has been created!'); + } else { + console.log('The topic already existed.'); + } + }); stream.set('hello'); var cachedValue = stream.get(); return stream.set('world'); diff --git a/js/package.json b/js/package.json index b8029250..3dc06e6c 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "diffusion-examples", - "version": "6.5.2", + "version": "6.5.18", "description": "Examples for using the Diffusion JavaScript client", "main": "index.js", "directories": { @@ -8,6 +8,6 @@ }, "license": "Apache-2.0", "dependencies": { - "diffusion": "6.5.2" + "diffusion": "6.5.18" } } diff --git a/ts/package.json b/ts/package.json index fccf8ed9..f3b0f566 100644 --- a/ts/package.json +++ b/ts/package.json @@ -1,6 +1,6 @@ { "name": "diffusion-examples", - "version": "6.5.2", + "version": "6.5.18", "description": "Examples for using the Diffusion JavaScript client", "main": "index.js", "directories": { @@ -11,7 +11,7 @@ }, "license": "Apache-2.0", "dependencies": { - "diffusion": "6.5.2", + "diffusion": "6.5.18", "typescript": "3.4.1" } } diff --git a/ts/src/fetchRequest.ts b/ts/src/fetchRequest.ts index 21576b48..2d1f744c 100644 --- a/ts/src/fetchRequest.ts +++ b/ts/src/fetchRequest.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2019 Push Technology Ltd. + * Copyright (C) 2019, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,3 +57,46 @@ export async function fetchRequestExample() { } } + +// example showcasing how to limit the branch depth when fetching topics +export async function fetchRequestLimitBranchDepthExample() { + + const jsonDataType = datatypes.json(); + const TopicType = topics.TopicType; + + + // Connect to the server. Change these options to suit your own environment. + // Node.js does not accept self-signed certificates by default. If you have + // one of these, set the environment variable NODE_TLS_REJECT_UNAUTHORIZED=0 + // before running this example. + const session: Session = await connect({ + host: 'diffusion.example.com', + port: 443, + secure: true, + principal: 'client', + credentials: 'password' + }); + + const fetchResult: FetchResult + = await session.fetchRequest() // obtain a FetchRequest + .limitBranchDepth(3, 3) // A deep branch has a root path that has a + // number of parts equal to the deep_branch_depth parameter. + // The deep_branch_limit specifies the maximum number of results for each deep branch. + .withProperties() // get the topic properties + .topicTypes([TopicType.STRING, TopicType.INT64]) // limit to string and integer topic types + .withValues(jsonDataType) // fetch values return them as JSON objects + .fetch("?.//") // perform the fetch using a topic selector + const results: TopicResult[] = fetchResult.results(); + console.log("Fetch Request returned "+results.length+" topics"); + + results.forEach((topicResult: TopicResult) => { + console.log("Path: ", topicResult.path()); + console.log("Type: ", topicResult.type()); + console.log("Value: ", topicResult.value().get()); + }); + + if (fetchResult.hasMore()) { + console.log("There are more topics remaining"); + } + +} diff --git a/ts/src/messages.ts b/ts/src/messages.ts index 7b0b471d..2d9bfbf8 100644 --- a/ts/src/messages.ts +++ b/ts/src/messages.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2019 Push Technology Ltd. + * Copyright (C) 2019, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,7 +58,7 @@ export async function messagesExample() { // Send a message to another session. It is the application's responsibility // to find the SessionID of the intended recipient. - const responseFoo = await session.messages.sendRequest('foo/bar', 'Hello World', 'another-session-id'); + const responseFoo = await session.messages.sendRequest('foo/bar', 'Hello World', 'another-session-id', datatypes.string(), datatypes.string()); console.log("Received response " + responseFoo); // 3. Messages can also be sent without a recipient, in which case they will be dispatched to any Message Handlers @@ -66,6 +66,17 @@ export async function messagesExample() { // message will only be dispatched to one handler. // Send a message at a lower path, without an explicit recipient - this will be received by the Handler. - const responseBar = await session.messages.sendRequest('foo/bar', 'Hello World'); + const responseBar = await session.messages.sendRequest('foo/bar', 'Hello World', datatypes.string(), datatypes.string()); console.log("Received response " + responseBar); + + // 4. The datatype of the message and the response can be specified using topic types or omitted altogether. + // In the latter case, the datatype is inferred from the value passed to sendRequest() + + // Send a message using topic types to specify the datatype + const responseBaz = await session.messages.sendRequest('foo/bar', 'Hello World', topics.TopicType.STRING, topics.TopicType.STRING); + console.log("Received response " + responseBaz); + + // Send a message leaving out the datatype + const responseQux = await session.messages.sendRequest('foo/bar', 'Hello World') + console.log("Received response " + responseQux); } diff --git a/ts/src/timeseries.ts b/ts/src/timeseries.ts index 91f2c38e..e93d0e20 100644 --- a/ts/src/timeseries.ts +++ b/ts/src/timeseries.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2019 Push Technology Ltd. + * Copyright (C) 2019, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ const TopicSpecification = topics.TopicSpecification; const TopicType = topics.TopicType; - const dataType = datatypes.int64(); + const dataType = datatypes.double(); // Connect to the server. Change these options to suit your own environment. // Node.js does not accept self-signed certificates by default. If you have @@ -35,9 +35,9 @@ }); // 1. Create a time series topic specification with events - // of type int64 + // of type double const specification: TopicSpecification = new TopicSpecification(TopicType.TIME_SERIES, { - TIME_SERIES_EVENT_VALUE_TYPE: "int64" + TIME_SERIES_EVENT_VALUE_TYPE: "double" }); // 2. Create a time series topic @@ -47,17 +47,22 @@ session.addStream('topic/timeseries', dataType).on('value', function(topic, specification, newValue, oldValue) { var value = newValue.toString(); - console.log(`New value ${newValue.isEditEvent?"editted on":"appended to"} topic: ${value}`); + console.log(`New value ${newValue.isEditEvent?"edited on":"appended to"} topic: ${value}`); }); // 4. Subscribe session.select('topic/timeseries'); for (var i = 0; i < 10; i++) { - // 4. Append values 0-9 to the topic + // 4. Append values 0 - 9 to the topic session.timeseries.append("topic/timeseries", i, dataType); } + for (var i = 10; i < 20; i++) { + // 5. Append values 10 - 19 to the topic using a value constructor to specify the datatype + session.timeseries.append("topic/timeseries", i, Number); + } + // 5. Retrieve the last time series event and edit it const result: QueryResult = await session.timeseries.rangeQuery().as(dataType).fromLast(1).selectFrom("topic/timeseries"); session.timeseries.edit("topic/timeseries", result.events[0].sequence, 999, dataType); diff --git a/ts/src/topicUpdate.ts b/ts/src/topicUpdate.ts index c3c04788..90662226 100644 --- a/ts/src/topicUpdate.ts +++ b/ts/src/topicUpdate.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2019 Push Technology Ltd. + * Copyright (C) 2019, 2021 Push Technology Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ * limitations under the License. *******************************************************************************/ - import { connect, datatypes, topics, updateConstraints, Session, SessionLock } from 'diffusion'; + import { connect, datatypes, topics, topicUpdate, updateConstraints, Session, SessionLock } from 'diffusion'; // example showcasing how to update topics using session.topicUpdate.set or topic update streams export async function topicUpdateExample() { @@ -93,7 +93,13 @@ async function createUpdateStreamThatAddsTopic() { const topicSpec = new topics.TopicSpecification(TopicType.STRING); const stream = session.topicUpdate.createUpdateStream('quux_topic', stringDataType, {specification: topicSpec}); - await stream.validate(); + // the first call to validate() or set() resolves in a TopicCreationResult + const result = await stream.validate(); + if (result === topicUpdate.TopicCreationResult.CREATED) { + console.log('A new topic has been created!'); + } else { + console.log('The topic already existed.'); + } await stream.set('hello'); const cachedValue = stream.get(); await stream.set('world'); @@ -103,7 +109,13 @@ const topicSpec = new topics.TopicSpecification(TopicType.STRING); const constraint = updateConstraints().noTopic(); const stream = session.topicUpdate.createUpdateStream('quuz_topic', stringDataType, {specification: topicSpec, constraint}); - await stream.validate(); + // the first call to validate() or set() resolves in a TopicCreationResult + const result = await stream.validate(); + if (result === topicUpdate.TopicCreationResult.CREATED) { + console.log('A new topic has been created!'); + } else { + console.log('The topic already existed.'); + } await stream.set('hello'); const cachedValue = stream.get(); await stream.set('world');