diff options
Diffstat (limited to 'vpp-api/java/jvpp/gen/jvppgen')
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/__init__.py | 0 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/callback_gen.py | 102 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/dto_gen.py | 177 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py | 525 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py | 297 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py | 315 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py | 173 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/notification_gen.py | 164 | ||||
-rw-r--r-- | vpp-api/java/jvpp/gen/jvppgen/util.py | 195 |
9 files changed, 1948 insertions, 0 deletions
diff --git a/vpp-api/java/jvpp/gen/jvppgen/__init__.py b/vpp-api/java/jvpp/gen/jvppgen/__init__.py new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/__init__.py diff --git a/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py b/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py new file mode 100644 index 00000000000..eadf3b5c50d --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import util +from string import Template + +from util import remove_suffix + +callback_suffix = "Callback" + +callback_template = Template(""" +package $base_package.$callback_package; + +/** + * <p>Represents callback for vpe.api message. + * <br>It was generated by callback_gen.py based on $inputfile preparsed data: + * <pre> +$docs + * </pre> + */ +public interface $cls_name extends $base_package.$callback_package.$callback_type { + + $callback_method + +} +""") + +global_callback_template = Template(""" +package $base_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). + */ +public interface JVppGlobalCallback extends $callbacks { +} +""") + + +def generate_callbacks(func_list, base_package, callback_package, dto_package, inputfile): + """ Generates callback interfaces """ + print "Generating Callback interfaces" + + if not os.path.exists(callback_package): + raise Exception("%s folder is missing" % callback_package) + + 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 not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']): + continue + + if util.is_reply(camel_case_name_with_suffix): + camel_case_name = util.remove_reply_suffix(camel_case_name_with_suffix) + callback_type = "JVppCallback" + else: + camel_case_name_with_suffix = util.add_notification_suffix(camel_case_name_with_suffix) + camel_case_name = camel_case_name_with_suffix + callback_type = "JVppNotificationCallback" + + callbacks.append("{0}.{1}.{2}".format(base_package, callback_package, camel_case_name + callback_suffix)) + callback_path = os.path.join(callback_package, camel_case_name + callback_suffix + ".java") + callback_file = open(callback_path, 'w') + + reply_type = "%s.%s.%s" % (base_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, + docs=util.api_message_to_javadoc(func), + cls_name=camel_case_name + callback_suffix, + callback_method=method, + base_package=base_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.write(global_callback_template.substitute(inputfile=inputfile, + callbacks=", ".join(callbacks), + base_package=base_package, + callback_package=callback_package)) + callback_file.flush() + callback_file.close() diff --git a/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py b/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py new file mode 100644 index 00000000000..426cd96b2ac --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from string import Template + +import util + +dto_template = Template(""" +package $base_package.$dto_package; + +/** + * <p>This class represents $description. + * <br>It was generated by dto_gen.py based on $inputfile preparsed data: + * <pre> +$docs + * </pre> + */ +public final class $cls_name implements $base_package.$dto_package.$base_type { + +$fields +$methods +} +""") + +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); + }\n""") + + +def generate_dtos(func_list, base_package, dto_package, inputfile): + """ Generates dto objects in a dedicated package """ + print "Generating DTOs" + + if not os.path.exists(dto_package): + raise Exception("%s folder is missing" % dto_package) + + for func in func_list: + camel_case_dto_name = util.underscore_to_camelcase_upper(func['name']) + 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']): + continue + + fields = "" + for t in zip(func['types'], func['args']): + # for retval don't generate dto field in Reply + field_name = util.underscore_to_camelcase(t[1]) + if util.is_reply(camel_case_dto_name) and util.is_retval_field(field_name): + continue + fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]], + name=field_name) + methods = "" + base_type = "" + + # Generate request/reply or dump/dumpReply even if structure can be used as notification + if not util.is_just_notification(func["name"]): + if util.is_reply(camel_case_dto_name): + description = "vpe.api reply DTO" + request_dto_name = get_request_name(camel_case_dto_name, func['name']) + if util.is_details(camel_case_dto_name): + # FIXME assumption that dump calls end with "Dump" suffix. Not enforced in vpe.api + base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name + "Dump") + generate_dump_reply_dto(request_dto_name, base_package, dto_package, camel_case_dto_name, + camel_case_method_name, func) + else: + base_type += "JVppReply<%s.%s.%s>" % (base_package, dto_package, request_dto_name) + else: + args = "" if fields is "" else "this" + methods = send_template.substitute(method_name=camel_case_method_name, + base_package=base_package, + args=args) + if util.is_dump(camel_case_dto_name): + base_type += "JVppDump" + description = "vpe.api dump request DTO" + else: + base_type += "JVppRequest" + description = "vpe.api request DTO" + + write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func, + inputfile, methods) + + # for structures that are also used as notifications, generate dedicated notification DTO + if util.is_notification(func["name"]): + base_type = "JVppNotification" + description = "vpe.api notification DTO" + camel_case_dto_name = util.add_notification_suffix(camel_case_dto_name) + methods = "" + dto_path = os.path.join(dto_package, camel_case_dto_name + ".java") + write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func, + inputfile, methods) + + flush_dump_reply_dtos(inputfile) + + +def write_dto_file(base_package, base_type, camel_case_dto_name, description, dto_package, dto_path, fields, func, + inputfile, methods): + dto_file = open(dto_path, 'w') + dto_file.write(dto_template.substitute(inputfile=inputfile, + description=description, + docs=util.api_message_to_javadoc(func), + cls_name=camel_case_dto_name, + fields=fields, + methods=methods, + base_package=base_package, + base_type=base_type, + dto_package=dto_package)) + dto_file.flush() + dto_file.close() + + +dump_dto_suffix = "ReplyDump" +dump_reply_artificial_dtos = {} + +# Returns request name or special one from unconventional_naming_rep_req map +def get_request_name(camel_case_dto_name, func_name): + return util.underscore_to_camelcase_upper( + util.unconventional_naming_rep_req[func_name]) if func_name in util.unconventional_naming_rep_req \ + else util.remove_reply_suffix(camel_case_dto_name) + + +def flush_dump_reply_dtos(inputfile): + for dump_reply_artificial_dto in dump_reply_artificial_dtos.values(): + dto_path = os.path.join(dump_reply_artificial_dto['dto_package'], + 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", + 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'], + base_package=dump_reply_artificial_dto['base_package'], + base_type=dump_reply_artificial_dto['base_type'], + dto_package=dump_reply_artificial_dto['dto_package'])) + dto_file.flush() + dto_file.close() + + +def generate_dump_reply_dto(request_dto_name, base_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) + 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 + + # In case of already existing artificial reply dump DTO, just update it + # Used for sub-dump dtos + if request_dto_name in dump_reply_artificial_dtos.keys(): + dump_reply_artificial_dtos[request_dto_name]['fields'] = \ + dump_reply_artificial_dtos[request_dto_name]['fields'] + '\n' + fields + else: + dump_reply_artificial_dtos[request_dto_name] = ({'docs': util.api_message_to_javadoc(func), + 'cls_name': cls_name, + 'fields': fields, + 'methods': "", + 'base_package': base_package, + 'base_type': base_type, + 'dto_package': dto_package, + }) diff --git a/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py new file mode 100644 index 00000000000..1796ac1719b --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# l +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os, util +from string import Template + +def is_manually_generated(f_name): + return f_name in {'control_ping_reply'} + + +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}")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + }""") + +find_class_template = Template(""" + ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${class_name}")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + }""") + +class_cache_template = Template(""" +$class_references +static int cache_class_references(JNIEnv* env) { + $find_class_invocations + return 0; +}""") + +def generate_class_cache(func_list): + class_references = [] + find_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): + 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( + ref_name=ref_name, + class_name=class_name)) + elif util.is_notification(c_name): + class_references.append(class_reference_template.substitute( + ref_name=util.add_notification_suffix(ref_name))) + find_class_invocations.append(find_class_invocation_template.substitute( + ref_name=util.add_notification_suffix(ref_name), + class_name=util.add_notification_suffix(class_name))) + + # add exception class to class cache + ref_name = 'callbackException' + class_name = 'org/openvpp/jvpp/VppCallbackException' + class_references.append(class_reference_template.substitute( + ref_name=ref_name)) + find_class_invocations.append(find_class_template.substitute( + ref_name=ref_name, + class_name=class_name)) + return class_cache_template.substitute( + class_references="".join(class_references), find_class_invocations="".join(find_class_invocations)) + + +# 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}");""") + +request_field_identifier_template = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, requestClass, "${java_name}", "${jni_signature}"); + ${jni_type} ${java_name} = (*env)->Get${jni_getter}(env, request, ${java_name}FieldId); + """) + +u8_struct_setter_template = Template(""" + mp->${c_name} = ${java_name};""") + +u16_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_u16(${java_name});""") + +u32_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_u32(${java_name});""") + +i32_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_i32(${java_name});!""") + +u64_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_u64(${java_name});""") + +u8_array_struct_setter_template = Template(""" + if (${java_name}) { + jsize cnt = (*env)->GetArrayLength (env, ${java_name}); + size_t max_size = ${field_length}; + if (max_size != 0 && cnt > max_size) cnt = max_size; + (*env)->GetByteArrayRegion(env, ${java_name}, 0, cnt, (jbyte *)mp->${c_name}); + } +""") + +u16_array_struct_setter_template = Template(""" + jshort * ${java_name}ArrayElements = (*env)->GetShortArrayElements(env, ${java_name}, NULL); + if (${java_name}) { + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${java_name}); + size_t max_size = ${field_length}; + if (max_size != 0 && cnt > max_size) cnt = max_size; + for (_i = 0; _i < cnt; _i++) { + mp->${c_name}[_i] = clib_host_to_net_u16(${java_name}ArrayElements[_i]); + } + } + (*env)->ReleaseShortArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0); + """) + +u32_array_struct_setter_template = Template(""" + jint * ${java_name}ArrayElements = (*env)->GetIntArrayElements(env, ${java_name}, NULL); + if (${java_name}) { + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${java_name}); + size_t max_size = ${field_length}; + if (max_size != 0 && cnt > max_size) cnt = max_size; + for (_i = 0; _i < cnt; _i++) { + mp->${c_name}[_i] = clib_host_to_net_u32(${java_name}ArrayElements[_i]); + } + } + (*env)->ReleaseIntArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0); + """) + +u64_array_struct_setter_template = Template(""" + jlong * ${java_name}ArrayElements = (*env)->GetLongArrayElements(env, ${java_name}, NULL); + if (${java_name}) { + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${java_name}); + size_t max_size = ${field_length}; + if (max_size != 0 && cnt > max_size) cnt = max_size; + for (_i = 0; _i < cnt; _i++) { + mp->${c_name}[_i] = clib_host_to_net_u64(${java_name}ArrayElements[_i]); + } + } + (*env)->ReleaseLongArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0); + """) + +vl_api_ip4_fib_counter_t_array_struct_setter_template = Template(""" + // vl_api_ip4_fib_counter_t_array_field_setter_template FIXME""") + +vl_api_ip6_fib_counter_t_array_struct_setter_template = Template(""" + // vl_api_ip6_fib_counter_t_array_field_setter_template FIXME""") + +struct_setter_templates = {'u8': u8_struct_setter_template, + 'u16': u16_struct_setter_template, + 'u32': u32_struct_setter_template, + 'i32': u32_struct_setter_template, + 'u64': u64_struct_setter_template, + 'u8[]': u8_array_struct_setter_template, + 'u16[]': u16_array_struct_setter_template, + 'u32[]': u32_array_struct_setter_template, + 'u64[]': u64_array_struct_setter_template, + 'vl_api_ip4_fib_counter_t[]': vl_api_ip4_fib_counter_t_array_struct_setter_template, + 'vl_api_ip6_fib_counter_t[]': vl_api_ip6_fib_counter_t_array_struct_setter_template + } + +jni_impl_template = Template(""" +/** + * JNI binding for sending ${c_name} vpe.api message. + * Generated based on $inputfile preparsed data: +$api_data + */ +JNIEXPORT jint JNICALL Java_org_openvpp_jvpp_JVppImpl_${java_name}0 +(JNIEnv * env, jclass clazz$args) { + vppjni_main_t *jm = &vppjni_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); + $request_class + $field_identifiers + M(${c_name_uppercase}, ${c_name}); + mp->context = clib_host_to_net_u32 (my_context_id); + $struct_setters + S; + if ((*env)->ExceptionCheck(env)) { + return JNI_ERR; + } + return my_context_id; +}""") + +def generate_jni_impl(func_list, 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) \ + or util.is_ignored(f_name) or util.is_just_notification(f_name): + continue + + arguments = '' + request_class = '' + field_identifiers = '' + struct_setters = '' + f_name_uppercase = f_name.upper() + + if f['args']: + 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) + + # field identifiers + for t in zip(f['types'], f['args']): + jni_type = t[0] + java_field_name = util.underscore_to_camelcase(t[1]) + jni_signature = util.jni_2_signature_mapping[jni_type] + jni_getter = util.jni_field_accessors[jni_type] + field_identifiers += request_field_identifier_template.substitute( + jni_type=jni_type, + java_name=java_field_name, + jni_signature=jni_signature, + jni_getter=jni_getter) + + # field setters + for t in zip(f['c_types'], f['args'], f['lengths']): + c_type = t[0] + c_name = t[1] + field_length = t[2][0] + + # check if we are processing variable length array: + if t[2][1]: + field_length = util.underscore_to_camelcase(t[2][0]) + + java_field_name = util.underscore_to_camelcase(c_name) + + struct_setter_template = struct_setter_templates[c_type] + + struct_setters += struct_setter_template.substitute( + c_name=c_name, + java_name=java_field_name, + field_length=field_length) + + jni_impl.append(jni_impl_template.substitute( + inputfile=inputfile, + api_data=util.api_message_to_javadoc(f), + java_name=camel_case_function_name, + c_name_uppercase=f_name_uppercase, + c_name=f_name, + request_class=request_class, + field_identifiers=field_identifiers, + struct_setters=struct_setters, + args=arguments)) + + return "\n".join(jni_impl) + + +dto_field_id_template = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");""") + +default_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, mp->${c_name}); +""") + +variable_length_array_value_template = Template("""mp->${length_var_name}""") +variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""") + +u16_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u16(mp->${c_name})); +""") + +u32_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u32(mp->${c_name})); +""") + +u64_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u64(mp->${c_name})); +""") + +u8_array_dto_field_setter_template = Template(""" + jbyteArray ${java_name} = (*env)->NewByteArray(env, ${field_length}); + (*env)->SetByteArrayRegion(env, ${java_name}, 0, ${field_length}, (const jbyte*)mp->${c_name}); + (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name}); +""") + +u16_array_dto_field_setter_template = Template(""" + { + jshortArray ${java_name} = (*env)->NewShortArray(env, ${field_length}); + jshort * ${java_name}ArrayElements = (*env)->GetShortArrayElements(env, ${java_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${java_name}ArrayElements[_i] = clib_net_to_host_u16(mp->${c_name}[_i]); + } + + (*env)->ReleaseShortArrayElements(env, ${java_name}, ${java_name}ArrayElements, 0); + (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name}); + } +""") + +u32_array_dto_field_setter_template = Template(""" + { + jintArray ${java_name} = (*env)->NewIntArray(env, ${field_length}); + jint * ${java_name}ArrayElements = (*env)->GetIntArrayElements(env, ${java_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${java_name}ArrayElements[_i] = clib_net_to_host_u32(mp->${c_name}[_i]); + } + + (*env)->ReleaseIntArrayElements(env, ${java_name}, ${java_name}ArrayElements, 0); + (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name}); + } +""") + +# For each u64 array we get its elements. Then we convert values to host byte order. +# All changes to jlong* buffer are written to jlongArray (isCopy is set to NULL) +u64_array_dto_field_setter_template = Template(""" + { + jlongArray ${java_name} = (*env)->NewLongArray(env, ${field_length}); + jlong * ${java_name}ArrayElements = (*env)->GetLongArrayElements(env, ${java_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${java_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]); + } + + (*env)->ReleaseLongArrayElements(env, ${java_name}, ${java_name}ArrayElements, 0); + (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name}); + } +""") + +dto_field_setter_templates = {'u8': default_dto_field_setter_template, + 'u16': u16_dto_field_setter_template, + 'u32': u32_dto_field_setter_template, + 'i32': u32_dto_field_setter_template, + 'u64': u64_dto_field_setter_template, + 'f64': default_dto_field_setter_template, #fixme + 'u8[]': u8_array_dto_field_setter_template, + 'u16[]': u16_array_dto_field_setter_template, + 'u32[]': u32_array_dto_field_setter_template, + 'u64[]': u64_array_dto_field_setter_template + } + +# code fragment for checking result of the operation before sending request reply +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); + return; + } + if (mp->retval == VNET_API_ERROR_IN_PROGRESS) { + clib_warning("Result in progress"); + return; + } +""") + +msg_handler_template = Template(""" +/** + * Handler for ${handler_name} vpe.api 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; + $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"); + + jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor); + $dto_setters + (*env)->CallVoidMethod(env, jm->callback, callbackMethod, dto); +}""") + +def generate_msg_handlers(func_list, 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): + continue + + if not util.is_reply(dto_name) and not util.is_notification(handler_name): + continue + + if util.is_notification(handler_name): + dto_name = util.add_notification_suffix(dto_name) + ref_name = util.add_notification_suffix(ref_name) + + dto_setters = '' + err_handler = '' + # dto setters + for t in zip(f['c_types'], f['types'], f['args'], f['lengths']): + c_type = t[0] + jni_type = t[1] + c_name = t[2] + field_length = t[3][0] + + if jni_type.endswith('Array') and field_length == '0': + raise Exception('Variable array \'%s\' defined in message \'%s\' ' + 'should have defined length (e.g. \'%s[%s_length]\'' + % (c_name, handler_name, c_name, c_name)) + + # check if we are processing variable length array + if t[3][1]: + length_var_name = t[3][0] + length_field_type = f['c_types'][f['args'].index(length_var_name)] + field_length = variable_length_array_value_template.substitute(length_var_name=length_var_name) + if length_field_type != 'u8': # we need net to host conversion: + field_length = variable_length_array_template.substitute( + length_field_type=length_field_type, value=field_length) + + # for retval don't generate setters and generate retval check + if util.is_retval_field(c_name): + err_handler = callback_err_handler_template.substitute( + handler_name=handler_name + ) + continue + + java_field_name = util.underscore_to_camelcase(c_name) + jni_signature = util.jni_2_signature_mapping[jni_type] + jni_setter = util.jni_field_accessors[jni_type] + + dto_setters += dto_field_id_template.substitute( + java_name=java_field_name, + class_ref_name=ref_name, + jni_signature=jni_signature) + + dto_setter_template = dto_field_setter_templates[c_type] + + dto_setters += dto_setter_template.substitute( + java_name=java_field_name, + jni_signature=jni_signature, + c_name=c_name, + jni_setter=jni_setter, + field_length=field_length) + + handlers.append(msg_handler_template.substitute( + inputfile=inputfile, + api_data=util.api_message_to_javadoc(f), + handler_name=handler_name, + dto_name=dto_name, + class_ref_name=ref_name, + dto_setters=dto_setters, + err_handler=err_handler)) + + return "\n".join(handlers) + + +handler_registration_template = Template("""_(${upercase_name}, ${name}) \\ +""") + + +def generate_handler_registration(func_list): + handler_registration = ["#define foreach_vpe_api_msg \\\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): + continue + + handler_registration.append(handler_registration_template.substitute( + name=name, + upercase_name=name.upper())) + + return "".join(handler_registration) + + +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). + */ + +void CallOnError(const char* call, int context, int retval); + +// JAVA class reference cache +$class_cache + +// JNI bindings +$jni_implementations + +// Message handlers +$msg_handlers + +// Registration of message handlers in vlib +$handler_registration +""") + +def generate_jvpp(func_list, 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) + handler_registration = generate_handler_registration(func_list) + + jvpp_c_file = open("jvpp_gen.h", 'w') + jvpp_c_file.write(jvpp_c_template.substitute( + inputfile=inputfile, + class_cache=class_cache, + jni_implementations=jni_impl, + msg_handlers=msg_handlers, + handler_registration=handler_registration)) + jvpp_c_file.flush() + jvpp_c_file.close() + diff --git a/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py b/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py new file mode 100644 index 00000000000..7df17486a60 --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os, util +from string import Template + +import callback_gen +import dto_gen + +jvpp_ifc_template = Template(""" +package $base_package.$callback_facade_package; + +/** + * <p>Callback Java API representation of vpe.api. + * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public interface CallbackJVpp extends $base_package.$notification_package.NotificationRegistryProvider, java.lang.AutoCloseable { + + @Override + void close(); + + // TODO add send + +$methods +} +""") + +jvpp_impl_template = Template(""" +package $base_package.$callback_facade_package; + +/** + * <p>Default implementation of CallbackJVpp interface. + * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public final class CallbackJVppFacade extends $base_package.$notification_package.NotificationRegistryProviderContext implements $base_package.$callback_facade_package.CallbackJVpp { + + private final $base_package.JVpp jvpp; + private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> callbacks; + + /** + * <p>Create CallbackJVppFacade object for provided JVpp instance. + * Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks + * and then connects to provided JVpp instance + * + * @param jvpp provided $base_package.JVpp instance + * + * @throws java.io.IOException in case instance cannot connect to JVPP + */ + public CallbackJVppFacade(final $base_package.JVpp jvpp) throws java.io.IOException { + this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null"); + this.callbacks = new java.util.HashMap<>(); + this.jvpp.connect(new CallbackJVppFacadeCallback(this.callbacks, getNotificationCallback())); + } + + @Override + public void close() { + } + + // TODO add send() + +$methods +} +""") + +method_template = Template( + """ 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($base_package.$dto_package.$request request, $base_package.$callback_package.$callback callback) throws org.openvpp.jvpp.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 { + synchronized (callbacks) { + callbacks.put(jvpp.$name(), callback); + } + } +""") + + +def generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile): + """ Generates callback facade """ + print "Generating JVpp callback facade" + + if os.path.exists(callback_facade_package): + util.remove_folder(callback_facade_package) + + os.mkdir(callback_facade_package) + + methods = [] + methods_impl = [] + 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): + continue + + # Strip suffix for dump calls + callback_type = get_request_name(camel_case_name_upper, func['name']) + callback_gen.callback_suffix + + if len(func['args']) == 0: + methods.append(no_arg_method_template.substitute(name=camel_case_name, + base_package=base_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, + dto_package=dto_package, + callback_package=callback_package, + callback=callback_type)) + else: + methods.append(method_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + base_package=base_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, + dto_package=dto_package, + callback_package=callback_package, + callback=callback_type)) + + join = os.path.join(callback_facade_package, "CallbackJVpp.java") + jvpp_file = open(join, 'w') + jvpp_file.write( + jvpp_ifc_template.substitute(inputfile=inputfile, + methods="\n".join(methods), + base_package=base_package, + dto_package=dto_package, + notification_package=notification_package, + callback_facade_package=callback_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVppFacade.java"), 'w') + jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile, + methods="\n".join(methods_impl), + base_package=base_package, + dto_package=dto_package, + notification_package=notification_package, + callback_package=callback_package, + callback_facade_package=callback_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + generate_callback(func_list, base_package, dto_package, callback_package, notification_package, callback_facade_package, inputfile) + + +jvpp_facade_callback_template = Template(""" +package $base_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). + */ +public final class CallbackJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback { + + private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requests; + private final $base_package.$notification_package.GlobalNotificationCallback notificationCallback; + private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVppFacadeCallback.class.getName()); + + public CallbackJVppFacadeCallback(final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requestMap, + final $base_package.$notification_package.GlobalNotificationCallback notificationCallback) { + this.requests = requestMap; + this.notificationCallback = notificationCallback; + } + + @Override + public void onError(org.openvpp.jvpp.VppCallbackException reply) { + + $base_package.$callback_package.JVppCallback failedCall; + synchronized(requests) { + failedCall = requests.remove(reply.getCtxId()); + } + + if(failedCall != null) { + try { + failedCall.onError(reply); + } catch(RuntimeException ex) { + ex.addSuppressed(reply); + LOG.log(java.util.logging.Level.WARNING, String.format("Callback: %s failed while handling exception: %s", failedCall, reply), ex); + } + } + } + +$methods +} +""") + +jvpp_facade_callback_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($base_package.$dto_package.$callback_dto reply) { + + $base_package.$callback_package.$callback callback; + synchronized(requests) { + callback = ($base_package.$callback_package.$callback) requests.remove(reply.context); + } + + if(callback != null) { + callback.on$callback_dto(reply); + } + } +""") + +jvpp_facade_callback_notification_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($base_package.$dto_package.$callback_dto notification) { + notificationCallback.on$callback_dto(notification); + } +""") + + +def generate_callback(func_list, base_package, dto_package, callback_package, 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_reply(camel_case_name_with_suffix): + callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_package=callback_package, + callback=util.remove_reply_suffix(camel_case_name_with_suffix) + callback_gen.callback_suffix, + callback_dto=camel_case_name_with_suffix)) + + if util.is_notification(func["name"]): + with_notification_suffix = util.add_notification_suffix(camel_case_name_with_suffix) + callbacks.append(jvpp_facade_callback_notification_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_package=callback_package, + callback=with_notification_suffix + callback_gen.callback_suffix, + callback_dto=with_notification_suffix)) + + jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVppFacadeCallback.java"), 'w') + jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile, + base_package=base_package, + dto_package=dto_package, + notification_package=notification_package, + callback_package=callback_package, + methods="".join(callbacks), + callback_facade_package=callback_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + +# Returns request name or special one from unconventional_naming_rep_req map +def get_request_name(camel_case_dto_name, func_name): + if func_name in reverse_dict(util.unconventional_naming_rep_req): + request_name = util.underscore_to_camelcase_upper(reverse_dict(util.unconventional_naming_rep_req)[func_name]) + else: + request_name = camel_case_dto_name + return remove_suffix(request_name) + + +def reverse_dict(map): + return dict((v, k) for k, v in map.iteritems()) + + +def remove_suffix(name): + if util.is_reply(name): + return util.remove_reply_suffix(name) + else: + if util.is_dump(name): + return util.remove_suffix(name, util.dump_suffix) + else: + return name diff --git a/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py b/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py new file mode 100644 index 00000000000..e1ca4d022e0 --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py @@ -0,0 +1,315 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from string import Template + +import dto_gen +import util + +jvpp_facade_callback_template = Template(""" +package $base_package.$future_package; + +/** + * <p>Async facade callback setting values to future objects + * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public final class FutureJVppFacadeCallback implements $base_package.$callback_package.JVppGlobalCallback { + + private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requests; + private final $base_package.$notification_package.GlobalNotificationCallback notificationCallback; + + public FutureJVppFacadeCallback(final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requestMap, + final $base_package.$notification_package.GlobalNotificationCallback notificationCallback) { + this.requests = requestMap; + this.notificationCallback = notificationCallback; + } + + @Override + @SuppressWarnings("unchecked") + public void onError(org.openvpp.jvpp.VppCallbackException reply) { + final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture; + + synchronized(requests) { + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(reply.getCtxId()); + } + + if(completableFuture != null) { + completableFuture.completeExceptionally(reply); + + synchronized(requests) { + requests.remove(reply.getCtxId()); + } + } + } + +$methods +} +""") + +jvpp_facade_callback_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($base_package.$dto_package.$callback_dto reply) { + final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture; + + synchronized(requests) { + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(reply.context); + } + + if(completableFuture != null) { + completableFuture.complete(reply); + + synchronized(requests) { + requests.remove(reply.context); + } + } + } +""") + +jvpp_facade_callback_notification_method_template = Template(""" + @Override + public void on$callback_dto($base_package.$dto_package.$callback_dto notification) { + notificationCallback.on$callback_dto(notification); + } +""") + +# TODO reuse common parts with generic method callback +jvpp_facade_control_ping_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($base_package.$dto_package.$callback_dto reply) { + final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture; + + synchronized(requests) { + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(reply.context); + } + + if(completableFuture != null) { + // Finish dump call + if (completableFuture instanceof $base_package.$future_package.FutureJVppFacade.CompletableDumpFuture) { + completableFuture.complete((($base_package.$future_package.FutureJVppFacade.CompletableDumpFuture) completableFuture).getReplyDump()); + // Remove future mapped to dump call context id + synchronized(requests) { + requests.remove((($base_package.$future_package.FutureJVppFacade.CompletableDumpFuture) completableFuture).getContextId()); + } + } else { + completableFuture.complete(reply); + } + + synchronized(requests) { + requests.remove(reply.context); + } + } + } +""") + +jvpp_facade_details_callback_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($base_package.$dto_package.$callback_dto reply) { + final FutureJVppFacade.CompletableDumpFuture<$base_package.$dto_package.$callback_dto_reply_dump> completableFuture; + + synchronized(requests) { + completableFuture = ($base_package.$future_package.FutureJVppFacade.CompletableDumpFuture<$base_package.$dto_package.$callback_dto_reply_dump>) requests.get(reply.context); + } + + if(completableFuture != null) { + $base_package.$dto_package.$callback_dto_reply_dump replyDump = completableFuture.getReplyDump(); + if(replyDump == null) { + replyDump = new $base_package.$dto_package.$callback_dto_reply_dump(); + completableFuture.setReplyDump(replyDump); + } + + replyDump.$callback_dto_field.add(reply); + } + } +""") + + +def generate_jvpp(func_list, base_package, dto_package, callback_package, notification_package, future_facade_package, inputfile): + """ Generates JVpp interface and JNI implementation """ + print "Generating JVpp future facade" + + if not os.path.exists(future_facade_package): + raise Exception("%s folder is missing" % future_facade_package) + + methods = [] + methods_impl = [] + 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 not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']): + continue + + camel_case_method_name = util.underscore_to_camelcase(func['name']) + + if not util.is_notification(func["name"]): + camel_case_request_method_name = util.remove_reply_suffix(util.underscore_to_camelcase(func['name'])) + if util.is_details(camel_case_name_with_suffix): + camel_case_reply_name = get_standard_dump_reply_name(util.underscore_to_camelcase_upper(func['name']), + func['name']) + callbacks.append(jvpp_facade_details_callback_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix, + callback_dto_field=camel_case_method_name, + callback_dto_reply_dump=camel_case_reply_name + dto_gen.dump_dto_suffix, + future_package=future_facade_package)) + + methods.append(future_jvpp_method_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name + + util.underscore_to_camelcase_upper(util.dump_suffix), + reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, + request_name=util.remove_reply_suffix(camel_case_reply_name) + + util.underscore_to_camelcase_upper(util.dump_suffix))) + methods_impl.append(future_jvpp_method_impl_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name + + util.underscore_to_camelcase_upper(util.dump_suffix), + reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, + request_name=util.remove_reply_suffix(camel_case_reply_name) + + util.underscore_to_camelcase_upper(util.dump_suffix))) + else: + request_name = util.underscore_to_camelcase_upper(util.unconventional_naming_rep_req[func['name']]) \ + if func['name'] in util.unconventional_naming_rep_req else util.remove_reply_suffix(camel_case_name_with_suffix) + + methods.append(future_jvpp_method_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name, + reply_name=camel_case_name_with_suffix, + request_name=request_name)) + methods_impl.append(future_jvpp_method_impl_template.substitute(base_package=base_package, + dto_package=dto_package, + method_name=camel_case_request_method_name, + reply_name=camel_case_name_with_suffix, + request_name=request_name)) + + # Callback handler is a bit special and a different template has to be used + if util.is_control_ping(camel_case_name_with_suffix): + callbacks.append(jvpp_facade_control_ping_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix, + future_package=future_facade_package)) + else: + callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix)) + + if util.is_notification(func["name"]): + callbacks.append(jvpp_facade_callback_notification_method_template.substitute(base_package=base_package, + dto_package=dto_package, + callback_dto=util.add_notification_suffix(camel_case_name_with_suffix))) + + jvpp_file = open(os.path.join(future_facade_package, "FutureJVppFacadeCallback.java"), 'w') + jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile, + base_package=base_package, + dto_package=dto_package, + notification_package=notification_package, + callback_package=callback_package, + methods="".join(callbacks), + future_package=future_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp.java"), 'w') + jvpp_file.write(future_jvpp_template.substitute(inputfile=inputfile, + base_package=base_package, + methods="".join(methods), + future_package=future_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open(os.path.join(future_facade_package, "FutureJVppFacade.java"), 'w') + jvpp_file.write(future_jvpp_facade_template.substitute(inputfile=inputfile, + base_package=base_package, + dto_package=dto_package, + methods="".join(methods_impl), + future_package=future_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + +future_jvpp_template = Template(''' +package $base_package.$future_package; + +/** + * <p>Async facade extension adding specific methods for each request invocation + * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public interface FutureJVpp extends FutureJVppInvoker { +$methods +} +''') + +future_jvpp_method_template = Template(''' + java.util.concurrent.CompletionStage<$base_package.$dto_package.$reply_name> $method_name($base_package.$dto_package.$request_name request); +''') + + +future_jvpp_facade_template = Template(''' +package $base_package.$future_package; + +/** + * <p>Implementation of FutureJVpp based on FutureJVppInvokerFacade + * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public class FutureJVppFacade extends FutureJVppInvokerFacade implements FutureJVpp { + + /** + * <p>Create FutureJVppFacade object for provided JVpp instance. + * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks + * and then connects to provided JVpp instance + * + * @param jvpp provided $base_package.JVpp instance + * + * @throws java.io.IOException in case instance cannot connect to JVPP + */ + public FutureJVppFacade(final $base_package.JVpp jvpp) throws java.io.IOException { + super(jvpp, new java.util.HashMap<>()); + jvpp.connect(new FutureJVppFacadeCallback(getRequests(), getNotificationCallback())); + } +$methods +} +''') + +future_jvpp_method_impl_template = Template(''' + @Override + public java.util.concurrent.CompletionStage<$base_package.$dto_package.$reply_name> $method_name($base_package.$dto_package.$request_name request) { + return send(request); + } +''') + +# Returns request name or special one from unconventional_naming_rep_req map +def get_standard_dump_reply_name(camel_case_dto_name, func_name): + # FIXME this is a hotfix for sub-details callbacks + # FIXME also for L2FibTableEntry + # It's all because unclear mapping between + # request -> reply, + # dump -> reply, details, + # notification_start -> reply, notifications + + # vpe.api needs to be "standardized" so we can parse the information and create maps before generating java code + suffix = func_name.split("_")[-1] + return util.underscore_to_camelcase_upper( + util.unconventional_naming_rep_req[func_name]) + util.underscore_to_camelcase_upper(suffix) if func_name in util.unconventional_naming_rep_req \ + else camel_case_dto_name diff --git a/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py b/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py new file mode 100644 index 00000000000..93ffd0fb359 --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os, util +from string import Template + +jvpp_ifc_template = Template(""" +package $base_package; + + +/** + * <p>Java representation of vpe.api. + * <br>It was generated by jvpp_impl_gen.py based on $inputfile + * <br>(python representation of vpe.api 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; + + /** + * Generic dispatch method for sending requests to VPP + * + * @throws org.openvpp.jvpp.VppInvocationException if send request had failed + */ + int send($base_package.$dto_package.JVppRequest request) throws org.openvpp.jvpp.VppInvocationException; + + @Override + void close(); + +$methods +} +""") + +jvpp_impl_template = Template(""" +package $base_package; + +/** + * <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). + */ +public final class JVppImpl implements $base_package.JVpp { + + private final $base_package.VppConnection connection; + + public JVppImpl(final $base_package.VppConnection connection) { + this.connection = java.util.Objects.requireNonNull(connection,"Connection is null"); + } + + @Override + public void connect($base_package.callback.JVppCallback callback) throws java.io.IOException { + connection.connect(callback); + } + + @Override + public void close() { + connection.close(); + } + + @Override + public int send($base_package.$dto_package.JVppRequest request) throws org.openvpp.jvpp.VppInvocationException { + return request.send(this); + } + +$methods +} +""") + +method_template = Template(""" int $name($base_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 { + java.util.Objects.requireNonNull(request,"Null request object"); + connection.checkActive(); + int result=${name}0(request); + if(result<0){ + throw new org.openvpp.jvpp.VppInvocationException("${name}",result); + } + return result; + } +""") + +no_arg_method_template = Template(""" int $name() throws org.openvpp.jvpp.VppInvocationException;""") +no_arg_method_native_template = Template(""" private static native int ${name}0() throws org.openvpp.jvpp.VppInvocationException;""") +no_arg_method_impl_template = Template(""" public final int $name() throws org.openvpp.jvpp.VppInvocationException { + connection.checkActive(); + int result=${name}0(); + if(result<0){ + throw new org.openvpp.jvpp.VppInvocationException("${name}",result); + } + return result; + } +""") + + +def generate_jvpp(func_list, base_package, dto_package, inputfile): + """ Generates JVpp interface and JNI implementation """ + print "Generating JVpp" + + methods = [] + methods_impl = [] + for func in func_list: + + # Skip structures that are used only as notifications + if util.is_just_notification(func['name']) or util.is_ignored(func['name']): + continue + + camel_case_name = util.underscore_to_camelcase(func['name']) + camel_case_name_upper = util.underscore_to_camelcase_upper(func['name']) + if util.is_reply(camel_case_name): + 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)) + else: + methods.append(method_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + base_package=base_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, + dto_package=dto_package)) + methods_impl.append(method_impl_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + base_package=base_package, + dto_package=dto_package)) + + jvpp_file = open("JVpp.java", 'w') + jvpp_file.write( + jvpp_ifc_template.substitute(inputfile=inputfile, + methods="\n".join(methods), + base_package=base_package, + dto_package=dto_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open("JVppImpl.java", 'w') + jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile, + methods="\n".join(methods_impl), + base_package=base_package, + dto_package=dto_package)) + jvpp_file.flush() + jvpp_file.close() diff --git a/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py b/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py new file mode 100644 index 00000000000..df6407f845d --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import callback_gen +import util +from string import Template + +from util import remove_suffix + +notification_registry_template = Template(""" +package $base_package.$notification_package; + +/** + * <p>Registry for notification callbacks. + * <br>It was generated by notification_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public interface NotificationRegistry extends java.lang.AutoCloseable { + + $register_callback_methods + + @Override + void close(); +} +""") + +global_notification_callback_template = Template(""" +package $base_package.$notification_package; + +/** + * <p>Aggregated callback interface for notifications only. + * <br>It was generated by notification_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public interface GlobalNotificationCallback extends $callbacks { + +} +""") + +notification_registry_impl_template = Template(""" +package $base_package.$notification_package; + +/** + * <p>Notification registry delegating notification processing to registered callbacks. + * <br>It was generated by notification_gen.py based on $inputfile + * <br>(python representation of vpe.api generated by vppapigen). + */ +public final class NotificationRegistryImpl implements NotificationRegistry, GlobalNotificationCallback { + + // TODO add a special NotificationCallback interface and only allow those to be registered + private final java.util.concurrent.ConcurrentMap<Class<? extends $base_package.$dto_package.JVppNotification>, $base_package.$callback_package.JVppNotificationCallback> registeredCallbacks = + new java.util.concurrent.ConcurrentHashMap<>(); + + $register_callback_methods + $handler_methods + + @Override + public void close() { + registeredCallbacks.clear(); + } +} +""") + +register_callback_impl_template = Template(""" + public java.lang.AutoCloseable register$callback(final $base_package.$callback_package.$callback callback){ + if(null != registeredCallbacks.putIfAbsent($base_package.$dto_package.$notification.class, callback)){ + throw new IllegalArgumentException("Callback for " + $base_package.$dto_package.$notification.class + + "notification already registered"); + } + return () -> registeredCallbacks.remove($base_package.$dto_package.$notification.class); + } +""") + +handler_impl_template = Template(""" + @Override + public void on$notification( + final $base_package.$dto_package.$notification notification) { + final $base_package.$callback_package.JVppNotificationCallback JVppNotificationCallback = registeredCallbacks.get($base_package.$dto_package.$notification.class); + if (null != JVppNotificationCallback) { + (($base_package.$callback_package.$callback) registeredCallbacks + .get($base_package.$dto_package.$notification.class)) + .on$notification(notification); + } + } +""") + + +def generate_notification_registry(func_list, base_package, notification_package, callback_package, dto_package, inputfile): + """ Generates notification registry interface and implementation """ + print "Generating Notification interfaces and implementation" + + if not os.path.exists(notification_package): + raise Exception("%s folder is missing" % notification_package) + + callbacks = [] + register_callback_methods = [] + register_callback_methods_impl = [] + handler_methods = [] + for func in func_list: + + if not util.is_notification(func['name']): + continue + + camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) + notification_dto = util.add_notification_suffix(camel_case_name_with_suffix) + callback_ifc = notification_dto + callback_gen.callback_suffix + fully_qualified_callback_ifc = "{0}.{1}.{2}".format(base_package, callback_package, callback_ifc) + callbacks.append(fully_qualified_callback_ifc) + + # TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate + # that the registration should be closed + register_callback_methods.append("java.lang.AutoCloseable register{0}({1} callback);" + .format(callback_ifc, fully_qualified_callback_ifc)) + register_callback_methods_impl.append(register_callback_impl_template.substitute(base_package=base_package, + callback_package=callback_package, + dto_package=dto_package, + notification=notification_dto, + callback=callback_ifc)) + handler_methods.append(handler_impl_template.substitute(base_package=base_package, + callback_package=callback_package, + dto_package=dto_package, + notification=notification_dto, + callback=callback_ifc)) + 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() diff --git a/vpp-api/java/jvpp/gen/jvppgen/util.py b/vpp-api/java/jvpp/gen/jvppgen/util.py new file mode 100644 index 00000000000..f22132dfbc1 --- /dev/null +++ b/vpp-api/java/jvpp/gen/jvppgen/util.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python +# +# Copyright (c) 2016 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os, pprint +from os import removedirs + + +def underscore_to_camelcase(name): + name = name.title().replace("_", "") + return name[0].lower() + name[1:] + + +def underscore_to_camelcase_upper(name): + name = name.title().replace("_", "") + return name[0].upper() + name[1:] + + +def remove_folder(folder): + """ Remove folder with all its files """ + for root, dirs, files in os.walk(folder, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + removedirs(folder) + + +reply_suffixes = ("reply", "details", "l2fibtableentry") + + +def is_reply(name): + return name.lower().endswith(reply_suffixes) + + +def is_details(name): + return name.lower().endswith(reply_suffixes[1]) or name.lower().endswith(reply_suffixes[2]) + +def is_retval_field(name): + return name == 'retval' + +dump_suffix = "dump" + + +def is_dump(name): + return name.lower().endswith(dump_suffix) + + +def get_reply_suffix(name): + for reply_suffix in reply_suffixes: + if name.lower().endswith(reply_suffix): + if reply_suffix == reply_suffixes[2]: + # FIXME workaround for l2_fib_table_entry + return 'entry' + else: + return reply_suffix + +# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html +jni_2_java_type_mapping = {'jbyte': 'byte', + 'jbyteArray': 'byte[]', + 'jchar': 'char', + 'jcharArray': 'char[]', + 'jshort': 'short', + 'jshortArray': 'short[]', + 'jint': 'int', + 'jintArray': 'int[]', + 'jlong': 'long', + 'jlongArray': 'long[]', + 'jdouble': 'double', + 'jdoubleArray': 'double[]', + 'jfloat': 'float', + 'jfloatArray': 'float[]', + 'void': 'void', + 'jstring': 'java.lang.String', + 'jobject': 'java.lang.Object', + 'jobjectArray': 'java.lang.Object[]' + } + +# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures +jni_2_signature_mapping = {'jbyte': 'B', + 'jbyteArray': '[B', + 'jchar': 'C', + 'jcharArray': '[C', + 'jshort': 'S', + 'jshortArray': '[S', + 'jint': 'I', + 'jintArray': '[I', + 'jlong': 'J', + 'jlongArray': '[J', + 'jdouble': 'D', + 'jdoubleArray': '[D', + 'jfloat': 'F', + 'jfloatArray': '[F' + } + +# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines +jni_field_accessors = { + 'jbyte': 'ByteField', + 'jbyteArray': 'ObjectField', + 'jchar': 'CharField', + 'jcharArray': 'ObjectField', + 'jshort': 'ShortField', + 'jshortArray': 'ObjectField', + 'jint': 'IntField', + 'jintArray': 'ObjectField', + 'jlong': 'LongField', + 'jlongArray': 'ObjectField', + 'jdouble': 'DoubleField', + 'jdoubleArray': 'ObjectField', + 'jfloat': 'FloatField', + 'jfloatArray': 'ObjectField' +} + +# TODO watch out for unsigned types +# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html +vpp_2_jni_type_mapping = {'u8': 'jbyte', # fixme + 'i8': 'jbyte', + 'u16': 'jshort', + 'i16': 'jshort', + 'u32': 'jint', # fixme + 'i32': 'jint', + 'u64': 'jlong', # fixme + 'i64': 'jlong', + 'f64': 'jdouble' + } + +# vpe.api calls that do not follow naming conventions and have to be handled exceptionally when finding reply -> request mapping +# FIXME in vpe.api +unconventional_naming_rep_req = { + 'cli_reply': 'cli_request', + 'vnet_summary_stats_reply': 'vnet_get_summary_stats', + # This below is actually a sub-details callback. We cannot derive the mapping of dump request + # belonging to this sub-details from naming conventions. We need special mapping + 'bridge_domain_sw_if_details': 'bridge_domain', + # This is standard dump call + details reply. However it's not called details but entry + 'l2_fib_table_entry': 'l2_fib_table' + } + +# +# FIXME no convention in the naming of events (notifications) in vpe.api +notifications_message_suffixes = ("event", "counters") +notification_messages_reused = ["sw_interface_set_flags"] + +# messages that must be ignored. These messages are INSUFFICIENTLY marked as disabled in vpe.api +# FIXME +ignored_messages = ["is_address_reachable"] + + +def is_notification(name): + """ Returns true if the structure is a notification regardless of its no other use """ + return is_just_notification(name) or name.lower() in notification_messages_reused + + +def is_just_notification(name): + """ Returns true if the structure is just a notification and has no other use """ + return name.lower().endswith(notifications_message_suffixes) + + +def is_ignored(param): + return param.lower() in ignored_messages + + +def remove_reply_suffix(camel_case_name_with_suffix): + return remove_suffix(camel_case_name_with_suffix, get_reply_suffix(camel_case_name_with_suffix)) + + +def remove_suffix(camel_case_name_with_suffix, suffix): + suffix_length = len(suffix) + return camel_case_name_with_suffix[:-suffix_length] if suffix_length != 0 else camel_case_name_with_suffix + + +def is_control_ping(camel_case_name_with_suffix): + return "controlping" in camel_case_name_with_suffix.lower() + +def api_message_to_javadoc(api_message): + """ Converts vpe.api message description to javadoc """ + str = pprint.pformat(api_message, indent=4, width=120, depth=None) + return " * " + str.replace("\n", "\n * ") + + +notification_dto_suffix = "Notification" + + +def add_notification_suffix(camel_case_dto_name): + camel_case_dto_name += notification_dto_suffix + return camel_case_dto_name |