VPP-205: jvpp plugin support.

Splits jvpp into two jars
jvpp-registry.jar - base jvpp functionality
jvpp-core.jar - Java wrapper for vpe.api

Plugins can be generated the same way jvpp-core.jar is.
Example (nsh):
https://gerrit.fd.io/r/#/c/2118/

Change-Id: I2254f90b2c3e423563bb91bf70877979f1e90a7d
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
This commit is contained in:
Marek Gradzki
2016-07-26 15:28:22 +02:00
committed by Dave Wallace
parent f4691cd7be
commit 66ea26b1bc
57 changed files with 2152 additions and 1312 deletions

View File

@ -1,201 +0,0 @@
= JVpp
JVpp is JNI based Java API for VPP.
== Features
It is:
* Asynchronous
* Fully generated
* Lightweight
== Architecture
JVpp and JNI
JVpp Java
/----------\ /--------\ /------------\ /------\
| VppConn* | | JVpp | | Callbacks | | DTOs |
\----+-----/ \----+---/ \------+-----/ \------/
^ ^ ^
| implements | implements | implements
/----+---------\ /----+-------\ /------+-----------\
| VppConnImpl* +<--------+ JVppImpl | | GlobalCallback |
\--------------/ uses \---+--------/ \-------+----------/
| ^
| uses | calls back
| |
-------------------------------|-----------------------|---------------------
| |
| +---------------+
C JNI | |
v | /------------\
/---+-------+--\ +---->+ jvpp.h* |
| +-----+ \------------/
| jvpp.c* |
| +-----+ /------------\
\--------------/ +---->+ jvpp_gen.h |
\------------/
* Components marked with an asterisk contain manually crafted Java 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.c calls back the GlobalCallback instance with every response. An instance of the
GlobalCallback is provided to jvpp.c by VppConnImpl while connecting to VPP.
Part of the JVpp is also Future facade. It is asynchronous API returning Future objects
on top of low level JVpp.
Future facade
/-------------\ /--------------------\
| FutureJVpp* | +-->+ FutureJVppRegistry |
\-----+-------/ | \----------+---------/
^ | ^
| implements | uses | uses
| | |
/--------+----------\ | /----------+---------\
| FutureJVppFacade* +----+ | FutureJVppCallback |
\---------+---------/ \----------+---------/
| |
---------------|-----------------------------|-------------------------------
| uses | implements
JVpp Java | |
| |
/---------\ | |
| JVpp +<--+ |
\----+----/ |
^ |
| implements v
/----+-------\ /----------+-------\
| JVppImpl | | GlobalCallback |
\------------/ \------------------/
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
/--------------\ /----------------------\
| CallbackJVpp | +-->+ CallbackJVppRegistry |
\-----+--------/ | \----------+-----------/
^ | ^
| implements | uses | uses
| | |
/--------+-----------\ | /----------+-----------\
| CallbackJVppFacade +-----+ | CallbackJVppCallback |
\---------+----------/ \----------+-----------/
| |
---------------|-----------------------------|-------------------------------
| uses | implements
JVpp Java | |
| |
/---------\ | |
| JVpp +<--+ |
\----+----/ |
^ |
| implements v
/----+-------\ /----------+-------\
| JVppImpl | | GlobalCallback |
\------------/ \------------------/
== Package structure
* *org.openvpp.jvpp* - top level package for generated JVpp interface+ implementation and hand-crafted
VppConnection interface + implementation
** *dto* - package for DTOs generated from VPP API structures + base/marker hand-crafted interfaces
** *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 3 files:
* *jvpp.c* - includes jvpp.h and jvpp_gen.h + contains hand crafted code for:
** VPP connection open/close
** Rx thread to java thread attach
** Callback instance store
* *jvpp.h* - contains hand-crafted macros and structures used by jvpp.c
* *jvpp_gen.h* - contains JNI compatible handlers for each VPP request and reply
== 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_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
__JVppGlobalCallback.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.java, FutureJVppFacade.java and FutureJVppCallback.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.java, CallbackJVppFacade.java and CallbackJVppCallback.java__
[NOTE]
====
Source: jvpp_callback_facade_gen.py
====

View File

@ -41,7 +41,8 @@ from jvppgen.util import vpp_2_jni_type_mapping
parser = argparse.ArgumentParser(description='VPP Java API generator')
parser.add_argument('-i', action="store", dest="inputfile")
parser.add_argument('--base_package', action="store", dest="base_package", default='org.openvpp.jvpp')
parser.add_argument('--plugin_name', action="store", dest="plugin_name")
parser.add_argument('--control_ping_class', action="store", dest="control_ping_class", default="ControlPing")
args = parser.parse_args()
sys.path.append(".")
@ -52,7 +53,10 @@ print "importdir %s" % importdir
inputfile = os.path.basename(args.inputfile)
inputfile = inputfile.replace('.py', '')
print "inputfile %s" % inputfile
base_package = args.base_package
plugin_name = args.plugin_name
print "plugin_name %s" % plugin_name
control_ping_class = args.control_ping_class
print "control_ping_class %s" % control_ping_class
sys.path.append(importdir)
cfg = importlib.import_module(inputfile, package=None)
@ -133,17 +137,20 @@ def get_definitions():
func_list, func_name = get_definitions()
base_package = 'org.openvpp.jvpp'
plugin_package = base_package + '.' + plugin_name
dto_package = 'dto'
callback_package = 'callback'
notification_package = 'notification'
future_package = 'future'
# TODO find better package name
callback_facade_package = 'callfacade'
control_ping_class_fqn = "%s.%s.%s" % (plugin_package, dto_package, control_ping_class)
dto_gen.generate_dtos(func_list, base_package, dto_package, args.inputfile)
jvpp_impl_gen.generate_jvpp(func_list, base_package, dto_package, args.inputfile)
callback_gen.generate_callbacks(func_list, base_package, callback_package, dto_package, args.inputfile)
notification_gen.generate_notification_registry(func_list, base_package, notification_package, callback_package, dto_package, args.inputfile)
jvpp_c_gen.generate_jvpp(func_list, args.inputfile)
jvpp_future_facade_gen.generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, future_package, args.inputfile)
jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, args.inputfile)
dto_gen.generate_dtos(func_list, base_package, plugin_package, plugin_name.title(), dto_package, args.inputfile)
jvpp_impl_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name, control_ping_class_fqn, dto_package, args.inputfile)
callback_gen.generate_callbacks(func_list, base_package, plugin_package, plugin_name.title(), callback_package, dto_package, args.inputfile)
notification_gen.generate_notification_registry(func_list, base_package, plugin_package, plugin_name.title(), notification_package, callback_package, dto_package, args.inputfile)
jvpp_c_gen.generate_jvpp(func_list, plugin_name, args.inputfile)
jvpp_future_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, future_package, args.inputfile)
jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, callback_facade_package, args.inputfile)

View File

@ -22,10 +22,10 @@ from util import remove_suffix
callback_suffix = "Callback"
callback_template = Template("""
package $base_package.$callback_package;
package $plugin_package.$callback_package;
/**
* <p>Represents callback for vpe.api message.
* <p>Represents callback for plugin's api file message.
* <br>It was generated by callback_gen.py based on $inputfile preparsed data:
* <pre>
$docs
@ -39,19 +39,19 @@ public interface $cls_name extends $base_package.$callback_package.$callback_typ
""")
global_callback_template = Template("""
package $base_package.$callback_package;
package $plugin_package.$callback_package;
/**
* <p>Global aggregated callback interface.
* <br>It was generated by callback_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public interface JVppGlobalCallback extends $callbacks {
public interface JVpp${plugin_name}GlobalCallback extends $base_package.$callback_package.ControlPingCallback, $callbacks {
}
""")
def generate_callbacks(func_list, base_package, callback_package, dto_package, inputfile):
def generate_callbacks(func_list, base_package, plugin_package, plugin_name, callback_package, dto_package, inputfile):
""" Generates callback interfaces """
print "Generating Callback interfaces"
@ -61,10 +61,10 @@ def generate_callbacks(func_list, base_package, callback_package, dto_package, i
callbacks = []
for func in func_list:
if util.is_ignored(func['name']):
continue
camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
if util.is_ignored(func['name']) or util.is_control_ping(camel_case_name_with_suffix):
continue
if not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']):
continue
@ -76,11 +76,11 @@ def generate_callbacks(func_list, base_package, callback_package, dto_package, i
camel_case_name = camel_case_name_with_suffix
callback_type = "JVppNotificationCallback"
callbacks.append("{0}.{1}.{2}".format(base_package, callback_package, camel_case_name + callback_suffix))
callbacks.append("{0}.{1}.{2}".format(plugin_package, callback_package, camel_case_name + callback_suffix))
callback_path = os.path.join(callback_package, camel_case_name + callback_suffix + ".java")
callback_file = open(callback_path, 'w')
reply_type = "%s.%s.%s" % (base_package, dto_package, camel_case_name_with_suffix)
reply_type = "%s.%s.%s" % (plugin_package, dto_package, camel_case_name_with_suffix)
method = "void on{0}({1} reply);".format(camel_case_name_with_suffix, reply_type)
callback_file.write(
callback_template.substitute(inputfile=inputfile,
@ -88,15 +88,18 @@ def generate_callbacks(func_list, base_package, callback_package, dto_package, i
cls_name=camel_case_name + callback_suffix,
callback_method=method,
base_package=base_package,
plugin_package=plugin_package,
callback_package=callback_package,
callback_type=callback_type))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(callback_package, "JVppGlobalCallback.java"), 'w')
callback_file = open(os.path.join(callback_package, "JVpp%sGlobalCallback.java" % plugin_name), 'w')
callback_file.write(global_callback_template.substitute(inputfile=inputfile,
callbacks=", ".join(callbacks),
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
callback_package=callback_package))
callback_file.flush()
callback_file.close()

View File

@ -19,7 +19,7 @@ from string import Template
import util
dto_template = Template("""
package $base_package.$dto_package;
package $plugin_package.$dto_package;
/**
* <p>This class represents $description.
@ -39,11 +39,11 @@ field_template = Template(""" public $type $name;\n""")
send_template = Template(""" @Override
public int send(final $base_package.JVpp jvpp) throws org.openvpp.jvpp.VppInvocationException {
return jvpp.$method_name($args);
return (($plugin_package.JVpp${plugin_name})jvpp).$method_name($args);
}\n""")
def generate_dtos(func_list, base_package, dto_package, inputfile):
def generate_dtos(func_list, base_package, plugin_package, plugin_name, dto_package, inputfile):
""" Generates dto objects in a dedicated package """
print "Generating DTOs"
@ -55,7 +55,7 @@ def generate_dtos(func_list, base_package, dto_package, inputfile):
camel_case_method_name = util.underscore_to_camelcase(func['name'])
dto_path = os.path.join(dto_package, camel_case_dto_name + ".java")
if util.is_ignored(func['name']):
if util.is_ignored(func['name']) or util.is_control_ping(camel_case_dto_name):
continue
fields = ""
@ -72,44 +72,46 @@ def generate_dtos(func_list, base_package, dto_package, inputfile):
# Generate request/reply or dump/dumpReply even if structure can be used as notification
if not util.is_just_notification(func["name"]):
if util.is_reply(camel_case_dto_name):
description = "vpe.api reply DTO"
description = "reply DTO"
request_dto_name = get_request_name(camel_case_dto_name, func['name'])
if util.is_details(camel_case_dto_name):
# FIXME assumption that dump calls end with "Dump" suffix. Not enforced in vpe.api
base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name + "Dump")
generate_dump_reply_dto(request_dto_name, base_package, dto_package, camel_case_dto_name,
base_type += "JVppReply<%s.%s.%s>" % (plugin_package, dto_package, request_dto_name + "Dump")
generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_package, camel_case_dto_name,
camel_case_method_name, func)
else:
base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name)
base_type += "JVppReply<%s.%s.%s>" % (plugin_package, dto_package, request_dto_name)
else:
args = "" if fields is "" else "this"
methods = send_template.substitute(method_name=camel_case_method_name,
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
args=args)
if util.is_dump(camel_case_dto_name):
base_type += "JVppDump"
description = "vpe.api dump request DTO"
description = "dump request DTO"
else:
base_type += "JVppRequest"
description = "vpe.api request DTO"
description = "request DTO"
write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func,
write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func,
inputfile, methods)
# for structures that are also used as notifications, generate dedicated notification DTO
if util.is_notification(func["name"]):
base_type = "JVppNotification"
description = "vpe.api notification DTO"
description = "notification DTO"
camel_case_dto_name = util.add_notification_suffix(camel_case_dto_name)
methods = ""
dto_path = os.path.join(dto_package, camel_case_dto_name + ".java")
write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func,
write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func,
inputfile, methods)
flush_dump_reply_dtos(inputfile)
def write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func,
def write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func,
inputfile, methods):
dto_file = open(dto_path, 'w')
dto_file.write(dto_template.substitute(inputfile=inputfile,
@ -119,6 +121,7 @@ def write_dto_file(base_package, base_type, camel_case_dto_name, description, dt
fields=fields,
methods=methods,
base_package=base_package,
plugin_package=plugin_package,
base_type=base_type,
dto_package=dto_package))
dto_file.flush()
@ -141,11 +144,12 @@ def flush_dump_reply_dtos(inputfile):
dump_reply_artificial_dto['cls_name'] + ".java")
dto_file = open(dto_path, 'w')
dto_file.write(dto_template.substitute(inputfile=inputfile,
description="vpe.api dump reply wrapper",
description="dump reply wrapper",
docs=dump_reply_artificial_dto['docs'],
cls_name=dump_reply_artificial_dto['cls_name'],
fields=dump_reply_artificial_dto['fields'],
methods=dump_reply_artificial_dto['methods'],
plugin_package=dump_reply_artificial_dto['plugin_package'],
base_package=dump_reply_artificial_dto['base_package'],
base_type=dump_reply_artificial_dto['base_type'],
dto_package=dump_reply_artificial_dto['dto_package']))
@ -153,11 +157,11 @@ def flush_dump_reply_dtos(inputfile):
dto_file.close()
def generate_dump_reply_dto(request_dto_name, base_package, dto_package, camel_case_dto_name, camel_case_method_name,
def generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_package, camel_case_dto_name, camel_case_method_name,
func):
base_type = "JVppReplyDump<%s.%s.%s, %s.%s.%s>" % (
base_package, dto_package, util.remove_reply_suffix(camel_case_dto_name) + "Dump",
base_package, dto_package, camel_case_dto_name)
plugin_package, dto_package, util.remove_reply_suffix(camel_case_dto_name) + "Dump",
plugin_package, dto_package, camel_case_dto_name)
fields = " public java.util.List<%s> %s = new java.util.ArrayList<>();" % (camel_case_dto_name, camel_case_method_name)
cls_name = camel_case_dto_name + dump_dto_suffix
@ -171,6 +175,7 @@ def generate_dump_reply_dto(request_dto_name, base_package, dto_package, camel_c
'cls_name': cls_name,
'fields': fields,
'methods': "",
'plugin_package': plugin_package,
'base_package': base_package,
'base_type': base_type,
'dto_package': dto_package,

View File

@ -17,7 +17,7 @@
import os, util
from string import Template
def is_manually_generated(f_name):
def is_manually_generated(f_name, plugin_name):
return f_name in {'control_ping_reply'}
@ -25,7 +25,7 @@ class_reference_template = Template("""jclass ${ref_name}Class;
""")
find_class_invocation_template = Template("""
${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "org/openvpp/jvpp/dto/${class_name}"));
${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "org/openvpp/jvpp/${plugin_name}/dto/${class_name}"));
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
return JNI_ERR;
@ -38,53 +38,71 @@ find_class_template = Template("""
return JNI_ERR;
}""")
delete_class_invocation_template = Template("""
if (${ref_name}Class) {
(*env)->DeleteGlobalRef(env, ${ref_name}Class);
}""")
class_cache_template = Template("""
$class_references
static int cache_class_references(JNIEnv* env) {
$find_class_invocations
return 0;
}
static void delete_class_references(JNIEnv* env) {
$delete_class_invocations
}""")
def generate_class_cache(func_list):
def generate_class_cache(func_list, plugin_name):
class_references = []
find_class_invocations = []
delete_class_invocations = []
for f in func_list:
c_name = f['name']
class_name = util.underscore_to_camelcase_upper(c_name)
ref_name = util.underscore_to_camelcase(c_name)
if util.is_ignored(c_name):
if util.is_ignored(c_name) or util.is_control_ping(class_name):
continue
if util.is_reply(class_name):
class_references.append(class_reference_template.substitute(
ref_name=ref_name))
find_class_invocations.append(find_class_invocation_template.substitute(
plugin_name=plugin_name,
ref_name=ref_name,
class_name=class_name))
delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
elif util.is_notification(c_name):
class_references.append(class_reference_template.substitute(
ref_name=util.add_notification_suffix(ref_name)))
find_class_invocations.append(find_class_invocation_template.substitute(
plugin_name=plugin_name,
ref_name=util.add_notification_suffix(ref_name),
class_name=util.add_notification_suffix(class_name)))
delete_class_invocations.append(delete_class_invocation_template.substitute(
ref_name=util.add_notification_suffix(ref_name)))
# add exception class to class cache
ref_name = 'callbackException'
class_name = 'org/openvpp/jvpp/VppCallbackException'
class_references.append(class_reference_template.substitute(
ref_name=ref_name))
ref_name=ref_name))
find_class_invocations.append(find_class_template.substitute(
ref_name=ref_name,
class_name=class_name))
delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
return class_cache_template.substitute(
class_references="".join(class_references), find_class_invocations="".join(find_class_invocations))
class_references="".join(class_references), find_class_invocations="".join(find_class_invocations),
delete_class_invocations="".join(delete_class_invocations))
# 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, "org/openvpp/jvpp/dto/${java_name_upper}");""")
jclass requestClass = (*env)->FindClass(env, "org/openvpp/jvpp/${plugin_name}/dto/${java_name_upper}");""")
request_field_identifier_template = Template("""
jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, requestClass, "${java_name}", "${jni_signature}");
@ -178,37 +196,40 @@ struct_setter_templates = {'u8': u8_struct_setter_template,
jni_impl_template = Template("""
/**
* JNI binding for sending ${c_name} vpe.api message.
* JNI binding for sending ${c_name} message.
* Generated based on $inputfile preparsed data:
$api_data
*/
JNIEXPORT jint JNICALL Java_org_openvpp_jvpp_JVppImpl_${java_name}0
JNIEXPORT jint JNICALL Java_org_openvpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${java_name}0
(JNIEnv * env, jclass clazz$args) {
vppjni_main_t *jm = &vppjni_main;
${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
vl_api_${c_name}_t * mp;
u32 my_context_id;
int rv;
rv = vppjni_sanity_check (jm);
if (rv) return rv;
my_context_id = vppjni_get_context_id (jm);
u32 my_context_id = vppjni_get_context_id (&jvpp_main);
$request_class
$field_identifiers
M(${c_name_uppercase}, ${c_name});
// create message:
mp = vl_msg_api_alloc(sizeof(*mp));
memset (mp, 0, sizeof (*mp));
mp->_vl_msg_id = ntohs (VL_API_${c_name_uppercase} + plugin_main->msg_id_base);
mp->client_index = plugin_main->my_client_index;
mp->context = clib_host_to_net_u32 (my_context_id);
$struct_setters
S;
// send message:
vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
if ((*env)->ExceptionCheck(env)) {
return JNI_ERR;
}
return my_context_id;
}""")
def generate_jni_impl(func_list, inputfile):
def generate_jni_impl(func_list, plugin_name, inputfile):
jni_impl = []
for f in func_list:
f_name = f['name']
camel_case_function_name = util.underscore_to_camelcase(f_name)
if is_manually_generated(f_name) or util.is_reply(camel_case_function_name) \
if is_manually_generated(f_name, plugin_name) or util.is_reply(camel_case_function_name) \
or util.is_ignored(f_name) or util.is_just_notification(f_name):
continue
@ -222,7 +243,9 @@ def generate_jni_impl(func_list, inputfile):
arguments = ', jobject request'
camel_case_function_name_upper = util.underscore_to_camelcase_upper(f_name)
request_class = request_class_template.substitute(java_name_upper=camel_case_function_name_upper)
request_class = request_class_template.substitute(
java_name_upper=camel_case_function_name_upper,
plugin_name=plugin_name)
# field identifiers
for t in zip(f['types'], f['args']):
@ -261,6 +284,8 @@ def generate_jni_impl(func_list, inputfile):
java_name=camel_case_function_name,
c_name_uppercase=f_name_uppercase,
c_name=f_name,
plugin_name=plugin_name,
java_plugin_name=plugin_name.title(),
request_class=request_class,
field_identifiers=field_identifiers,
struct_setters=struct_setters,
@ -357,7 +382,7 @@ dto_field_setter_templates = {'u8': default_dto_field_setter_template,
callback_err_handler_template = Template("""
// for negative result don't send callback message but send error callback
if (mp->retval<0) {
CallOnError("${handler_name}",mp->context,mp->retval);
call_on_error("${handler_name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass);
return;
}
if (mp->retval == VNET_API_ERROR_IN_PROGRESS) {
@ -368,32 +393,34 @@ callback_err_handler_template = Template("""
msg_handler_template = Template("""
/**
* Handler for ${handler_name} vpe.api message.
* Handler for ${handler_name} message.
* Generated based on $inputfile preparsed data:
$api_data
*/
static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp)
{
vppjni_main_t * jm = &vppjni_main;
JNIEnv *env = jm->jenv;
${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
JNIEnv *env = jvpp_main.jenv;
$err_handler
jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
jmethodID callbackMethod = (*env)->GetMethodID(env, jm->callbackClass, "on${dto_name}", "(Lorg/openvpp/jvpp/dto/${dto_name};)V");
jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lorg/openvpp/jvpp/${plugin_name}/dto/${dto_name};)V");
jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
$dto_setters
(*env)->CallVoidMethod(env, jm->callback, callbackMethod, dto);
(*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
}""")
def generate_msg_handlers(func_list, inputfile):
def generate_msg_handlers(func_list, plugin_name, inputfile):
handlers = []
for f in func_list:
handler_name = f['name']
dto_name = util.underscore_to_camelcase_upper(handler_name)
ref_name = util.underscore_to_camelcase(handler_name)
if is_manually_generated(handler_name) or util.is_ignored(handler_name):
if is_manually_generated(handler_name, plugin_name) or util.is_ignored(handler_name):
continue
if not util.is_reply(dto_name) and not util.is_notification(handler_name):
@ -455,6 +482,7 @@ def generate_msg_handlers(func_list, inputfile):
inputfile=inputfile,
api_data=util.api_message_to_javadoc(f),
handler_name=handler_name,
plugin_name=plugin_name,
dto_name=dto_name,
class_ref_name=ref_name,
dto_setters=dto_setters,
@ -468,12 +496,13 @@ handler_registration_template = Template("""_(${upercase_name}, ${name}) \\
def generate_handler_registration(func_list):
handler_registration = ["#define foreach_vpe_api_msg \\\n"]
handler_registration = ["#define foreach_api_reply_handler \\\n"]
for f in func_list:
name = f['name']
camelcase_name = util.underscore_to_camelcase(f['name'])
if (not util.is_reply(camelcase_name) and not util.is_notification(name)) or util.is_ignored(name):
if (not util.is_reply(camelcase_name) and not util.is_notification(name)) or util.is_ignored(name) \
or util.is_control_ping(camelcase_name):
continue
handler_registration.append(handler_registration_template.substitute(
@ -486,11 +515,9 @@ def generate_handler_registration(func_list):
jvpp_c_template = Template("""/**
* This file contains JNI bindings for jvpp Java API.
* It was generated by jvpp_c_gen.py based on $inputfile
* (python representation of vpe.api generated by vppapigen).
* (python representation of api file generated by vppapigen).
*/
void CallOnError(const char* call, int context, int retval);
// JAVA class reference cache
$class_cache
@ -504,16 +531,16 @@ $msg_handlers
$handler_registration
""")
def generate_jvpp(func_list, inputfile):
def generate_jvpp(func_list, plugin_name, inputfile):
""" Generates jvpp C file """
print "Generating jvpp C"
class_cache = generate_class_cache(func_list)
jni_impl = generate_jni_impl(func_list, inputfile)
msg_handlers = generate_msg_handlers(func_list, inputfile)
class_cache = generate_class_cache(func_list, plugin_name)
jni_impl = generate_jni_impl(func_list, plugin_name, inputfile)
msg_handlers = generate_msg_handlers(func_list, plugin_name, inputfile)
handler_registration = generate_handler_registration(func_list)
jvpp_c_file = open("jvpp_gen.h", 'w')
jvpp_c_file = open("jvpp_%s_gen.h" % plugin_name, 'w')
jvpp_c_file.write(jvpp_c_template.substitute(
inputfile=inputfile,
class_cache=class_cache,

View File

@ -20,17 +20,14 @@ import callback_gen
import dto_gen
jvpp_ifc_template = Template("""
package $base_package.$callback_facade_package;
package $plugin_package.$callback_facade_package;
/**
* <p>Callback Java API representation of vpe.api.
* <p>Callback Java API representation of $plugin_package plugin.
* <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public interface CallbackJVpp extends $base_package.$notification_package.NotificationRegistryProvider, java.lang.AutoCloseable {
@Override
void close();
public interface CallbackJVpp${plugin_name} extends $base_package.$notification_package.NotificationRegistryProvider, java.lang.AutoCloseable {
// TODO add send
@ -39,20 +36,20 @@ $methods
""")
jvpp_impl_template = Template("""
package $base_package.$callback_facade_package;
package $plugin_package.$callback_facade_package;
/**
* <p>Default implementation of CallbackJVpp interface.
* <p>Default implementation of Callback${plugin_name}JVpp interface.
* <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public final class CallbackJVppFacade extends $base_package.$notification_package.NotificationRegistryProviderContext implements $base_package.$callback_facade_package.CallbackJVpp {
public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plugin_name} {
private final $base_package.JVpp jvpp;
private final $plugin_package.JVpp${plugin_name} jvpp;
private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> callbacks;
private final $plugin_package.$notification_package.${plugin_name}NotificationRegistryImpl notificationRegistry = new $plugin_package.$notification_package.${plugin_name}NotificationRegistryImpl();
/**
* <p>Create CallbackJVppFacade object for provided JVpp instance.
* <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
*
@ -60,14 +57,21 @@ public final class CallbackJVppFacade extends $base_package.$notification_packag
*
* @throws java.io.IOException in case instance cannot connect to JVPP
*/
public CallbackJVppFacade(final $base_package.JVpp jvpp) throws java.io.IOException {
public CallbackJVpp${plugin_name}Facade(final $base_package.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<>();
this.jvpp.connect(new CallbackJVppFacadeCallback(this.callbacks, getNotificationCallback()));
java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
registry.register(jvpp, new CallbackJVpp${plugin_name}FacadeCallback(this.callbacks, notificationRegistry));
}
@Override
public void close() {
public $plugin_package.$notification_package.${plugin_name}NotificationRegistry getNotificationRegistry() {
return notificationRegistry;
}
@Override
public void close() throws Exception {
jvpp.close();
}
// TODO add send()
@ -77,17 +81,17 @@ $methods
""")
method_template = Template(
""" void $name($base_package.$dto_package.$request request, $base_package.$callback_package.$callback callback) throws org.openvpp.jvpp.VppInvocationException;""")
""" void $name($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException;""")
method_impl_template = Template(""" public final void $name($base_package.$dto_package.$request request, $base_package.$callback_package.$callback callback) throws org.openvpp.jvpp.VppInvocationException {
method_impl_template = Template(""" public final void $name($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException {
synchronized (callbacks) {
callbacks.put(jvpp.$name(request), callback);
}
}
""")
no_arg_method_template = Template(""" void $name($base_package.$callback_package.$callback callback) throws org.openvpp.jvpp.VppInvocationException;""")
no_arg_method_impl_template = Template(""" public final void $name($base_package.$callback_package.$callback callback) throws org.openvpp.jvpp.VppInvocationException {
no_arg_method_template = Template(""" void $name($plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException;""")
no_arg_method_impl_template = Template(""" public final void $name($plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException {
synchronized (callbacks) {
callbacks.put(jvpp.$name(), callback);
}
@ -95,7 +99,7 @@ no_arg_method_impl_template = Template(""" public final void $name($base_pack
""")
def generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile):
def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile):
""" Generates callback facade """
print "Generating JVpp callback facade"
@ -109,12 +113,11 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, notifi
for func in func_list:
if util.is_notification(func['name']) or util.is_ignored(func['name']):
# TODO handle notifications
continue
camel_case_name = util.underscore_to_camelcase(func['name'])
camel_case_name_upper = util.underscore_to_camelcase_upper(func['name'])
if util.is_reply(camel_case_name):
if util.is_reply(camel_case_name) or util.is_control_ping(camel_case_name):
continue
# Strip suffix for dump calls
@ -123,11 +126,13 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, notifi
if len(func['args']) == 0:
methods.append(no_arg_method_template.substitute(name=camel_case_name,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package,
callback_package=callback_package,
callback=callback_type))
methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package,
callback_package=callback_package,
callback=callback_type))
@ -135,32 +140,38 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, notifi
methods.append(method_template.substitute(name=camel_case_name,
request=camel_case_name_upper,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package,
callback_package=callback_package,
callback=callback_type))
methods_impl.append(method_impl_template.substitute(name=camel_case_name,
request=camel_case_name_upper,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package,
callback_package=callback_package,
callback=callback_type))
join = os.path.join(callback_facade_package, "CallbackJVpp.java")
join = os.path.join(callback_facade_package, "CallbackJVpp%s.java" % plugin_name)
jvpp_file = open(join, 'w')
jvpp_file.write(
jvpp_ifc_template.substitute(inputfile=inputfile,
methods="\n".join(methods),
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
dto_package=dto_package,
notification_package=notification_package,
callback_facade_package=callback_facade_package))
jvpp_file.flush()
jvpp_file.close()
jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVppFacade.java"), 'w')
jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVpp%sFacade.java" % plugin_name), 'w')
jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile,
methods="\n".join(methods_impl),
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
dto_package=dto_package,
notification_package=notification_package,
callback_package=callback_package,
@ -168,31 +179,31 @@ def generate_jvpp(func_list, base_package, dto_package, callback_package, notifi
jvpp_file.flush()
jvpp_file.close()
generate_callback(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile)
generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile)
jvpp_facade_callback_template = Template("""
package $base_package.$callback_facade_package;
package $plugin_package.$callback_facade_package;
/**
* <p>Implementation of JVppGlobalCallback interface for Java Callback API.
* <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public final class CallbackJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback {
public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback {
private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requests;
private final $base_package.$notification_package.GlobalNotificationCallback notificationCallback;
private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVppFacadeCallback.class.getName());
private final $plugin_package.$notification_package.Global${plugin_name}NotificationCallback notificationCallback;
private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVpp${plugin_name}FacadeCallback.class.getName());
public CallbackJVppFacadeCallback(final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requestMap,
final $base_package.$notification_package.GlobalNotificationCallback notificationCallback) {
public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requestMap,
final $plugin_package.$notification_package.Global${plugin_name}NotificationCallback notificationCallback) {
this.requests = requestMap;
this.notificationCallback = notificationCallback;
}
@Override
public void onError(org.openvpp.jvpp.VppCallbackException reply) {
public void onError($base_package.VppCallbackException reply) {
$base_package.$callback_package.JVppCallback failedCall;
synchronized(requests) {
@ -209,6 +220,20 @@ public final class CallbackJVppFacadeCallback implements $base_package.$callback
}
}
@Override
@SuppressWarnings("unchecked")
public void onControlPingReply($base_package.$dto_package.ControlPingReply reply) {
$base_package.$callback_package.ControlPingCallback callback;
synchronized(requests) {
callback = ($base_package.$callback_package.ControlPingCallback) requests.remove(reply.context);
}
if(callback != null) {
callback.onControlPingReply(reply);
}
}
$methods
}
""")
@ -216,11 +241,11 @@ $methods
jvpp_facade_callback_method_template = Template("""
@Override
@SuppressWarnings("unchecked")
public void on$callback_dto($base_package.$dto_package.$callback_dto reply) {
public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) {
$base_package.$callback_package.$callback callback;
$plugin_package.$callback_package.$callback callback;
synchronized(requests) {
callback = ($base_package.$callback_package.$callback) requests.remove(reply.context);
callback = ($plugin_package.$callback_package.$callback) requests.remove(reply.context);
}
if(callback != null) {
@ -232,23 +257,23 @@ jvpp_facade_callback_method_template = Template("""
jvpp_facade_callback_notification_method_template = Template("""
@Override
@SuppressWarnings("unchecked")
public void on$callback_dto($base_package.$dto_package.$callback_dto notification) {
public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) {
notificationCallback.on$callback_dto(notification);
}
""")
def generate_callback(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile):
def generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile):
callbacks = []
for func in func_list:
if util.is_ignored(func['name']):
continue
camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
if util.is_ignored(func['name']) or util.is_control_ping(camel_case_name_with_suffix):
continue
if util.is_reply(camel_case_name_with_suffix):
callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package,
callbacks.append(jvpp_facade_callback_method_template.substitute(plugin_package=plugin_package,
dto_package=dto_package,
callback_package=callback_package,
callback=util.remove_reply_suffix(camel_case_name_with_suffix) + callback_gen.callback_suffix,
@ -256,15 +281,17 @@ def generate_callback(func_list, base_package, dto_package, callback_package, no
if util.is_notification(func["name"]):
with_notification_suffix = util.add_notification_suffix(camel_case_name_with_suffix)
callbacks.append(jvpp_facade_callback_notification_method_template.substitute(base_package=base_package,
callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package,
dto_package=dto_package,
callback_package=callback_package,
callback=with_notification_suffix + callback_gen.callback_suffix,
callback_dto=with_notification_suffix))
jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVppFacadeCallback.java"), 'w')
jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVpp%sFacadeCallback.java" % plugin_name), 'w')
jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile,
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
dto_package=dto_package,
notification_package=notification_package,
callback_package=callback_package,

File diff suppressed because it is too large Load Diff

View File

@ -17,25 +17,14 @@ import os, util
from string import Template
jvpp_ifc_template = Template("""
package $base_package;
package $plugin_package;
/**
* <p>Java representation of vpe.api.
* <p>Java representation of plugin's api file.
* <br>It was generated by jvpp_impl_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public interface JVpp extends java.lang.AutoCloseable {
/**
* Generic connect with $base_package.callback.JVppCallback callback handler
* providing connecting to VPP
*
* @param callback JVppCallback instance providing callback handling
*
* @throws java.io.IOException if connection cannot be initiated
*/
void connect($base_package.callback.JVppCallback callback) throws java.io.IOException;
public interface JVpp${plugin_name} extends $base_package.JVpp {
/**
* Generic dispatch method for sending requests to VPP
@ -44,52 +33,110 @@ public interface JVpp extends java.lang.AutoCloseable {
*/
int send($base_package.$dto_package.JVppRequest request) throws org.openvpp.jvpp.VppInvocationException;
@Override
void close();
$methods
}
""")
jvpp_impl_template = Template("""
package $base_package;
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 $base_package.callback.JVppCallback;
import $base_package.VppConnection;
import $base_package.JVppRegistry;
/**
* <p>Default implementation of JVpp interface.
* <br>It was generated by jvpp_impl_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public final class JVppImpl implements $base_package.JVpp {
public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugin_name} {
private final $base_package.VppConnection connection;
private final static Logger LOG = Logger.getLogger(JVpp${plugin_name}Impl.class.getName());
private static final String LIBNAME = "libjvpp_${plugin_name_underscore}.so.0.0.0";
public JVppImpl(final $base_package.VppConnection connection) {
this.connection = java.util.Objects.requireNonNull(connection,"Connection is null");
// 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 connect($base_package.callback.JVppCallback callback) throws java.io.IOException {
connection.connect(callback);
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() {
connection.close();
close0();
}
@Override
public int send($base_package.$dto_package.JVppRequest request) throws org.openvpp.jvpp.VppInvocationException {
public int send($base_package.$dto_package.JVppRequest request) throws org.openvpp.jvpp.VppInvocationException {
return request.send(this);
}
@Override
public final int controlPing(final org.openvpp.jvpp.dto.ControlPing controlPing) throws org.openvpp.jvpp.VppInvocationException {
return registry.controlPing(JVpp${plugin_name}Impl.class);
}
$methods
}
""")
method_template = Template(""" int $name($base_package.$dto_package.$request request) throws org.openvpp.jvpp.VppInvocationException;""")
method_template = Template(""" int $name($plugin_package.$dto_package.$request request) throws org.openvpp.jvpp.VppInvocationException;""")
method_native_template = Template(
""" private static native int ${name}0($base_package.$dto_package.$request request);""")
method_impl_template = Template(""" public final int $name($base_package.$dto_package.$request request) throws org.openvpp.jvpp.VppInvocationException {
""" private static native int ${name}0($plugin_package.$dto_package.$request request);""")
method_impl_template = Template(""" public final int $name($plugin_package.$dto_package.$request request) throws org.openvpp.jvpp.VppInvocationException {
java.util.Objects.requireNonNull(request,"Null request object");
connection.checkActive();
int result=${name}0(request);
@ -113,9 +160,10 @@ no_arg_method_impl_template = Template(""" public final int $name() throws or
""")
def generate_jvpp(func_list, base_package, dto_package, inputfile):
def generate_jvpp(func_list, base_package, plugin_package, plugin_name_underscore, control_ping_class, dto_package, inputfile):
""" Generates JVpp interface and JNI implementation """
print "Generating JVpp"
plugin_name = util.underscore_to_camelcase_upper(plugin_name_underscore)
methods = []
methods_impl = []
@ -131,43 +179,42 @@ def generate_jvpp(func_list, base_package, dto_package, inputfile):
continue
if len(func['args']) == 0:
methods.append(no_arg_method_template.substitute(name=camel_case_name,
base_package=base_package,
dto_package=dto_package))
methods_impl.append(
no_arg_method_native_template.substitute(name=camel_case_name,
base_package=base_package,
dto_package=dto_package))
methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name,
base_package=base_package,
dto_package=dto_package))
methods.append(no_arg_method_template.substitute(name=camel_case_name))
methods_impl.append(no_arg_method_native_template.substitute(name=camel_case_name))
methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name))
else:
methods.append(method_template.substitute(name=camel_case_name,
request=camel_case_name_upper,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package))
methods_impl.append(method_native_template.substitute(name=camel_case_name,
request=camel_case_name_upper,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package))
methods_impl.append(method_impl_template.substitute(name=camel_case_name,
request=camel_case_name_upper,
base_package=base_package,
plugin_package=plugin_package,
dto_package=dto_package))
jvpp_file = open("JVpp.java", 'w')
jvpp_file = open("JVpp%s.java" % plugin_name, 'w')
jvpp_file.write(
jvpp_ifc_template.substitute(inputfile=inputfile,
methods="\n".join(methods),
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
dto_package=dto_package))
jvpp_file.flush()
jvpp_file.close()
jvpp_file = open("JVppImpl.java", 'w')
jvpp_file = open("JVpp%sImpl.java" % plugin_name, 'w')
jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile,
methods="\n".join(methods_impl),
base_package=base_package,
dto_package=dto_package))
plugin_package=plugin_package,
plugin_name=plugin_name,
plugin_name_underscore=plugin_name_underscore,
dto_package=dto_package,
control_ping_class=control_ping_class))
jvpp_file.flush()
jvpp_file.close()

View File

@ -19,17 +19,15 @@ import callback_gen
import util
from string import Template
from util import remove_suffix
notification_registry_template = Template("""
package $base_package.$notification_package;
package $plugin_package.$notification_package;
/**
* <p>Registry for notification callbacks.
* <p>Registry for notification callbacks defined in ${plugin_name}.
* <br>It was generated by notification_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public interface NotificationRegistry extends java.lang.AutoCloseable {
public interface ${plugin_name}NotificationRegistry extends $base_package.$notification_package.NotificationRegistry {
$register_callback_methods
@ -39,27 +37,27 @@ public interface NotificationRegistry extends java.lang.AutoCloseable {
""")
global_notification_callback_template = Template("""
package $base_package.$notification_package;
package $plugin_package.$notification_package;
/**
* <p>Aggregated callback interface for notifications only.
* <br>It was generated by notification_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public interface GlobalNotificationCallback extends $callbacks {
public interface Global${plugin_name}NotificationCallback$callbacks {
}
""")
notification_registry_impl_template = Template("""
package $base_package.$notification_package;
package $plugin_package.$notification_package;
/**
* <p>Notification registry delegating notification processing to registered callbacks.
* <br>It was generated by notification_gen.py based on $inputfile
* <br>(python representation of vpe.api generated by vppapigen).
* <br>(python representation of api file generated by vppapigen).
*/
public final class NotificationRegistryImpl implements NotificationRegistry, GlobalNotificationCallback {
public final class ${plugin_name}NotificationRegistryImpl implements ${plugin_name}NotificationRegistry, Global${plugin_name}NotificationCallback {
// TODO add a special NotificationCallback interface and only allow those to be registered
private final java.util.concurrent.ConcurrentMap<Class<? extends $base_package.$dto_package.JVppNotification>, $base_package.$callback_package.JVppNotificationCallback> registeredCallbacks =
@ -76,30 +74,45 @@ public final class NotificationRegistryImpl implements NotificationRegistry, Glo
""")
register_callback_impl_template = Template("""
public java.lang.AutoCloseable register$callback(final $base_package.$callback_package.$callback callback){
if(null != registeredCallbacks.putIfAbsent($base_package.$dto_package.$notification.class, callback)){
throw new IllegalArgumentException("Callback for " + $base_package.$dto_package.$notification.class +
public java.lang.AutoCloseable register$callback(final $plugin_package.$callback_package.$callback callback){
if(null != registeredCallbacks.putIfAbsent($plugin_package.$dto_package.$notification.class, callback)){
throw new IllegalArgumentException("Callback for " + $plugin_package.$dto_package.$notification.class +
"notification already registered");
}
return () -> registeredCallbacks.remove($base_package.$dto_package.$notification.class);
return () -> registeredCallbacks.remove($plugin_package.$dto_package.$notification.class);
}
""")
handler_impl_template = Template("""
@Override
public void on$notification(
final $base_package.$dto_package.$notification notification) {
final $base_package.$callback_package.JVppNotificationCallback JVppNotificationCallback = registeredCallbacks.get($base_package.$dto_package.$notification.class);
if (null != JVppNotificationCallback) {
(($base_package.$callback_package.$callback) registeredCallbacks
.get($base_package.$dto_package.$notification.class))
final $plugin_package.$dto_package.$notification notification) {
final $base_package.$callback_package.JVppNotificationCallback jVppNotificationCallback = registeredCallbacks.get($plugin_package.$dto_package.$notification.class);
if (null != jVppNotificationCallback) {
(($plugin_package.$callback_package.$callback) registeredCallbacks
.get($plugin_package.$dto_package.$notification.class))
.on$notification(notification);
}
}
""")
notification_provider_template = Template("""
package $plugin_package.$notification_package;
def generate_notification_registry(func_list, base_package, notification_package, callback_package, dto_package, inputfile):
/**
* Provides ${plugin_name}NotificationRegistry.
* <br>The file was generated by notification_gen.py based on $inputfile
* <br>(python representation of api file generated by vppapigen).
*/
public interface ${plugin_name}NotificationRegistryProvider extends $base_package.$notification_package.NotificationRegistryProvider {
@Override
public ${plugin_name}NotificationRegistry getNotificationRegistry();
}
""")
def generate_notification_registry(func_list, base_package, plugin_package, plugin_name, notification_package, callback_package, dto_package, inputfile):
""" Generates notification registry interface and implementation """
print "Generating Notification interfaces and implementation"
@ -118,47 +131,69 @@ def generate_notification_registry(func_list, base_package, notification_package
camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
notification_dto = util.add_notification_suffix(camel_case_name_with_suffix)
callback_ifc = notification_dto + callback_gen.callback_suffix
fully_qualified_callback_ifc = "{0}.{1}.{2}".format(base_package, callback_package, callback_ifc)
fully_qualified_callback_ifc = "{0}.{1}.{2}".format(plugin_package, callback_package, callback_ifc)
callbacks.append(fully_qualified_callback_ifc)
# TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate
# that the registration should be closed
register_callback_methods.append("java.lang.AutoCloseable register{0}({1} callback);"
.format(callback_ifc, fully_qualified_callback_ifc))
register_callback_methods_impl.append(register_callback_impl_template.substitute(base_package=base_package,
register_callback_methods_impl.append(register_callback_impl_template.substitute(plugin_package=plugin_package,
callback_package=callback_package,
dto_package=dto_package,
notification=notification_dto,
callback=callback_ifc))
handler_methods.append(handler_impl_template.substitute(base_package=base_package,
plugin_package=plugin_package,
callback_package=callback_package,
dto_package=dto_package,
notification=notification_dto,
callback=callback_ifc))
if(callbacks):
callback_file = open(os.path.join(notification_package, "NotificationRegistry.java"), 'w')
callback_file.write(notification_registry_template.substitute(inputfile=inputfile,
register_callback_methods="\n ".join(register_callback_methods),
base_package=base_package,
notification_package=notification_package))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(notification_package, "GlobalNotificationCallback.java"), 'w')
callback_file.write(global_notification_callback_template.substitute(inputfile=inputfile,
callbacks=", ".join(callbacks),
base_package=base_package,
notification_package=notification_package))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(notification_package, "NotificationRegistryImpl.java"), 'w')
callback_file.write(notification_registry_impl_template.substitute(inputfile=inputfile,
callback_package=callback_package,
dto_package=dto_package,
register_callback_methods="".join(register_callback_methods_impl),
handler_methods="".join(handler_methods),
base_package=base_package,
notification_package=notification_package))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(notification_package, "%sNotificationRegistry.java" % plugin_name), 'w')
callback_file.write(notification_registry_template.substitute(inputfile=inputfile,
register_callback_methods="\n ".join(register_callback_methods),
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
notification_package=notification_package))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(notification_package, "Global%sNotificationCallback.java" % plugin_name), 'w')
global_notification_callback_callbacks = ""
if (callbacks):
global_notification_callback_callbacks = " extends " + ", ".join(callbacks)
callback_file.write(global_notification_callback_template.substitute(inputfile=inputfile,
callbacks=global_notification_callback_callbacks,
plugin_package=plugin_package,
plugin_name=plugin_name,
notification_package=notification_package))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(notification_package, "%sNotificationRegistryImpl.java" % plugin_name), 'w')
callback_file.write(notification_registry_impl_template.substitute(inputfile=inputfile,
callback_package=callback_package,
dto_package=dto_package,
register_callback_methods="".join(register_callback_methods_impl),
handler_methods="".join(handler_methods),
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
notification_package=notification_package))
callback_file.flush()
callback_file.close()
callback_file = open(os.path.join(notification_package, "%sNotificationRegistryProvider.java" % plugin_name), 'w')
callback_file.write(notification_provider_template.substitute(inputfile=inputfile,
base_package=base_package,
plugin_package=plugin_package,
plugin_name=plugin_name,
notification_package=notification_package))
callback_file.flush()
callback_file.close()

View File

@ -120,15 +120,18 @@ jni_field_accessors = {
'jfloatArray': 'ObjectField'
}
# TODO watch out for unsigned types
# Mapping according to:
# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
vpp_2_jni_type_mapping = {'u8': 'jbyte', # fixme
#
# Unsigned types are converted to signed java types that have the same size.
# It is the API user responsibility to interpret them correctly.
vpp_2_jni_type_mapping = {'u8': 'jbyte',
'i8': 'jbyte',
'u16': 'jshort',
'i16': 'jshort',
'u32': 'jint', # fixme
'u32': 'jint',
'i32': 'jint',
'u64': 'jlong', # fixme
'u64': 'jlong',
'i64': 'jlong',
'f64': 'jdouble'
}
@ -179,7 +182,7 @@ def remove_suffix(camel_case_name_with_suffix, suffix):
def is_control_ping(camel_case_name_with_suffix):
return "controlping" in camel_case_name_with_suffix.lower()
return camel_case_name_with_suffix.lower().startswith("controlping");
def api_message_to_javadoc(api_message):
""" Converts vpe.api message description to javadoc """

File diff suppressed because it is too large Load Diff

View File

@ -1,112 +0,0 @@
/*
* 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_vppjni_h__
#define __included_vppjni_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>
typedef struct {
/* Unique identifier used for matching replays with requests */
volatile u32 context_id;
/* Spinlock */
volatile u32 lock;
u32 tag;
/* Used for first control ping */
// TODO better names?
volatile u32 result_ready;
volatile i32 retval;
/* JNI Native Method Interface pointer for message handlers */
JNIEnv *jenv;
/* thread cleanup */
pthread_key_t cleanup_rx_thread_key;
/* JNI Invoke Interface pointer for attachment of rx thread to java thread */
JavaVM *jvm;
/* Callback object and class references enabling asynchronous Java calls */
jobject callback;
jclass callbackClass;
/* Connected indication */
volatile u8 is_connected;
/* Convenience */
unix_shared_memory_queue_t * vl_input_queue;
u32 my_client_index;
} vppjni_main_t;
vppjni_main_t vppjni_main __attribute__((aligned (64)));
static inline u32 vppjni_get_context_id (vppjni_main_t * jm)
{
return __sync_add_and_fetch (&jm->context_id, 1);
}
static inline void vppjni_lock (vppjni_main_t * jm, u32 tag)
{
while (__sync_lock_test_and_set (&jm->lock, 1))
;
jm->tag = tag;
}
static inline void vppjni_unlock (vppjni_main_t * jm)
{
jm->tag = 0;
CLIB_MEMORY_BARRIER();
jm->lock = 0;
}
static inline int vppjni_sanity_check (vppjni_main_t * jm)
{
if (!jm->is_connected)
return VNET_API_ERROR_NOT_CONNECTED;
return 0;
}
// TODO remove macros (code is now fully autogenerated)
/* M: construct, but don't yet send a message */
#define M(T,t) \
do { \
jm->result_ready = 0; \
mp = vl_msg_api_alloc(sizeof(*mp)); \
memset (mp, 0, sizeof (*mp)); \
mp->_vl_msg_id = ntohs (VL_API_##T); \
mp->client_index = jm->my_client_index; \
} while(0);
#define M2(T,t,n) \
do { \
jm->result_ready = 0; \
mp = vl_msg_api_alloc(sizeof(*mp)+(n)); \
memset (mp, 0, sizeof (*mp)); \
mp->_vl_msg_id = ntohs (VL_API_##T); \
mp->client_index = jm->my_client_index; \
} while(0);
/* S: send a message */
#define S (vl_msg_api_send_shmem (jm->vl_input_queue, (u8 *)&mp))
#endif /* __included_vppjni_h__ */

View File

@ -1,60 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp;
/**
* 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 );
}
}
/**
* 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;
}
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp;
/**
* Callback 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 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 int ctxId, final int errorCode ){
super(methodName, errorCode);
this.ctxId = ctxId;
}
/**
* Returns api request context identifier.
*
* @return value of context identifier
*/
public int getCtxId() {
return ctxId;
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp;
import java.io.IOException;
/**
* Representation of a management connection to VPP.
* Connection is initiated when instance is created, closed with close().
*/
public interface VppConnection extends AutoCloseable {
/**
* Open VppConnection for communication with VPP
*
* @param callback instance handling responses
*
* @throws IOException if connection is not established
*/
void connect(final org.openvpp.jvpp.callback.JVppCallback callback) throws IOException;
/**
* Check if this instance connection is active.
*
* @throws IllegalStateException if this instance was disconnected.
*/
void checkActive();
/**
* Closes Vpp connection.
*/
@Override
void close();
}

View File

@ -1,33 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp;
/**
* 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);
}
}

View File

@ -1,143 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp;
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.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;
import org.openvpp.jvpp.callback.JVppCallback;
/**
* JNI based representation of a management connection to VPP
*/
public final class VppJNIConnection implements VppConnection {
private final static Logger LOG = Logger.getLogger(VppJNIConnection.class.getName());
private static final String LIBNAME = "libjvpp.so.0.0.0";
static {
try {
loadLibrary();
} catch (Exception e) {
LOG.severe("Can't find vpp 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 = VppJNIConnection.class.getResourceAsStream('/' + LIBNAME)) {
if (is == null) {
throw new IOException("Failed to open library resource " + LIBNAME);
}
loadStream(is);
}
}
private final String clientName;
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");
}
/**
* 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.
*
* @param callback global callback to receive response calls from vpp
*
* @throws IOException in case the connection could not be established
*/
public void connect(final JVppCallback callback) throws IOException {
synchronized (VppJNIConnection.class) {
if(connections.containsKey(clientName)) {
throw new IOException("Client " + clientName + " already connected");
}
final int ret = clientConnect(clientName, callback);
if (ret != 0) {
throw new IOException("Connection returned error " + ret);
}
connections.put(clientName, this);
}
}
@Override
public final void checkActive() {
if (disconnected) {
throw new IllegalStateException("Disconnected client " + clientName);
}
}
@Override
public synchronized final void close() {
if (!disconnected) {
disconnected = true;
try {
clientDisconnect();
} finally {
synchronized (VppJNIConnection.class) {
connections.remove(clientName);
}
}
}
}
private static native int clientConnect(String clientName, JVppCallback callback);
private static native void clientDisconnect();
}

View File

@ -1,29 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.callback;
import org.openvpp.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);
}

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.callback;
/**
* Notification callback
*/
public interface JVppNotificationCallback {
}

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.dto;
/**
* Base interface for all dump requests
*/
public interface JVppDump extends JVppRequest {
}

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.dto;
/**
* Base interface for all notification DTOs
*/
public interface JVppNotification {
}

View File

@ -1,24 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.dto;
/**
* Base interface for all reply DTOs
*/
public interface JVppReply<REQ extends JVppRequest> {
}

View File

@ -1,25 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.dto;
/**
* Base interface for all dump replies
*/
public interface JVppReplyDump<REQ extends JVppRequest, RESP extends JVppReply<REQ>>
extends JVppReply<REQ> {
}

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.dto;
import org.openvpp.jvpp.JVpp;
import org.openvpp.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 incomming response
*/
int send(JVpp jvpp) throws VppInvocationException;
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.future;
import org.openvpp.jvpp.dto.JVppReply;
import org.openvpp.jvpp.dto.JVppRequest;
import java.util.concurrent.CompletionStage;
import org.openvpp.jvpp.notification.NotificationRegistryProvider;
/**
* Future facade on top of JVpp
*/
public interface FutureJVppInvoker extends NotificationRegistryProvider, AutoCloseable {
/**
* Invoke asynchronous operation on VPP
*
* @return CompletionStage with future result of an async VPP call
* @throws org.openvpp.jvpp.VppInvocationException when send request failed with details
*/
<REQ extends JVppRequest, REPLY extends JVppReply<REQ>> CompletionStage<REPLY> send(REQ req);
}

View File

@ -1,112 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.future;
import org.openvpp.jvpp.JVpp;
import org.openvpp.jvpp.VppInvocationException;
import org.openvpp.jvpp.dto.*;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.openvpp.jvpp.notification.NotificationRegistryProviderContext;
/**
* Future facade on top of JVpp
*/
public class FutureJVppInvokerFacade extends NotificationRegistryProviderContext implements FutureJVppInvoker {
private final JVpp jvpp;
/**
* Guarded by self
*/
private final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requests;
public FutureJVppInvokerFacade(final JVpp jvpp,
final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requestMap) {
this.jvpp = Objects.requireNonNull(jvpp, "Null jvpp");
// Request map represents the shared state between this facade and it's callback
// where facade puts futures in and callback completes + removes them
// TODO what if the call never completes ?
this.requests = Objects.requireNonNull(requestMap, "Null requestMap");
}
protected final Map<Integer, CompletableFuture<? extends JVppReply<?>>> getRequests() {
return this.requests;
}
// TODO use Optional in Future, java8
@Override
@SuppressWarnings("unchecked")
public <REQ extends JVppRequest, REPLY extends JVppReply<REQ>> CompletionStage<REPLY> send(REQ req) {
synchronized(requests) {
try {
final CompletableFuture<REPLY> replyCompletableFuture;
final int contextId = jvpp.send(req);
if(req instanceof JVppDump) {
replyCompletableFuture = (CompletableFuture<REPLY>) new CompletableDumpFuture<>(contextId);
} else {
replyCompletableFuture = new CompletableFuture<>();
}
requests.put(contextId, replyCompletableFuture);
if(req instanceof JVppDump) {
requests.put(jvpp.send(new ControlPing()), replyCompletableFuture);
}
return replyCompletableFuture;
} catch (VppInvocationException ex) {
final CompletableFuture<REPLY> replyCompletableFuture = new CompletableFuture<>();
replyCompletableFuture.completeExceptionally(ex);
return replyCompletableFuture;
}
}
}
static final class CompletableDumpFuture<T extends JVppReplyDump<?, ?>> extends CompletableFuture<T> {
// The reason why this is not final is the instantiation of ReplyDump DTOs
// Their instantiation must be generated, so currently the DTOs are created in callback and set when first dump reponses
// is handled in the callback.
private T replyDump;
private final long contextId;
CompletableDumpFuture(final long contextId) {
this.contextId = contextId;
}
long getContextId() {
return contextId;
}
T getReplyDump() {
return replyDump;
}
void setReplyDump(final T replyDump) {
this.replyDump = replyDump;
}
}
@Override
public void close() throws Exception {
// NOOP
}
}

View File

@ -1,12 +0,0 @@
package org.openvpp.jvpp.notification;
/**
* Provides notification registry
*/
public interface NotificationRegistryProvider {
/**
* Get current notification registry instance
*/
NotificationRegistry getNotificationRegistry();
}

View File

@ -1,20 +0,0 @@
package org.openvpp.jvpp.notification;
/**
* Base class for notification aware JVpp facades
*/
public abstract class NotificationRegistryProviderContext implements NotificationRegistryProvider {
private final NotificationRegistryImpl notificationRegistry = new NotificationRegistryImpl();
public final NotificationRegistry getNotificationRegistry() {
return notificationRegistry;
}
/**
* Get instance of notification callback. Can be used to propagate notifications from JVpp facade
*/
protected final GlobalNotificationCallback getNotificationCallback() {
return notificationRegistry;
}
}

View File

@ -1,89 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.test;
import org.openvpp.jvpp.JVpp;
import org.openvpp.jvpp.JVppImpl;
import org.openvpp.jvpp.VppCallbackException;
import org.openvpp.jvpp.VppJNIConnection;
import org.openvpp.jvpp.callback.GetNodeIndexCallback;
import org.openvpp.jvpp.callback.ShowVersionCallback;
import org.openvpp.jvpp.callback.SwInterfaceCallback;
import org.openvpp.jvpp.dto.*;
public class CallbackApiTest {
private static class TestCallback implements GetNodeIndexCallback, ShowVersionCallback, SwInterfaceCallback {
@Override
public void onGetNodeIndexReply(final GetNodeIndexReply msg) {
System.out.printf("Received GetNodeIndexReply: context=%d, nodeIndex=%d\n",
msg.context, msg.nodeIndex);
}
@Override
public void onShowVersionReply(final ShowVersionReply msg) {
System.out.printf("Received ShowVersionReply: context=%d, program=%s, version=%s, " +
"buildDate=%s, buildDirectory=%s\n",
msg.context, new String(msg.program), new String(msg.version),
new String(msg.buildDate), new String(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",
new String(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());
}
}
private static void testCallbackApi() throws Exception {
System.out.println("Testing Java callback API");
JVpp jvpp = new JVppImpl(new VppJNIConnection("CallbackApiTest"));
jvpp.connect(new TestCallback());
System.out.println("Successfully connected to VPP");
System.out.println("Sending ShowVersion request...");
jvpp.send(new ShowVersion());
System.out.println("Sending GetNodeIndex request...");
GetNodeIndex getNodeIndexRequest = new GetNodeIndex();
getNodeIndexRequest.nodeName = "node0".getBytes();
jvpp.send(getNodeIndexRequest);
System.out.println("Sending SwInterfaceDump request...");
SwInterfaceDump swInterfaceDumpRequest = new SwInterfaceDump();
swInterfaceDumpRequest.nameFilterValid = 0;
swInterfaceDumpRequest.nameFilter = "".getBytes();
jvpp.send(swInterfaceDumpRequest);
Thread.sleep(5000);
System.out.println("Disconnecting...");
jvpp.close();
Thread.sleep(1000);
}
public static void main(String[] args) throws Exception {
testCallbackApi();
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openvpp.jvpp.test;
import org.openvpp.jvpp.JVpp;
import org.openvpp.jvpp.JVppImpl;
import org.openvpp.jvpp.VppCallbackException;
import org.openvpp.jvpp.VppJNIConnection;
import org.openvpp.jvpp.callback.WantInterfaceEventsCallback;
import org.openvpp.jvpp.callfacade.CallbackJVppFacade;
import org.openvpp.jvpp.dto.WantInterfaceEventsReply;
public class CallbackJVppFacadeNotificationTest {
private static void testCallbackFacade() throws Exception {
System.out.println("Testing CallbackJVppFacade for notifications");
JVpp jvpp = new JVppImpl(new VppJNIConnection("CallbackApiTest"));
CallbackJVppFacade jvppCallbackFacade = new CallbackJVppFacade(jvpp);
System.out.println("Successfully connected to VPP");
final AutoCloseable notificationListenerReg =
jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback(
NotificationUtils::printNotification
);
jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getEnableInterfaceNotificationsReq(),
new WantInterfaceEventsCallback() {
@Override
public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) {
System.out.println("Interface events started");
}
@Override
public void onError(final VppCallbackException ex) {
System.out.printf("Received onError exception: call=%s, context=%d, retval=%d\n",
ex.getMethodName(), ex.getCtxId(), ex.getErrorCode());
}
});
System.out.println("Changing interface configuration");
NotificationUtils.getChangeInterfaceState().send(jvpp);
Thread.sleep(1000);
jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getDisableInterfaceNotificationsReq(),
new WantInterfaceEventsCallback() {
@Override
public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) {
System.out.println("Interface events stopped");
}
@Override
public void onError(final VppCallbackException ex) {
System.out.printf("Received onError exception: call=%s, context=%d, retval=%d\n",
ex.getMethodName(), ex.getCtxId(), ex.getErrorCode());
}
});
notificationListenerReg.close();
Thread.sleep(2000);
System.out.println("Disconnecting...");
jvpp.close();
Thread.sleep(1000);
}
public static void main(String[] args) throws Exception {
testCallbackFacade();
}
}

Some files were not shown because too many files have changed in this diff Show More