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 <mmarsale@cisco.com>
diff --git a/vpp-api/java/Makefile.am b/vpp-api/java/Makefile.am
index 29c4aab..cb841ee 100644
--- a/vpp-api/java/Makefile.am
+++ b/vpp-api/java/Makefile.am
@@ -119,14 +119,15 @@
 	cp -rf jvpp/org/openvpp/jvpp/* -t jvpp/gen/target/org/openvpp/jvpp/;	\
 	$(CC) $(CPPFLAGS) -E -P -C -x c $<			                  \
 	| vppapigen --input - --python defs_vpp_papi.py; 				\
-	mkdir -p dto future callfacade callback; 	\
+	mkdir -p dto future callfacade callback notification; 	\
 	./jvpp/gen/jvpp_gen.py -i defs_vpp_papi.py;				\
-	cp -rf dto future callfacade callback *.java -t jvpp/gen/target/org/openvpp/jvpp/;	\
+	cp -rf dto future callfacade callback notification *.java -t jvpp/gen/target/org/openvpp/jvpp/;	\
 	cp -rf jvpp_gen.h -t jvpp/gen/target;	\
-	rm -rf dto future callfacade callback *.java jvpp_gen.h;	\
+	rm -rf dto future callfacade callback notification *.java jvpp_gen.h;	\
 	$(JAVAC) -classpath . -d . jvpp/gen/target/org/openvpp/jvpp/*.java \
 		jvpp/gen/target/org/openvpp/jvpp/dto/*.java  \
 		jvpp/gen/target/org/openvpp/jvpp/callback/*.java \
+		jvpp/gen/target/org/openvpp/jvpp/notification/*.java \
 		jvpp/gen/target/org/openvpp/jvpp/callfacade/*.java \
 		jvpp/gen/target/org/openvpp/jvpp/future/*.java 	\
 		jvpp/gen/target/org/openvpp/jvpp/test/*.java	\
diff --git a/vpp-api/java/jvpp/gen/callback_gen.py b/vpp-api/java/jvpp/gen/callback_gen.py
index 8a0d201..eadf3b5 100644
--- a/vpp-api/java/jvpp/gen/callback_gen.py
+++ b/vpp-api/java/jvpp/gen/callback_gen.py
@@ -31,7 +31,7 @@
 $docs
  * </pre>
  */
-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 @@
     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 @@
                                          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 05859db..426cd96 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 @@
         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 @@
         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 @@
                                                 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 082fd5d..c006c34 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 @@
         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 @@
             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 @@
         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 @@
         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 @@
         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 acf29eb..7df1748 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 @@
  * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
  * <br>(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 @@
  * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
  * <br>(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<Integer, $base_package.$callback_package.JVppCallback> callbacks;
@@ -63,7 +63,7 @@
     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 @@
 """)
 
 
-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 @@
                                      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 @@
                                                   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 @@
 public final class CallbackJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback {
 
     private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> 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<Integer, $base_package.$callback_package.JVppCallback> requestMap) {
+    public CallbackJVppFacadeCallback(final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requestMap,
+                                      final $base_package.$notification_package.GlobalNotificationCallback notificationCallback) {
         this.requests = requestMap;
+        this.notificationCallback = notificationCallback;
     }
 
     @Override
@@ -224,29 +229,44 @@
     }
 """)
 
+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 7a5a166..e1ca4d0 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 @@
 public final class FutureJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback {
 
     private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requests;
+    private final $base_package.$notification_package.GlobalNotificationCallback notificationCallback;
 
-    public FutureJVppFacadeCallback(final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requestMap) {
+    public FutureJVppFacadeCallback(final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> 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_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 @@
 """)
 
 
-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 @@
     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)
+        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,
-                                                                  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))
+                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 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 e2ff2ad..c08593e 100755
--- a/vpp-api/java/jvpp/gen/jvpp_gen.py
+++ b/vpp-api/java/jvpp/gen/jvpp_gen.py
@@ -19,6 +19,7 @@
 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 @@
 #
 # 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 @@
 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 @@
 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 dfec6a7..93ffd0f 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 @@
     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 0000000..4ca3c07
--- /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;
+
+/**
+ * <p>Registry for notification callbacks.
+ * <br>It was generated by notification_gen.py based on $inputfile
+ * <br>(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;
+
+/**
+ * <p>Aggregated callback interface for notifications only.
+ * <br>It was generated by notification_gen.py based on $inputfile
+ * <br>(python representation of vpe.api generated by vppapigen).
+ */
+public interface GlobalNotificationCallback extends $callbacks {
+
+}
+""")
+
+notification_registry_impl_template = Template("""
+package $base_package.$notification_package;
+
+/**
+ * <p>Notification registry delegating notification processing to registered callbacks.
+ * <br>It was generated by notification_gen.py based on $inputfile
+ * <br>(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<Class<? extends $base_package.$dto_package.JVppNotification>, $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 072c9d5..12c8bc3 100644
--- a/vpp-api/java/jvpp/gen/util.py
+++ b/vpp-api/java/jvpp/gen/util.py
@@ -148,15 +148,21 @@
 #
 # 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 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 0000000..72a75c8
--- /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 0000000..7d0fecb
--- /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 9219e35..1683bd7 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.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 69967a1..a60e1b2 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.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 @@
     private final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requests;
 
     public FutureJVppInvokerFacade(final JVpp jvpp,
-                     final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requestMap) {
+                                   final Map<Integer, CompletableFuture<? extends JVppReply<?>>> 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 0000000..50b72be
--- /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 0000000..8e70381
--- /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 0000000..430ce88
--- /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 0000000..5bf2b21
--- /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 0000000..c48f86d
--- /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 0000000..9c24d57
--- /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 f9c67dd..e0aa4f4 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 @@
 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