summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore16
-rw-r--r--.gitreview4
-rw-r--r--CMakeLists.txt111
-rw-r--r--Requierements/Makefile268
-rw-r--r--Test/framework.py1324
-rw-r--r--Test/test_jvpp.py147
-rwxr-xr-xclean.sh10
-rw-r--r--docs/jvpp.rst140
-rwxr-xr-xinstall_jvpp.sh20
-rw-r--r--java/.gitignore6
-rw-r--r--java/CMakeLists.txt331
-rw-r--r--java/Readme.txt236
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java135
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java101
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java159
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java68
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt4
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java33
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java62
-rw-r--r--java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt4
-rw-r--r--java/jvpp-acl/jvpp_acl.c107
-rw-r--r--java/jvpp-acl/jvpp_acl.h42
-rw-r--r--java/jvpp-common/jvpp_common.c99
-rw-r--r--java/jvpp-common/jvpp_common.h75
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java100
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiReadPerfTest.java146
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiWritePerfTest.java162
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackCliApiExample.java68
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java110
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java97
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java88
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java121
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java127
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java67
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiReadPerfTest.java137
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java206
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java125
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java52
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt24
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java33
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java79
-rw-r--r--java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt18
-rw-r--r--java/jvpp-core/jvpp_core.c136
-rw-r--r--java/jvpp-core/jvpp_core.h50
-rw-r--r--java/jvpp-gtpu/jvpp_gtpu.c107
-rw-r--r--java/jvpp-gtpu/jvpp_gtpu.h42
-rw-r--r--java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java56
-rw-r--r--java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt4
-rw-r--r--java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java33
-rw-r--r--java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java60
-rw-r--r--java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt4
-rw-r--r--java/jvpp-ioamexport/jvpp_ioam_export.c107
-rw-r--r--java/jvpp-ioamexport/jvpp_ioam_export.h42
-rw-r--r--java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java76
-rw-r--r--java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt4
-rw-r--r--java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java33
-rw-r--r--java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java66
-rw-r--r--java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt4
-rw-r--r--java/jvpp-ioampot/jvpp_ioam_pot.c107
-rw-r--r--java/jvpp-ioampot/jvpp_ioam_pot.h42
-rw-r--r--java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java77
-rw-r--r--java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt4
-rw-r--r--java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java33
-rw-r--r--java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java60
-rw-r--r--java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt4
-rw-r--r--java/jvpp-ioamtrace/jvpp_ioam_trace.c107
-rw-r--r--java/jvpp-ioamtrace/jvpp_ioam_trace.h42
-rw-r--r--java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java68
-rw-r--r--java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt1
-rw-r--r--java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java33
-rw-r--r--java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java66
-rw-r--r--java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt4
-rw-r--r--java/jvpp-nat/jvpp_nat.c107
-rw-r--r--java/jvpp-nat/jvpp_nat.h42
-rw-r--r--java/jvpp-nsh/jvpp_nsh.c106
-rw-r--r--java/jvpp-nsh/jvpp_nsh.h42
-rw-r--r--java/jvpp-pppoe/jvpp_pppoe.c107
-rw-r--r--java/jvpp-pppoe/jvpp_pppoe.h42
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java63
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java32
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java56
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java76
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java187
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java73
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java79
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java48
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java45
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java33
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java152
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java29
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java29
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java24
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java40
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java34
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java58
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java24
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java24
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java25
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java34
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java180
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java49
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistry.java25
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistryProvider.java28
-rw-r--r--java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java44
-rw-r--r--java/jvpp-registry/jvpp_registry.c410
-rwxr-xr-xjava/jvpp/gen/jvpp_gen.py93
-rw-r--r--java/jvpp/gen/jvppgen/__init__.py0
-rwxr-xr-xjava/jvpp/gen/jvppgen/callback_gen.py73
-rwxr-xr-xjava/jvpp/gen/jvppgen/dto_gen.py231
-rwxr-xr-xjava/jvpp/gen/jvppgen/enums_gen.py74
-rwxr-xr-xjava/jvpp/gen/jvppgen/jni_common_gen.py482
-rwxr-xr-xjava/jvpp/gen/jvppgen/jni_gen.py160
-rwxr-xr-xjava/jvpp/gen/jvppgen/jni_impl_gen.py116
-rwxr-xr-xjava/jvpp/gen/jvppgen/jni_msg_handlers_gen.py107
-rwxr-xr-xjava/jvpp/gen/jvppgen/jni_type_handlers_gen.py230
-rw-r--r--java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py289
-rwxr-xr-xjava/jvpp/gen/jvppgen/jvpp_common_gen.py114
-rw-r--r--java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py338
-rwxr-xr-xjava/jvpp/gen/jvppgen/jvpp_ifc_gen.py71
-rwxr-xr-xjava/jvpp/gen/jvppgen/jvpp_impl_gen.py173
-rwxr-xr-xjava/jvpp/gen/jvppgen/jvpp_model.py624
-rw-r--r--java/jvpp/gen/jvppgen/notification_gen.py226
-rwxr-xr-xjava/jvpp/gen/jvppgen/types_gen.py60
-rwxr-xr-xjava/jvpp/gen/jvppgen/unions_gen.py98
-rwxr-xr-xversion63
125 files changed, 12703 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c6533a7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+.idea
+/build-root/lib
+/build-root/packages
+/build-root/.deps.ok
+/target
+**/core
+/Makefile
+
+cmake-build*/
+_CPack_Packages/
+CMakeFiles/
+CMakeCache.txt
+install_manifest.txt
+*.cmake
+*.cbp
+*.pyc
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..422d3ba
--- /dev/null
+++ b/.gitreview
@@ -0,0 +1,4 @@
+[gerrit]
+host=gerrit.fd.io
+port=29418
+project=jvpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..427f31b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,111 @@
+# 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.
+
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+add_custom_target(install-dep COMMAND make install-dep
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Requirements
+ )
+
+project(jvpp)
+
+include(CheckCCompilerFlag)
+
+if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")
+ set(CMAKE_C_FLAGS "-march=corei7 -mtune=corei7-avx ${CMAKE_C_FLAGS}")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
+ set(CMAKE_C_FLAGS "-march=armv8-a+crc ${CMAKE_C_FLAGS}")
+endif()
+
+check_c_compiler_flag("-Wno-address-of-packed-member" compiler_flag_no_address_of_packed_member)
+if (compiler_flag_no_address_of_packed_member)
+ add_definitions(-Wno-address-of-packed-member)
+endif()
+
+message("\nEnvironment Variables:")
+
+# JVPP RELATED VARIABLES
+unset(JVPP_VERSION)
+unset(JAPI_LIB_VERSION)
+execute_process(
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
+ COMMAND ./version
+ OUTPUT_VARIABLE JVPP_VERSION
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+string(REPLACE "-" ";" JAPI_LIB_VERSION ${JVPP_VERSION})
+list(GET JAPI_LIB_VERSION 0 JAPI_LIB_VERSION)
+
+message(" JVPP Main Version: ${JVPP_VERSION}")
+message(" JVPP Version: ${JAPI_LIB_VERSION}")
+
+# OS RELATED VARIABLES
+unset(RELEASE_ID)
+unset(RELEASE_CODENAME)
+find_program(LSB_RELEASE_EXEC lsb_release)
+execute_process(COMMAND ${LSB_RELEASE_EXEC} -is
+ OUTPUT_VARIABLE RELEASE_ID
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+execute_process(COMMAND ${LSB_RELEASE_EXEC} -cs
+ OUTPUT_VARIABLE RELEASE_CODENAME
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
+message(" OS Release Id: ${RELEASE_ID} ")
+message(" OS Release Codename: ${RELEASE_CODENAME} ")
+
+
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build-root/lib)
+set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
+set(CMAKE_INSTALL_MESSAGE NEVER)
+
+find_package(Threads REQUIRED)
+
+unset(dirlist)
+
+macro(subdirlist dirlist dirpath)
+ file(GLOB dirs RELATIVE ${dirpath} ${dirpath}/*)
+ foreach(dir ${dirs})
+ if(IS_DIRECTORY ${dirpath}/${dir})
+ list(APPEND dirlist ${dirpath}/${dir})
+ endif()
+ endforeach()
+endmacro()
+
+#list(APPEND dirlist $ENV{JAVA_HOME})
+#subdirlist(dirlist /usr/lib/jvm)
+#subdirlist(dirlist /usr/lib64/jvm)
+#
+#unset(JAVA_HOME_SET)
+#find_path(JAVA_HOME_SET NAMES include/jni.h PATHS ${dirlist})
+#if (NOT JAVA_HOME_SET)
+# message("JAVA_HOME is not found")
+#else()
+# message("JAVA HOME: ${JAVA_HOME}")
+# set(ENV{JAVA_HOME} "${JAVA_HOME_SET}")
+#endif()
+
+message("\nJAVA:")
+message(" JAVA_HOME: $ENV{JAVA_HOME}")
+
+find_package(Java 1.8 REQUIRED COMPONENTS Development)
+message("JAVA: ${Java}")
+get_filename_component(jvm_path ${Java_JAVAC_EXECUTABLE} DIRECTORY)
+set (Java_INCLUDE_DIRS ${jvm_path}/../include ${jvm_path}/../include/linux)
+
+message(" Java headers: ${Java_INCLUDE_DIRS}")
+message(" Java compiler: ${Java_JAVAC_EXECUTABLE}")
+add_subdirectory(java)
+
+#find_package(vpp REQUIRED)
diff --git a/Requierements/Makefile b/Requierements/Makefile
new file mode 100644
index 0000000..6be83fd
--- /dev/null
+++ b/Requierements/Makefile
@@ -0,0 +1,268 @@
+# 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.
+
+export WS_ROOT=$(CURDIR)
+export BR=$(WS_ROOT)/build-root
+CCACHE_DIR?=$(BR)/.ccache
+GDB?=gdb
+PLATFORM?=vpp
+SAMPLE_PLUGIN?=no
+STARTUP_DIR?=$(PWD)
+MACHINE=$(shell uname -m)
+SUDO?=sudo
+
+,:=,
+define disable_plugins
+$(if $(1), \
+ "plugins {" \
+ $(patsubst %,"plugin %_plugin.so { disable }",$(subst $(,), ,$(1))) \
+ " }" \
+ ,)
+endef
+
+MINIMAL_STARTUP_CONF=" \
+unix { \
+ interactive \
+ cli-listen /run/vpp/cli.sock \
+ gid $(shell id -g) \
+ $(if $(wildcard startup.vpp),"exec startup.vpp",) \
+} \
+$(if $(DPDK_CONFIG), "dpdk { $(DPDK_CONFIG) }",) \
+$(call disable_plugins,$(DISABLED_PLUGINS)) \
+"
+
+GDB_ARGS= -ex "handle SIGUSR1 noprint nostop"
+
+#
+# OS Detection
+#
+# We allow Darwin (MacOS) for docs generation; VPP build will still fail.
+ifneq ($(shell uname),Darwin)
+OS_ID = $(shell grep '^ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
+OS_VERSION_ID= $(shell grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g')
+endif
+
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+PKG=deb
+else ifeq ($(filter rhel centos fedora opensuse opensuse-leap opensuse-tumbleweed,$(OS_ID)),$(OS_ID))
+PKG=rpm
+endif
+
+# +libganglia1-dev if building the gmond plugin
+
+DEB_DEPENDS = curl build-essential autoconf automake ccache
+DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-systemd
+DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config
+DEB_DEPENDS += lcov chrpath autoconf indent clang-format libnuma-dev
+DEB_DEPENDS += python-all python-dev python-virtualenv python-pip libffi6 check
+DEB_DEPENDS += libboost-all-dev libffi-dev python-ply libmbedtls-dev
+DEB_DEPENDS += cmake ninja-build
+
+# ADD JDK for JVPP
+ifeq ($(OS_VERSION_ID),14.04)
+ DEB_DEPENDS += openjdk-8-jdk-headless
+ DEB_DEPENDS += libssl-dev
+else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8)
+ DEB_DEPENDS += openjdk-8-jdk-headless
+ DEB_DEPENDS += libssl-dev
+ APT_ARGS = -t jessie-backports
+else ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-9)
+ DEB_DEPENDS += default-jdk-headless
+ DEB_DEPENDS += libssl1.0-dev
+else
+ DEB_DEPENDS += default-jdk-headless
+ DEB_DEPENDS += libssl-dev
+endif
+
+# ADD JDK for JVPP
+RPM_DEPENDS = redhat-lsb glibc-static java-1.8.0-openjdk-devel yum-utils
+
+RPM_DEPENDS += apr-devel
+RPM_DEPENDS += numactl-devel
+RPM_DEPENDS += check check-devel
+RPM_DEPENDS += boost boost-devel
+RPM_DEPENDS += selinux-policy selinux-policy-devel
+RPM_DEPENDS += cmake3 ninja-build
+
+ifeq ($(OS_ID)-$(OS_VERSION_ID),fedora-25)
+ RPM_DEPENDS += subunit subunit-devel
+ RPM_DEPENDS += openssl-devel
+ RPM_DEPENDS += python-devel python2-ply
+ RPM_DEPENDS += python2-virtualenv
+ RPM_DEPENDS += mbedtls-devel
+ RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries'
+else ifeq ($(shell if [ "$(OS_ID)" = "fedora" ]; then test $(OS_VERSION_ID) -gt 25; echo $$?; fi),0)
+ RPM_DEPENDS += subunit subunit-devel
+ RPM_DEPENDS += compat-openssl10-devel
+ RPM_DEPENDS += python2-devel python2-ply
+ RPM_DEPENDS += python2-virtualenv
+ RPM_DEPENDS += mbedtls-devel
+ RPM_DEPENDS_GROUPS = 'C Development Tools and Libraries'
+else
+ RPM_DEPENDS += openssl-devel
+ RPM_DEPENDS += python-devel python-ply
+ RPM_DEPENDS += python-virtualenv
+ RPM_DEPENDS += devtoolset-7
+ RPM_DEPENDS_GROUPS = 'Development Tools'
+endif
+
+# +ganglia-devel if building the ganglia plugin
+
+RPM_DEPENDS += chrpath libffi-devel rpm-build
+
+SUSE_NAME= $(shell grep '^NAME=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g' | cut -d' ' -f2)
+SUSE_ID= $(shell grep '^VERSION_ID=' /etc/os-release | cut -f2- -d= | sed -e 's/\"//g' | cut -d' ' -f2)
+RPM_SUSE_BUILDTOOLS_DEPS = autoconf automake ccache check-devel chrpath
+RPM_SUSE_BUILDTOOLS_DEPS += clang cmake indent libtool make ninja python-ply
+
+# ADD JDK for JVPP
+RPM_SUSE_DEVEL_DEPS = glibc-devel-static java-1_8_0-openjdk-devel libnuma-devel
+
+RPM_SUSE_DEVEL_DEPS += libopenssl-devel openssl-devel mbedtls-devel
+
+RPM_SUSE_PYTHON_DEPS = python-devel python3-devel python-pip python3-pip
+RPM_SUSE_PYTHON_DEPS += python-rpm-macros python3-rpm-macros
+
+RPM_SUSE_PLATFORM_DEPS = distribution-release shadow rpm-build
+
+ifeq ($(OS_ID),opensuse)
+ifeq ($(SUSE_NAME),Tumbleweed)
+ RPM_SUSE_DEVEL_DEPS = libboost_headers-devel libboost_thread-devel gcc
+ RPM_SUSE_PYTHON_DEPS += python2-ply python2-virtualenv
+endif
+ifeq ($(SUSE_ID),15.0)
+ RPM_SUSE_DEVEL_DEPS = libboost_headers-devel libboost_thread-devel gcc6
+ RPM_SUSE_PYTHON_DEPS += python2-ply python2-virtualenv
+else
+ RPM_SUSE_DEVEL_DEPS += boost_1_61-devel gcc6
+ RPM_SUSE_PYTHON_DEPS += python-virtualenv
+endif
+endif
+
+ifeq ($(OS_ID),opensuse-leap)
+ifeq ($(SUSE_ID),15.0)
+ RPM_SUSE_DEVEL_DEPS = libboost_headers-devel libboost_thread-devel gcc6
+ RPM_SUSE_PYTHON_DEPS += python2-ply python2-virtualenv
+endif
+endif
+
+RPM_SUSE_DEPENDS += $(RPM_SUSE_BUILDTOOLS_DEPS) $(RPM_SUSE_DEVEL_DEPS) $(RPM_SUSE_PYTHON_DEPS) $(RPM_SUSE_PLATFORM_DEPS)
+
+ifneq ($(wildcard $(STARTUP_DIR)/startup.conf),)
+ STARTUP_CONF ?= $(STARTUP_DIR)/startup.conf
+endif
+
+ifeq ($(findstring y,$(UNATTENDED)),y)
+CONFIRM=-y
+FORCE=--force-yes
+endif
+
+TARGETS = vpp
+
+ifneq ($(SAMPLE_PLUGIN),no)
+TARGETS += sample-plugin
+endif
+
+.PHONY: help wipe wipe-release build build-release rebuild rebuild-release
+.PHONY: run run-release debug debug-release build-vat run-vat pkg-deb pkg-rpm
+.PHONY: ctags cscope
+.PHONY: test test-debug retest retest-debug test-doc test-wipe-doc test-help test-wipe
+.PHONY: test-cov test-wipe-cov
+
+define banner
+ @echo "========================================================================"
+ @echo " $(1)"
+ @echo "========================================================================"
+ @echo " "
+endef
+
+help:
+ @echo "Make Targets:"
+ @echo " install-dep - install software dependencies"
+ @echo ""
+
+$(BR)/.deps.ok:
+ifeq ($(findstring y,$(UNATTENDED)),y)
+ make install-dep
+endif
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+ @MISSING=$$(apt-get install -y -qq -s $(DEB_DEPENDS) | grep "^Inst ") ; \
+ if [ -n "$$MISSING" ] ; then \
+ echo "\nPlease install missing packages: \n$$MISSING\n" ; \
+ echo "by executing \"make install-dep\"\n" ; \
+ exit 1 ; \
+ fi ; \
+ exit 0
+else ifneq ("$(wildcard /etc/redhat-release)","")
+ @for i in $(RPM_DEPENDS) ; do \
+ RPM=$$(basename -s .rpm "$${i##*/}" | cut -d- -f1,2,3) ; \
+ MISSING+=$$(rpm -q $$RPM | grep "^package") ; \
+ done ; \
+ if [ -n "$$MISSING" ] ; then \
+ echo "Please install missing RPMs: \n$$MISSING\n" ; \
+ echo "by executing \"make install-dep\"\n" ; \
+ exit 1 ; \
+ fi ; \
+ exit 0
+endif
+ @touch $@
+
+install-dep:
+ifeq ($(filter ubuntu debian,$(OS_ID)),$(OS_ID))
+ifeq ($(OS_VERSION_ID),14.04)
+ @sudo -E apt-get $(CONFIRM) $(FORCE) install software-properties-common
+ @sudo -E add-apt-repository ppa:openjdk-r/ppa $(CONFIRM)
+endif
+ifeq ($(OS_ID)-$(OS_VERSION_ID),debian-8)
+ @grep -q jessie-backports /etc/apt/sources.list /etc/apt/sources.list.d/* 2> /dev/null \
+ || ( echo "Please install jessie-backports" ; exit 1 )
+endif
+ @sudo -E apt-get update
+ @sudo -E apt-get $(APT_ARGS) $(CONFIRM) $(FORCE) install $(DEB_DEPENDS)
+else ifneq ("$(wildcard /etc/redhat-release)","")
+ifeq ($(OS_ID),rhel)
+ @sudo -E yum-config-manager --enable rhel-server-rhscl-7-rpms
+else ifeq ($(OS_ID),centos)
+ @sudo -E yum install $(CONFIRM) centos-release-scl-rh
+endif
+ @sudo -E yum groupinstall $(CONFIRM) $(RPM_DEPENDS_GROUPS)
+ @sudo -E yum install $(CONFIRM) $(RPM_DEPENDS)
+ @sudo -E debuginfo-install $(CONFIRM) glibc openssl-libs mbedtls-devel zlib
+else ifeq ($(filter opensuse-tumbleweed,$(OS_ID)),$(OS_ID))
+ @sudo -E zypper refresh
+ @sudo -E zypper install -y $(RPM_SUSE_DEPENDS)
+else ifeq ($(filter opensuse-leap,$(OS_ID)),$(OS_ID))
+ @sudo -E zypper refresh
+ @sudo -E zypper install -y $(RPM_SUSE_DEPENDS)
+else ifeq ($(filter opensuse,$(OS_ID)),$(OS_ID))
+ @sudo -E zypper refresh
+ @sudo -E zypper install -y $(RPM_SUSE_DEPENDS)
+else
+ $(error "This option currently works only on Ubuntu, Debian, RHEL, CentOS or openSUSE systems")
+endif
+
+define make
+ @make -C $(BR) PLATFORM=$(PLATFORM) TAG=$(1) $(2)
+endef
+
+$(BR)/scripts/.version:
+ifneq ("$(wildcard /etc/redhat-release)","")
+ $(shell $(BR)/scripts/version rpm-string > $(BR)/scripts/.version)
+else
+ $(shell $(BR)/scripts/version > $(BR)/scripts/.version)
+endif
+
+DIST_FILE = $(BR)/vpp-$(shell src/scripts/version).tar
+DIST_SUBDIR = vpp-$(shell src/scripts/version|cut -f1 -d-)
+
+
diff --git a/Test/framework.py b/Test/framework.py
new file mode 100644
index 0000000..54b7a2d
--- /dev/null
+++ b/Test/framework.py
@@ -0,0 +1,1324 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import gc
+import sys
+import os
+import select
+import unittest
+import tempfile
+import time
+import faulthandler
+import random
+import copy
+import psutil
+from collections import deque
+from threading import Thread, Event
+from inspect import getdoc, isclass
+from traceback import format_exception
+from logging import FileHandler, DEBUG, Formatter
+from scapy.packet import Raw
+from hook import StepHook, PollHook, VppDiedError
+from vpp_pg_interface import VppPGInterface
+from vpp_sub_interface import VppSubInterface
+from vpp_lo_interface import VppLoInterface
+from vpp_papi_provider import VppPapiProvider
+from vpp_papi.vpp_stats import VPPStats
+from log import RED, GREEN, YELLOW, double_line_delim, single_line_delim, \
+ get_logger, colorize
+from vpp_object import VppObjectRegistry
+from util import ppp, is_core_present
+from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror
+from scapy.layers.inet6 import ICMPv6DestUnreach, ICMPv6EchoRequest
+from scapy.layers.inet6 import ICMPv6EchoReply
+if os.name == 'posix' and sys.version_info[0] < 3:
+ # using subprocess32 is recommended by python official documentation
+ # @ https://docs.python.org/2/library/subprocess.html
+ import subprocess32 as subprocess
+else:
+ import subprocess
+
+
+PASS = 0
+FAIL = 1
+ERROR = 2
+SKIP = 3
+TEST_RUN = 4
+
+
+debug_framework = False
+if os.getenv('TEST_DEBUG', "0") == "1":
+ debug_framework = True
+ import debug_internal
+
+
+"""
+ Test framework module.
+
+ The module provides a set of tools for constructing and running tests and
+ representing the results.
+"""
+
+
+class _PacketInfo(object):
+ """Private class to create packet info object.
+
+ Help process information about the next packet.
+ Set variables to default values.
+ """
+ #: Store the index of the packet.
+ index = -1
+ #: Store the index of the source packet generator interface of the packet.
+ src = -1
+ #: Store the index of the destination packet generator interface
+ #: of the packet.
+ dst = -1
+ #: Store expected ip version
+ ip = -1
+ #: Store expected upper protocol
+ proto = -1
+ #: Store the copy of the former packet.
+ data = None
+
+ def __eq__(self, other):
+ index = self.index == other.index
+ src = self.src == other.src
+ dst = self.dst == other.dst
+ data = self.data == other.data
+ return index and src and dst and data
+
+
+def pump_output(testclass):
+ """ pump output from vpp stdout/stderr to proper queues """
+ stdout_fragment = ""
+ stderr_fragment = ""
+ while not testclass.pump_thread_stop_flag.is_set():
+ readable = select.select([testclass.vpp.stdout.fileno(),
+ testclass.vpp.stderr.fileno(),
+ testclass.pump_thread_wakeup_pipe[0]],
+ [], [])[0]
+ if testclass.vpp.stdout.fileno() in readable:
+ read = os.read(testclass.vpp.stdout.fileno(), 102400)
+ if len(read) > 0:
+ split = read.splitlines(True)
+ if len(stdout_fragment) > 0:
+ split[0] = "%s%s" % (stdout_fragment, split[0])
+ if len(split) > 0 and split[-1].endswith("\n"):
+ limit = None
+ else:
+ limit = -1
+ stdout_fragment = split[-1]
+ testclass.vpp_stdout_deque.extend(split[:limit])
+ if not testclass.cache_vpp_output:
+ for line in split[:limit]:
+ testclass.logger.debug(
+ "VPP STDOUT: %s" % line.rstrip("\n"))
+ if testclass.vpp.stderr.fileno() in readable:
+ read = os.read(testclass.vpp.stderr.fileno(), 102400)
+ if len(read) > 0:
+ split = read.splitlines(True)
+ if len(stderr_fragment) > 0:
+ split[0] = "%s%s" % (stderr_fragment, split[0])
+ if len(split) > 0 and split[-1].endswith("\n"):
+ limit = None
+ else:
+ limit = -1
+ stderr_fragment = split[-1]
+ testclass.vpp_stderr_deque.extend(split[:limit])
+ if not testclass.cache_vpp_output:
+ for line in split[:limit]:
+ testclass.logger.debug(
+ "VPP STDERR: %s" % line.rstrip("\n"))
+ # ignoring the dummy pipe here intentionally - the flag will take care
+ # of properly terminating the loop
+
+
+def running_extended_tests():
+ s = os.getenv("EXTENDED_TESTS", "n")
+ return True if s.lower() in ("y", "yes", "1") else False
+
+
+def running_on_centos():
+ os_id = os.getenv("OS_ID", "")
+ return True if "centos" in os_id.lower() else False
+
+
+class KeepAliveReporter(object):
+ """
+ Singleton object which reports test start to parent process
+ """
+ _shared_state = {}
+
+ def __init__(self):
+ self.__dict__ = self._shared_state
+ self._pipe = None
+
+ @property
+ def pipe(self):
+ return self._pipe
+
+ @pipe.setter
+ def pipe(self, pipe):
+ if self._pipe is not None:
+ raise Exception("Internal error - pipe should only be set once.")
+ self._pipe = pipe
+
+ def send_keep_alive(self, test, desc=None):
+ """
+ Write current test tmpdir & desc to keep-alive pipe to signal liveness
+ """
+ if self.pipe is None:
+ # if not running forked..
+ return
+
+ if isclass(test):
+ desc = '%s (%s)' % (desc, unittest.util.strclass(test))
+ else:
+ desc = test.id()
+
+ self.pipe.send((desc, test.vpp_bin, test.tempdir, test.vpp.pid))
+
+
+class VppTestCase(unittest.TestCase):
+ """This subclass is a base class for VPP test cases that are implemented as
+ classes. It provides methods to create and run test case.
+ """
+
+ @property
+ def packet_infos(self):
+ """List of packet infos"""
+ return self._packet_infos
+
+ @classmethod
+ def get_packet_count_for_if_idx(cls, dst_if_index):
+ """Get the number of packet info for specified destination if index"""
+ if dst_if_index in cls._packet_count_for_dst_if_idx:
+ return cls._packet_count_for_dst_if_idx[dst_if_index]
+ else:
+ return 0
+
+ @classmethod
+ def instance(cls):
+ """Return the instance of this testcase"""
+ return cls.test_instance
+
+ @classmethod
+ def set_debug_flags(cls, d):
+ cls.debug_core = False
+ cls.debug_gdb = False
+ cls.debug_gdbserver = False
+ if d is None:
+ return
+ dl = d.lower()
+ if dl == "core":
+ cls.debug_core = True
+ elif dl == "gdb":
+ cls.debug_gdb = True
+ elif dl == "gdbserver":
+ cls.debug_gdbserver = True
+ else:
+ raise Exception("Unrecognized DEBUG option: '%s'" % d)
+
+ @staticmethod
+ def get_least_used_cpu():
+ cpu_usage_list = [set(range(psutil.cpu_count()))]
+ vpp_processes = [p for p in psutil.process_iter(attrs=['pid', 'name'])
+ if 'vpp_main' == p.info['name']]
+ for vpp_process in vpp_processes:
+ for cpu_usage_set in cpu_usage_list:
+ try:
+ cpu_num = vpp_process.cpu_num()
+ if cpu_num in cpu_usage_set:
+ cpu_usage_set_index = cpu_usage_list.index(
+ cpu_usage_set)
+ if cpu_usage_set_index == len(cpu_usage_list) - 1:
+ cpu_usage_list.append({cpu_num})
+ else:
+ cpu_usage_list[cpu_usage_set_index + 1].add(
+ cpu_num)
+ cpu_usage_set.remove(cpu_num)
+ break
+ except psutil.NoSuchProcess:
+ pass
+
+ for cpu_usage_set in cpu_usage_list:
+ if len(cpu_usage_set) > 0:
+ min_usage_set = cpu_usage_set
+ break
+
+ return random.choice(tuple(min_usage_set))
+
+ @classmethod
+ def print_header(cls):
+ if not hasattr(cls, '_header_printed'):
+ print(double_line_delim)
+ print(colorize(getdoc(cls).splitlines()[0], GREEN))
+ print(double_line_delim)
+ cls._header_printed = True
+
+ @classmethod
+ def setUpConstants(cls):
+ """ Set-up the test case class based on environment variables """
+ s = os.getenv("STEP", "n")
+ cls.step = True if s.lower() in ("y", "yes", "1") else False
+ d = os.getenv("DEBUG", None)
+ c = os.getenv("CACHE_OUTPUT", "1")
+ cls.cache_vpp_output = False if c.lower() in ("n", "no", "0") else True
+ cls.set_debug_flags(d)
+ cls.vpp_bin = os.getenv('VPP_TEST_BIN', "vpp")
+ cls.plugin_path = os.getenv('VPP_TEST_PLUGIN_PATH')
+ cls.extern_plugin_path = os.getenv('EXTERN_PLUGINS')
+ plugin_path = None
+ if cls.plugin_path is not None:
+ if cls.extern_plugin_path is not None:
+ plugin_path = "%s:%s" % (
+ cls.plugin_path, cls.extern_plugin_path)
+ else:
+ plugin_path = cls.plugin_path
+ elif cls.extern_plugin_path is not None:
+ plugin_path = cls.extern_plugin_path
+ debug_cli = ""
+ if cls.step or cls.debug_gdb or cls.debug_gdbserver:
+ debug_cli = "cli-listen localhost:5002"
+ coredump_size = None
+ size = os.getenv("COREDUMP_SIZE")
+ if size is not None:
+ coredump_size = "coredump-size %s" % size
+ if coredump_size is None:
+ coredump_size = "coredump-size unlimited"
+
+ cpu_core_number = cls.get_least_used_cpu()
+
+ cls.vpp_cmdline = [cls.vpp_bin, "unix",
+ "{", "nodaemon", debug_cli, "full-coredump",
+ coredump_size, "runtime-dir", cls.tempdir, "}",
+ "api-trace", "{", "on", "}", "api-segment", "{",
+ "prefix", cls.shm_prefix, "}", "cpu", "{",
+ "main-core", str(cpu_core_number), "}", "statseg",
+ "{", "socket-name", cls.stats_sock, "}", "plugins",
+ "{", "plugin", "dpdk_plugin.so", "{", "disable",
+ "}", "plugin", "unittest_plugin.so", "{", "enable",
+ "}", "}", ]
+ if plugin_path is not None:
+ cls.vpp_cmdline.extend(["plugin_path", plugin_path])
+ cls.logger.info("vpp_cmdline args: %s" % cls.vpp_cmdline)
+ cls.logger.info("vpp_cmdline: %s" % " ".join(cls.vpp_cmdline))
+
+ @classmethod
+ def wait_for_enter(cls):
+ if cls.debug_gdbserver:
+ print(double_line_delim)
+ print("Spawned GDB server with PID: %d" % cls.vpp.pid)
+ elif cls.debug_gdb:
+ print(double_line_delim)
+ print("Spawned VPP with PID: %d" % cls.vpp.pid)
+ else:
+ cls.logger.debug("Spawned VPP with PID: %d" % cls.vpp.pid)
+ return
+ print(single_line_delim)
+ print("You can debug the VPP using e.g.:")
+ if cls.debug_gdbserver:
+ print("gdb " + cls.vpp_bin + " -ex 'target remote localhost:7777'")
+ print("Now is the time to attach a gdb by running the above "
+ "command, set up breakpoints etc. and then resume VPP from "
+ "within gdb by issuing the 'continue' command")
+ elif cls.debug_gdb:
+ print("gdb " + cls.vpp_bin + " -ex 'attach %s'" % cls.vpp.pid)
+ print("Now is the time to attach a gdb by running the above "
+ "command and set up breakpoints etc.")
+ print(single_line_delim)
+ raw_input("Press ENTER to continue running the testcase...")
+
+ @classmethod
+ def run_vpp(cls):
+ cmdline = cls.vpp_cmdline
+
+ if cls.debug_gdbserver:
+ gdbserver = '/usr/bin/gdbserver'
+ if not os.path.isfile(gdbserver) or \
+ not os.access(gdbserver, os.X_OK):
+ raise Exception("gdbserver binary '%s' does not exist or is "
+ "not executable" % gdbserver)
+
+ cmdline = [gdbserver, 'localhost:7777'] + cls.vpp_cmdline
+ cls.logger.info("Gdbserver cmdline is %s", " ".join(cmdline))
+
+ try:
+ cls.vpp = subprocess.Popen(cmdline,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ bufsize=1)
+ except subprocess.CalledProcessError as e:
+ cls.logger.critical("Couldn't start vpp: %s" % e)
+ raise
+
+ cls.wait_for_enter()
+
+ @classmethod
+ def wait_for_stats_socket(cls):
+ deadline = time.time() + 3
+ ok = False
+ while time.time() < deadline or \
+ cls.debug_gdb or cls.debug_gdbserver:
+ if os.path.exists(cls.stats_sock):
+ ok = True
+ break
+ time.sleep(0.8)
+ if not ok:
+ cls.logger.critical("Couldn't stat : {}".format(cls.stats_sock))
+
+ @classmethod
+ def setUpClass(cls):
+ """
+ Perform class setup before running the testcase
+ Remove shared memory files, start vpp and connect the vpp-api
+ """
+ gc.collect() # run garbage collection first
+ random.seed()
+ cls.print_header()
+ cls.logger = get_logger(cls.__name__)
+ if hasattr(cls, 'parallel_handler'):
+ cls.logger.addHandler(cls.parallel_handler)
+ cls.logger.propagate = False
+ cls.tempdir = tempfile.mkdtemp(
+ prefix='vpp-unittest-%s-' % cls.__name__)
+ cls.stats_sock = "%s/stats.sock" % cls.tempdir
+ cls.file_handler = FileHandler("%s/log.txt" % cls.tempdir)
+ cls.file_handler.setFormatter(
+ Formatter(fmt='%(asctime)s,%(msecs)03d %(message)s',
+ datefmt="%H:%M:%S"))
+ cls.file_handler.setLevel(DEBUG)
+ cls.logger.addHandler(cls.file_handler)
+ cls.shm_prefix = os.path.basename(cls.tempdir)
+ os.chdir(cls.tempdir)
+ cls.logger.info("Temporary dir is %s, shm prefix is %s",
+ cls.tempdir, cls.shm_prefix)
+ cls.setUpConstants()
+ cls.reset_packet_infos()
+ cls._captures = []
+ cls._zombie_captures = []
+ cls.verbose = 0
+ cls.vpp_dead = False
+ cls.registry = VppObjectRegistry()
+ cls.vpp_startup_failed = False
+ cls.reporter = KeepAliveReporter()
+ # need to catch exceptions here because if we raise, then the cleanup
+ # doesn't get called and we might end with a zombie vpp
+ try:
+ cls.run_vpp()
+ cls.reporter.send_keep_alive(cls, 'setUpClass')
+ VppTestResult.current_test_case_info = TestCaseInfo(
+ cls.logger, cls.tempdir, cls.vpp.pid, cls.vpp_bin)
+ cls.vpp_stdout_deque = deque()
+ cls.vpp_stderr_deque = deque()
+ cls.pump_thread_stop_flag = Event()
+ cls.pump_thread_wakeup_pipe = os.pipe()
+ cls.pump_thread = Thread(target=pump_output, args=(cls,))
+ cls.pump_thread.daemon = True
+ cls.pump_thread.start()
+ if cls.debug_gdb or cls.debug_gdbserver:
+ read_timeout = 0
+ else:
+ read_timeout = 5
+ cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls,
+ read_timeout)
+ if cls.step:
+ hook = StepHook(cls)
+ else:
+ hook = PollHook(cls)
+ cls.vapi.register_hook(hook)
+ cls.wait_for_stats_socket()
+ cls.statistics = VPPStats(socketname=cls.stats_sock)
+ try:
+ hook.poll_vpp()
+ except VppDiedError:
+ cls.vpp_startup_failed = True
+ cls.logger.critical(
+ "VPP died shortly after startup, check the"
+ " output to standard error for possible cause")
+ raise
+ try:
+ cls.vapi.connect()
+ except Exception:
+ try:
+ cls.vapi.disconnect()
+ except Exception:
+ pass
+ if cls.debug_gdbserver:
+ print(colorize("You're running VPP inside gdbserver but "
+ "VPP-API connection failed, did you forget "
+ "to 'continue' VPP from within gdb?", RED))
+ raise
+ except Exception:
+ try:
+ cls.quit()
+ except Exception:
+ pass
+ raise
+
+ @classmethod
+ def quit(cls):
+ """
+ Disconnect vpp-api, kill vpp and cleanup shared memory files
+ """
+ if (cls.debug_gdbserver or cls.debug_gdb) and hasattr(cls, 'vpp'):
+ cls.vpp.poll()
+ if cls.vpp.returncode is None:
+ print(double_line_delim)
+ print("VPP or GDB server is still running")
+ print(single_line_delim)
+ raw_input("When done debugging, press ENTER to kill the "
+ "process and finish running the testcase...")
+
+ # first signal that we want to stop the pump thread, then wake it up
+ if hasattr(cls, 'pump_thread_stop_flag'):
+ cls.pump_thread_stop_flag.set()
+ if hasattr(cls, 'pump_thread_wakeup_pipe'):
+ os.write(cls.pump_thread_wakeup_pipe[1], 'ding dong wake up')
+ if hasattr(cls, 'pump_thread'):
+ cls.logger.debug("Waiting for pump thread to stop")
+ cls.pump_thread.join()
+ if hasattr(cls, 'vpp_stderr_reader_thread'):
+ cls.logger.debug("Waiting for stdderr pump to stop")
+ cls.vpp_stderr_reader_thread.join()
+
+ if hasattr(cls, 'vpp'):
+ if hasattr(cls, 'vapi'):
+ cls.vapi.disconnect()
+ del cls.vapi
+ cls.vpp.poll()
+ if cls.vpp.returncode is None:
+ cls.logger.debug("Sending TERM to vpp")
+ cls.vpp.kill()
+ cls.logger.debug("Waiting for vpp to die")
+ cls.vpp.communicate()
+ del cls.vpp
+
+ if cls.vpp_startup_failed:
+ stdout_log = cls.logger.info
+ stderr_log = cls.logger.critical
+ else:
+ stdout_log = cls.logger.info
+ stderr_log = cls.logger.info
+
+ if hasattr(cls, 'vpp_stdout_deque'):
+ stdout_log(single_line_delim)
+ stdout_log('VPP output to stdout while running %s:', cls.__name__)
+ stdout_log(single_line_delim)
+ vpp_output = "".join(cls.vpp_stdout_deque)
+ with open(cls.tempdir + '/vpp_stdout.txt', 'w') as f:
+ f.write(vpp_output)
+ stdout_log('\n%s', vpp_output)
+ stdout_log(single_line_delim)
+
+ if hasattr(cls, 'vpp_stderr_deque'):
+ stderr_log(single_line_delim)
+ stderr_log('VPP output to stderr while running %s:', cls.__name__)
+ stderr_log(single_line_delim)
+ vpp_output = "".join(cls.vpp_stderr_deque)
+ with open(cls.tempdir + '/vpp_stderr.txt', 'w') as f:
+ f.write(vpp_output)
+ stderr_log('\n%s', vpp_output)
+ stderr_log(single_line_delim)
+
+ @classmethod
+ def tearDownClass(cls):
+ """ Perform final cleanup after running all tests in this test-case """
+ cls.reporter.send_keep_alive(cls, 'tearDownClass')
+ cls.quit()
+ cls.file_handler.close()
+ cls.reset_packet_infos()
+ if debug_framework:
+ debug_internal.on_tear_down_class(cls)
+
+ def tearDown(self):
+ """ Show various debug prints after each test """
+ self.logger.debug("--- tearDown() for %s.%s(%s) called ---" %
+ (self.__class__.__name__, self._testMethodName,
+ self._testMethodDoc))
+ if not self.vpp_dead:
+ self.logger.debug(self.vapi.cli("show trace"))
+ self.logger.info(self.vapi.ppcli("show interface"))
+ self.logger.info(self.vapi.ppcli("show hardware"))
+ self.logger.info(self.statistics.set_errors_str())
+ self.logger.info(self.vapi.ppcli("show run"))
+ self.logger.info(self.vapi.ppcli("show log"))
+ self.registry.remove_vpp_config(self.logger)
+ # Save/Dump VPP api trace log
+ api_trace = "vpp_api_trace.%s.log" % self._testMethodName
+ tmp_api_trace = "/tmp/%s" % api_trace
+ vpp_api_trace_log = "%s/%s" % (self.tempdir, api_trace)
+ self.logger.info(self.vapi.ppcli("api trace save %s" % api_trace))
+ self.logger.info("Moving %s to %s\n" % (tmp_api_trace,
+ vpp_api_trace_log))
+ os.rename(tmp_api_trace, vpp_api_trace_log)
+ self.logger.info(self.vapi.ppcli("api trace custom-dump %s" %
+ vpp_api_trace_log))
+ else:
+ self.registry.unregister_all(self.logger)
+
+ def setUp(self):
+ """ Clear trace before running each test"""
+ self.reporter.send_keep_alive(self)
+ self.logger.debug("--- setUp() for %s.%s(%s) called ---" %
+ (self.__class__.__name__, self._testMethodName,
+ self._testMethodDoc))
+ if self.vpp_dead:
+ raise Exception("VPP is dead when setting up the test")
+ self.sleep(.1, "during setUp")
+ self.vpp_stdout_deque.append(
+ "--- test setUp() for %s.%s(%s) starts here ---\n" %
+ (self.__class__.__name__, self._testMethodName,
+ self._testMethodDoc))
+ self.vpp_stderr_deque.append(
+ "--- test setUp() for %s.%s(%s) starts here ---\n" %
+ (self.__class__.__name__, self._testMethodName,
+ self._testMethodDoc))
+ self.vapi.cli("clear trace")
+ # store the test instance inside the test class - so that objects
+ # holding the class can access instance methods (like assertEqual)
+ type(self).test_instance = self
+
+ @classmethod
+ def pg_enable_capture(cls, interfaces=None):
+ """
+ Enable capture on packet-generator interfaces
+
+ :param interfaces: iterable interface indexes (if None,
+ use self.pg_interfaces)
+
+ """
+ if interfaces is None:
+ interfaces = cls.pg_interfaces
+ for i in interfaces:
+ i.enable_capture()
+
+ @classmethod
+ def register_capture(cls, cap_name):
+ """ Register a capture in the testclass """
+ # add to the list of captures with current timestamp
+ cls._captures.append((time.time(), cap_name))
+ # filter out from zombies
+ cls._zombie_captures = [(stamp, name)
+ for (stamp, name) in cls._zombie_captures
+ if name != cap_name]
+
+ @classmethod
+ def pg_start(cls):
+ """ Remove any zombie captures and enable the packet generator """
+ # how long before capture is allowed to be deleted - otherwise vpp
+ # crashes - 100ms seems enough (this shouldn't be needed at all)
+ capture_ttl = 0.1
+ now = time.time()
+ for stamp, cap_name in cls._zombie_captures:
+ wait = stamp + capture_ttl - now
+ if wait > 0:
+ cls.sleep(wait, "before deleting capture %s" % cap_name)
+ now = time.time()
+ cls.logger.debug("Removing zombie capture %s" % cap_name)
+ cls.vapi.cli('packet-generator delete %s' % cap_name)
+
+ cls.vapi.cli("trace add pg-input 50") # 50 is maximum
+ cls.vapi.cli('packet-generator enable')
+ cls._zombie_captures = cls._captures
+ cls._captures = []
+
+ @classmethod
+ def create_pg_interfaces(cls, interfaces):
+ """
+ Create packet-generator interfaces.
+
+ :param interfaces: iterable indexes of the interfaces.
+ :returns: List of created interfaces.
+
+ """
+ result = []
+ for i in interfaces:
+ intf = VppPGInterface(cls, i)
+ setattr(cls, intf.name, intf)
+ result.append(intf)
+ cls.pg_interfaces = result
+ return result
+
+ @classmethod
+ def create_loopback_interfaces(cls, count):
+ """
+ Create loopback interfaces.
+
+ :param count: number of interfaces created.
+ :returns: List of created interfaces.
+ """
+ result = [VppLoInterface(cls) for i in range(count)]
+ for intf in result:
+ setattr(cls, intf.name, intf)
+ cls.lo_interfaces = result
+ return result
+
+ @staticmethod
+ def extend_packet(packet, size, padding=' '):
+ """
+ Extend packet to given size by padding with spaces or custom padding
+ NOTE: Currently works only when Raw layer is present.
+
+ :param packet: packet
+ :param size: target size
+ :param padding: padding used to extend the payload
+
+ """
+ packet_len = len(packet) + 4
+ extend = size - packet_len
+ if extend > 0:
+ num = (extend / len(padding)) + 1
+ packet[Raw].load += (padding * num)[:extend]
+
+ @classmethod
+ def reset_packet_infos(cls):
+ """ Reset the list of packet info objects and packet counts to zero """
+ cls._packet_infos = {}
+ cls._packet_count_for_dst_if_idx = {}
+
+ @classmethod
+ def create_packet_info(cls, src_if, dst_if):
+ """
+ Create packet info object containing the source and destination indexes
+ and add it to the testcase's packet info list
+
+ :param VppInterface src_if: source interface
+ :param VppInterface dst_if: destination interface
+
+ :returns: _PacketInfo object
+
+ """
+ info = _PacketInfo()
+ info.index = len(cls._packet_infos)
+ info.src = src_if.sw_if_index
+ info.dst = dst_if.sw_if_index
+ if isinstance(dst_if, VppSubInterface):
+ dst_idx = dst_if.parent.sw_if_index
+ else:
+ dst_idx = dst_if.sw_if_index
+ if dst_idx in cls._packet_count_for_dst_if_idx:
+ cls._packet_count_for_dst_if_idx[dst_idx] += 1
+ else:
+ cls._packet_count_for_dst_if_idx[dst_idx] = 1
+ cls._packet_infos[info.index] = info
+ return info
+
+ @staticmethod
+ def info_to_payload(info):
+ """
+ Convert _PacketInfo object to packet payload
+
+ :param info: _PacketInfo object
+
+ :returns: string containing serialized data from packet info
+ """
+ return "%d %d %d %d %d" % (info.index, info.src, info.dst,
+ info.ip, info.proto)
+
+ @staticmethod
+ def payload_to_info(payload):
+ """
+ Convert packet payload to _PacketInfo object
+
+ :param payload: packet payload
+
+ :returns: _PacketInfo object containing de-serialized data from payload
+
+ """
+ numbers = payload.split()
+ info = _PacketInfo()
+ info.index = int(numbers[0])
+ info.src = int(numbers[1])
+ info.dst = int(numbers[2])
+ info.ip = int(numbers[3])
+ info.proto = int(numbers[4])
+ return info
+
+ def get_next_packet_info(self, info):
+ """
+ Iterate over the packet info list stored in the testcase
+ Start iteration with first element if info is None
+ Continue based on index in info if info is specified
+
+ :param info: info or None
+ :returns: next info in list or None if no more infos
+ """
+ if info is None:
+ next_index = 0
+ else:
+ next_index = info.index + 1
+ if next_index == len(self._packet_infos):
+ return None
+ else:
+ return self._packet_infos[next_index]
+
+ def get_next_packet_info_for_interface(self, src_index, info):
+ """
+ Search the packet info list for the next packet info with same source
+ interface index
+
+ :param src_index: source interface index to search for
+ :param info: packet info - where to start the search
+ :returns: packet info or None
+
+ """
+ while True:
+ info = self.get_next_packet_info(info)
+ if info is None:
+ return None
+ if info.src == src_index:
+ return info
+
+ def get_next_packet_info_for_interface2(self, src_index, dst_index, info):
+ """
+ Search the packet info list for the next packet info with same source
+ and destination interface indexes
+
+ :param src_index: source interface index to search for
+ :param dst_index: destination interface index to search for
+ :param info: packet info - where to start the search
+ :returns: packet info or None
+
+ """
+ while True:
+ info = self.get_next_packet_info_for_interface(src_index, info)
+ if info is None:
+ return None
+ if info.dst == dst_index:
+ return info
+
+ def assert_equal(self, real_value, expected_value, name_or_class=None):
+ if name_or_class is None:
+ self.assertEqual(real_value, expected_value)
+ return
+ try:
+ msg = "Invalid %s: %d('%s') does not match expected value %d('%s')"
+ msg = msg % (getdoc(name_or_class).strip(),
+ real_value, str(name_or_class(real_value)),
+ expected_value, str(name_or_class(expected_value)))
+ except Exception:
+ msg = "Invalid %s: %s does not match expected value %s" % (
+ name_or_class, real_value, expected_value)
+
+ self.assertEqual(real_value, expected_value, msg)
+
+ def assert_in_range(self,
+ real_value,
+ expected_min,
+ expected_max,
+ name=None):
+ if name is None:
+ msg = None
+ else:
+ msg = "Invalid %s: %s out of range <%s,%s>" % (
+ name, real_value, expected_min, expected_max)
+ self.assertTrue(expected_min <= real_value <= expected_max, msg)
+
+ def assert_packet_checksums_valid(self, packet,
+ ignore_zero_udp_checksums=True):
+ received = packet.__class__(str(packet))
+ self.logger.debug(
+ ppp("Verifying packet checksums for packet:", received))
+ udp_layers = ['UDP', 'UDPerror']
+ checksum_fields = ['cksum', 'chksum']
+ checksums = []
+ counter = 0
+ temp = received.__class__(str(received))
+ while True:
+ layer = temp.getlayer(counter)
+ if layer:
+ for cf in checksum_fields:
+ if hasattr(layer, cf):
+ if ignore_zero_udp_checksums and \
+ 0 == getattr(layer, cf) and \
+ layer.name in udp_layers:
+ continue
+ delattr(layer, cf)
+ checksums.append((counter, cf))
+ else:
+ break
+ counter = counter + 1
+ if 0 == len(checksums):
+ return
+ temp = temp.__class__(str(temp))
+ for layer, cf in checksums:
+ calc_sum = getattr(temp[layer], cf)
+ self.assert_equal(
+ getattr(received[layer], cf), calc_sum,
+ "packet checksum on layer #%d: %s" % (layer, temp[layer].name))
+ self.logger.debug(
+ "Checksum field `%s` on `%s` layer has correct value `%s`" %
+ (cf, temp[layer].name, calc_sum))
+
+ def assert_checksum_valid(self, received_packet, layer,
+ field_name='chksum',
+ ignore_zero_checksum=False):
+ """ Check checksum of received packet on given layer """
+ received_packet_checksum = getattr(received_packet[layer], field_name)
+ if ignore_zero_checksum and 0 == received_packet_checksum:
+ return
+ recalculated = received_packet.__class__(str(received_packet))
+ delattr(recalculated[layer], field_name)
+ recalculated = recalculated.__class__(str(recalculated))
+ self.assert_equal(received_packet_checksum,
+ getattr(recalculated[layer], field_name),
+ "packet checksum on layer: %s" % layer)
+
+ def assert_ip_checksum_valid(self, received_packet,
+ ignore_zero_checksum=False):
+ self.assert_checksum_valid(received_packet, 'IP',
+ ignore_zero_checksum=ignore_zero_checksum)
+
+ def assert_tcp_checksum_valid(self, received_packet,
+ ignore_zero_checksum=False):
+ self.assert_checksum_valid(received_packet, 'TCP',
+ ignore_zero_checksum=ignore_zero_checksum)
+
+ def assert_udp_checksum_valid(self, received_packet,
+ ignore_zero_checksum=True):
+ self.assert_checksum_valid(received_packet, 'UDP',
+ ignore_zero_checksum=ignore_zero_checksum)
+
+ def assert_embedded_icmp_checksum_valid(self, received_packet):
+ if received_packet.haslayer(IPerror):
+ self.assert_checksum_valid(received_packet, 'IPerror')
+ if received_packet.haslayer(TCPerror):
+ self.assert_checksum_valid(received_packet, 'TCPerror')
+ if received_packet.haslayer(UDPerror):
+ self.assert_checksum_valid(received_packet, 'UDPerror',
+ ignore_zero_checksum=True)
+ if received_packet.haslayer(ICMPerror):
+ self.assert_checksum_valid(received_packet, 'ICMPerror')
+
+ def assert_icmp_checksum_valid(self, received_packet):
+ self.assert_checksum_valid(received_packet, 'ICMP')
+ self.assert_embedded_icmp_checksum_valid(received_packet)
+
+ def assert_icmpv6_checksum_valid(self, pkt):
+ if pkt.haslayer(ICMPv6DestUnreach):
+ self.assert_checksum_valid(pkt, 'ICMPv6DestUnreach', 'cksum')
+ self.assert_embedded_icmp_checksum_valid(pkt)
+ if pkt.haslayer(ICMPv6EchoRequest):
+ self.assert_checksum_valid(pkt, 'ICMPv6EchoRequest', 'cksum')
+ if pkt.haslayer(ICMPv6EchoReply):
+ self.assert_checksum_valid(pkt, 'ICMPv6EchoReply', 'cksum')
+
+ def assert_packet_counter_equal(self, counter, expected_value):
+ counters = self.vapi.cli("sh errors").split('\n')
+ counter_value = -1
+ for i in range(1, len(counters)-1):
+ results = counters[i].split()
+ if results[1] == counter:
+ counter_value = int(results[0])
+ break
+ self.assert_equal(counter_value, expected_value,
+ "packet counter `%s'" % counter)
+
+ @classmethod
+ def sleep(cls, timeout, remark=None):
+ if hasattr(cls, 'logger'):
+ cls.logger.debug("Starting sleep for %ss (%s)" % (timeout, remark))
+ before = time.time()
+ time.sleep(timeout)
+ after = time.time()
+ if after - before > 2 * timeout:
+ cls.logger.error("unexpected time.sleep() result - "
+ "slept for %ss instead of ~%ss!" % (
+ after - before, timeout))
+ if hasattr(cls, 'logger'):
+ cls.logger.debug(
+ "Finished sleep (%s) - slept %ss (wanted %ss)" % (
+ remark, after - before, timeout))
+
+ def send_and_assert_no_replies(self, intf, pkts, remark="", timeout=None):
+ self.vapi.cli("clear trace")
+ intf.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ if not timeout:
+ timeout = 1
+ for i in self.pg_interfaces:
+ i.get_capture(0, timeout=timeout)
+ i.assert_nothing_captured(remark=remark)
+ timeout = 0.1
+
+ def send_and_expect(self, input, pkts, output):
+ self.vapi.cli("clear trace")
+ input.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ if isinstance(object, (list,)):
+ rx = []
+ for o in output:
+ rx.append(output.get_capture(len(pkts)))
+ else:
+ rx = output.get_capture(len(pkts))
+ return rx
+
+ def send_and_expect_only(self, input, pkts, output, timeout=None):
+ self.vapi.cli("clear trace")
+ input.add_stream(pkts)
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+ if isinstance(object, (list,)):
+ outputs = output
+ rx = []
+ for o in outputs:
+ rx.append(output.get_capture(len(pkts)))
+ else:
+ rx = output.get_capture(len(pkts))
+ outputs = [output]
+ if not timeout:
+ timeout = 1
+ for i in self.pg_interfaces:
+ if i not in outputs:
+ i.get_capture(0, timeout=timeout)
+ i.assert_nothing_captured()
+ timeout = 0.1
+
+ return rx
+
+
+def get_testcase_doc_name(test):
+ return getdoc(test.__class__).splitlines()[0]
+
+
+def get_test_description(descriptions, test):
+ short_description = test.shortDescription()
+ if descriptions and short_description:
+ return short_description
+ else:
+ return str(test)
+
+
+class TestCaseInfo(object):
+ def __init__(self, logger, tempdir, vpp_pid, vpp_bin_path):
+ self.logger = logger
+ self.tempdir = tempdir
+ self.vpp_pid = vpp_pid
+ self.vpp_bin_path = vpp_bin_path
+ self.core_crash_test = None
+
+
+class VppTestResult(unittest.TestResult):
+ """
+ @property result_string
+ String variable to store the test case result string.
+ @property errors
+ List variable containing 2-tuples of TestCase instances and strings
+ holding formatted tracebacks. Each tuple represents a test which
+ raised an unexpected exception.
+ @property failures
+ List variable containing 2-tuples of TestCase instances and strings
+ holding formatted tracebacks. Each tuple represents a test where
+ a failure was explicitly signalled using the TestCase.assert*()
+ methods.
+ """
+
+ failed_test_cases_info = set()
+ core_crash_test_cases_info = set()
+ current_test_case_info = None
+
+ def __init__(self, stream, descriptions, verbosity, runner):
+ """
+ :param stream File descriptor to store where to report test results.
+ Set to the standard error stream by default.
+ :param descriptions Boolean variable to store information if to use
+ test case descriptions.
+ :param verbosity Integer variable to store required verbosity level.
+ """
+ unittest.TestResult.__init__(self, stream, descriptions, verbosity)
+ self.stream = stream
+ self.descriptions = descriptions
+ self.verbosity = verbosity
+ self.result_string = None
+ self.runner = runner
+
+ def addSuccess(self, test):
+ """
+ Record a test succeeded result
+
+ :param test:
+
+ """
+ if self.current_test_case_info:
+ self.current_test_case_info.logger.debug(
+ "--- addSuccess() %s.%s(%s) called" % (test.__class__.__name__,
+ test._testMethodName,
+ test._testMethodDoc))
+ unittest.TestResult.addSuccess(self, test)
+ self.result_string = colorize("OK", GREEN)
+
+ self.send_result_through_pipe(test, PASS)
+
+ def addSkip(self, test, reason):
+ """
+ Record a test skipped.
+
+ :param test:
+ :param reason:
+
+ """
+ if self.current_test_case_info:
+ self.current_test_case_info.logger.debug(
+ "--- addSkip() %s.%s(%s) called, reason is %s" %
+ (test.__class__.__name__, test._testMethodName,
+ test._testMethodDoc, reason))
+ unittest.TestResult.addSkip(self, test, reason)
+ self.result_string = colorize("SKIP", YELLOW)
+
+ self.send_result_through_pipe(test, SKIP)
+
+ def symlink_failed(self):
+ if self.current_test_case_info:
+ try:
+ failed_dir = os.getenv('VPP_TEST_FAILED_DIR')
+ link_path = os.path.join(
+ failed_dir,
+ '%s-FAILED' %
+ os.path.basename(self.current_test_case_info.tempdir))
+ if self.current_test_case_info.logger:
+ self.current_test_case_info.logger.debug(
+ "creating a link to the failed test")
+ self.current_test_case_info.logger.debug(
+ "os.symlink(%s, %s)" %
+ (self.current_test_case_info.tempdir, link_path))
+ if os.path.exists(link_path):
+ if self.current_test_case_info.logger:
+ self.current_test_case_info.logger.debug(
+ 'symlink already exists')
+ else:
+ os.symlink(self.current_test_case_info.tempdir, link_path)
+
+ except Exception as e:
+ if self.current_test_case_info.logger:
+ self.current_test_case_info.logger.error(e)
+
+ def send_result_through_pipe(self, test, result):
+ if hasattr(self, 'test_framework_result_pipe'):
+ pipe = self.test_framework_result_pipe
+ if pipe:
+ pipe.send((test.id(), result))
+
+ def log_error(self, test, err, fn_name):
+ if self.current_test_case_info:
+ if isinstance(test, unittest.suite._ErrorHolder):
+ test_name = test.description
+ else:
+ test_name = '%s.%s(%s)' % (test.__class__.__name__,
+ test._testMethodName,
+ test._testMethodDoc)
+ self.current_test_case_info.logger.debug(
+ "--- %s() %s called, err is %s" %
+ (fn_name, test_name, err))
+ self.current_test_case_info.logger.debug(
+ "formatted exception is:\n%s" %
+ "".join(format_exception(*err)))
+
+ def add_error(self, test, err, unittest_fn, error_type):
+ if error_type == FAIL:
+ self.log_error(test, err, 'addFailure')
+ error_type_str = colorize("FAIL", RED)
+ elif error_type == ERROR:
+ self.log_error(test, err, 'addError')
+ error_type_str = colorize("ERROR", RED)
+ else:
+ raise Exception('Error type %s cannot be used to record an '
+ 'error or a failure' % error_type)
+
+ unittest_fn(self, test, err)
+ if self.current_test_case_info:
+ self.result_string = "%s [ temp dir used by test case: %s ]" % \
+ (error_type_str,
+ self.current_test_case_info.tempdir)
+ self.symlink_failed()
+ self.failed_test_cases_info.add(self.current_test_case_info)
+ if is_core_present(self.current_test_case_info.tempdir):
+ if not self.current_test_case_info.core_crash_test:
+ if isinstance(test, unittest.suite._ErrorHolder):
+ test_name = str(test)
+ else:
+ test_name = "'{}' ({})".format(
+ get_testcase_doc_name(test), test.id())
+ self.current_test_case_info.core_crash_test = test_name
+ self.core_crash_test_cases_info.add(
+ self.current_test_case_info)
+ else:
+ self.result_string = '%s [no temp dir]' % error_type_str
+
+ self.send_result_through_pipe(test, error_type)
+
+ def addFailure(self, test, err):
+ """
+ Record a test failed result
+
+ :param test:
+ :param err: error message
+
+ """
+ self.add_error(test, err, unittest.TestResult.addFailure, FAIL)
+
+ def addError(self, test, err):
+ """
+ Record a test error result
+
+ :param test:
+ :param err: error message
+
+ """
+ self.add_error(test, err, unittest.TestResult.addError, ERROR)
+
+ def getDescription(self, test):
+ """
+ Get test description
+
+ :param test:
+ :returns: test description
+
+ """
+ return get_test_description(self.descriptions, test)
+
+ def startTest(self, test):
+ """
+ Start a test
+
+ :param test:
+
+ """
+ test.print_header()
+
+ unittest.TestResult.startTest(self, test)
+ if self.verbosity > 0:
+ self.stream.writeln(
+ "Starting " + self.getDescription(test) + " ...")
+ self.stream.writeln(single_line_delim)
+
+ def stopTest(self, test):
+ """
+ Called when the given test has been run
+
+ :param test:
+
+ """
+ unittest.TestResult.stopTest(self, test)
+ if self.verbosity > 0:
+ self.stream.writeln(single_line_delim)
+ self.stream.writeln("%-73s%s" % (self.getDescription(test),
+ self.result_string))
+ self.stream.writeln(single_line_delim)
+ else:
+ self.stream.writeln("%-73s%s" % (self.getDescription(test),
+ self.result_string))
+
+ self.send_result_through_pipe(test, TEST_RUN)
+
+ def printErrors(self):
+ """
+ Print errors from running the test case
+ """
+ if len(self.errors) > 0 or len(self.failures) > 0:
+ self.stream.writeln()
+ self.printErrorList('ERROR', self.errors)
+ self.printErrorList('FAIL', self.failures)
+
+ # ^^ that is the last output from unittest before summary
+ if not self.runner.print_summary:
+ devnull = unittest.runner._WritelnDecorator(open(os.devnull, 'w'))
+ self.stream = devnull
+ self.runner.stream = devnull
+
+ def printErrorList(self, flavour, errors):
+ """
+ Print error list to the output stream together with error type
+ and test case description.
+
+ :param flavour: error type
+ :param errors: iterable errors
+
+ """
+ for test, err in errors:
+ self.stream.writeln(double_line_delim)
+ self.stream.writeln("%s: %s" %
+ (flavour, self.getDescription(test)))
+ self.stream.writeln(single_line_delim)
+ self.stream.writeln("%s" % err)
+
+
+class VppTestRunner(unittest.TextTestRunner):
+ """
+ A basic test runner implementation which prints results to standard error.
+ """
+ @property
+ def resultclass(self):
+ """Class maintaining the results of the tests"""
+ return VppTestResult
+
+ def __init__(self, keep_alive_pipe=None, descriptions=True, verbosity=1,
+ result_pipe=None, failfast=False, buffer=False,
+ resultclass=None, print_summary=True):
+
+ # ignore stream setting here, use hard-coded stdout to be in sync
+ # with prints from VppTestCase methods ...
+ super(VppTestRunner, self).__init__(sys.stdout, descriptions,
+ verbosity, failfast, buffer,
+ resultclass)
+ KeepAliveReporter.pipe = keep_alive_pipe
+
+ self.orig_stream = self.stream
+ self.resultclass.test_framework_result_pipe = result_pipe
+
+ self.print_summary = print_summary
+
+ def _makeResult(self):
+ return self.resultclass(self.stream,
+ self.descriptions,
+ self.verbosity,
+ self)
+
+ def run(self, test):
+ """
+ Run the tests
+
+ :param test:
+
+ """
+ faulthandler.enable() # emit stack trace to stderr if killed by signal
+
+ result = super(VppTestRunner, self).run(test)
+ if not self.print_summary:
+ self.stream = self.orig_stream
+ result.stream = self.orig_stream
+ return result
+
+
+class Worker(Thread):
+ def __init__(self, args, logger, env={}):
+ self.logger = logger
+ self.args = args
+ self.result = None
+ self.env = copy.deepcopy(env)
+ super(Worker, self).__init__()
+
+ def run(self):
+ executable = self.args[0]
+ self.logger.debug("Running executable w/args `%s'" % self.args)
+ env = os.environ.copy()
+ env.update(self.env)
+ env["CK_LOG_FILE_NAME"] = "-"
+ self.process = subprocess.Popen(
+ self.args, shell=False, env=env, preexec_fn=os.setpgrp,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = self.process.communicate()
+ self.logger.debug("Finished running `%s'" % executable)
+ self.logger.info("Return code is `%s'" % self.process.returncode)
+ self.logger.info(single_line_delim)
+ self.logger.info("Executable `%s' wrote to stdout:" % executable)
+ self.logger.info(single_line_delim)
+ self.logger.info(out)
+ self.logger.info(single_line_delim)
+ self.logger.info("Executable `%s' wrote to stderr:" % executable)
+ self.logger.info(single_line_delim)
+ self.logger.info(err)
+ self.logger.info(single_line_delim)
+ self.result = self.process.returncode
diff --git a/Test/test_jvpp.py b/Test/test_jvpp.py
new file mode 100644
index 0000000..6151ebd
--- /dev/null
+++ b/Test/test_jvpp.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+import os
+import subprocess
+import unittest
+
+from framework import VppTestCase, running_extended_tests
+
+# Api files path
+API_FILES_PATH = "japi/java"
+
+# Registry jar file name prefix
+REGISTRY_JAR_PREFIX = "jvpp-registry"
+
+
+@unittest.skipUnless(running_extended_tests(), "part of extended tests")
+class TestJVpp(VppTestCase):
+ """ JVPP Core Test Case """
+
+ def invoke_for_jvpp_core(self, api_jar_name, test_class_name):
+ self.jvpp_connection_test(api_jar_name=api_jar_name,
+ test_class_name=test_class_name,
+ timeout=10)
+
+ def test_vpp_core_callback_api(self):
+ """ JVPP Core Callback Api Test Case """
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-core",
+ test_class_name="io.fd.vpp.jvpp.core.test."
+ "CallbackApiTest")
+
+ def test_vpp_core_future_api(self):
+ """JVPP Core Future Api Test Case"""
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-core",
+ test_class_name="io.fd.vpp.jvpp.core.test."
+ "FutureApiTest")
+
+ def test_vpp_acl_callback_api(self):
+ """ JVPP Acl Callback Api Test Case """
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-acl",
+ test_class_name="io.fd.vpp.jvpp.acl.test."
+ "CallbackApiTest")
+
+ def test_vpp_acl_future_api(self):
+ """JVPP Acl Future Api Test Case"""
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-acl",
+ test_class_name="io.fd.vpp.jvpp.acl.test."
+ "FutureApiTest")
+
+ def test_vpp_ioamexport_callback_api(self):
+ """ JVPP Ioamexport Callback Api Test Case """
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamexport",
+ test_class_name="io.fd.vpp.jvpp.ioamexport."
+ "test.CallbackApiTest")
+
+ def test_vpp_ioamexport_future_api(self):
+ """JVPP Ioamexport Future Api Test Case"""
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamexport",
+ test_class_name="io.fd.vpp.jvpp.ioamexport."
+ "test.FutureApiTest")
+
+ def test_vpp_ioampot_callback_api(self):
+ """ JVPP Ioampot Callback Api Test Case """
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-ioampot",
+ test_class_name="io.fd.vpp.jvpp.ioampot."
+ "test.CallbackApiTest")
+
+ def test_vpp_ioampot_future_api(self):
+ """JVPP Ioampot Future Api Test Case"""
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-ioampot",
+ test_class_name="io.fd.vpp.jvpp.ioampot."
+ "test.FutureApiTest")
+
+ def test_vpp_ioamtrace_callback_api(self):
+ """ JVPP Ioamtrace Callback Api Test Case """
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamtrace",
+ test_class_name="io.fd.vpp.jvpp.ioamtrace."
+ "test.CallbackApiTest")
+
+ def test_vpp_ioamtrace_future_api(self):
+ """JVPP Ioamtrace Future Api Test Case"""
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamtrace",
+ test_class_name="io.fd.vpp.jvpp.ioamtrace."
+ "test.FutureApiTest")
+
+ def test_vpp_snat_callback_api(self):
+ """ JVPP Snat Callback Api Test Case """
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-nat",
+ test_class_name="io.fd.vpp.jvpp.nat.test."
+ "CallbackApiTest")
+
+ def test_vpp_snat_future_api(self):
+ """JVPP Snat Future Api Test Case"""
+ self.invoke_for_jvpp_core(api_jar_name="jvpp-nat",
+ test_class_name="io.fd.vpp.jvpp.nat.test."
+ "FutureApiTest")
+
+ def full_jar_name(self, install_dir, jar_name, version):
+ return os.path.join(install_dir, API_FILES_PATH,
+ "{0}-{1}.jar".format(jar_name, version))
+
+ def jvpp_connection_test(self, api_jar_name, test_class_name, timeout):
+ install_dir = os.getenv('VPP_TEST_BUILD_DIR')
+ self.logger.info("Install directory : {0}".format(install_dir))
+
+ version_reply = self.vapi.show_version()
+ version = version_reply.version.split("-")[0]
+ registry_jar_path = self.full_jar_name(install_dir,
+ REGISTRY_JAR_PREFIX, version)
+ self.logger.info("JVpp Registry jar path : {0}"
+ .format(registry_jar_path))
+ if (not os.path.isfile(registry_jar_path)):
+ raise Exception(
+ "JVpp Registry jar has not been found: {0}"
+ .format(registry_jar_path))
+
+ api_jar_path = self.full_jar_name(install_dir, api_jar_name, version)
+ self.logger.info("Api jar path : {0}".format(api_jar_path))
+ if (not os.path.isfile(api_jar_path)):
+ raise Exception(
+ "Api jar has not been found: {0}".format(api_jar_path))
+
+ # passes shm prefix as parameter to create connection with same value
+ command = ["java", "-cp",
+ "{0}:{1}".format(registry_jar_path, api_jar_path),
+ test_class_name, "/{0}-vpe-api".format(self.shm_prefix)]
+ self.logger.info("Test Command : {0}, Timeout : {1}".
+ format(command, timeout))
+
+ self.process = subprocess.Popen(command, shell=False,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, bufsize=1,
+ universal_newlines=True)
+
+ out, err = self.process.communicate()
+ self.logger.info("Process output : {0}{1}".format(os.linesep, out))
+
+ if self.process.returncode != 0:
+ raise subprocess.CalledProcessError(
+ "Command {0} failed with return code: {1}.{2}"
+ "Process error output: {2}{3}"
+ .format(command, self.process.returncode, os.linesep, err))
+
+ def tearDown(self):
+ self.logger.info("Tearing down jvpp test")
+ super(TestJVpp, self).tearDown()
+ if hasattr(self, 'process') and self.process.poll() is None:
+ self.process.kill()
diff --git a/clean.sh b/clean.sh
new file mode 100755
index 0000000..4f81a09
--- /dev/null
+++ b/clean.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+echo "JVPP cleanup started"
+make clean
+rm -rf build-root/packages/*
+rm -rf java/*.jar
+# clean cmake cache
+find . -iwholename '*cmake*' -not -name CMakeLists.txt -delete
+# clean cpack cache
+find . -iwholename '*cpack*' -delete
+echo "... cleanup finished."
diff --git a/docs/jvpp.rst b/docs/jvpp.rst
new file mode 100644
index 0000000..4fbeb39
--- /dev/null
+++ b/docs/jvpp.rst
@@ -0,0 +1,140 @@
+.. _JVPP:
+
+.. toctree::
+
+Building JVPP from source
+=========================
+
+JVPP uses cmake to configure and build source code. The minimal version of cmake is 3.5.
+Ensure that you have proper version of cmake installed.
+
+JVPP depends on VPP packages (Debian or Centos):
+* vpp
+* vpp-plugins
+* vpp-dev (vpp-devel on Centos)
+
+Vpp and vpp-plugins packages contain API source file, which are used to generate Java API bindings. Vpp dev package
+contains header files needed for build.
+
+Obtaining the source
+--------------------
+
+You can get JVPP source code from gerrit.fd.io using git:
+git clone https://gerrit.fd.io/r/jvpp
+
+or from github:
+https://github.com/FDio/jvpp.git
+
+
+Install dependencies (Optional, one time only)
+------------------
+
+You can install all dependencies using provided install-dep target:
+
+.. code-block:: console
+
+ make install-dep
+
+Cleanup (Optional)
+------------------
+
+Whenever you need to clean the setup you can use "clean.sh" script in the root folder. This cleans the build and clears
+the cache. This is usefull mostly when you change something in JVPP to ensure everything is rebuild from scratch.
+
+Configuring using cmake
+-----------------------
+
+Configuration using cmake is very easy, the whole process is automated. All you need to do is issue following command
+from JVPP's root directory:
+
+.. code-block:: console
+
+ cmake .
+
+This will configure all variables and setup the build.
+
+Building the source
+-------------------
+
+To build the source use make command:
+
+.. code-block:: console
+
+ make
+
+You can also install the library using (you need to use sudo or have root privileges to install libraries):
+
+.. code-block:: console
+
+ sudo make install
+
+Building the packages
+---------------------
+
+During the setup using cmake the operating system is automatically recognised and everything is set up so you can build
+packages for your current operating system. Only Debian based (tested on Ubuntu) and Centos (tested on Centos 7)
+are supported for now.
+
+To build the package you need to call:
+
+.. code-block:: console
+
+ make package
+
+You can find the packages in build-root/packages folder.
+
+Getting JVPP jar
+================
+
+VPP provides java bindings which can be downloaded at:
+
+* https://nexus.fd.io/content/repositories/fd.io.release/io/fd/vpp/jvpp-core/19.01/jvpp-core-19.01.jar
+
+Getting JVPP via maven
+------------------------------------
+
+**1. Add the following to the repositories section in your ~/.m2/settings.xml to pick up the fd.io maven repo:**
+
+.. code-block:: console
+
+ <repository>
+ <id>fd.io-release</id>
+ <name>fd.io-release</name>
+ <url>https://nexus.fd.io/content/repositories/fd.io.release/</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+
+For more information on setting up maven repositories in settings.xml, please look at:
+
+* https://maven.apache.org/guides/mini/guide-multiple-repositories.html
+
+**2. Then you can get JVPP by putting in the dependencies section of your pom.xml file:**
+
+.. code-block:: console
+
+ <dependency>
+ <groupId>io.fd.vpp</groupId>
+ <artifactId>jvpp-core</artifactId>
+ <version>19.01</version>
+ </dependency>
+
+For more information on maven dependency managment, please look at:
+
+* https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
+
+
+Installing JVPP manually to local maven repository
+--------------------------------------------------
+
+Once JVPP is successfully built, you can install it to local .m2 repository.
+To do so use provided script in JVPP root directory:
+
+.. code-block:: console
+
+ ./install_jvpp.sh
+
diff --git a/install_jvpp.sh b/install_jvpp.sh
new file mode 100755
index 0000000..1c3a101
--- /dev/null
+++ b/install_jvpp.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+MAIN_VER="$(./version | cut -f1 -d"-")"
+VERSION="$(./version | cut -f1 -d"~")"
+
+echo "Main version: ${MAIN_VER}"
+echo "full version: ${VERSION}"
+cd ./java
+
+echo "Installing jars to Maven:"
+mvn install:install-file -Dfile=jvpp-registry-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-registry -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-core-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-core -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-ioampot-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-ioampot -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-ioamtrace-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-ioamtrace -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-ioamexport-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-ioamexport -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-nat-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-nat -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-nsh-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-nsh -Dversion=${VERSION} -Dpackaging=jar
+mvn install:install-file -Dfile=jvpp-acl-${MAIN_VER}.jar -DgroupId=io.fd.vpp -DartifactId=jvpp-acl -Dversion=${VERSION} -Dpackaging=jar
+
+echo "all done." \ No newline at end of file
diff --git a/java/.gitignore b/java/.gitignore
new file mode 100644
index 0000000..c69ba33
--- /dev/null
+++ b/java/.gitignore
@@ -0,0 +1,6 @@
+# Negate "No core files" pattern from the toplevel .gitignore
+!**/core
+/CMakeFiles
+Makefile
+cmake_install.cmake
+*.jar \ No newline at end of file
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
new file mode 100644
index 0000000..2da75d6
--- /dev/null
+++ b/java/CMakeLists.txt
@@ -0,0 +1,331 @@
+# 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.
+
+find_path(VNET_INCLUDE_DIR NAMES vnet/api_errno.h)
+find_library(VPPINFRA_DIR NAMES vppinfra REQUIRED)
+find_library(VLIBMEMORYCLIENT_DIR NAMES vlibmemoryclient REQUIRED)
+find_library(SVM_DIR NAMES svm REQUIRED)
+
+include_directories(${VNET_INCLUDE_DIR}
+ ${VNET_INCLUDE_DIR}/vpp_plugins
+ ${VNET_INCLUDE_DIR}/vpp_api
+ ${Java_INCLUDE_DIRS}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR/../vpp/plugins})
+
+add_compile_options(-Wall)
+############# Common package ##################
+add_library(jvpp_common SHARED jvpp-common/jvpp_common.c)
+set_target_properties(jvpp_common PROPERTIES SOVERSION ${JAPI_LIB_VERSION})
+target_link_libraries(jvpp_common ${VPPINFRA_DIR})
+install(TARGETS jvpp_common DESTINATION lib COMPONENT libjvpp_common)
+install(FILES jvpp-common/jvpp_common.h DESTINATION include/japi/)
+
+set(JVPP_LIBS jvpp_common ${VPPINFRA_DIR} ${VLIBMEMORYCLIENT_DIR} ${SVM_DIR}
+ Threads::Threads m rt)
+
+############# Registry package ##################
+set(PACKAGE_DIR_JVPP_REGISTRY io/fd/vpp/jvpp)
+unset(files)
+FILE(GLOB files RELATIVE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/jvpp-registry/${PACKAGE_DIR_JVPP_REGISTRY}/*.java
+ ${CMAKE_CURRENT_SOURCE_DIR}/jvpp-registry/${PACKAGE_DIR_JVPP_REGISTRY}/*/*.java
+)
+
+add_custom_target (jvpp-registry-classes)
+add_custom_command (TARGET jvpp-registry-classes
+ PRE_BUILD
+ COMMAND mkdir -p jvpp-registry/target
+ COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/jvpp-registry
+ COMMAND ${Java_JAVAC_EXECUTABLE}
+ ARGS -d ${CMAKE_CURRENT_SOURCE_DIR}/jvpp-registry/target -h jvpp-registry ${files}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+add_library(jvpp_registry SHARED jvpp-registry/jvpp_registry.c)
+target_link_libraries(jvpp_registry ${JVPP_LIBS})
+include_directories(jvpp-registry)
+add_dependencies(jvpp_registry jvpp_common jvpp-registry-classes)
+
+add_custom_target (jvpp-registry)
+add_dependencies(jvpp-registry jvpp_registry)
+add_custom_command(TARGET jvpp-registry
+ PRE_BUILD
+ COMMAND cp ${CMAKE_BINARY_DIR}/build-root/lib/libjvpp_registry.so jvpp-registry/target
+ COMMAND ${Java_JAR_EXECUTABLE} ARGS cf
+ ${CMAKE_CURRENT_BINARY_DIR}/jvpp-registry-${JAPI_LIB_VERSION}.jar
+ -C jvpp-registry/target .
+ COMMAND rm ARGS -rf jvpp-registry/target
+ jvpp-registry/io_fd_vpp_jvpp_*.h
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "JAR_GEN registry"
+)
+install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/jvpp-registry-${JAPI_LIB_VERSION}.jar
+ DESTINATION share/java
+)
+
+############## Functions #########################
+function(japigen name)
+ if(NOT VPP_JAVA_APIGEN)
+ set(VPP_JAVA_APIGEN ${CMAKE_CURRENT_SOURCE_DIR}/jvpp/gen/jvpp_gen.py)
+ endif()
+ add_custom_target(japigen-${name} DEPENDS jvpp-registry)
+ add_custom_command(TARGET japigen-${name}
+ POST_BUILD
+ COMMAND mkdir -p jvpp-${name}/target
+ COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/jvpp-${name}
+ COMMAND ${VPP_JAVA_APIGEN}
+ ARGS --plugin_name ${name} --root_dir jvpp-${name} -i ${ARGN}
+ COMMAND find jvpp-${name} -name \*.java > jvpp-${name}/jvpp-${name}.files
+ COMMAND ${Java_JAVAC_EXECUTABLE}
+ ARGS -cp ${CMAKE_CURRENT_BINARY_DIR}/jvpp-registry-${JAPI_LIB_VERSION}.jar -d
+ ${CMAKE_CURRENT_SOURCE_DIR}/jvpp-${name}/target -h jvpp-${name}
+ @jvpp-${name}/jvpp-${name}.files
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "JAVA_API_GEN ${name}"
+ )
+endfunction()
+
+function(jargen name)
+ add_custom_command(TARGET jvpp_${name}
+ POST_BUILD
+ COMMAND cp ${CMAKE_BINARY_DIR}/build-root/lib/libjvpp_${name}.so jvpp-${name}/target
+ COMMAND ${Java_JAR_EXECUTABLE} ARGS cf
+ ${CMAKE_CURRENT_BINARY_DIR}/jvpp-${name}-${JAPI_LIB_VERSION}.jar
+ -C jvpp-${name}/target .
+ COMMAND rm ARGS -rf jvpp-${name}/target jvpp-${name}/jvpp-${name}.files
+ jvpp-${name}/jvpp_${name}_gen.h jvpp-${name}/io_fd_vpp_jvpp_*.h
+ jvpp-registry/io_fd_vpp_jvpp_*.h
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ COMMENT "JAR_GEN ${name}"
+ )
+ install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/jvpp-${name}-${JAPI_LIB_VERSION}.jar
+ DESTINATION share/java
+ )
+endfunction()
+
+function(java_api_binding name src_file)
+ japigen (${name} ${ARGN})
+ add_library(jvpp_${name} SHARED jvpp-${name}/jvpp_${src_file}.c)
+ target_link_libraries(jvpp_${name} ${JVPP_LIBS})
+ include_directories(jvpp-${name})
+ add_dependencies(jvpp_${name} jvpp_common jvpp_registry japigen-${name})
+ jargen (${name})
+endfunction()
+
+############ Core Package #######################
+unset (files)
+unset (corefiles)
+
+if(${RELEASE_ID} MATCHES Ubuntu)
+ execute_process(COMMAND dpkg-query -L vpp
+ COMMAND grep -v memclnt
+ COMMAND grep api.json OUTPUT_VARIABLE corefiles)
+ STRING(REGEX REPLACE "\n" ";" corefiles "${corefiles}")
+elseif(${RELEASE_ID} MATCHES CentOS)
+ # Exclude vpp plugin api files. VPP package should not contain VPP-plugins api files and vice-versa. Temporary fixing
+ # by excluding them manually.
+ execute_process(COMMAND rpm -ql vpp
+ COMMAND grep -v memclnt
+ COMMAND grep -v abf
+ COMMAND grep -v acl
+ COMMAND grep -v avf
+ COMMAND grep -v cdp
+ COMMAND grep -v dpdk
+ COMMAND grep -v flowprobe
+ COMMAND grep -v gbp
+ COMMAND grep -v gtpu
+ COMMAND grep -v igmp
+ COMMAND grep -v ioam_cache
+ COMMAND grep -v ioam_export
+ COMMAND grep -v ioam_vxlan_gpe
+ COMMAND grep -v l2e
+ COMMAND grep -v lacp
+ COMMAND grep -v lb
+ COMMAND grep -v mactime
+ COMMAND grep -v map
+ COMMAND grep -v memif
+ COMMAND grep -v nat
+ COMMAND grep -v nsh
+ COMMAND grep -v nsim
+ COMMAND grep -v pot
+ COMMAND grep -v pppoe
+ COMMAND grep -v stn
+ COMMAND grep -v svs
+ COMMAND grep -v trace
+ COMMAND grep -v udp_ping
+ COMMAND grep -v vmxnet3
+ COMMAND grep -v vxlan_gpe_ioam_export
+ COMMAND grep api.json OUTPUT_VARIABLE corefiles)
+ STRING(REGEX REPLACE "\n" ";" corefiles "${corefiles}")
+endif()
+
+FILE(GLOB_RECURSE files RELATIVE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${corefiles}
+)
+
+# message("COREFILES: ${corefiles}")
+java_api_binding (core core ${files})
+
+############ Plugin Packages #######################
+unset (files)
+unset (pluginfiles)
+unset (ACL_JSON_FILE)
+unset (NAT_JSON_FILE)
+unset (NSH_JSON_FILE)
+unset (GTPU_JSON_FILE)
+unset (PPPOE_JSON_FILE)
+unset (IOAM_TRACE_JSON_FILE)
+unset (IOAM_POT_JSON_FILE)
+unset (IOAM_EXPORT_JSON_FILE)
+
+execute_process(COMMAND dpkg-query -L vpp-plugins
+ COMMAND grep api.json OUTPUT_VARIABLE pluginfiles)
+STRING(REGEX REPLACE "\n" ";" pluginfiles "${pluginfiles}")
+if(${RELEASE_ID} MATCHES Ubuntu)
+ execute_process(COMMAND dpkg-query -L vpp-plugins
+ COMMAND grep api.json OUTPUT_VARIABLE pluginfiles)
+ STRING(REGEX REPLACE "\n" ";" pluginfiles "${pluginfiles}")
+elseif(${RELEASE_ID} MATCHES CentOS)
+ execute_process(COMMAND rpm -ql vpp-plugins
+ COMMAND grep api.json OUTPUT_VARIABLE pluginfiles)
+ STRING(REGEX REPLACE "\n" ";" pluginfiles "${pluginfiles}")
+endif()
+
+# message("PLUGINFILES: ${pluginfiles}")
+FILE(GLOB_RECURSE files RELATIVE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${pluginfiles}
+ )
+
+foreach (FILE ${pluginfiles})
+ if ("${FILE}" MATCHES "/acl.api.json")
+ set(ACL_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/nat.api.json")
+ set(NAT_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/nsh.api.json")
+ set(NSH_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/gtpu.api.json")
+ set(GTPU_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/pppoe.api.json")
+ set(PPPOE_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/trace.api.json")
+ set(IOAM_TRACE_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/pot.api.json")
+ set(IOAM_POT_JSON_FILE ${FILE})
+ endif ()
+ if ("${FILE}" MATCHES "/ioam_export.api.json")
+ set(IOAM_EXPORT_JSON_FILE ${FILE})
+ endif ()
+endforeach()
+
+if(ACL_JSON_FILE)
+ java_api_binding (acl acl ${ACL_JSON_FILE})
+endif()
+
+if(NAT_JSON_FILE)
+ java_api_binding (nat nat ${NAT_JSON_FILE})
+endif()
+
+if(NSH_JSON_FILE)
+ java_api_binding (nsh nsh ${NSH_JSON_FILE})
+endif()
+
+if(GTPU_JSON_FILE)
+ java_api_binding (gtpu gtpu ${GTPU_JSON_FILE})
+endif()
+
+if(PPPOE_JSON_FILE)
+ java_api_binding (pppoe pppoe ${PPPOE_JSON_FILE})
+endif()
+
+if(IOAM_TRACE_JSON_FILE)
+ java_api_binding (ioamtrace ioam_trace ${IOAM_TRACE_JSON_FILE})
+endif()
+
+if(IOAM_POT_JSON_FILE)
+ java_api_binding (ioampot ioam_pot ${IOAM_POT_JSON_FILE})
+endif()
+
+if(IOAM_EXPORT_JSON_FILE)
+ java_api_binding (ioamexport ioam_export ${IOAM_EXPORT_JSON_FILE})
+endif()
+
+# Package Generator #######################################################
+unset(vpp_version)
+set(COLUMNS 200)
+execute_process(
+ COMMAND dpkg -l vpp
+ COMMAND grep vpp
+ COMMAND awk "{print $3}" OUTPUT_VARIABLE vpp_version)
+string(REGEX REPLACE "\n$" "" vpp_version "${vpp_version}")
+
+set(CPACK_PACKAGE_DESCRIPTION "VPP-Java-API-bindings
+ This package contains VPP java api bindings.")
+set(CPACK_PACKAGE_NAME "vpp-api-java")
+set(CPACK_PACKAGE_VERSION "${JVPP_VERSION}")
+set(CPACK_PACKAGE_SECTION net)
+set(CPACK_PACKAGE_VENDOR "Cisco")
+set(CPACK_PACKAGE_CONTACT "hc2vpp@lists.fd.io")
+set(CPACK_OUTPUT_FILE_PREFIX build-root/packages)
+set(CPACK_PACKAGE_FILE_NAME "vpp-api-java_${JVPP_VERSION}")
+if (${RELEASE_ID} MATCHES "Ubuntu")
+ set(CPACK_GENERATOR "DEB")
+ set(CPACK_DEBIAN_PACKAGE_NAME "${CPACK_PACKAGE_NAME}")
+ set(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}")
+ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
+ set(CPACK_DEBIAN_PACKAGE_SECTION "${CPACK_PACKAGE_SECTION}")
+ set(CPACK_DEBIAN_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME})
+ set(CPACK_DEBIAN_PACKAGE_PRIORITY extra)
+ set(CPACK_DEBIAN_PACKAGE_DEPENDS "vpp (>= ${vpp_version}), vpp-plugins (>= ${vpp_version})")
+
+ # Print Debian package summary
+ message("\nDebian package:")
+ message(" Name: ${CPACK_DEBIAN_PACKAGE_NAME}")
+ message(" Version: ${CPACK_DEBIAN_PACKAGE_VERSION}")
+ message(" Description: ${CPACK_DEBIAN_PACKAGE_DESCRIPTION}")
+ message(" File name: ${CPACK_DEBIAN_PACKAGE_FILE_NAME}")
+ message(" Depends on: ${CPACK_DEBIAN_PACKAGE_DEPENDS}\n")
+ include (CPack)
+elseif(${RELEASE_ID} MATCHES "CentOS")
+ set(CPACK_GENERATOR "RPM")
+ # Excluding /usr/share/java top level directory due to conflict with javapackages-tools (openjdk dependency)
+ set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/usr/share/java")
+ set(CPACK_RPM_PACKAGE_NAME "${CPACK_PACKAGE_NAME}")
+ set(CPACK_RPM_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}")
+ set(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}")
+ set(CPACK_RPM_PACKAGE_SECTION "${CPACK_PACKAGE_SECTION}")
+ set(CPACK_RPM_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME})
+ set(CPACK_RPM_PACKAGE_PRIORITY extra)
+ set(CPACK_RPM_PACKAGE_DEPENDS "vpp (>= ${vpp_version}), vpp-plugins (>= ${vpp_version})")
+
+ # Print CentOS package summary
+ message("\nRPM package:")
+ message(" Name: ${CPACK_RPM_PACKAGE_NAME}")
+ message(" Version: ${CPACK_RPM_PACKAGE_VERSION}")
+ message(" Description: ${CPACK_RPM_PACKAGE_DESCRIPTION}")
+ message(" File name: ${CPACK_RPM_PACKAGE_FILE_NAME}")
+ message(" Depends on: ${CPACK_RPM_PACKAGE_DEPENDS}\n")
+ include (CPack)
+endif ()
+
diff --git a/java/Readme.txt b/java/Readme.txt
new file mode 100644
index 0000000..689b9b3
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java b/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java
new file mode 100644
index 0000000..4806052
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java b/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java
new file mode 100644
index 0000000..199b1b6
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java b/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java
new file mode 100644
index 0000000..149ea46
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java b/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java
new file mode 100644
index 0000000..862df8d
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt b/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt
new file mode 100644
index 0000000..d17fbfc
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java b/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java
new file mode 100644
index 0000000..a7bbb7f
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java b/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java
new file mode 100644
index 0000000..ff1c73c
--- /dev/null
+++ b/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/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt b/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt
new file mode 100644
index 0000000..1b46585
--- /dev/null
+++ b/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/java/jvpp-acl/jvpp_acl.c b/java/jvpp-acl/jvpp_acl.c
new file mode 100644
index 0000000..22e44f1
--- /dev/null
+++ b/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/java/jvpp-acl/jvpp_acl.h b/java/jvpp-acl/jvpp_acl.h
new file mode 100644
index 0000000..d1ec78f
--- /dev/null
+++ b/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/java/jvpp-common/jvpp_common.c b/java/jvpp-common/jvpp_common.c
new file mode 100644
index 0000000..2425607
--- /dev/null
+++ b/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/java/jvpp-common/jvpp_common.h b/java/jvpp-common/jvpp_common.h
new file mode 100644
index 0000000..e12141b
--- /dev/null
+++ b/java/jvpp-common/jvpp_common.h
@@ -0,0 +1,75 @@
+/*
+ * 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 <vlibapi/api_types.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 clib_atomic_add_fetch(&jm->context_id, 1);
+}
+
+static_always_inline void vppjni_lock(jvpp_main_t * jm, u32 tag) {
+ while (clib_atomic_test_and_set(&jm->lock))
+ ;
+ 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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java
new file mode 100644
index 0000000..b6558ff
--- /dev/null
+++ b/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,
+ msg.program,
+ msg.version,
+ msg.buildDate,
+ msg.buildDirectory);
+ }
+
+ @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",
+ msg.interfaceName, 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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiReadPerfTest.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiReadPerfTest.java
new file mode 100644
index 0000000..6ff440d
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiWritePerfTest.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiWritePerfTest.java
new file mode 100644
index 0000000..1940ddc
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackCliApiExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackCliApiExample.java
new file mode 100644
index 0000000..b692291
--- /dev/null
+++ b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackCliApiExample.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+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.CliInbandReplyCallback;
+import io.fd.vpp.jvpp.core.dto.CliInband;
+import io.fd.vpp.jvpp.core.dto.CliInbandReply;
+
+import java.nio.charset.StandardCharsets;
+
+public class CallbackCliApiExample {
+
+ public static void main(String[] args) throws Exception {
+ testCallbackApi();
+ }
+
+ private static void testCallbackApi() throws Exception {
+ System.out.println("Testing Java callback API for Cli with JVppRegistry");
+ try (final JVppRegistry registry = new JVppRegistryImpl("CallbackCliApiExample");
+ final JVpp jvpp = new JVppCoreImpl()) {
+ registry.register(jvpp, new TestCallback());
+
+ System.out.println("Sending CliInband request...");
+ CliInband req = new CliInband();
+ req.cmd = "create loopback interface";
+ final int result = jvpp.send(req);
+ System.out.printf("CliInband send result = %d%n", result);
+
+ Thread.sleep(1000);
+ System.out.println("Disconnecting...");
+ }
+ Thread.sleep(1000);
+ }
+
+ static class TestCallback implements CliInbandReplyCallback {
+
+ @Override
+ public void onCliInbandReply(final CliInbandReply msg) {
+ System.out.printf("%s", msg.toString());
+ System.out.printf("Received CliInbandReply: context=%d, reply=%s", msg.context, msg.reply);
+ }
+
+ @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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java
new file mode 100644
index 0000000..a8b9186
--- /dev/null
+++ b/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,
+ msg.program,
+ msg.version,
+ msg.buildDate,
+ msg.buildDirectory);
+ }
+
+ @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,
+ msg.program,
+ msg.version,
+ msg.buildDate,
+ msg.buildDirectory);
+ }
+
+ @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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java
new file mode 100644
index 0000000..832464a
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java
new file mode 100644
index 0000000..9ed418e
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java
new file mode 100644
index 0000000..3db6d30
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java
new file mode 100644
index 0000000..030e689
--- /dev/null
+++ b/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, reply.program,
+ reply.version,
+ reply.buildDate,
+ reply.buildDirectory));
+ }
+
+ 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",
+ details.interfaceName,
+ 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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java
new file mode 100644
index 0000000..3c84fd7
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiReadPerfTest.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiReadPerfTest.java
new file mode 100644
index 0000000..f335b28
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java
new file mode 100644
index 0000000..0be85d4
--- /dev/null
+++ b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java
@@ -0,0 +1,206 @@
+/*
+ * 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;
+
+/**
+ * <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 final char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+
+ 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 String bytesToHex(byte[] bytes) {
+ char[] hexChars = new char[bytes.length * 2];
+ for ( int j = 0; j < bytes.length; j++ ) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = hexArray[v >>> 4];
+ hexChars[j * 2 + 1] = hexArray[v & 0x0F];
+ }
+ return new java.lang.String(hexChars);
+ }
+
+ 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: " + bytesToHex(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: " + bytesToHex(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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java
new file mode 100644
index 0000000..f637669
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java
new file mode 100644
index 0000000..e963d63
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt b/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt
new file mode 100644
index 0000000..9fe3c7a
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java b/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java
new file mode 100644
index 0000000..493116c
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
new file mode 100644
index 0000000..d3acecc
--- /dev/null
+++ b/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/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt b/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt
new file mode 100644
index 0000000..b74cf60
--- /dev/null
+++ b/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/java/jvpp-core/jvpp_core.c b/java/jvpp-core/jvpp_core.c
new file mode 100644
index 0000000..2e62426
--- /dev/null
+++ b/java/jvpp-core/jvpp_core.c
@@ -0,0 +1,136 @@
+/*
+ * 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 <jvpp-common/jvpp_common.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_core.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);
+}
+
+
+static void _host_to_net_string(JNIEnv * env, jstring javaString, vl_api_string_t * vl_api_string)
+{
+ const char *nativeString;
+ // prevent null, which causes jni to crash
+ if (NULL != javaString) {
+ nativeString = (*env)->GetStringUTFChars(env, javaString, 0);
+ } else{
+ nativeString = "";
+ }
+
+ vl_api_to_api_string(jstr_length(env, javaString) + 1, nativeString, vl_api_string);
+
+ (*env)->ReleaseStringUTFChars(env, javaString, nativeString);
+}
+
+
+static jstring _net_to_host_string(JNIEnv * env, const vl_api_string_t * _net)
+{
+ return (*env)->NewStringUTF(env, (char *)_net->buf);
+}
+
+
+static size_t jstr_length(JNIEnv *env, jstring string)
+{
+ return ((int) (*env)->GetStringUTFLength(env, string));
+}
diff --git a/java/jvpp-core/jvpp_core.h b/java/jvpp-core/jvpp_core.h
new file mode 100644
index 0000000..032dd33
--- /dev/null
+++ b/java/jvpp-core/jvpp_core.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef VPP_JVPP_CORE_H
+#define VPP_JVPP_CORE_H
+
+#include <vlibapi/api_types.h>
+
+#endif //VPP_JVPP_CORE_H
+
+// /**
+// * Host to network byte order conversion for string type. Converts String in Java to VPP string type.
+// * typedef struct
+// * {
+// * u32 length;
+// * u8 buf[0];
+// * } __attribute__ ((packed)) vl_api_string_t;
+// */
+static void _host_to_net_string(JNIEnv * env, jstring javaString, vl_api_string_t * vl_api_string);
+
+
+//
+// /**
+// * Network to host byte order conversion for string type. Converts VPP string type to String in Java
+// * typedef struct
+// * {
+// * u32 length;
+// * u8 buf[0];
+// * } __attribute__ ((packed)) vl_api_string_t;
+// */
+static jstring _net_to_host_string(JNIEnv * env, const vl_api_string_t * _net);
+
+
+//
+// /**
+// * Returns the length of jstring as size_t
+// */
+static size_t jstr_length(JNIEnv *env, jstring string);
diff --git a/java/jvpp-gtpu/jvpp_gtpu.c b/java/jvpp-gtpu/jvpp_gtpu.c
new file mode 100644
index 0000000..8e523ca
--- /dev/null
+++ b/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/java/jvpp-gtpu/jvpp_gtpu.h b/java/jvpp-gtpu/jvpp_gtpu.h
new file mode 100644
index 0000000..fa7c8b7
--- /dev/null
+++ b/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/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java b/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java
new file mode 100644
index 0000000..2f5b7db
--- /dev/null
+++ b/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/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt b/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt
new file mode 100644
index 0000000..f2dfe91
--- /dev/null
+++ b/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/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java b/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java
new file mode 100644
index 0000000..ba49d77
--- /dev/null
+++ b/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/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java b/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java
new file mode 100644
index 0000000..048d244
--- /dev/null
+++ b/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/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt b/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt
new file mode 100644
index 0000000..820071a
--- /dev/null
+++ b/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/java/jvpp-ioamexport/jvpp_ioam_export.c b/java/jvpp-ioamexport/jvpp_ioam_export.c
new file mode 100644
index 0000000..74ae438
--- /dev/null
+++ b/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/java/jvpp-ioamexport/jvpp_ioam_export.h b/java/jvpp-ioamexport/jvpp_ioam_export.h
new file mode 100644
index 0000000..596a054
--- /dev/null
+++ b/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/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java b/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java
new file mode 100644
index 0000000..b9ed7d0
--- /dev/null
+++ b/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/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt b/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt
new file mode 100644
index 0000000..e91550b
--- /dev/null
+++ b/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/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java b/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java
new file mode 100644
index 0000000..20b85d8
--- /dev/null
+++ b/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/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java b/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java
new file mode 100644
index 0000000..6401c67
--- /dev/null
+++ b/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/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt b/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt
new file mode 100644
index 0000000..f3cae26
--- /dev/null
+++ b/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/java/jvpp-ioampot/jvpp_ioam_pot.c b/java/jvpp-ioampot/jvpp_ioam_pot.c
new file mode 100644
index 0000000..ce1da69
--- /dev/null
+++ b/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/java/jvpp-ioampot/jvpp_ioam_pot.h b/java/jvpp-ioampot/jvpp_ioam_pot.h
new file mode 100644
index 0000000..51db3da
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java b/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java
new file mode 100644
index 0000000..d63d137
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt b/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt
new file mode 100644
index 0000000..e8c3907
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java b/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java
new file mode 100644
index 0000000..4a71db5
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java b/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java
new file mode 100644
index 0000000..4e13ed1
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt b/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt
new file mode 100644
index 0000000..9a1ba82
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/java/jvpp-ioamtrace/jvpp_ioam_trace.c
new file mode 100644
index 0000000..3532d91
--- /dev/null
+++ b/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/java/jvpp-ioamtrace/jvpp_ioam_trace.h b/java/jvpp-ioamtrace/jvpp_ioam_trace.h
new file mode 100644
index 0000000..752b599
--- /dev/null
+++ b/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/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java b/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java
new file mode 100644
index 0000000..e0d93ff
--- /dev/null
+++ b/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/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt b/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt
new file mode 100644
index 0000000..ac75e04
--- /dev/null
+++ b/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/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java b/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java
new file mode 100644
index 0000000..a6f8214
--- /dev/null
+++ b/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/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java b/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java
new file mode 100644
index 0000000..26d6b98
--- /dev/null
+++ b/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/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt b/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt
new file mode 100644
index 0000000..6f75808
--- /dev/null
+++ b/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/java/jvpp-nat/jvpp_nat.c b/java/jvpp-nat/jvpp_nat.c
new file mode 100644
index 0000000..bdae1e6
--- /dev/null
+++ b/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/java/jvpp-nat/jvpp_nat.h b/java/jvpp-nat/jvpp_nat.h
new file mode 100644
index 0000000..9bff974
--- /dev/null
+++ b/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/java/jvpp-nsh/jvpp_nsh.c b/java/jvpp-nsh/jvpp_nsh.c
new file mode 100644
index 0000000..1736ce7
--- /dev/null
+++ b/java/jvpp-nsh/jvpp_nsh.c
@@ -0,0 +1,106 @@
+/*
+ * 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>
+
+#define vl_typedefs /* define message structures */
+#include <nsh/nsh.api.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-nsh/io_fd_vpp_jvpp_nsh_JVppNshImpl.h"
+#include "jvpp_nsh.h"
+#include "jvpp-nsh/jvpp_nsh_gen.h"
+
+/*
+ * Class: io_fd_vpp_jvpp_nsh_JVppnshImpl
+ * Method: init0
+ * Signature: (JI)V
+ */
+JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_nsh_JVppNshImpl_init0
+ (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+ nsh_main_t * plugin_main = &nsh_main;
+ clib_warning ("Java_io_fd_vpp_jvpp_nsh_JVppNshImpl_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_nsh_JVppNshImpl_close0
+(JNIEnv *env, jclass clazz) {
+ nsh_main_t * plugin_main = &nsh_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/java/jvpp-nsh/jvpp_nsh.h b/java/jvpp-nsh/jvpp_nsh.h
new file mode 100644
index 0000000..5f62af6
--- /dev/null
+++ b/java/jvpp-nsh/jvpp_nsh.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_nsh_h__
+#define __included_jvpp_nsh_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-nsh */
+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;
+
+} nsh_main_t;
+
+nsh_main_t nsh_main __attribute__((aligned (64)));
+
+
+#endif /* __included_jvpp_nsh_h__ */
diff --git a/java/jvpp-pppoe/jvpp_pppoe.c b/java/jvpp-pppoe/jvpp_pppoe.c
new file mode 100644
index 0000000..02bd20e
--- /dev/null
+++ b/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/java/jvpp-pppoe/jvpp_pppoe.h b/java/jvpp-pppoe/jvpp_pppoe.h
new file mode 100644
index 0000000..7606a7e
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java b/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java
new file mode 100644
index 0000000..d221d1e
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java b/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java
new file mode 100644
index 0000000..f8b591f
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java b/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java
new file mode 100644
index 0000000..55f25a7
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java b/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java
new file mode 100644
index 0000000..6535db0
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java b/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java
new file mode 100644
index 0000000..baef14c
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java b/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java
new file mode 100644
index 0000000..ce6d1bf
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java b/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java
new file mode 100644
index 0000000..7fc1682
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java b/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java
new file mode 100644
index 0000000..adcc5d2
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java b/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java
new file mode 100644
index 0000000..e6fd3bd
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java b/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java
new file mode 100644
index 0000000..a7ccb19
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java b/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java
new file mode 100644
index 0000000..6a414f3
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java b/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java
new file mode 100644
index 0000000..efddfdb
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java b/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java
new file mode 100644
index 0000000..ae02063
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java b/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java
new file mode 100644
index 0000000..8ab0cb2
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java b/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java
new file mode 100644
index 0000000..1e780bb
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java b/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java
new file mode 100644
index 0000000..984e167
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java b/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java
new file mode 100644
index 0000000..61e4d0e
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java b/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java
new file mode 100644
index 0000000..60b9898
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java b/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java
new file mode 100644
index 0000000..73f512d
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java b/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java
new file mode 100644
index 0000000..1511139
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java b/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java
new file mode 100644
index 0000000..9b301da
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java b/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java
new file mode 100644
index 0000000..ac85f53
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java b/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java
new file mode 100644
index 0000000..65250ed
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistry.java b/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistry.java
new file mode 100644
index 0000000..12515a5
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistryProvider.java b/java/jvpp-registry/io/fd/vpp/jvpp/notification/EventRegistryProvider.java
new file mode 100644
index 0000000..1ac5d55
--- /dev/null
+++ b/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/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java b/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java
new file mode 100644
index 0000000..27b4d29
--- /dev/null
+++ b/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/java/jvpp-registry/jvpp_registry.c b/java/jvpp-registry/jvpp_registry.c
new file mode 100644
index 0000000..bbe9719
--- /dev/null
+++ b/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/java/jvpp/gen/jvpp_gen.py b/java/jvpp/gen/jvpp_gen.py
new file mode 100755
index 0000000..067a92f
--- /dev/null
+++ b/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/java/jvpp/gen/jvppgen/__init__.py b/java/jvpp/gen/jvppgen/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/java/jvpp/gen/jvppgen/__init__.py
diff --git a/java/jvpp/gen/jvppgen/callback_gen.py b/java/jvpp/gen/jvppgen/callback_gen.py
new file mode 100755
index 0000000..b1ad201
--- /dev/null
+++ b/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 jvppgen/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 jvppgen/callback_gen.py based on $json_filename.
+ */
+public interface JVpp${plugin_name}GlobalCallback extends io.fd.vpp.jvpp.callback.ControlPingCallback, $callbacks {
+}
+""")
diff --git a/java/jvpp/gen/jvppgen/dto_gen.py b/java/jvpp/gen/jvppgen/dto_gen.py
new file mode 100755
index 0000000..cbd969d
--- /dev/null
+++ b/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 java.lang.String toString() {
+ return "${details_class}ReplyDump{" +
+ "${details_field}=" + ${details_field} + "}";
+ }
+
+
+}
+""")
diff --git a/java/jvpp/gen/jvppgen/enums_gen.py b/java/jvpp/gen/jvppgen/enums_gen.py
new file mode 100755
index 0000000..8ba9655
--- /dev/null
+++ b/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/java/jvpp/gen/jvppgen/jni_common_gen.py b/java/jvpp/gen/jvppgen/jni_common_gen.py
new file mode 100755
index 0000000..b52e5ff
--- /dev/null
+++ b/java/jvpp/gen/jvppgen/jni_common_gen.py
@@ -0,0 +1,482 @@
+#!/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, is_alias):
+ initialization = []
+ for field in element.fields:
+ initialization.append(generate_j2c_field_swap(field, struct_ref_name, is_alias))
+ return "\n".join(initialization)
+
+
+def generate_j2c_field_swap(field, struct_ref_name, is_alias):
+ if is_array(field):
+ return _generate_j2c_array_swap(field, struct_ref_name, is_alias)
+ else:
+ return _generate_j2c_scalar_swap(field, struct_ref_name, is_alias)
+
+
+def _generate_j2c_array_swap(field, struct_ref_name, is_alias):
+ # 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, is_alias)
+
+
+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, is_alias):
+ field_type = field.type
+ if not is_alias:
+ template = _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+ else:
+ template = _J2C_ALIAS_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+
+ return 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});
+ }
+""")
+
+
+_J2C_ALIAS_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});
+ }
+""")
+
+
+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, is_alias):
+ field_type = field.type
+ if field_type.is_swap_needed:
+ host = field.java_name
+ if not is_alias:
+ net = "%s->%s" % (struct_ref_name, field.name)
+ if field_type.name == "string":
+ net = "%s->%s" % (struct_ref_name, field.name)
+ return " _host_to_net_%s(env, %s, (vl_api_string_t *) &%s);" % (field_type.name, host, net)
+ else:
+ return " %s;" % field_type.get_host_to_net_function(host, net)
+ else:
+ net = "%s" % struct_ref_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, is_alias):
+ 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, is_alias))
+ else:
+ initialization.append(_generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias))
+ return "".join(initialization)
+
+
+def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias):
+ # 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, is_alias)
+
+
+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, is_alias):
+ field_type = field.type
+ if not is_alias:
+ template = _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+ else:
+ template = _C2J_ALIAS_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+ return 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});
+""")
+
+
+_C2J_ALIAS_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});
+ (*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, is_alias):
+ 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, is_alias)
+ 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, is_alias):
+ field_type = field.type
+ if not is_alias:
+ if field_type.name == "string":
+ template = _C2J_STRING_TYPE_SWAP_TEMPLATE
+ else:
+ template = _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE
+ else:
+ template = _C2J_ALIAS_PRIMITIVE_TYPE_SWAP_TEMPLATE
+ return 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}));
+""")
+
+
+_C2J_STRING_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}(env, (const vl_api_string_t *) &${struct_ref_name}->${c_name}));
+""")
+
+
+_C2J_ALIAS_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}));
+""")
+
+
+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/java/jvpp/gen/jvppgen/jni_gen.py b/java/jvpp/gen/jvppgen/jni_gen.py
new file mode 100755
index 0000000..ad6c261
--- /dev/null
+++ b/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/java/jvpp/gen/jvppgen/jni_impl_gen.py b/java/jvpp/gen/jvppgen/jni_impl_gen.py
new file mode 100755
index 0000000..ef1bcbb
--- /dev/null
+++ b/java/jvpp/gen/jvppgen/jni_impl_gen.py
@@ -0,0 +1,116 @@
+#!/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", is_alias=False)
+
+ 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
+ if field.type.name == "string":
+ _size_components += " + 1 + jstr_length(env, %s) * sizeof(u8)" % field.name
+ return msg_size + "".join(_size_components)
diff --git a/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py b/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py
new file mode 100755
index 0000000..ccd3dbc
--- /dev/null
+++ b/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py
@@ -0,0 +1,107 @@
+#!/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", is_alias=False)
+ ))
+ 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/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py b/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
new file mode 100755
index 0000000..05c3e52
--- /dev/null
+++ b/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
@@ -0,0 +1,230 @@
+#!/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
+ is_alias = t.name in model._aliases
+
+ 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", is_alias=is_alias)
+ ))
+ 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", is_alias=is_alias)
+ ))
+
+
+_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}");
+ jmethodID getValueMethod = (*env)->GetMethodID(env, enumClass, "ordinal", "()I");
+ ${jni_type} value = (*env)->CallIntMethod(env, _host, getValueMethod);
+ ${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
+ is_alias = t.name in model._aliases
+ 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", is_alias=is_alias)
+ ))
+
+ 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):
+ is_alias = t.name in model._aliases
+ 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", is_alias=is_alias)
+ )
+
+
+_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/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py b/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py
new file mode 100644
index 0000000..ebc552b
--- /dev/null
+++ b/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, java.lang.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(java.lang.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(java.lang.String.format("Received ${message} event message: %s", notification));
+ }
+ eventCallback.on${message}(notification);
+ }
+""")
diff --git a/java/jvpp/gen/jvppgen/jvpp_common_gen.py b/java/jvpp/gen/jvppgen/jvpp_common_gen.py
new file mode 100755
index 0000000..499adbc
--- /dev/null
+++ b/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 java.lang.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/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py b/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py
new file mode 100644
index 0000000..3da367a
--- /dev/null
+++ b/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(java.lang.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(java.lang.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(java.lang.String.format("Received $callback_dto event message: %s", notification));
+ }
+ notificationCallback.on$callback_dto(notification);
+ }
+""")
diff --git a/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py b/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py
new file mode 100755
index 0000000..e2b2922
--- /dev/null
+++ b/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/java/jvpp/gen/jvppgen/jvpp_impl_gen.py b/java/jvpp/gen/jvppgen/jvpp_impl_gen.py
new file mode 100755
index 0000000..376952b
--- /dev/null
+++ b/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 java.lang.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(java.lang.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/java/jvpp/gen/jvppgen/jvpp_model.py b/java/jvpp/gen/jvppgen/jvpp_model.py
new file mode 100755
index 0000000..9a3204e
--- /dev/null
+++ b/java/jvpp/gen/jvppgen/jvpp_model.py
@@ -0,0 +1,624 @@
+#!/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
+
+import binascii
+
+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, name=None):
+ if name is None:
+ name = base_type.name + _ARRAY_SUFFIX
+ super(Array, self).__init__(
+ name=name,
+ 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'
+
+
+def crc(block):
+ s = str(block).encode()
+ return binascii.crc32(s) & 0xffffffff
+
+
+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 = {}
+ self._aliases = {}
+ 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._aliases.update(j['aliases'])
+
+ self._parse_types(types)
+
+ def _parse_aliases(self, types):
+
+ # model aliases
+ for alias_name in self._aliases:
+ alias = self._aliases[alias_name]
+ alias_type = {"type": "type"}
+ java_name_lower = _underscore_to_camelcase_lower(alias_name)
+ vpp_type = alias["type"]
+ crc_value = '0x%08x' % crc(alias_name)
+ if "length" in alias:
+ length = alias["length"]
+ alias_type["data"] = [
+ alias_name,
+ [
+ vpp_type,
+ java_name_lower,
+ length
+ ],
+ {
+ "crc": crc_value
+ }
+ ]
+ else:
+ alias_type["data"] = [
+ alias_name,
+ [
+ vpp_type,
+ java_name_lower
+ ],
+ {
+ "crc": crc_value
+ }
+ ]
+
+ types[alias_name] = alias_type
+
+ def _parse_types(self, types):
+ self._parse_simple_types()
+ self._parse_aliases(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'),
+ 'string': SimpleType('string', 'String', 'Ljava/lang/String;', 'jstring', 'Object',
+ host_to_net_function='_host_to_net_string',
+ net_to_host_function='_net_to_host_string')
+ })
+
+ 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/java/jvpp/gen/jvppgen/notification_gen.py b/java/jvpp/gen/jvppgen/notification_gen.py
new file mode 100644
index 0000000..fa86fe4
--- /dev/null
+++ b/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(java.lang.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, java.lang.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/java/jvpp/gen/jvppgen/types_gen.py b/java/jvpp/gen/jvppgen/types_gen.py
new file mode 100755
index 0000000..3767b53
--- /dev/null
+++ b/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/java/jvpp/gen/jvppgen/unions_gen.py b/java/jvpp/gen/jvppgen/unions_gen.py
new file mode 100755
index 0000000..f67704f
--- /dev/null
+++ b/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/version b/version
new file mode 100755
index 0000000..55f8754
--- /dev/null
+++ b/version
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+# 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.
+
+path=$( cd "$(dirname "${BASH_SOURCE}")" ; pwd -P )
+
+cd "$path"
+
+if [ -f .version ]; then
+ vstring=$(cat .version)
+else
+ vstring=$(git describe)
+ if [ $? != 0 ]; then
+ exit 1
+ fi
+fi
+
+TAG=$(echo ${vstring} | cut -d- -f1 | sed -e 's/^v//')
+ADD=$(echo ${vstring} | cut -s -d- -f2)
+
+git rev-parse 2> /dev/null
+if [ $? == 0 ]; then
+ CMT=$(git describe --dirty --long | cut -s -d- -f3,4)
+else
+ CMT=$(echo ${vstring} | cut -s -d- -f3,4)
+fi
+CMTR=$(echo $CMT | sed 's/-/_/')
+
+if [ -n "${BUILD_NUMBER}" ]; then
+ BLD="~b${BUILD_NUMBER}"
+fi
+
+if [ "$1" = "rpm-version" ]; then
+ echo ${TAG}
+ exit
+fi
+
+if [ "$1" = "rpm-release" ]; then
+ [ -z "${ADD}" ] && echo release && exit
+ echo ${ADD}${CMTR:+~${CMTR}}${BLD}
+ exit
+fi
+
+ if [ -n "${ADD}" ]; then
+ if [ "$1" = "rpm-string" ]; then
+ echo ${TAG}-${ADD}${CMTR:+~${CMTR}}${BLD}
+ else
+ echo ${TAG}-${ADD}${CMT:+~${CMT}}${BLD}
+ fi
+ else
+ echo ${TAG}-release
+fi