From 7becd08c4b641886ad08dedb90be156f305140ee Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 31 May 2016 17:45:16 +0200 Subject: VPP-119: JVpp notifications - add notification DTOs to JVpp - add notification callbacks - add notification registry - provide/implement notification registry from future and callback facades Change-Id: I1060ef2ec8ba1eb2e8cff279c93b73aa7c9f9aee Signed-off-by: Maros Marsalek --- vpp-api/java/jvpp/gen/callback_gen.py | 19 ++- vpp-api/java/jvpp/gen/dto_gen.py | 93 +++++++----- vpp-api/java/jvpp/gen/jvpp_c_gen.py | 36 +++-- vpp-api/java/jvpp/gen/jvpp_callback_facade_gen.py | 52 +++++-- vpp-api/java/jvpp/gen/jvpp_future_facade_gen.py | 128 +++++++++------- vpp-api/java/jvpp/gen/jvpp_gen.py | 9 +- vpp-api/java/jvpp/gen/jvpp_impl_gen.py | 4 +- vpp-api/java/jvpp/gen/notification_gen.py | 164 +++++++++++++++++++++ vpp-api/java/jvpp/gen/util.py | 22 ++- .../jvpp/callback/JVppNotificationCallback.java | 24 +++ .../org/openvpp/jvpp/dto/JVppNotification.java | 23 +++ .../org/openvpp/jvpp/future/FutureJVppInvoker.java | 3 +- .../jvpp/future/FutureJVppInvokerFacade.java | 5 +- .../notification/NotificationRegistryProvider.java | 12 ++ .../NotificationRegistryProviderContext.java | 20 +++ .../test/CallbackJVppFacadeNotificationTest.java | 87 +++++++++++ .../jvpp/test/CallbackNotificationApiTest.java | 93 ++++++++++++ .../jvpp/test/FutureApiNotificationTest.java | 60 ++++++++ .../org/openvpp/jvpp/test/NotificationUtils.java | 38 +++++ vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt | 3 + 20 files changed, 757 insertions(+), 138 deletions(-) create mode 100644 vpp-api/java/jvpp/gen/notification_gen.py create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/callback/JVppNotificationCallback.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/dto/JVppNotification.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProvider.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProviderContext.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackJVppFacadeNotificationTest.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackNotificationApiTest.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/test/FutureApiNotificationTest.java create mode 100644 vpp-api/java/jvpp/org/openvpp/jvpp/test/NotificationUtils.java (limited to 'vpp-api/java/jvpp') diff --git a/vpp-api/java/jvpp/gen/callback_gen.py b/vpp-api/java/jvpp/gen/callback_gen.py index 8a0d20124ae..eadf3b5c50d 100644 --- a/vpp-api/java/jvpp/gen/callback_gen.py +++ b/vpp-api/java/jvpp/gen/callback_gen.py @@ -31,7 +31,7 @@ package $base_package.$callback_package; $docs * */ -public interface $cls_name extends $base_package.$callback_package.JVppCallback { +public interface $cls_name extends $base_package.$callback_package.$callback_type { $callback_method @@ -61,15 +61,21 @@ def generate_callbacks(func_list, base_package, callback_package, dto_package, i callbacks = [] for func in func_list: - if util.is_notification(func['name']) or util.is_ignored(func['name']): - # FIXME handle notifications + if util.is_ignored(func['name']): continue camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - if not util.is_reply(camel_case_name_with_suffix): + if not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']): continue - camel_case_name = util.remove_reply_suffix(camel_case_name_with_suffix) + if util.is_reply(camel_case_name_with_suffix): + camel_case_name = util.remove_reply_suffix(camel_case_name_with_suffix) + callback_type = "JVppCallback" + else: + camel_case_name_with_suffix = util.add_notification_suffix(camel_case_name_with_suffix) + camel_case_name = camel_case_name_with_suffix + callback_type = "JVppNotificationCallback" + callbacks.append("{0}.{1}.{2}".format(base_package, callback_package, camel_case_name + callback_suffix)) callback_path = os.path.join(callback_package, camel_case_name + callback_suffix + ".java") callback_file = open(callback_path, 'w') @@ -82,7 +88,8 @@ def generate_callbacks(func_list, base_package, callback_package, dto_package, i cls_name=camel_case_name + callback_suffix, callback_method=method, base_package=base_package, - callback_package=callback_package)) + callback_package=callback_package, + callback_type=callback_type)) callback_file.flush() callback_file.close() diff --git a/vpp-api/java/jvpp/gen/dto_gen.py b/vpp-api/java/jvpp/gen/dto_gen.py index 05859dbe83a..426cd96b2ac 100644 --- a/vpp-api/java/jvpp/gen/dto_gen.py +++ b/vpp-api/java/jvpp/gen/dto_gen.py @@ -13,9 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, util +import os from string import Template +import util + dto_template = Template(""" package $base_package.$dto_package; @@ -40,6 +42,7 @@ send_template = Template(""" @Override return jvpp.$method_name($args); }\n""") + def generate_dtos(func_list, base_package, dto_package, inputfile): """ Generates dto objects in a dedicated package """ print "Generating DTOs" @@ -52,8 +55,7 @@ def generate_dtos(func_list, base_package, dto_package, inputfile): camel_case_method_name = util.underscore_to_camelcase(func['name']) dto_path = os.path.join(dto_package, camel_case_dto_name + ".java") - if util.is_notification(func['name']) or util.is_ignored(func['name']): - # TODO handle notifications + if util.is_ignored(func['name']): continue fields = "" @@ -66,44 +68,63 @@ def generate_dtos(func_list, base_package, dto_package, inputfile): name=field_name) methods = "" base_type = "" - if util.is_reply(camel_case_dto_name): - description = "vpe.api reply DTO" - request_dto_name = get_request_name(camel_case_dto_name, func['name']) - if util.is_details(camel_case_dto_name): - # FIXME assumption that dump calls end with "Dump" suffix. Not enforced in vpe.api - base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name + "Dump") - generate_dump_reply_dto(request_dto_name, base_package, dto_package, camel_case_dto_name, - camel_case_method_name, func) - else: - base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name) - else: - args = "" if fields is "" else "this" - methods = send_template.substitute(method_name=camel_case_method_name, - base_package=base_package, - args=args) - if util.is_dump(camel_case_dto_name): - base_type += "JVppDump" - description = "vpe.api dump request DTO" - else: - base_type += "JVppRequest" - description = "vpe.api request DTO" - dto_file = open(dto_path, 'w') - dto_file.write(dto_template.substitute(inputfile=inputfile, - description=description, - docs=util.api_message_to_javadoc(func), - cls_name=camel_case_dto_name, - fields=fields, - methods=methods, - base_package=base_package, - base_type=base_type, - dto_package=dto_package)) - dto_file.flush() - dto_file.close() + # Generate request/reply or dump/dumpReply even if structure can be used as notification + if not util.is_just_notification(func["name"]): + if util.is_reply(camel_case_dto_name): + description = "vpe.api reply DTO" + request_dto_name = get_request_name(camel_case_dto_name, func['name']) + if util.is_details(camel_case_dto_name): + # FIXME assumption that dump calls end with "Dump" suffix. Not enforced in vpe.api + base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name + "Dump") + generate_dump_reply_dto(request_dto_name, base_package, dto_package, camel_case_dto_name, + camel_case_method_name, func) + else: + base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name) + else: + args = "" if fields is "" else "this" + methods = send_template.substitute(method_name=camel_case_method_name, + base_package=base_package, + args=args) + if util.is_dump(camel_case_dto_name): + base_type += "JVppDump" + description = "vpe.api dump request DTO" + else: + base_type += "JVppRequest" + description = "vpe.api request DTO" + + write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func, + inputfile, methods) + + # for structures that are also used as notifications, generate dedicated notification DTO + if util.is_notification(func["name"]): + base_type = "JVppNotification" + description = "vpe.api notification DTO" + camel_case_dto_name = util.add_notification_suffix(camel_case_dto_name) + methods = "" + dto_path = os.path.join(dto_package, camel_case_dto_name + ".java") + write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func, + inputfile, methods) flush_dump_reply_dtos(inputfile) +def write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func, + inputfile, methods): + dto_file = open(dto_path, 'w') + dto_file.write(dto_template.substitute(inputfile=inputfile, + description=description, + docs=util.api_message_to_javadoc(func), + cls_name=camel_case_dto_name, + fields=fields, + methods=methods, + base_package=base_package, + base_type=base_type, + dto_package=dto_package)) + dto_file.flush() + dto_file.close() + + dump_dto_suffix = "ReplyDump" dump_reply_artificial_dtos = {} diff --git a/vpp-api/java/jvpp/gen/jvpp_c_gen.py b/vpp-api/java/jvpp/gen/jvpp_c_gen.py index 082fd5d73ae..c006c34103b 100644 --- a/vpp-api/java/jvpp/gen/jvpp_c_gen.py +++ b/vpp-api/java/jvpp/gen/jvpp_c_gen.py @@ -53,16 +53,21 @@ def generate_class_cache(func_list): class_name = util.underscore_to_camelcase_upper(c_name) ref_name = util.underscore_to_camelcase(c_name) - if not util.is_reply(class_name) or util.is_ignored(c_name) or util.is_notification(c_name): - # TODO handle notifications + if util.is_ignored(c_name): continue - class_references.append(class_reference_template.substitute( + if util.is_reply(class_name): + class_references.append(class_reference_template.substitute( ref_name=ref_name)) - - find_class_invocations.append(find_class_invocation_template.substitute( + find_class_invocations.append(find_class_invocation_template.substitute( ref_name=ref_name, class_name=class_name)) + elif util.is_notification(c_name): + class_references.append(class_reference_template.substitute( + ref_name=util.add_notification_suffix(ref_name))) + find_class_invocations.append(find_class_invocation_template.substitute( + ref_name=util.add_notification_suffix(ref_name), + class_name=util.add_notification_suffix(class_name))) # add exception class to class cache ref_name = 'callbackException' @@ -73,7 +78,7 @@ def generate_class_cache(func_list): ref_name=ref_name, class_name=class_name)) return class_cache_template.substitute( - class_references="".join(class_references), find_class_invocations="".join(find_class_invocations)) + class_references="".join(class_references), find_class_invocations="".join(find_class_invocations)) # TODO: cache method and field identifiers to achieve better performance @@ -174,7 +179,7 @@ def generate_jni_impl(func_list, inputfile): f_name = f['name'] camel_case_function_name = util.underscore_to_camelcase(f_name) if is_manually_generated(f_name) or util.is_reply(camel_case_function_name) \ - or util.is_ignored(f_name) or util.is_notification(f_name): + or util.is_ignored(f_name) or util.is_just_notification(f_name): continue arguments = '' @@ -331,10 +336,16 @@ def generate_msg_handlers(func_list, inputfile): dto_name = util.underscore_to_camelcase_upper(handler_name) ref_name = util.underscore_to_camelcase(handler_name) - if is_manually_generated(handler_name) or not util.is_reply(dto_name) or util.is_ignored(handler_name) or util.is_notification(handler_name): - # TODO handle notifications + if is_manually_generated(handler_name) or util.is_ignored(handler_name): + continue + + if not util.is_reply(dto_name) and not util.is_notification(handler_name): continue + if util.is_notification(handler_name): + dto_name = util.add_notification_suffix(dto_name) + ref_name = util.add_notification_suffix(ref_name) + dto_setters = '' err_handler = '' # dto setters @@ -391,13 +402,12 @@ def generate_handler_registration(func_list): name = f['name'] camelcase_name = util.underscore_to_camelcase(f['name']) - if not util.is_reply(camelcase_name) or util.is_ignored(name) or util.is_notification(name): - # TODO handle notifications + if (not util.is_reply(camelcase_name) and not util.is_notification(name)) or util.is_ignored(name): continue handler_registration.append(handler_registration_template.substitute( - name=name, - upercase_name=name.upper())) + name=name, + upercase_name=name.upper())) return "".join(handler_registration) diff --git a/vpp-api/java/jvpp/gen/jvpp_callback_facade_gen.py b/vpp-api/java/jvpp/gen/jvpp_callback_facade_gen.py index acf29eb85b8..7df17486a60 100644 --- a/vpp-api/java/jvpp/gen/jvpp_callback_facade_gen.py +++ b/vpp-api/java/jvpp/gen/jvpp_callback_facade_gen.py @@ -27,7 +27,7 @@ package $base_package.$callback_facade_package; *
It was generated by jvpp_callback_facade_gen.py based on $inputfile *
(python representation of vpe.api generated by vppapigen). */ -public interface CallbackJVpp extends java.lang.AutoCloseable { +public interface CallbackJVpp extends $base_package.$notification_package.NotificationRegistryProvider, java.lang.AutoCloseable { @Override void close(); @@ -46,7 +46,7 @@ package $base_package.$callback_facade_package; *
It was generated by jvpp_callback_facade_gen.py based on $inputfile *
(python representation of vpe.api generated by vppapigen). */ -public final class CallbackJVppFacade implements $base_package.$callback_facade_package.CallbackJVpp { +public final class CallbackJVppFacade extends $base_package.$notification_package.NotificationRegistryProviderContext implements $base_package.$callback_facade_package.CallbackJVpp { private final $base_package.JVpp jvpp; private final java.util.Map callbacks; @@ -63,7 +63,7 @@ public final class CallbackJVppFacade implements $base_package.$callback_facade_ public CallbackJVppFacade(final $base_package.JVpp jvpp) throws java.io.IOException { this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null"); this.callbacks = new java.util.HashMap<>(); - this.jvpp.connect(new CallbackJVppFacadeCallback(this.callbacks)); + this.jvpp.connect(new CallbackJVppFacadeCallback(this.callbacks, getNotificationCallback())); } @Override @@ -95,7 +95,7 @@ no_arg_method_impl_template = Template(""" public final void $name($base_pack """) -def generate_jvpp(func_list, base_package, dto_package, callback_package, callback_facade_package, inputfile): +def generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile): """ Generates callback facade """ print "Generating JVpp callback facade" @@ -152,6 +152,7 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, callba methods="\n".join(methods), base_package=base_package, dto_package=dto_package, + notification_package=notification_package, callback_facade_package=callback_facade_package)) jvpp_file.flush() jvpp_file.close() @@ -161,12 +162,13 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, callba methods="\n".join(methods_impl), base_package=base_package, dto_package=dto_package, + notification_package=notification_package, callback_package=callback_package, callback_facade_package=callback_facade_package)) jvpp_file.flush() jvpp_file.close() - generate_callback(func_list, base_package, dto_package, callback_package, callback_facade_package, inputfile) + generate_callback(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile) jvpp_facade_callback_template = Template(""" @@ -180,10 +182,13 @@ package $base_package.$callback_facade_package; public final class CallbackJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback { private final java.util.Map requests; + private final $base_package.$notification_package.GlobalNotificationCallback notificationCallback; private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVppFacadeCallback.class.getName()); - public CallbackJVppFacadeCallback(final java.util.Map requestMap) { + public CallbackJVppFacadeCallback(final java.util.Map requestMap, + final $base_package.$notification_package.GlobalNotificationCallback notificationCallback) { this.requests = requestMap; + this.notificationCallback = notificationCallback; } @Override @@ -224,29 +229,44 @@ jvpp_facade_callback_method_template = Template(""" } """) +jvpp_facade_callback_notification_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($base_package.$dto_package.$callback_dto notification) { + notificationCallback.on$callback_dto(notification); + } +""") -def generate_callback(func_list, base_package, dto_package, callback_package, callback_facade_package, inputfile): + +def generate_callback(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile): callbacks = [] for func in func_list: - if util.is_notification(func['name']) or util.is_ignored(func['name']): - # TODO handle notifications + if util.is_ignored(func['name']): continue camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - if not util.is_reply(camel_case_name_with_suffix): - continue - callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, - dto_package=dto_package, - callback_package=callback_package, - callback=util.remove_reply_suffix(camel_case_name_with_suffix) + callback_gen.callback_suffix, - callback_dto=camel_case_name_with_suffix)) + if util.is_reply(camel_case_name_with_suffix): + callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_package=callback_package, + callback=util.remove_reply_suffix(camel_case_name_with_suffix) + callback_gen.callback_suffix, + callback_dto=camel_case_name_with_suffix)) + + if util.is_notification(func["name"]): + with_notification_suffix = util.add_notification_suffix(camel_case_name_with_suffix) + callbacks.append(jvpp_facade_callback_notification_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_package=callback_package, + callback=with_notification_suffix + callback_gen.callback_suffix, + callback_dto=with_notification_suffix)) jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVppFacadeCallback.java"), 'w') jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile, base_package=base_package, dto_package=dto_package, + notification_package=notification_package, callback_package=callback_package, methods="".join(callbacks), callback_facade_package=callback_facade_package)) diff --git a/vpp-api/java/jvpp/gen/jvpp_future_facade_gen.py b/vpp-api/java/jvpp/gen/jvpp_future_facade_gen.py index 7a5a166733c..e1ca4d022e0 100644 --- a/vpp-api/java/jvpp/gen/jvpp_future_facade_gen.py +++ b/vpp-api/java/jvpp/gen/jvpp_future_facade_gen.py @@ -30,12 +30,16 @@ package $base_package.$future_package; public final class FutureJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback { private final java.util.Map>> requests; + private final $base_package.$notification_package.GlobalNotificationCallback notificationCallback; - public FutureJVppFacadeCallback(final java.util.Map>> requestMap) { + public FutureJVppFacadeCallback(final java.util.Map>> requestMap, + final $base_package.$notification_package.GlobalNotificationCallback notificationCallback) { this.requests = requestMap; + this.notificationCallback = notificationCallback; } @Override + @SuppressWarnings("unchecked") public void onError(org.openvpp.jvpp.VppCallbackException reply) { final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply> completableFuture; @@ -76,6 +80,13 @@ jvpp_facade_callback_method_template = Template(""" } """) +jvpp_facade_callback_notification_method_template = Template(""" + @Override + public void on$callback_dto($base_package.$dto_package.$callback_dto notification) { + notificationCallback.on$callback_dto(notification); + } +""") + # TODO reuse common parts with generic method callback jvpp_facade_control_ping_method_template = Template(""" @Override @@ -129,7 +140,7 @@ jvpp_facade_details_callback_method_template = Template(""" """) -def generate_jvpp(func_list, base_package, dto_package, callback_package, future_facade_package, inputfile): +def generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, future_facade_package, inputfile): """ Generates JVpp interface and JNI implementation """ print "Generating JVpp future facade" @@ -141,70 +152,77 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, future callbacks = [] for func in func_list: - if util.is_notification(func['name']) or util.is_ignored(func['name']): - # TODO handle notifications + if util.is_ignored(func['name']): continue camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - if not util.is_reply(camel_case_name_with_suffix): + if not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']): continue camel_case_method_name = util.underscore_to_camelcase(func['name']) - camel_case_request_method_name = util.remove_reply_suffix(util.underscore_to_camelcase(func['name'])) - if util.is_details(camel_case_name_with_suffix): - camel_case_reply_name = get_standard_dump_reply_name(util.underscore_to_camelcase_upper(func['name']), - func['name']) - callbacks.append(jvpp_facade_details_callback_method_template.substitute(base_package=base_package, - dto_package=dto_package, - callback_dto=camel_case_name_with_suffix, - callback_dto_field=camel_case_method_name, - callback_dto_reply_dump=camel_case_reply_name + dto_gen.dump_dto_suffix, - future_package=future_facade_package)) - - methods.append(future_jvpp_method_template.substitute(base_package=base_package, - dto_package=dto_package, - method_name=camel_case_request_method_name + - util.underscore_to_camelcase_upper(util.dump_suffix), - reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, - request_name=util.remove_reply_suffix(camel_case_reply_name) + - util.underscore_to_camelcase_upper(util.dump_suffix))) - methods_impl.append(future_jvpp_method_impl_template.substitute(base_package=base_package, - dto_package=dto_package, - method_name=camel_case_request_method_name + - util.underscore_to_camelcase_upper(util.dump_suffix), - reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, - request_name=util.remove_reply_suffix(camel_case_reply_name) + - util.underscore_to_camelcase_upper(util.dump_suffix))) - else: - request_name = util.underscore_to_camelcase_upper(util.unconventional_naming_rep_req[func['name']]) \ - if func['name'] in util.unconventional_naming_rep_req else util.remove_reply_suffix(camel_case_name_with_suffix) - - methods.append(future_jvpp_method_template.substitute(base_package=base_package, - dto_package=dto_package, - method_name=camel_case_request_method_name, - reply_name=camel_case_name_with_suffix, - request_name=request_name)) - methods_impl.append(future_jvpp_method_impl_template.substitute(base_package=base_package, - dto_package=dto_package, - method_name=camel_case_request_method_name, - reply_name=camel_case_name_with_suffix, - request_name=request_name)) - - # Callback handler is a bit special and a different template has to be used - if util.is_control_ping(camel_case_name_with_suffix): - callbacks.append(jvpp_facade_control_ping_method_template.substitute(base_package=base_package, - dto_package=dto_package, - callback_dto=camel_case_name_with_suffix, - future_package=future_facade_package)) + + if not util.is_notification(func["name"]): + camel_case_request_method_name = util.remove_reply_suffix(util.underscore_to_camelcase(func['name'])) + if util.is_details(camel_case_name_with_suffix): + camel_case_reply_name = get_standard_dump_reply_name(util.underscore_to_camelcase_upper(func['name']), + func['name']) + callbacks.append(jvpp_facade_details_callback_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix, + callback_dto_field=camel_case_method_name, + callback_dto_reply_dump=camel_case_reply_name + dto_gen.dump_dto_suffix, + future_package=future_facade_package)) + + methods.append(future_jvpp_method_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name + + util.underscore_to_camelcase_upper(util.dump_suffix), + reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, + request_name=util.remove_reply_suffix(camel_case_reply_name) + + util.underscore_to_camelcase_upper(util.dump_suffix))) + methods_impl.append(future_jvpp_method_impl_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name + + util.underscore_to_camelcase_upper(util.dump_suffix), + reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, + request_name=util.remove_reply_suffix(camel_case_reply_name) + + util.underscore_to_camelcase_upper(util.dump_suffix))) else: - callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, - dto_package=dto_package, - callback_dto=camel_case_name_with_suffix)) + request_name = util.underscore_to_camelcase_upper(util.unconventional_naming_rep_req[func['name']]) \ + if func['name'] in util.unconventional_naming_rep_req else util.remove_reply_suffix(camel_case_name_with_suffix) + + methods.append(future_jvpp_method_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name, + reply_name=camel_case_name_with_suffix, + request_name=request_name)) + methods_impl.append(future_jvpp_method_impl_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name, + reply_name=camel_case_name_with_suffix, + request_name=request_name)) + + # Callback handler is a bit special and a different template has to be used + if util.is_control_ping(camel_case_name_with_suffix): + callbacks.append(jvpp_facade_control_ping_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix, + future_package=future_facade_package)) + else: + callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix)) + + if util.is_notification(func["name"]): + callbacks.append(jvpp_facade_callback_notification_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=util.add_notification_suffix(camel_case_name_with_suffix))) jvpp_file = open(os.path.join(future_facade_package, "FutureJVppFacadeCallback.java"), 'w') jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile, base_package=base_package, dto_package=dto_package, + notification_package=notification_package, callback_package=callback_package, methods="".join(callbacks), future_package=future_facade_package)) @@ -268,7 +286,7 @@ public class FutureJVppFacade extends FutureJVppInvokerFacade implements FutureJ */ public FutureJVppFacade(final $base_package.JVpp jvpp) throws java.io.IOException { super(jvpp, new java.util.HashMap<>()); - jvpp.connect(new FutureJVppFacadeCallback(getRequests())); + jvpp.connect(new FutureJVppFacadeCallback(getRequests(), getNotificationCallback())); } $methods } diff --git a/vpp-api/java/jvpp/gen/jvpp_gen.py b/vpp-api/java/jvpp/gen/jvpp_gen.py index e2ff2adcf14..c08593e180f 100755 --- a/vpp-api/java/jvpp/gen/jvpp_gen.py +++ b/vpp-api/java/jvpp/gen/jvpp_gen.py @@ -19,6 +19,7 @@ import importlib import sys import callback_gen +import notification_gen import dto_gen import jvpp_callback_facade_gen import jvpp_future_facade_gen @@ -32,7 +33,7 @@ import util # # Compilation: # ~/Projects/vpp/vpp-api/jvpp/gen/java/org/openvpp/jvpp$ javac *.java dto/*.java callback/*.java -# +# # where # defs_api_vpp_papi.py - vpe.api in python format (generated by vppapigen) from util import vpp_2_jni_type_mapping @@ -122,6 +123,7 @@ func_list, func_name = get_definitions() base_package = 'org.openvpp.jvpp' dto_package = 'dto' callback_package = 'callback' +notification_package = 'notification' future_package = 'future' # TODO find better package name callback_facade_package = 'callfacade' @@ -129,6 +131,7 @@ callback_facade_package = 'callfacade' dto_gen.generate_dtos(func_list, base_package, dto_package, args.inputfile) jvpp_impl_gen.generate_jvpp(func_list, base_package, dto_package, args.inputfile) callback_gen.generate_callbacks(func_list, base_package, callback_package, dto_package, args.inputfile) +notification_gen.generate_notification_registry(func_list, base_package, notification_package, callback_package, dto_package, args.inputfile) jvpp_c_gen.generate_jvpp(func_list, args.inputfile) -jvpp_future_facade_gen.generate_jvpp(func_list, base_package, dto_package, callback_package, future_package, args.inputfile) -jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, dto_package, callback_package, callback_facade_package, args.inputfile) +jvpp_future_facade_gen.generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, future_package, args.inputfile) +jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, args.inputfile) diff --git a/vpp-api/java/jvpp/gen/jvpp_impl_gen.py b/vpp-api/java/jvpp/gen/jvpp_impl_gen.py index dfec6a743de..93ffd0fb359 100644 --- a/vpp-api/java/jvpp/gen/jvpp_impl_gen.py +++ b/vpp-api/java/jvpp/gen/jvpp_impl_gen.py @@ -121,8 +121,8 @@ def generate_jvpp(func_list, base_package, dto_package, inputfile): methods_impl = [] for func in func_list: - if util.is_notification(func['name']) or util.is_ignored(func['name']): - # TODO handle notifications + # Skip structures that are used only as notifications + if util.is_just_notification(func['name']) or util.is_ignored(func['name']): continue camel_case_name = util.underscore_to_camelcase(func['name']) diff --git a/vpp-api/java/jvpp/gen/notification_gen.py b/vpp-api/java/jvpp/gen/notification_gen.py new file mode 100644 index 00000000000..4ca3c070431 --- /dev/null +++ b/vpp-api/java/jvpp/gen/notification_gen.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# 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. + +import os + +import callback_gen +import util +from string import Template + +from util import remove_suffix + +notification_registry_template = Template(""" +package $base_package.$notification_package; + +/** + *

Registry for notification callbacks. + *
It was generated by notification_gen.py based on $inputfile + *
(python representation of vpe.api generated by vppapigen). + */ +public interface NotificationRegistry extends java.lang.AutoCloseable { + + $register_callback_methods + + @Override + void close(); +} +""") + +global_notification_callback_template = Template(""" +package $base_package.$notification_package; + +/** + *

Aggregated callback interface for notifications only. + *
It was generated by notification_gen.py based on $inputfile + *
(python representation of vpe.api generated by vppapigen). + */ +public interface GlobalNotificationCallback extends $callbacks { + +} +""") + +notification_registry_impl_template = Template(""" +package $base_package.$notification_package; + +/** + *

Notification registry delegating notification processing to registered callbacks. + *
It was generated by notification_gen.py based on $inputfile + *
(python representation of vpe.api generated by vppapigen). + */ +public final class NotificationRegistryImpl implements NotificationRegistry, GlobalNotificationCallback { + + // TODO add a special NotificationCallback interface and only allow those to be registered + private final java.util.concurrent.ConcurrentMap, $base_package.$callback_package.JVppNotificationCallback> registeredCallbacks = + new java.util.concurrent.ConcurrentHashMap<>(); + + $register_callback_methods + $handler_methods + + @Override + public void close() { + registeredCallbacks.clear(); + } +} +""") + +register_callback_impl_template = Template(""" + public java.lang.AutoCloseable register$callback(final $base_package.$callback_package.$callback callback){ + if(null != registeredCallbacks.putIfAbsent($base_package.$dto_package.$notification.class, callback)){ + throw new IllegalArgumentException("Callback for " + $base_package.$dto_package.$notification.class + + "notification already registered"); + } + return () -> registeredCallbacks.remove($base_package.$dto_package.$notification.class); + } +""") + +handler_impl_template = Template(""" + @Override + public void on$notification( + final $base_package.$dto_package.$notification notification) { + final $base_package.$callback_package.JVppNotificationCallback JVppNotificationCallback = registeredCallbacks.get($base_package.$dto_package.$notification.class); + if (null != JVppNotificationCallback) { + (($base_package.$callback_package.$callback) registeredCallbacks + .get($base_package.$dto_package.$notification.class)) + .on$notification(notification); + } + } +""") + + +def generate_notification_registry(func_list, base_package, notification_package, callback_package, dto_package, inputfile): + """ Generates notification registry interface and implementation """ + print "Generating Notification interfaces and implementation" + + if not os.path.exists(notification_package): + raise Exception("%s folder is missing" % notification_package) + + callbacks = [] + register_callback_methods = [] + register_callback_methods_impl = [] + handler_methods = [] + for func in func_list: + + if not util.is_notification(func['name']): + continue + + camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) + notification_dto = util.add_notification_suffix(camel_case_name_with_suffix) + callback_ifc = notification_dto + callback_gen.callback_suffix + fully_qualified_callback_ifc = "{0}.{1}.{2}".format(base_package, callback_package, callback_ifc) + callbacks.append(fully_qualified_callback_ifc) + + # TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate + # that the registration should be closed + register_callback_methods.append("java.lang.AutoCloseable register{0}({1} callback);" + .format(callback_ifc, fully_qualified_callback_ifc)) + register_callback_methods_impl.append(register_callback_impl_template.substitute(base_package=base_package, + callback_package=callback_package, + dto_package=dto_package, + notification=notification_dto, + callback=callback_ifc)) + handler_methods.append(handler_impl_template.substitute(base_package=base_package, + callback_package=callback_package, + dto_package=dto_package, + notification=notification_dto, + callback=callback_ifc)) + + callback_file = open(os.path.join(notification_package, "NotificationRegistry.java"), 'w') + callback_file.write(notification_registry_template.substitute(inputfile=inputfile, + register_callback_methods="\n ".join(register_callback_methods), + base_package=base_package, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() + + callback_file = open(os.path.join(notification_package, "GlobalNotificationCallback.java"), 'w') + callback_file.write(global_notification_callback_template.substitute(inputfile=inputfile, + callbacks=", ".join(callbacks), + base_package=base_package, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() + + callback_file = open(os.path.join(notification_package, "NotificationRegistryImpl.java"), 'w') + callback_file.write(notification_registry_impl_template.substitute(inputfile=inputfile, + callback_package=callback_package, + dto_package=dto_package, + register_callback_methods="".join(register_callback_methods_impl), + handler_methods="".join(handler_methods), + base_package=base_package, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() diff --git a/vpp-api/java/jvpp/gen/util.py b/vpp-api/java/jvpp/gen/util.py index 072c9d592f2..12c8bc3170f 100644 --- a/vpp-api/java/jvpp/gen/util.py +++ b/vpp-api/java/jvpp/gen/util.py @@ -148,15 +148,21 @@ unconventional_naming_rep_req = { # # FIXME no convention in the naming of events (notifications) in vpe.api notifications_message_suffixes = ("event", "counters") -notification_messages = ["from_netconf_client", "from_netconf_server", "to_netconf_client", "to_netconf_server"] +notification_messages_reused = ["sw_interface_set_flags"] # messages that must be ignored. These messages are INSUFFICIENTLY marked as disabled in vpe.api # FIXME ignored_messages = ["is_address_reachable"] -def is_notification(param): - return param.lower().endswith(notifications_message_suffixes) or param.lower() in notification_messages +def is_notification(name): + """ Returns true if the structure is a notification regardless of its no other use """ + return is_just_notification(name) or name.lower() in notification_messages_reused + + +def is_just_notification(name): + """ Returns true if the structure is just a notification and has no other use """ + return name.lower().endswith(notifications_message_suffixes) def is_ignored(param): @@ -178,4 +184,12 @@ def is_control_ping(camel_case_name_with_suffix): def api_message_to_javadoc(api_message): """ Converts vpe.api message description to javadoc """ str = pprint.pformat(api_message, indent=4, width=120, depth=None) - return " * " + str.replace("\n", "\n * ") \ No newline at end of file + return " * " + str.replace("\n", "\n * ") + + +notification_dto_suffix = "Notification" + + +def add_notification_suffix(camel_case_dto_name): + camel_case_dto_name += notification_dto_suffix + return camel_case_dto_name \ No newline at end of file diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/callback/JVppNotificationCallback.java b/vpp-api/java/jvpp/org/openvpp/jvpp/callback/JVppNotificationCallback.java new file mode 100644 index 00000000000..72a75c83942 --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/callback/JVppNotificationCallback.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 org.openvpp.jvpp.callback; + +/** +* Notification callback +*/ +public interface JVppNotificationCallback { + +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/dto/JVppNotification.java b/vpp-api/java/jvpp/org/openvpp/jvpp/dto/JVppNotification.java new file mode 100644 index 00000000000..7d0fecb7d78 --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/dto/JVppNotification.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 org.openvpp.jvpp.dto; + +/** +* Base interface for all notification DTOs +*/ +public interface JVppNotification { +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvoker.java b/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvoker.java index 9219e353cdb..1683bd75139 100644 --- a/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvoker.java +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvoker.java @@ -21,11 +21,12 @@ import org.openvpp.jvpp.dto.JVppReply; import org.openvpp.jvpp.dto.JVppRequest; import java.util.concurrent.CompletionStage; +import org.openvpp.jvpp.notification.NotificationRegistryProvider; /** * Future facade on top of JVpp */ -public interface FutureJVppInvoker extends AutoCloseable { +public interface FutureJVppInvoker extends NotificationRegistryProvider, AutoCloseable { /** * Invoke asynchronous operation on VPP diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvokerFacade.java b/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvokerFacade.java index 69967a1dbc1..a60e1b285dd 100644 --- a/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvokerFacade.java +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/future/FutureJVppInvokerFacade.java @@ -25,11 +25,12 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; +import org.openvpp.jvpp.notification.NotificationRegistryProviderContext; /** * Future facade on top of JVpp */ -public class FutureJVppInvokerFacade implements FutureJVppInvoker { +public class FutureJVppInvokerFacade extends NotificationRegistryProviderContext implements FutureJVppInvoker { private final JVpp jvpp; @@ -39,7 +40,7 @@ public class FutureJVppInvokerFacade implements FutureJVppInvoker { private final Map>> requests; public FutureJVppInvokerFacade(final JVpp jvpp, - final Map>> requestMap) { + final Map>> requestMap) { this.jvpp = Objects.requireNonNull(jvpp, "Null jvpp"); // Request map represents the shared state between this facade and it's callback // where facade puts futures in and callback completes + removes them diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProvider.java b/vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProvider.java new file mode 100644 index 00000000000..50b72be5805 --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProvider.java @@ -0,0 +1,12 @@ +package org.openvpp.jvpp.notification; + +/** + * Provides notification registry + */ +public interface NotificationRegistryProvider { + + /** + * Get current notification registry instance + */ + NotificationRegistry getNotificationRegistry(); +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProviderContext.java b/vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProviderContext.java new file mode 100644 index 00000000000..8e703812eee --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/notification/NotificationRegistryProviderContext.java @@ -0,0 +1,20 @@ +package org.openvpp.jvpp.notification; + +/** + * Base class for notification aware JVpp facades + */ +public abstract class NotificationRegistryProviderContext implements NotificationRegistryProvider { + + private final NotificationRegistryImpl notificationRegistry = new NotificationRegistryImpl(); + + public final NotificationRegistry getNotificationRegistry() { + return notificationRegistry; + } + + /** + * Get instance of notification callback. Can be used to propagate notifications from JVpp facade + */ + protected final GlobalNotificationCallback getNotificationCallback() { + return notificationRegistry; + } +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackJVppFacadeNotificationTest.java b/vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackJVppFacadeNotificationTest.java new file mode 100644 index 00000000000..430ce8812c2 --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackJVppFacadeNotificationTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 org.openvpp.jvpp.test; + +import org.openvpp.jvpp.JVpp; +import org.openvpp.jvpp.JVppImpl; +import org.openvpp.jvpp.VppCallbackException; +import org.openvpp.jvpp.VppJNIConnection; +import org.openvpp.jvpp.callback.WantInterfaceEventsCallback; +import org.openvpp.jvpp.callfacade.CallbackJVppFacade; +import org.openvpp.jvpp.dto.WantInterfaceEventsReply; + +public class CallbackJVppFacadeNotificationTest { + + private static void testCallbackFacade() throws Exception { + System.out.println("Testing CallbackJVppFacade for notifications"); + + JVpp jvpp = new JVppImpl(new VppJNIConnection("CallbackApiTest")); + + CallbackJVppFacade jvppCallbackFacade = new CallbackJVppFacade(jvpp); + System.out.println("Successfully connected to VPP"); + + final AutoCloseable notificationListenerReg = + jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( + NotificationUtils::printNotification + ); + + jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getEnableInterfaceNotificationsReq(), + new WantInterfaceEventsCallback() { + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { + System.out.println("Interface events started"); + } + + @Override + public void onError(final VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d\n", + ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); + } + }); + + System.out.println("Changing interface configuration"); + NotificationUtils.getChangeInterfaceState().send(jvpp); + + Thread.sleep(1000); + + jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getDisableInterfaceNotificationsReq(), + new WantInterfaceEventsCallback() { + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { + System.out.println("Interface events stopped"); + } + + @Override + public void onError(final VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d\n", + ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); + } + }); + + notificationListenerReg.close(); + + Thread.sleep(2000); + + System.out.println("Disconnecting..."); + jvpp.close(); + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackFacade(); + } +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackNotificationApiTest.java b/vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackNotificationApiTest.java new file mode 100644 index 00000000000..5bf2b212f73 --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/CallbackNotificationApiTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 org.openvpp.jvpp.test; + +import static org.openvpp.jvpp.test.NotificationUtils.getChangeInterfaceState; +import static org.openvpp.jvpp.test.NotificationUtils.getDisableInterfaceNotificationsReq; +import static org.openvpp.jvpp.test.NotificationUtils.getEnableInterfaceNotificationsReq; +import static org.openvpp.jvpp.test.NotificationUtils.printNotification; + +import org.openvpp.jvpp.JVpp; +import org.openvpp.jvpp.JVppImpl; +import org.openvpp.jvpp.VppCallbackException; +import org.openvpp.jvpp.VppJNIConnection; +import org.openvpp.jvpp.callback.SwInterfaceSetFlagsCallback; +import org.openvpp.jvpp.callback.SwInterfaceSetFlagsNotificationCallback; +import org.openvpp.jvpp.callback.WantInterfaceEventsCallback; +import org.openvpp.jvpp.dto.SwInterfaceSetFlagsNotification; +import org.openvpp.jvpp.dto.SwInterfaceSetFlagsReply; +import org.openvpp.jvpp.dto.WantInterfaceEventsReply; + +public class CallbackNotificationApiTest { + + private static class TestCallback implements SwInterfaceSetFlagsNotificationCallback, + WantInterfaceEventsCallback, SwInterfaceSetFlagsCallback { + + @Override + public void onSwInterfaceSetFlagsNotification( + final SwInterfaceSetFlagsNotification msg) { + printNotification(msg); + } + + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply wantInterfaceEventsReply) { + System.out.println("Interface notification stream updated"); + } + + @Override + public void onSwInterfaceSetFlagsReply(final SwInterfaceSetFlagsReply swInterfaceSetFlagsReply) { + System.out.println("Interface flags set successfully"); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d\n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + + } + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for notifications"); + JVpp jvpp = new JVppImpl( new VppJNIConnection("CallbackApiTest")); + jvpp.connect( new TestCallback()); + System.out.println("Successfully connected to VPP"); + + getEnableInterfaceNotificationsReq().send(jvpp); + System.out.println("Interface notifications started"); + // TODO test ifc dump which also triggers interface flags send + + System.out.println("Changing interface configuration"); + getChangeInterfaceState().send(jvpp); + + // Notification is received + Thread.sleep(500); + + getDisableInterfaceNotificationsReq().send(jvpp); + System.out.println("Interface events stopped"); + + Thread.sleep(2000); + + System.out.println("Disconnecting..."); + jvpp.close(); + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/FutureApiNotificationTest.java b/vpp-api/java/jvpp/org/openvpp/jvpp/test/FutureApiNotificationTest.java new file mode 100644 index 00000000000..c48f86d4f00 --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/FutureApiNotificationTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * 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 org.openvpp.jvpp.test; + +import static org.openvpp.jvpp.test.NotificationUtils.getChangeInterfaceState; +import static org.openvpp.jvpp.test.NotificationUtils.getDisableInterfaceNotificationsReq; +import static org.openvpp.jvpp.test.NotificationUtils.getEnableInterfaceNotificationsReq; + +import org.openvpp.jvpp.VppJNIConnection; +import org.openvpp.jvpp.future.FutureJVppFacade; + +public class FutureApiNotificationTest { + + private static void testFutureApi() throws Exception { + System.out.println("Testing Java future API for notifications"); + + final org.openvpp.jvpp.JVppImpl impl = + new org.openvpp.jvpp.JVppImpl(new VppJNIConnection("FutureApiTest")); + final FutureJVppFacade jvppFacade = new FutureJVppFacade(impl); + System.out.println("Successfully connected to VPP"); + + final AutoCloseable notificationListenerReg = + jvppFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback(NotificationUtils::printNotification); + + jvppFacade.wantInterfaceEvents(getEnableInterfaceNotificationsReq()).toCompletableFuture().get(); + System.out.println("Interface events started"); + + System.out.println("Changing interface configuration"); + jvppFacade.swInterfaceSetFlags(getChangeInterfaceState()).toCompletableFuture().get(); + + Thread.sleep(1000); + + jvppFacade.wantInterfaceEvents(getDisableInterfaceNotificationsReq()).toCompletableFuture().get(); + System.out.println("Interface events stopped"); + + notificationListenerReg.close(); + + System.out.println("Disconnecting..."); + // TODO we should consider adding jvpp.close(); to the facade + impl.close(); + } + + public static void main(String[] args) throws Exception { + testFutureApi(); + } +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/NotificationUtils.java b/vpp-api/java/jvpp/org/openvpp/jvpp/test/NotificationUtils.java new file mode 100644 index 00000000000..9c24d572cbc --- /dev/null +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/NotificationUtils.java @@ -0,0 +1,38 @@ +package org.openvpp.jvpp.test; + +import java.io.PrintStream; +import org.openvpp.jvpp.dto.SwInterfaceSetFlags; +import org.openvpp.jvpp.dto.SwInterfaceSetFlagsNotification; +import org.openvpp.jvpp.dto.WantInterfaceEvents; + +final class NotificationUtils { + + private NotificationUtils() {} + + static PrintStream printNotification(final SwInterfaceSetFlagsNotification msg) { + return System.out.printf("Received interface notification: ifc: %d, admin: %d, link: %d, deleted: %d\n", + msg.swIfIndex, msg.adminUpDown, msg.linkUpDown, msg.deleted); + } + + static SwInterfaceSetFlags getChangeInterfaceState() { + final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); + swInterfaceSetFlags.swIfIndex = 0; + swInterfaceSetFlags.adminUpDown = 1; + swInterfaceSetFlags.deleted = 0; + return swInterfaceSetFlags; + } + + static WantInterfaceEvents getEnableInterfaceNotificationsReq() { + WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = 1; + return wantInterfaceEvents; + } + + static WantInterfaceEvents getDisableInterfaceNotificationsReq() { + WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = 0; + return wantInterfaceEvents; + } +} diff --git a/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt b/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt index f9c67ddbcb6..e0aa4f4d085 100644 --- a/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt +++ b/vpp-api/java/jvpp/org/openvpp/jvpp/test/Readme.txt @@ -7,8 +7,11 @@ This package contains basic tests for jvpp. To run the tests: Available tests: ControlPingTest - Simple test executing a single control ping using low level JVpp APIs CallbackApiTest - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs +CallbackNotificationApiTest - Tests interface notifications using low level JVpp APIs FutureApiTest - Execution of more complex calls using Future based JVpp facade +FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade +CallbackJVppFacadeNotificationTest - Tests interface notifications using Callback based JVpp facade L2AclTest - Tests L2 ACL creation CreateSubInterfaceTest - Tests sub-interface creation OnErrorCallbackTest - simple test failing with onError -- cgit 1.2.3-korg