Move java api to extras/

Change-Id: Ibd5cbbdfb22a235442cddaebc9eae9a3c4e35ec9
Signed-off-by: Damjan Marion <damarion@cisco.com>
diff --git a/extras/japi/Makefile.am b/extras/japi/Makefile.am
new file mode 100644
index 0000000..81ff5bb
--- /dev/null
+++ b/extras/japi/Makefile.am
@@ -0,0 +1,5 @@
+
+
+AUTOMAKE_OPTIONS = foreign subdir-objects
+
+SUBDIRS = java
diff --git a/extras/japi/configure.ac b/extras/japi/configure.ac
new file mode 100644
index 0000000..08f0897
--- /dev/null
+++ b/extras/japi/configure.ac
@@ -0,0 +1,36 @@
+AC_INIT(japi, 18.10)
+LT_INIT
+AM_INIT_AUTOMAKE
+AM_SILENT_RULES([yes])
+AC_PREFIX_DEFAULT([/usr])
+
+PKG_CHECK_MODULES([CHECK], [check], [HAVE_CHECK=1], [HAVE_CHECK=0])
+AM_CONDITIONAL([USE_CHECK],[test "$HAVE_CHECK" -eq 1])
+
+AC_PROG_CC
+AC_PROG_CXX
+
+AM_CONDITIONAL(ENABLE_ACL_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_NAT_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_L2E_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_GBP_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_PPPOE_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_GTPU_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_IOAM_PLUGIN, test "yes" = "yes")
+
+AX_VPP_FIND_JDK8
+AC_SUBST(JAVA_HOME)
+AC_SUBST(JAVAC)
+AC_SUBST(JAVAH)
+AC_SUBST(JAR)
+
+AS_CASE([$host_cpu],
+	[x86_64], [CPU_FLAGS="-march=corei7 -mtune=corei7-avx"],
+	[aarch64], [CPU_FLAGS="-march=armv8-a+crc"],
+	[CPU_FLAGS=""],
+)
+AC_SUBST([CPU_FLAGS])
+
+AC_OUTPUT([Makefile java/Makefile])
+
+AC_CONFIG_MACRO_DIR([m4])
diff --git a/extras/japi/java/.gitignore b/extras/japi/java/.gitignore
new file mode 100644
index 0000000..62165b4
--- /dev/null
+++ b/extras/japi/java/.gitignore
@@ -0,0 +1,2 @@
+# Negate "No core files" pattern from the toplevel .gitignore
+!**/core
\ No newline at end of file
diff --git a/extras/japi/java/Makefile.am b/extras/japi/java/Makefile.am
new file mode 100644
index 0000000..7ef42ba
--- /dev/null
+++ b/extras/japi/java/Makefile.am
@@ -0,0 +1,259 @@
+# 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.
+
+AUTOMAKE_OPTIONS = foreign subdir-objects
+ACLOCAL_AMFLAGS = -I m4
+AM_LIBTOOLFLAGS = --quiet
+
+AM_CFLAGS = @CPU_FLAGS@ -Wall -I${top_srcdir} -I${top_builddir} \
+  -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \
+  -I@top_srcdir@/plugins -I@top_builddir@/plugins
+
+AM_LDFLAGS = -shared -avoid-version -rpath /none -no-undefined
+
+BUILT_SOURCES =
+bin_PROGRAMS =
+noinst_LTLIBRARIES =
+JAR_FILES =
+CLEANDIRS =
+
+#
+# jvpp-common
+#
+
+nobase_include_HEADERS =                \
+  jvpp-common/jvpp_common.h
+
+lib_LTLIBRARIES = libjvpp_common.la
+libjvpp_common_la_SOURCES = jvpp-common/jvpp_common.c
+libjvpp_common_la_LDFLAGS = shared -rpath /none -no-undefined -lvppinfra
+
+JVPP_LIBS = \
+  libjvpp_common.la \
+  -lvppinfra -lvlibmemoryclient -lsvm -lpthread -lm -lrt
+
+#
+# jvpp-registry (connection management + plugin registry)
+#
+
+noinst_LTLIBRARIES += libjvpp_registry.la
+
+libjvpp_registry_la_SOURCES = jvpp-registry/jvpp_registry.c
+libjvpp_registry_la_CPPFLAGS = -Ijvpp-registry
+libjvpp_registry_la_LIBADD = $(JVPP_LIBS)
+libjvpp_registry_la_DEPENDENCIES = libjvpp_common.la
+
+packagedir_jvpp_registry = io/fd/vpp/jvpp
+jvpp_registry_src_files := \
+  $(wildcard @srcdir@/jvpp-registry/$(packagedir_jvpp_registry)/*.java) \
+  $(wildcard @srcdir@/jvpp-registry/$(packagedir_jvpp_registry)/**/*.java)
+
+BUILT_SOURCES += jvpp-registry/io_fd_vpp_jvpp_VppJNIConnection.h
+CLEANDIRS += jvpp-registry/target
+JAR_FILES += jvpp-registry-$(PACKAGE_VERSION).jar
+
+jvpp_registry_ok = jvpp-registry/io_fd_vpp_jvpp_VppJNIConnection.h
+
+jvpp-registry/io_fd_vpp_jvpp_VppJNIConnection.h: $(jvpp_registry_src_files)
+	@echo "  JVPP GEN  $@"
+	@rm -rf jvpp-registry/target
+	@mkdir -p jvpp-registry/target
+	@$(JAVAC) -source 8 -target 8 -d jvpp-registry/target -h jvpp-registry $^
+	@touch jvpp-registry.ok
+
+define japigen
+	@echo "  JVPP GEN  $@"
+	@rm -rf jvpp-$(1)/target
+	@ @srcdir@/jvpp/gen/jvpp_gen.py --plugin_name $(1) --root_dir jvpp-$(1) \
+	  -i $(jvpp_$(1)_json_files)
+	@find jvpp-$(1)/target -name \*.java > jvpp-$(1).generated.files
+	@find @srcdir@/jvpp-$(1) -name \*.java > jvpp-$(1).static.files
+	@$(JAVAC) -source 8 -target 8 -classpath jvpp-registry/target \
+	  -d jvpp-$(1)/target -h jvpp-$(1) @jvpp-$(1).generated.files @jvpp-$(1).static.files
+endef
+
+#
+# jvpp-core (Java wrapper for vpe.api)
+#
+noinst_LTLIBRARIES += libjvpp_core.la
+libjvpp_core_la_SOURCES = jvpp-core/jvpp_core.c jvpp-core/jvpp_core_gen.h
+libjvpp_core_la_CPPFLAGS = -Ijvpp-registry -Ijvpp-core
+libjvpp_core_la_LIBADD = $(JVPP_LIBS)
+libjvpp_core_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h
+JAR_FILES += jvpp-core-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-core/target
+
+# Include all VPP API files
+jvpp_core_json_files = $(shell find @top_builddir@/../vpp/vnet  -type f -name '*.api.json')
+jvpp_core_json_files += $(shell find @top_builddir@/../vpp/vpp -type f -name '*.api.json')
+# except for plugins and unsupported ones:
+# jvpp_core_json_files += @top_builddir@/vlibmemory/memclnt.api.json
+
+jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h: $(jvpp_registry_ok) $(jvpp_core_json_files)
+	$(call japigen,core,JVppCoreImpl)
+
+#
+# ACL Plugin
+#
+if ENABLE_ACL_PLUGIN
+noinst_LTLIBRARIES += libjvpp_acl.la
+libjvpp_acl_la_SOURCES = jvpp-acl/jvpp_acl.c
+libjvpp_acl_la_CPPFLAGS = -Ijvpp-acl
+libjvpp_acl_la_LIBADD = $(JVPP_LIBS)
+libjvpp_acl_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h
+JAR_FILES += jvpp-acl-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-acl/target
+
+jvpp_acl_json_files = @top_builddir@/../vpp/plugins/acl/acl.api.json
+
+jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h: $(jvpp_registry_ok) $(jvpp_acl_json_files)
+	$(call japigen,acl,JVppAclImpl)
+endif
+
+#
+# GTPU Plugin
+#
+if ENABLE_GTPU_PLUGIN
+noinst_LTLIBRARIES += libjvpp_gtpu.la
+libjvpp_gtpu_la_SOURCES = jvpp-gtpu/jvpp_gtpu.c
+libjvpp_gtpu_la_CPPFLAGS = -Ijvpp-gtpu
+libjvpp_gtpu_la_LIBADD = $(JVPP_LIBS)
+libjvpp_gtpu_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h
+JAR_FILES += jvpp-gtpu-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-gtpu/target
+
+jvpp_gtpu_json_files = @top_builddir@/../vpp/plugins/gtpu/gtpu.api.json
+
+jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h: $(jvpp_registry_ok) $(jvpp_gtpu_json_files)
+	$(call japigen,gtpu,JVppGtpuImpl)
+endif
+
+#
+# PPPOE Plugin
+#
+if ENABLE_PPPOE_PLUGIN
+noinst_LTLIBRARIES += libjvpp_pppoe.la
+libjvpp_pppoe_la_SOURCES = jvpp-pppoe/jvpp_pppoe.c
+libjvpp_pppoe_la_CPPFLAGS = -Ijvpp-pppoe
+libjvpp_pppoe_la_LIBADD = $(JVPP_LIBS)
+libjvpp_pppoe_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h
+JAR_FILES += jvpp-pppoe-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-pppoe/target
+
+jvpp_pppoe_json_files = @top_builddir@/../vpp/plugins/pppoe/pppoe.api.json
+
+jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h: $(jvpp_registry_ok) $(jvpp_pppoe_json_files)
+	$(call japigen,pppoe,JVppPppoeImpl)
+endif
+
+#
+# NAT Plugin
+#
+if ENABLE_NAT_PLUGIN
+noinst_LTLIBRARIES += libjvpp_nat.la
+libjvpp_nat_la_SOURCES = jvpp-nat/jvpp_nat.c
+libjvpp_nat_la_CPPFLAGS = -Ijvpp-nat
+libjvpp_nat_la_LIBADD = $(JVPP_LIBS)
+libjvpp_nat_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-nat/io_fd_vpp_jvpp_nat_JVppNatImpl.h
+JAR_FILES += jvpp-nat-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-nat/target
+
+jvpp_nat_json_files = @top_builddir@/../vpp/plugins/nat/nat.api.json
+
+jvpp-nat/io_fd_vpp_jvpp_nat_JVppNatImpl.h: $(jvpp_registry_ok) $(jvpp_nat_json_files)
+	$(call japigen,nat,JVppNatImpl)
+endif
+
+#
+# iOAM Trace Plugin
+#
+if ENABLE_IOAM_PLUGIN
+noinst_LTLIBRARIES += libjvpp_ioamtrace.la
+libjvpp_ioamtrace_la_SOURCES = jvpp-ioamtrace/jvpp_ioam_trace.c
+libjvpp_ioamtrace_la_LIBADD = $(JVPP_LIBS)
+libjvpp_ioamtrace_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h
+JAR_FILES += jvpp-ioamtrace-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-ioamtrace/target
+
+jvpp_ioamtrace_json_files = @top_builddir@/../vpp/plugins/ioam/lib-trace/trace.api.json
+
+jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h: $(jvpp_registry_ok) $(jvpp_ioamtrace_json_files)
+	$(call japigen,ioamtrace,JVppIoamtraceImpl)
+
+#
+# iOAM POT Plugin
+#
+noinst_LTLIBRARIES += libjvpp_ioampot.la
+libjvpp_ioampot_la_SOURCES = jvpp-ioampot/jvpp_ioam_pot.c
+libjvpp_ioampot_la_LIBADD = $(JVPP_LIBS)
+libjvpp_ioampot_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h
+JAR_FILES += jvpp-ioampot-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-ioampot/target
+
+jvpp_ioampot_json_files = @top_builddir@/../vpp/plugins/ioam/lib-pot/pot.api.json
+
+jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h: $(jvpp_registry_ok) $(jvpp_ioampot_json_files)
+	$(call japigen,ioampot,JVppIoampotImpl)
+
+#
+# iOAM Export Plugin
+#
+noinst_LTLIBRARIES += libjvpp_ioamexport.la
+libjvpp_ioamexport_la_SOURCES = jvpp-ioamexport/jvpp_ioam_export.c
+libjvpp_ioamexport_la_LIBADD = $(JVPP_LIBS)
+libjvpp_ioamexport_la_DEPENDENCIES = libjvpp_common.la
+
+BUILT_SOURCES += jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h
+JAR_FILES += jvpp-ioamexport-$(PACKAGE_VERSION).jar
+CLEANDIRS += jvpp-ioamexport/target
+
+jvpp_ioamexport_json_files = @top_builddir@/../vpp/plugins/ioam/export/ioam_export.api.json
+
+jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h: $(jvpp_registry_ok) $(jvpp_ioamexport_json_files)
+	$(call japigen,ioamexport,JVppIoamexportImpl)
+endif
+
+#
+# JAR creation
+#
+jvpp-%-$(PACKAGE_VERSION).jar: libjvpp_%.la
+	@echo "  JAR      $@"
+	@cp .libs/libjvpp_$*.so jvpp-$*/target
+	@$(JAR) cf $(JARFLAGS) $@ -C jvpp-$*/target .
+
+jardir = $(prefix)/share/java
+jar_DATA = $(JAR_FILES)
+
+all-local: $(JAR_FILES)
+
+#
+# Cleanup
+#
+CLEANFILES = jvpp-registry.ok $(JAR_FILES) $(BUILT_SOURCES) *.files */*.h
+
+clean-local:
+	rm -rf $(CLEANDIRS)
diff --git a/extras/japi/java/Readme.txt b/extras/japi/java/Readme.txt
new file mode 100644
index 0000000..689b9b3
--- /dev/null
+++ b/extras/japi/java/Readme.txt
@@ -0,0 +1,236 @@
+= JVpp
+
+JVpp is JNI based Java API for VPP.
+
+== Features
+It is:
+
+* Asynchronous
+* Fully generated
+* Lightweight
+
+== Architecture
+
+=== Plugin support
+
+  /-------------\        /--------------\          /---------------\
+  | JvppPlugin1 +<-------+ JVppRegistry +--------->+ VppConnection |
+  \-------------/  inits \--+-----------/   uses   \---------------/
+                            |
+  /-------------\           |
+  | JvppPlugin2 +<----------+ inits
+  \-------------/           |
+                            |
+  ...                       |
+                            |
+  /----------\              |
+  | JVppCore +<-------------+
+  \----------/
+
+
+VppRegistry opens connection to vpp (VppConnection) and manages jvpp plugins.
+Each plugin needs to be registered in the VppRegistry. Registration involves
+plugin initialization (providing JNI implementation with JVppCallback reference,
+vpp client identifier and vpp shared memory queue address).
+
+API user sends message by calling a method of appropriate plugin interface.
+The call is delegated to JNI implementation provided by the particular plugin.
+When JNI code receives reply, it invokes callback method of JVppCallback
+that corresponds to the received message reply.
+
+=== JVppCore as an example of JVpp plugin architecture
+
+ JVpp Java
+
+  /--------------\             /----------\          /------------\    /------\
+  | JVppRegistry |             | JVppCore |          |  Callbacks |    | DTOs |
+  \----+---------/             \----+-----/          \------+-----/    \------/
+       ^                            ^                       ^
+       | implements                 | implements            | implements
+  /----+--------------\         /---+----------\      /-----+---------\
+  | JVppRegistryImpl* +-------->+ JVppCoreImpl |      |  JVppCallback |
+  \-------+-----------/  inits  \---+----------/      \-------+-------/
+          |                          |                       ^
+          |                          | uses                  | calls back
+          |                          |                       |
+----------|--------------------------|-----------------------|---------------------
+          |                          |                       |
+ C JNI    |                          +-------------------+   |       /-----------------\
+          v                                              |   |   +-->+ jvpp_core_gen.h |
+  /--------+--------\                                    |   |   |   \-----------------/
+  | jpp_registry.c* +---+   /--------+----+----\         |   |   |
+  \-----------------/   |   | << shared lib >> |        /-+--+---+------\
+                        + ->+   jvpp_common*   <--------+  jvpp_core.c* |
+                      uses  \------------------/  uses  \---------------/
+
+
+* Components marked with an asterisk contain manually crafted code, which in addition
+to generated classes form jvpp. Exception applies to Callbacks and DTOs, since there are
+manually crafted marker interfaces in callback and dto package (dto/JVppRequest, dto/JVppReply,
+dto/JVppDump, dto/JVppReplyDump, callback/JVppCallback)
+
+Note: jvpp_core.c calls back the JVppCallback instance with every response. An instance of the
+JVppCallback is provided to jvpp_core.c by JVppRegistryImpl on JVppCoreImpl initialization.
+
+Part of the JVpp is also Future facade. It is asynchronous API returning Future objects
+on top of low level JVpp. It wraps dump reply messages in one DTO using control_ping message
+(provided by JVppRegistry).
+
+
+Future facade
+
+        /----------------\          /---------------\
+        | FutureJVppCore |      +-->+ JVppRegistry* |
+        \-----+----------/      |   \---------------/
+              ^                 |
+              | implements      | uses
+              |                 |
+     /--------+-------------\   |    /------------------------------\
+     | FutureJVppCoreFacade +---+--->+ FutureJVppCoreFacadeCallback |
+     \---------+------------/  uses  \-------+----------------------/
+               |                             |
+---------------|-----------------------------|-------------------------------
+               | uses                        | implements
+JVpp Java      |                             |
+               |                             |
+ /----------\  |                             |
+ | JVppCore +<-+                             |
+ \----+-----/                                |
+      ^                                      |
+      | implements                           v
+ /----+---------\                   /--------+---------------\
+ | JVppCoreImpl |                   | JVppCoreGlobalCallback |
+ \--------------/                   \------------------------/
+
+
+
+Another useful utility of the JVpp is Callback facade. It is asynchronous API
+capable of calling specific callback instance (provided when performing a call)
+per call.
+
+
+Callback facade
+
+        /------------------\          /---------------\
+        | CallbackJVppCore |      +-->+ JVppRegistry* |
+        \-----+------------/      |   \---------------/
+              ^                   |
+              | implements        | uses
+              |                   |
+     /--------+---------------\   |    /--------------------------\
+     | CallbackJVppCoreFacade +---+--->+ CallbackJVppCoreCallback |
+     \---------+--------------/  uses  \-----+--------------------/
+               |                             |
+---------------|-----------------------------|-------------------------------
+               | uses                        | implements
+JVpp Java      |                             |
+               |                             |
+ /----------\  |                             |
+ | JVppCore +<-+                             |
+ \----+-----/                                |
+      ^                                      |
+      | implements                           v
+ /----+---------\                   /----------+-------------\
+ | JVppCoreImpl |                   | JVppCoreGlobalCallback |
+ \--------------/                   \------------------------/
+
+
+== Package structure
+
+* *io.fd.vpp.jvpp* - top level package for generated JVpp interface+ implementation and hand-crafted
+VppConnection interface + implementation - packaged as jvpp-registry-version.jar
+
+* *io.fd.vpp.jvpp.[plugin]* - top level package for generated JVpp interface + implementation
++ plugin's API tests - packaged as jvpp-[plugin]-version.jar
+
+** *dto* - package for DTOs generated from VPP API structures + base/marker hand-crafted interfaces
+(in case of jvpp-registry)
+** *callback* - package for low-level JVpp callbacks and a global callback interface implementing each of
+the low-level JVppcallbacks
+** *future* - package for future based facade on top of JVpp and callbacks
+** *callfacade* - package for callback based facade on top of JVpp and callbacks. Allowing
+users to provide callback per request
+** *test* - package for JVpp standalone tests. Can also serve as samples for JVpp.
+
+C code is structured into modules:
+
+* *jvpp_common* - shared library that provides jvpp_main_t reference used by jvpp_registry and plugins.
+
+* *jvpp_registry* - native library used by JVppRegistryImpl, responsible for:
+
+** VPP connection open/close
+** Rx thread to java thread attach
+** control ping message handling
+
+* *jvpp_core* - native library used by jvpp core plugin:
+** *jvpp_core.c* - contains hand crafted code for core plugin initialization
+** *jvpp_core_gen.h* - contains generated JNI compatible handlers for all requests and replies defined in vpe.api
+
+== Code generators
+All of the required code except the base/marker interfaces is generated using
+simple python2 code generators. The generators use __defs_vpp_papi.py__ input
+file produced by __vppapigen__ from vpe.api file.
+
+=== JNI compatible C code
+Produces __jvpp_[plugin]_gen.h__ file containing JNI compatible handlers for each VPP
+request and reply.
+
+[NOTE]
+====
+Source: jvpp_c_gen.py
+====
+
+=== Request/Reply DTOs
+For all the structures in __defs_vpp_papi.py__ a POJO DTO is produced. Logically,
+there are 4 types of DTOs:
+
+* Request - requests that can be sent to VPP and only a single response is expected
+* DumpRequest - requests that can be sent to VPP and a stream of responses is expected
+* Reply - reply to a simple request or a single response from dump triggered response stream
+* ReplyDump - collection of replies from a single dump request
+* Notifications/Events - Not implemented yet
+
+[NOTE]
+====
+Source: dto_gen.py
+====
+
+=== JVpp
+Produces __JVpp.java__ and __JVppImpl.java__. This is the layer right above JNI compatible C
+code.
+
+[NOTE]
+====
+Source: jvpp_impl_gen.py
+====
+
+=== Callbacks
+Produces callback interface for each VPP reply + a global callback interface called
+__JVpp[plugin]GlobalCallback.java__ aggregating all of the callback interfaces. The JNI
+compatible C code expects only a single instance of this global callback and calls
+it with every reply.
+
+[NOTE]
+====
+Source: callback_gen.py
+====
+
+=== Future facade
+Produces an asynchronous facade on top of JVpp and callbacks, which returns a Future that provides
+matching reply once VPP invocation finishes. Sources produced:
+__FutureJVpp[plugin].java, FutureJVpp[plugin]Facade.java and FutureJVpp[plugin]Callback.java__
+
+[NOTE]
+====
+Source: jvpp_future_facade_gen.py
+====
+
+=== Callback facade
+Similar to future facade, only this facade takes callback objects as part of the invocation
+and the callback is called with result once VPP invocation finishes. Sources produced:
+__CallbackJVpp[plugin].java, CallbackJVpp[plugin]Facade.java and CallbackJVpp[plugin]Callback.java__
+
+[NOTE]
+====
+Source: jvpp_callback_facade_gen.py
+====
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java
new file mode 100644
index 0000000..4806052
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java
@@ -0,0 +1,135 @@
+/*
+ * 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 io.fd.vpp.jvpp.acl.examples;
+
+
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_ADDRESS_2_AS_ARRAY;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_ADDRESS_AS_ARRAY;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_DST_ICMP_TYPE_END;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_DST_ICMP_TYPE_START;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_MAC;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_MAC_MASK;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_PREFIX;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_PREFIX_2;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_END;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_START;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.ICMP_PROTOCOL;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_ADDRESS_2_AS_ARRAY;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_ADDRESS_AS_ARRAY;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_DST_PORT_RANGE_END;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_DST_PORT_RANGE_START;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_MAC;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_MAC_MASK;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_PREFIX;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_PREFIX_2;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_SRC_PORT_RANGE_END;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_SRC_PORT_RANGE_START;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.UDP_PROTOCOL;
+
+import io.fd.vpp.jvpp.acl.dto.AclDetails;
+import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetails;
+import io.fd.vpp.jvpp.acl.dto.MacipAclDetails;
+import io.fd.vpp.jvpp.acl.types.AclRule;
+import io.fd.vpp.jvpp.acl.types.MacipAclRule;
+import java.util.Arrays;
+
+class AclExpectedDumpData {
+
+    static void verifyMacIpDump(final MacipAclDetails macipAclDetails) {
+        // asserting data create by previous call
+        assertEquals(0, macipAclDetails.aclIndex);
+        assertEquals(2, macipAclDetails.count);
+
+        final MacipAclRule currentIpv4Rule = macipAclDetails.r[0];
+        final MacipAclRule currentIpv6Rule = macipAclDetails.r[1];
+
+        // Comparing one property at the time to better pointer if something is wrong
+        //Ipv4 rule
+        assertEquals(0, currentIpv4Rule.isIpv6);
+        assertEquals(1, currentIpv4Rule.isPermit);
+
+        // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always
+        assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4));
+        assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen);
+        assertArrays(FIRST_RULE_MAC, currentIpv4Rule.srcMac);
+        assertArrays(FIRST_RULE_MAC_MASK, currentIpv4Rule.srcMacMask);
+
+        //Ipv6 rule
+        assertEquals(1, currentIpv6Rule.isIpv6);
+        assertEquals(0, currentIpv6Rule.isPermit);
+        assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr);
+        assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen);
+        assertArrays(SECOND_RULE_MAC, currentIpv6Rule.srcMac);
+        assertArrays(SECOND_RULE_MAC_MASK, currentIpv6Rule.srcMacMask);
+    }
+
+    static void verifyAclDump(final AclDetails aclDetails) {
+        assertEquals(0, aclDetails.aclIndex);
+        assertEquals(2, aclDetails.count);
+
+        final AclRule currentIpv4Rule = aclDetails.r[0];
+        final AclRule currentIpv6Rule = aclDetails.r[1];
+
+        // Comparing one property at the time to better pointer if something is wrong
+        //Ipv4 rule
+        assertEquals(0, currentIpv4Rule.isIpv6);
+        assertEquals(1, currentIpv4Rule.isPermit);
+
+        // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always
+        assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4));
+        assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen);
+        assertArrays(FIRST_RULE_ADDRESS_2_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.dstIpAddr, 0, 4));
+        assertEquals(FIRST_RULE_PREFIX_2, currentIpv4Rule.dstIpPrefixLen);
+
+        assertEquals(ICMP_PROTOCOL, currentIpv4Rule.proto);
+        assertEquals(FIRST_RULE_SRC_ICMP_TYPE_START, currentIpv4Rule.srcportOrIcmptypeFirst);
+        assertEquals(FIRST_RULE_SRC_ICMP_TYPE_END, currentIpv4Rule.srcportOrIcmptypeLast);
+        assertEquals(FIRST_RULE_DST_ICMP_TYPE_START, currentIpv4Rule.dstportOrIcmpcodeFirst);
+        assertEquals(FIRST_RULE_DST_ICMP_TYPE_END, currentIpv4Rule.dstportOrIcmpcodeLast);
+
+        assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr);
+        assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen);
+        assertArrays(SECOND_RULE_ADDRESS_2_AS_ARRAY, currentIpv6Rule.dstIpAddr);
+        assertEquals(SECOND_RULE_PREFIX_2, currentIpv6Rule.dstIpPrefixLen);
+
+        assertEquals(UDP_PROTOCOL, currentIpv6Rule.proto);
+        assertEquals(SECOND_RULE_SRC_PORT_RANGE_START, currentIpv6Rule.srcportOrIcmptypeFirst);
+        assertEquals(SECOND_RULE_SRC_PORT_RANGE_END, currentIpv6Rule.srcportOrIcmptypeLast);
+        assertEquals(SECOND_RULE_DST_PORT_RANGE_START, currentIpv6Rule.dstportOrIcmpcodeFirst);
+        assertEquals(SECOND_RULE_DST_PORT_RANGE_END, currentIpv6Rule.dstportOrIcmpcodeLast);
+    }
+
+    static void verifyAclInterfaceList(final AclInterfaceListDetails aclInterfaceListDetails) {
+        assertEquals(1, aclInterfaceListDetails.count);
+        assertEquals(1, aclInterfaceListDetails.acls[0]);
+        assertEquals(0, aclInterfaceListDetails.nInput);
+        assertEquals(0, aclInterfaceListDetails.swIfIndex);
+    }
+
+    private static void assertArrays(final byte[] expected, final byte[] actual) {
+        if (!Arrays.equals(expected, actual)) {
+            throw new IllegalArgumentException(
+                    String.format("Expected[%s]/Actual[%s]", Arrays.toString(expected), Arrays.toString(actual)));
+        }
+    }
+
+    private static void assertEquals(final int expected, final int actual) {
+        if (expected != actual) {
+            throw new IllegalArgumentException(String.format("Expected[%s]/Actual[%s]", expected, actual));
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java
new file mode 100644
index 0000000..199b1b6
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java
@@ -0,0 +1,101 @@
+/*
+ * 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 io.fd.vpp.jvpp.acl.examples;
+
+
+import io.fd.vpp.jvpp.acl.types.AclRule;
+import io.fd.vpp.jvpp.acl.types.MacipAclRule;
+
+class AclTestData {
+
+    static final byte[] FIRST_RULE_ADDRESS_AS_ARRAY = {-64, -88, 2, 1};
+    static final byte[] FIRST_RULE_ADDRESS_2_AS_ARRAY = {-64, -88, 2, 3};
+    static final byte[] SECOND_RULE_ADDRESS_AS_ARRAY =
+            {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1};
+    static final byte[] SECOND_RULE_ADDRESS_2_AS_ARRAY =
+            {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1};
+    static final byte[] FIRST_RULE_MAC = {11, 11, 11, 11, 11, 11};
+    static final byte[] FIRST_RULE_MAC_MASK = {0, 0, 0, 0, 0, 0};
+    static final byte[] SECOND_RULE_MAC = {11, 12, 11, 11, 12, 11};
+    static final byte[] SECOND_RULE_MAC_MASK = {(byte) 170, 0, 0, 0, 0, 0};
+    static final int FIRST_RULE_PREFIX = 32;
+    static final int FIRST_RULE_PREFIX_2 = 24;
+    static final int SECOND_RULE_PREFIX = 64;
+    static final int SECOND_RULE_PREFIX_2 = 62;
+    static final int FIRST_RULE_DST_ICMP_TYPE_START = 0;
+    static final int FIRST_RULE_DST_ICMP_TYPE_END = 8;
+    static final int FIRST_RULE_SRC_ICMP_TYPE_START = 1;
+    static final int FIRST_RULE_SRC_ICMP_TYPE_END = 7;
+    static final int ICMP_PROTOCOL = 1;
+    static final int SECOND_RULE_DST_PORT_RANGE_START = 2000;
+    static final int SECOND_RULE_DST_PORT_RANGE_END = 6000;
+    static final int SECOND_RULE_SRC_PORT_RANGE_START = 400;
+    static final int SECOND_RULE_SRC_PORT_RANGE_END = 2047;
+    static final int UDP_PROTOCOL = 17;
+
+
+    static MacipAclRule[] createMacipRules() {
+        MacipAclRule ruleOne = new MacipAclRule();
+        ruleOne.isIpv6 = 0;
+        ruleOne.isPermit = 1;
+        ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY;
+        ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX;
+        ruleOne.srcMac = FIRST_RULE_MAC;
+        ruleOne.srcMacMask = FIRST_RULE_MAC_MASK;// no mask
+
+        MacipAclRule ruleTwo = new MacipAclRule();
+        ruleTwo.isIpv6 = 1;
+        ruleTwo.isPermit = 0;
+        ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY;
+        ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX;
+        ruleTwo.srcMac = SECOND_RULE_MAC;
+        ruleTwo.srcMacMask = SECOND_RULE_MAC_MASK;
+
+        return new MacipAclRule[]{ruleOne, ruleTwo};
+    }
+
+    static AclRule[] createAclRules() {
+        AclRule ruleOne = new AclRule();
+
+        ruleOne.isIpv6 = 0;
+        ruleOne.isPermit = 1;
+        ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY;
+        ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX;
+        ruleOne.dstIpAddr = FIRST_RULE_ADDRESS_2_AS_ARRAY;
+        ruleOne.dstIpPrefixLen = FIRST_RULE_PREFIX_2;
+        ruleOne.dstportOrIcmpcodeFirst = FIRST_RULE_DST_ICMP_TYPE_START;
+        ruleOne.dstportOrIcmpcodeLast = FIRST_RULE_DST_ICMP_TYPE_END;
+        ruleOne.srcportOrIcmptypeFirst = FIRST_RULE_SRC_ICMP_TYPE_START;
+        ruleOne.srcportOrIcmptypeLast = FIRST_RULE_SRC_ICMP_TYPE_END;
+        ruleOne.proto = ICMP_PROTOCOL; //ICMP
+
+        AclRule ruleTwo = new AclRule();
+        ruleTwo.isIpv6 = 1;
+        ruleTwo.isPermit = 0;
+        ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY;
+        ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX;
+        ruleTwo.dstIpAddr = SECOND_RULE_ADDRESS_2_AS_ARRAY;
+        ruleTwo.dstIpPrefixLen = SECOND_RULE_PREFIX_2;
+        ruleTwo.dstportOrIcmpcodeFirst = SECOND_RULE_DST_PORT_RANGE_START;
+        ruleTwo.dstportOrIcmpcodeLast = SECOND_RULE_DST_PORT_RANGE_END;
+        ruleTwo.srcportOrIcmptypeFirst = SECOND_RULE_SRC_PORT_RANGE_START;
+        ruleTwo.srcportOrIcmptypeLast = SECOND_RULE_SRC_PORT_RANGE_END;
+        ruleTwo.proto = UDP_PROTOCOL; //UDP
+
+        return new AclRule[]{ruleOne, ruleTwo};
+    }
+}
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java
new file mode 100644
index 0000000..149ea46
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java
@@ -0,0 +1,159 @@
+/*
+ * 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 io.fd.vpp.jvpp.acl.examples;
+
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.createAclRules;
+import static io.fd.vpp.jvpp.acl.examples.AclTestData.createMacipRules;
+
+import io.fd.vpp.jvpp.VppInvocationException;
+import io.fd.vpp.jvpp.acl.dto.AclAddReplace;
+import io.fd.vpp.jvpp.acl.dto.AclAddReplaceReply;
+import io.fd.vpp.jvpp.acl.dto.AclDel;
+import io.fd.vpp.jvpp.acl.dto.AclDelReply;
+import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump;
+import io.fd.vpp.jvpp.acl.dto.AclDump;
+import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetailsReplyDump;
+import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDump;
+import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList;
+import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclListReply;
+import io.fd.vpp.jvpp.acl.dto.MacipAclAdd;
+import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply;
+import io.fd.vpp.jvpp.acl.dto.MacipAclAddReplace;
+import io.fd.vpp.jvpp.acl.dto.MacipAclAddReplaceReply;
+import io.fd.vpp.jvpp.acl.dto.MacipAclDel;
+import io.fd.vpp.jvpp.acl.dto.MacipAclDelReply;
+import io.fd.vpp.jvpp.acl.dto.MacipAclDetailsReplyDump;
+import io.fd.vpp.jvpp.acl.dto.MacipAclDump;
+import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade;
+import java.util.concurrent.ExecutionException;
+
+class AclTestRequests {
+
+    static MacipAclDetailsReplyDump sendMacIpDumpRequest(final FutureJVppAclFacade jvpp)
+            throws ExecutionException, InterruptedException {
+        System.out.println("Sending MacipAclDump request...");
+        MacipAclDetailsReplyDump dump = jvpp.macipAclDump(new MacipAclDump()).toCompletableFuture().get();
+        System.out.println("MacipAclDump returned");
+        return dump;
+    }
+
+    static void sendMacIpAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException {
+        final MacipAclAdd request = createMacIpAddRequest();
+        System.out.printf("Sending MacipAclAdd request %s%n", request.toString());
+        final MacipAclAddReply reply = jvpp.macipAclAdd(createMacIpAddRequest()).toCompletableFuture().get();
+        System.out.printf("MacipAclAdd send result = %s%n", reply);
+    }
+
+    static void sendMacIpAddReplaceRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException {
+        final MacipAclAddReplace request = createMacIpAddReplaceRequest();
+        System.out.printf("Sending MacipAclAddReplace request %s%n", request.toString());
+        final MacipAclAddReplaceReply reply = jvpp.macipAclAddReplace(createMacIpAddReplaceRequest()).toCompletableFuture().get();
+        System.out.printf("MacipAclAddReplace send result = %s%n", reply);
+    }
+
+    static void sendMacIpDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException {
+        final MacipAclDel request = new MacipAclDel();
+        request.aclIndex = 0;
+        System.out.printf("Sending MacipAclDel request %s%n", request.toString());
+        final MacipAclDelReply reply = jvpp.macipAclDel(request).toCompletableFuture().get();
+        System.out.printf("MacipAclDel send result = %s%n", reply);
+    }
+
+    static void sendAclAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException {
+        final AclAddReplace request = createAclAddRequest();
+        System.out.printf("Sending AclAddReplace request %s%n", request.toString());
+        final AclAddReplaceReply reply = jvpp.aclAddReplace(request).toCompletableFuture().get();
+        System.out.printf("AclAddReplace send result = %s%n", reply);
+    }
+
+    static AclDetailsReplyDump sendAclDumpRequest(final FutureJVppAclFacade jvpp)
+            throws InterruptedException, VppInvocationException, ExecutionException {
+        System.out.println("Sending AclDump request...");
+        final AclDetailsReplyDump dump = jvpp.aclDump(new AclDump()).toCompletableFuture().get();
+        System.out.printf("AclDump send result = %s%n", dump);
+        return dump;
+    }
+
+    static void sendAclDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException {
+        final AclDel request = new AclDel();
+        request.aclIndex = 0;
+        System.out.printf("Sending AclDel request %s%n", request.toString());
+        final AclDelReply reply = jvpp.aclDel(request).toCompletableFuture().get();
+        System.out.printf("AclDel send result = %s%n", reply);
+    }
+
+    static AclInterfaceListDetailsReplyDump sendAclInterfaceListDumpRequest(final FutureJVppAclFacade jvpp)
+            throws InterruptedException, ExecutionException {
+        final AclInterfaceListDump request = new AclInterfaceListDump();
+        request.swIfIndex = 0;
+        System.out.printf("Sending AclInterfaceListDump request %s%n", request.toString());
+        final AclInterfaceListDetailsReplyDump dump = jvpp.aclInterfaceListDump(request).toCompletableFuture().get();
+        System.out.printf("AclInterfaceListDump send result = %s%n", dump);
+        return dump;
+    }
+
+    static void sendAclInterfaceSetAclList(final FutureJVppAclFacade jvpp)
+            throws InterruptedException, ExecutionException {
+        final AclInterfaceSetAclList request = new AclInterfaceSetAclList();
+        request.count = 1;
+        request.acls = new int[]{1};
+        request.swIfIndex = 0;
+        request.nInput = 0;
+        System.out.printf("Sending AclInterfaceSetAclList request %s%n", request.toString());
+        final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get();
+        System.out.printf("AclInterfaceSetAclList send result = %s%n", reply);
+    }
+
+    static void sendAclInterfaceDeleteList(final FutureJVppAclFacade jvpp)
+            throws InterruptedException, ExecutionException {
+        // uses same api but sets list to empty
+        final AclInterfaceSetAclList request = new AclInterfaceSetAclList();
+        request.count = 0;
+        request.acls = new int[]{};
+        request.swIfIndex = 0;
+        request.nInput = 0;
+        System.out.printf("Sending AclInterfaceSetAclList(Delete) request %s%n", request.toString());
+        final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get();
+        System.out.printf("AclInterfaceSetAclList(Delete) send result = %s%n", reply);
+    }
+
+    private static MacipAclAdd createMacIpAddRequest() {
+        MacipAclAdd request = new MacipAclAdd();
+
+        request.count = 2;
+        request.r = createMacipRules();
+        return request;
+    }
+
+    private static MacipAclAddReplace createMacIpAddReplaceRequest() {
+        MacipAclAddReplace request = new MacipAclAddReplace();
+
+        request.count = 2;
+        request.aclIndex = 0;
+        request.r = createMacipRules();
+        return request;
+    }
+
+    private static AclAddReplace createAclAddRequest() {
+        AclAddReplace request = new AclAddReplace();
+
+        request.aclIndex = -1;// to define new one
+        request.count = 2;
+        request.r = createAclRules();
+        return request;
+    }
+}
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java
new file mode 100644
index 0000000..862df8d
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java
@@ -0,0 +1,68 @@
+/*
+ * 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 io.fd.vpp.jvpp.acl.examples;
+
+import static io.fd.vpp.jvpp.acl.examples.AclExpectedDumpData.verifyAclDump;
+import static io.fd.vpp.jvpp.acl.examples.AclExpectedDumpData.verifyAclInterfaceList;
+import static io.fd.vpp.jvpp.acl.examples.AclExpectedDumpData.verifyMacIpDump;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclAddRequest;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclDelRequest;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclDumpRequest;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclInterfaceDeleteList;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclInterfaceListDumpRequest;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclInterfaceSetAclList;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendMacIpAddRequest;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendMacIpDelRequest;
+import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendMacIpDumpRequest;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.acl.JVppAclImpl;
+import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade;
+
+public class FutureApiExample {
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi();
+    }
+
+    private static void testCallbackApi() throws Exception {
+        System.out.println("Testing Java callback API for acl plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("macipAclAddTest");
+             final FutureJVppAclFacade jvpp = new FutureJVppAclFacade(registry, new JVppAclImpl())) {
+
+            // adds,dump and verifies  Mac-Ip acl
+            sendMacIpAddRequest(jvpp);
+            verifyMacIpDump(sendMacIpDumpRequest(jvpp).macipAclDetails.get(0));
+
+            // adds,dumps and verifies Acl acl
+            sendAclAddRequest(jvpp);
+            verifyAclDump(sendAclDumpRequest(jvpp).aclDetails.get(0));
+
+            // adds,dumps and verifies Interface for acl
+            sendAclInterfaceSetAclList(jvpp);
+            verifyAclInterfaceList(sendAclInterfaceListDumpRequest(jvpp).aclInterfaceListDetails.get(0));
+
+            // deletes all created data
+            sendAclInterfaceDeleteList(jvpp);
+            sendAclDelRequest(jvpp);
+            sendMacIpDelRequest(jvpp);
+
+            System.out.println("Disconnecting...");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt
new file mode 100644
index 0000000..d17fbfc
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.examples.FutureApiExample
+debug version:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-debug-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.examples.FutureApiExample
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java
new file mode 100644
index 0000000..a7bbb7f
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp.acl.test;
+
+import io.fd.vpp.jvpp.AbstractCallbackApiTest;
+import io.fd.vpp.jvpp.acl.JVppAclImpl;
+
+import java.util.logging.Logger;
+
+public class CallbackApiTest extends AbstractCallbackApiTest {
+
+    private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName());
+
+
+    public static void main(String[] args) throws Exception {
+        LOG.info("Testing ControlPing using Java callback API for core plugin");
+        testControlPing(args[0], new JVppAclImpl());
+    }
+}
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java
new file mode 100644
index 0000000..ff1c73c
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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 io.fd.vpp.jvpp.acl.test;
+
+import io.fd.vpp.jvpp.Assertions;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.acl.JVppAclImpl;
+import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump;
+import io.fd.vpp.jvpp.acl.dto.AclDump;
+import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi(args);
+    }
+
+    private static void testFutureApi(String[] args) throws Exception {
+        LOG.info("Testing Java future API for core plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]);
+             final FutureJVppAclFacade jvppFacade = new FutureJVppAclFacade(registry, new JVppAclImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testAclDump(jvppFacade);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    private static void testAclDump(final FutureJVppAclFacade jvpp) throws Exception {
+        LOG.info("Sending AclDump request...");
+        final AclDump request = new AclDump();
+
+        final CompletableFuture<AclDetailsReplyDump>
+            replyFuture = jvpp.aclDump(request).toCompletableFuture();
+        final AclDetailsReplyDump reply = replyFuture.get();
+
+        Assertions.assertNotNull(reply);
+    }
+
+
+}
diff --git a/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt
new file mode 100644
index 0000000..1b46585
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.test.[test-name]
+debug version:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.test.[test-name]
diff --git a/extras/japi/java/jvpp-acl/jvpp_acl.c b/extras/japi/java/jvpp-acl/jvpp_acl.c
new file mode 100644
index 0000000..22e44f1
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/jvpp_acl.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <acl/acl_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <acl/acl_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h"
+#include "jvpp_acl.h"
+#include "jvpp-acl/jvpp_acl_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_acl_JVppaclImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  acl_main_t * plugin_main = &acl_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+      if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_close0
+(JNIEnv *env, jclass clazz) {
+  acl_main_t * plugin_main = &acl_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP ACL */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP ACL */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-acl/jvpp_acl.h b/extras/japi/java/jvpp-acl/jvpp_acl.h
new file mode 100644
index 0000000..d1ec78f
--- /dev/null
+++ b/extras/japi/java/jvpp-acl/jvpp_acl.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_acl_h__
+#define __included_jvpp_acl_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-acl */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} acl_main_t;
+
+acl_main_t acl_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_acl_h__ */
diff --git a/extras/japi/java/jvpp-common/jvpp_common.c b/extras/japi/java/jvpp-common/jvpp_common.c
new file mode 100644
index 0000000..2425607
--- /dev/null
+++ b/extras/japi/java/jvpp-common/jvpp_common.c
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+#define _GNU_SOURCE /* for strcasestr(3) */
+
+#include <vnet/api_errno.h>
+#include "jvpp_common.h"
+
+#ifndef JVPP_DEBUG
+#define JVPP_DEBUG 0
+#endif
+
+#if JVPP_DEBUG == 1
+#define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+#define DEBUG_LOG(...)
+#endif
+
+#define _(error,errorCode,msg)  \
+if (errorCode == code)          \
+    message = msg;              \
+else
+
+#define get_error_message(errno)    \
+int code = errno;                   \
+foreach_vnet_api_error              \
+    message = "Reason unknown";
+
+/* shared jvpp main structure */
+jvpp_main_t jvpp_main __attribute__((aligned (64)));
+
+void call_on_error(const char* callName, int contextId, int retval,
+        jclass callbackClass, jobject callbackObject,
+        jclass callbackExceptionClass) {
+    DEBUG_LOG("\nCallOnError : callback=%s, retval=%d, context=%d\n", callName,
+            clib_net_to_host_u32(retval), clib_net_to_host_u32(context));
+    JNIEnv *env = jvpp_main.jenv;
+    if (!callbackClass) {
+        DEBUG_LOG("CallOnError : jm->callbackClass is null!\n");
+        return;
+    }
+    jmethodID excConstructor = (*env)->GetMethodID(env, callbackExceptionClass,
+            "<init>", "(Ljava/lang/String;Ljava/lang/String;II)V");
+    if (!excConstructor) {
+        DEBUG_LOG("CallOnError : excConstructor is null!\n");
+        return;
+    }
+    jmethodID callbackExcMethod = (*env)->GetMethodID(env, callbackClass,
+            "onError", "(Lio/fd/vpp/jvpp/VppCallbackException;)V");
+    if (!callbackExcMethod) {
+        DEBUG_LOG("CallOnError : callbackExcMethod is null!\n");
+        return;
+    }
+
+    char *message;
+    get_error_message(clib_net_to_host_u32(retval));
+    jobject excObject = (*env)->NewObject(env, callbackExceptionClass,
+            excConstructor, (*env)->NewStringUTF(env, callName),
+            (*env)->NewStringUTF(env, message),
+            clib_net_to_host_u32(contextId), clib_net_to_host_u32(retval));
+    if (!excObject) {
+        DEBUG_LOG("CallOnError : excObject is null!\n");
+        return;
+    }
+
+    (*env)->CallVoidMethod(env, callbackObject, callbackExcMethod, excObject);
+    DEBUG_LOG("CallOnError : Response sent\n");
+}
+#undef _
+
+u32 get_message_id(JNIEnv *env, const char *key) {
+    uword *p = hash_get(jvpp_main.messages_hash, key);
+    if (!p) {
+        jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException");
+        char *msgBuf  = clib_mem_alloc(strlen(key) + 70);
+        strcpy(msgBuf, "API mismatch detected: ");
+        strcat(msgBuf, key);
+        strcat(msgBuf, " is missing in global name_crc hash table.");
+        DEBUG_LOG("%s", msgBuf);
+        DEBUG_LOG("Possible reasons:");
+        DEBUG_LOG("1) incompatible VPP version used");
+        DEBUG_LOG("2) message present in JSON file but not in global name_crc table");
+        (*env)->ThrowNew(env, exClass, msgBuf);
+        clib_mem_free(msgBuf);
+        return 0;
+    }
+    return (u32) p[0];
+}
diff --git a/extras/japi/java/jvpp-common/jvpp_common.h b/extras/japi/java/jvpp-common/jvpp_common.h
new file mode 100644
index 0000000..14027a9
--- /dev/null
+++ b/extras/japi/java/jvpp-common/jvpp_common.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_common_h__
+#define __included_jvpp_common_h__
+//
+#include <vppinfra/types.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+typedef struct {
+    /* Unique identifier used for matching replays with requests  */
+    volatile u32 context_id;
+
+    /* Spinlock */
+    volatile u32 lock;
+    u32 tag;
+
+    /* JNI Native Method Interface pointer for message handlers */
+    JNIEnv *jenv;
+
+    /* JNI Invoke Interface pointer for attachment of rx thread to java thread */
+    JavaVM *jvm;
+
+    /* Convenience */
+    svm_queue_t * vl_input_queue;
+    u32 my_client_index;
+    uword *messages_hash;
+} jvpp_main_t;
+
+extern jvpp_main_t jvpp_main __attribute__((aligned (64)));
+
+static_always_inline u32 vppjni_get_context_id(jvpp_main_t * jm) {
+    return __sync_add_and_fetch(&jm->context_id, 1);
+}
+
+static_always_inline void vppjni_lock(jvpp_main_t * jm, u32 tag) {
+    while (__sync_lock_test_and_set(&jm->lock, 1))
+        ;
+    jm->tag = tag;
+}
+
+static_always_inline void vppjni_unlock(jvpp_main_t * jm) {
+    jm->tag = 0;
+    CLIB_MEMORY_BARRIER();
+    jm->lock = 0;
+}
+
+/**
+ * Calls onError callback on callbackObject reference. Passes instance of callbackExceptionClass as parameter.
+ */
+void call_on_error(const char* callName, int contextId, int retval,
+        jclass callbackClass, jobject callbackObject,
+        jclass callbackExceptionClass);
+
+/**
+ * Retrieves message id based on message name and crc (key format: name_crc).
+ * Throws java/lang/IllegalStateException on failure.
+ */
+u32 get_message_id(JNIEnv *env, const char* key);
+
+#endif /* __included_jvpp_common_h__ */
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java
new file mode 100644
index 0000000..b99979c
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java
@@ -0,0 +1,100 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.callback.GetNodeIndexReplyCallback;
+import io.fd.vpp.jvpp.core.callback.ShowVersionReplyCallback;
+import io.fd.vpp.jvpp.core.callback.SwInterfaceDetailsCallback;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
+import io.fd.vpp.jvpp.core.dto.ShowVersion;
+import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
+import java.nio.charset.StandardCharsets;
+
+public class CallbackApiExample {
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi();
+    }
+
+    private static void testCallbackApi() throws Exception {
+        System.out.println("Testing Java callback API with JVppRegistry");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CallbackApiExample");
+             final JVpp jvpp = new JVppCoreImpl()) {
+            registry.register(jvpp, new TestCallback());
+
+            System.out.println("Sending ShowVersion request...");
+            final int result = jvpp.send(new ShowVersion());
+            System.out.printf("ShowVersion send result = %d%n", result);
+
+            System.out.println("Sending GetNodeIndex request...");
+            GetNodeIndex getNodeIndexRequest = new GetNodeIndex();
+            getNodeIndexRequest.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8);
+            jvpp.send(getNodeIndexRequest);
+
+            System.out.println("Sending SwInterfaceDump request...");
+            SwInterfaceDump swInterfaceDumpRequest = new SwInterfaceDump();
+            swInterfaceDumpRequest.nameFilterValid = 0;
+            swInterfaceDumpRequest.nameFilter = "".getBytes(StandardCharsets.UTF_8);
+            jvpp.send(swInterfaceDumpRequest);
+
+            Thread.sleep(1000);
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    static class TestCallback implements GetNodeIndexReplyCallback, ShowVersionReplyCallback, SwInterfaceDetailsCallback {
+
+        @Override
+        public void onGetNodeIndexReply(final GetNodeIndexReply msg) {
+            System.out.printf("Received GetNodeIndexReply: %s%n", msg);
+        }
+
+        @Override
+        public void onShowVersionReply(final ShowVersionReply msg) {
+            System.out.printf("Received ShowVersionReply: context=%d, program=%s, version=%s, "
+                    + "buildDate=%s, buildDirectory=%s%n",
+                msg.context,
+                new String(msg.program, StandardCharsets.UTF_8),
+                new String(msg.version, StandardCharsets.UTF_8),
+                new String(msg.buildDate, StandardCharsets.UTF_8),
+                new String(msg.buildDirectory, StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public void onSwInterfaceDetails(final SwInterfaceDetails msg) {
+            System.out.printf("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, "
+                    + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n",
+                new String(msg.interfaceName, StandardCharsets.UTF_8), msg.l2AddressLength, msg.adminUpDown,
+                msg.linkUpDown, msg.linkSpeed, (int) msg.linkMtu);
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+                ex.getCtxId(), ex.getErrorCode());
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiReadPerfTest.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiReadPerfTest.java
new file mode 100644
index 0000000..6ff440d
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiReadPerfTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.callback.ShowVersionReplyCallback;
+import io.fd.vpp.jvpp.core.dto.*;
+
+import java.util.logging.Logger;
+
+public class CallbackApiReadPerfTest {
+
+    private static final Logger LOG = Logger.getLogger(CallbackApiReadPerfTest.class.getName());
+    private static final ShowVersion REQUEST = new ShowVersion();
+
+    /**
+     *
+     * @param args - for running for one sec requires no parameter
+     *             - for running for set amount of requests requires one parameters, desired REQUEST amount
+     * @throws Exception if arguments aren't String representations of numbers
+     */
+    public static void main(String[] args) throws Exception {
+        if (args.length != 0) {
+            testInvokeCounter(true, Integer.parseUnsignedInt(args[0]));
+        } else {
+            testInvokeCounter(false, 0);
+        }
+    }
+
+    /**
+     *
+     * @param setCount true = run with set amount of requests, false = run for 1 sec
+     * @param count number of request with which test should be run
+     * @throws Exception
+     */
+    private static void testInvokeCounter(boolean setCount, int count) throws Exception {
+        LOG.info("Testing callback API Invocation Counter");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CallbackApiReadPerfTest");
+             final JVpp jvpp = new JVppCoreImpl()) {
+            TestCallback callback = new TestCallback(count);
+            registry.register(jvpp, callback);
+            if (!setCount) {
+                for(int i = 0; i < 5; i++) {
+                    callback.reset();
+                    LOG.info("Starting invocation for 1sec");
+                    long time = System.nanoTime();
+                    do {
+                        jvpp.send(REQUEST);
+                    } while (System.nanoTime() - time < 1000000000 || callback.stop());
+                    int replyCount =  callback.getReplyCounter();
+                    LOG.info(String.format("Invocation count within 1 second: %d", replyCount));
+                }
+            } else {
+                for (int i = 0; i < 5; i++) {
+                    LOG.info("Starting invocations");
+                    callback.reset();
+                    long time = System.nanoTime();
+                    for (int x = 0; x < count; x++) {
+                        jvpp.send(REQUEST);
+                    }
+                    long timeAfter = callback.getTime();
+                    LOG.info(String.format("Invocations took %d ns (%f invocations/s)", timeAfter - time,
+                            count * (1000000000.0/(timeAfter - time))));
+                }
+            }
+
+
+            Thread.sleep(1000);
+            LOG.info("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    static class TestCallback implements ShowVersionReplyCallback {
+
+        private int replyCounter = 0;
+        private int count;
+        private long time = 0;
+        private boolean stop = false;
+
+        public TestCallback(int count) throws Exception {
+            this.count = count;
+        }
+
+        public int getReplyCounter() {
+            return replyCounter;
+        }
+
+        public void reset() {
+            replyCounter = 0;
+            time = 0;
+            stop = false;
+        }
+
+        public boolean stop() {
+            this.stop = true;
+            return false;
+        }
+
+        /* actual method called from VPP
+           not thread safe but since there's only one VPP thread listening for requests and calling
+           this method it's OK
+         */
+        @Override
+        public void onShowVersionReply(final ShowVersionReply msg) {
+            if (stop) {
+                return;
+            }
+            replyCounter++;
+            if (replyCounter == count ) {
+                time = System.nanoTime();
+            }
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+                ex.getCtxId(), ex.getErrorCode());
+        }
+
+        public long getTime() throws Exception {
+            while(time == 0) {
+                Thread.sleep(1000);
+            }
+            return time;
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiWritePerfTest.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiWritePerfTest.java
new file mode 100644
index 0000000..1940ddc
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiWritePerfTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2017 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.*;
+import io.fd.vpp.jvpp.core.callback.ClassifyAddDelTableReplyCallback;
+
+import java.util.logging.Logger;
+
+public class CallbackApiWritePerfTest {
+
+    private static final Logger LOG = Logger.getLogger(CallbackApiWritePerfTest.class.getName());
+    private static final ClassifyAddDelTable REQUEST = createAddDelTable();
+
+    private static ClassifyAddDelTable createAddDelTable () {
+        ClassifyAddDelTable addDelTable = new ClassifyAddDelTable();
+        addDelTable.isAdd = 1;
+        addDelTable.tableIndex = -1;
+        addDelTable.nbuckets = 2;
+        addDelTable.memorySize = 2 << 20;
+        addDelTable.nextTableIndex = ~0; // default
+        addDelTable.missNextIndex = ~0; // default
+        addDelTable.skipNVectors = 0;
+        addDelTable.matchNVectors = 1;
+        addDelTable.mask =
+                new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+                        (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00};
+        return addDelTable;
+    };
+
+    /**
+     *
+     * @param args - for running for one sec requires no parameter
+     *             - for running for set amount of requests requires one parameters, desired REQUEST amount
+     * @throws Exception if arguments aren't String representations of numbers
+     */
+    public static void main(String[] args) throws Exception {
+        if (args.length != 0) {
+            testInvokeCounter(true, Integer.parseUnsignedInt(args[0]));
+        } else {
+            testInvokeCounter(false, 0);
+        }
+    }
+
+    /**
+     *
+     * @param setCount true = run with set amount of requests, false = run for 1 sec
+     * @param count number of requests with which test should be run
+     * @throws Exception
+     */
+    private static void testInvokeCounter(boolean setCount, int count) throws Exception {
+        LOG.info("Testing callback API Invocation Counter");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CallbackApiWritePerfTest");
+             final JVpp jvpp = new JVppCoreImpl()) {
+            TestCallback callback = new TestCallback(count);
+            registry.register(jvpp, callback);
+            if (!setCount) {
+                for(int i = 0; i < 5; i++) {
+                    callback.reset();
+                    LOG.info("Starting invocation for 1sec");
+                    long time = System.nanoTime();
+                    do {
+                        jvpp.send(REQUEST);
+                    } while (System.nanoTime() - time < 1000000000 || callback.stop());
+                    int replyCount = callback.getReplyCounter();
+                    LOG.info(String.format("Invocation count within 1 second: %d", replyCount));
+                }
+            } else {
+                for(int i = 0; i < 5; i++) {
+                    LOG.info("Starting invocations");
+                    callback.reset();
+                    long time = System.nanoTime();
+                    for (int x = 1; x <= count; x++) {
+                        jvpp.send(REQUEST);
+                    }
+                    long timeAfter = callback.getTime();
+                    LOG.info(String.format("Invocations took %d ns (%f invocations/s)", timeAfter - time,
+                            count * (1000000000.0 / (timeAfter - time))));
+                }
+            }
+
+
+            Thread.sleep(1000);
+            LOG.info("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    static class TestCallback implements ClassifyAddDelTableReplyCallback {
+
+        private int replyCounter = 0;
+        private int count;
+        private long time = 0;
+        private boolean stop = false;
+
+        public TestCallback(int count) throws Exception {
+            this.count = count;
+        }
+
+        public int getReplyCounter() {
+            return replyCounter;
+        }
+
+        public void reset() {
+            replyCounter = 0;
+            time = 0;
+            stop = false;
+        }
+
+        public boolean stop() {
+            this.stop = true;
+            return false;
+        }
+
+        /* actual method called from VPP
+           not thread safe but since there's only one VPP thread listening for requests and calling
+           this method it's OK
+         */
+        @Override
+        public void onClassifyAddDelTableReply(final ClassifyAddDelTableReply msg) {
+            if (stop) {
+                return;
+            }
+            replyCounter++;
+            if (replyCounter == count ) {
+                time = System.nanoTime();
+            }
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+                    ex.getCtxId(), ex.getErrorCode());
+        }
+
+        public long getTime() throws Exception {
+            while(time == 0) {
+                Thread.sleep(1000);
+            }
+            return time;
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java
new file mode 100644
index 0000000..dc2bdcb
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java
@@ -0,0 +1,110 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.callback.GetNodeIndexReplyCallback;
+import io.fd.vpp.jvpp.core.callback.ShowVersionReplyCallback;
+import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
+import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * CallbackJVppFacade together with CallbackJVppFacadeCallback allow for setting different callback for each request.
+ * This is more convenient than the approach shown in CallbackApiExample.
+ */
+public class CallbackJVppFacadeExample {
+
+    private static ShowVersionReplyCallback showVersionCallback1 = new ShowVersionReplyCallback() {
+        @Override
+        public void onShowVersionReply(final ShowVersionReply msg) {
+            System.out.printf("ShowVersionCallback1 received ShowVersionReply: context=%d, program=%s,"
+                    + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context,
+                new String(msg.program, StandardCharsets.UTF_8),
+                new String(msg.version, StandardCharsets.UTF_8),
+                new String(msg.buildDate, StandardCharsets.UTF_8),
+                new String(msg.buildDirectory, StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception in showVersionCallback1: call=%s, reply=%d, context=%d%n",
+                ex.getMethodName(), ex.getErrorCode(), ex.getCtxId());
+        }
+    };
+
+    private static ShowVersionReplyCallback showVersionCallback2 = new ShowVersionReplyCallback() {
+        @Override
+        public void onShowVersionReply(final ShowVersionReply msg) {
+            System.out.printf("ShowVersionCallback2 received ShowVersionReply: context=%d, program=%s,"
+                    + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context,
+                new String(msg.program, StandardCharsets.UTF_8),
+                new String(msg.version, StandardCharsets.UTF_8),
+                new String(msg.buildDate, StandardCharsets.UTF_8),
+                new String(msg.buildDirectory, StandardCharsets.UTF_8));
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception in showVersionCallback2: call=%s, reply=%d, context=%d%n",
+                ex.getMethodName(), ex.getErrorCode(), ex.getCtxId());
+        }
+
+    };
+
+    private static GetNodeIndexReplyCallback getNodeIndexCallback = new GetNodeIndexReplyCallback() {
+        @Override
+        public void onGetNodeIndexReply(final GetNodeIndexReply msg) {
+            System.out.printf("Received GetNodeIndexReply: %s%n", msg);
+        }
+
+        @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 testCallbackFacade() throws Exception {
+        System.out.println("Testing CallbackJVppFacade");
+
+        try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeExample");
+             final CallbackJVppCoreFacade callbackFacade = new CallbackJVppCoreFacade(registry, new JVppCoreImpl())) {
+            System.out.println("Successfully connected to VPP");
+
+            callbackFacade.showVersion(showVersionCallback1);
+            callbackFacade.showVersion(showVersionCallback2);
+
+            GetNodeIndex getNodeIndexRequest = new GetNodeIndex();
+            getNodeIndexRequest.nodeName = "dummyNode".getBytes(StandardCharsets.UTF_8);
+            callbackFacade.getNodeIndex(getNodeIndexRequest, getNodeIndexCallback);
+
+            Thread.sleep(2000);
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCallbackFacade();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java
new file mode 100644
index 0000000..832464a
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java
@@ -0,0 +1,97 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.core.JVppCore;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsReplyCallback;
+import io.fd.vpp.jvpp.core.callback.SwInterfaceEventCallback;
+import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade;
+import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceEvent;
+
+public class CallbackJVppFacadeNotificationExample {
+
+    private static void testCallbackFacade() throws Exception {
+        System.out.println("Testing CallbackJVppFacade for notifications");
+
+        try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeExample");
+             final JVppCore jvpp = new JVppCoreImpl()) {
+            final CallbackJVppCoreFacade jvppCallbackFacade = new CallbackJVppCoreFacade(registry, jvpp);
+            System.out.println("Successfully connected to VPP");
+
+            final AutoCloseable notificationListenerReg =
+                jvppCallbackFacade.getEventRegistry().registerSwInterfaceEventCallback(
+                        new SwInterfaceEventCallback() {
+                            public void onSwInterfaceEvent(SwInterfaceEvent reply) {
+                                System.out.printf("Received interface notification: ifc: %s%n", reply);
+                            }
+
+                            public void onError (VppCallbackException ex) {
+                                System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n",
+                                        ex.getMethodName(), ex.getCtxId(), ex.getErrorCode());
+                            }
+                        });
+
+            jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getEnableInterfaceNotificationsReq(),
+                new WantInterfaceEventsReplyCallback() {
+                    @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 WantInterfaceEventsReplyCallback() {
+                    @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...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCallbackFacade();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java
new file mode 100644
index 0000000..9ed418e
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java
@@ -0,0 +1,88 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getChangeInterfaceState;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getDisableInterfaceNotificationsReq;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getEnableInterfaceNotificationsReq;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.printNotification;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.callback.SwInterfaceEventCallback;
+import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsReplyCallback;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceEvent;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply;
+import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply;
+
+public class CallbackNotificationApiExample {
+
+    private static void testCallbackApi() throws Exception {
+        System.out.println("Testing Java callback API for notifications");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CallbackNotificationApiExample");
+             final JVpp jvpp = new JVppCoreImpl()) {
+            registry.register(jvpp, 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);
+
+            // Notifications are received
+            Thread.sleep(500);
+
+            getDisableInterfaceNotificationsReq().send(jvpp);
+            System.out.println("Interface events stopped");
+
+            Thread.sleep(2000);
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi();
+    }
+
+    private static class TestCallback implements SwInterfaceEventCallback,
+            WantInterfaceEventsReplyCallback {
+
+        @Override
+        public void onSwInterfaceEvent(
+            final SwInterfaceEvent msg) {
+            printNotification(msg);
+        }
+
+        @Override
+        public void onWantInterfaceEventsReply(final WantInterfaceEventsReply wantInterfaceEventsReply) {
+            System.out.println("Interface notification stream updated");
+        }
+
+        @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());
+
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java
new file mode 100644
index 0000000..3db6d30
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java
@@ -0,0 +1,121 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import static java.util.Objects.requireNonNull;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.CreateSubif;
+import io.fd.vpp.jvpp.core.dto.CreateSubifReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * <p>Tests sub-interface creation.<br> Equivalent to:<br>
+ *
+ * <pre>{@code
+ * vppctl create sub GigabitEthernet0/9/0 1 dot1q 100 inner-dot1q any
+ * }
+ * </pre>
+ *
+ * To verify invoke:<br>
+ * <pre>{@code
+ * vpp_api_test json
+ * vat# sw_interface_dump
+ * }
+ */
+public class CreateSubInterfaceExample {
+
+    private static SwInterfaceDump createSwInterfaceDumpRequest(final String ifaceName) {
+        SwInterfaceDump request = new SwInterfaceDump();
+        request.nameFilter = ifaceName.getBytes(StandardCharsets.UTF_8);
+        request.nameFilterValid = 1;
+        return request;
+    }
+
+    private static void requireSingleIface(final SwInterfaceDetailsReplyDump response, final String ifaceName) {
+        if (response.swInterfaceDetails.size() != 1) {
+            throw new IllegalStateException(
+                String.format("Expected one interface matching filter %s but was %d", ifaceName,
+                    response.swInterfaceDetails.size()));
+        }
+    }
+
+    private static CreateSubif createSubifRequest(final int swIfIndex, final int subId) {
+        CreateSubif request = new CreateSubif();
+        request.swIfIndex = swIfIndex; // super interface id
+        request.subId = subId;
+        request.noTags = 0;
+        request.oneTag = 0;
+        request.twoTags = 1;
+        request.dot1Ad = 0;
+        request.exactMatch = 1;
+        request.defaultSub = 0;
+        request.outerVlanIdAny = 0;
+        request.innerVlanIdAny = 1;
+        request.outerVlanId = 100;
+        request.innerVlanId = 0;
+        return request;
+    }
+
+    private static void print(CreateSubifReply reply) {
+        System.out.printf("CreateSubifReply: %s%n", reply);
+    }
+
+    private static void testCreateSubInterface() throws Exception {
+        System.out.println("Testing sub-interface creation using Java callback API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CreateSubInterfaceExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            System.out.println("Successfully connected to VPP");
+            Thread.sleep(1000);
+
+            final String ifaceName = "Gigabitethernet0/8/0";
+
+            final SwInterfaceDetailsReplyDump swInterfaceDetails =
+                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(ifaceName)).toCompletableFuture().get();
+
+            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
+            requireNonNull(swInterfaceDetails.swInterfaceDetails, "swInterfaceDetails is null");
+            requireSingleIface(swInterfaceDetails, ifaceName);
+
+            final int swIfIndex = swInterfaceDetails.swInterfaceDetails.get(0).swIfIndex;
+            final int subId = 1;
+
+            final CreateSubifReply createSubifReply =
+                jvppFacade.createSubif(createSubifRequest(swIfIndex, subId)).toCompletableFuture().get();
+            print(createSubifReply);
+
+            final String subIfaceName = "Gigabitethernet0/8/0." + subId;
+            final SwInterfaceDetailsReplyDump subIface =
+                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(subIfaceName)).toCompletableFuture().get();
+            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
+            requireNonNull(subIface.swInterfaceDetails, "swInterfaceDump returned null");
+            requireSingleIface(swInterfaceDetails, ifaceName);
+
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCreateSubInterface();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java
new file mode 100644
index 0000000..931c9b3
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java
@@ -0,0 +1,127 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDump;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
+import io.fd.vpp.jvpp.core.dto.ShowVersion;
+import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FutureApiExample {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiExample.class.getName());
+
+    private static void testShowVersion(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending ShowVersion request...");
+        final Future<ShowVersionReply> replyFuture = jvpp.showVersion(new ShowVersion()).toCompletableFuture();
+        final ShowVersionReply reply = replyFuture.get();
+        LOG.info(
+            String.format(
+                "Received ShowVersionReply: context=%d, program=%s, version=%s, buildDate=%s, buildDirectory=%s%n",
+                reply.context, new String(reply.program, StandardCharsets.UTF_8),
+                new String(reply.version, StandardCharsets.UTF_8),
+                new String(reply.buildDate, StandardCharsets.UTF_8),
+                new String(reply.buildDirectory, StandardCharsets.UTF_8)));
+    }
+
+    private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending ShowVersion request...");
+        final BridgeDomainDump request = new BridgeDomainDump();
+        request.bdId = -1; // dump call
+
+        final CompletableFuture<BridgeDomainDetailsReplyDump>
+            replyFuture = jvpp.bridgeDomainDump(request).toCompletableFuture();
+        final BridgeDomainDetailsReplyDump reply = replyFuture.get();
+
+        if (reply == null || reply.bridgeDomainDetails == null) {
+            LOG.severe("Received null response for empty dump: " + reply);
+        } else {
+            LOG.info(
+                String.format(
+                    "Received bridge-domain dump reply with list of bridge-domains: %s",
+                    reply.bridgeDomainDetails));
+        }
+    }
+
+    private static void testGetNodeIndex(final FutureJVppCoreFacade jvpp) {
+        LOG.info("Sending GetNodeIndex request...");
+        final GetNodeIndex request = new GetNodeIndex();
+        request.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8);
+        final Future<GetNodeIndexReply> replyFuture = jvpp.getNodeIndex(request).toCompletableFuture();
+        try {
+            final GetNodeIndexReply reply = replyFuture.get();
+            LOG.info(
+                String.format(
+                    "Received GetNodeIndexReply: context=%d, nodeIndex=%d%n", reply.context, reply.nodeIndex));
+        } catch (Exception e) {
+            LOG.log(Level.SEVERE, "GetNodeIndex request failed", e);
+        }
+    }
+
+    private static void testSwInterfaceDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending SwInterfaceDump request...");
+        final SwInterfaceDump request = new SwInterfaceDump();
+        request.nameFilterValid = 0;
+        request.nameFilter = "".getBytes(StandardCharsets.UTF_8);
+
+        final Future<SwInterfaceDetailsReplyDump> replyFuture = jvpp.swInterfaceDump(request).toCompletableFuture();
+        final SwInterfaceDetailsReplyDump reply = replyFuture.get();
+        for (SwInterfaceDetails details : reply.swInterfaceDetails) {
+            Objects.requireNonNull(details, "reply.swInterfaceDetails contains null element!");
+            LOG.info(
+                String.format("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, "
+                        + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n",
+                    new String(details.interfaceName, StandardCharsets.UTF_8),
+                    details.l2AddressLength, details.adminUpDown,
+                    details.linkUpDown, details.linkSpeed, (int) details.linkMtu));
+        }
+    }
+
+    private static void testFutureApi() throws Exception {
+        LOG.info("Testing Java future API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testEmptyBridgeDomainDump(jvppFacade);
+            testShowVersion(jvppFacade);
+            testGetNodeIndex(jvppFacade);
+            testSwInterfaceDump(jvppFacade);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java
new file mode 100644
index 0000000..3c84fd7
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java
@@ -0,0 +1,67 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getChangeInterfaceState;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getDisableInterfaceNotificationsReq;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getEnableInterfaceNotificationsReq;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import io.fd.vpp.jvpp.core.callback.SwInterfaceEventCallback;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceEvent;
+import io.fd.vpp.jvpp.VppCallbackException;
+
+public class FutureApiNotificationExample {
+
+    private static void testFutureApi() throws Exception {
+        System.out.println("Testing Java future API for notifications");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiNotificationExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl());
+             final AutoCloseable notificationListenerReg =
+                 jvppFacade.getEventRegistry()
+                     .registerSwInterfaceEventCallback(new SwInterfaceEventCallback() {
+                         public void onSwInterfaceEvent(SwInterfaceEvent reply) {
+                             System.out.printf("Received interface notification: ifc: %s%n", reply);
+                         }
+
+                         public void onError (VppCallbackException ex) {
+                             System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n",
+                                     ex.getMethodName(), ex.getCtxId(), ex.getErrorCode());
+                         }
+                     })) {
+            System.out.println("Successfully connected to VPP");
+            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");
+            System.out.println("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiReadPerfTest.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiReadPerfTest.java
new file mode 100644
index 0000000..f335b28
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiReadPerfTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2017 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.callback.ShowVersionReplyCallback;
+import io.fd.vpp.jvpp.core.dto.*;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.logging.Logger;
+
+public class FutureApiReadPerfTest {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiReadPerfTest.class.getName());
+    private static final ShowVersion REQUEST = new ShowVersion();
+    private static volatile int currentCount = 0;
+    private static int desiredCount = 0;
+    private static long timeAfter = 0;
+    private static volatile boolean stop = false;
+    /**
+     * Run after reply message is received
+     * in case of running for 1 sec check if time passed (stop variable) and if it does skip processing
+     * in case of running for set amount of REQUEST, record time in which was last reply received
+     * not thread save but since reading part process only one message at a time it's ok
+     */
+    private static Runnable replyFc = () -> {
+        if (stop) {
+            return;
+        }
+        currentCount++;
+        if(currentCount == desiredCount) {
+            timeAfter = System.nanoTime();
+        }
+    };
+
+    /**
+     * Used to reset counters and flags between runs
+     */
+    private static void reset() {
+        currentCount = 0;
+        timeAfter = 0;
+        stop = false;
+    }
+
+    public static boolean stop() {
+        stop = true;
+        return false;
+    }
+
+    /**
+     *
+     * @return time of last reply received
+     * @throws Exception during thread sleep
+     */
+    private static long getTime() throws Exception {
+        while(timeAfter == 0) {
+            LOG.info(String.format("Received %d replies", currentCount));
+            Thread.sleep(1000);
+        }
+        return timeAfter;
+    }
+
+    /**
+     *
+     * @param args - for running for one sec requires no parameter
+     *             - for running for set amount of requests requires one parameters, desired REQUEST amount
+     * @throws Exception if arguments aren't String representations of numbers
+     */
+    public static void main(String[] args) throws Exception {
+        if (args.length == 1) {
+            desiredCount =  Integer.parseUnsignedInt(args[0]);
+            testInvokeCounter(true);
+        } else {
+            testInvokeCounter(false);
+        }
+    }
+
+    /**
+     *
+     * @param setCount true = run with set amount of requests, false = run for 1 sec
+     * @throws Exception
+     */
+    private static void testInvokeCounter(boolean setCount) throws Exception {
+        LOG.info("Testing callback API Invocation Counter");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiReadPerfTest");
+             final FutureJVppCoreFacade jvpp = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            if (!setCount) {
+                for(int i = 0; i < 5; i++) {
+                    reset();
+                    LOG.info("Starting invocation for 1sec");
+                    long time = System.nanoTime();
+                    do {
+                        CompletableFuture<ShowVersionReply> replyFuture = jvpp.showVersion(REQUEST).toCompletableFuture();
+                        replyFuture.thenRun(replyFc);
+                    } while (System.nanoTime() - time < 1000000000 || stop());
+                    LOG.info(String.format("Invocation count within 1 second: %d", currentCount));
+                }
+            } else {
+                for (int i = 0; i < 5; i++) {
+                    LOG.info("Starting invocations");
+                    reset();
+                    long time = System.nanoTime();
+                    for (int x = 0; x < desiredCount; x++) {
+                        CompletableFuture<ShowVersionReply> replyFuture = jvpp.showVersion(REQUEST).toCompletableFuture();
+                        replyFuture.thenRun(replyFc);
+                    }
+                    LOG.info("Invocations send");
+                    long timeAfter = getTime();
+                    LOG.info(String.format("Invocations took %d ns (%f invocations/s)", timeAfter - time,
+                            desiredCount * (1000000000.0/(timeAfter - time))));
+                }
+            }
+
+
+            Thread.sleep(1000);
+            LOG.info("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java
new file mode 100644
index 0000000..f89043a
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java
@@ -0,0 +1,195 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
+import io.fd.vpp.jvpp.core.dto.ClassifySessionDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.ClassifySessionDump;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableIds;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableIdsReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableInfo;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableInfoReply;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * <p>Tests L2 ACL creation and read.<br> Equivalent to the following vppctl commands:<br>
+ *
+ * <pre>{@code
+ * vppctl classify table mask l2 src
+ * vppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06
+ * vppctl set int input acl intfc local0 l2-table 0
+ * vppctl sh class table verbose
+ * }
+ * </pre>
+ */
+public class L2AclExample {
+
+    private static final int LOCAL0_IFACE_ID = 0;
+
+    private static ClassifyAddDelTable createClassifyTable() {
+        ClassifyAddDelTable request = new ClassifyAddDelTable();
+        request.isAdd = 1;
+        request.tableIndex = ~0; // default
+        request.nbuckets = 2;
+        request.memorySize = 2 << 20;
+        request.nextTableIndex = ~0; // default
+        request.missNextIndex = ~0; // default
+        request.skipNVectors = 0;
+        request.matchNVectors = 1;
+        request.mask =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+                (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00};
+        return request;
+    }
+
+    private static ClassifyTableInfo createClassifyTableInfoRequest(final int tableId) {
+        ClassifyTableInfo request = new ClassifyTableInfo();
+        request.tableId = tableId;
+        return request;
+    }
+
+    private static ClassifyAddDelSession createClassifySession(final int tableIndex) {
+        ClassifyAddDelSession request = new ClassifyAddDelSession();
+        request.isAdd = 1;
+        request.tableIndex = tableIndex;
+        request.hitNextIndex = 0; // deny
+        request.opaqueIndex = 0;
+        request.advance = 0; // default
+        // match 01:02:03:04:05:06 mac address
+        request.match =
+            new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+                (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00};
+        return request;
+    }
+
+    private static ClassifySessionDump createClassifySessionDumpRequest(final int newTableIndex) {
+        ClassifySessionDump request = new ClassifySessionDump();
+        request.tableId = newTableIndex;
+        return request;
+    }
+
+    private static InputAclSetInterface aclSetInterface() {
+        InputAclSetInterface request = new InputAclSetInterface();
+        request.isAdd = 1;
+        request.swIfIndex = LOCAL0_IFACE_ID;
+        request.ip4TableIndex = ~0; // skip
+        request.ip6TableIndex = ~0; // skip
+        request.l2TableIndex = 0;
+        return request;
+    }
+
+    private static ClassifyTableByInterface createClassifyTableByInterfaceRequest() {
+        ClassifyTableByInterface request = new ClassifyTableByInterface();
+        request.swIfIndex = LOCAL0_IFACE_ID;
+        return request;
+    }
+
+    private static void print(ClassifyAddDelTableReply reply) {
+        System.out.printf("ClassifyAddDelTableReply: %s%n", reply);
+    }
+
+    private static void print(ClassifyTableIdsReply reply) {
+        System.out.printf("ClassifyTableIdsReply: %s%n", reply);
+    }
+
+    private static void print(final ClassifyTableInfoReply reply) {
+        System.out.println(reply);
+        if (reply != null) {
+            System.out.println("Mask hex: " + DatatypeConverter.printHexBinary(reply.mask));
+        }
+    }
+
+    private static void print(ClassifyAddDelSessionReply reply) {
+        System.out.printf("ClassifyAddDelSessionReply: context=%s%n", reply);
+    }
+
+    private static void print(final ClassifySessionDetailsReplyDump reply) {
+        System.out.println(reply);
+        reply.classifySessionDetails.forEach(detail -> {
+            System.out.println(detail);
+            System.out.println("Match hex: " + DatatypeConverter.printHexBinary(detail.match));
+        });
+    }
+
+    private static void print(final InputAclSetInterfaceReply reply) {
+        System.out.printf("InputAclSetInterfaceReply: context=%s%n", reply);
+    }
+
+    private static void print(final ClassifyTableByInterfaceReply reply) {
+        System.out.printf("ClassifyAddDelTableReply: %s%n", reply);
+    }
+
+    private static void testL2Acl() throws Exception {
+        System.out.println("Testing L2 ACLs using Java callback API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("L2AclExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+
+            System.out.println("Successfully connected to VPP");
+            Thread.sleep(1000);
+
+            final ClassifyAddDelTableReply classifyAddDelTableReply =
+                jvppFacade.classifyAddDelTable(createClassifyTable()).toCompletableFuture().get();
+            print(classifyAddDelTableReply);
+
+            final ClassifyTableIdsReply classifyTableIdsReply =
+                jvppFacade.classifyTableIds(new ClassifyTableIds()).toCompletableFuture().get();
+            print(classifyTableIdsReply);
+
+            final ClassifyTableInfoReply classifyTableInfoReply =
+                jvppFacade.classifyTableInfo(createClassifyTableInfoRequest(classifyAddDelTableReply.newTableIndex))
+                    .toCompletableFuture().get();
+            print(classifyTableInfoReply);
+
+            final ClassifyAddDelSessionReply classifyAddDelSessionReply =
+                jvppFacade.classifyAddDelSession(createClassifySession(classifyAddDelTableReply.newTableIndex))
+                    .toCompletableFuture().get();
+            print(classifyAddDelSessionReply);
+
+            final ClassifySessionDetailsReplyDump classifySessionDetailsReplyDump =
+                jvppFacade.classifySessionDump(createClassifySessionDumpRequest(classifyAddDelTableReply.newTableIndex))
+                    .toCompletableFuture().get();
+            print(classifySessionDetailsReplyDump);
+
+            final InputAclSetInterfaceReply inputAclSetInterfaceReply =
+                jvppFacade.inputAclSetInterface(aclSetInterface()).toCompletableFuture().get();
+            print(inputAclSetInterfaceReply);
+
+            final ClassifyTableByInterfaceReply classifyTableByInterfaceReply =
+                jvppFacade.classifyTableByInterface(createClassifyTableByInterfaceRequest()).toCompletableFuture()
+                    .get();
+            print(classifyTableByInterfaceReply);
+
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testL2Acl();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java
new file mode 100644
index 0000000..f637669
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java
@@ -0,0 +1,125 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.LispAddDelAdjacency;
+import io.fd.vpp.jvpp.core.dto.LispAddDelLocalEid;
+import io.fd.vpp.jvpp.core.dto.LispAddDelLocatorSet;
+import io.fd.vpp.jvpp.core.dto.LispAddDelRemoteMapping;
+import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGet;
+import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGetReply;
+import io.fd.vpp.jvpp.core.dto.LispEnableDisable;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+
+/**
+ * Tests lisp adjacency creation and read (custom vpe.api type support showcase).
+ */
+public class LispAdjacencyExample {
+
+    private static final Logger LOG = Logger.getLogger(LispAdjacencyExample.class.getName());
+
+    private static void enableLisp(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispEnableDisable request = new LispEnableDisable();
+        request.isEn = 1;
+        jvpp.lispEnableDisable(request).toCompletableFuture().get();
+        LOG.info("Lisp enabled successfully");
+    }
+
+    private static void addLocatorSet(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispAddDelLocatorSet request = new LispAddDelLocatorSet();
+        request.isAdd = 1;
+        request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8);
+        jvpp.lispAddDelLocatorSet(request).toCompletableFuture().get();
+        LOG.info("Locator set created successfully:" + request.toString());
+    }
+
+    private static void addLocalEid(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispAddDelLocalEid request = new LispAddDelLocalEid();
+        request.isAdd = 1;
+        request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8);
+        request.eid = new byte[] {1, 2, 1, 10};
+        request.eidType = 0; // ip4
+        request.vni = 0;
+        request.prefixLen = 32;
+        jvpp.lispAddDelLocalEid(request).toCompletableFuture().get();
+        LOG.info("Local EID created successfully:" + request.toString());
+    }
+
+    private static void addRemoteMapping(final FutureJVppCoreFacade jvpp)
+        throws ExecutionException, InterruptedException {
+        final LispAddDelRemoteMapping request = new LispAddDelRemoteMapping();
+        request.isAdd = 1;
+        request.vni = 0;
+        request.eid = new byte[] {1, 2, 1, 20};
+        request.eidLen = 32;
+        request.rlocNum = 1;
+        // FIXME!!!!
+        //request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+        jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get();
+        LOG.info("Remote mapping created successfully:" + request.toString());
+    }
+
+    private static void addAdjacency(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispAddDelAdjacency request = new LispAddDelAdjacency();
+        request.isAdd = 1;
+        request.leid = new byte[] {1, 2, 1, 10};
+        request.leidLen = 32;
+        request.reid = new byte[] {1, 2, 1, 20};
+        request.reidLen = 32;
+        request.eidType = 0; // ip4
+        request.vni = 0;
+        jvpp.lispAddDelAdjacency(request).toCompletableFuture().get();
+        LOG.info("Lisp adjacency created successfully:" + request.toString());
+    }
+
+    private static void showAdjacencies(final FutureJVppCoreFacade jvpp)
+        throws ExecutionException, InterruptedException {
+        final LispAdjacenciesGetReply reply =
+            jvpp.lispAdjacenciesGet(new LispAdjacenciesGet()).toCompletableFuture().get();
+        LOG.info("Lisp adjacency received successfully:" + reply.toString());
+    }
+
+    private static void testAdjacency(final FutureJVppCoreFacade jvpp) throws Exception {
+        enableLisp(jvpp);
+        addLocatorSet(jvpp);
+        addLocalEid(jvpp);
+        addRemoteMapping(jvpp);
+        addAdjacency(jvpp);
+        showAdjacencies(jvpp);
+    }
+
+    private static void testFutureApi() throws Exception {
+        LOG.info("Create lisp adjacency test");
+        try (final JVppRegistry registry = new JVppRegistryImpl("LispAdjacencyExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testAdjacency(jvppFacade);
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java
new file mode 100644
index 0000000..e963d63
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.examples;
+
+import java.io.PrintStream;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceEvent;
+import io.fd.vpp.jvpp.core.dto.WantInterfaceEvents;
+
+final class NotificationUtils {
+
+    private NotificationUtils() {}
+
+    static PrintStream printNotification(final SwInterfaceEvent msg) {
+        return System.out.printf("Received interface notification: ifc: %s%n", msg);
+    }
+
+    static SwInterfaceSetFlags getChangeInterfaceState() {
+        final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags();
+        swInterfaceSetFlags.swIfIndex = 0;
+        swInterfaceSetFlags.adminUpDown = 1;
+        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/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt
new file mode 100644
index 0000000..9fe3c7a
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt
@@ -0,0 +1,24 @@
+This package contains basic examples for jvpp. To run the examples:
+
+- Make sure VPP is running
+- From VPP's build-root/ folder execute:
+  - release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-18.01.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-18.01.jar io.fd.vpp.jvpp.core.examples.[test name]
+  - debug version: sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-18.01.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-core-18.01.jar io.fd.vpp.jvpp.core.examples.[test name]
+
+Available examples:
+CallbackApiExample - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs
+CallbackJVppFacadeNotificationExample - Example of interface notifications using Callback based JVpp facade
+CallbackJVppFacadeExample - Execution of more complex calls using Callback based JVpp facade
+CallbackNotificationApiExample -  Example of interface notifications using low level JVpp APIs
+CreateSubInterfaceExample - Example of sub-interface creation
+FutureApiNotificationExample - Example of interface notifications using Future based JVpp facade
+FutureApiExample - Execution of more complex calls using Future based JVpp facade
+L2AclExample - Example of L2 ACL creation
+LispAdjacencyExample - Example of lisp adjacency creation and read (custom vpe.api type support showcase)
+
+CallbackApiReadPerfTest, FutureApiReadPerfTest, CallbackApiWritePerfTest - test provide two ways to count invocations:
+1) maximum number of invocations and received replyies within 1 sec
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-18.01.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-18.01.jar io.fd.vpp.jvpp.core.examples.[test name]
+2) measure time in ns from first request to receiving last reply over set amount of requests
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-18.01.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-18.01.jar io.fd.vpp.jvpp.core.examples.[test name] [number of request to send]
+
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java
new file mode 100644
index 0000000..493116c
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.test;
+
+import io.fd.vpp.jvpp.AbstractCallbackApiTest;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+
+import java.util.logging.Logger;
+
+public class CallbackApiTest extends AbstractCallbackApiTest {
+
+    private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName());
+
+
+    public static void main(String[] args) throws Exception {
+        LOG.info("Testing ControlPing using Java callback API for core plugin");
+        testControlPing(args[0], new JVppCoreImpl());
+    }
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
new file mode 100644
index 0000000..d3acecc
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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 io.fd.vpp.jvpp.core.test;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDump;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
+import io.fd.vpp.jvpp.core.dto.ShowVersion;
+import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi(args);
+    }
+
+    private static void testFutureApi(String[] args) throws Exception {
+        LOG.info("Testing Java future API for core plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]);
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testEmptyBridgeDomainDump(jvppFacade);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending BridgeDomainDump request...");
+        final BridgeDomainDump request = new BridgeDomainDump();
+        request.bdId = -1; // dump call
+
+        final CompletableFuture<BridgeDomainDetailsReplyDump>
+            replyFuture = jvpp.bridgeDomainDump(request).toCompletableFuture();
+        final BridgeDomainDetailsReplyDump reply = replyFuture.get();
+
+        if (reply == null || reply.bridgeDomainDetails == null) {
+            throw new IllegalStateException("Received null response for empty dump: " + reply);
+        } else {
+            LOG.info(
+                String.format(
+                    "Received bridge-domain dump reply with list of bridge-domains: %s",
+                    reply.bridgeDomainDetails));
+        }
+    }
+
+
+}
diff --git a/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt
new file mode 100644
index 0000000..b74cf60
--- /dev/null
+++ b/extras/japi/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt
@@ -0,0 +1,18 @@
+This package contains basic tests for jvpp. To run the tests:
+
+- Make sure VPP is running
+- From VPP's build-root/ folder execute:
+  - release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.test.[test name]
+  - debug version: sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.test.[test name]
+
+Available tests:
+CallbackApiTest - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs
+CallbackJVppFacadeNotificationTest - Tests interface notifications using Callback based JVpp facade
+CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade
+CallbackNotificationApiTest - Tests interface notifications using low level JVpp APIs
+ControlPingTest - Simple test executing a single control ping using low level JVpp APIs
+CreateSubInterfaceTest - Tests sub-interface creation
+FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade
+FutureApiTest - Execution of more complex calls using Future based JVpp facade
+L2AclTest - Tests L2 ACL creation
+LispAdjacencyTest - Tests lisp adjacency creation and read (custom vpe.api type support showcase)
diff --git a/extras/japi/java/jvpp-core/jvpp_core.c b/extras/japi/java/jvpp-core/jvpp_core.c
new file mode 100644
index 0000000..09d7530
--- /dev/null
+++ b/extras/japi/java/jvpp-core/jvpp_core.c
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <vpp/api/vpe_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+#include <jvpp-common/jvpp_common.h>
+
+// TODO: generate jvpp_plugin_name.c files (or at least reuse plugin's main structure)
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} core_main_t;
+
+core_main_t core_main __attribute__((aligned (64)));
+
+#include "io_fd_vpp_jvpp_core_JVppCoreImpl.h"
+#include "jvpp_core_gen.h"
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_init0
+(JNIEnv * env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+    core_main_t * plugin_main = &core_main;
+    plugin_main->my_client_index = my_client_index;
+    plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
+
+    plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+    plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+    // verify API has not changed since jar generation (exit on mismatch)
+    #define _(N)             \
+        if (get_message_id(env, #N) == 0) return;
+        foreach_supported_api_message;
+    #undef _
+
+    #define _(N,n)                                  \
+        vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+                vl_api_##n##_t_handler,             \
+                vl_noop_handler,                    \
+                vl_noop_handler,              \
+                vl_noop_handler,               \
+                sizeof(vl_api_##n##_t), 1);
+        foreach_api_reply_handler;
+    #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_close0
+(JNIEnv *env, jclass clazz) {
+    core_main_t * plugin_main = &core_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
+
+
+
diff --git a/extras/japi/java/jvpp-gtpu/jvpp_gtpu.c b/extras/japi/java/jvpp-gtpu/jvpp_gtpu.c
new file mode 100644
index 0000000..8e523ca
--- /dev/null
+++ b/extras/japi/java/jvpp-gtpu/jvpp_gtpu.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <gtpu/gtpu_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <gtpu/gtpu_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h"
+#include "jvpp_gtpu.h"
+#include "jvpp-gtpu/jvpp_gtpu_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_gtpu_JVppgtpuImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_gtpu_JVppGtpuImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  gtpu_main_t * plugin_main = &gtpu_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_gtpu_JVppGtpuImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = (svm_queue_t *)queue_address;
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+      if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_gtpu_JVppGtpuImpl_close0
+(JNIEnv *env, jclass clazz) {
+  gtpu_main_t * plugin_main = &gtpu_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP ACL */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP ACL */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-gtpu/jvpp_gtpu.h b/extras/japi/java/jvpp-gtpu/jvpp_gtpu.h
new file mode 100644
index 0000000..fa7c8b7
--- /dev/null
+++ b/extras/japi/java/jvpp-gtpu/jvpp_gtpu.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_gtpu_h__
+#define __included_jvpp_gtpu_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-gtpu */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} gtpu_main_t;
+
+gtpu_main_t gtpu_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_gtpu_h__ */
diff --git a/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java
new file mode 100644
index 0000000..2f5b7db
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java
@@ -0,0 +1,56 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioamexport.examples;
+
+import java.net.InetAddress;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl;
+import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade;
+import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisable;
+import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisableReply;
+
+public class IoamExportApiExample {
+
+    public static void main(String[] args) throws Exception {
+        ioamExportTestApi();
+    }
+
+    private static void ioamExportTestApi() throws Exception {
+        System.out.println("Testing Java API for ioam export plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("ioamExportApiExample");
+             final JVpp jvpp = new JVppIoamexportImpl()) {
+	    FutureJVppIoamexportFacade ioamexportJvpp = new FutureJVppIoamexportFacade(registry,jvpp);
+            System.out.println("Sending ioam export request...");
+	    IoamExportIp6EnableDisable request = new IoamExportIp6EnableDisable();
+            request.isDisable = 0;
+	    InetAddress collectorAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0000");
+	    InetAddress srcAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0001");
+	    request.collectorAddress = collectorAddress.getAddress();
+	    request.srcAddress = srcAddress.getAddress();
+	    IoamExportIp6EnableDisableReply reply = ioamexportJvpp.ioamExportIp6EnableDisable(request).toCompletableFuture().get();
+            System.out.printf("IoamExportIp6EnableDisableReply = "+reply.toString()+"%n");
+
+            Thread.sleep(1000);
+
+            System.out.println("Disconnecting...");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt
new file mode 100644
index 0000000..f2dfe91
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.examples.IoamExportApiExample
+debug vresion:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.examples.IoamExportApiExample
diff --git a/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java
new file mode 100644
index 0000000..ba49d77
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioamexport.test;
+
+import io.fd.vpp.jvpp.AbstractCallbackApiTest;
+import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl;
+
+import java.util.logging.Logger;
+
+
+public class CallbackApiTest extends AbstractCallbackApiTest {
+
+    private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        LOG.info("Testing ControlPing using Java callback API for ioamexport plugin");
+        testControlPing(args[0], new JVppIoamexportImpl());
+    }
+}
diff --git a/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java
new file mode 100644
index 0000000..048d244
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.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 io.fd.vpp.jvpp.ioamexport.test;
+
+
+import io.fd.vpp.jvpp.Assertions;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl;
+import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisable;
+import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisableReply;
+import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade;
+
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi(args);
+    }
+
+    private static void testCallbackApi(String[] args) throws Exception {
+        LOG.info("Testing Java callback API for ioamexport plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]);
+             final FutureJVppIoamexportFacade jvpp = new FutureJVppIoamexportFacade(registry, new JVppIoamexportImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testIoamExportIp6EnableDisable(jvpp);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    private static void testIoamExportIp6EnableDisable(FutureJVppIoamexportFacade jvpp) throws Exception {
+        LOG.info("Sending IoamExportIp6EnableDisable request...");
+        final IoamExportIp6EnableDisable request = new IoamExportIp6EnableDisable();
+
+        final Future<IoamExportIp6EnableDisableReply> replyFuture = jvpp.ioamExportIp6EnableDisable(request).toCompletableFuture();
+        final IoamExportIp6EnableDisableReply reply = replyFuture.get();
+
+        Assertions.assertNotNull(reply);
+    }
+}
diff --git a/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt
new file mode 100644
index 0000000..820071a
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.test.[test-name]
+debug version:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.test.[test-name]
diff --git a/extras/japi/java/jvpp-ioamexport/jvpp_ioam_export.c b/extras/japi/java/jvpp-ioamexport/jvpp_ioam_export.c
new file mode 100644
index 0000000..74ae438
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/jvpp_ioam_export.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <ioam/export/ioam_export_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <ioam/export/ioam_export_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h"
+#include "jvpp_ioam_export.h"
+#include "jvpp-ioamexport/jvpp_ioamexport_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  ioamexport_main_t * plugin_main = &ioamexport_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+      if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_close0
+(JNIEnv *env, jclass clazz) {
+  ioamexport_main_t * plugin_main = &ioamexport_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP iOAM EXPORT */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP iOAM EXPORT */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-ioamexport/jvpp_ioam_export.h b/extras/japi/java/jvpp-ioamexport/jvpp_ioam_export.h
new file mode 100644
index 0000000..596a054
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamexport/jvpp_ioam_export.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_ioam_export_h__
+#define __included_jvpp_ioam_export_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-IOAM-EXPORT */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} ioamexport_main_t;
+
+ioamexport_main_t ioamexport_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_ioam_export_h__ */
diff --git a/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java
new file mode 100644
index 0000000..b9ed7d0
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java
@@ -0,0 +1,76 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioampot.examples;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl;
+import io.fd.vpp.jvpp.ioampot.callback.PotProfileAddReplyCallback;
+import io.fd.vpp.jvpp.ioampot.dto.PotProfileAdd;
+import io.fd.vpp.jvpp.ioampot.dto.PotProfileAddReply;
+import java.nio.charset.StandardCharsets;
+
+public class IoamPotApiExample {
+
+    static class IoamPotTestCallback implements PotProfileAddReplyCallback {
+
+        @Override
+        public void onPotProfileAddReply(final PotProfileAddReply reply) {
+            System.out.printf("Received PotProfileAddReply reply: context=%d%n",
+                reply.context);
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+                ex.getCtxId(), ex.getErrorCode());
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        ioamPotTestApi();
+    }
+
+    private static void ioamPotTestApi() throws Exception {
+        System.out.println("Testing Java API for ioam pot plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("ioamPotApiExample");
+             final JVpp jvpp = new JVppIoampotImpl()) {
+            registry.register(jvpp, new IoamPotTestCallback());
+
+            System.out.println("Sending ioam pot profile add request...");
+            PotProfileAdd request = new PotProfileAdd();
+            request.id = 0;
+            request.validator = 4;
+            request.secretKey = 1;
+            request.secretShare = 2;
+            request.prime = 1234;
+            request.maxBits = 53;
+            request.lpc = 1234;
+            request.polynomialPublic = 1234;
+            request.listNameLen = (byte)"test pot profile".getBytes(StandardCharsets.UTF_8).length;
+            request.listName = "test pot profile".getBytes(StandardCharsets.UTF_8);
+            final int result = jvpp.send(request);
+            System.out.printf("PotProfileAdd send result = %d%n", result);
+
+            Thread.sleep(1000);
+
+            System.out.println("Disconnecting...");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt
new file mode 100644
index 0000000..e91550b
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.examples.IoamPotApiExample
+debug vresion:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.examples.IoamPotApiExample
diff --git a/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java
new file mode 100644
index 0000000..20b85d8
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioampot.test;
+
+import io.fd.vpp.jvpp.AbstractCallbackApiTest;
+import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl;
+
+import java.util.logging.Logger;
+
+public class CallbackApiTest extends AbstractCallbackApiTest {
+
+    private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName());
+
+
+    public static void main(String[] args) throws Exception {
+        LOG.info("Testing ControlPing using Java callback API for ioampot plugin");
+        testControlPing(args[0], new JVppIoampotImpl());
+    }
+}
diff --git a/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java
new file mode 100644
index 0000000..6401c67
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioampot.test;
+
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl;
+import io.fd.vpp.jvpp.ioampot.dto.PotProfileShowConfigDetailsReplyDump;
+import io.fd.vpp.jvpp.ioampot.dto.PotProfileShowConfigDump;
+import io.fd.vpp.jvpp.ioampot.future.FutureJVppIoampotFacade;
+
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(io.fd.vpp.jvpp.ioampot.test.FutureApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi(args);
+    }
+
+    private static void testCallbackApi(String[] args) throws Exception {
+        LOG.info("Testing Java callback API for ioampot plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]);
+             final FutureJVppIoampotFacade jvpp = new FutureJVppIoampotFacade(registry, new JVppIoampotImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testPotProfileShowConfigDump(jvpp);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    private static void testPotProfileShowConfigDump(FutureJVppIoampotFacade jvpp) throws Exception {
+        LOG.info("Sending PotProfileShowConfigDump request...");
+        final PotProfileShowConfigDump request = new PotProfileShowConfigDump();
+
+        final Future<PotProfileShowConfigDetailsReplyDump> replyFuture = jvpp.potProfileShowConfigDump(request).toCompletableFuture();
+        final PotProfileShowConfigDetailsReplyDump reply = replyFuture.get();
+
+        if (reply == null || reply.potProfileShowConfigDetails == null) {
+            throw new IllegalStateException("Received null response for empty dump: " + reply);
+        } else {
+            LOG.info(
+                    String.format(
+                            "Received pot profile show config dump reply: %s",
+                            reply.potProfileShowConfigDetails));
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt
new file mode 100644
index 0000000..f3cae26
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.test.[test-name]
+debug version:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.test.[test-name]
diff --git a/extras/japi/java/jvpp-ioampot/jvpp_ioam_pot.c b/extras/japi/java/jvpp-ioampot/jvpp_ioam_pot.c
new file mode 100644
index 0000000..ce1da69
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/jvpp_ioam_pot.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <ioam/lib-pot/pot_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <ioam/lib-pot/pot_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h"
+#include "jvpp_ioam_pot.h"
+#include "jvpp-ioampot/jvpp_ioampot_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_ioampot_JVppIoampotImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  ioampot_main_t * plugin_main = &ioampot_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+	  if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_close0
+(JNIEnv *env, jclass clazz) {
+  ioampot_main_t * plugin_main = &ioampot_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP iOAM POT */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP iOAM POT */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-ioampot/jvpp_ioam_pot.h b/extras/japi/java/jvpp-ioampot/jvpp_ioam_pot.h
new file mode 100644
index 0000000..51db3da
--- /dev/null
+++ b/extras/japi/java/jvpp-ioampot/jvpp_ioam_pot.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_ioam_pot_h__
+#define __included_jvpp_ioam_pot_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-IOAM-POT */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} ioampot_main_t;
+
+ioampot_main_t ioampot_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_ioam_pot_h__ */
diff --git a/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java
new file mode 100644
index 0000000..d63d137
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java
@@ -0,0 +1,77 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioamtrace.examples;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade;
+import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl;
+import io.fd.vpp.jvpp.ioamtrace.callback.TraceProfileAddReplyCallback;
+import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAdd;
+import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAddReply;
+import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfig;
+import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfigReply;
+
+public class IoamTraceApiExample {
+
+    static class IoamTraceTestCallback implements TraceProfileAddReplyCallback {
+
+        @Override
+        public void onTraceProfileAddReply(final TraceProfileAddReply reply) {
+            System.out.printf("Received TraceProfileAddReply reply: context=%d%n",
+                reply.context);
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+                ex.getCtxId(), ex.getErrorCode());
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        ioamTraceTestApi();
+    }
+
+    private static void ioamTraceTestApi() throws Exception {
+        System.out.println("Testing Java API for ioam trace plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("ioamTraceApiTest");
+             final JVpp jvpp = new JVppIoamtraceImpl()) {
+	    FutureJVppIoamtraceFacade ioamtraceJvpp = new FutureJVppIoamtraceFacade(registry,jvpp);
+
+            System.out.println("Sending ioam trace profile add request...");
+            TraceProfileAdd request = new TraceProfileAdd();
+            request.traceType = 0x1f;
+            request.numElts = 4;
+            request.nodeId = 1;
+            request.traceTsp = 2;
+            request.appData = 1234;
+            final int result = jvpp.send(request);
+            System.out.printf("TraceProfileAdd send result = %d%n", result);
+
+            Thread.sleep(1000);
+
+	    TraceProfileShowConfig showRequest = new TraceProfileShowConfig();
+	    TraceProfileShowConfigReply reply = ioamtraceJvpp.traceProfileShowConfig(showRequest).toCompletableFuture().get();
+            System.out.printf("TraceProfileShowConfig result = "+ reply.toString());
+
+            System.out.println("Disconnecting...");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt
new file mode 100644
index 0000000..e8c3907
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.examples.IoamTraceApiExample
+debug vresion:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.examples.IoamTraceApiExample
diff --git a/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java
new file mode 100644
index 0000000..4a71db5
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp.ioamtrace.test;
+
+import io.fd.vpp.jvpp.AbstractCallbackApiTest;
+import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl;
+
+import java.util.logging.Logger;
+
+public class CallbackApiTest extends AbstractCallbackApiTest {
+
+    private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName());
+
+
+    public static void main(String[] args) throws Exception {
+        LOG.info("Testing ControlPing using Java callback API for ioamtrace plugin");
+        testControlPing(args[0], new JVppIoamtraceImpl());
+    }
+}
diff --git a/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java
new file mode 100644
index 0000000..4e13ed1
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.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 io.fd.vpp.jvpp.ioamtrace.test;
+
+
+import io.fd.vpp.jvpp.Assertions;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl;
+import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfig;
+import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfigReply;
+import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade;
+
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(io.fd.vpp.jvpp.ioamtrace.test.FutureApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi(args);
+    }
+
+    private static void testCallbackApi(String[] args) throws Exception {
+        LOG.info("Testing Java callback API for ioamtrace plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]);
+             final FutureJVppIoamtraceFacade jvpp = new FutureJVppIoamtraceFacade(registry, new JVppIoamtraceImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testTraceProfileShowConfig(jvpp);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    private static void testTraceProfileShowConfig(FutureJVppIoamtraceFacade jvpp) throws Exception {
+        LOG.info("Sending TraceProfileShowConfig request...");
+        final TraceProfileShowConfig request = new TraceProfileShowConfig();
+
+        final Future<TraceProfileShowConfigReply> replyFuture = jvpp.traceProfileShowConfig(request).toCompletableFuture();
+        final TraceProfileShowConfigReply reply = replyFuture.get();
+
+        Assertions.assertNotNull(reply);
+    }
+}
diff --git a/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt
new file mode 100644
index 0000000..9a1ba82
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.test.[test-name]
+debug version:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.test.[test-name]
diff --git a/extras/japi/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/extras/japi/java/jvpp-ioamtrace/jvpp_ioam_trace.c
new file mode 100644
index 0000000..3532d91
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/jvpp_ioam_trace.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <ioam/lib-trace/trace_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <ioam/lib-trace/trace_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h"
+#include "jvpp_ioam_trace.h"
+#include "jvpp-ioamtrace/jvpp_ioamtrace_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  ioamtrace_main_t * plugin_main = &ioamtrace_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+      if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_close0
+(JNIEnv *env, jclass clazz) {
+  ioamtrace_main_t * plugin_main = &ioamtrace_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP iOAM Trace */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP iOAM Trace */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-ioamtrace/jvpp_ioam_trace.h b/extras/japi/java/jvpp-ioamtrace/jvpp_ioam_trace.h
new file mode 100644
index 0000000..752b599
--- /dev/null
+++ b/extras/japi/java/jvpp-ioamtrace/jvpp_ioam_trace.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_ioam_trace_h__
+#define __included_jvpp_ioam_trace_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-IOAM-TRACE */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} ioamtrace_main_t;
+
+ioamtrace_main_t ioamtrace_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_ioam_trace_h__ */
diff --git a/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java
new file mode 100644
index 0000000..e0d93ff
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java
@@ -0,0 +1,68 @@
+/*
+ * 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 io.fd.vpp.jvpp.nat.examples;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.VppCallbackException;
+import io.fd.vpp.jvpp.nat.JVppNatImpl;
+import io.fd.vpp.jvpp.nat.callback.Nat44InterfaceAddDelFeatureReplyCallback;
+import io.fd.vpp.jvpp.nat.dto.Nat44InterfaceAddDelFeature;
+import io.fd.vpp.jvpp.nat.dto.Nat44InterfaceAddDelFeatureReply;
+
+public class CallbackApiExample {
+
+    static class TestCallback implements Nat44InterfaceAddDelFeatureReplyCallback {
+
+        @Override
+        public void onNat44InterfaceAddDelFeatureReply(final Nat44InterfaceAddDelFeatureReply msg) {
+            System.out.printf("Received Nat44InterfaceAddDelFeatureReply: context=%d%n",
+                msg.context);
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+                ex.getCtxId(), ex.getErrorCode());
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi();
+    }
+
+    private static void testCallbackApi() throws Exception {
+        System.out.println("Testing Java callback API for nat plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("NatCallbackApiTest");
+             final JVpp jvpp = new JVppNatImpl()) {
+            registry.register(jvpp, new TestCallback());
+
+            System.out.println("Sending Nat44InterfaceAddDelFeature request...");
+            Nat44InterfaceAddDelFeature request = new Nat44InterfaceAddDelFeature();
+            request.isAdd = 1;
+            request.isInside = 1;
+            request.swIfIndex = 1;
+            final int result = jvpp.send(request);
+            System.out.printf("Nat44InterfaceAddDelFeature send result = %d%n", result);
+
+            Thread.sleep(1000);
+
+            System.out.println("Disconnecting...");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt
new file mode 100644
index 0000000..ac75e04
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt
@@ -0,0 +1 @@
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-nat-17.10.jar io.fd.vpp.jvpp.nat.examples.CallbackApiExample
diff --git a/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java
new file mode 100644
index 0000000..a6f8214
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp.nat.test;
+
+import io.fd.vpp.jvpp.AbstractCallbackApiTest;
+import io.fd.vpp.jvpp.nat.JVppNatImpl;
+
+import java.util.logging.Logger;
+
+public class CallbackApiTest extends AbstractCallbackApiTest {
+
+    private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName());
+
+
+    public static void main(String[] args) throws Exception {
+        LOG.info("Testing ControlPing using Java callback API for core plugin");
+        testControlPing(args[0], new JVppNatImpl());
+    }
+}
diff --git a/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java
new file mode 100644
index 0000000..26d6b98
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 io.fd.vpp.jvpp.nat.test;
+
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.nat.JVppNatImpl;
+import io.fd.vpp.jvpp.nat.dto.Nat44AddressDetailsReplyDump;
+import io.fd.vpp.jvpp.nat.dto.Nat44AddressDump;
+import io.fd.vpp.jvpp.nat.future.FutureJVppNatFacade;
+
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(io.fd.vpp.jvpp.nat.test.FutureApiTest.class.getName());
+
+    public static void main(String[] args) throws Exception {
+        testCallbackApi(args);
+    }
+
+    private static void testCallbackApi(String[] args) throws Exception {
+        LOG.info("Testing Java callback API for nat plugin");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]);
+             final FutureJVppNatFacade jvpp = new FutureJVppNatFacade(registry, new JVppNatImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testAclDump(jvpp);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    private static void testAclDump(FutureJVppNatFacade jvpp) throws Exception {
+        LOG.info("Sending Nat44AddressDump request...");
+        final Nat44AddressDump request = new Nat44AddressDump();
+
+        final Future<Nat44AddressDetailsReplyDump> replyFuture = jvpp.nat44AddressDump(request).toCompletableFuture();
+        final Nat44AddressDetailsReplyDump reply = replyFuture.get();
+
+        if (reply == null || reply.nat44AddressDetails == null) {
+            throw new IllegalStateException("Received null response for empty dump: " + reply);
+        } else {
+            LOG.info(
+                    String.format(
+                            "Received nat address dump reply with list of nat address: %s",
+                            reply.nat44AddressDetails));
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt
new file mode 100644
index 0000000..6f75808
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt
@@ -0,0 +1,4 @@
+release version:
+sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-nat-17.10.jar io.fd.vpp.jvpp.nat.test.[test-name]
+debug version:
+sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-nat-17.10.jar io.fd.vpp.jvpp.nat.test.[test-name]
diff --git a/extras/japi/java/jvpp-nat/jvpp_nat.c b/extras/japi/java/jvpp-nat/jvpp_nat.c
new file mode 100644
index 0000000..bdae1e6
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/jvpp_nat.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <nat/nat_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <nat/nat_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-nat/io_fd_vpp_jvpp_nat_JVppNatImpl.h"
+#include "jvpp_nat.h"
+#include "jvpp-nat/jvpp_nat_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_nat_JVppNatImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_nat_JVppNatImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  nat_main_t * plugin_main = &nat_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_nat_JVppNatImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+      if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_nat_JVppNatImpl_close0
+(JNIEnv *env, jclass clazz) {
+  nat_main_t * plugin_main = &nat_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP SNAT */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP SNAT */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-nat/jvpp_nat.h b/extras/japi/java/jvpp-nat/jvpp_nat.h
new file mode 100644
index 0000000..9bff974
--- /dev/null
+++ b/extras/japi/java/jvpp-nat/jvpp_nat.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_nat_h__
+#define __included_jvpp_nat_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-NAT */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} nat_main_t;
+
+nat_main_t nat_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_nat_h__ */
diff --git a/extras/japi/java/jvpp-pppoe/jvpp_pppoe.c b/extras/japi/java/jvpp-pppoe/jvpp_pppoe.c
new file mode 100644
index 0000000..02bd20e
--- /dev/null
+++ b/extras/japi/java/jvpp-pppoe/jvpp_pppoe.c
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <pppoe/pppoe_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <pppoe/pppoe_all_api_h.h>
+#undef vl_typedefs
+
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+
+#if VPPJNI_DEBUG == 1
+  #define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
+#else
+  #define DEBUG_LOG(...)
+#endif
+
+#include <jvpp-common/jvpp_common.h>
+
+#include "jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h"
+#include "jvpp_pppoe.h"
+#include "jvpp-pppoe/jvpp_pppoe_gen.h"
+
+/*
+ * Class:     io_fd_vpp_jvpp_pppoe_JVpppppoeImpl
+ * Method:    init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_pppoe_JVppPppoeImpl_init0
+  (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+  pppoe_main_t * plugin_main = &pppoe_main;
+  clib_warning ("Java_io_fd_vpp_jvpp_pppoe_JVppPppoeImpl_init0");
+
+  plugin_main->my_client_index = my_client_index;
+  plugin_main->vl_input_queue = (svm_queue_t *)queue_address;
+
+  plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+  plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
+
+  // verify API has not changed since jar generation
+  #define _(N)             \
+      if (get_message_id(env, #N) == 0) return;
+      foreach_supported_api_message;
+  #undef _
+
+  #define _(N,n)                                  \
+      vl_msg_api_set_handlers(get_message_id(env, #N), #n,     \
+              vl_api_##n##_t_handler,             \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              vl_noop_handler,                    \
+              sizeof(vl_api_##n##_t), 1);
+      foreach_api_reply_handler;
+  #undef _
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_pppoe_JVppPppoeImpl_close0
+(JNIEnv *env, jclass clazz) {
+  pppoe_main_t * plugin_main = &pppoe_main;
+
+    // cleanup:
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+    (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
+
+    plugin_main->callbackClass = NULL;
+    plugin_main->callbackObject = NULL;
+}
+
+/* Attach thread to JVM and cache class references when initiating JVPP ACL */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    if (cache_class_references(env) != 0) {
+        clib_warning ("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_8;
+}
+
+/* Clean up cached references when disposing JVPP ACL */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+    delete_class_references(env);
+}
diff --git a/extras/japi/java/jvpp-pppoe/jvpp_pppoe.h b/extras/japi/java/jvpp-pppoe/jvpp_pppoe.h
new file mode 100644
index 0000000..7606a7e
--- /dev/null
+++ b/extras/japi/java/jvpp-pppoe/jvpp_pppoe.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+#ifndef __included_jvpp_pppoe_h__
+#define __included_jvpp_pppoe_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-pppoe */
+typedef struct {
+    /* Pointer to shared memory queue */
+    svm_queue_t * vl_input_queue;
+
+    /* VPP api client index */
+    u32 my_client_index;
+
+    /* Callback object and class references enabling asynchronous Java calls */
+    jobject callbackObject;
+    jclass callbackClass;
+
+} pppoe_main_t;
+
+pppoe_main_t pppoe_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_pppoe_h__ */
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java
new file mode 100644
index 0000000..d221d1e
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 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 io.fd.vpp.jvpp;
+
+import io.fd.vpp.jvpp.callback.ControlPingCallback;
+import io.fd.vpp.jvpp.dto.ControlPing;
+import io.fd.vpp.jvpp.dto.ControlPingReply;
+
+public abstract class AbstractCallbackApiTest {
+
+    private static int receivedPingCount = 0;
+    private static int errorPingCount = 0;
+
+    public static void testControlPing(String shm_prefix, JVpp jvpp) throws Exception {
+        try (JVppRegistry registry = new JVppRegistryImpl("CallbackApiTest", shm_prefix)) {
+
+            registry.register(jvpp, new ControlPingCallback() {
+                @Override
+                public void onControlPingReply(final ControlPingReply reply) {
+                    System.out.printf("Received ControlPingReply: %s%n", reply);
+                    receivedPingCount++;
+                }
+
+                @Override
+                public void onError(VppCallbackException ex) {
+                    System.out.printf("Received onError exception: call=%s, reply=%d, context=%d ", ex.getMethodName(),
+                            ex.getErrorCode(), ex.getCtxId());
+                    errorPingCount++;
+                }
+
+            });
+            System.out.println("Successfully connected to VPP");
+            Thread.sleep(1000);
+
+            System.out.println("Sending control ping using JVppRegistry");
+            registry.controlPing(jvpp.getClass());
+
+            Thread.sleep(2000);
+
+            System.out.println("Sending control ping using JVpp plugin");
+            jvpp.send(new ControlPing());
+
+            Thread.sleep(2000);
+            System.out.println("Disconnecting...");
+            Assertions.assertEquals(2, receivedPingCount);
+            Assertions.assertEquals(0, errorPingCount);
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java
new file mode 100644
index 0000000..f8b591f
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017 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 io.fd.vpp.jvpp;
+
+public class Assertions {
+
+    public static void assertEquals(final int expected, final int actual) {
+        if (expected != actual) {
+            throw new IllegalArgumentException(String.format("Expected[%s]/Actual[%s]", expected, actual));
+        }
+    }
+
+    public static void assertNotNull(final Object value) {
+        if (value == null) {
+            throw new IllegalArgumentException("Variable is null");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java
new file mode 100644
index 0000000..55f25a7
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java
@@ -0,0 +1,56 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+import io.fd.vpp.jvpp.callback.JVppCallback;
+import io.fd.vpp.jvpp.dto.ControlPing;
+import io.fd.vpp.jvpp.dto.JVppRequest;
+
+/**
+ * Base interface for plugin's Java API.
+ */
+public interface JVpp extends AutoCloseable {
+
+    /**
+     * Sends request to vpp.
+     *
+     * @param request request to be sent
+     * @return unique identifer of message in message queue
+     * @throws VppInvocationException when message could not be sent
+     */
+    int send(final JVppRequest request) throws VppInvocationException;
+
+    /**
+     * Initializes plugin's Java API.
+     *
+     * @param registry     plugin registry
+     * @param callback     called by vpe.api message handlers
+     * @param queueAddress address of vpp shared memory queue
+     * @param clientIndex  vpp client identifier
+     */
+    void init(final JVppRegistry registry, final JVppCallback callback, final long queueAddress,
+              final int clientIndex);
+
+    /**
+     * Sends control_ping message.
+     *
+     * @param controlPing request DTO
+     * @return unique identifer of message in message queue
+     * @throws VppInvocationException when message could not be sent
+     */
+    int controlPing(final ControlPing controlPing) throws VppInvocationException;
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java
new file mode 100644
index 0000000..6535db0
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java
@@ -0,0 +1,76 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+import io.fd.vpp.jvpp.callback.JVppCallback;
+
+/**
+ * Manages VPP connection and stores plugin callbacks.
+ */
+public interface JVppRegistry extends AutoCloseable {
+
+    /**
+     * Vpp connection managed by the registry.
+     *
+     * @return representation of vpp connection
+     */
+    VppConnection getConnection();
+
+    /**
+     * Registers callback and initializes Java API for given plugin.
+     *
+     * @param jvpp     plugin name
+     * @param callback callback provided by the plugin
+     * @throws NullPointerException     if name or callback is null
+     * @throws IllegalArgumentException if plugin was already registered
+     */
+    void register(final JVpp jvpp, final JVppCallback callback);
+
+    /**
+     * Unregisters callback for the given plugin.
+     *
+     * @param name plugin name
+     * @throws NullPointerException     if name is null
+     * @throws IllegalArgumentException if plugin was not registered
+     */
+    void unregister(final String name);
+
+    /**
+     * Returns callback registered for the plugin.
+     *
+     * @param name plugin name
+     * @return callback provided by the plugin
+     * @throws NullPointerException     if name is null
+     * @throws IllegalArgumentException if plugin was not registered
+     */
+    JVppCallback get(final String name);
+
+    /**
+     * Sends control ping. Reply handler calls callback registered for give plugin.
+     *
+     * Control ping is used for initial RX thread to Java thread attachment
+     * that takes place in the plugin's JNI lib
+     * and to wrap dump message replies in one list.
+     *
+     * VPP plugins don't have to provide special control ping, therefore
+     * it is necessary to providing control ping support in JVppRegistry.
+
+     * @param clazz identifies plugin that should receive ping callback
+     * @return unique identifier of message in message queue
+     */
+    int controlPing(final Class<? extends JVpp> clazz) throws VppInvocationException;
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java
new file mode 100644
index 0000000..baef14c
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java
@@ -0,0 +1,187 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+import static java.util.Objects.requireNonNull;
+
+import io.fd.vpp.jvpp.callback.ControlPingCallback;
+import io.fd.vpp.jvpp.callback.JVppCallback;
+import io.fd.vpp.jvpp.dto.ControlPingReply;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Default implementation of JVppRegistry.
+ */
+public final class JVppRegistryImpl implements JVppRegistry, ControlPingCallback {
+
+    private static final Logger LOG = Logger.getLogger(JVppRegistryImpl.class.getName());
+
+    private final VppJNIConnection connection;
+    // Unguarded concurrent map, no race conditions expected on top of that
+    private final Map<String, JVppCallback> pluginRegistry;
+    // Guarded by self
+    private final Map<Integer, ControlPingCallback> pingCalls;
+
+    public JVppRegistryImpl(final String clientName) throws IOException {
+        connection = new VppJNIConnection(clientName);
+        connection.connect();
+        pluginRegistry = new ConcurrentHashMap<>();
+        pingCalls = new HashMap<>();
+    }
+
+    public JVppRegistryImpl(final String clientName, final String shmPrefix) throws IOException {
+        connection = new VppJNIConnection(clientName, shmPrefix);
+        connection.connect();
+        pluginRegistry = new ConcurrentHashMap<>();
+        pingCalls = new HashMap<>();
+    }
+
+    @Override
+    public VppConnection getConnection() {
+        return connection;
+    }
+
+    @Override
+    public void register(final JVpp jvpp, final JVppCallback callback) {
+        requireNonNull(jvpp, "jvpp should not be null");
+        requireNonNull(callback, "Callback should not be null");
+        final String name = jvpp.getClass().getName();
+        if (pluginRegistry.containsKey(name)) {
+            throw new IllegalArgumentException(
+                String.format("Callback for plugin %s was already registered", name));
+        }
+        jvpp.init(this, callback, connection.getConnectionInfo().queueAddress,
+            connection.getConnectionInfo().clientIndex);
+        pluginRegistry.put(name, callback);
+    }
+
+    @Override
+    public void unregister(final String name) {
+        requireNonNull(name, "Plugin name should not be null");
+        final JVppCallback previous = pluginRegistry.remove(name);
+        assertPluginWasRegistered(name, previous);
+    }
+
+    @Override
+    public JVppCallback get(final String name) {
+        requireNonNull(name, "Plugin name should not be null");
+        JVppCallback value = pluginRegistry.get(name);
+        assertPluginWasRegistered(name, value);
+        return value;
+    }
+
+    private native int controlPing0() throws VppInvocationException;
+
+    @Override
+    public int controlPing(final Class<? extends JVpp> clazz) throws VppInvocationException {
+        connection.checkActive();
+        final String name = clazz.getName();
+
+        final ControlPingCallback callback = (ControlPingCallback) pluginRegistry.get(clazz.getName());
+        assertPluginWasRegistered(name, callback);
+
+        // controlPing0 is sending function and can go to waiting in case of e. g. full queue
+        // because of that it cant be in same synchronization block as used by reply handler function
+        int context = controlPing0();
+        if (context < 0) {
+            throw new VppInvocationException("controlPing", context);
+        }
+
+        synchronized (pingCalls) {
+            // if callback is in map it's because reply was already received
+            EarlyControlPingReply earlyReplyCallback = (EarlyControlPingReply) pingCalls.remove(context);
+            if(earlyReplyCallback == null) {
+                pingCalls.put(context, callback);
+            } else {
+                callback.onControlPingReply(earlyReplyCallback.getReply());
+            }
+        }
+
+        return context;
+    }
+
+    @Override
+    public void onControlPingReply(final ControlPingReply reply) {
+        final ControlPingCallback callback;
+        synchronized (pingCalls) {
+            callback = pingCalls.remove(reply.context);
+            if (callback == null) {
+                // reply received early, because we don't know callback to call
+                // we wrap the reply and let the sender to call it
+                pingCalls.put(reply.context, new EarlyControlPingReply(reply));
+                return;
+            }
+        }
+        // pass the reply to the callback registered by the ping caller
+        callback.onControlPingReply(reply);
+    }
+
+    @Override
+    public void onError(final VppCallbackException ex) {
+        final int ctxId = ex.getCtxId();
+        final ControlPingCallback callback;
+
+        synchronized (pingCalls) {
+            callback = pingCalls.get(ctxId);
+        }
+        if (callback == null) {
+            LOG.log(Level.WARNING, "No callback was registered for reply id={0} ", ctxId);
+            return;
+        }
+        // pass the error to the callback registered by the ping caller
+        callback.onError(ex);
+    }
+
+    private static void assertPluginWasRegistered(final String name, final JVppCallback value) {
+        if (value == null) {
+            throw new IllegalArgumentException(String.format("Callback for plugin %s is not registered", name));
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        connection.close();
+    }
+
+    private static class EarlyControlPingReply implements ControlPingCallback {
+
+        private final ControlPingReply reply;
+
+        public EarlyControlPingReply(final ControlPingReply reply) {
+            this.reply = reply;
+        }
+
+        public ControlPingReply getReply() {
+            return reply;
+        }
+
+        @Override
+        public void onError(VppCallbackException ex) {
+            throw new IllegalStateException("Calling onError in EarlyControlPingReply");
+        }
+
+        @Override
+        public void onControlPingReply(ControlPingReply reply) {
+            throw new IllegalStateException("Calling onControlPingReply in EarlyControlPingReply");
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java
new file mode 100644
index 0000000..ce6d1bf
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java
@@ -0,0 +1,73 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Utility class for loading JNI libraries.
+ */
+public final class NativeLibraryLoader {
+
+    private static final Logger LOG = Logger.getLogger(NativeLibraryLoader.class.getName());
+
+    private NativeLibraryLoader() {
+        throw new UnsupportedOperationException("This utility class cannot be instantiated.");
+    }
+
+    /**
+     * Loads JNI library using class loader of the given class.
+     *
+     * @param libName name of the library to be loaded
+     */
+    public static void loadLibrary(final String libName, final Class clazz) throws IOException {
+        java.util.Objects.requireNonNull(libName, "libName should not be null");
+        java.util.Objects.requireNonNull(clazz, "clazz should not be null");
+        try (final InputStream is = clazz.getResourceAsStream('/' + libName)) {
+            if (is == null) {
+                throw new IOException("Failed to open library resource " + libName);
+            }
+            loadStream(libName, is);
+        }
+    }
+
+    private static void loadStream(final String libName, final InputStream is) throws IOException {
+        final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
+        final Path p = Files.createTempFile(libName, null, PosixFilePermissions.asFileAttribute(perms));
+        try {
+            Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
+            Runtime.getRuntime().load(p.toString());
+        } catch (Exception e) {
+            throw new IOException("Failed to load library " + p, e);
+        } finally {
+            try {
+                Files.deleteIfExists(p);
+            } catch (IOException e) {
+                LOG.log(Level.WARNING, String.format("Failed to delete temporary file %s.", p), e);
+            }
+        }
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java
new file mode 100644
index 0000000..7fc1682
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java
@@ -0,0 +1,79 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+
+/**
+ * Base exception representing failed operation of JVpp request call
+ */
+public abstract class VppBaseCallException extends Exception {
+    private final String methodName;
+    private final int errorCode;
+
+    /**
+     * Constructs an VppCallbackException with the specified api method name and error code.
+     *
+     * @param methodName name of a method, which invocation or execution failed
+     * @param errorCode  negative error code value associated with this failure
+     * @throws NullPointerException     if apiMethodName is null
+     */
+    public VppBaseCallException(final String methodName, final int errorCode) {
+        super(String.format("vppApi.%s failed with error code: %d", methodName, errorCode));
+        this.methodName = java.util.Objects.requireNonNull(methodName, "apiMethodName is null!");
+        this.errorCode = errorCode;
+        if(errorCode >= 0) {
+            throw new IllegalArgumentException("Error code must be < 0. Was " + errorCode +
+                    " for " + methodName );
+        }
+    }
+
+    /**
+     * Constructs an VppCallbackException with the specified api method name, error description and error code.
+     *
+     * @param methodName name of a method, which invocation or execution failed
+     * @param message    description of error reason
+     * @param errorCode  negative error code value associated with this failure
+     * @throws NullPointerException     if apiMethodName is null
+     */
+    public VppBaseCallException(final String methodName, final String message, final int errorCode) {
+        super(String.format("vppApi.%s failed: %s (error code: %d)", methodName,message, errorCode));
+        this.methodName = java.util.Objects.requireNonNull(methodName, "apiMethodName is null!");
+        this.errorCode = errorCode;
+        if(errorCode >= 0) {
+            throw new IllegalArgumentException("Error code must be < 0. Was " + errorCode +
+                    " for " + methodName );
+        }
+    }
+
+    /**
+     * Returns  name of a method, which invocation failed.
+     *
+     * @return method name
+     */
+    public String getMethodName() {
+        return methodName;
+    }
+
+    /**
+     * Returns the error code associated with this failure.
+     *
+     * @return a negative integer error code
+     */
+    public int getErrorCode() {
+        return errorCode;
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java
new file mode 100644
index 0000000..adcc5d2
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java
@@ -0,0 +1,48 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+/**
+ * Callback Exception representing failed operation of JVpp request call
+ */
+public class VppCallbackException extends VppBaseCallException {
+    private final int ctxId;
+
+    /**
+     * Constructs an VppCallbackException with the specified api method name and error code.
+     *
+     * @param methodName  name of a method, which invocation failed.
+     * @param message    description of error reason
+     * @param ctxId      api request context identifier
+     * @param errorCode  negative error code value associated with this failure
+     * @throws NullPointerException     if apiMethodName is null
+     */
+    public VppCallbackException(final String methodName, final String message, final int ctxId, final int errorCode ){
+        super(methodName, message, errorCode);
+        this.ctxId = ctxId;
+    }
+
+    /**
+     * Returns api request context identifier.
+     *
+     * @return value of context identifier
+     */
+    public int getCtxId() {
+        return ctxId;
+    }
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java
new file mode 100644
index 0000000..e6fd3bd
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java
@@ -0,0 +1,45 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+import java.io.IOException;
+
+/**
+ * Representation of a management connection to VPP.
+ */
+public interface VppConnection extends AutoCloseable {
+
+    /**
+     * Opens VppConnection for communication with VPP.
+     *
+     * @throws IOException if connection is not established
+     */
+    void connect() throws IOException;
+
+    /**
+     * Checks if this instance connection is active.
+     *
+     * @throws IllegalStateException if this instance was disconnected.
+     */
+    void checkActive();
+
+    /**
+     * Closes Vpp connection.
+     */
+    @Override
+    void close();
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java
new file mode 100644
index 0000000..a7ccb19
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java
@@ -0,0 +1,33 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+/**
+ * Exception thrown when Vpp jAPI method invocation failed.
+ */
+public class VppInvocationException extends VppBaseCallException {
+    /**
+     * Constructs an VppApiInvocationFailedException with the specified api method name and error code.
+     *
+     * @param methodName  name of a method, which invocation failed.
+     * @param errorCode  negative error code value associated with this failure
+     * @throws NullPointerException     if apiMethodName is null
+     */
+    public VppInvocationException(final String methodName, final int errorCode) {
+        super(methodName, errorCode);
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java
new file mode 100644
index 0000000..6a414f3
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java
@@ -0,0 +1,152 @@
+/*
+ * 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 io.fd.vpp.jvpp;
+
+import static io.fd.vpp.jvpp.NativeLibraryLoader.loadLibrary;
+import static java.lang.String.format;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * JNI based representation of a management connection to VPP.
+ */
+public final class VppJNIConnection implements VppConnection {
+    private static final Logger LOG = Logger.getLogger(VppJNIConnection.class.getName());
+    private static final String DEFAULT_SHM_PREFIX = "/vpe-api";
+
+    static {
+        final String libName = "libjvpp_registry.so";
+        try {
+            loadLibrary(libName, VppJNIConnection.class);
+        } catch (IOException e) {
+            LOG.log(Level.SEVERE, format("Can't find vpp jni library: %s", libName), e);
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private ConnectionInfo connectionInfo;
+
+    private final String clientName;
+    private final String shmPrefix;
+    private volatile boolean disconnected = false;
+
+    /**
+     * Create VPPJNIConnection instance for client connecting to VPP.
+     *
+     * @param clientName client name instance to be used for communication. Single connection per clientName is
+     *                   allowed.
+     */
+    public VppJNIConnection(final String clientName) {
+        this.clientName = Objects.requireNonNull(clientName, "Null clientName");
+        this.shmPrefix = DEFAULT_SHM_PREFIX;
+    }
+
+    public VppJNIConnection(final String clientName, final String shmPrefix) {
+        this.clientName = Objects.requireNonNull(clientName, "Null clientName");
+        this.shmPrefix = Objects.requireNonNull(shmPrefix, "Null shmPrefix");
+    }
+
+    /**
+     * Guarded by VppJNIConnection.class
+     */
+    private static final Map<String, VppJNIConnection> connections = new HashMap<>();
+
+    /**
+     * Initiate VPP connection for current instance
+     *
+     * Multiple instances are allowed since this class is not a singleton (VPP allows multiple management connections).
+     *
+     * However only a single connection per clientName is allowed.
+     *
+     * @throws IOException in case the connection could not be established
+     */
+
+    @Override
+    public void connect() throws IOException {
+        _connect(shmPrefix);
+    }
+
+    private void _connect(final String shmPrefix) throws IOException {
+        Objects.requireNonNull(shmPrefix, "Shared memory prefix must be defined");
+
+        synchronized (VppJNIConnection.class) {
+            if (connections.containsKey(clientName)) {
+                throw new IOException("Client " + clientName + " already connected");
+            }
+
+            connectionInfo = clientConnect(shmPrefix, clientName);
+            if (connectionInfo.status != 0) {
+                throw new IOException("Connection returned error " + connectionInfo.status);
+            }
+            connections.put(clientName, this);
+        }
+    }
+
+    @Override
+    public final void checkActive() {
+        if (disconnected) {
+            throw new IllegalStateException("Disconnected client " + clientName);
+        }
+    }
+
+    @Override
+    public final synchronized void close() {
+        if (!disconnected) {
+            disconnected = true;
+            try {
+                clientDisconnect();
+            } finally {
+                synchronized (VppJNIConnection.class) {
+                    connections.remove(clientName);
+                }
+            }
+        }
+    }
+
+    public ConnectionInfo getConnectionInfo() {
+        return connectionInfo;
+    }
+
+    /**
+     * VPP connection information used by plugins to reuse the connection.
+     */
+    public static final class ConnectionInfo {
+        public final long queueAddress;
+        public final int clientIndex;
+        public final int status; // FIXME throw exception instead
+        public final int pid;
+
+        public ConnectionInfo(long queueAddress, int clientIndex, int status, int pid) {
+            this.queueAddress = queueAddress;
+            this.clientIndex = clientIndex;
+            this.status = status;
+            this.pid = pid;
+        }
+    }
+
+    private static native ConnectionInfo clientConnect(String shmPrefix, String clientName);
+
+    private static native void clientDisconnect();
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java
new file mode 100644
index 0000000..efddfdb
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java
@@ -0,0 +1,29 @@
+/*
+ * 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 io.fd.vpp.jvpp.callback;
+
+import io.fd.vpp.jvpp.dto.ControlPingReply;
+
+/**
+ * Represents callback for control_ping message.
+ */
+public interface ControlPingCallback extends JVppCallback {
+
+    void onControlPingReply(ControlPingReply reply);
+
+}
+
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java
new file mode 100644
index 0000000..ae02063
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java
@@ -0,0 +1,29 @@
+/*
+ * 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 io.fd.vpp.jvpp.callback;
+import io.fd.vpp.jvpp.VppCallbackException;
+
+/**
+ * Base JVppCallback interface
+ */
+public interface JVppCallback {
+    /**
+     * onError callback handler used to report failing operation
+     * @param ex VppCallbackException object containing details about failing operation
+     */
+    void onError(VppCallbackException ex);
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java
new file mode 100644
index 0000000..8ab0cb2
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/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 io.fd.vpp.jvpp.callback;
+
+/**
+* Notification callback
+*/
+public interface JVppNotificationCallback {
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java
new file mode 100644
index 0000000..1e780bb
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2017 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 io.fd.vpp.jvpp.coverity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Used to suppress FindBugs warnings found by Coverity. <br>
+ * We don't want extra dependency, so we define our own annotation version.
+ *
+ * @see <a href="https://sourceforge.net/p/findbugs/feature-requests/298/#5e88"/>Findbugs sourceforge</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+public @interface SuppressFBWarnings {
+    /**
+     * The set of FindBugs warnings that are to be suppressed in annotated element. The value can be a bug category,
+     * kind or pattern.
+     */
+    String[] value() default {};
+
+    /**
+     * Optional documentation of the reason why the warning is suppressed
+     */
+    String justification() default "";
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java
new file mode 100644
index 0000000..984e167
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java
@@ -0,0 +1,34 @@
+/*
+ * 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 io.fd.vpp.jvpp.dto;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.VppInvocationException;
+
+/**
+ * Represents request DTO for control_ping message.
+ */
+public final class ControlPing implements JVppRequest {
+
+    @Override
+    public int send(final JVpp jvpp) throws VppInvocationException {
+        return jvpp.controlPing(this);
+    }
+
+}
+
+
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java
new file mode 100644
index 0000000..61e4d0e
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java
@@ -0,0 +1,58 @@
+/*
+ * 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 io.fd.vpp.jvpp.dto;
+
+import java.util.Objects;
+
+/**
+ * Represents reply DTO for control_ping message.
+ */
+public final class ControlPingReply implements JVppReply<ControlPing> {
+
+    public int context;
+    public int clientIndex;
+    public int vpePid;
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final ControlPingReply that = (ControlPingReply) o;
+        return context == that.context &&
+                clientIndex == that.clientIndex &&
+                vpePid == that.vpePid;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(context, clientIndex, vpePid);
+    }
+
+    @Override
+    public String toString() {
+        return "ControlPingReply{" +
+                "context=" + context +
+                ", clientIndex=" + clientIndex +
+                ", vpePid=" + vpePid +
+                '}';
+    }
+}
+
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java
new file mode 100644
index 0000000..60b9898
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.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 io.fd.vpp.jvpp.dto;
+
+/**
+* Base interface for all dump requests
+*/
+public interface JVppDump extends JVppRequest {
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java
new file mode 100644
index 0000000..73f512d
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.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 io.fd.vpp.jvpp.dto;
+
+/**
+* Base interface for all reply DTOs
+*/
+public interface JVppReply<REQ extends JVppRequest> {
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java
new file mode 100644
index 0000000..1511139
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java
@@ -0,0 +1,25 @@
+/*
+ * 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 io.fd.vpp.jvpp.dto;
+
+/**
+* Base interface for all dump replies
+*/
+public interface JVppReplyDump<REQ extends JVppRequest, RESP extends JVppReply<REQ>>
+    extends JVppReply<REQ> {
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java
new file mode 100644
index 0000000..9b301da
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java
@@ -0,0 +1,34 @@
+/*
+ * 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 io.fd.vpp.jvpp.dto;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.VppInvocationException;
+
+/**
+* Base interface for all request DTOs
+*/
+public interface JVppRequest {
+
+    /**
+     * Invoke current operation asynchronously on VPP
+     *
+     * @return context id of this request. Can be used to track incoming response
+     */
+    int send(JVpp jvpp) throws VppInvocationException;
+
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java
new file mode 100644
index 0000000..ac85f53
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java
@@ -0,0 +1,180 @@
+/*
+ * 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 io.fd.vpp.jvpp.future;
+
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+import io.fd.vpp.jvpp.JVpp;
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.VppInvocationException;
+import io.fd.vpp.jvpp.dto.JVppDump;
+import io.fd.vpp.jvpp.dto.JVppReply;
+import io.fd.vpp.jvpp.dto.JVppReplyDump;
+import io.fd.vpp.jvpp.dto.JVppRequest;
+
+/**
+ * Future facade on top of JVpp
+ */
+public abstract class AbstractFutureJVppInvoker implements FutureJVppInvoker {
+
+    private final JVpp jvpp;
+    private final JVppRegistry registry;
+
+    /**
+     * Guarded by self
+     */
+    private final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requests;
+
+    protected AbstractFutureJVppInvoker(final JVpp jvpp, final JVppRegistry registry,
+                                        final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requestMap) {
+        this.jvpp =  Objects.requireNonNull(jvpp, "jvpp should not be null");
+        this.registry =  Objects.requireNonNull(registry, "registry should not be null");
+        // Request map represents the shared state between this facade and it's callback
+        // where facade puts futures in and callback completes + removes them
+        this.requests = Objects.requireNonNull(requestMap, "Null requestMap");
+    }
+
+    protected final Map<Integer, CompletableFuture<? extends JVppReply<?>>> getRequests() {
+        synchronized (requests) {
+            return requests;
+        }
+    }
+
+    // TODO use Optional in Future, java8
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <REQ extends JVppRequest, REPLY extends JVppReply<REQ>> CompletionStage<REPLY> send(REQ req) {
+        try {
+            // jvpp.send() can go to waiting state if sending queue is full, putting it into same
+            // synchronization block as used by receiving part (synchronized(requests)) can lead
+            // to deadlock between these two sides or at least slowing sending process by slow
+            // reader
+            final CompletableFuture<REPLY> replyCompletableFuture;
+            final int contextId = jvpp.send(req);
+
+            if(req instanceof JVppDump) {
+                throw new IllegalArgumentException("Send with empty reply dump has to be used in case of dump calls");
+            }
+
+            synchronized(requests) {
+                CompletableFuture<? extends JVppReply<?>> replyFuture = requests.get(contextId);
+                if (replyFuture == null) {
+                    // reply not yet received, put new future into map
+                    replyCompletableFuture = new CompletableFuture<>();
+                    requests.put(contextId, replyCompletableFuture);
+                } else {
+                    // reply already received (should be completed by reader),
+                    // remove future from map and return it to caller
+                    replyCompletableFuture = (CompletableFuture<REPLY>) replyFuture;
+                    requests.remove(contextId);
+                }
+            }
+
+            // TODO in case of timeouts/missing replies, requests from the map are not removed
+            // consider adding cancel method, that would remove requests from the map and cancel
+            // associated replyCompletableFuture
+
+            return replyCompletableFuture;
+        } catch (VppInvocationException ex) {
+            final CompletableFuture<REPLY> replyCompletableFuture = new CompletableFuture<>();
+            replyCompletableFuture.completeExceptionally(ex);
+            return replyCompletableFuture;
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <REQ extends JVppRequest, REPLY extends JVppReply<REQ>, DUMP extends JVppReplyDump<REQ, REPLY>> CompletionStage<DUMP> send(
+            REQ req, DUMP emptyReplyDump) {
+      try {
+          // jvpp.send() and registry.controlPing() can go to waiting state if sending queue is full,
+          // putting it into same synchronization block as used by receiving part (synchronized(requests))
+          // can lead to deadlock between these two sides or at least slowing sending process by slow reader
+          final CompletableDumpFuture<DUMP> replyDumpFuture;
+          final int contextId = jvpp.send(req);
+
+          if(!(req instanceof JVppDump)) {
+              throw new IllegalArgumentException("Send without empty reply dump has to be used in case of regular calls");
+          }
+
+          synchronized(requests) {
+              CompletableFuture<? extends JVppReply<?>> replyFuture = requests.get(contextId);
+              if (replyFuture == null) {
+                  // reply not received yet, put new future to map
+                  replyDumpFuture = new CompletableDumpFuture<>(contextId, emptyReplyDump);
+                  requests.put(contextId, replyDumpFuture);
+              } else {
+                  // reply already received, save existing future
+                  replyDumpFuture = (CompletableDumpFuture<DUMP>) replyFuture;
+              }
+          }
+
+          final int pingId = registry.controlPing(jvpp.getClass());
+
+          synchronized(requests) {
+              if (requests.remove(pingId) == null) {
+                  // reply not received yet, put future into map under pingId
+                  requests.put(pingId, replyDumpFuture);
+              } else {
+                  // reply already received, complete future
+                  // ping reply couldn't complete the future because it is not in map under
+                  // ping id
+                  replyDumpFuture.complete(replyDumpFuture.getReplyDump());
+                  requests.remove(contextId);
+              }
+          }
+
+          // TODO in case of timeouts/missing replies, requests from the map are not removed
+          // consider adding cancel method, that would remove requests from the map and cancel
+          // associated replyCompletableFuture
+
+          return replyDumpFuture;
+      } catch (VppInvocationException ex) {
+          final CompletableFuture<DUMP> replyCompletableFuture = new CompletableFuture<>();
+          replyCompletableFuture.completeExceptionally(ex);
+          return replyCompletableFuture;
+      }
+    }
+
+    public static final class CompletableDumpFuture<T extends JVppReplyDump<?, ?>> extends CompletableFuture<T> {
+        private final T replyDump;
+        private final int contextId;
+
+        public CompletableDumpFuture(final int contextId, final T emptyDump) {
+            this.contextId = contextId;
+            this.replyDump = emptyDump;
+        }
+
+        public int getContextId() {
+            return contextId;
+        }
+
+        public T getReplyDump() {
+            return replyDump;
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        jvpp.close();
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java
new file mode 100644
index 0000000..65250ed
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java
@@ -0,0 +1,49 @@
+/*
+ * 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 io.fd.vpp.jvpp.future;
+
+
+import io.fd.vpp.jvpp.dto.JVppReply;
+import io.fd.vpp.jvpp.dto.JVppReplyDump;
+import io.fd.vpp.jvpp.dto.JVppRequest;
+
+import java.util.concurrent.CompletionStage;
+import io.fd.vpp.jvpp.notification.EventRegistryProvider;
+
+/**
+* Future facade on top of JVpp
+*/
+public interface FutureJVppInvoker extends EventRegistryProvider, AutoCloseable {
+
+    /**
+     * Invoke asynchronous operation on VPP
+     *
+     * @return CompletionStage with future result of an async VPP call
+     * @throws io.fd.vpp.jvpp.VppInvocationException when send request failed with details
+     */
+    <REQ extends JVppRequest, REPLY extends JVppReply<REQ>> CompletionStage<REPLY> send(REQ req);
+
+
+    /**
+     * Invoke asynchronous dump operation on VPP
+     *
+     * @return CompletionStage with aggregated future result of an async VPP dump call
+     * @throws io.fd.vpp.jvpp.VppInvocationException when send request failed with details
+     */
+    <REQ extends JVppRequest, REPLY extends JVppReply<REQ>, DUMP extends JVppReplyDump<REQ, REPLY>> CompletionStage<DUMP> send(
+            REQ req, DUMP emptyReplyDump);
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistry.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistry.java
new file mode 100644
index 0000000..12515a5
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistry.java
@@ -0,0 +1,25 @@
+/*
+ * 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 io.fd.vpp.jvpp.notification;
+
+/**
+ * Base registry for notification callbacks.
+ */
+public interface EventRegistry extends AutoCloseable {
+
+    void close();
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistryProvider.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistryProvider.java
new file mode 100644
index 0000000..1ac5d55
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistryProvider.java
@@ -0,0 +1,28 @@
+/*
+ * 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 io.fd.vpp.jvpp.notification;
+
+/**
+ * Provides notification registry
+ */
+public interface EventRegistryProvider {
+
+    /**
+     * Get current notification registry instance
+     */
+    EventRegistry getEventRegistry();
+}
diff --git a/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java
new file mode 100644
index 0000000..27b4d29
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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 io.fd.vpp.jvpp.test;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+
+/**
+ * Run using:
+ * sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-16.09.jar io.fd.vpp.jvpp.test.ConnectionTest
+ */
+public class ConnectionTest {
+
+    private static void testConnect() throws Exception {
+        System.out.println("Testing JNI connection with JVppRegistry");
+        final JVppRegistry registry = new JVppRegistryImpl("ConnectionTest");
+        try {
+            System.out.println("Successfully connected to vpp");
+            Thread.sleep(5000);
+            System.out.println("Disconnecting...");
+            Thread.sleep(1000);
+        } finally {
+            registry.close();
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testConnect();
+    }
+}
diff --git a/extras/japi/java/jvpp-registry/jvpp_registry.c b/extras/japi/java/jvpp-registry/jvpp_registry.c
new file mode 100644
index 0000000..bbe9719
--- /dev/null
+++ b/extras/japi/java/jvpp-registry/jvpp_registry.c
@@ -0,0 +1,410 @@
+/*
+ * 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.
+ */
+#define _GNU_SOURCE /* for strcasestr(3) */
+#include <vnet/vnet.h>
+
+#define vl_api_version(n,v) static u32 vpe_api_version = (v);
+#include <vpp/api/vpe.api.h>
+#undef vl_api_version
+
+
+#include <jni.h>
+#include <jvpp-common/jvpp_common.h>
+#include "io_fd_vpp_jvpp_VppJNIConnection.h"
+#include "io_fd_vpp_jvpp_JVppRegistryImpl.h"
+
+#include <vpp/api/vpe_msg_enum.h>
+#define vl_typedefs             /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...)
+#define vl_printfun
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_printfun
+
+vlib_main_t vlib_global_main;
+vlib_main_t **vlib_mains;
+
+/*
+ * The Java runtime isn't compile w/ -fstack-protector,
+ * so we have to supply missing external references for the
+ * regular vpp libraries.
+ */
+void __stack_chk_guard(void) __attribute__((weak));
+void __stack_chk_guard(void) {
+}
+
+#define CONTROL_PING_MESSAGE "control_ping"
+#define CONTROL_PING_REPLY_MESSAGE "control_ping_reply"
+
+typedef struct {
+    /* UThread attachment */
+    volatile u32 control_ping_result_ready;
+    volatile i32 control_ping_retval;
+
+    /* Control ping callback */
+    jobject registryObject;
+    jclass registryClass;
+    jclass controlPingReplyClass;
+    jclass callbackExceptionClass;
+    int control_ping_msg_id;
+    int control_ping_reply_msg_id;
+
+    /* Thread cleanup */
+    pthread_key_t cleanup_rx_thread_key;
+
+    /* Connected indication */
+    volatile u8 is_connected;
+    u32 vpe_pid;
+} jvpp_registry_main_t;
+
+jvpp_registry_main_t jvpp_registry_main __attribute__((aligned (64)));
+
+void vl_client_add_api_signatures(vl_api_memclnt_create_t *mp) {
+    /*
+     * Send the main API signature in slot 0. This bit of code must
+     * match the checks in ../vpe/api/api.c: vl_msg_api_version_check().
+     */
+    mp->api_versions[0] = clib_host_to_net_u32(vpe_api_version);
+}
+
+/* cleanup handler for RX thread */
+static_always_inline void cleanup_rx_thread(void *arg) {
+    jvpp_main_t * jm = &jvpp_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+
+    vppjni_lock(jm, 99);
+
+    int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **) &(jm->jenv),
+    JNI_VERSION_1_8);
+    if (getEnvStat == JNI_EVERSION) {
+        clib_warning("Unsupported JNI version\n");
+        rm->control_ping_retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION;
+        goto out;
+    } else if (getEnvStat != JNI_EDETACHED) {
+        (*jm->jvm)->DetachCurrentThread(jm->jvm);
+    }
+    out: vppjni_unlock(jm);
+}
+
+static void vl_api_control_ping_reply_t_handler(
+        vl_api_control_ping_reply_t * mp) {
+    jvpp_main_t * jm = &jvpp_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+    char was_thread_connected = 0;
+
+    // attach to java thread if not attached
+    int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **) &(jm->jenv),
+            JNI_VERSION_1_8);
+    if (getEnvStat == JNI_EDETACHED) {
+        if ((*jm->jvm)->AttachCurrentThread(jm->jvm, (void **) &(jm->jenv),
+                NULL) != 0) {
+            clib_warning("Failed to attach thread\n");
+            rm->control_ping_retval =
+                    VNET_API_ERROR_FAILED_TO_ATTACH_TO_JAVA_THREAD;
+            goto out;
+        }
+
+        // workaround as we can't use pthread_cleanup_push
+        pthread_key_create(&rm->cleanup_rx_thread_key, cleanup_rx_thread);
+        // destructor is only called if the value of key is non null
+        pthread_setspecific(rm->cleanup_rx_thread_key, (void *) 1);
+        was_thread_connected = 1;
+    } else if (getEnvStat == JNI_EVERSION) {
+        clib_warning("Unsupported JNI version\n");
+        rm->control_ping_retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION;
+        goto out;
+    }
+
+    if (was_thread_connected == 0) {
+        JNIEnv *env = jm->jenv;
+        if (mp->retval < 0) {
+            call_on_error("controlPing", mp->context, mp->retval,
+                    rm->registryClass, rm->registryObject,
+                    rm->callbackExceptionClass);
+        } else {
+            jmethodID constructor = (*env)->GetMethodID(env,
+                    rm->controlPingReplyClass, "<init>", "()V");
+            jmethodID callbackMethod = (*env)->GetMethodID(env,
+                    rm->registryClass, "onControlPingReply",
+                    "(Lio/fd/vpp/jvpp/dto/ControlPingReply;)V");
+
+            jobject dto = (*env)->NewObject(env, rm->controlPingReplyClass,
+                    constructor);
+
+            jfieldID contextFieldId = (*env)->GetFieldID(env,
+                    rm->controlPingReplyClass, "context", "I");
+            (*env)->SetIntField(env, dto, contextFieldId,
+                    clib_net_to_host_u32(mp->context));
+
+            jfieldID clientIndexFieldId = (*env)->GetFieldID(env,
+                    rm->controlPingReplyClass, "clientIndex", "I");
+            (*env)->SetIntField(env, dto, clientIndexFieldId,
+                    clib_net_to_host_u32(mp->client_index));
+
+            jfieldID vpePidFieldId = (*env)->GetFieldID(env,
+                    rm->controlPingReplyClass, "vpePid", "I");
+            (*env)->SetIntField(env, dto, vpePidFieldId,
+                    clib_net_to_host_u32(mp->vpe_pid));
+
+            (*env)->CallVoidMethod(env, rm->registryObject, callbackMethod,
+                    dto);
+            (*env)->DeleteLocalRef(env, dto);
+        }
+    }
+
+    out: rm->vpe_pid = clib_net_to_host_u32(mp->vpe_pid);
+    rm->control_ping_result_ready = 1;
+}
+
+static int find_ping_id() {
+    int rv = 0;
+    jvpp_main_t * jm = &jvpp_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+    api_main_t *am = &api_main;
+    hash_pair_t *hp;
+    jm->messages_hash = am->msg_index_by_name_and_crc;
+
+    rm->control_ping_msg_id = -1;
+    rm->control_ping_reply_msg_id = -1;
+
+    hash_foreach_pair (hp, jm->messages_hash,
+    ({
+        char *key = (char *)hp->key; // key format: name_crc
+        int msg_name_len = strlen(key) - 9; // ignore crc
+        if (strlen(CONTROL_PING_MESSAGE) == msg_name_len &&
+                strncmp(CONTROL_PING_MESSAGE, (char *)hp->key, msg_name_len) == 0) {
+            rm->control_ping_msg_id = (u32)hp->value[0];
+        }
+        if (strlen(CONTROL_PING_REPLY_MESSAGE) == msg_name_len &&
+                strncmp(CONTROL_PING_REPLY_MESSAGE, (char *)hp->key, msg_name_len) == 0) {
+            rm->control_ping_reply_msg_id  = (u32)hp->value[0];
+        }
+    }));
+    if (rm->control_ping_msg_id == -1) {
+        clib_warning("failed to find id for %s", CONTROL_PING_MESSAGE);
+        rv = -1;
+    }
+    if (rm->control_ping_reply_msg_id == -1) {
+        clib_warning("failed to find id for %s", CONTROL_PING_REPLY_MESSAGE);
+        rv = -1;
+    }
+    return rv;
+}
+
+static int send_initial_control_ping() {
+    f64 timeout;
+    clib_time_t clib_time;
+    vl_api_control_ping_t * mp;
+    jvpp_main_t * jm = &jvpp_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+
+    clib_time_init(&clib_time);
+
+    rm->control_ping_result_ready = 0;
+    mp = vl_msg_api_alloc(sizeof(*mp));
+    memset(mp, 0, sizeof(*mp));
+    mp->_vl_msg_id = ntohs(rm->control_ping_msg_id);
+    mp->client_index = jm->my_client_index;
+
+    // send message:
+    vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
+
+    // wait for results: Current time + 10 seconds is the timeout
+    timeout = clib_time_now(&clib_time) + 10.0;
+    int rv = VNET_API_ERROR_RESPONSE_NOT_READY;
+    while (clib_time_now(&clib_time) < timeout) {
+        if (rm->control_ping_result_ready == 1) {
+            rv = rm->control_ping_retval;
+            break;
+        }
+    }
+
+    if (rv != 0) {
+        vl_msg_api_clean_handlers(rm->control_ping_reply_msg_id);
+        clib_warning("first control ping failed: %d", rv);
+    }
+    return rv;
+}
+
+#if USE_DLMALLOC == 1
+void * __jvpp_heap;
+#endif
+
+static int connect_to_vpe(char *shm_prefix, char *name) {
+    jvpp_main_t * jm = &jvpp_main;
+    api_main_t * am = &api_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+
+#if USE_DLMALLOC == 1
+    __jvpp_heap = clib_mem_init (0, 1<<20);
+#endif
+
+    if (vl_client_connect_to_vlib(shm_prefix, name, 32) < 0)
+        return -1;
+    jm->my_client_index = am->my_client_index;
+
+    jm->vl_input_queue = am->shmem_hdr->vl_input_queue;
+
+    if (find_ping_id() < 0)
+        return -1;
+
+    vl_msg_api_set_handlers(rm->control_ping_reply_msg_id, CONTROL_PING_REPLY_MESSAGE,
+            vl_api_control_ping_reply_t_handler, vl_noop_handler,
+            vl_api_control_ping_reply_t_endian,
+            vl_api_control_ping_reply_t_print,
+            sizeof(vl_api_control_ping_reply_t), 1);
+
+    return send_initial_control_ping();
+}
+
+JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect(
+        JNIEnv *env, jclass obj, jstring shmPrefix, jstring clientName) {
+    /*
+     * TODO introducing memory prefix as variable can be used in hc2vpp
+     * to be able to run without root privileges
+     * https://jira.fd.io/browse/HC2VPP-176
+     */
+    int rv;
+    const char *client_name;
+    const char *shm_prefix;
+    void vl_msg_reply_handler_hookup(void);
+    jvpp_main_t * jm = &jvpp_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+
+    jclass connectionInfoClass = (*env)->FindClass(env,
+            "io/fd/vpp/jvpp/VppJNIConnection$ConnectionInfo");
+    jmethodID connectionInfoConstructor = (*env)->GetMethodID(env,
+            connectionInfoClass, "<init>", "(JIII)V");
+
+    if (rm->is_connected) {
+        return (*env)->NewObject(env, connectionInfoClass,
+                connectionInfoConstructor, 0, 0,
+                VNET_API_ERROR_ALREADY_CONNECTED, 0);
+    }
+
+    client_name = (*env)->GetStringUTFChars(env, clientName, 0);
+    shm_prefix = (*env)->GetStringUTFChars(env, shmPrefix, 0);
+
+    if (!client_name) {
+        return (*env)->NewObject(env, connectionInfoClass,
+                connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, 0, shmPrefix);
+    }
+
+    if (!shm_prefix) {
+        return (*env)->NewObject(env, connectionInfoClass,
+                connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, 0, shmPrefix);
+    }
+
+    rv = connect_to_vpe((char *) shm_prefix, (char *) client_name);
+
+    if (rv < 0)
+        clib_warning("connection failed, rv %d", rv);
+
+    (*env)->ReleaseStringUTFChars(env, clientName, client_name);
+    (*env)->ReleaseStringUTFChars(env, shmPrefix, shm_prefix);
+
+    return (*env)->NewObject(env, connectionInfoClass,
+            connectionInfoConstructor, (jlong) pointer_to_uword (jm->vl_input_queue),
+            (jint) jm->my_client_index, (jint) rv, (jint) rm->vpe_pid);
+}
+
+JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_JVppRegistryImpl_controlPing0(
+        JNIEnv *env, jobject regstryObject) {
+    jvpp_main_t * jm = &jvpp_main;
+    vl_api_control_ping_t * mp;
+    u32 my_context_id = vppjni_get_context_id(&jvpp_main);
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+
+    if (rm->registryObject == 0) {
+        rm->registryObject = (*env)->NewGlobalRef(env, regstryObject);
+    }
+    if (rm->registryClass == 0) {
+        rm->registryClass = (jclass) (*env)->NewGlobalRef(env,
+                (*env)->GetObjectClass(env, regstryObject));
+    }
+
+    mp = vl_msg_api_alloc(sizeof(*mp));
+    memset(mp, 0, sizeof(*mp));
+    mp->_vl_msg_id = ntohs(rm->control_ping_msg_id);
+    mp->client_index = jm->my_client_index;
+    mp->context = clib_host_to_net_u32(my_context_id);
+
+    // send message:
+    vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp);
+    return my_context_id;
+}
+
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientDisconnect(
+        JNIEnv *env, jclass clazz) {
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+    rm->is_connected = 0; // TODO make thread safe
+    vl_client_disconnect_from_vlib();
+
+    // cleanup:
+    if (rm->registryObject) {
+        (*env)->DeleteGlobalRef(env, rm->registryObject);
+        rm->registryObject = 0;
+    }
+    if (rm->registryClass) {
+        (*env)->DeleteGlobalRef(env, rm->registryClass);
+        rm->registryClass = 0;
+    }
+}
+
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    jvpp_main_t * jm = &jvpp_main;
+    jvpp_registry_main_t * rm = &jvpp_registry_main;
+    JNIEnv* env;
+
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return JNI_EVERSION;
+    }
+
+    rm->controlPingReplyClass = (jclass) (*env)->NewGlobalRef(env,
+            (*env)->FindClass(env, "io/fd/vpp/jvpp/dto/ControlPingReply"));
+    if ((*env)->ExceptionCheck(env)) {
+        (*env)->ExceptionDescribe(env);
+        clib_warning("Failed to cache class references\n");
+        return JNI_ERR;
+    }
+
+    rm->callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env,
+            (*env)->FindClass(env, "io/fd/vpp/jvpp/VppCallbackException"));
+    if ((*env)->ExceptionCheck(env)) {
+        (*env)->ExceptionDescribe(env);
+        return JNI_ERR;
+    }
+
+    jm->jvm = vm;
+    return JNI_VERSION_1_8;
+}
+
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+    jvpp_main_t * jm = &jvpp_main;
+    JNIEnv* env;
+    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+        return;
+    }
+
+    jm->jenv = NULL;
+    jm->jvm = NULL;
+}
diff --git a/extras/japi/java/jvpp/gen/jvpp_gen.py b/extras/japi/java/jvpp/gen/jvpp_gen.py
new file mode 100755
index 0000000..067a92f
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvpp_gen.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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 argparse
+import logging
+import os
+import sys
+
+from jvppgen.types_gen import generate_types
+from jvppgen.enums_gen import generate_enums
+from jvppgen.unions_gen import generate_unions
+from jvppgen.dto_gen import generate_dtos
+from jvppgen.jvpp_ifc_gen import generate_java_ifc
+from jvppgen.jvpp_impl_gen import generate_java_impl
+from jvppgen.callback_gen import generate_callbacks
+from jvppgen.jni_gen import generate_jni
+from jvppgen.notification_gen import generate_notifications
+from jvppgen.jvpp_future_facade_gen import generate_future_facade
+from jvppgen.jvpp_callback_facade_gen import generate_callback_facade
+from jvppgen.jvpp_model import JVppModel
+
+
+def generate_jvpp(root_dir, model, logger):
+    base_dir = "%s/target/%s" % (root_dir, model.plugin_package.replace(".", "/"))
+    generate_types(_work_dir(base_dir, "types"), model, logger)
+    generate_enums(_work_dir(base_dir, "types"), model, logger)
+    generate_unions(_work_dir(base_dir, "types"), model, logger)
+    generate_dtos(_work_dir(base_dir, "dto"), model, logger)
+    generate_java_ifc(_work_dir(base_dir), model, logger)
+    generate_java_impl(_work_dir(base_dir), model, logger)
+    generate_callbacks(_work_dir(base_dir, "callback"), model, logger)
+    generate_jni(root_dir, model, logger)
+    generate_notifications(_work_dir(base_dir, "notification"), model, logger)
+    generate_future_facade(_work_dir(base_dir, "future"), model, logger)
+    generate_callback_facade(_work_dir(base_dir, "callfacade"), model, logger)
+
+
+def _work_dir(work_dir, sub_dir=None):
+    if sub_dir:
+        work_dir = "%s/%s" % (work_dir, sub_dir)
+    try:
+        os.makedirs(work_dir)
+    except OSError:
+        if not os.path.isdir(work_dir):
+            raise
+    return work_dir
+
+
+def _init_logger():
+    try:
+        verbose = int(os.getenv("V", 0))
+    except:
+        verbose = 0
+
+    log_level = logging.WARNING
+    if verbose == 1:
+        log_level = logging.INFO
+    elif verbose >= 2:
+        log_level = logging.DEBUG
+
+    logging.basicConfig(stream=sys.stdout, level=log_level)
+    logger = logging.getLogger("JVPP GEN")
+    logger.setLevel(log_level)
+    return logger
+
+
+if __name__ == '__main__':
+    logger = _init_logger()
+
+    argparser = argparse.ArgumentParser(description="VPP Java API generator")
+    argparser.add_argument('-i', nargs='+', metavar='api_file.json', help="json vpp api file(s)")
+    argparser.add_argument('--plugin_name')
+    argparser.add_argument('--root_dir')
+    args = argparser.parse_args()
+
+    logger.info("Generating Java API for %s" % args.i)
+    logger.debug("plugin_name: %s" % args.plugin_name)
+    logger.debug("root_dir: %s" % args.root_dir)
+
+    model = JVppModel(logger, args.i, args.plugin_name)
+    generate_jvpp(args.root_dir, model, logger)
diff --git a/extras/japi/java/jvpp/gen/jvppgen/__init__.py b/extras/japi/java/jvpp/gen/jvppgen/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/__init__.py
diff --git a/extras/japi/java/jvpp/gen/jvppgen/callback_gen.py b/extras/japi/java/jvpp/gen/jvppgen/callback_gen.py
new file mode 100755
index 0000000..14aa8b7
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/callback_gen.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+#
+from string import Template
+
+from jvpp_model import is_request, is_dump, is_control_ping, is_control_ping_reply
+
+
+def generate_callbacks(work_dir, model, logger):
+    json_api_files = model.json_api_files
+    logger.debug("Generating Callback interfaces for %s" % json_api_files)
+    plugin_package = model.plugin_package
+
+    callbacks = []
+    for msg in model.messages:
+        name = msg.java_name_upper
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+
+        callbacks.append("%s.callback.%sCallback" % (plugin_package, name))
+        callback = _CALLBACK_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            json_filename=json_api_files,
+            name=name)
+
+        with open("%s/%sCallback.java" % (work_dir, name), "w") as f:
+            f.write(callback)
+
+    plugin_name = model.plugin_java_name
+    with open("%s/JVpp%sGlobalCallback.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_GLOBAL_CALLBACK_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            json_filename=json_api_files,
+            plugin_name=plugin_name,
+            callbacks=", ".join(callbacks)
+        ))
+
+_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback;
+
+/**
+ * <p>Represents callback for plugin's api message.
+ * <br>It was generated by jvpp_callback_gen.py based on $json_filename.
+ */
+public interface ${name}Callback extends io.fd.vpp.jvpp.callback.JVppCallback {
+
+    void on${name}(${plugin_package}.dto.${name} reply);
+}
+""")
+
+_GLOBAL_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback;
+
+/**
+ * <p>Global aggregated callback interface.
+ * <br>It was generated by jvpp_callback_gen.py based on $json_filename.
+ */
+public interface JVpp${plugin_name}GlobalCallback extends io.fd.vpp.jvpp.callback.ControlPingCallback, $callbacks {
+}
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/dto_gen.py b/extras/japi/java/jvpp/gen/jvppgen/dto_gen.py
new file mode 100755
index 0000000..ca015a3
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/dto_gen.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+
+from string import Template
+
+from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
+from jvpp_model import is_request, is_reply, is_retval, is_dump, is_details, is_event, is_control_ping, \
+    is_control_ping_reply
+
+
+def generate_dtos(work_dir, model, logger):
+    logger.debug("Generating DTOs for %s " % model.json_api_files)
+    _generate_message_dtos(work_dir, model, logger)
+    _generate_dump_reply_wrappers(work_dir, model, logger)
+
+
+def _generate_message_dtos(work_dir, model, logger):
+    for msg in model.messages:
+        logger.debug("Generating DTO for message %s", msg)
+        class_name = msg.java_name_upper
+
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_request(msg):
+            dto = _generate_request_dto(msg, model, base_type="JVppRequest")
+        elif is_dump(msg):
+            dto = _generate_request_dto(msg, model, base_type="JVppDump")
+        elif is_reply(msg) or is_details(msg):
+            dto = _generate_reply_dto(msg, model)
+        elif is_event(msg):
+            dto = _generate_event_dto(msg, model)
+        else:
+            logger.warn("Failed to generate DTO for: %s. Message type is not supported." % msg)
+            continue
+        with open("%s/%s.java" % (work_dir, class_name), "w") as f:
+            f.write(dto)
+
+
+def _generate_request_dto(msg, model, base_type):
+    msg_java_name_upper = msg.java_name_upper
+    fields = msg.fields
+    return _REQUEST_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        json_filename=model.json_api_files,
+        json_definition=msg.doc,
+        class_name=msg_java_name_upper,
+        base_type=base_type,
+        fields=generate_fields(fields),
+        hash_code=generate_hash_code(fields),
+        equals=generate_equals(msg_java_name_upper, fields),
+        to_string=generate_to_string(msg_java_name_upper, fields),
+        send=_generate_send(model, msg))
+
+_REQUEST_TEMPLATE = Template("""
+package $plugin_package.dto;
+
+/**
+ * <p>This class represents request DTO.
+ * <br>It was generated by dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class $class_name implements io.fd.vpp.jvpp.dto.$base_type {
+$fields
+$hash_code
+$equals
+$to_string
+$send
+}
+""")
+
+
+def _generate_send(model, msg):
+    return _SEND_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        plugin_name=model.plugin_java_name,
+        method_name=msg.java_name_lower,
+        args="this" if msg.has_fields else ""
+    )
+
+_SEND_TEMPLATE = Template("""
+    @Override
+    public int send(final io.fd.vpp.jvpp.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException {
+        return (($plugin_package.JVpp${plugin_name})jvpp).$method_name($args);
+    }""")
+
+
+def _generate_reply_dto(msg, model):
+    msg_java_name_upper = msg.java_name_upper
+    # Negative retval is mapped to java exception, so filter it out:
+    fields = filter(lambda field: not is_retval(field), msg.fields)
+    return _REPLY_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        json_filename=model.json_api_files,
+        json_definition=msg.doc,
+        class_name=msg_java_name_upper,
+        request_name=msg.request_java,
+        fields=generate_fields(fields),
+        hash_code=generate_hash_code(fields),
+        equals=generate_equals(msg_java_name_upper, fields),
+        to_string=generate_to_string(msg_java_name_upper, fields))
+
+_REPLY_TEMPLATE = Template("""
+package $plugin_package.dto;
+
+/**
+ * <p>This class represents reply DTO.
+ * <br>It was generated by jvpp_dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class $class_name implements io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_name> {
+$fields
+$hash_code
+$equals
+$to_string
+}
+""")
+
+
+def _generate_event_dto(msg, model):
+    msg_java_name_upper = msg.java_name_upper
+    # Negative retval is mapped to java exception, so filter it out:
+    fields = filter(lambda field: not is_retval(field), msg.fields)
+    return _EVENT_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        json_filename=model.json_api_files,
+        json_definition=msg.doc,
+        class_name=msg_java_name_upper,
+        fields=generate_fields(fields),
+        hash_code=generate_hash_code(fields),
+        equals=generate_equals(msg_java_name_upper, fields),
+        to_string=generate_to_string(msg_java_name_upper, fields))
+
+_EVENT_TEMPLATE = Template("""
+package $plugin_package.dto;
+
+/**
+ * <p>This class represents event DTO.
+ * <br>It was generated by jvpp_dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class $class_name {
+$fields
+$hash_code
+$equals
+$to_string
+}
+""")
+
+
+def _generate_dump_reply_wrappers(work_dir, model, logger):
+    for msg in model.messages:
+        if is_details(msg):
+            logger.debug("Generating ReplyDump DTO for message %s", msg)
+            details_class = msg.java_name_upper
+            dto = _REPLY_DUMP_TEMPLATE.substitute(
+                plugin_package=model.plugin_package,
+                json_filename=model.json_api_files,
+                json_definition=msg.doc,
+                details_class=details_class,
+                details_field=msg.java_name_lower,
+                dump_class=msg.request_java
+            )
+            with open("%s/%sReplyDump.java" % (work_dir, details_class), "w") as f:
+                f.write(dto)
+
+_REPLY_DUMP_TEMPLATE = Template("""
+package $plugin_package.dto;
+
+/**
+ * <p>This class represents dump reply wrapper.
+ * <br>It was generated by jvpp_dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class ${details_class}ReplyDump implements io.fd.vpp.jvpp.dto.JVppReplyDump<${plugin_package}.dto.${dump_class}, ${plugin_package}.dto.${details_class}> {
+
+    public java.util.List<${details_class}> ${details_field} = new java.util.ArrayList<>();
+
+    @Override
+    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+    public int hashCode() {
+        return java.util.Objects.hash(${details_field});
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final ${details_class}ReplyDump other = (${details_class}ReplyDump) o;
+
+        if (!java.util.Objects.equals(this.${details_field}, other.${details_field})) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "${details_class}ReplyDump{" +
+                "${details_field}=" + ${details_field} + "}";
+    }
+
+
+}
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/enums_gen.py b/extras/japi/java/jvpp/gen/jvppgen/enums_gen.py
new file mode 100755
index 0000000..8ba9655
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/enums_gen.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+from string import Template
+
+from jvpp_model import Enum
+
+
+def generate_enums(work_dir, model, logger):
+    logger.debug("Generating enums for %s " % model.json_api_files)
+
+    for t in model.types:
+        if not isinstance(t, Enum):
+            continue
+        logger.debug("Generating DTO for enum %s", t)
+        type_class_name = t.java_name
+        type_class = _ENUM_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            c_type_name=t.name,
+            json_filename=model.json_api_files,
+            json_definition=t.doc,
+            java_enum_name=type_class_name,
+            constants=_generate_constants(t.constants),
+            value_type=t.value.type.java_name
+        )
+        with open("%s/%s.java" % (work_dir, type_class_name), "w") as f:
+            f.write(type_class)
+
+_ENUM_TEMPLATE = Template("""
+package $plugin_package.types;
+
+/**
+ * <p>This class represents $c_type_name enum definition.
+ * <br>It was generated by enums_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public enum $java_enum_name {
+$constants;
+
+    public final $value_type value;
+
+    $java_enum_name(final $value_type value) {
+        this.value = value;
+    }
+    
+    public static $java_enum_name forValue(final $value_type value) {
+        for ($java_enum_name enumeration : $java_enum_name.values()) {
+            if (value == enumeration.value) {
+                return enumeration;
+            }
+        }
+        return null;
+    }
+}
+""")
+
+
+def _generate_constants(constants):
+    return ",\n".join(_CONSTANT_TEMPLATE.substitute(name=c['name'], value=c['value']) for c in constants)
+
+_CONSTANT_TEMPLATE = Template("""    $name($value)""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jni_common_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jni_common_gen.py
new file mode 100755
index 0000000..397b92b
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jni_common_gen.py
@@ -0,0 +1,427 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+#
+from string import Template
+
+from jvpp_model import is_array, is_retval, Class, Enum, Union
+
+
+def generate_j2c_identifiers(element, class_ref_name, object_ref_name):
+    identifiers = []
+    for field in element.fields:
+        field_type = field.type
+        identifiers.append(_REQUEST_FIELD_IDENTIFIER_TEMPLATE.substitute(
+            java_name=field.java_name,
+            class_ref_name=class_ref_name,
+            jni_signature=field_type.jni_signature,
+            jni_type=field_type.jni_type,
+            jni_accessor=field_type.jni_accessor,
+            object_ref_name=object_ref_name
+        ))
+    return "".join(identifiers)
+
+_REQUEST_FIELD_IDENTIFIER_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}, "${java_name}", "${jni_signature}");
+    ${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId);
+""")
+
+
+# TODO(VPP-1187): do not inline JNI object creation inside message handlers to reduce number of special cases
+def generate_j2c_swap(element, struct_ref_name):
+    initialization = []
+    for field in element.fields:
+        initialization.append(generate_j2c_field_swap(field, struct_ref_name))
+    return "\n".join(initialization)
+
+
+def generate_j2c_field_swap(field, struct_ref_name):
+    if is_array(field):
+        return _generate_j2c_array_swap(field, struct_ref_name)
+    else:
+        return _generate_j2c_scalar_swap(field, struct_ref_name)
+
+
+def _generate_j2c_array_swap(field, struct_ref_name):
+    # TODO(VPP-1186): move the logic to JNI generators
+    base_type = field.type.base_type
+    if isinstance(base_type, (Class, Enum, Union)):
+        return _generate_j2c_object_array_swap(field, struct_ref_name)
+    elif base_type.is_swap_needed:
+        return _generate_j2c_primitive_type_array_swap(field, struct_ref_name)
+    else:
+        return _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name)
+
+
+def _generate_j2c_object_array_swap(field, struct_ref_name):
+    field_type = field.type
+    field_reference_name = field.java_name
+    c_name = field.name
+    host = "%sArrayElement" % field_reference_name
+    net = "%s->%s[_i]" % (struct_ref_name, c_name)
+    swap_elements = field_type.get_host_to_net_function(host, net)
+    return _J2C_OBJECT_ARRAY_SWAP_TEMPLATE.substitute(
+        field_reference_name=field_reference_name,
+        field_length_check=_generate_field_length_check(field),
+        swap_elements=swap_elements)
+
+_J2C_OBJECT_ARRAY_SWAP_TEMPLATE = Template("""
+    {
+        if (${field_reference_name}) {
+            size_t _i;
+            jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
+            ${field_length_check}
+            for (_i = 0; _i < cnt; _i++) {
+                jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i);
+                ${swap_elements};
+            }
+        }
+    }
+""")
+
+
+def _generate_j2c_primitive_type_array_swap(field, struct_ref_name):
+    field_reference_name = field.java_name
+    field_type = field.type
+    host = "%sArrayElements[_i]" % field_reference_name
+    net = "%s->%s[_i]" % (struct_ref_name,  field.name)
+    swap_elements = field_type.get_host_to_net_function(host, net)
+    return _J2C_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE.substitute(
+        field_reference_name=field_reference_name,
+        field_length_check=_generate_field_length_check(field),
+        base_type=field_type.base_type.jni_accessor,
+        jni_base_type=field_type.base_type.jni_type,
+        swap_elements=swap_elements
+    )
+
+_J2C_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE = Template("""
+    if (${field_reference_name}) {
+        ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL);
+        size_t _i;
+        jsize cnt = (*env)->GetArrayLength(env, ${field_reference_name});
+        ${field_length_check}
+        for (_i = 0; _i < cnt; _i++) {
+            ${swap_elements};
+        }
+        (*env)->Release${base_type}ArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
+    }
+    """)
+
+
+def _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name):
+    field_type = field.type
+    return _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE.substitute(
+        field_reference_name=field.java_name,
+        field_length_check=_generate_field_length_check(field),
+        base_type=field_type.base_type.jni_accessor,
+        jni_base_type=field_type.base_type.jni_type,
+        struct_reference_name=struct_ref_name,
+        c_name=field.name
+    )
+
+_J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
+    if (${field_reference_name}) {
+        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
+        ${field_length_check}
+        (*env)->Get${base_type}ArrayRegion(env, ${field_reference_name}, 0, cnt, (${jni_base_type} *)${struct_reference_name}->${c_name});
+    }
+""")
+
+
+def _generate_field_length_check(field):
+    # Enforce max length if array has fixed length or uses variable length syntax
+    field_length = str(field.array_len)
+    if field.array_len_field:
+        field_length = field.array_len_field.java_name
+
+    # TODO: remove when ZLAs without length field are disabled
+    if field_length != "0":
+        return _FIELD_LENGTH_CHECK.substitute(field_length=field_length)
+    else:
+        return ""
+
+# Make sure we do not write more elements that are expected
+_FIELD_LENGTH_CHECK = Template("""
+        size_t max_size = ${field_length};
+        if (cnt > max_size) cnt = max_size;""")
+
+
+def _generate_j2c_scalar_swap(field, struct_ref_name):
+    field_type = field.type
+    if field_type.is_swap_needed:
+        host = field.java_name
+        net = "%s->%s" % (struct_ref_name, field.name)
+        return "    %s;" % field_type.get_host_to_net_function(host, net)
+    else:
+        return "    %s->%s = %s;" % (struct_ref_name, field.name, field.java_name)
+
+
+def generate_c2j_swap(element, object_ref_name, struct_ref_name):
+    msg_java_name = element.java_name_lower
+    initialization = []
+    for field in element.fields:
+        if is_retval(field):
+            # For retval don't generate setters and generate retval check
+            continue
+        elif is_array(field):
+            initialization.append(_generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name))
+        else:
+            initialization.append(_generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name))
+    return "".join(initialization)
+
+
+def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    # TODO(VPP-1186): move the logic to JNI generators
+    base_type = field.type.base_type
+    if isinstance(base_type, (Class, Union)):
+        return _generate_c2j_object_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+    elif isinstance(base_type, Enum):
+        return _generate_c2j_enum_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+    elif base_type.is_swap_needed:
+        return _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+    else:
+        return _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+
+
+def _generate_c2j_object_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_OBJECT_ARRAY_SWAP_TEMPLATE.substitute(
+        field_reference_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_name=field_type.base_type.jni_name,
+        field_length=_generate_array_length(field, struct_ref_name),
+        net_to_host_function=field_type.net_to_host_function,
+        struct_ref_name=struct_ref_name,
+        object_ref_name=object_ref_name,
+        c_name=field.name
+    )
+
+_C2J_OBJECT_ARRAY_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    {
+        jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}");
+        jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
+        jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
+            jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class,  ${field_reference_name}Constructor);
+            ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}[_i]), ${field_reference_name}ArrayElement);
+            (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
+            (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
+        }
+        (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+        (*env)->DeleteLocalRef(env, ${field_reference_name});
+    }
+""")
+
+
+def _generate_c2j_enum_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    base_type = field_type.base_type
+    return _C2J_ENUM_ARRAY_SWAP_TEMPLATE.substitute(
+        field_reference_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_name=base_type.jni_name,
+        field_length=_generate_array_length(field, struct_ref_name),
+        net_to_host_function=field_type.net_to_host_function,
+        jni_signature_enum_value=base_type.value.type.jni_signature,
+        struct_ref_name=struct_ref_name,
+        object_ref_name=object_ref_name,
+        c_name=field.name
+    )
+
+_C2J_ENUM_ARRAY_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    {
+        jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}");
+        jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
+        jmethodID ${field_reference_name}Constructor = (*env)->GetStaticMethodID(env, ${field_reference_name}Class, "forValue", "(${jni_signature_enum_value})${jni_signature}");
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
+            jobject ${field_reference_name}ArrayElement = (*env)->CallStaticObjectMethod(env, ${field_reference_name}Class, ${field_reference_name}Constructor, ${net_to_host_function}(${struct_ref_name}->${c_name}[_i]));
+            (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
+            (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
+        }
+        (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+        (*env)->DeleteLocalRef(env, ${field_reference_name});
+    }
+""")
+
+
+def _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE.substitute(
+        field_reference_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_type=field_type.jni_type,
+        base_type=field_type.base_type.jni_accessor,
+        field_length=_generate_array_length(field, struct_ref_name),
+        jni_base_type=field_type.base_type.jni_type,
+        object_ref_name=object_ref_name,
+        struct_ref_name=struct_ref_name,
+        net_to_host_function=field_type.net_to_host_function,
+        c_name=field.name
+    )
+
+_C2J_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    {
+        ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
+        ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL);
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
+            ${field_reference_name}ArrayElements[_i] = ${net_to_host_function}(${struct_ref_name}->${c_name}[_i]);
+        }
+
+        (*env)->Release${base_type}ArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
+        (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+        (*env)->DeleteLocalRef(env, ${field_reference_name});
+    }
+""")
+
+
+def _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE.substitute(
+        field_reference_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_type=field_type.jni_type,
+        base_type=field_type.base_type.jni_accessor,
+        field_length=_generate_array_length(field, struct_ref_name),
+        jni_base_type=field_type.base_type.jni_type,
+        object_ref_name=object_ref_name,
+        struct_ref_name=struct_ref_name,
+        c_name=field.name
+    )
+
+_C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
+    (*env)->Set${base_type}ArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const ${jni_base_type}*)${struct_ref_name}->${c_name});
+    (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+    (*env)->DeleteLocalRef(env, ${field_reference_name});
+""")
+
+
+def _generate_array_length(field, struct_ref_name):
+    if field.array_len_field:
+        len_field = field.array_len_field
+        if len_field.type.is_swap_needed:
+            return "%s(%s->%s)" % (len_field.type.host_to_net_function, struct_ref_name, len_field.name)
+        else:
+            return "%s->%s" % (struct_ref_name, len_field.name)
+    return field.array_len
+
+
+def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    if field_type.is_swap_needed:
+        # TODO(VPP-1186): move the logic to JNI generators
+        if isinstance(field_type, (Class, Union)):
+            return _generate_c2j_object_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+        elif isinstance(field_type, Enum):
+            return _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+        else:
+            return _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+    else:
+        return _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+
+
+def _generate_c2j_object_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_OBJECT_SWAP_TEMPLATE.substitute(
+        java_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_name=field_type.jni_name,
+        jni_accessor=field_type.jni_accessor,
+        object_ref_name=object_ref_name,
+        struct_ref_name=struct_ref_name,
+        net_to_host_function=field_type.net_to_host_function,
+        c_name=field.name)
+
+_C2J_OBJECT_SWAP_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
+    jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}");
+    jmethodID ${java_name}Constructor = (*env)->GetMethodID(env, ${java_name}Class, "<init>", "()V");
+    jobject ${java_name} = (*env)->NewObject(env, ${java_name}Class,  ${java_name}Constructor);
+    ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}), ${java_name});
+    (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${java_name});
+    (*env)->DeleteLocalRef(env, ${java_name});
+""")
+
+
+def _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_ENUM_SWAP_TEMPLATE.substitute(
+        java_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_signature_enum_value=field_type.value.type.jni_signature,
+        jni_name=field_type.jni_name,
+        jni_accessor=field_type.jni_accessor,
+        object_ref_name=object_ref_name,
+        struct_ref_name=struct_ref_name,
+        net_to_host_function=field_type.net_to_host_function,
+        c_name=field.name)
+
+_C2J_ENUM_SWAP_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
+    jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}");
+    jmethodID ${java_name}Constructor = (*env)->GetStaticMethodID(env, ${java_name}Class, "forValue", "(${jni_signature_enum_value})${jni_signature}");
+    jobject ${java_name} = (*env)->CallStaticObjectMethod(env, ${java_name}Class, ${java_name}Constructor, ${net_to_host_function}(${struct_ref_name}->${c_name}));
+    (*env)->SetObjectField(env, ${object_ref_name}, ${java_name}FieldId, ${java_name});
+    (*env)->DeleteLocalRef(env, ${java_name});
+""")
+
+
+def _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE.substitute(
+        java_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_accessor=field_type.jni_accessor,
+        object_ref_name=object_ref_name,
+        net_to_host_function=field_type.net_to_host_function,
+        struct_ref_name=struct_ref_name,
+        c_name=field.name
+    )
+
+_C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
+    (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${net_to_host_function}(${struct_ref_name}->${c_name}));
+""")
+
+
+def _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+    field_type = field.type
+    return _C2J_PRIMITIVE_TYPE_NO_SWAP_TEMPLATE.substitute(
+        java_name=field.java_name,
+        class_ref_name=msg_java_name,
+        jni_signature=field_type.jni_signature,
+        jni_accessor=field_type.jni_accessor,
+        object_ref_name=object_ref_name,
+        struct_ref_name=struct_ref_name,
+        c_name=field.name
+    )
+
+_C2J_PRIMITIVE_TYPE_NO_SWAP_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
+    (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${struct_ref_name}->${c_name});
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jni_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jni_gen.py
new file mode 100755
index 0000000..ad6c261
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jni_gen.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+#
+from string import Template
+
+from jni_impl_gen import generate_jni_impl
+from jni_msg_handlers_gen import generate_jni_handlers
+from jni_type_handlers_gen import generate_type_handlers
+from jvpp_model import is_control_ping, is_dump, is_request, is_control_ping_reply
+
+
+def generate_jni(work_dir, model, logger):
+    logger.debug("Generating jvpp C for %s" % model.json_api_files)
+    plugin_name = model.plugin_name
+    messages = model.messages
+
+    with open("%s/jvpp_%s_gen.h" % (work_dir, plugin_name), "w") as f:
+        f.write(_JVPP_C_TEMPLATE.substitute(
+            json_filename=model.json_api_files,
+            class_cache=_generate_class_cache(plugin_name, messages),
+            api_verification=_generate_api_verification(messages),
+            type_handlers=generate_type_handlers(model, logger),
+            jni_implementations=generate_jni_impl(model),
+            msg_handlers=generate_jni_handlers(model),
+            handler_registration=_generate_handler_registration(messages)))
+
+_JVPP_C_TEMPLATE = Template("""/**
+ * This file contains JNI bindings for jvpp Java API.
+ * It was generated by jvpp_jni_gen.py based on $json_filename.
+ */
+$class_cache
+
+$api_verification
+
+// Type handlers
+$type_handlers
+
+// JNI bindings
+$jni_implementations
+
+// Message handlers
+$msg_handlers
+
+$handler_registration
+""")
+
+
+def _generate_class_cache(plugin_name, messages):
+    references = []
+    for msg in messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        references.append((
+            msg.java_name_lower,
+            'io/fd/vpp/jvpp/%s/dto/%s' % (plugin_name, msg.java_name_upper)
+        ))
+
+    references.append(('callbackException', 'io/fd/vpp/jvpp/VppCallbackException'))
+
+    return _CLASS_CACHE_TEMPLATE.substitute(
+        class_references=_generate_class_references(references),
+        create_references=_generate_create_references(references),
+        delete_references=_generate_delete_references(references)
+    )
+
+_CLASS_CACHE_TEMPLATE = Template("""
+// JAVA class reference cache
+$class_references
+
+static int cache_class_references(JNIEnv* env) {
+$create_references
+    return 0;
+}
+
+static void delete_class_references(JNIEnv* env) {
+$delete_references
+}""")
+
+
+def _generate_class_references(references):
+    return "\n".join("jclass %sClass;" % r[0] for r in references)
+
+
+def _generate_create_references(references):
+    items = []
+    for r in references:
+        items.append(_CREATE_GLOBAL_REF_TEMPLATE.substitute(
+            ref_name=r[0],
+            fqn_name=r[1]
+        ))
+    return "".join(items)
+
+_CREATE_GLOBAL_REF_TEMPLATE = Template("""
+    ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${fqn_name}"));
+    if ((*env)->ExceptionCheck(env)) {
+        (*env)->ExceptionDescribe(env);
+        return JNI_ERR;
+    }""")
+
+
+def _generate_delete_references(references):
+    items = []
+    for r in references:
+        items.append(_DELETE_CLASS_INVOCATION_TEMPLATE.substitute(ref_name=r[0]))
+    return "".join(items)
+
+_DELETE_CLASS_INVOCATION_TEMPLATE = Template("""
+    if (${ref_name}Class) {
+        (*env)->DeleteGlobalRef(env, ${ref_name}Class);
+    }""")
+
+
+def _generate_api_verification(messages):
+    items = []
+    for msg in messages:
+        items.append("_(%s_%s) \\" % (msg.name, msg.crc))
+    return _API_VERIFICATION_TEMPLATE.substitute(messages="\n".join(items))
+
+_API_VERIFICATION_TEMPLATE = Template("""
+// List of supported API messages used for verification
+#define foreach_supported_api_message \\
+$messages
+""")
+
+
+def _generate_handler_registration(messages):
+    """
+    Generates msg handler registration for all messages except for dumps and requests.
+    :param messages: collection of VPP API messages.
+    """
+    handlers = []
+    for msg in messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+        name = msg.name
+        crc = msg.crc
+        handlers.append("_(%s_%s, %s) \\" % (name, crc, name))
+    return _HANDLER_REGISTRATION_TEMPLATE.substitute(handlers="\n".join(handlers))
+
+_HANDLER_REGISTRATION_TEMPLATE = Template("""
+// Registration of message handlers in vlib
+#define foreach_api_reply_handler \\
+$handlers
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jni_impl_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jni_impl_gen.py
new file mode 100755
index 0000000..bf75236
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jni_impl_gen.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+#
+from string import Template
+
+from jni_common_gen import generate_j2c_identifiers, generate_j2c_swap
+from jvpp_model import is_dump, is_request, is_control_ping, is_control_ping_reply
+
+
+def generate_jni_impl(model):
+    """
+    Generates JNI bindings for sending dump and request messages.
+    :param model: meta-model of VPP API used for jVPP generation.
+    """
+    jni_impl = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if not (is_dump(msg) or is_request(msg)):
+            continue
+        arguments = ""
+        request_class = ""
+        jni_identifiers = ""
+        msg_initialization = ""
+
+        if msg.has_fields:
+            arguments = ", jobject request"
+            request_class = _REQUEST_CLASS_TEMPLATE.substitute(
+                plugin_name=model.plugin_name,
+                java_dto_name=msg.java_name_upper
+            )
+            jni_identifiers = generate_j2c_identifiers(msg, class_ref_name="requestClass", object_ref_name="request")
+            msg_initialization = generate_j2c_swap(msg, struct_ref_name="mp")
+
+        jni_impl.append(_JNI_IMPL_TEMPLATE.substitute(
+            c_name=msg.name,
+            json_filename=model.json_api_files,
+            json_definition=msg.doc,
+            plugin_name=model.plugin_name,
+            plugin_java_name=model.plugin_java_name,
+            java_method_name=msg.java_name_lower,
+            arguments=arguments,
+            request_class=request_class,
+            jni_identifiers=jni_identifiers,
+            msg_size=_generate_msg_size(msg),
+            crc=msg.crc,
+            msg_initialization=msg_initialization
+        ))
+    return "".join(jni_impl)
+
+
+_JNI_IMPL_TEMPLATE = Template("""
+/**
+ * JNI binding for sending ${c_name} message.
+ * Generated based on $json_filename:
+$json_definition
+ */
+JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${plugin_java_name}Impl_${java_method_name}0
+(JNIEnv * env, jclass clazz${arguments}) {
+    ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
+    vl_api_${c_name}_t * mp;
+    u32 my_context_id = vppjni_get_context_id (&jvpp_main);
+$request_class
+$jni_identifiers
+
+    // create message:
+    const size_t _size = ${msg_size};
+    mp = vl_msg_api_alloc(_size);
+    memset (mp, 0, _size);
+    mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}"));
+    mp->client_index = plugin_main->my_client_index;
+    mp->context = clib_host_to_net_u32 (my_context_id);
+
+$msg_initialization
+
+    // send message:
+    if (CLIB_DEBUG > 1)
+        clib_warning ("Sending ${c_name} message");
+    vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
+    if ((*env)->ExceptionCheck(env)) {
+        return JNI_ERR;
+    }
+    return my_context_id;
+}""")
+
+# TODO: cache method and field identifiers to achieve better performance
+# https://jira.fd.io/browse/HONEYCOMB-42
+_REQUEST_CLASS_TEMPLATE = Template("""    jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_dto_name}");
+""")
+
+
+def _generate_msg_size(msg):
+    msg_size = "sizeof(*mp)"
+    _size_components = []
+    for field in msg.fields:
+        # Ignore ZLAs for simplicity (to support them we need to call JNI functions to check actual size)
+        if field.array_len_field:
+            _size_components += " + %s*sizeof(%s)" % (field.array_len_field.java_name, field.type.base_type.vpp_name)
+            # FIXME(VPP-586): for proper nested structures support, we need generate functions computing type sizes
+            # and use it instead of sizeof
+
+    return msg_size + "".join(_size_components)
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py
new file mode 100755
index 0000000..8f6410f
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+#
+from string import Template
+
+from jni_common_gen import generate_c2j_swap
+from jvpp_model import is_dump, is_request, is_control_ping, is_control_ping_reply, is_retval
+
+
+def generate_jni_handlers(model):
+    """
+    Generates msg handlers for all messages except for dumps and requests (handled by vpp, not client).
+    :param model: meta-model of VPP API used for jVPP generation.
+    """
+    jni_impl = []
+    for msg in model.messages:
+        msg_name = msg.name
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+
+        jni_impl.append(_MSG_HANDLER_TEMPLATE.substitute(
+            c_name=msg_name,
+            json_filename=model.json_api_files,
+            json_definition=msg.doc,
+            plugin_name=model.plugin_name,
+            err_handler=_generate_error_handler(msg),
+            class_ref_name=msg.java_name_lower,
+            dto_name=msg.java_name_upper,
+            dto_setters=generate_c2j_swap(msg, object_ref_name="dto", struct_ref_name="mp")
+        ))
+    return "".join(jni_impl)
+
+_MSG_HANDLER_TEMPLATE = Template("""
+/**
+ * Handler for ${c_name} message.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static void vl_api_${c_name}_t_handler (vl_api_${c_name}_t * mp)
+{
+    ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
+    JNIEnv *env = jvpp_main.jenv;
+    jthrowable exc;
+$err_handler
+
+    if (CLIB_DEBUG > 1)
+        clib_warning ("Received ${c_name} event message");
+
+    jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
+
+    // User does not have to provide callbacks for all VPP messages.
+    // We are ignoring messages that are not supported by user.
+    (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared
+    jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V");
+    exc = (*env)->ExceptionOccurred(env);
+    if (exc) {
+        clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n");
+        (*env)->ExceptionDescribe(env);
+        (*env)->ExceptionClear(env);
+        return;
+    }
+
+    jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
+$dto_setters
+
+    (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
+    // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni
+    (*env)->DeleteLocalRef(env, dto);
+}""")
+
+
+def _generate_error_handler(msg):
+    err_handler = ""
+    for field in msg.fields:
+        if is_retval(field):
+            err_handler = _ERR_HANDLER_TEMPLATE.substitute(name=msg.name)
+    return err_handler
+
+# Code fragment for checking result of the operation before sending request reply.
+# Error checking is optional (some messages, e.g. detail messages do not have retval field).
+_ERR_HANDLER_TEMPLATE = Template("""
+    // for negative result don't send callback message but send error callback
+    if (mp->retval<0) {
+        call_on_error("${name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass);
+        return;
+    }
+    if (mp->retval == VNET_API_ERROR_IN_PROGRESS) {
+        clib_warning("Result in progress");
+        return;
+    }""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
new file mode 100755
index 0000000..a76aadf
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+#
+from string import Template
+
+from jni_common_gen import generate_j2c_swap, generate_j2c_field_swap, generate_j2c_identifiers, generate_c2j_swap
+from jvpp_model import Class, Enum, Union
+
+
+def generate_type_handlers(model, logger):
+    """
+    Generates host-to-net and net-to-host functions for all custom types defined in the VPP API
+    :param model: meta-model of VPP API used for jVPP generation.
+    :param logger: jVPP logger
+    """
+    type_handlers = []
+    for t in model.types:
+        #TODO(VPP-1186): move the logic to JNI generators
+        if isinstance(t, Class):
+            _generate_class(model, t, type_handlers)
+        elif isinstance(t, Enum):
+            _generate_enum(model, t, type_handlers)
+        elif isinstance(t, Union):
+            _generate_union(model, t, type_handlers)
+        else:
+            logger.debug("Skipping custom JNI type handler generation for %s", t)
+
+    return "\n".join(type_handlers)
+
+
+def _generate_class(model, t, type_handlers):
+    ref_name = t.java_name_lower
+    type_handlers.append(_TYPE_HOST_TO_NET_TEMPLATE.substitute(
+        c_name=t.name,
+        json_filename=model.json_api_files,
+        json_definition=t.doc,
+        type_reference_name=ref_name,
+        class_FQN=t.jni_name,
+        jni_identifiers=generate_j2c_identifiers(t, class_ref_name="%sClass" % ref_name, object_ref_name="_host"),
+        type_swap=generate_j2c_swap(t, struct_ref_name="_net")
+    ))
+    type_handlers.append(_TYPE_NET_TO_HOST_TEMPLATE.substitute(
+        c_name=t.name,
+        json_filename=model.json_api_files,
+        json_definition=t.doc,
+        type_reference_name=ref_name,
+        class_FQN=t.jni_name,
+        type_swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
+    ))
+
+_TYPE_HOST_TO_NET_TEMPLATE = Template("""
+/**
+ * Host to network byte order conversion for ${c_name} type.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
+{
+    jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
+$jni_identifiers
+$type_swap
+}""")
+
+_TYPE_NET_TO_HOST_TEMPLATE = Template("""
+/**
+ * Network to host byte order conversion for ${c_name} type.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host)
+{
+    jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
+$type_swap
+}""")
+
+
+def _generate_enum(model, t, type_handlers):
+    value_type = t.value.type
+    type_handlers.append(_ENUM_NET_TO_HOST_TEMPLATE.substitute(
+        c_name=t.name,
+        json_filename=model.json_api_files,
+        json_definition=t.doc,
+        class_FQN=t.jni_name,
+        jni_signature=value_type.jni_signature,
+        jni_type=value_type.jni_type,
+        jni_accessor=value_type.jni_accessor,
+        swap=_generate_scalar_host_to_net_swap(t.value)
+    ))
+
+    type_handlers.append(_ENUM_HOST_TO_NET_TEMPLATE.substitute(
+        c_name=t.name,
+        json_filename=model.json_api_files,
+        json_definition=t.doc,
+        class_FQN=t.jni_name,
+        jni_type=value_type.jni_type,
+        type_swap=_generate_scalar_net_to_host_swap(t.value)
+    ))
+
+_ENUM_NET_TO_HOST_TEMPLATE = Template("""
+/**
+ * Host to network byte order conversion for ${c_name} enum.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
+{
+    jclass enumClass = (*env)->FindClass(env, "${class_FQN}");
+    jfieldID valueFieldId = (*env)->GetStaticFieldID(env, enumClass, "value", "${jni_signature}");
+    ${jni_type} value = (*env)->GetStatic${jni_accessor}Field(env, enumClass, valueFieldId);
+    ${swap};
+}""")
+
+_ENUM_HOST_TO_NET_TEMPLATE = Template("""
+/**
+ * Network to host byte order conversion for ${c_name} type.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline ${jni_type} _net_to_host_${c_name}(vl_api_${c_name}_t _net)
+{
+    return (${jni_type}) $type_swap
+}""")
+
+
+def _generate_scalar_host_to_net_swap(field):
+    field_type = field.type
+    if field_type.is_swap_needed:
+        return field_type.get_host_to_net_function(field.java_name, "*_net")
+    else:
+        return "*_net = %s" % field.java_name
+
+
+def _generate_scalar_net_to_host_swap(field):
+    field_type = field.type
+    if field_type.is_swap_needed:
+        return "%s((%s) _net);" % (field_type.net_to_host_function, field_type.name)
+    else:
+        return "_net"
+
+
+def _generate_union(model, t, type_handlers):
+    type_handlers.append(_generate_union_host_to_net(model, t))
+    type_handlers.append(_generate_union_net_to_host(model, t))
+
+
+def _generate_union_host_to_net(model, t):
+    swap = []
+    for i, field in enumerate(t.fields):
+        field_type = field.type
+        swap.append(_UNION_FIELD_HOST_TO_NET_TEMPLATE.substitute(
+            field_index=i,
+            java_name=field.java_name,
+            jni_signature=field_type.jni_signature,
+            jni_type=field_type.jni_type,
+            jni_accessor=field_type.jni_accessor,
+            swap=generate_j2c_field_swap(field, struct_ref_name="_net")
+        ))
+
+    return _UNION_HOST_TO_NET_TEMPLATE.substitute(
+        c_name=t.name,
+        json_filename=model.json_api_files,
+        json_definition=t.doc,
+        class_FQN=t.jni_name,
+        swap="".join(swap)
+    )
+
+_UNION_FIELD_HOST_TO_NET_TEMPLATE = Template("""
+    if (_activeMember == ${field_index}) {
+        jfieldID fieldId = (*env)->GetFieldID(env, _class, "${java_name}", "${jni_signature}");
+        ${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, _host, fieldId);
+    ${swap}
+    }""")
+
+_UNION_HOST_TO_NET_TEMPLATE = Template("""
+/**
+ * Host to network byte order conversion for ${c_name} union.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
+{
+    jclass _class = (*env)->FindClass(env, "${class_FQN}");
+
+    jfieldID _activeMemberFieldId = (*env)->GetFieldID(env, _class, "_activeMember", "I");
+    jint _activeMember = (*env)->GetIntField(env, _host, _activeMemberFieldId);
+$swap
+}""")
+
+
+def _generate_union_net_to_host(model, t):
+    return _UNION_NET_TO_HOST_TEMPLATE.substitute(
+        c_name=t.name,
+        json_filename=model.json_api_files,
+        json_definition=t.doc,
+        type_reference_name=t.java_name_lower,
+        class_FQN=t.jni_name,
+        swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
+    )
+
+_UNION_NET_TO_HOST_TEMPLATE = Template("""
+/**
+ * Network to host byte order conversion for ${c_name} union.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host)
+{
+    jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
+$swap
+}""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py
new file mode 100644
index 0000000..c5634de
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py
@@ -0,0 +1,289 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+from string import Template
+
+from jvpp_model import is_control_ping, is_dump, is_request, is_event, is_control_ping_reply
+
+
+def generate_callback_facade(work_dir, model, logger):
+    """ Generates callback facade """
+    logger.debug("Generating JVpp callback facade for %s" % model.json_api_files)
+    _generate_ifc(work_dir, model),
+    _generate_impl(work_dir, model)
+    _generate_callback(work_dir, model)
+
+
+def _generate_ifc(work_dir, model):
+    with open("%s/CallbackJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_IFC_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_ifc_methods(model)
+        ))
+
+_IFC_TEMPLATE = Template("""
+package $plugin_package.callfacade;
+
+/**
+ * <p>Callback Java API representation of $plugin_package plugin.
+ * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
+ */
+public interface CallbackJVpp${plugin_name} extends io.fd.vpp.jvpp.notification.EventRegistryProvider, java.lang.AutoCloseable {
+
+    // TODO add send
+
+$methods
+}
+""")
+
+
+def _generate_ifc_methods(model):
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if not (is_dump(msg) or is_request(msg)):
+            # Skip replies and messages that do not not have replies (e.g events/counters).
+            continue
+        template = _IFC_NO_ARG_METHOD_TEMPLATE
+        if msg.has_fields:
+            template = _IFC_METHOD_TEMPLATE
+        methods.append(template.substitute(
+            name=msg.java_name_lower,
+            plugin_package=plugin_package,
+            request=msg.java_name_upper,
+            reply=msg.reply_java
+        ))
+    return "\n".join(methods)
+
+_IFC_NO_ARG_METHOD_TEMPLATE = Template(
+    """    void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+_IFC_METHOD_TEMPLATE = Template(
+    """    void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+
+def _generate_impl(work_dir, model):
+    with open("%s/CallbackJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_IMPL_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_impl_methods(model)
+        ))
+
+_IMPL_TEMPLATE = Template("""
+package $plugin_package.callfacade;
+
+/**
+ * <p>Default implementation of Callback${plugin_name}JVpp interface.
+ * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
+ */
+public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plugin_name} {
+
+    private final $plugin_package.JVpp${plugin_name} jvpp;
+    private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> callbacks;
+    private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl();
+    /**
+     * <p>Create CallbackJVpp${plugin_name}Facade object for provided JVpp instance.
+     * Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks
+     * and then connects to provided JVpp instance
+     *
+     * @param jvpp provided io.fd.vpp.jvpp.JVpp instance
+     *
+     * @throws java.io.IOException in case instance cannot connect to JVPP
+     */
+    public CallbackJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException {
+        this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null");
+        this.callbacks = new java.util.HashMap<>();
+        java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
+        registry.register(jvpp, new CallbackJVpp${plugin_name}FacadeCallback(this.callbacks, eventRegistry));
+    }
+
+    @Override
+    public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() {
+        return eventRegistry;
+    }
+
+    @Override
+    public void close() throws Exception {
+        jvpp.close();
+    }
+
+    // TODO add send()
+
+$methods
+}
+""")
+
+
+def _generate_impl_methods(model):
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if not (is_dump(msg) or is_request(msg)):
+            # Skip replies and messages that do not not have replies (e.g events/counters).
+            continue
+        template = _IMPL_NO_ARG_METHOD_TEMPLATE
+        if msg.has_fields:
+            template = _IMPL_METHOD_TEMPLATE
+        methods.append(template.substitute(
+            name=msg.java_name_lower,
+            plugin_package=plugin_package,
+            request=msg.java_name_upper,
+            reply=msg.reply_java
+        ))
+    return "\n".join(methods)
+
+_IMPL_NO_ARG_METHOD_TEMPLATE = Template(
+    """    public final void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException {
+        synchronized (callbacks) {
+            callbacks.put(jvpp.$name(), callback);
+        }
+    }
+""")
+
+_IMPL_METHOD_TEMPLATE = Template("""    public final void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException {
+        synchronized (callbacks) {
+            callbacks.put(jvpp.$name(request), callback);
+        }
+    }
+""")
+
+
+def _generate_callback(work_dir, model):
+    with open("%s/CallbackJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_CALLBACK_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_callback_methods(model)
+        ))
+
+_CALLBACK_TEMPLATE = Template("""
+package $plugin_package.callfacade;
+
+/**
+ * <p>Implementation of JVppGlobalCallback interface for Java Callback API.
+ * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
+ */
+public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback {
+
+    private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requests;
+    private final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback;
+    private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVpp${plugin_name}FacadeCallback.class.getName());
+
+    public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requestMap,
+                                      final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback) {
+        this.requests = requestMap;
+        this.eventCallback = eventCallback;
+    }
+
+    @Override
+    public void onError(io.fd.vpp.jvpp.VppCallbackException reply) {
+
+        io.fd.vpp.jvpp.callback.JVppCallback failedCall;
+        synchronized(requests) {
+            failedCall = requests.remove(reply.getCtxId());
+        }
+
+        if(failedCall != null) {
+            try {
+                failedCall.onError(reply);
+            } catch(RuntimeException ex) {
+                ex.addSuppressed(reply);
+                LOG.log(java.util.logging.Level.WARNING, String.format("Callback: %s failed while handling exception: %s", failedCall, reply), ex);
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) {
+
+        io.fd.vpp.jvpp.callback.ControlPingCallback callback;
+        final int replyId = reply.context;
+        synchronized(requests) {
+            callback = (io.fd.vpp.jvpp.callback.ControlPingCallback) requests.remove(replyId);
+        }
+
+        if(callback != null) {
+            callback.onControlPingReply(reply);
+        }
+    }
+
+$methods
+}
+""")
+
+
+def _generate_callback_methods(model):
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in model.messages:
+        if is_dump(msg) or is_request(msg):
+            continue
+        if is_control_ping_reply(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+
+        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
+        template = _CALLBACK_METHOD_TEMPLATE
+        if is_event(msg):
+            template = _CALLBACK_EVENT_METHOD_TEMPLATE
+        msg_name = msg.java_name_upper
+        methods.append(template.substitute(
+            message=msg_name,
+            callback="%sCallback" % msg_name,
+            plugin_package=plugin_package
+        ))
+    return "\n".join(methods)
+
+_CALLBACK_METHOD_TEMPLATE = Template("""
+    @Override
+    @SuppressWarnings("unchecked")
+    public void on${message}(final $plugin_package.dto.${message} reply) {
+
+        $plugin_package.callback.$callback callback;
+        final int replyId = reply.context;
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received ${message} event message: %s", reply));
+        }
+        synchronized(requests) {
+            callback = ($plugin_package.callback.$callback) requests.remove(replyId);
+        }
+
+        if(callback != null) {
+            callback.on${message}(reply);
+        }
+    }
+""")
+
+_CALLBACK_EVENT_METHOD_TEMPLATE = Template("""
+    @Override
+    @SuppressWarnings("unchecked")
+    public void on${message}($plugin_package.dto.${message} notification) {
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received ${message} event message: %s", notification));
+        }
+        eventCallback.on${message}(notification);
+    }
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_common_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_common_gen.py
new file mode 100755
index 0000000..83226ea
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_common_gen.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+from string import Template
+
+from jvpp_model import is_array
+
+
+def generate_fields(fields, access_modifier="public"):
+    return "\n".join(_FIELD_TEMPLATE
+                     .substitute(access_modifier=access_modifier, type=f.type.java_name_fqn, name=f.java_name)
+                     for f in fields)
+
+_FIELD_TEMPLATE = Template("""    ${access_modifier} ${type} ${name};""")
+
+
+def generate_hash_code(fields):
+    if len(fields) == 1 and is_array(fields[0]):
+        return _HASH_CODE_SINGLE_ARRAY_TEMPLATE.substitute(array_field=fields[0].java_name)
+    return _HASH_CODE_TEMPLATE.substitute(fields=", ".join(f.java_name for f in fields))
+
+_HASH_CODE_TEMPLATE = Template("""
+    @Override
+    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+    public int hashCode() {
+        return java.util.Objects.hash($fields);
+    }""")
+
+_HASH_CODE_SINGLE_ARRAY_TEMPLATE = Template("""
+    @Override
+    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+    public int hashCode() {
+        return java.util.Arrays.hashCode($array_field);
+    }""")
+
+
+def generate_equals(class_name, fields):
+    comparisons = []
+    for f in fields:
+        if is_array(f):
+            comparisons.append(_EQUALS_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+        else:
+            comparisons.append(_EQUALS_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+
+    if comparisons:
+        comparisons.insert(0, _EQUALS_OTHER_TEMPLATE.substitute(cls_name=class_name))
+    return _EQUALS_TEMPLATE.substitute(comparisons="\n".join(comparisons))
+
+_EQUALS_TEMPLATE = Template("""
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+$comparisons
+
+        return true;
+    }""")
+
+_EQUALS_OTHER_TEMPLATE = Template("""
+        final $cls_name other = ($cls_name) o;
+""")
+
+_EQUALS_FIELD_TEMPLATE = Template("""        if (!java.util.Objects.equals(this.$field_name, other.$field_name)) {
+            return false;
+        }""")
+
+_EQUALS_ARRAY_FIELD_TEMPLATE = Template("""        if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) {
+            return false;
+        }""")
+
+
+def generate_to_string(class_name, fields):
+    to_string = []
+    for f in fields:
+        if is_array(f):
+            to_string.append(_TO_STRING_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+        else:
+            to_string.append(_TO_STRING_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+
+    to_string_fields = " \"}\";"
+    if to_string:
+        to_string_fields = " + \", \" +\n".join(to_string) + " + \"}\";"
+
+    return _TO_STRING_TEMPLATE.substitute(
+        class_name=class_name,
+        to_string_fields=to_string_fields
+    )
+
+_TO_STRING_TEMPLATE = Template("""
+    @Override
+    public String toString() {
+        return "$class_name{" +
+$to_string_fields
+    }""")
+
+_TO_STRING_FIELD_TEMPLATE = Template("""                \"$field_name=\" + $field_name""")
+
+_TO_STRING_ARRAY_FIELD_TEMPLATE = Template(
+    """                \"$field_name=\" + java.util.Arrays.toString($field_name)""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py
new file mode 100644
index 0000000..47a9985
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py
@@ -0,0 +1,338 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+from string import Template
+
+from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request, is_details, is_reply, is_event
+
+
+def generate_future_facade(work_dir, model, logger):
+    logger.debug("Generating JVpp future facade for %s" % model.json_api_files)
+    _generate_future_jvpp(work_dir, model),
+    _generate_future_jvpp_facade(work_dir, model)
+    _generate_future_jvpp_callback(work_dir, model)
+
+
+def _generate_future_jvpp(work_dir, model):
+    with open("%s/FutureJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_FUTURE_JVPP_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_future_jvpp_methods(model)
+        ))
+
+_FUTURE_JVPP_TEMPLATE = Template('''
+package $plugin_package.future;
+
+/**
+ * <p>Async facade extension adding specific methods for each request invocation
+ * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename.
+ */
+public interface FutureJVpp${plugin_name} extends io.fd.vpp.jvpp.future.FutureJVppInvoker {
+$methods
+
+    @Override
+    public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry();
+
+}
+''')
+
+
+def _generate_future_jvpp_methods(model):
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        reply_name = None
+        if is_request(msg):
+            reply_name = msg.reply_java
+        elif is_dump(msg):
+            # use reply dump wrappers
+            reply_name = "%sReplyDump" % msg.reply_java
+        else:
+            continue
+
+        methods.append(_FUTURE_JVPP_METHOD_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            method_name=msg.java_name_lower,
+            reply_name=reply_name,
+            request_name=msg.java_name_upper
+        ))
+    return "".join(methods)
+
+_FUTURE_JVPP_METHOD_TEMPLATE = Template('''
+    java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}> ${method_name}(${plugin_package}.dto.${request_name} request);
+''')
+
+
+def _generate_future_jvpp_facade(work_dir, model):
+    with open("%s/FutureJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_FUTURE_JVPP_FACADE_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_future_jvpp_facade_methods(model)
+        ))
+
+_FUTURE_JVPP_FACADE_TEMPLATE = Template('''
+package $plugin_package.future;
+
+/**
+ * <p>Implementation of FutureJVpp based on AbstractFutureJVppInvoker
+ * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename.
+ */
+public class FutureJVpp${plugin_name}Facade extends io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker implements FutureJVpp${plugin_name} {
+
+    private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl();
+
+    /**
+     * <p>Create FutureJVpp${plugin_name}Facade object for provided JVpp instance.
+     * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks
+     * and then connects to provided JVpp instance
+     *
+     * @param jvpp provided io.fd.vpp.jvpp.JVpp instance
+     *
+     * @throws java.io.IOException in case instance cannot connect to JVPP
+     */
+    public FutureJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final io.fd.vpp.jvpp.JVpp jvpp) throws java.io.IOException {
+        super(jvpp, registry, new java.util.HashMap<>());
+        java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
+        registry.register(jvpp, new FutureJVpp${plugin_name}FacadeCallback(getRequests(), eventRegistry));
+    }
+
+    @Override
+    public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() {
+        return eventRegistry;
+    }
+
+$methods
+}
+''')
+
+
+def _generate_future_jvpp_facade_methods(model):
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        template = None
+        if is_request(msg):
+            template = _FUTURE_JVPP_FACADE_REQUEST_TEMPLATE
+        elif is_dump(msg):
+            template = _FUTURE_JVPP_FACADE_DUMP_TEMPLATE
+        else:
+            continue
+
+        methods.append(template.substitute(
+            plugin_package=model.plugin_package,
+            method_name=msg.java_name_lower,
+            reply_name=msg.reply_java,
+            request_name=msg.java_name_upper
+        ))
+    return "".join(methods)
+
+_FUTURE_JVPP_FACADE_REQUEST_TEMPLATE = Template('''
+    @Override
+    public java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}> ${method_name}(${plugin_package}.dto.${request_name} request) {
+        return send(request);
+    }
+''')
+
+_FUTURE_JVPP_FACADE_DUMP_TEMPLATE = Template('''
+    @Override
+    public java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}ReplyDump> ${method_name}(${plugin_package}.dto.${request_name} request) {
+        return send(request, new ${plugin_package}.dto.${reply_name}ReplyDump());
+    }
+''')
+
+
+def _generate_future_jvpp_callback(work_dir, model):
+    with open("%s/FutureJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_FUTURE_JVPP_CALLBACK_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_future_jvpp_callback_methods(model)
+        ))
+
+_FUTURE_JVPP_CALLBACK_TEMPLATE = Template("""
+package $plugin_package.future;
+
+/**
+ * <p>Async facade callback setting values to future objects
+ * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename.
+ */
+public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback {
+
+    private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends io.fd.vpp.jvpp.dto.JVppReply<?>>> requests;
+    private final $plugin_package.notification.Global${plugin_name}EventCallback notificationCallback;
+    private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(FutureJVpp${plugin_name}FacadeCallback.class.getName());
+
+    public FutureJVpp${plugin_name}FacadeCallback(
+        final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends io.fd.vpp.jvpp.dto.JVppReply<?>>> requestMap,
+        final $plugin_package.notification.Global${plugin_name}EventCallback notificationCallback) {
+        this.requests = requestMap;
+        this.notificationCallback = notificationCallback;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onError(io.fd.vpp.jvpp.VppCallbackException reply) {
+        final java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>> completableFuture;
+
+        synchronized(requests) {
+            completableFuture = (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>>) requests.get(reply.getCtxId());
+        }
+
+        if(completableFuture != null) {
+            completableFuture.completeExceptionally(reply);
+
+            synchronized(requests) {
+                requests.remove(reply.getCtxId());
+            }
+        }
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) {
+        java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>> completableFuture;
+
+        final int replyId = reply.context;
+        synchronized(requests) {
+            completableFuture = (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>>) requests.get(replyId);
+
+            if(completableFuture != null) {
+                // Finish dump call
+                if (completableFuture instanceof io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) {
+                    completableFuture.complete(((io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getReplyDump());
+                    // Remove future mapped to dump call context id
+                    requests.remove(((io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getContextId());
+                } else {
+                    // reply to regular control ping, complete the future
+                    completableFuture.complete(reply);
+                }
+                requests.remove(replyId);
+            } else {
+                // future not yet created by writer, create new future, complete it and put to map under ping id
+                completableFuture = new java.util.concurrent.CompletableFuture<>();
+                completableFuture.complete(reply);
+                requests.put(replyId, completableFuture);
+            }
+        }
+    }
+
+$methods
+}
+""")
+
+
+def _generate_future_jvpp_callback_methods(model):
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+
+        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
+        template = None
+        request_dto = None
+        if is_details(msg):
+            template = _FUTURE_JVPP_FACADE_DETAILS_CALLBACK_TEMPLATE
+            request_dto = msg.request_java
+        elif is_reply(msg):
+            template = _FUTURE_JVPP_FACADE_REPLY_CALLBACK_TEMPLATE
+            request_dto = msg.request_java
+        elif is_event(msg):
+            template = _FUTURE_JVPP_FACADE_EVENT_CALLBACK_TEMPLATE
+        else:
+            raise TypeError("Unknown message type %s", msg)
+
+        methods.append(template.substitute(
+            plugin_package=model.plugin_package,
+            callback_dto=msg.java_name_upper,
+            request_dto=request_dto,
+            callback_dto_field=msg.java_name_lower,
+        ))
+    return "".join(methods)
+
+
+_FUTURE_JVPP_FACADE_DETAILS_CALLBACK_TEMPLATE = Template("""
+    @Override
+    @SuppressWarnings("unchecked")
+    public void on$callback_dto(final $plugin_package.dto.$callback_dto reply) {
+        io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.dto.${callback_dto}ReplyDump> completableFuture;
+        final int replyId = reply.context;
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received $callback_dto event message: %s", reply));
+        }
+        synchronized(requests) {
+            completableFuture = (io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.dto.${callback_dto}ReplyDump>) requests.get(replyId);
+
+            if(completableFuture == null) {
+                // reply received before writer created future,
+                // create new future, and put into map to notify sender that reply is already received,
+                // following details replies will add information to this future
+                completableFuture = new io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<>(replyId,
+                    new $plugin_package.dto.${callback_dto}ReplyDump());
+                requests.put(replyId, completableFuture);
+            }
+            completableFuture.getReplyDump().$callback_dto_field.add(reply);
+        }
+    }
+""")
+
+_FUTURE_JVPP_FACADE_REPLY_CALLBACK_TEMPLATE = Template("""
+    @Override
+    @SuppressWarnings("unchecked")
+    public void on$callback_dto(final $plugin_package.dto.$callback_dto reply) {
+        java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_dto>> completableFuture;
+        final int replyId = reply.context;
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received $callback_dto event message: %s", reply));
+        }
+        synchronized(requests) {
+            completableFuture =
+            (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_dto>>) requests.get(replyId);
+
+            if(completableFuture != null) {
+                // received reply on request, complete future created by sender and remove it from map
+                completableFuture.complete(reply);
+                requests.remove(replyId);
+            } else {
+                // reply received before writer created future,
+                // create new future, complete it and put into map to
+                // notify sender that reply is already received
+                completableFuture = new  java.util.concurrent.CompletableFuture<>();
+                completableFuture.complete(reply);
+                requests.put(replyId, completableFuture);
+            }
+        }
+    }
+""")
+
+_FUTURE_JVPP_FACADE_EVENT_CALLBACK_TEMPLATE = Template("""
+    @Override
+    public void on$callback_dto($plugin_package.dto.$callback_dto notification) {
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received $callback_dto event message: %s", notification));
+        }
+        notificationCallback.on$callback_dto(notification);
+    }
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py
new file mode 100755
index 0000000..e2b2922
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+#
+from string import Template
+
+from jvpp_model import is_request, is_dump, is_event
+
+
+def generate_java_ifc(work_dir, model, logger):
+    logger.debug("Generating JVpp interface for %s" % model.json_api_files)
+    messages = filter(_jvpp_ifc_filter, model.messages)
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in messages:
+        if msg.has_fields:
+            methods.append(_JVPP_IFC_METHOD_TEMPLATE.substitute(
+                name=msg.java_name_lower,
+                plugin_package=plugin_package,
+                type=msg.java_name_upper))
+        else:
+            methods.append(_JVPP_IFC_NO_ARG_METHOD_TEMPLATE.substitute(name=msg.java_name_lower))
+
+    plugin_name = model.plugin_java_name
+    jvpp_interface = _JVPP_IFC_TEMPLATE.substitute(
+        plugin_package=plugin_package,
+        json_filename=model.json_api_files,
+        plugin_name=plugin_name,
+        methods="\n".join(methods)
+    )
+    with open("%s/JVpp%s.java" % (work_dir, plugin_name), "w") as f:
+        f.write(jvpp_interface)
+
+
+def _jvpp_ifc_filter(msg):
+    return is_request(msg) or is_dump(msg) or is_event(msg)
+
+
+_JVPP_IFC_METHOD_TEMPLATE = Template(
+    """    int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+_JVPP_IFC_NO_ARG_METHOD_TEMPLATE = Template("""    int $name() throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+_JVPP_IFC_TEMPLATE = Template("""package $plugin_package;
+
+/**
+ * <p>Java representation of plugin's api file.
+ * <br>It was generated by jvpp_impl_gen.py based on $json_filename.
+ * <br>(python representation of api file generated by vppapigen)
+ */
+public interface JVpp${plugin_name} extends io.fd.vpp.jvpp.JVpp {
+    /**
+     * Generic dispatch method for sending requests to VPP
+     *
+     * @throws io.fd.vpp.jvpp.VppInvocationException if send request had failed
+     */
+    int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException;
+$methods
+}
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_impl_gen.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_impl_gen.py
new file mode 100755
index 0000000..aff8d7f
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_impl_gen.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+#
+from string import Template
+
+from jvpp_model import is_request, is_dump, is_event
+
+
+def generate_java_impl(work_dir, model, logger):
+    logger.debug("Generating JVpp implementation for %s" % model.json_api_files)
+    messages = filter(_jvpp_impl_filter, model.messages)
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in messages:
+        if msg.has_fields:
+            methods.append(_JVPP_IMPL_METHOD_TEMPLATE.substitute(
+                name=msg.java_name_lower,
+                plugin_package=plugin_package,
+                type=msg.java_name_upper))
+        else:
+            methods.append(_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE.substitute(
+                name=msg.java_name_lower,
+                type=msg.java_name_upper))
+
+    plugin_name = model.plugin_java_name
+    jvpp_impl = _JVPP_IMPL_TEMPLATE.substitute(
+        plugin_package=plugin_package,
+        json_filename=model.json_api_files,
+        plugin_name=model.plugin_java_name,
+        plugin_name_underscore=model.plugin_name,
+        methods="\n".join(methods))
+
+    with open("%s/JVpp%sImpl.java" % (work_dir, plugin_name), "w") as f:
+        f.write(jvpp_impl)
+
+
+def _jvpp_impl_filter(msg):
+    return is_request(msg) or is_dump(msg) or is_event(msg)
+
+
+_JVPP_IMPL_TEMPLATE = Template("""package $plugin_package;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+import io.fd.vpp.jvpp.callback.JVppCallback;
+import io.fd.vpp.jvpp.VppConnection;
+import io.fd.vpp.jvpp.JVppRegistry;
+
+/**
+ * <p>Default implementation of JVpp interface.
+ * <br>It was generated by jvpp_impl_gen.py based on $json_filename.
+ * <br>(python representation of api file generated by vppapigen)
+ */
+public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugin_name} {
+
+    private final static Logger LOG = Logger.getLogger(JVpp${plugin_name}Impl.class.getName());
+    private static final String LIBNAME = "libjvpp_${plugin_name_underscore}.so";
+
+    // FIXME using NativeLibraryLoader makes load fail could not find (WantInterfaceEventsReply).
+    static {
+        try {
+            loadLibrary();
+        } catch (Exception e) {
+            LOG.severe("Can't find jvpp jni library: " + LIBNAME);
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    private static void loadStream(final InputStream is) throws IOException {
+        final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
+        final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
+        try {
+            Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
+
+            try {
+                Runtime.getRuntime().load(p.toString());
+            } catch (UnsatisfiedLinkError e) {
+                throw new IOException("Failed to load library " + p, e);
+            }
+        } finally {
+            try {
+                Files.deleteIfExists(p);
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private static void loadLibrary() throws IOException {
+        try (final InputStream is = JVpp${plugin_name}Impl.class.getResourceAsStream('/' + LIBNAME)) {
+            if (is == null) {
+                throw new IOException("Failed to open library resource " + LIBNAME);
+            }
+            loadStream(is);
+        }
+    }
+
+    private VppConnection connection;
+    private JVppRegistry registry;
+
+    private static native void init0(final JVppCallback callback, final long queueAddress, final int clientIndex);
+    @Override
+    public void init(final JVppRegistry registry, final JVppCallback callback, final long queueAddress, final int clientIndex) {
+        this.registry = java.util.Objects.requireNonNull(registry, "registry should not be null");
+        this.connection = java.util.Objects.requireNonNull(registry.getConnection(), "connection should not be null");
+        connection.checkActive();
+        init0(callback, queueAddress, clientIndex);
+    }
+
+    private static native void close0();
+    @Override
+    public void close() {
+        close0();
+    }
+
+    @Override
+    public int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException {
+        return request.send(this);
+    }
+
+    @Override
+    public final int controlPing(final io.fd.vpp.jvpp.dto.ControlPing controlPing) throws io.fd.vpp.jvpp.VppInvocationException {
+        return registry.controlPing(JVpp${plugin_name}Impl.class);
+    }
+$methods
+}
+""")
+
+_JVPP_IMPL_METHOD_TEMPLATE = Template("""
+    private static native int ${name}0($plugin_package.dto.$type request);
+    public final int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException {
+        java.util.Objects.requireNonNull(request, "Null request object");
+        connection.checkActive();
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine(String.format("Sending $type event message: %s", request));
+        }
+        int result=${name}0(request);
+        if (result<0){
+            throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result);
+        }
+        return result;
+    }""")
+
+_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE = Template("""
+    private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException;
+    public final int $name() throws io.fd.vpp.jvpp.VppInvocationException {
+        connection.checkActive();
+        LOG.fine("Sending $type event message");
+        int result=${name}0();
+        if(result<0){
+            throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result);
+        }
+        return result;
+    }""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py
new file mode 100755
index 0000000..299796b
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py
@@ -0,0 +1,570 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 json
+import pprint
+from collections import OrderedDict
+
+BASE_PACKAGE = "io.fd.vpp.jvpp"
+
+
+class ParseException(Exception):
+    pass
+
+
+class Type(object):
+    def __init__(self, name, java_name, java_name_fqn, jni_signature, jni_type, jni_accessor,
+                 host_to_net_function, net_to_host_function):
+        """
+        Initializes Type class.
+
+        :param name: name of type as defined in .api file, e.g. u8, u32[] or mac_entry
+        :param java_name: corresponding java name, e.g. byte, int[] or MacEntry
+        :param java_name_fqn: fully qualified java name, e.g. io.fd.vpp.jvpp.core.types.MacEntry
+        :param jni_signature: JNI Type signature, e.g. B, [I or Lio.fd.vpp.jvpp.core.types.MacEntry;
+                              See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures
+        :param jni_type: JNI reference type, e.g. jbyte jintArray, jobject
+                         See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#reference_types
+        :param jni_accessor: Java type do by used in Get<type>Field, Set<type>Field and other functions.
+                             See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#accessing_fields_of_objects
+        :param host_to_net_function: name of function host to net byte order swap function
+        :param net_to_host_function: name of function net to host byte order swap function
+        """
+        self.name = name
+        self.java_name = java_name
+
+        # Java generation specific properties, TODO(VPP-1186): move to Java specific subclass
+        self.java_name_fqn = java_name_fqn
+
+        # JNI generation specific properties, TODO(VPP-1186): move to JNI specific subclass
+        self.jni_signature = jni_signature
+
+        # Native type, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#primitive_types
+        # and
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#reference_types
+        self.jni_type = jni_type
+
+        # Java type do by used in Get<type>Field, Set<type>Field and other functions, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#accessing_fields_of_objects
+        self.jni_accessor = jni_accessor
+
+        self.host_to_net_function = host_to_net_function
+        self.net_to_host_function = net_to_host_function
+        self.is_swap_needed = host_to_net_function and net_to_host_function
+
+
+class SimpleType(Type):
+    def __init__(self, name, java_name, jni_signature, jni_type, jni_accessor,
+                 host_to_net_function=None, net_to_host_function=None):
+        super(SimpleType, self).__init__(
+            name=name,
+            java_name=java_name,
+            java_name_fqn=java_name,
+            jni_signature=jni_signature,
+            jni_type=jni_type,
+            jni_accessor=jni_accessor,
+            host_to_net_function=host_to_net_function,
+            net_to_host_function=net_to_host_function
+        )
+        self.vpp_name = name
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return "%s = %s(%s)" % (net_ref_name, self.host_to_net_function, host_ref_name)
+
+    def __str__(self):
+        return "SimpleType{name:%s, java_name:%s}" % (self.name, self.java_name)
+
+
+# TODO(VPP-1187): add array host to net functions to reduce number of members and simplify JNI generation
+class Array(Type):
+    def __init__(self, base_type):
+        super(Array, self).__init__(
+            name=base_type.name + _ARRAY_SUFFIX,
+            java_name=base_type.java_name + _ARRAY_SUFFIX,
+            java_name_fqn=base_type.java_name_fqn + _ARRAY_SUFFIX,
+            jni_signature="[%s" % base_type.jni_signature,
+            jni_type="%sArray" % base_type.jni_type,
+            jni_accessor="Object",
+            host_to_net_function=base_type.host_to_net_function,
+            net_to_host_function=base_type.net_to_host_function
+        )
+        self.base_type = base_type
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return self.base_type.get_host_to_net_function(host_ref_name, net_ref_name)
+
+    def __str__(self):
+        return "Array{name:%s, java_name:%s}" % (self.name, self.java_name)
+
+
+class Enum(Type):
+    def __init__(self, name, value, constants, definition, plugin_name):
+        _java_name = _underscore_to_camelcase_upper(name)
+
+        super(Enum, self).__init__(
+            name=name,
+            java_name=_java_name,
+            java_name_fqn="io.fd.vpp.jvpp.%s.types.%s" % (plugin_name, _java_name),
+            jni_signature="Lio/fd/vpp/jvpp/%s/types/%s;" % (plugin_name, _java_name),
+            jni_type="jobject",
+            jni_accessor="Object",
+            host_to_net_function="_host_to_net_%s" % name,
+            net_to_host_function="_net_to_host_%s" % name
+        )
+
+        self.value = value
+        self.constants = constants
+        self.doc = _message_to_javadoc(definition)
+        self.java_name_lower = _underscore_to_camelcase_lower(name)
+        self.vpp_name = "%s%s%s" % (_VPP_TYPE_PREFIX, name, _VPP_TYPE_SUFFIX)
+        # Fully qualified class name used by FindClass function, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass
+        self.jni_name = "io/fd/vpp/jvpp/%s/types/%s" % (plugin_name, _java_name)
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name)
+
+
+class Class(Type):
+    def __init__(self, name, crc, fields, definition, plugin_name):
+        _java_name = _underscore_to_camelcase_upper(name)
+
+        super(Class, self).__init__(
+            name=name,
+            java_name=_java_name,
+            java_name_fqn="io.fd.vpp.jvpp.%s.types.%s" % (plugin_name, _java_name),
+            jni_signature="Lio/fd/vpp/jvpp/%s/types/%s;" % (plugin_name, _java_name),
+            jni_type="jobject",
+            jni_accessor="Object",
+            host_to_net_function="_host_to_net_%s" % name,
+            net_to_host_function="_net_to_host_%s" % name
+        )
+
+        self.crc = crc
+        self.fields = fields
+        self.doc = _message_to_javadoc(definition)
+        self.java_name_lower = _underscore_to_camelcase_lower(name)
+        self.vpp_name = "%s%s%s" % (_VPP_TYPE_PREFIX, name, _VPP_TYPE_SUFFIX)
+        # Fully qualified class name used by FindClass function, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass
+        self.jni_name = "io/fd/vpp/jvpp/%s/types/%s" % (plugin_name, _java_name)
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name)
+
+
+class Union(Type):
+    def __init__(self, name, crc, fields, definition, plugin_name):
+        _java_name = _underscore_to_camelcase_upper(name)
+
+        super(Union, self).__init__(
+            name=name,
+            java_name=_java_name,
+            java_name_fqn="io.fd.vpp.jvpp.%s.types.%s" % (plugin_name, _java_name),
+            jni_signature="Lio/fd/vpp/jvpp/%s/types/%s;" % (plugin_name, _java_name),
+            jni_type="jobject",
+            jni_accessor="Object",
+            host_to_net_function="_host_to_net_%s" % name,
+            net_to_host_function="_net_to_host_%s" % name
+        )
+
+        self.crc = crc
+        self.fields = fields
+        self.doc = _message_to_javadoc(definition)
+        self.java_name_lower = _underscore_to_camelcase_lower(name)
+        self.vpp_name = "%s%s%s" % (_VPP_TYPE_PREFIX, name, _VPP_TYPE_SUFFIX)
+        # Fully qualified class name used by FindClass function, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass
+        self.jni_name = "io/fd/vpp/jvpp/%s/types/%s" % (plugin_name, _java_name)
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name)
+
+
+class Field(object):
+    def __init__(self, name, field_type, array_len=None, array_len_field=None):
+        self.name = name
+        self.java_name = _underscore_to_camelcase_lower(name)
+        self.java_name_upper = _underscore_to_camelcase_upper(name)
+        self.type = field_type
+        self.array_len = array_len
+        self.array_len_field = array_len_field
+
+    def __str__(self):
+        return "Field{name:%s, java_name:%s, type:%s}" % (self.name, self.java_name, self.type)
+
+
+class Message(object):
+    def __init__(self, name, crc, fields, definition):
+        self.name = name
+        self.java_name_upper = _underscore_to_camelcase_upper(name)
+        self.java_name_lower = _underscore_to_camelcase_lower(name)
+        self.crc = crc[2:]
+        self.fields = fields
+        self.has_fields = fields != []
+        self.doc = _message_to_javadoc(definition)
+
+    def __str__(self):
+        return "Message{name:%s, java_name:%s, crc:%s, fields:%s}" % (
+            self.name, self.java_name_upper, self.crc, self.fields)
+
+
+class Event(Message):
+    def __init__(self, name, crc, fields, definition):
+        super(Event, self).__init__(name, crc, fields, definition)
+
+
+class Request(Message):
+    def __init__(self, name, reply, crc, fields, definition):
+        super(Request, self).__init__(name, crc, fields, definition)
+        self.reply = reply
+        self.reply_java = _underscore_to_camelcase_upper(reply)
+
+    def __str__(self):
+        return "Request{name:%s, reply:%s, crc:%s, fields:%s}" % (self.name, self.reply, self.crc, self.fields)
+
+
+class Reply(Message):
+    def __init__(self, name, request, crc, fields, definition):
+        super(Reply, self).__init__(name, crc, fields, definition)
+        self.request = request
+        self.request_java = _underscore_to_camelcase_upper(request)
+
+    def __str__(self):
+        return "Reply{name:%s, request:%s, crc:%s, fields:%s}" % (self.name, self.request, self.crc, self.fields)
+
+
+class Dump(Message):
+    def __init__(self, name, details, crc, fields, definition):
+        super(Dump, self).__init__(name, crc, fields, definition)
+        self.details = details
+        self.reply_java = _underscore_to_camelcase_upper(details)
+
+    def __str__(self):
+        return "Dump{name:%s, details:%s, crc:%s, fields:%s}" % (self.name, self.details, self.crc, self.fields)
+
+
+class Details(Message):
+    def __init__(self, name, dump, crc, fields, definition):
+        super(Details, self).__init__(name, crc, fields, definition)
+        self.dump = dump
+        self.request_java = _underscore_to_camelcase_upper(dump)
+
+    def __str__(self):
+        return "Details{name:%s, dump:%s, crc:%s, fields:%s}" % (self.name, self.dump, self.crc, self.fields)
+
+
+def is_retval(field):
+    return field.name == u'retval'
+
+
+def is_array(field):
+    return field.array_len is not None
+
+
+def is_request(msg):
+    return hasattr(msg, 'reply')
+
+
+def is_reply(msg):
+    return hasattr(msg, 'request')
+
+
+def is_dump(msg):
+    return hasattr(msg, 'details')
+
+
+def is_details(msg):
+    return hasattr(msg, 'dump')
+
+
+def is_event(msg):
+    return isinstance(msg, Event)
+
+
+def is_control_ping(msg):
+    return msg.name == u'control_ping'
+
+
+def is_control_ping_reply(msg):
+    return msg.name == u'control_ping_reply'
+
+
+class JVppModel(object):
+    def __init__(self, logger, json_api_files, plugin_name):
+        self.logger = logger
+        # TODO(VPP-1188): provide json_file_by_definition map to improve javadoc
+        self.json_api_files = json_api_files
+        self.plugin_package = BASE_PACKAGE + "." + plugin_name
+        self.plugin_name = plugin_name
+        self.plugin_java_name = _underscore_to_camelcase_upper(plugin_name)
+        self._load_json_files(json_api_files)
+        self._parse_services()
+        self._parse_messages()
+        self._validate_messages()
+
+    def _load_json_files(self, json_api_files):
+        types = {}
+        self._messages = []
+        self._services = {}
+        for file_name in json_api_files:
+            with open(file_name) as f:
+                j = json.load(f)
+                types.update({d[0]: {'type': 'enum', 'data': d} for d in j['enums']})
+                types.update({d[0]: {'type': 'type', 'data': d} for d in j['types']})
+                types.update({d[0]: {'type': 'union', 'data': d} for d in j['unions']})
+                self._messages.extend(j['messages'])
+                self._services.update(j['services'])
+        self._parse_types(types)
+
+    def _parse_types(self, types):
+        self._parse_simple_types()
+        i = 0
+        while True:
+            unresolved = {}
+            for name, value in types.items():
+                if name in self._types_by_name:
+                    continue
+
+                type = value['type']
+                data = value['data'][1:]
+                try:
+                    if type == 'enum':
+                        type = self._parse_enum(name, data)
+                    elif type == 'union':
+                        type = self._parse_union(name, data)
+                    elif type == 'type':
+                        type = self._parse_type(name, data)
+                    else:
+                        self.logger.warning("Unsupported type %s. Ignoring...", type)
+                        continue
+
+                    self._types_by_name[name] = type
+                    self._types_by_name[name + _ARRAY_SUFFIX] = Array(type)
+                except ParseException as e:
+                    self.logger.debug("Failed to parse %s type in iteration %s: %s.", name, i, e)
+                    unresolved[name] = value
+            if len(unresolved) == 0:
+                break
+            if i > 3:
+                raise ParseException('Unresolved type definitions {}'
+                                     .format(unresolved))
+            types = unresolved
+            i += 1
+
+        self.types = self._types_by_name.values()
+
+    def _parse_simple_types(self):
+        # Mapping according to:
+        # http://docs.oracle.com/javase/7/do+'[]'cs/technotes/guides/jni/spec/types.html
+        # and
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines
+        #
+        # Unsigned types are converted to signed java types that have the same size.
+        # It is the API user responsibility to interpret them correctly.
+
+        self._types_by_name = OrderedDict({
+            'u8': SimpleType('u8', 'byte', 'B', 'jbyte', 'Byte'),
+            'i8': SimpleType('i8', 'byte', 'B', 'jbyte', 'Byte'),
+            'u16': SimpleType('u16', 'short', 'S', 'jshort', 'Short',
+                              host_to_net_function='clib_host_to_net_u16',
+                              net_to_host_function='clib_net_to_host_u16'),
+            'i16': SimpleType('i16', 'short', 'S', 'jshort', 'Short',
+                              host_to_net_function='clib_host_to_net_i16',
+                              net_to_host_function='clib_net_to_host_i16'),
+            'u32': SimpleType('u32', 'int', 'I', 'jint', 'Int',
+                              host_to_net_function='clib_host_to_net_u32',
+                              net_to_host_function='clib_net_to_host_u32'),
+            'i32': SimpleType('i32', 'int', 'I', 'jint', 'Int',
+                              host_to_net_function='clib_host_to_net_i32',
+                              net_to_host_function='clib_net_to_host_i32'),
+            'u64': SimpleType('u64', 'long', 'J', 'jlong', 'Long',
+                              host_to_net_function='clib_host_to_net_u64',
+                              net_to_host_function='clib_net_to_host_u64'),
+            'i64': SimpleType('i64', 'long', 'J', 'jlong', 'Long',
+                              host_to_net_function='clib_host_to_net_i64',
+                              net_to_host_function='clib_net_to_host_i64'),
+            'f64': SimpleType('f64', 'double', 'D', 'jdouble', 'Double')
+        })
+
+        for n, t in self._types_by_name.items():
+            self._types_by_name[n + _ARRAY_SUFFIX] = Array(t)
+
+    def _parse_enum(self, name, definition):
+        self.logger.debug("Parsing enum %s: %s", name, definition)
+        constants = []
+        type_name = None
+        for item in definition:
+            if type(item) is dict and 'enumtype' in item:
+                type_name = item['enumtype']
+                continue
+            constants.append({'name': item[0], 'value': item[1]})
+        if not type_name:
+            raise ParseException("'enumtype' was not defined for %s" % definition)
+        return Enum(name, Field('value', self._types_by_name[type_name]), constants, definition, self.plugin_name)
+
+    def _parse_union(self, name, definition):
+        self.logger.debug("Parsing union %s: %s", name, definition)
+        crc, fields = self._parse_fields(definition)
+        return Union(name, crc, fields, definition, self.plugin_name)
+
+    def _parse_type(self, name, definition):
+        self.logger.debug("Parsing type %s: %s", name, definition)
+        crc, fields = self._parse_fields(definition)
+        return Class(name, crc, fields, definition, self.plugin_name)
+
+    def _parse_services(self):
+        self._dumps_by_details = {}
+        self._requests_by_reply = {}
+        for name, service in self._services.iteritems():
+            if _is_stream(service):
+                self._dumps_by_details[service['reply']] = name
+            else:
+                self._requests_by_reply[service['reply']] = name
+
+    def _parse_messages(self):
+        # Preserve ordering from JSON file to make debugging easier.
+        self._messages_by_name = OrderedDict()
+        for msg in self._messages:
+            try:
+                name = msg[0]
+                definition = msg[1:]
+                self._messages_by_name[name] = self._parse_message(name, definition)
+            except ParseException as e:
+                self.logger.warning("Failed to parse message %s: %s. Skipping message.", name, e)
+
+    def _parse_message(self, name, definition):
+        self.logger.debug("Parsing message %s: %s", name, definition)
+        crc, fields = self._parse_fields(definition)
+        if name in self._services:
+            service = self._services[name]
+            reply = service['reply']
+            if _is_stream(service):
+                return Dump(name, reply, crc, filter(_is_request_field, fields), definition)
+            if reply:
+                return Request(name, reply, crc, filter(_is_request_field, fields), definition)
+            else:
+                return Event(name, crc, filter(_is_request_field, fields), definition)
+        elif name in self._requests_by_reply:
+            return Reply(name, self._requests_by_reply[name], crc, filter(_is_reply_field, fields), definition)
+        elif name in self._dumps_by_details:
+            return Details(name, self._dumps_by_details[name], crc, filter(_is_reply_field, fields), definition)
+        else:
+            # TODO: some messages like combined_counters are not visible in the services.
+            # Throw exception instead (requires fixing vppagigen).
+            return Event(name, crc, filter(_is_request_field, fields), definition)
+
+    def _parse_fields(self, definition):
+        crc = None
+        fields = []
+        for item in definition:
+            if type(item) == dict and 'crc' in item:
+                crc = item['crc']
+            else:
+                fields.append(self._parse_field(item, fields))
+        if not crc:
+            raise ParseException("CRC was not defined for %s" % definition)
+        return crc, fields
+
+    def _parse_field(self, field, fields):
+        type_name = _extract_type_name(field[0])
+        if type_name in self._types_by_name:
+            if len(field) > 2:
+                # Array field
+                array_len_field = None
+                if len(field) == 4:
+                    for f in fields:
+                        if f.name == field[3]:
+                            array_len_field = f
+                    if not array_len_field:
+                        raise ParseException("Could not find field %s declared as length of array %s",
+                                             field[3], field[1])
+                return Field(field[1], self._types_by_name[type_name + _ARRAY_SUFFIX], field[2], array_len_field)
+            else:
+                return Field(field[1], self._types_by_name[type_name])
+        else:
+            raise ParseException("Unknown field type %s" % field)
+
+    def _validate_messages(self):
+        """
+        In case if message A is known to be reply for message B, and message B was not correctly parsed,
+        remove message A from the set of all messages.
+        """
+        to_be_removed = []
+        messages = self._messages_by_name
+        for name, msg in messages.iteritems():
+            if (is_request(msg) and msg.reply not in messages) \
+                    or (is_reply(msg) and msg.request not in messages) \
+                    or (is_dump(msg) and msg.details not in messages) \
+                    or (is_details(msg) and msg.dump not in messages):
+                to_be_removed.append(name)
+
+        for name in to_be_removed:
+            del messages[name]
+
+        self.messages = self._messages_by_name.values()
+
+_ARRAY_SUFFIX = '[]'
+
+
+def _underscore_to_camelcase_upper(name):
+    return name.title().replace("_", "")
+
+
+def _underscore_to_camelcase_lower(name):
+    name = name.title().replace("_", "")
+    return name[0].lower() + name[1:]
+
+
+def _message_to_javadoc(message_definition):
+    """ Converts JSON message definition to javadoc """
+    formatted_message = pprint.pformat(message_definition, indent=4, width=120, depth=None)
+    return " * " + formatted_message.replace("\n", "\n * ")
+
+
+def _is_stream(service):
+    """
+    Checks if service represents stream, e.g.:
+    "ip_address_dump": {
+        "reply": "ip_address_details",
+        "stream": true
+    }
+    :param service: JSON definition of service
+    :return: value assigned to "stream" or None
+    """
+    return "stream" in service
+
+
+def _extract_type_name(name):
+    if name.startswith(_VPP_TYPE_PREFIX) and name.endswith(_VPP_TYPE_SUFFIX):
+        return name[len(_VPP_TYPE_PREFIX): - len(_VPP_TYPE_SUFFIX)]
+    return name
+
+_VPP_TYPE_PREFIX = "vl_api_"
+
+_VPP_TYPE_SUFFIX = "_t"
+
+
+def _is_request_field(field):
+    # Skip fields that are hidden to the jvpp user (handled by JNI layer)
+    return field.name not in {'_vl_msg_id', 'client_index', 'context'}
+
+
+def _is_reply_field(field):
+    # Skip fields that are hidden to the jvpp user:
+    # _vl_msg_id is handled at JNI layer,
+    # Unlike in the request case, context is visible to allow matching replies with requests at Java layer.
+    return field.name not in {'_vl_msg_id'}
diff --git a/extras/japi/java/jvpp/gen/jvppgen/notification_gen.py b/extras/japi/java/jvpp/gen/jvppgen/notification_gen.py
new file mode 100644
index 0000000..69e870e
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/notification_gen.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+from string import Template
+
+from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request
+
+
+def generate_notifications(work_dir, model, logger):
+    """ Generates notification registry interface and implementation """
+    logger.debug("Generating Notification interfaces and implementation for %s" % model.json_api_files)
+    messages = filter(_notification_filter, model.messages)
+    _generate_global_event_callback(work_dir, model, messages)
+    _generate_event_registry(work_dir, model, messages)
+    _generate_event_registry_impl(work_dir, model, messages)
+    _generate_event_registry_provider(work_dir, model)
+
+
+def _notification_filter(msg):
+    # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
+    # Also skip control ping managed by jvpp registry.
+    return (not is_control_ping(msg)) and \
+           (not is_control_ping_reply(msg)) and \
+           (not is_dump(msg)) and \
+           (not is_request(msg))
+
+
+def _generate_event_registry(work_dir, model, messages):
+    plugin_name = model.plugin_java_name
+    plugin_package = model.plugin_package
+
+    register_callback_methods = []
+    for msg in messages:
+        name = _callback_name(msg)
+        fqn_name = _fqn_callback_name(plugin_package, name)
+        # 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%s(%s callback);" % (name, fqn_name))
+
+    with open("%s/%sEventRegistry.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_EVENT_REGISTRY_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files,
+            register_callback_methods="\n".join(register_callback_methods)
+        ))
+
+_EVENT_REGISTRY_TEMPLATE = Template("""
+package $plugin_package.notification;
+
+/**
+ * <p>Registry for notification callbacks defined in ${plugin_name}.
+ * <br>It was generated by notification_gen.py based on $json_filename.
+ */
+public interface ${plugin_name}EventRegistry extends io.fd.vpp.jvpp.notification.EventRegistry {
+
+$register_callback_methods
+
+    @Override
+    void close();
+}
+""")
+
+
+def _generate_event_registry_impl(work_dir, model, messages):
+    plugin_name = model.plugin_java_name
+    plugin_package = model.plugin_package
+
+    register_callback_methods = []
+    handler_methods = []
+    for msg in messages:
+        notification = msg.java_name_upper
+        callback = "%sCallback" % notification
+        register_callback_methods.append(_REGISTER_CALLBACK_IMPL_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            notification=notification,
+            callback=callback
+        ))
+        handler_methods.append(_HANDLER_IMPL_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            notification=notification,
+            callback=callback
+        ))
+
+    with open("%s/%sEventRegistryImpl.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_EVENT_REGISTRY_IMPL_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files,
+            register_callback_methods="".join(register_callback_methods),
+            handler_methods="".join(handler_methods)
+        ))
+
+_REGISTER_CALLBACK_IMPL_TEMPLATE = Template("""
+    public java.lang.AutoCloseable register$callback(final $plugin_package.callback.$callback callback){
+        if(null != registeredCallbacks.putIfAbsent($plugin_package.dto.$notification.class, callback)){
+            throw new IllegalArgumentException("Callback for " + $plugin_package.dto.$notification.class +
+                "notification already registered");
+        }
+        return () -> registeredCallbacks.remove($plugin_package.dto.$notification.class);
+    }
+""")
+
+_HANDLER_IMPL_TEMPLATE = Template("""
+    @Override
+    public void on$notification(
+        final $plugin_package.dto.$notification notification) {
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received $notification event message: %s", notification));
+        }
+        final io.fd.vpp.jvpp.callback.JVppCallback jVppCallback = registeredCallbacks.get($plugin_package.dto.$notification.class);
+        if (null != jVppCallback) {
+            (($plugin_package.callback.$callback) registeredCallbacks
+                .get($plugin_package.dto.$notification.class))
+                .on$notification(notification);
+        }
+    }
+""")
+
+_EVENT_REGISTRY_IMPL_TEMPLATE = Template("""
+package $plugin_package.notification;
+
+/**
+ * <p>Notification registry delegating notification processing to registered callbacks.
+ * <br>It was generated by notification_gen.py based on $json_filename.
+ */
+public final class ${plugin_name}EventRegistryImpl implements ${plugin_name}EventRegistry, Global${plugin_name}EventCallback {
+
+    // TODO add a special NotificationCallback interface and only allow those to be registered
+    private final java.util.concurrent.ConcurrentMap<Class<?>, io.fd.vpp.jvpp.callback.JVppCallback> registeredCallbacks =
+        new java.util.concurrent.ConcurrentHashMap<>();
+    private static java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName());
+
+    $register_callback_methods
+    $handler_methods
+
+    @Override
+    public void close() {
+        registeredCallbacks.clear();
+    }
+
+    @Override
+    public void onError(io.fd.vpp.jvpp.VppCallbackException ex) {
+        java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName());
+        LOG.log(java.util.logging.Level.WARNING, String.format("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
+            ex.getCtxId(), ex.getErrorCode()), ex);
+    }
+}
+""")
+
+
+def _generate_global_event_callback(work_dir, model, messages):
+    plugin_name = model.plugin_java_name
+    plugin_package = model.plugin_package
+
+    callbacks = ""
+    callback_list = []
+    for msg in messages:
+        fqn_name = _fqn_callback_name(plugin_package, _callback_name(msg))
+        callback_list.append(fqn_name)
+
+    if callback_list:
+        callbacks = " extends %s" % ", ".join(callback_list)
+
+    with open("%s/Global%sEventCallback.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_GLOBAL_EVENT_CALLBACK_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files,
+            callbacks=callbacks
+        ))
+
+_GLOBAL_EVENT_CALLBACK_TEMPLATE = Template("""
+package $plugin_package.notification;
+
+/**
+ * <p>Aggregated callback interface for notifications only.
+ * <br>It was generated by notification_gen.py based on $json_filename.
+ */
+public interface Global${plugin_name}EventCallback$callbacks {
+
+}
+""")
+
+
+def _generate_event_registry_provider(work_dir, model):
+    plugin_name = model.plugin_java_name
+    with open("%s/%sEventRegistryProvider.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_EVENT_REGISTRY_PROVIDER_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files
+        ))
+
+_EVENT_REGISTRY_PROVIDER_TEMPLATE = Template("""
+package $plugin_package.notification;
+
+ /**
+ * Provides ${plugin_name}EventRegistry.
+ * <br>The file was generated by notification_gen.py based on $json_filename.
+ */
+public interface ${plugin_name}EventRegistryProvider extends io.fd.vpp.jvpp.notification.EventRegistryProvider {
+
+    @Override
+    public ${plugin_name}EventRegistry getEventRegistry();
+}
+""")
+
+
+def _callback_name(msg):
+    return "%sCallback" % msg.java_name_upper
+
+
+def _fqn_callback_name(plugin_package, callback_name):
+    return "%s.callback.%s" % (plugin_package, callback_name)
diff --git a/extras/japi/java/jvpp/gen/jvppgen/types_gen.py b/extras/japi/java/jvpp/gen/jvppgen/types_gen.py
new file mode 100755
index 0000000..3767b53
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/types_gen.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2016,2018 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.
+from string import Template
+
+from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
+from jvpp_model import Class
+
+
+def generate_types(work_dir, model, logger):
+    logger.debug("Generating custom types for %s " % model.json_api_files)
+
+    for t in model.types:
+        if not isinstance(t, Class):
+            continue
+        logger.debug("Generating DTO for type %s", t)
+        type_class_name = t.java_name
+        fields = t.fields
+        type_class = _TYPE_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            c_type_name=t.name,
+            json_filename=model.json_api_files,
+            json_definition=t.doc,
+            java_type_name=type_class_name,
+            fields=generate_fields(fields),
+            hash_code=generate_hash_code(fields),
+            equals=generate_equals(type_class_name, fields),
+            to_string=generate_to_string(type_class_name, fields)
+        )
+        with open("%s/%s.java" % (work_dir, type_class_name), "w") as f:
+            f.write(type_class)
+
+_TYPE_TEMPLATE = Template("""
+package $plugin_package.types;
+
+/**
+ * <p>This class represents $c_type_name type definition.
+ * <br>It was generated by jvpp_types_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class $java_type_name {
+$fields
+$hash_code
+$equals
+$to_string
+}
+""")
diff --git a/extras/japi/java/jvpp/gen/jvppgen/unions_gen.py b/extras/japi/java/jvpp/gen/jvppgen/unions_gen.py
new file mode 100755
index 0000000..f67704f
--- /dev/null
+++ b/extras/japi/java/jvpp/gen/jvppgen/unions_gen.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+
+from string import Template
+
+from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
+from jvpp_model import Union
+
+
+def generate_unions(work_dir, model, logger):
+    logger.debug("Generating unions for %s " % model.json_api_files)
+
+    for t in model.types:
+        if not isinstance(t, Union):
+            continue
+        logger.debug("Generating DTO for union %s", t)
+        java_union_name = t.java_name
+        fields = t.fields
+        type_class = _UNION_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            c_type_name=t.name,
+            json_filename=model.json_api_files,
+            json_definition=t.doc,
+            java_union_name=java_union_name,
+            fields=generate_fields(fields, access_modifier="private"),
+            constructors=_generate_constructors(java_union_name, fields),
+            getters=_generate_getters(fields),
+            hash_code=generate_hash_code(fields),
+            equals=generate_equals(java_union_name, fields),
+            to_string=generate_to_string(java_union_name, fields)
+        )
+        with open("%s/%s.java" % (work_dir, java_union_name), "w") as f:
+            f.write(type_class)
+
+_UNION_TEMPLATE = Template("""
+package ${plugin_package}.types;
+
+/**
+ * <p>This class represents ${c_type_name} union definition.
+ * <br>It was generated by unions_gen.py based on ${json_filename}:
+ * <pre>
+${json_definition}
+ * </pre>
+ */
+public class ${java_union_name} {
+    private final int _activeMember;
+${fields}
+    private ${java_union_name}() {
+        // Constructor for JNI usage. All members can be read.
+        _activeMember = -1;
+    }
+${constructors}
+${getters}
+${hash_code}
+${equals}
+${to_string}
+}
+""")
+
+
+def _generate_constructors(union_name, fields):
+    return "".join(
+        _CONSTRUCTOR_TEMPLATE
+        .substitute(union_name=union_name,
+                    field_type=f.type.java_name_fqn,
+                    field_name=f.java_name,
+                    field_index=i) for i, f in enumerate(fields))
+
+_CONSTRUCTOR_TEMPLATE = Template("""
+    public ${union_name}(${field_type} ${field_name}) {
+        this.${field_name} = java.util.Objects.requireNonNull(${field_name}, "${field_name} should not be null");
+        _activeMember = $field_index;
+    }""")
+
+
+def _generate_getters(fields):
+    return "".join(_GETTER_TEMPLATE.substitute(
+        type=f.type.java_name_fqn,
+        getter_name=f.java_name_upper,
+        field_name=f.java_name
+    ) for f in fields)
+
+_GETTER_TEMPLATE = Template("""
+    public ${type} get${getter_name}() {
+        return ${field_name};
+    }""")
diff --git a/extras/japi/m4/ax_vpp_find_jdk8.m4 b/extras/japi/m4/ax_vpp_find_jdk8.m4
new file mode 100644
index 0000000..94b7850
--- /dev/null
+++ b/extras/japi/m4/ax_vpp_find_jdk8.m4
@@ -0,0 +1,43 @@
+
+
+AC_DEFUN([AX_VPP_FIND_JDK8],
+[
+while true
+do
+  if test "${JAVA_HOME+set}" = set ; then
+    AC_MSG_CHECKING([${JAVA_HOME} for Java 8 compiler])
+    JAVAC=${JAVA_HOME}/bin/javac
+    JAVAH=${JAVA_HOME}/bin/javah
+    JAR=${JAVA_HOME}/bin/jar
+    JAVA_VERSION=$(${JAVA_HOME}/bin/javac -source 8 -version 2>&1)
+    if test 0 -eq "$?"; then
+      JAVA_VERSION=$(echo "${JAVA_VERSION}" | cut -d\  -f2)
+      AC_MSG_RESULT([ok])
+    else
+      AC_MSG_RESULT([no])
+      AC_MSG_ERROR([Java in ${JAVA_HOME} (path specified in JAVA_HOME) cannot compile Java 8 code])
+    fi
+    break
+  fi
+
+  for dir in $(find /usr/lib*/jvm/* -maxdepth 0 -type d); do
+    AC_MSG_CHECKING([${dir} for Java 8 compiler])
+    JAVA_VERSION=$(${dir}/bin/javac -source 8 -version 2>&1)
+    if test 0 -eq "$?"; then
+      JAVA_VERSION=$(echo "${JAVA_VERSION}" | cut -d\  -f2)
+      JAVA_HOME=${dir}
+      JAVAC=${dir}/bin/javac
+      JAVAH=${dir}/bin/javah
+      JAR=${dir}/bin/jar
+      AC_MSG_RESULT([found version $JAVA_VERSION])
+      break
+    else
+      JAVA_VERSION=""
+      AC_MSG_RESULT([no])
+    fi
+  done
+
+  test "${JAVA_HOME}set" = set && AC_MSG_ERROR([Could not find Java 8 compiler])
+  break
+done
+])
diff --git a/extras/rpm/vpp-suse.spec b/extras/rpm/vpp-suse.spec
index ea62eee..7fea9fe 100644
--- a/extras/rpm/vpp-suse.spec
+++ b/extras/rpm/vpp-suse.spec
@@ -227,7 +227,7 @@
 
 # Java bindings
 mkdir -p -m755 %{buildroot}%{_datadir}/java
-for file in $(find %{_vpp_install_dir}/vpp/share/java -type f -name '*.jar' -print )
+for file in $(find %{_vpp_install_dir}/japi/share/java -type f -name '*.jar' -print )
 do
         install -p -m 644 $file %{buildroot}%{_datadir}/java
 done
@@ -258,8 +258,8 @@
 done
 
 mkdir -p -m755 %{buildroot}%{python_sitelib}/jvppgen
-install -p -m755 %{_vpp_build_dir}/../src/vpp-api/java/jvpp/gen/jvpp_gen.py %{buildroot}%{_prefix}/bin
-for i in $(ls %{_vpp_build_dir}/../src/vpp-api/java/jvpp/gen/jvppgen/*.py); do
+install -p -m755 %{_vpp_build_dir}/../extras/japi/java/jvpp/gen/jvpp_gen.py %{buildroot}%{_prefix}/bin
+for i in $(ls %{_vpp_build_dir}/../extras/japi/java/jvpp/gen/jvppgen/*.py); do
    install -p -m755 ${i} %{buildroot}%{python_sitelib}/jvppgen
 done;
 
diff --git a/extras/rpm/vpp.spec b/extras/rpm/vpp.spec
index c6951bf..69d4d6b 100644
--- a/extras/rpm/vpp.spec
+++ b/extras/rpm/vpp.spec
@@ -223,7 +223,7 @@
 
 # Java bindings
 mkdir -p -m755 %{buildroot}/usr/share/java
-for file in $(find %{_mu_build_dir}/%{_vpp_install_dir}/vpp/share/java -type f -name '*.jar' -print )
+for file in $(find %{_mu_build_dir}/%{_vpp_install_dir}/japi/share/java -type f -name '*.jar' -print )
 do
 	install -p -m 644 $file %{buildroot}/usr/share/java
 done
@@ -260,8 +260,8 @@
 done
 
 mkdir -p -m755 %{buildroot}%{python2_sitelib}/jvppgen
-install -p -m755 %{_mu_build_dir}/../src/vpp-api/java/jvpp/gen/jvpp_gen.py %{buildroot}/usr/bin
-for i in $(ls %{_mu_build_dir}/../src/vpp-api/java/jvpp/gen/jvppgen/*.py); do
+install -p -m755 %{_mu_build_dir}/../extras/japi/java/jvpp/gen/jvpp_gen.py %{buildroot}/usr/bin
+for i in $(ls %{_mu_build_dir}/../extras/japi/java/jvpp/gen/jvppgen/*.py); do
    install -p -m666 ${i} %{buildroot}%{python2_sitelib}/jvppgen
 done;