diff options
Diffstat (limited to 'src/vpp-api/java/jvpp/gen/jvppgen')
18 files changed, 2194 insertions, 2146 deletions
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py index 13a415595c2..14aa8b760a5 100644..100755 --- a/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,87 +12,62 @@ # 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 -callback_suffix = "Callback" +from jvpp_model import is_request, is_dump, is_control_ping, is_control_ping_reply -callback_template = Template(""" -package $plugin_package.$callback_package; + +def generate_callbacks(work_dir, model, logger): + json_api_files = model.json_api_files + logger.debug("Generating Callback interfaces for %s" % json_api_files) + plugin_package = model.plugin_package + + callbacks = [] + for msg in model.messages: + name = msg.java_name_upper + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + if is_dump(msg) or is_request(msg): + continue + + callbacks.append("%s.callback.%sCallback" % (plugin_package, name)) + callback = _CALLBACK_TEMPLATE.substitute( + plugin_package=plugin_package, + json_filename=json_api_files, + name=name) + + with open("%s/%sCallback.java" % (work_dir, name), "w") as f: + f.write(callback) + + plugin_name = model.plugin_java_name + with open("%s/JVpp%sGlobalCallback.java" % (work_dir, plugin_name), "w") as f: + f.write(_GLOBAL_CALLBACK_TEMPLATE.substitute( + plugin_package=plugin_package, + json_filename=json_api_files, + plugin_name=plugin_name, + callbacks=", ".join(callbacks) + )) + +_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback; /** - * <p>Represents callback for plugin's api file message. - * <br>It was generated by callback_gen.py based on $inputfile preparsed data: - * <pre> -$docs - * </pre> + * <p>Represents callback for plugin's api message. + * <br>It was generated by jvpp_callback_gen.py based on $json_filename. */ -public interface $cls_name extends $base_package.$callback_package.$callback_type { - - $callback_method +public interface ${name}Callback extends io.fd.vpp.jvpp.callback.JVppCallback { + void on${name}(${plugin_package}.dto.${name} reply); } """) -global_callback_template = Template(""" -package $plugin_package.$callback_package; +_GLOBAL_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback; /** * <p>Global aggregated callback interface. - * <br>It was generated by callback_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by jvpp_callback_gen.py based on $json_filename. */ -public interface JVpp${plugin_name}GlobalCallback extends $base_package.$callback_package.ControlPingCallback, $callbacks { +public interface JVpp${plugin_name}GlobalCallback extends io.fd.vpp.jvpp.callback.ControlPingCallback, $callbacks { } """) - - -def generate_callbacks(func_list, base_package, plugin_package, plugin_name, callback_package, dto_package, inputfile, - logger): - """ Generates callback interfaces """ - logger.debug("Generating Callback interfaces for %s" % inputfile) - - if not os.path.exists(callback_package): - os.mkdir(callback_package) - - callbacks = [] - for func in func_list: - camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - - if util.is_control_ping(camel_case_name_with_suffix): - # Skip control_ping managed by jvpp registry. - continue - if util.is_dump(func['name']) or util.is_request(func['name'], func_list): - continue - - # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). - callback_type = "JVppCallback" - callbacks.append("{0}.{1}.{2}".format(plugin_package, callback_package, camel_case_name_with_suffix + callback_suffix)) - callback_path = os.path.join(callback_package, camel_case_name_with_suffix + callback_suffix + ".java") - callback_file = open(callback_path, 'w') - - 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, - docs=util.api_message_to_javadoc(func), - cls_name=camel_case_name_with_suffix + 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, "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() diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py index 035a5b49eb8..ca015a30456 100644..100755 --- a/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -13,160 +13,196 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os from string import Template -import util +from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields +from jvpp_model import is_request, is_reply, is_retval, is_dump, is_details, is_event, is_control_ping, \ + is_control_ping_reply -dto_template = Template(""" -package $plugin_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 { +def generate_dtos(work_dir, model, logger): + logger.debug("Generating DTOs for %s " % model.json_api_files) + _generate_message_dtos(work_dir, model, logger) + _generate_dump_reply_wrappers(work_dir, model, logger) -$fields -$methods -} -""") -dto_template_typeless = Template(""" -package $plugin_package.$dto_package; +def _generate_message_dtos(work_dir, model, logger): + for msg in model.messages: + logger.debug("Generating DTO for message %s", msg) + class_name = msg.java_name_upper + + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + if is_request(msg): + dto = _generate_request_dto(msg, model, base_type="JVppRequest") + elif is_dump(msg): + dto = _generate_request_dto(msg, model, base_type="JVppDump") + elif is_reply(msg) or is_details(msg): + dto = _generate_reply_dto(msg, model) + elif is_event(msg): + dto = _generate_event_dto(msg, model) + else: + logger.warn("Failed to generate DTO for: %s. Message type is not supported." % msg) + continue + with open("%s/%s.java" % (work_dir, class_name), "w") as f: + f.write(dto) + + +def _generate_request_dto(msg, model, base_type): + msg_java_name_upper = msg.java_name_upper + fields = msg.fields + return _REQUEST_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + json_definition=msg.doc, + class_name=msg_java_name_upper, + base_type=base_type, + fields=generate_fields(fields), + hash_code=generate_hash_code(fields), + equals=generate_equals(msg_java_name_upper, fields), + to_string=generate_to_string(msg_java_name_upper, fields), + send=_generate_send(model, msg)) + +_REQUEST_TEMPLATE = Template(""" +package $plugin_package.dto; /** - * <p>This class represents $description. - * <br>It was generated by dto_gen.py based on $inputfile preparsed data: + * <p>This class represents request DTO. + * <br>It was generated by dto_gen.py based on $json_filename: * <pre> -$docs +$json_definition * </pre> */ -public final class $cls_name { - +public final class $class_name implements io.fd.vpp.jvpp.dto.$base_type { $fields -$methods +$hash_code +$equals +$to_string +$send } """) -field_template = Template(""" public $type $name;\n""") -send_template = Template(""" @Override - public int send(final $base_package.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException { +def _generate_send(model, msg): + return _SEND_TEMPLATE.substitute( + plugin_package=model.plugin_package, + plugin_name=model.plugin_java_name, + method_name=msg.java_name_lower, + args="this" if msg.has_fields else "" + ) + +_SEND_TEMPLATE = Template(""" + @Override + public int send(final io.fd.vpp.jvpp.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException { return (($plugin_package.JVpp${plugin_name})jvpp).$method_name($args); }""") -def generate_dtos(func_list, base_package, plugin_package, plugin_name, dto_package, inputfile, logger): - """ Generates dto objects in a dedicated package """ - logger.debug("Generating DTOs for %s" % inputfile) - - if not os.path.exists(dto_package): - os.mkdir(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_control_ping(camel_case_dto_name): - continue - - fields = generate_dto_fields(camel_case_dto_name, func) - methods = generate_dto_base_methods(camel_case_dto_name, func) - base_type = "" - - if util.is_reply(camel_case_dto_name): - description = "reply DTO" - request_dto_name = util.remove_reply_suffix(camel_case_dto_name) - if util.is_details(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>" % (plugin_package, dto_package, request_dto_name) - elif util.is_dump(camel_case_dto_name) or util.is_request(func['name'], func_list): - 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 = "dump request DTO" - else: - base_type += "JVppRequest" - description = "request DTO" - else: - description = "event DTO" - dto_path = os.path.join(dto_package, camel_case_dto_name + ".java") - - 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 _generate_reply_dto(msg, model): + msg_java_name_upper = msg.java_name_upper + # Negative retval is mapped to java exception, so filter it out: + fields = filter(lambda field: not is_retval(field), msg.fields) + return _REPLY_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + json_definition=msg.doc, + class_name=msg_java_name_upper, + request_name=msg.request_java, + fields=generate_fields(fields), + hash_code=generate_hash_code(fields), + equals=generate_equals(msg_java_name_upper, fields), + to_string=generate_to_string(msg_java_name_upper, fields)) + +_REPLY_TEMPLATE = Template(""" +package $plugin_package.dto; -def generate_dto_base_methods(camel_case_dto_name, func): - methods = generate_dto_hash(func) - methods += generate_dto_equals(camel_case_dto_name, func) - methods += generate_dto_tostring(camel_case_dto_name, func) - return methods +/** + * <p>This class represents reply DTO. + * <br>It was generated by jvpp_dto_gen.py based on $json_filename: + * <pre> +$json_definition + * </pre> + */ +public final class $class_name implements io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_name> { +$fields +$hash_code +$equals +$to_string +} +""") -def generate_dto_fields(camel_case_dto_name, func): - 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) - return fields +def _generate_event_dto(msg, model): + msg_java_name_upper = msg.java_name_upper + # Negative retval is mapped to java exception, so filter it out: + fields = filter(lambda field: not is_retval(field), msg.fields) + return _EVENT_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + json_definition=msg.doc, + class_name=msg_java_name_upper, + fields=generate_fields(fields), + hash_code=generate_hash_code(fields), + equals=generate_equals(msg_java_name_upper, fields), + to_string=generate_to_string(msg_java_name_upper, fields)) +_EVENT_TEMPLATE = Template(""" +package $plugin_package.dto; -tostring_field_template = Template(""" \"$field_name=\" + $field_name + ", " +\n""") -tostring_array_field_template = Template(""" \"$field_name=\" + java.util.Arrays.toString($field_name) + ", " +\n""") -tostring_template = Template(""" @Override - public String toString() { - return "$cls_name{" + -$fields_tostring "}"; - }\n\n""") +/** + * <p>This class represents event DTO. + * <br>It was generated by jvpp_dto_gen.py based on $json_filename: + * <pre> +$json_definition + * </pre> + */ +public final class $class_name { +$fields +$hash_code +$equals +$to_string +} +""") -def generate_dto_tostring(camel_case_dto_name, func): - tostring_fields = "" - for t in zip(func['types'], func['args']): +def _generate_dump_reply_wrappers(work_dir, model, logger): + for msg in model.messages: + if is_details(msg): + logger.debug("Generating ReplyDump DTO for message %s", msg) + details_class = msg.java_name_upper + dto = _REPLY_DUMP_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + json_definition=msg.doc, + details_class=details_class, + details_field=msg.java_name_lower, + dump_class=msg.request_java + ) + with open("%s/%sReplyDump.java" % (work_dir, details_class), "w") as f: + f.write(dto) + +_REPLY_DUMP_TEMPLATE = Template(""" +package $plugin_package.dto; - field_name = util.underscore_to_camelcase(t[1]) - # for retval don't generate dto field in Reply - if util.is_retval_field(field_name): - continue +/** + * <p>This class represents dump reply wrapper. + * <br>It was generated by jvpp_dto_gen.py based on $json_filename: + * <pre> +$json_definition + * </pre> + */ +public final class ${details_class}ReplyDump implements io.fd.vpp.jvpp.dto.JVppReplyDump<${plugin_package}.dto.${dump_class}, ${plugin_package}.dto.${details_class}> { - # handle array types - if util.is_array(util.jni_2_java_type_mapping[t[0]]): - tostring_fields += tostring_array_field_template.substitute(field_name=field_name) - else: - tostring_fields += tostring_field_template.substitute(field_name=field_name) + public java.util.List<${details_class}> ${details_field} = new java.util.ArrayList<>(); - return tostring_template.substitute(cls_name=camel_case_dto_name, - fields_tostring=tostring_fields[:-8]) + @Override + @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") + public int hashCode() { + return java.util.Objects.hash(${details_field}); + } -equals_other_template = Template(""" - final $cls_name other = ($cls_name) o; -\n""") -equals_field_template = Template(""" if (!java.util.Objects.equals(this.$field_name, other.$field_name)) { - return false; - }\n""") -equals_array_field_template = Template(""" if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) { - return false; - }\n""") -equals_template = Template(""" @Override + @Override public boolean equals(final Object o) { if (this == o) { return true; @@ -174,147 +210,22 @@ equals_template = Template(""" @Override if (o == null || getClass() != o.getClass()) { return false; } -$comparisons - return true; - }\n\n""") - - -def generate_dto_equals(camel_case_dto_name, func): - equals_fields = "" - for t in zip(func['types'], func['args']): - field_name = util.underscore_to_camelcase(t[1]) - # for retval don't generate dto field in Reply - if util.is_retval_field(field_name): - continue - - # handle array types - if util.is_array(util.jni_2_java_type_mapping[t[0]]): - equals_fields += equals_array_field_template.substitute(field_name=field_name) - else: - equals_fields += equals_field_template.substitute(field_name=field_name) - - if equals_fields != "": - equals_fields = equals_other_template.substitute(cls_name=camel_case_dto_name) + equals_fields - - return equals_template.substitute(comparisons=equals_fields) + final ${details_class}ReplyDump other = (${details_class}ReplyDump) o; -hash_template = Template(""" @Override - @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") - public int hashCode() { - return java.util.Objects.hash($fields); - }\n\n""") -hash_single_array_type_template = Template(""" @Override - @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") - public int hashCode() { - return java.util.Arrays.hashCode($fields); - }\n\n""") - + if (!java.util.Objects.equals(this.${details_field}, other.${details_field})) { + return false; + } -def generate_dto_hash(func): - hash_fields = "" + return true; + } - # Special handling for hashCode in case just a single array field is present. Cannot use Objects.equals since the - # array is mistaken for a varargs parameter. Instead use Arrays.hashCode in such case. - if len(func['args']) == 1: - single_type = func['types'][0] - single_type_name = func['args'][0] - if util.is_array(util.jni_2_java_type_mapping[single_type]): - return hash_single_array_type_template.substitute(fields=util.underscore_to_camelcase(single_type_name)) + @Override + public String toString() { + return "${details_class}ReplyDump{" + + "${details_field}=" + ${details_field} + "}"; + } - for t in zip(func['types'], func['args']): - field_name = util.underscore_to_camelcase(t[1]) - # for retval don't generate dto field in Reply - if util.is_retval_field(field_name): - continue - hash_fields += field_name + ", " - - return hash_template.substitute(fields=hash_fields[:-2]) - - -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') - if base_type != "": - 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, - plugin_package=plugin_package, - base_type=base_type, - dto_package=dto_package)) - else: - dto_file.write(dto_template_typeless.substitute(inputfile=inputfile, - description=description, - docs=util.api_message_to_javadoc(func), - cls_name=camel_case_dto_name, - fields=fields, - methods=methods, - plugin_package=plugin_package, - dto_package=dto_package)) - dto_file.flush() - dto_file.close() - - -dump_dto_suffix = "ReplyDump" -dump_reply_artificial_dtos = {} - - -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="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'])) - dto_file.flush() - dto_file.close() - - -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>" % ( - 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 - # using artificial type for fields, just to bypass the is_array check in base methods generators - # the type is not really used - artificial_type = 'u8' - - # 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'] += '\n' + fields - dump_reply_artificial_dtos[request_dto_name]['field_names'].append(func['name']) - dump_reply_artificial_dtos[request_dto_name]['field_types'].append(artificial_type) - methods = '\n' + generate_dto_base_methods(dump_reply_artificial_dtos[request_dto_name]['cls_name'], - {'args': dump_reply_artificial_dtos[request_dto_name]['field_names'], - 'types': dump_reply_artificial_dtos[request_dto_name]['field_types']}) - dump_reply_artificial_dtos[request_dto_name]['methods'] = methods - else: - methods = '\n' + generate_dto_base_methods(cls_name, {'args': [func['name']], - 'types': [artificial_type]}) - dump_reply_artificial_dtos[request_dto_name] = ({'docs': util.api_message_to_javadoc(func), - 'cls_name': cls_name, - 'fields': fields, - 'field_names': [func['name']], - 'field_types': [artificial_type], - # strip too many newlines at the end of base method block - 'methods': methods, - 'plugin_package': plugin_package, - 'base_package': base_package, - 'base_type': base_type, - 'dto_package': dto_package}) +} +""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/enum_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/enum_gen.py deleted file mode 100644 index 51a74e678e2..00000000000 --- a/src/vpp-api/java/jvpp/gen/jvppgen/enum_gen.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2018 Cisco and/or its affiliates. -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import util -import jni_gen - - -def generate_enums(enum_list, inputfile, logger): - """ - Generates Java representation of enum types defined in the provided JSON file. - """ - - if not enum_list: - logger.debug("Skipping enum generation (%s does not define enum types)." % inputfile) - return - - logger.debug("Generating enums for %s" % inputfile) - - for enum in enum_list: - enum_name = None - enum_type = None - for e in enum: - if isinstance(e, basestring): - enum_name = e - elif type(e) is dict and 'enumtype' in e: - enum_type = e['enumtype'] - - if not enum_name: - logger.warn("%s enum is missing name. Skipping" % enum) - continue - - if not enum_type: - logger.warn("%s enum is missing value type. Skipping" % enum) - continue - - # TODO(VPP-1153): add real enum support. - # But first refactor java api generation - # (either VPP-1154 or VPP-1155, see also VPP-480). - - # As a workaround we just update all the mappings - # used for VPP API definitions to JAVA and C/JNI translation. - enum_array_type = enum_type + "[]" - type_name = "vl_api_" + enum_name + "_t" - array_type_name = type_name + "[]" - util.vpp_2_jni_type_mapping[type_name] = util.vpp_2_jni_type_mapping[enum_type] - util.vpp_2_jni_type_mapping[array_type_name] = util.vpp_2_jni_type_mapping[enum_array_type] - util.jni_2_java_type_mapping[type_name] = util.jni_2_java_type_mapping[enum_type] - util.jni_2_java_type_mapping[array_type_name] = util.jni_2_java_type_mapping[enum_array_type] - util.jni_2_signature_mapping[type_name] = util.jni_2_signature_mapping[enum_type] - util.jni_2_signature_mapping[array_type_name] = util.jni_2_signature_mapping[enum_array_type] - util.jni_field_accessors[type_name] = util.jni_field_accessors[enum_type] - util.jni_field_accessors[array_type_name] = util.jni_field_accessors[enum_array_type] - jni_gen.struct_setter_templates[type_name] = jni_gen.struct_setter_templates[enum_type] - jni_gen.struct_setter_templates[array_type_name] = jni_gen.struct_setter_templates[enum_array_type] - jni_gen.dto_field_setter_templates[type_name] = jni_gen.dto_field_setter_templates[enum_type] - jni_gen.dto_field_setter_templates[array_type_name] = jni_gen.dto_field_setter_templates[enum_array_type] diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py new file mode 100755 index 00000000000..6f2995d4f8c --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py @@ -0,0 +1,267 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from string import Template + +from jvpp_model import is_array, Class, is_retval + + +def generate_j2c_identifiers(element, class_ref_name, object_ref_name): + identifiers = [] + for field in element.fields: + field_type = field.type + identifiers.append(_REQUEST_FIELD_IDENTIFIER_TEMPLATE.substitute( + java_name=field.java_name, + class_ref_name=class_ref_name, + jni_signature=field_type.jni_signature, + jni_type=field_type.jni_type, + jni_accessor=field_type.jni_accessor, + object_ref_name=object_ref_name + )) + return "".join(identifiers) + +_REQUEST_FIELD_IDENTIFIER_TEMPLATE = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}, "${java_name}", "${jni_signature}"); + ${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId); +""") + + +# TODO(VPP-1185): introduce host_to_net functions wrappers and simplify j2c and c2j swap generation +def generate_j2c_swap(element, struct_ref_name): + initialization = [] + for field in element.fields: + field_type = field.type + if is_array(field): + template = _ARRAY_J2C_NO_SWAP_TEMPLATE + field_reference_name = field.java_name + c_name = field.name + swap_elements = None + jni_name = None + if field_type.is_swap_needed: + template = _ARRAY_J2C_SWAP_TEMPLATE + host = "%sArrayElements[_i]" % field_reference_name + net = "%s->%s[_i]" % (struct_ref_name, c_name) + swap_elements = field_type.get_host_to_net_function(host, net) + if isinstance(field_type.base_type, Class): + jni_name = field_type.base_type.jni_name + host = "%sArrayElement" % field_reference_name + net = "%s->%s[_i]" % (struct_ref_name, c_name) + swap_elements = field_type.get_host_to_net_function(host, net) + template = _CLASS_ARRAY_J2C_TEMPLATE + + initialization.append(template.substitute( + field_reference_name=field_reference_name, + field_length_check=_generate_field_length_check(field), + base_type=field_type.base_type.jni_accessor, + jni_base_type=field_type.base_type.jni_type, + struct_reference_name=struct_ref_name, + jni_name=jni_name, + c_name=c_name, + swap_elements=swap_elements + )) + else: + if field_type.is_swap_needed: + host = field.java_name + net = "%s->%s" % (struct_ref_name, field.name) + initialization.append(" %s;" % field_type.get_host_to_net_function(host, net)) + else: + initialization.append(" %s->%s = %s;" % (struct_ref_name, field.name, field.java_name)) + + return "\n".join(initialization) + +_ARRAY_J2C_NO_SWAP_TEMPLATE = Template(""" + if (${field_reference_name}) { + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + (*env)->Get${base_type}ArrayRegion(env, ${field_reference_name}, 0, cnt, (${jni_base_type} *)${struct_reference_name}->${c_name}); + } +""") + +_ARRAY_J2C_SWAP_TEMPLATE = Template(""" + if (${field_reference_name}) { + ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL); + size_t _i; + jsize cnt = (*env)->GetArrayLength(env, ${field_reference_name}); + ${field_length_check} + for (_i = 0; _i < cnt; _i++) { + ${swap_elements}; + } + (*env)->Release${base_type}ArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + } + """) + +_CLASS_ARRAY_J2C_TEMPLATE = Template(""" + { + if (${field_reference_name}) { + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + for (_i = 0; _i < cnt; _i++) { + jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i); + ${swap_elements}; + } + } + } +""") + + +def _generate_field_length_check(field): + # Enforce max length if array has fixed length or uses variable length syntax + field_length = str(field.array_len) + if field.array_len_field: + field_length = field.array_len_field.java_name + + # TODO: remove when ZLAs without length field are disabled + if field_length != "0": + return _FIELD_LENGTH_CHECK.substitute(field_length=field_length) + else: + return "" + +# Make sure we do not write more elements that are expected +_FIELD_LENGTH_CHECK = Template(""" + size_t max_size = ${field_length}; + if (cnt > max_size) cnt = max_size;""") + + +def generate_c2j_swap(element, object_ref_name, struct_ref_name): + msg_java_name = element.java_name_lower + setters = [] + for field in element.fields: + field_type = field.type + if is_retval(field): + # For retval don't generate setters and generate retval check + continue + elif is_array(field): + jni_name = "" + template = _ARRAY_C2J_SWAP_TEMPLATE if field_type.is_swap_needed else _ARRAY_C2J_NO_SWAP_TEMPLATE + if isinstance(field_type.base_type, Class): + template = _ARRAY_C2J_CLASS_SWAP_TEMPLATE + jni_name = field_type.base_type.jni_name + setters.append(template.substitute( + field_reference_name=field.java_name, + class_ref_name=msg_java_name, + jni_signature=field_type.jni_signature, + jni_type=field_type.jni_type, + jni_name=jni_name, + base_type=field_type.base_type.jni_accessor, + field_length=_generate_array_length(field, struct_ref_name), + jni_base_type=field_type.base_type.jni_type, + object_ref_name=object_ref_name, + struct_ref_name=struct_ref_name, + net_to_host_function=field_type.net_to_host_function, + c_name=field.name + )) + else: + if field_type.is_swap_needed: + template = _SIMPLE_TYPE_FIELD_SETTER_TEMPLATE + jni_name = "" + if isinstance(field_type, Class): + template = _STRUCT_SETTER_TEMPLATE + jni_name = field_type.jni_name + setters.append(template.substitute( + java_name=field.java_name, + class_ref_name=msg_java_name, + jni_signature=field_type.jni_signature, + jni_name=jni_name, + jni_accessor=field_type.jni_accessor, + object_ref_name=object_ref_name, + struct_ref_name=struct_ref_name, + net_to_host_function=field_type.net_to_host_function, + c_name=field.name + )) + else: + setters.append(_SIMPLE_TYPE_NO_SWAP_FIELD_SETTER_TEMPLATE.substitute( + java_name=field.java_name, + class_ref_name=msg_java_name, + jni_signature=field_type.jni_signature, + jni_accessor=field_type.jni_accessor, + object_ref_name=object_ref_name, + struct_ref_name=struct_ref_name, + c_name=field.name + )) + return "".join(setters) + + +_SIMPLE_TYPE_FIELD_SETTER_TEMPLATE = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}"); + (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${net_to_host_function}(${struct_ref_name}->${c_name})); +""") + +_STRUCT_SETTER_TEMPLATE = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}"); + jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}"); + jmethodID ${java_name}Constructor = (*env)->GetMethodID(env, ${java_name}Class, "<init>", "()V"); + jobject ${java_name} = (*env)->NewObject(env, ${java_name}Class, ${java_name}Constructor); + ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}), ${java_name}); + (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${java_name}); + (*env)->DeleteLocalRef(env, ${java_name}); +""") + +_SIMPLE_TYPE_NO_SWAP_FIELD_SETTER_TEMPLATE = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}"); + (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${struct_ref_name}->${c_name}); +""") + +_ARRAY_C2J_NO_SWAP_TEMPLATE = Template(""" + jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}"); + ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length}); + (*env)->Set${base_type}ArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const ${jni_base_type}*)${struct_ref_name}->${c_name}); + (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); +""") + +_ARRAY_C2J_SWAP_TEMPLATE = Template(""" + jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}"); + { + ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length}); + ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${field_reference_name}ArrayElements[_i] = ${net_to_host_function}(${struct_ref_name}->${c_name}[_i]); + } + + (*env)->Release${base_type}ArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); + } +""") + +_ARRAY_C2J_CLASS_SWAP_TEMPLATE = Template(""" + jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}"); + { + jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}"); + jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0); + jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V"); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); + ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}[_i]), ${field_reference_name}ArrayElement); + (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement); + (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement); + } + (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); + } +""") + + +def _generate_array_length(field, struct_ref_name): + if field.array_len_field: + len_field = field.array_len_field + if len_field.type.is_swap_needed: + return "%s(%s->%s)" % (len_field.type.host_to_net_function, struct_ref_name, len_field.name) + else: + return "%s->%s" % (struct_ref_name, len_field.name) + return field.array_len diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py index 8c8be5b8877..a20a90d4720 100644..100755 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,285 +12,149 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +# from string import Template -import util - -variable_length_array_value_template = Template("""mp->${length_var_name}""") -variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""") +from jni_impl_gen import generate_jni_impl +from jni_msg_handlers_gen import generate_jni_handlers +from jni_type_handlers_gen import generate_type_handlers +from jvpp_model import is_control_ping, is_dump, is_request, is_control_ping_reply -dto_field_id_template = Template(""" - jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_name}", "${jni_signature}");""") -default_dto_field_setter_template = Template(""" - (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, mp->${c_name}); -""") - -u16_dto_field_setter_template = Template(""" - (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u16(mp->${c_name})); -""") +def generate_jni(work_dir, model, logger): + logger.debug("Generating jvpp C for %s" % model.json_api_files) + plugin_name = model.plugin_name + messages = model.messages -u32_dto_field_setter_template = Template(""" - (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u32(mp->${c_name})); -""") + with open("%s/jvpp_%s_gen.h" % (work_dir, plugin_name), "w") as f: + f.write(_JVPP_C_TEMPLATE.substitute( + json_filename=model.json_api_files, + class_cache=_generate_class_cache(plugin_name, messages), + api_verification=_generate_api_verification(messages), + type_handlers=generate_type_handlers(model), + jni_implementations=generate_jni_impl(model), + msg_handlers=generate_jni_handlers(model), + handler_registration=_generate_handler_registration(messages))) -u64_dto_field_setter_template = Template(""" - (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u64(mp->${c_name})); -""") +_JVPP_C_TEMPLATE = Template("""/** + * This file contains JNI bindings for jvpp Java API. + * It was generated by jvpp_jni_gen.py based on $json_filename. + */ +$class_cache -u8_array_dto_field_setter_template = Template(""" - jbyteArray ${field_reference_name} = (*env)->NewByteArray(env, ${field_length}); - (*env)->SetByteArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const jbyte*)mp->${c_name}); - (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); - (*env)->DeleteLocalRef(env, ${field_reference_name}); -""") +$api_verification -u16_array_dto_field_setter_template = Template(""" - { - jshortArray ${field_reference_name} = (*env)->NewShortArray(env, ${field_length}); - jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL); - unsigned int _i; - for (_i = 0; _i < ${field_length}; _i++) { - ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u16(mp->${c_name}[_i]); - } +// Type handlers +$type_handlers - (*env)->ReleaseShortArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); - (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); - (*env)->DeleteLocalRef(env, ${field_reference_name}); - } -""") +// JNI bindings +$jni_implementations -u32_array_dto_field_setter_template = Template(""" - { - jintArray ${field_reference_name} = (*env)->NewIntArray(env, ${field_length}); - jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL); - unsigned int _i; - for (_i = 0; _i < ${field_length}; _i++) { - ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u32(mp->${c_name}[_i]); - } +// Message handlers +$msg_handlers - (*env)->ReleaseIntArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); - (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); - (*env)->DeleteLocalRef(env, ${field_reference_name}); - } +$handler_registration """) -# 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 ${field_reference_name} = (*env)->NewLongArray(env, ${field_length}); - jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL); - unsigned int _i; - for (_i = 0; _i < ${field_length}; _i++) { - ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]); - } - (*env)->ReleaseLongArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); - (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); - (*env)->DeleteLocalRef(env, ${field_reference_name}); - } +def _generate_class_cache(plugin_name, messages): + references = [] + for msg in messages: + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + references.append(( + msg.java_name_lower, + 'io/fd/vpp/jvpp/%s/dto/%s' % (plugin_name, msg.java_name_upper) + )) + + references.append(('callbackException', 'io/fd/vpp/jvpp/VppCallbackException')) + + return _CLASS_CACHE_TEMPLATE.substitute( + class_references=_generate_class_references(references), + create_references=_generate_create_references(references), + delete_references=_generate_delete_references(references) + ) + +_CLASS_CACHE_TEMPLATE = Template(""" +// JAVA class reference cache +$class_references + +static int cache_class_references(JNIEnv* env) { +$create_references + return 0; +} + +static void delete_class_references(JNIEnv* env) { +$delete_references +}""") + + +def _generate_class_references(references): + return "\n".join("jclass %sClass;" % r[0] for r in references) + + +def _generate_create_references(references): + items = [] + for r in references: + items.append(_CREATE_GLOBAL_REF_TEMPLATE.substitute( + ref_name=r[0], + fqn_name=r[1] + )) + return "".join(items) + +_CREATE_GLOBAL_REF_TEMPLATE = Template(""" + ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${fqn_name}")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + }""") + + +def _generate_delete_references(references): + items = [] + for r in references: + items.append(_DELETE_CLASS_INVOCATION_TEMPLATE.substitute(ref_name=r[0])) + return "".join(items) + +_DELETE_CLASS_INVOCATION_TEMPLATE = Template(""" + if (${ref_name}Class) { + (*env)->DeleteGlobalRef(env, ${ref_name}Class); + }""") + + +def _generate_api_verification(messages): + items = [] + for msg in messages: + items.append("_(%s_%s) \\" % (msg.name, msg.crc)) + return _API_VERIFICATION_TEMPLATE.substitute(messages="\n".join(items)) + +_API_VERIFICATION_TEMPLATE = Template(""" +// List of supported API messages used for verification +#define foreach_supported_api_message \\ +$messages """) -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 - } - -def jni_reply_handler_for_type(handler_name, ref_name, field_type, c_name, field_reference_name, - field_name, field_length, is_variable_len_array, length_field_type, - object_name="dto"): +def _generate_handler_registration(messages): """ - Generates jni code that initializes a field of java object (dto or custom type). - To be used in reply message handlers. - :param field_type: type of the field to be initialized (as defined in vpe.api) - :param c_name: name of the message struct member that stores initialization value - :param field_reference_name: name of the field reference in generated code - :param field_name: name of the field (camelcase) - :param field_length: integer or name of variable that stores field length - :param object_name: name of the object to be initialized + Generates msg handler registration for all messages except for dumps and requests. + :param messages: collection of VPP API messages. """ - - # todo move validation to vppapigen - if field_type.endswith('[]') and field_length == '0': - raise Exception('Variable array \'%s\' defined in \'%s\' ' - 'should have defined length (e.g. \'%s[%s_length]\'' - % (c_name, handler_name, c_name, c_name)) - - if is_variable_len_array: - length_var_name = field_length - 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 - if util.is_retval_field(c_name): - return "" - - jni_signature = util.jni_2_signature_mapping[field_type] - jni_setter = util.jni_field_accessors[field_type] - - result = dto_field_id_template.substitute( - field_reference_name=field_reference_name, - field_name=field_name, - class_ref_name=ref_name, - jni_signature=jni_signature) - - dto_setter_template = dto_field_setter_templates[field_type] - - result += dto_setter_template.substitute( - jni_signature=jni_signature, - object_name=object_name, - field_reference_name=field_reference_name, - c_name=c_name, - jni_setter=jni_setter, - field_length=field_length) - return result - - -request_field_identifier_template = Template(""" - jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}"); - ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId); - """) - -array_length_enforcement_template = Template(""" - size_t max_size = ${field_length}; - if (cnt > max_size) cnt = max_size;""") - -u8_struct_setter_template = Template(""" - mp->${c_name} = ${field_reference_name};""") - -u16_struct_setter_template = Template(""" - mp->${c_name} = clib_host_to_net_u16(${field_reference_name});""") - -u32_struct_setter_template = Template(""" - mp->${c_name} = clib_host_to_net_u32(${field_reference_name});""") - -i32_struct_setter_template = Template(""" - mp->${c_name} = clib_host_to_net_i32(${field_reference_name});!""") - -u64_struct_setter_template = Template(""" - mp->${c_name} = clib_host_to_net_u64(${field_reference_name});""") - -u8_array_struct_setter_template = Template(""" - if (${field_reference_name}) { - jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); - ${field_length_check} - (*env)->GetByteArrayRegion(env, ${field_reference_name}, 0, cnt, (jbyte *)mp->${c_name}); - } + handlers = [] + for msg in messages: + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + if is_dump(msg) or is_request(msg): + continue + name = msg.name + crc = msg.crc + handlers.append("_(%s_%s, %s) \\" % (name, crc, name)) + return _HANDLER_REGISTRATION_TEMPLATE.substitute(handlers="\n".join(handlers)) + +_HANDLER_REGISTRATION_TEMPLATE = Template(""" +// Registration of message handlers in vlib +#define foreach_api_reply_handler \\ +$handlers """) - -u16_array_struct_setter_template = Template(""" - if (${field_reference_name}) { - jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL); - size_t _i; - jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); - ${field_length_check} - for (_i = 0; _i < cnt; _i++) { - mp->${c_name}[_i] = clib_host_to_net_u16(${field_reference_name}ArrayElements[_i]); - } - (*env)->ReleaseShortArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); - } - """) - -u32_array_struct_setter_template = Template(""" - if (${field_reference_name}) { - jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL); - size_t _i; - jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); - ${field_length_check} - for (_i = 0; _i < cnt; _i++) { - mp->${c_name}[_i] = clib_host_to_net_u32(${field_reference_name}ArrayElements[_i]); - } - (*env)->ReleaseIntArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); - } - """) - -u64_array_struct_setter_template = Template(""" - if (${field_reference_name}) { - jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL); - size_t _i; - jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); - ${field_length_check} - for (_i = 0; _i < cnt; _i++) { - mp->${c_name}[_i] = clib_host_to_net_u64(${field_reference_name}ArrayElements[_i]); - } - (*env)->ReleaseLongArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); - } - """) - -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 - } - - -def jni_request_identifiers_for_type(field_type, field_reference_name, field_name, object_name="request"): - """ - Generates jni code that defines C variable corresponding to field of java object - (dto or custom type). To be used in request message handlers. - :param field_type: type of the field to be initialized (as defined in vpe.api) - :param field_reference_name: name of the field reference in generated code - :param field_name: name of the field (camelcase) - :param object_name: name of the object to be initialized - """ - # field identifiers - jni_type = util.vpp_2_jni_type_mapping[field_type] - jni_signature = util.jni_2_signature_mapping[field_type] - jni_getter = util.jni_field_accessors[field_type] - - # field identifier - return request_field_identifier_template.substitute( - jni_type=jni_type, - field_reference_name=field_reference_name, - field_name=field_name, - jni_signature=jni_signature, - jni_getter=jni_getter, - object_name=object_name) - - -def jni_request_binding_for_type(field_type, c_name, field_reference_name, field_length, is_variable_len_array): - """ - Generates jni code that initializes C structure that corresponds to a field of java object - (dto or custom type). To be used in request message handlers. - :param field_type: type of the field to be initialized (as defined in vpe.api) - :param c_name: name of the message struct member to be initialized - :param field_reference_name: name of the field reference in generated code - :param field_length: integer or name of variable that stores field length - """ - - # field setter - field_length_check = "" - - # check if we are processing variable length array: - if is_variable_len_array: - field_length = util.underscore_to_camelcase(field_length) - - # enforce max length if array has fixed length or uses variable length syntax - if str(field_length) != "0": - field_length_check = array_length_enforcement_template.substitute(field_length=field_length) - - struct_setter_template = struct_setter_templates[field_type] - - msg_initialization = struct_setter_template.substitute( - c_name=c_name, - field_reference_name=field_reference_name, - field_length_check=field_length_check) - - return msg_initialization diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_impl_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_impl_gen.py new file mode 100755 index 00000000000..bf7523670a4 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_impl_gen.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from string import Template + +from jni_common_gen import generate_j2c_identifiers, generate_j2c_swap +from jvpp_model import is_dump, is_request, is_control_ping, is_control_ping_reply + + +def generate_jni_impl(model): + """ + Generates JNI bindings for sending dump and request messages. + :param model: meta-model of VPP API used for jVPP generation. + """ + jni_impl = [] + for msg in model.messages: + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control ping managed by jvpp registry. + continue + if not (is_dump(msg) or is_request(msg)): + continue + arguments = "" + request_class = "" + jni_identifiers = "" + msg_initialization = "" + + if msg.has_fields: + arguments = ", jobject request" + request_class = _REQUEST_CLASS_TEMPLATE.substitute( + plugin_name=model.plugin_name, + java_dto_name=msg.java_name_upper + ) + jni_identifiers = generate_j2c_identifiers(msg, class_ref_name="requestClass", object_ref_name="request") + msg_initialization = generate_j2c_swap(msg, struct_ref_name="mp") + + jni_impl.append(_JNI_IMPL_TEMPLATE.substitute( + c_name=msg.name, + json_filename=model.json_api_files, + json_definition=msg.doc, + plugin_name=model.plugin_name, + plugin_java_name=model.plugin_java_name, + java_method_name=msg.java_name_lower, + arguments=arguments, + request_class=request_class, + jni_identifiers=jni_identifiers, + msg_size=_generate_msg_size(msg), + crc=msg.crc, + msg_initialization=msg_initialization + )) + return "".join(jni_impl) + + +_JNI_IMPL_TEMPLATE = Template(""" +/** + * JNI binding for sending ${c_name} message. + * Generated based on $json_filename: +$json_definition + */ +JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${plugin_java_name}Impl_${java_method_name}0 +(JNIEnv * env, jclass clazz${arguments}) { + ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; + vl_api_${c_name}_t * mp; + u32 my_context_id = vppjni_get_context_id (&jvpp_main); +$request_class +$jni_identifiers + + // create message: + const size_t _size = ${msg_size}; + mp = vl_msg_api_alloc(_size); + memset (mp, 0, _size); + mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}")); + mp->client_index = plugin_main->my_client_index; + mp->context = clib_host_to_net_u32 (my_context_id); + +$msg_initialization + + // send message: + if (CLIB_DEBUG > 1) + clib_warning ("Sending ${c_name} message"); + vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp); + if ((*env)->ExceptionCheck(env)) { + return JNI_ERR; + } + return my_context_id; +}""") + +# TODO: cache method and field identifiers to achieve better performance +# https://jira.fd.io/browse/HONEYCOMB-42 +_REQUEST_CLASS_TEMPLATE = Template(""" jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_dto_name}"); +""") + + +def _generate_msg_size(msg): + msg_size = "sizeof(*mp)" + _size_components = [] + for field in msg.fields: + # Ignore ZLAs for simplicity (to support them we need to call JNI functions to check actual size) + if field.array_len_field: + _size_components += " + %s*sizeof(%s)" % (field.array_len_field.java_name, field.type.base_type.vpp_name) + # FIXME(VPP-586): for proper nested structures support, we need generate functions computing type sizes + # and use it instead of sizeof + + return msg_size + "".join(_size_components) diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py new file mode 100755 index 00000000000..8f6410f1af2 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from string import Template + +from jni_common_gen import generate_c2j_swap +from jvpp_model import is_dump, is_request, is_control_ping, is_control_ping_reply, is_retval + + +def generate_jni_handlers(model): + """ + Generates msg handlers for all messages except for dumps and requests (handled by vpp, not client). + :param model: meta-model of VPP API used for jVPP generation. + """ + jni_impl = [] + for msg in model.messages: + msg_name = msg.name + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control ping managed by jvpp registry. + continue + if is_dump(msg) or is_request(msg): + continue + + jni_impl.append(_MSG_HANDLER_TEMPLATE.substitute( + c_name=msg_name, + json_filename=model.json_api_files, + json_definition=msg.doc, + plugin_name=model.plugin_name, + err_handler=_generate_error_handler(msg), + class_ref_name=msg.java_name_lower, + dto_name=msg.java_name_upper, + dto_setters=generate_c2j_swap(msg, object_ref_name="dto", struct_ref_name="mp") + )) + return "".join(jni_impl) + +_MSG_HANDLER_TEMPLATE = Template(""" +/** + * Handler for ${c_name} message. + * Generated based on $json_filename: +$json_definition + */ +static void vl_api_${c_name}_t_handler (vl_api_${c_name}_t * mp) +{ + ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; + JNIEnv *env = jvpp_main.jenv; + jthrowable exc; +$err_handler + + if (CLIB_DEBUG > 1) + clib_warning ("Received ${c_name} event message"); + + jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V"); + + // User does not have to provide callbacks for all VPP messages. + // We are ignoring messages that are not supported by user. + (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared + jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V"); + exc = (*env)->ExceptionOccurred(env); + if (exc) { + clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n"); + (*env)->ExceptionDescribe(env); + (*env)->ExceptionClear(env); + return; + } + + jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor); +$dto_setters + + (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto); + // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni + (*env)->DeleteLocalRef(env, dto); +}""") + + +def _generate_error_handler(msg): + err_handler = "" + for field in msg.fields: + if is_retval(field): + err_handler = _ERR_HANDLER_TEMPLATE.substitute(name=msg.name) + return err_handler + +# Code fragment for checking result of the operation before sending request reply. +# Error checking is optional (some messages, e.g. detail messages do not have retval field). +_ERR_HANDLER_TEMPLATE = Template(""" + // for negative result don't send callback message but send error callback + if (mp->retval<0) { + call_on_error("${name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass); + return; + } + if (mp->retval == VNET_API_ERROR_IN_PROGRESS) { + clib_warning("Result in progress"); + return; + }""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py new file mode 100755 index 00000000000..eb6ab235ba5 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from string import Template + +from jni_common_gen import generate_j2c_swap, generate_j2c_identifiers, generate_c2j_swap +from jvpp_model import Class + + +def generate_type_handlers(model): + """ + Generates msg handlers for all messages except for dumps and requests (handled by vpp, not client). + :param model: meta-model of VPP API used for jVPP generation. + """ + type_handlers = [] + for t in model.types: + if not isinstance(t, Class): + continue + ref_name = t.java_name_lower + jni_identifiers = generate_j2c_identifiers(t, class_ref_name="%sClass" % ref_name, object_ref_name="_host") + type_handlers.append(_TYPE_NET_TO_HOST_TEMPLATE.substitute( + c_name=t.name, + json_filename=model.json_api_files, + json_definition=t.doc, + type_reference_name=ref_name, + class_FQN=t.jni_name, + jni_identifiers=jni_identifiers, + type_swap=generate_j2c_swap(t, struct_ref_name="_net") + )) + + type_handlers.append(_TYPE_HOST_TO_NET_TEMPLATE.substitute( + c_name=t.name, + json_filename=model.json_api_files, + json_definition=t.doc, + type_reference_name=ref_name, + class_FQN=t.jni_name, + jni_identifiers=jni_identifiers, + type_swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net") + )) + + return "\n".join(type_handlers) + +_TYPE_NET_TO_HOST_TEMPLATE = Template(""" +/** + * Host to network byte order conversion for ${c_name} type. + * Generated based on $json_filename: +$json_definition + */ +static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net) +{ + jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); +$jni_identifiers +$type_swap +}""") + +_TYPE_HOST_TO_NET_TEMPLATE = Template(""" +/** + * Network to host byte order conversion for ${c_name} type. + * Generated based on $json_filename: +$json_definition + */ +static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host) +{ + jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); +$type_swap +}""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py deleted file mode 100644 index b97a9f95435..00000000000 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py +++ /dev/null @@ -1,389 +0,0 @@ -#!/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 util -from string import Template - -import jni_gen - - -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, "io/fd/vpp/jvpp/${plugin_name}/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; - }""") - -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, 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_control_ping(class_name): - continue - - 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)) - - # add exception class to class cache - ref_name = 'callbackException' - class_name = 'io/fd/vpp/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)) - 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), - 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, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_name_upper}");""") - -request_field_identifier_template = Template(""" - jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}"); - ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId); - """) - -jni_msg_size_template = Template(""" + ${array_length}*sizeof(${element_type})""") - -jni_impl_template = Template(""" -/** - * JNI binding for sending ${c_name} message. - * Generated based on $inputfile preparsed data: -$api_data - */ -JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${field_name}0 -(JNIEnv * env, jclass clazz$args) { - ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; - vl_api_${c_name}_t * mp; - u32 my_context_id = vppjni_get_context_id (&jvpp_main); - $request_class - - $jni_identifiers - - // create message: - mp = vl_msg_api_alloc(${msg_size}); - memset (mp, 0, ${msg_size}); - mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}")); - mp->client_index = plugin_main->my_client_index; - mp->context = clib_host_to_net_u32 (my_context_id); - - $msg_initialization - - // send message: - if (CLIB_DEBUG > 1) - clib_warning ("Sending ${field_name} event 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, 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): - # Skip control ping managed by jvpp registry. - continue - if not (util.is_dump(f_name) or util.is_request(f_name, func_list)): - continue - - # Generate jni bindings for sending dump and request messages. - arguments = '' - request_class = '' - jni_identifiers = '' - msg_initialization = '' - f_name_uppercase = f_name.upper() - msg_size = 'sizeof(*mp)' - - 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, - plugin_name=plugin_name) - - for t in zip(f['types'], f['args'], f['lengths'], f['arg_types']): - field_name = util.underscore_to_camelcase(t[1]) - is_variable_len_array = t[2][1] - if is_variable_len_array: - msg_size += jni_msg_size_template.substitute(array_length=util.underscore_to_camelcase(t[2][0]), - element_type=t[3]) - jni_identifiers += jni_gen.jni_request_identifiers_for_type(field_type=t[0], - field_reference_name=field_name, - field_name=field_name) - msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1], - field_reference_name=field_name, - field_length=t[2][0], - is_variable_len_array=is_variable_len_array) - - jni_impl.append(jni_impl_template.substitute( - inputfile=inputfile, - api_data=util.api_message_to_javadoc(f), - field_reference_name=camel_case_function_name, - field_name=camel_case_function_name, - c_name_uppercase=f_name_uppercase, - c_name=f_name, - crc=f['crc'], - plugin_name=plugin_name, - java_plugin_name=plugin_name.title(), - request_class=request_class, - jni_identifiers=jni_identifiers, - msg_size=msg_size, - msg_initialization=msg_initialization, - args=arguments)) - - return "\n".join(jni_impl) - -# 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) { - 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) { - clib_warning("Result in progress"); - return; - } -""") - -msg_handler_template = Template(""" -/** - * 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) -{ - ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; - JNIEnv *env = jvpp_main.jenv; - jthrowable exc; - $err_handler - - if (CLIB_DEBUG > 1) - clib_warning ("Received ${handler_name} event message"); - - jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V"); - - // User does not have to provide callbacks for all VPP messages. - // We are ignoring messages that are not supported by user. - (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared - jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V"); - exc = (*env)->ExceptionOccurred(env); - if (exc) { - clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n"); - (*env)->ExceptionDescribe(env); - (*env)->ExceptionClear(env); - return; - } - - jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor); - $dto_setters - - (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto); - // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni - (*env)->DeleteLocalRef(env, dto); -}""") - - -def generate_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): - # Skip control ping managed by jvpp registry. - continue - if util.is_dump(handler_name) or util.is_request(handler_name, func_list): - continue - - # Generate msg handlers for all messages except for dumps and requests (handled by vpp, not client). - dto_setters = '' - err_handler = '' - # dto setters - for t in zip(f['types'], f['args'], f['lengths']): - c_name = t[1] - java_name = util.underscore_to_camelcase(c_name) - field_length = t[2][0] - is_variable_len_array = t[2][1] - length_field_type = None - if is_variable_len_array: - length_field_type = f['types'][f['args'].index(field_length)] - dto_setters += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, ref_name=ref_name, - field_type=t[0], c_name=t[1], - field_reference_name=java_name, - field_name=java_name, field_length=field_length, - is_variable_len_array=is_variable_len_array, - length_field_type=length_field_type) - - # 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 - - handlers.append(msg_handler_template.substitute( - 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, - err_handler=err_handler)) - - return "\n".join(handlers) - - -handler_registration_template = Template("""_(${name}_${crc}, ${name}) \\ -""") - - -def generate_handler_registration(func_list): - handler_registration = ["#define foreach_api_reply_handler \\\n"] - for f in func_list: - name = f['name'] - camelcase_name = util.underscore_to_camelcase(name) - - if util.is_control_ping(camelcase_name): - # Skip control ping managed by registry. - continue - if util.is_dump(name) or util.is_request(name, func_list): - continue - - # Generate msg handler registration for all messages except for dumps and requests. - handler_registration.append(handler_registration_template.substitute( - name=name, - crc=f['crc'])) - - return "".join(handler_registration) - - -api_verification_template = Template("""_(${name}_${crc}) \\ -""") - - -def generate_api_verification(func_list): - api_verification = ["#define foreach_supported_api_message \\\n"] - for f in func_list: - name = f['name'] - - api_verification.append(api_verification_template.substitute( - name=name, - crc=f['crc'])) - - return "".join(api_verification) - - -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 api file generated by vppapigen). - */ - -// JAVA class reference cache -$class_cache - -// List of supported API messages used for verification -$api_verification - -// JNI bindings -$jni_implementations - -// Message handlers -$msg_handlers - -// Registration of message handlers in vlib -$handler_registration -""") - - -def generate_jvpp(func_list, plugin_name, inputfile, path, logger): - """ Generates jvpp C file """ - logger.debug("Generating jvpp C for %s" % 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) - api_verification = generate_api_verification(func_list) - - jvpp_c_file = open("%s/jvpp_%s_gen.h" % (path, plugin_name), 'w') - jvpp_c_file.write(jvpp_c_template.substitute( - inputfile=inputfile, - class_cache=class_cache, - api_verification=api_verification, - jni_implementations=jni_impl, - msg_handlers=msg_handlers, - handler_registration=handler_registration)) - jvpp_c_file.flush() - jvpp_c_file.close() - diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py index c378721c000..c5634de2e48 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,21 +12,36 @@ # 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 +from jvpp_model import is_control_ping, is_dump, is_request, is_event, is_control_ping_reply + + +def generate_callback_facade(work_dir, model, logger): + """ Generates callback facade """ + logger.debug("Generating JVpp callback facade for %s" % model.json_api_files) + _generate_ifc(work_dir, model), + _generate_impl(work_dir, model) + _generate_callback(work_dir, model) + + +def _generate_ifc(work_dir, model): + with open("%s/CallbackJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f: + f.write(_IFC_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + methods=_generate_ifc_methods(model) + )) -jvpp_ifc_template = Template(""" -package $plugin_package.$callback_facade_package; +_IFC_TEMPLATE = Template(""" +package $plugin_package.callfacade; /** * <p>Callback Java API representation of $plugin_package plugin. - * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename. */ -public interface CallbackJVpp${plugin_name} extends $base_package.$notification_package.EventRegistryProvider, java.lang.AutoCloseable { +public interface CallbackJVpp${plugin_name} extends io.fd.vpp.jvpp.notification.EventRegistryProvider, java.lang.AutoCloseable { // TODO add send @@ -34,29 +49,66 @@ $methods } """) -jvpp_impl_template = Template(""" -package $plugin_package.$callback_facade_package; + +def _generate_ifc_methods(model): + plugin_package = model.plugin_package + methods = [] + for msg in model.messages: + if is_control_ping(msg): + # Skip control ping managed by jvpp registry. + continue + if not (is_dump(msg) or is_request(msg)): + # Skip replies and messages that do not not have replies (e.g events/counters). + continue + template = _IFC_NO_ARG_METHOD_TEMPLATE + if msg.has_fields: + template = _IFC_METHOD_TEMPLATE + methods.append(template.substitute( + name=msg.java_name_lower, + plugin_package=plugin_package, + request=msg.java_name_upper, + reply=msg.reply_java + )) + return "\n".join(methods) + +_IFC_NO_ARG_METHOD_TEMPLATE = Template( + """ void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""") + +_IFC_METHOD_TEMPLATE = Template( + """ void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""") + + +def _generate_impl(work_dir, model): + with open("%s/CallbackJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f: + f.write(_IMPL_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + methods=_generate_impl_methods(model) + )) + +_IMPL_TEMPLATE = Template(""" +package $plugin_package.callfacade; /** * <p>Default implementation of Callback${plugin_name}JVpp interface. - * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename. */ public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plugin_name} { private final $plugin_package.JVpp${plugin_name} jvpp; - private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> callbacks; - private final $plugin_package.$notification_package.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.$notification_package.${plugin_name}EventRegistryImpl(); + private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> callbacks; + private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl(); /** * <p>Create CallbackJVpp${plugin_name}Facade object for provided JVpp instance. * Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks * and then connects to provided JVpp instance * - * @param jvpp provided $base_package.JVpp instance + * @param jvpp provided io.fd.vpp.jvpp.JVpp instance * * @throws java.io.IOException in case instance cannot connect to JVPP */ - public CallbackJVpp${plugin_name}Facade(final $base_package.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException { + public CallbackJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException { this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null"); this.callbacks = new java.util.HashMap<>(); java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null"); @@ -64,7 +116,7 @@ public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plu } @Override - public $plugin_package.$notification_package.${plugin_name}EventRegistry getEventRegistry() { + public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() { return eventRegistry; } @@ -79,139 +131,76 @@ $methods } """) -method_template = Template( - """ 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($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException { +def _generate_impl_methods(model): + plugin_package = model.plugin_package + methods = [] + for msg in model.messages: + if is_control_ping(msg): + # Skip control ping managed by jvpp registry. + continue + if not (is_dump(msg) or is_request(msg)): + # Skip replies and messages that do not not have replies (e.g events/counters). + continue + template = _IMPL_NO_ARG_METHOD_TEMPLATE + if msg.has_fields: + template = _IMPL_METHOD_TEMPLATE + methods.append(template.substitute( + name=msg.java_name_lower, + plugin_package=plugin_package, + request=msg.java_name_upper, + reply=msg.reply_java + )) + return "\n".join(methods) + +_IMPL_NO_ARG_METHOD_TEMPLATE = Template( + """ public final void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException { synchronized (callbacks) { - callbacks.put(jvpp.$name(request), callback); + callbacks.put(jvpp.$name(), callback); } } """) -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 { +_IMPL_METHOD_TEMPLATE = Template(""" public final void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException { synchronized (callbacks) { - callbacks.put(jvpp.$name(), callback); + callbacks.put(jvpp.$name(request), callback); } } """) -def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, - notification_package, callback_facade_package, inputfile, logger): - """ Generates callback facade """ - logger.debug("Generating JVpp callback facade for %s" % inputfile) - - if os.path.exists(callback_facade_package): - util.remove_folder(callback_facade_package) - - os.mkdir(callback_facade_package) - - methods = [] - methods_impl = [] - - # Generate methods for sending messages. - for func in func_list: - 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) or util.is_control_ping(camel_case_name): - continue +def _generate_callback(work_dir, model): + with open("%s/CallbackJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f: + f.write(_CALLBACK_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + methods=_generate_callback_methods(model) + )) - # Strip suffix for dump calls - callback_type = get_request_name(camel_case_name_upper) - if util.is_dump(camel_case_name_upper): - callback_type += "Details" - elif util.is_request(func['name'], func_list): - callback_type += "Reply" - else: - # Skip messages that do not not have replies (e.g events/counters). - continue - callback_type += callback_gen.callback_suffix - - 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)) - 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, - 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%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, "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, - callback_facade_package=callback_facade_package)) - jvpp_file.flush() - jvpp_file.close() - - 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 $plugin_package.$callback_facade_package; +_CALLBACK_TEMPLATE = Template(""" +package $plugin_package.callfacade; /** * <p>Implementation of JVppGlobalCallback interface for Java Callback API. - * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename. */ -public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback { +public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback { - private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requests; - private final $plugin_package.$notification_package.Global${plugin_name}EventCallback eventCallback; + private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requests; + private final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback; private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVpp${plugin_name}FacadeCallback.class.getName()); - public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requestMap, - final $plugin_package.$notification_package.Global${plugin_name}EventCallback eventCallback) { + public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requestMap, + final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback) { this.requests = requestMap; this.eventCallback = eventCallback; } @Override - public void onError($base_package.VppCallbackException reply) { + public void onError(io.fd.vpp.jvpp.VppCallbackException reply) { - $base_package.$callback_package.JVppCallback failedCall; + io.fd.vpp.jvpp.callback.JVppCallback failedCall; synchronized(requests) { failedCall = requests.remove(reply.getCtxId()); } @@ -228,12 +217,12 @@ public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_p @Override @SuppressWarnings("unchecked") - public void onControlPingReply(final $base_package.$dto_package.ControlPingReply reply) { + public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) { - $base_package.$callback_package.ControlPingCallback callback; + io.fd.vpp.jvpp.callback.ControlPingCallback callback; final int replyId = reply.context; synchronized(requests) { - callback = ($base_package.$callback_package.ControlPingCallback) requests.remove(replyId); + callback = (io.fd.vpp.jvpp.callback.ControlPingCallback) requests.remove(replyId); } if(callback != null) { @@ -245,88 +234,56 @@ $methods } """) -jvpp_facade_callback_method_template = Template(""" + +def _generate_callback_methods(model): + plugin_package = model.plugin_package + methods = [] + for msg in model.messages: + if is_dump(msg) or is_request(msg): + continue + if is_control_ping_reply(msg): + # Skip control ping managed by jvpp registry. + continue + + # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). + template = _CALLBACK_METHOD_TEMPLATE + if is_event(msg): + template = _CALLBACK_EVENT_METHOD_TEMPLATE + msg_name = msg.java_name_upper + methods.append(template.substitute( + message=msg_name, + callback="%sCallback" % msg_name, + plugin_package=plugin_package + )) + return "\n".join(methods) + +_CALLBACK_METHOD_TEMPLATE = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) { + public void on${message}(final $plugin_package.dto.${message} reply) { - $plugin_package.$callback_package.$callback callback; + $plugin_package.callback.$callback callback; final int replyId = reply.context; if (LOG.isLoggable(java.util.logging.Level.FINE)) { - LOG.fine(String.format("Received $callback_dto event message: %s", reply)); + LOG.fine(String.format("Received ${message} event message: %s", reply)); } synchronized(requests) { - callback = ($plugin_package.$callback_package.$callback) requests.remove(replyId); + callback = ($plugin_package.callback.$callback) requests.remove(replyId); } if(callback != null) { - callback.on$callback_dto(reply); + callback.on${message}(reply); } } """) -jvpp_facade_callback_notification_method_template = Template(""" +_CALLBACK_EVENT_METHOD_TEMPLATE = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) { + public void on${message}($plugin_package.dto.${message} notification) { if (LOG.isLoggable(java.util.logging.Level.FINE)) { - LOG.fine(String.format("Received $callback_dto event message: %s", notification)); + LOG.fine(String.format("Received ${message} event message: %s", notification)); } - eventCallback.on$callback_dto(notification); + eventCallback.on${message}(notification); } """) - - -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: - camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - - if util.is_control_ping(camel_case_name_with_suffix): - # Skip control ping managed by jvpp registry. - continue - if util.is_dump(func['name']) or util.is_request(func['name'], func_list): - continue - - # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). - if util.is_reply(camel_case_name_with_suffix): - request_method = camel_case_name_with_suffix - callbacks.append(jvpp_facade_callback_method_template.substitute(plugin_package=plugin_package, - dto_package=dto_package, - callback_package=callback_package, - callback=camel_case_name_with_suffix + callback_gen.callback_suffix, - callback_dto=request_method)) - else: - callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package, - dto_package=dto_package, - callback_package=callback_package, - callback=camel_case_name_with_suffix + callback_gen.callback_suffix, - callback_dto=camel_case_name_with_suffix)) - - 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, - methods="".join(callbacks), - callback_facade_package=callback_facade_package)) - jvpp_file.flush() - jvpp_file.close() - - -# Returns request name -def get_request_name(camel_case_dto_name): - return remove_suffix(camel_case_dto_name) - - -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/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py new file mode 100755 index 00000000000..73615ca7178 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from string import Template + +from jvpp_model import is_array + + +def generate_fields(fields): + return "\n".join(_FIELD_TEMPLATE.substitute(type=f.type.java_name_fqn, name=f.java_name) for f in fields) + +_FIELD_TEMPLATE = Template(""" public $type $name;""") + + +def generate_hash_code(fields): + if len(fields) == 1 and is_array(fields[0]): + return _HASH_CODE_SINGLE_ARRAY_TEMPLATE.substitute(array_field=fields[0].java_name) + return _HASH_CODE_TEMPLATE.substitute(fields=", ".join(f.java_name for f in fields)) + +_HASH_CODE_TEMPLATE = Template(""" + @Override + @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") + public int hashCode() { + return java.util.Objects.hash($fields); + }""") + +_HASH_CODE_SINGLE_ARRAY_TEMPLATE = Template(""" + @Override + @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") + public int hashCode() { + return java.util.Arrays.hashCode($array_field); + }""") + + +def generate_equals(class_name, fields): + comparisons = [] + for f in fields: + if is_array(f): + comparisons.append(_EQUALS_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name)) + else: + comparisons.append(_EQUALS_FIELD_TEMPLATE.substitute(field_name=f.java_name)) + + if comparisons: + comparisons.insert(0, _EQUALS_OTHER_TEMPLATE.substitute(cls_name=class_name)) + return _EQUALS_TEMPLATE.substitute(comparisons="\n".join(comparisons)) + +_EQUALS_TEMPLATE = Template(""" + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } +$comparisons + + return true; + }""") + +_EQUALS_OTHER_TEMPLATE = Template(""" + final $cls_name other = ($cls_name) o; +""") + +_EQUALS_FIELD_TEMPLATE = Template(""" if (!java.util.Objects.equals(this.$field_name, other.$field_name)) { + return false; + }""") + +_EQUALS_ARRAY_FIELD_TEMPLATE = Template(""" if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) { + return false; + }""") + + +def generate_to_string(class_name, fields): + to_string = [] + for f in fields: + if is_array(f): + to_string.append(_TO_STRING_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name)) + else: + to_string.append(_TO_STRING_FIELD_TEMPLATE.substitute(field_name=f.java_name)) + + to_string_fields = " \"}\";" + if to_string: + to_string_fields = " + \", \" +\n".join(to_string) + " + \"}\";" + + return _TO_STRING_TEMPLATE.substitute( + class_name=class_name, + to_string_fields=to_string_fields + ) + +_TO_STRING_TEMPLATE = Template(""" + @Override + public String toString() { + return "$class_name{" + +$to_string_fields + }""") + +_TO_STRING_FIELD_TEMPLATE = Template(""" \"$field_name=\" + $field_name""") + +_TO_STRING_ARRAY_FIELD_TEMPLATE = Template( + """ \"$field_name=\" + java.util.Arrays.toString($field_name)""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py index c1a7f1c1799..47a9985144d 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,41 +12,190 @@ # 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 +from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request, is_details, is_reply, is_event + + +def generate_future_facade(work_dir, model, logger): + logger.debug("Generating JVpp future facade for %s" % model.json_api_files) + _generate_future_jvpp(work_dir, model), + _generate_future_jvpp_facade(work_dir, model) + _generate_future_jvpp_callback(work_dir, model) + + +def _generate_future_jvpp(work_dir, model): + with open("%s/FutureJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f: + f.write(_FUTURE_JVPP_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + methods=_generate_future_jvpp_methods(model) + )) + +_FUTURE_JVPP_TEMPLATE = Template(''' +package $plugin_package.future; + +/** + * <p>Async facade extension adding specific methods for each request invocation + * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename. + */ +public interface FutureJVpp${plugin_name} extends io.fd.vpp.jvpp.future.FutureJVppInvoker { +$methods + + @Override + public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry(); + +} +''') + + +def _generate_future_jvpp_methods(model): + methods = [] + for msg in model.messages: + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + reply_name = None + if is_request(msg): + reply_name = msg.reply_java + elif is_dump(msg): + # use reply dump wrappers + reply_name = "%sReplyDump" % msg.reply_java + else: + continue + + methods.append(_FUTURE_JVPP_METHOD_TEMPLATE.substitute( + plugin_package=model.plugin_package, + method_name=msg.java_name_lower, + reply_name=reply_name, + request_name=msg.java_name_upper + )) + return "".join(methods) + +_FUTURE_JVPP_METHOD_TEMPLATE = Template(''' + java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}> ${method_name}(${plugin_package}.dto.${request_name} request); +''') + + +def _generate_future_jvpp_facade(work_dir, model): + with open("%s/FutureJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f: + f.write(_FUTURE_JVPP_FACADE_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + methods=_generate_future_jvpp_facade_methods(model) + )) -jvpp_facade_callback_template = Template(""" -package $plugin_package.$future_package; +_FUTURE_JVPP_FACADE_TEMPLATE = Template(''' +package $plugin_package.future; + +/** + * <p>Implementation of FutureJVpp based on AbstractFutureJVppInvoker + * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename. + */ +public class FutureJVpp${plugin_name}Facade extends io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker implements FutureJVpp${plugin_name} { + + private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl(); + + /** + * <p>Create FutureJVpp${plugin_name}Facade object for provided JVpp instance. + * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks + * and then connects to provided JVpp instance + * + * @param jvpp provided io.fd.vpp.jvpp.JVpp instance + * + * @throws java.io.IOException in case instance cannot connect to JVPP + */ + public FutureJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final io.fd.vpp.jvpp.JVpp jvpp) throws java.io.IOException { + super(jvpp, registry, new java.util.HashMap<>()); + java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null"); + registry.register(jvpp, new FutureJVpp${plugin_name}FacadeCallback(getRequests(), eventRegistry)); + } + + @Override + public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() { + return eventRegistry; + } + +$methods +} +''') + + +def _generate_future_jvpp_facade_methods(model): + methods = [] + for msg in model.messages: + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + template = None + if is_request(msg): + template = _FUTURE_JVPP_FACADE_REQUEST_TEMPLATE + elif is_dump(msg): + template = _FUTURE_JVPP_FACADE_DUMP_TEMPLATE + else: + continue + + methods.append(template.substitute( + plugin_package=model.plugin_package, + method_name=msg.java_name_lower, + reply_name=msg.reply_java, + request_name=msg.java_name_upper + )) + return "".join(methods) + +_FUTURE_JVPP_FACADE_REQUEST_TEMPLATE = Template(''' + @Override + public java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}> ${method_name}(${plugin_package}.dto.${request_name} request) { + return send(request); + } +''') + +_FUTURE_JVPP_FACADE_DUMP_TEMPLATE = Template(''' + @Override + public java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}ReplyDump> ${method_name}(${plugin_package}.dto.${request_name} request) { + return send(request, new ${plugin_package}.dto.${reply_name}ReplyDump()); + } +''') + + +def _generate_future_jvpp_callback(work_dir, model): + with open("%s/FutureJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f: + f.write(_FUTURE_JVPP_CALLBACK_TEMPLATE.substitute( + plugin_package=model.plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + methods=_generate_future_jvpp_callback_methods(model) + )) + +_FUTURE_JVPP_CALLBACK_TEMPLATE = Template(""" +package $plugin_package.future; /** * <p>Async facade callback setting values to future objects - * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename. */ -public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback { +public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback { - private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requests; - private final $plugin_package.$notification_package.Global${plugin_name}EventCallback notificationCallback; + private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends io.fd.vpp.jvpp.dto.JVppReply<?>>> requests; + private final $plugin_package.notification.Global${plugin_name}EventCallback notificationCallback; private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(FutureJVpp${plugin_name}FacadeCallback.class.getName()); public FutureJVpp${plugin_name}FacadeCallback( - final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requestMap, - final $plugin_package.$notification_package.Global${plugin_name}EventCallback notificationCallback) { + final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends io.fd.vpp.jvpp.dto.JVppReply<?>>> requestMap, + final $plugin_package.notification.Global${plugin_name}EventCallback notificationCallback) { this.requests = requestMap; this.notificationCallback = notificationCallback; } @Override @SuppressWarnings("unchecked") - public void onError($base_package.VppCallbackException reply) { - final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture; + public void onError(io.fd.vpp.jvpp.VppCallbackException reply) { + final java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>> completableFuture; synchronized(requests) { - completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(reply.getCtxId()); + completableFuture = (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>>) requests.get(reply.getCtxId()); } if(completableFuture != null) { @@ -60,19 +209,19 @@ public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_pac @Override @SuppressWarnings("unchecked") - public void onControlPingReply(final $base_package.$dto_package.ControlPingReply reply) { - java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture; + public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) { + java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>> completableFuture; final int replyId = reply.context; synchronized(requests) { - completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(replyId); + completableFuture = (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>>) requests.get(replyId); if(completableFuture != null) { // Finish dump call - if (completableFuture instanceof $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) { - completableFuture.complete((($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getReplyDump()); + if (completableFuture instanceof io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) { + completableFuture.complete(((io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getReplyDump()); // Remove future mapped to dump call context id - requests.remove((($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getContextId()); + requests.remove(((io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getContextId()); } else { // reply to regular control ping, complete the future completableFuture.complete(reply); @@ -91,269 +240,99 @@ $methods } """) -jvpp_facade_callback_method_template = Template(""" + +def _generate_future_jvpp_callback_methods(model): + methods = [] + for msg in model.messages: + if is_control_ping(msg) or is_control_ping_reply(msg): + # Skip control_ping managed by jvpp registry. + continue + if is_dump(msg) or is_request(msg): + continue + + # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). + template = None + request_dto = None + if is_details(msg): + template = _FUTURE_JVPP_FACADE_DETAILS_CALLBACK_TEMPLATE + request_dto = msg.request_java + elif is_reply(msg): + template = _FUTURE_JVPP_FACADE_REPLY_CALLBACK_TEMPLATE + request_dto = msg.request_java + elif is_event(msg): + template = _FUTURE_JVPP_FACADE_EVENT_CALLBACK_TEMPLATE + else: + raise TypeError("Unknown message type %s", msg) + + methods.append(template.substitute( + plugin_package=model.plugin_package, + callback_dto=msg.java_name_upper, + request_dto=request_dto, + callback_dto_field=msg.java_name_lower, + )) + return "".join(methods) + + +_FUTURE_JVPP_FACADE_DETAILS_CALLBACK_TEMPLATE = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) { - java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<$plugin_package.$dto_package.$request_dto>> completableFuture; + public void on$callback_dto(final $plugin_package.dto.$callback_dto reply) { + io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.dto.${callback_dto}ReplyDump> completableFuture; final int replyId = reply.context; if (LOG.isLoggable(java.util.logging.Level.FINE)) { LOG.fine(String.format("Received $callback_dto event message: %s", reply)); } synchronized(requests) { - completableFuture = - (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<$plugin_package.$dto_package.$request_dto>>) requests.get(replyId); + completableFuture = (io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.dto.${callback_dto}ReplyDump>) requests.get(replyId); - if(completableFuture != null) { - // received reply on request, complete future created by sender and remove it from map - completableFuture.complete(reply); - requests.remove(replyId); - } else { + if(completableFuture == null) { // reply received before writer created future, - // create new future, complete it and put into map to - // notify sender that reply is already received - completableFuture = new java.util.concurrent.CompletableFuture<>(); - completableFuture.complete(reply); + // create new future, and put into map to notify sender that reply is already received, + // following details replies will add information to this future + completableFuture = new io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<>(replyId, + new $plugin_package.dto.${callback_dto}ReplyDump()); requests.put(replyId, completableFuture); } + completableFuture.getReplyDump().$callback_dto_field.add(reply); } } """) -jvpp_facade_callback_notification_method_template = Template(""" - @Override - public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) { - if (LOG.isLoggable(java.util.logging.Level.FINE)) { - LOG.fine(String.format("Received $callback_dto event message: %s", notification)); - } - notificationCallback.on$callback_dto(notification); - } -""") - -jvpp_facade_details_callback_method_template = Template(""" +_FUTURE_JVPP_FACADE_REPLY_CALLBACK_TEMPLATE = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) { - $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump> completableFuture; + public void on$callback_dto(final $plugin_package.dto.$callback_dto reply) { + java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_dto>> completableFuture; final int replyId = reply.context; if (LOG.isLoggable(java.util.logging.Level.FINE)) { LOG.fine(String.format("Received $callback_dto event message: %s", reply)); } synchronized(requests) { - completableFuture = ($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump>) requests.get(replyId); + completableFuture = + (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_dto>>) requests.get(replyId); - if(completableFuture == null) { + if(completableFuture != null) { + // received reply on request, complete future created by sender and remove it from map + completableFuture.complete(reply); + requests.remove(replyId); + } else { // reply received before writer created future, - // create new future, and put into map to notify sender that reply is already received, - // following details replies will add information to this future - completableFuture = new $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<>(replyId, - new $plugin_package.$dto_package.$callback_dto_reply_dump()); + // create new future, complete it and put into map to + // notify sender that reply is already received + completableFuture = new java.util.concurrent.CompletableFuture<>(); + completableFuture.complete(reply); requests.put(replyId, completableFuture); } - completableFuture.getReplyDump().$callback_dto_field.add(reply); } } """) - -def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, - notification_package, future_facade_package, inputfile, logger): - """ Generates JVpp interface and JNI implementation """ - logger.debug("Generating JVpp future facade for %s" % inputfile) - - if not os.path.exists(future_facade_package): - os.mkdir(future_facade_package) - - methods = [] - methods_impl = [] - - # Generate methods for sending messages. - for func in func_list: - camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - - if util.is_control_ping(camel_case_name_with_suffix): - # Skip control ping managed by jvpp registry. - continue - - # Process replies instead of requests (optimization). - if not util.is_reply(camel_case_name_with_suffix): - # Do not generate send methods for messages that do not have replies. - continue - - 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 = util.underscore_to_camelcase_upper(func['name']) - methods.append(future_jvpp_method_template.substitute(plugin_package=plugin_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_dump_method_impl_template.substitute(plugin_package=plugin_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.remove_reply_suffix(camel_case_name_with_suffix) - - methods.append(future_jvpp_method_template.substitute(plugin_package=plugin_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(plugin_package=plugin_package, - dto_package=dto_package, - method_name=camel_case_request_method_name, - reply_name=camel_case_name_with_suffix, - request_name=request_name)) - - jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%s.java" % plugin_name), 'w') - jvpp_file.write(future_jvpp_template.substitute(inputfile=inputfile, - base_package=base_package, - plugin_package=plugin_package, - plugin_name=plugin_name, - notification_package=notification_package, - methods="".join(methods), - future_package=future_facade_package)) - jvpp_file.flush() - jvpp_file.close() - - jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%sFacade.java" % plugin_name), 'w') - jvpp_file.write(future_jvpp_facade_template.substitute(inputfile=inputfile, - base_package=base_package, - plugin_package=plugin_package, - plugin_name=plugin_name, - dto_package=dto_package, - notification_package=notification_package, - methods="".join(methods_impl), - future_package=future_facade_package)) - jvpp_file.flush() - jvpp_file.close() - - generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, future_facade_package, inputfile) - - -future_jvpp_template = Template(''' -package $plugin_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 api file generated by vppapigen). - */ -public interface FutureJVpp${plugin_name} extends $base_package.$future_package.FutureJVppInvoker { -$methods - - @Override - public $plugin_package.$notification_package.${plugin_name}EventRegistry getEventRegistry(); - -} -''') - -future_jvpp_method_template = Template(''' - java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request); -''') - - -future_jvpp_facade_template = Template(''' -package $plugin_package.$future_package; - -/** - * <p>Implementation of FutureJVpp based on AbstractFutureJVppInvoker - * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). - */ -public class FutureJVpp${plugin_name}Facade extends $base_package.$future_package.AbstractFutureJVppInvoker implements FutureJVpp${plugin_name} { - - private final $plugin_package.$notification_package.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.$notification_package.${plugin_name}EventRegistryImpl(); - - /** - * <p>Create FutureJVpp${plugin_name}Facade object for provided JVpp instance. - * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks - * and then connects to provided JVpp instance - * - * @param jvpp provided $base_package.JVpp instance - * - * @throws java.io.IOException in case instance cannot connect to JVPP - */ - public FutureJVpp${plugin_name}Facade(final $base_package.JVppRegistry registry, final $base_package.JVpp jvpp) throws java.io.IOException { - super(jvpp, registry, new java.util.HashMap<>()); - java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null"); - registry.register(jvpp, new FutureJVpp${plugin_name}FacadeCallback(getRequests(), eventRegistry)); - } - +_FUTURE_JVPP_FACADE_EVENT_CALLBACK_TEMPLATE = Template(""" @Override - public $plugin_package.$notification_package.${plugin_name}EventRegistry getEventRegistry() { - return eventRegistry; - } - -$methods -} -''') - -future_jvpp_method_impl_template = Template(''' - @Override - public java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request) { - return send(request); - } -''') - -future_jvpp_dump_method_impl_template = Template(''' - @Override - public java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request) { - return send(request, new $plugin_package.$dto_package.$reply_name()); + public void on$callback_dto($plugin_package.dto.$callback_dto notification) { + if (LOG.isLoggable(java.util.logging.Level.FINE)) { + LOG.fine(String.format("Received $callback_dto event message: %s", notification)); + } + notificationCallback.on$callback_dto(notification); } -''') - - -def generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, future_facade_package, inputfile): - callbacks = [] - for func in func_list: - camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - - if util.is_control_ping(camel_case_name_with_suffix): - # Skip control ping managed by jvpp registry. - continue - if util.is_dump(func['name']) or util.is_request(func['name'], func_list): - continue - - # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). - if util.is_details(camel_case_name_with_suffix): - # Callbacks for detail messages that append replies to a list. - camel_case_method_name = util.underscore_to_camelcase(func['name']) - camel_case_reply_name = util.underscore_to_camelcase_upper(func['name']) - callbacks.append(jvpp_facade_details_callback_method_template.substitute(base_package=base_package, - plugin_package=plugin_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)) - elif util.is_reply(camel_case_name_with_suffix): - request_dto = util.remove_reply_suffix(util.underscore_to_camelcase_upper(func['name'])) - callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, - plugin_package=plugin_package, - dto_package=dto_package, - callback_dto=camel_case_name_with_suffix, - request_dto=request_dto)) - else: - callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package, - dto_package=dto_package, - callback_dto=camel_case_name_with_suffix)) - - jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%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, - methods="".join(callbacks), - future_package=future_facade_package)) - jvpp_file.flush() - jvpp_file.close() +""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py new file mode 100755 index 00000000000..e2b2922ed20 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from string import Template + +from jvpp_model import is_request, is_dump, is_event + + +def generate_java_ifc(work_dir, model, logger): + logger.debug("Generating JVpp interface for %s" % model.json_api_files) + messages = filter(_jvpp_ifc_filter, model.messages) + plugin_package = model.plugin_package + methods = [] + for msg in messages: + if msg.has_fields: + methods.append(_JVPP_IFC_METHOD_TEMPLATE.substitute( + name=msg.java_name_lower, + plugin_package=plugin_package, + type=msg.java_name_upper)) + else: + methods.append(_JVPP_IFC_NO_ARG_METHOD_TEMPLATE.substitute(name=msg.java_name_lower)) + + plugin_name = model.plugin_java_name + jvpp_interface = _JVPP_IFC_TEMPLATE.substitute( + plugin_package=plugin_package, + json_filename=model.json_api_files, + plugin_name=plugin_name, + methods="\n".join(methods) + ) + with open("%s/JVpp%s.java" % (work_dir, plugin_name), "w") as f: + f.write(jvpp_interface) + + +def _jvpp_ifc_filter(msg): + return is_request(msg) or is_dump(msg) or is_event(msg) + + +_JVPP_IFC_METHOD_TEMPLATE = Template( + """ int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException;""") + +_JVPP_IFC_NO_ARG_METHOD_TEMPLATE = Template(""" int $name() throws io.fd.vpp.jvpp.VppInvocationException;""") + +_JVPP_IFC_TEMPLATE = Template("""package $plugin_package; + +/** + * <p>Java representation of plugin's api file. + * <br>It was generated by jvpp_impl_gen.py based on $json_filename. + * <br>(python representation of api file generated by vppapigen) + */ +public interface JVpp${plugin_name} extends io.fd.vpp.jvpp.JVpp { + /** + * Generic dispatch method for sending requests to VPP + * + * @throws io.fd.vpp.jvpp.VppInvocationException if send request had failed + */ + int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException; +$methods +} +""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py index 69eb67e8357..aff8d7fc99c 100644..100755 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,33 +12,45 @@ # 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 util +# from string import Template -jvpp_ifc_template = Template(""" -package $plugin_package; +from jvpp_model import is_request, is_dump, is_event -/** - * <p>Java representation of plugin's api file. - * <br>It was generated by jvpp_impl_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). - */ -public interface JVpp${plugin_name} extends $base_package.JVpp { - /** - * Generic dispatch method for sending requests to VPP - * - * @throws io.fd.vpp.jvpp.VppInvocationException if send request had failed - */ - int send($base_package.$dto_package.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException; +def generate_java_impl(work_dir, model, logger): + logger.debug("Generating JVpp implementation for %s" % model.json_api_files) + messages = filter(_jvpp_impl_filter, model.messages) + plugin_package = model.plugin_package + methods = [] + for msg in messages: + if msg.has_fields: + methods.append(_JVPP_IMPL_METHOD_TEMPLATE.substitute( + name=msg.java_name_lower, + plugin_package=plugin_package, + type=msg.java_name_upper)) + else: + methods.append(_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE.substitute( + name=msg.java_name_lower, + type=msg.java_name_upper)) + + plugin_name = model.plugin_java_name + jvpp_impl = _JVPP_IMPL_TEMPLATE.substitute( + plugin_package=plugin_package, + json_filename=model.json_api_files, + plugin_name=model.plugin_java_name, + plugin_name_underscore=model.plugin_name, + methods="\n".join(methods)) -$methods -} -""") + with open("%s/JVpp%sImpl.java" % (work_dir, plugin_name), "w") as f: + f.write(jvpp_impl) + + +def _jvpp_impl_filter(msg): + return is_request(msg) or is_dump(msg) or is_event(msg) -jvpp_impl_template = Template(""" -package $plugin_package; + +_JVPP_IMPL_TEMPLATE = Template("""package $plugin_package; import java.io.IOException; import java.io.InputStream; @@ -50,14 +62,14 @@ import java.nio.file.attribute.PosixFilePermissions; import java.util.Set; import java.util.logging.Logger; import java.util.logging.Level; -import $base_package.callback.JVppCallback; -import $base_package.VppConnection; -import $base_package.JVppRegistry; +import io.fd.vpp.jvpp.callback.JVppCallback; +import io.fd.vpp.jvpp.VppConnection; +import io.fd.vpp.jvpp.JVppRegistry; /** * <p>Default implementation of JVpp interface. - * <br>It was generated by jvpp_impl_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by jvpp_impl_gen.py based on $json_filename. + * <br>(python representation of api file generated by vppapigen) */ public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugin_name} { @@ -121,7 +133,7 @@ public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugi } @Override - public int send($base_package.$dto_package.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException { + public int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException { return request.send(this); } @@ -129,91 +141,33 @@ public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugi public final int controlPing(final io.fd.vpp.jvpp.dto.ControlPing controlPing) throws io.fd.vpp.jvpp.VppInvocationException { return registry.controlPing(JVpp${plugin_name}Impl.class); } - $methods } """) -method_template = Template(""" int $name($plugin_package.$dto_package.$request request) throws io.fd.vpp.jvpp.VppInvocationException;""") -method_native_template = Template( - """ 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 io.fd.vpp.jvpp.VppInvocationException { - java.util.Objects.requireNonNull(request,"Null request object"); +_JVPP_IMPL_METHOD_TEMPLATE = Template(""" + private static native int ${name}0($plugin_package.dto.$type request); + public final int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException { + java.util.Objects.requireNonNull(request, "Null request object"); connection.checkActive(); - if(LOG.isLoggable(Level.FINE)) { - LOG.fine(String.format("Sending $name event message: %s", request)); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine(String.format("Sending $type event message: %s", request)); } int result=${name}0(request); - if(result<0){ - throw new io.fd.vpp.jvpp.VppInvocationException("${name}",result); + if (result<0){ + throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result); } return result; - } -""") + }""") -no_arg_method_template = Template(""" int $name() throws io.fd.vpp.jvpp.VppInvocationException;""") -no_arg_method_native_template = Template(""" private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException;""") -no_arg_method_impl_template = Template(""" public final int $name() throws io.fd.vpp.jvpp.VppInvocationException { +_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE = Template(""" + private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException; + public final int $name() throws io.fd.vpp.jvpp.VppInvocationException { connection.checkActive(); - LOG.fine("Sending $name event message"); + LOG.fine("Sending $type event message"); int result=${name}0(); if(result<0){ - throw new io.fd.vpp.jvpp.VppInvocationException("${name}",result); + throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result); } return result; - } -""") - - -def generate_jvpp(func_list, base_package, plugin_package, plugin_name_underscore, dto_package, inputfile, logger): - """ Generates JVpp interface and JNI implementation """ - logger.debug("Generating JVpp interface implementation for %s" % inputfile) - plugin_name = util.underscore_to_camelcase_upper(plugin_name_underscore) - - methods = [] - methods_impl = [] - for func in func_list: - 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)) - 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, - plugin_package=plugin_package, - dto_package=dto_package)) - methods_impl.append(method_native_template.substitute(name=camel_case_name, - request=camel_case_name_upper, - plugin_package=plugin_package, - dto_package=dto_package)) - methods_impl.append(method_impl_template.substitute(name=camel_case_name, - request=camel_case_name_upper, - plugin_package=plugin_package, - dto_package=dto_package)) - - 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("JVpp%sImpl.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, - plugin_name_underscore=plugin_name_underscore, - dto_package=dto_package)) - jvpp_file.flush() - jvpp_file.close() + }""") diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py new file mode 100755 index 00000000000..3b09a81cfc4 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py @@ -0,0 +1,467 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2018 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import json +import pprint +from collections import OrderedDict + +BASE_PACKAGE = "io.fd.vpp.jvpp" + + +class ParseException(Exception): + pass + + +class Type(object): + def __init__(self, name, java_name, java_name_fqn, jni_signature, jni_type, jni_accessor, + host_to_net_function, net_to_host_function): + """ + Initializes Type class. + + :param name: name of type as defined in .api file, e.g. u8, u32[] or mac_entry + :param java_name: corresponding java name, e.g. byte, int[] or MacEntry + :param java_name_fqn: fully qualified java name, e.g. io.fd.vpp.jvpp.core.types.MacEntry + :param jni_signature: JNI Type signature, e.g. B, [I or Lio.fd.vpp.jvpp.core.types.MacEntry; + See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures + :param jni_type: JNI reference type, e.g. jbyte jintArray, jobject + See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#reference_types + :param jni_accessor: Java type do by used in Get<type>Field, Set<type>Field and other functions. + See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#accessing_fields_of_objects + :param host_to_net_function: name of function host to net byte order swap function + :param net_to_host_function: name of function net to host byte order swap function + """ + self.name = name + self.java_name = java_name + + # Java generation specific properties, TODO(VPP-1186): move to Java specific subclass + self.java_name_fqn = java_name_fqn + + # JNI generation specific properties, TODO(VPP-1186): move to JNI specific subclass + self.jni_signature = jni_signature + + # Native type, see: + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#primitive_types + # and + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#reference_types + self.jni_type = jni_type + + # Java type do by used in Get<type>Field, Set<type>Field and other functions, see: + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#accessing_fields_of_objects + self.jni_accessor = jni_accessor + + self.host_to_net_function = host_to_net_function + self.net_to_host_function = net_to_host_function + self.is_swap_needed = host_to_net_function and net_to_host_function + + +class SimpleType(Type): + def __init__(self, name, java_name, jni_signature, jni_type, jni_accessor, + host_to_net_function=None, net_to_host_function=None): + super(SimpleType, self).__init__( + name=name, + java_name=java_name, + java_name_fqn=java_name, + jni_signature=jni_signature, + jni_type=jni_type, + jni_accessor=jni_accessor, + host_to_net_function=host_to_net_function, + net_to_host_function=net_to_host_function + ) + self.vpp_name = name + + def get_host_to_net_function(self, host_ref_name, net_ref_name): + return "%s = %s(%s)" % (net_ref_name, self.host_to_net_function, host_ref_name) + + def __str__(self): + return "SimpleType{name:%s, java_name:%s}" % (self.name, self.java_name) + + +# TODO(VPP-1187): add array host to net functions to reduce number of members and simplify JNI generation +class Array(Type): + def __init__(self, base_type): + super(Array, self).__init__( + name=base_type.name + _ARRAY_SUFFIX, + java_name=base_type.java_name + _ARRAY_SUFFIX, + java_name_fqn=base_type.java_name_fqn + _ARRAY_SUFFIX, + jni_signature="[%s" % base_type.jni_signature, + jni_type="%sArray" % base_type.jni_type, + jni_accessor="Object", + host_to_net_function=base_type.host_to_net_function, + net_to_host_function=base_type.net_to_host_function + ) + self.base_type = base_type + + def get_host_to_net_function(self, host_ref_name, net_ref_name): + return self.base_type.get_host_to_net_function(host_ref_name, net_ref_name) + + def __str__(self): + return "Array{name:%s, java_name:%s}" % (self.name, self.java_name) + + +class Class(Type): + def __init__(self, name, crc, fields, definition, plugin_name): + _java_name = _underscore_to_camelcase_upper(name) + + super(Class, self).__init__( + name=name, + java_name=_java_name, + java_name_fqn="io.fd.vpp.jvpp.%s.types.%s" % (plugin_name, _java_name), + jni_signature="Lio/fd/vpp/jvpp/%s/types/%s;" % (plugin_name, _java_name), + jni_type="jobject", + jni_accessor="Object", + host_to_net_function="_host_to_net_%s" % name, + net_to_host_function="_net_to_host_%s" % name + ) + + self.crc = crc + self.fields = fields + self.doc = _message_to_javadoc(definition) + self.java_name_lower = _underscore_to_camelcase_lower(name) + self.vpp_name = "%s%s%s" % (_VPP_TYPE_PREFIX, name, _VPP_TYPE_SUFFIX) + # Fully qualified class name used by FindClass function, see: + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass + self.jni_name = "io/fd/vpp/jvpp/%s/types/%s" % (plugin_name, _java_name) + + def get_host_to_net_function(self, host_ref_name, net_ref_name): + return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name) + + +class Field(object): + def __init__(self, name, field_type, array_len=None, array_len_field=None): + self.name = name + self.java_name = _underscore_to_camelcase_lower(name) + self.type = field_type + self.array_len = array_len + self.array_len_field = array_len_field + + def __str__(self): + return "Field{name:%s, java_name:%s, type:%s}" % (self.name, self.java_name, self.type) + + +class Message(object): + def __init__(self, name, crc, fields, definition): + self.name = name + self.java_name_upper = _underscore_to_camelcase_upper(name) + self.java_name_lower = _underscore_to_camelcase_lower(name) + self.crc = crc[2:] + self.fields = fields + self.has_fields = fields != [] + self.doc = _message_to_javadoc(definition) + + def __str__(self): + return "Message{name:%s, java_name:%s, crc:%s, fields:%s}" % ( + self.name, self.java_name_upper, self.crc, self.fields) + + +class Event(Message): + def __init__(self, name, crc, fields, definition): + super(Event, self).__init__(name, crc, fields, definition) + + +class Request(Message): + def __init__(self, name, reply, crc, fields, definition): + super(Request, self).__init__(name, crc, fields, definition) + self.reply = reply + self.reply_java = _underscore_to_camelcase_upper(reply) + + def __str__(self): + return "Request{name:%s, reply:%s, crc:%s, fields:%s}" % (self.name, self.reply, self.crc, self.fields) + + +class Reply(Message): + def __init__(self, name, request, crc, fields, definition): + super(Reply, self).__init__(name, crc, fields, definition) + self.request = request + self.request_java = _underscore_to_camelcase_upper(request) + + def __str__(self): + return "Reply{name:%s, request:%s, crc:%s, fields:%s}" % (self.name, self.request, self.crc, self.fields) + + +class Dump(Message): + def __init__(self, name, details, crc, fields, definition): + super(Dump, self).__init__(name, crc, fields, definition) + self.details = details + self.reply_java = _underscore_to_camelcase_upper(details) + + def __str__(self): + return "Dump{name:%s, details:%s, crc:%s, fields:%s}" % (self.name, self.details, self.crc, self.fields) + + +class Details(Message): + def __init__(self, name, dump, crc, fields, definition): + super(Details, self).__init__(name, crc, fields, definition) + self.dump = dump + self.request_java = _underscore_to_camelcase_upper(dump) + + def __str__(self): + return "Details{name:%s, dump:%s, crc:%s, fields:%s}" % (self.name, self.dump, self.crc, self.fields) + + +def is_retval(field): + return field.name == u'retval' + + +def is_array(field): + return field.array_len is not None + + +def is_request(msg): + return hasattr(msg, 'reply') + + +def is_reply(msg): + return hasattr(msg, 'request') + + +def is_dump(msg): + return hasattr(msg, 'details') + + +def is_details(msg): + return hasattr(msg, 'dump') + + +def is_event(msg): + return isinstance(msg, Event) + + +def is_control_ping(msg): + return msg.name == u'control_ping' + + +def is_control_ping_reply(msg): + return msg.name == u'control_ping_reply' + + +class JVppModel(object): + def __init__(self, logger, json_api_files, plugin_name): + self.logger = logger + # TODO(VPP-1188): provide json_file_by_definition map to improve javadoc + self.json_api_files = json_api_files + self.plugin_package = BASE_PACKAGE + "." + plugin_name + self.plugin_name = plugin_name + self.plugin_java_name = _underscore_to_camelcase_upper(plugin_name) + self._load_json_files(json_api_files) + self._parse_types() + self._parse_services() + self._parse_messages() + self._validate_messages() + + def _load_json_files(self, json_api_files): + self._enums = [] + self._types = [] + self._messages = [] + self._services = {} + for file_name in json_api_files: + with open(file_name) as f: + j = json.load(f) + self._enums.extend(j['enums']) + self._types.extend(j['types']) + self._messages.extend(j['messages']) + self._services.update(j['services']) + + def _parse_types(self): + # Mapping according to: + # http://docs.oracle.com/javase/7/do+'[]'cs/technotes/guides/jni/spec/types.html + # and + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines + # + # Unsigned types are converted to signed java types that have the same size. + # It is the API user responsibility to interpret them correctly. + + self._types_by_name = OrderedDict({ + 'u8': SimpleType('u8', 'byte', 'B', 'jbyte', 'Byte'), + 'i8': SimpleType('i8', 'byte', 'B', 'jbyte', 'Byte'), + 'u16': SimpleType('u16', 'short', 'S', 'jshort', 'Short', + host_to_net_function='clib_host_to_net_u16', + net_to_host_function='clib_net_to_host_u16'), + 'i16': SimpleType('i16', 'short', 'S', 'jshort', 'Short', + host_to_net_function='clib_host_to_net_i16', + net_to_host_function='clib_net_to_host_i16'), + 'u32': SimpleType('u32', 'int', 'I', 'jint', 'Int', + host_to_net_function='clib_host_to_net_u32', + net_to_host_function='clib_net_to_host_u32'), + 'i32': SimpleType('i32', 'int', 'I', 'jint', 'Int', + host_to_net_function='clib_host_to_net_i32', + net_to_host_function='clib_net_to_host_i32'), + 'u64': SimpleType('u64', 'long', 'J', 'jlong', 'Long', + host_to_net_function='clib_host_to_net_u64', + net_to_host_function='clib_net_to_host_u64'), + 'i64': SimpleType('i64', 'long', 'J', 'jlong', 'Long', + host_to_net_function='clib_host_to_net_i64', + net_to_host_function='clib_net_to_host_i64'), + 'f64': SimpleType('f64', 'double', 'D', 'jdouble', 'Double') + }) + + for n, t in self._types_by_name.items(): + self._types_by_name[n + _ARRAY_SUFFIX] = Array(t) + + for json_type in self._types: + name = json_type[0] + definition = json_type[1:] + _type = self._parse_type(name, definition) + self._types_by_name[name] = _type + self._types_by_name[name + _ARRAY_SUFFIX] = Array(_type) + + self.types = self._types_by_name.values() + + def _parse_type(self, name, definition): + self.logger.debug("Parsing type %s: %s", name, definition) + crc, fields = self._parse_fields(definition) + return Class(name, crc, fields, definition, self.plugin_name) + + def _parse_services(self): + self._dumps_by_details = {} + self._requests_by_reply = {} + for name, service in self._services.iteritems(): + if _is_stream(service): + self._dumps_by_details[service['reply']] = name + else: + self._requests_by_reply[service['reply']] = name + + def _parse_messages(self): + # Preserve ordering from JSON file to make debugging easier. + self._messages_by_name = OrderedDict() + for msg in self._messages: + try: + name = msg[0] + definition = msg[1:] + self._messages_by_name[name] = self._parse_message(name, definition) + except ParseException as e: + self.logger.warning("Failed to parse message %s: %s. Skipping message.", name, e) + + def _parse_message(self, name, definition): + self.logger.debug("Parsing message %s: %s", name, definition) + crc, fields = self._parse_fields(definition) + if name in self._services: + service = self._services[name] + reply = service['reply'] + if _is_stream(service): + return Dump(name, reply, crc, filter(_is_request_field, fields), definition) + if reply: + return Request(name, reply, crc, filter(_is_request_field, fields), definition) + else: + return Event(name, crc, filter(_is_request_field, fields), definition) + elif name in self._requests_by_reply: + return Reply(name, self._requests_by_reply[name], crc, filter(_is_reply_field, fields), definition) + elif name in self._dumps_by_details: + return Details(name, self._dumps_by_details[name], crc, filter(_is_reply_field, fields), definition) + else: + # TODO: some messages like combined_counters are not visible in the services. + # Throw exception instead (requires fixing vppagigen). + return Event(name, crc, filter(_is_request_field, fields), definition) + + def _parse_fields(self, definition): + crc = None + fields = [] + for item in definition: + if type(item) == dict and 'crc' in item: + crc = item['crc'] + else: + fields.append(self._parse_field(item, fields)) + if not crc: + raise ParseException("CRC was not defined for %s" % definition) + return crc, fields + + def _parse_field(self, field, fields): + type_name = _extract_type_name(field[0]) + if type_name in self._types_by_name: + if len(field) > 2: + # Array field + array_len_field = None + if len(field) == 4: + for f in fields: + if f.name == field[3]: + array_len_field = f + if not array_len_field: + raise ParseException("Could not find field %s declared as length of array %s", + field[3], field[1]) + return Field(field[1], self._types_by_name[type_name + _ARRAY_SUFFIX], field[2], array_len_field) + else: + return Field(field[1], self._types_by_name[type_name]) + else: + raise ParseException("Unknown field type %s" % field) + + def _validate_messages(self): + """ + In case if message A is known to be reply for message B, and message B was not correctly parsed, + remove message A from the set of all messages. + """ + to_be_removed = [] + messages = self._messages_by_name + for name, msg in messages.iteritems(): + if (is_request(msg) and msg.reply not in messages) \ + or (is_reply(msg) and msg.request not in messages) \ + or (is_dump(msg) and msg.details not in messages) \ + or (is_details(msg) and msg.dump not in messages): + to_be_removed.append(name) + + for name in to_be_removed: + del messages[name] + + self.messages = self._messages_by_name.values() + +_ARRAY_SUFFIX = '[]' + + +def _underscore_to_camelcase_upper(name): + return name.title().replace("_", "") + + +def _underscore_to_camelcase_lower(name): + name = name.title().replace("_", "") + return name[0].lower() + name[1:] + + +def _message_to_javadoc(message_definition): + """ Converts JSON message definition to javadoc """ + formatted_message = pprint.pformat(message_definition, indent=4, width=120, depth=None) + return " * " + formatted_message.replace("\n", "\n * ") + + +def _is_stream(service): + """ + Checks if service represents stream, e.g.: + "ip_address_dump": { + "reply": "ip_address_details", + "stream": true + } + :param service: JSON definition of service + :return: value assigned to "stream" or None + """ + return "stream" in service + + +def _extract_type_name(name): + if name.startswith(_VPP_TYPE_PREFIX) and name.endswith(_VPP_TYPE_SUFFIX): + return name[len(_VPP_TYPE_PREFIX): - len(_VPP_TYPE_SUFFIX)] + return name + +_VPP_TYPE_PREFIX = "vl_api_" + +_VPP_TYPE_SUFFIX = "_t" + + +def _is_request_field(field): + # Skip fields that are hidden to the jvpp user (handled by JNI layer) + return field.name not in {'_vl_msg_id', 'client_index', 'context'} + + +def _is_reply_field(field): + # Skip fields that are hidden to the jvpp user: + # _vl_msg_id is handled at JNI layer, + # Unlike in the request case, context is visible to allow matching replies with requests at Java layer. + return field.name not in {'_vl_msg_id'} diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py index 2008a42297e..69e870ed33d 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,55 +12,133 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from string import Template -import os +from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request -import callback_gen -import util -from string import Template -notification_registry_template = Template(""" -package $plugin_package.$notification_package; +def generate_notifications(work_dir, model, logger): + """ Generates notification registry interface and implementation """ + logger.debug("Generating Notification interfaces and implementation for %s" % model.json_api_files) + messages = filter(_notification_filter, model.messages) + _generate_global_event_callback(work_dir, model, messages) + _generate_event_registry(work_dir, model, messages) + _generate_event_registry_impl(work_dir, model, messages) + _generate_event_registry_provider(work_dir, model) + + +def _notification_filter(msg): + # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). + # Also skip control ping managed by jvpp registry. + return (not is_control_ping(msg)) and \ + (not is_control_ping_reply(msg)) and \ + (not is_dump(msg)) and \ + (not is_request(msg)) + + +def _generate_event_registry(work_dir, model, messages): + plugin_name = model.plugin_java_name + plugin_package = model.plugin_package + + register_callback_methods = [] + for msg in messages: + name = _callback_name(msg) + fqn_name = _fqn_callback_name(plugin_package, name) + # TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate + # that the registration should be closed + register_callback_methods.append(" java.lang.AutoCloseable register%s(%s callback);" % (name, fqn_name)) + + with open("%s/%sEventRegistry.java" % (work_dir, plugin_name), "w") as f: + f.write(_EVENT_REGISTRY_TEMPLATE.substitute( + plugin_package=plugin_package, + plugin_name=plugin_name, + json_filename=model.json_api_files, + register_callback_methods="\n".join(register_callback_methods) + )) + +_EVENT_REGISTRY_TEMPLATE = Template(""" +package $plugin_package.notification; /** * <p>Registry for notification callbacks defined in ${plugin_name}. - * <br>It was generated by notification_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by notification_gen.py based on $json_filename. */ -public interface ${plugin_name}EventRegistry extends $base_package.$notification_package.EventRegistry { +public interface ${plugin_name}EventRegistry extends io.fd.vpp.jvpp.notification.EventRegistry { - $register_callback_methods +$register_callback_methods @Override void close(); } """) -global_notification_callback_template = Template(""" -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 api file generated by vppapigen). - */ -public interface Global${plugin_name}EventCallback$callbacks { +def _generate_event_registry_impl(work_dir, model, messages): + plugin_name = model.plugin_java_name + plugin_package = model.plugin_package -} + register_callback_methods = [] + handler_methods = [] + for msg in messages: + notification = msg.java_name_upper + callback = "%sCallback" % notification + register_callback_methods.append(_REGISTER_CALLBACK_IMPL_TEMPLATE.substitute( + plugin_package=plugin_package, + notification=notification, + callback=callback + )) + handler_methods.append(_HANDLER_IMPL_TEMPLATE.substitute( + plugin_package=plugin_package, + notification=notification, + callback=callback + )) + + with open("%s/%sEventRegistryImpl.java" % (work_dir, plugin_name), "w") as f: + f.write(_EVENT_REGISTRY_IMPL_TEMPLATE.substitute( + plugin_package=plugin_package, + plugin_name=plugin_name, + json_filename=model.json_api_files, + register_callback_methods="".join(register_callback_methods), + handler_methods="".join(handler_methods) + )) + +_REGISTER_CALLBACK_IMPL_TEMPLATE = Template(""" + public java.lang.AutoCloseable register$callback(final $plugin_package.callback.$callback callback){ + if(null != registeredCallbacks.putIfAbsent($plugin_package.dto.$notification.class, callback)){ + throw new IllegalArgumentException("Callback for " + $plugin_package.dto.$notification.class + + "notification already registered"); + } + return () -> registeredCallbacks.remove($plugin_package.dto.$notification.class); + } +""") + +_HANDLER_IMPL_TEMPLATE = Template(""" + @Override + public void on$notification( + final $plugin_package.dto.$notification notification) { + if (LOG.isLoggable(java.util.logging.Level.FINE)) { + LOG.fine(String.format("Received $notification event message: %s", notification)); + } + final io.fd.vpp.jvpp.callback.JVppCallback jVppCallback = registeredCallbacks.get($plugin_package.dto.$notification.class); + if (null != jVppCallback) { + (($plugin_package.callback.$callback) registeredCallbacks + .get($plugin_package.dto.$notification.class)) + .on$notification(notification); + } + } """) -notification_registry_impl_template = Template(""" -package $plugin_package.$notification_package; +_EVENT_REGISTRY_IMPL_TEMPLATE = Template(""" +package $plugin_package.notification; /** * <p>Notification registry delegating notification processing to registered callbacks. - * <br>It was generated by notification_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>It was generated by notification_gen.py based on $json_filename. */ public final class ${plugin_name}EventRegistryImpl implements ${plugin_name}EventRegistry, Global${plugin_name}EventCallback { // TODO add a special NotificationCallback interface and only allow those to be registered - private final java.util.concurrent.ConcurrentMap<Class<?>, $base_package.$callback_package.JVppCallback> registeredCallbacks = + private final java.util.concurrent.ConcurrentMap<Class<?>, io.fd.vpp.jvpp.callback.JVppCallback> registeredCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); private static java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName()); @@ -81,41 +159,58 @@ public final class ${plugin_name}EventRegistryImpl implements ${plugin_name}Even } """) -register_callback_impl_template = Template(""" - 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($plugin_package.$dto_package.$notification.class); - } -""") -handler_impl_template = Template(""" - @Override - public void on$notification( - final $plugin_package.$dto_package.$notification_reply notification) { - if (LOG.isLoggable(java.util.logging.Level.FINE)) { - LOG.fine(String.format("Received $notification event message: %s", notification)); - } - final $base_package.$callback_package.JVppCallback jVppCallback = registeredCallbacks.get($plugin_package.$dto_package.$notification.class); - if (null != jVppCallback) { - (($plugin_package.$callback_package.$callback) registeredCallbacks - .get($plugin_package.$dto_package.$notification.class)) - .on$notification(notification); - } - } +def _generate_global_event_callback(work_dir, model, messages): + plugin_name = model.plugin_java_name + plugin_package = model.plugin_package + + callbacks = "" + callback_list = [] + for msg in messages: + fqn_name = _fqn_callback_name(plugin_package, _callback_name(msg)) + callback_list.append(fqn_name) + + if callback_list: + callbacks = " extends %s" % ", ".join(callback_list) + + with open("%s/Global%sEventCallback.java" % (work_dir, plugin_name), "w") as f: + f.write(_GLOBAL_EVENT_CALLBACK_TEMPLATE.substitute( + plugin_package=plugin_package, + plugin_name=plugin_name, + json_filename=model.json_api_files, + callbacks=callbacks + )) + +_GLOBAL_EVENT_CALLBACK_TEMPLATE = Template(""" +package $plugin_package.notification; + +/** + * <p>Aggregated callback interface for notifications only. + * <br>It was generated by notification_gen.py based on $json_filename. + */ +public interface Global${plugin_name}EventCallback$callbacks { + +} """) -notification_provider_template = Template(""" -package $plugin_package.$notification_package; + +def _generate_event_registry_provider(work_dir, model): + plugin_name = model.plugin_java_name + with open("%s/%sEventRegistryProvider.java" % (work_dir, plugin_name), "w") as f: + f.write(_EVENT_REGISTRY_PROVIDER_TEMPLATE.substitute( + plugin_package=model.plugin_package, + plugin_name=plugin_name, + json_filename=model.json_api_files + )) + +_EVENT_REGISTRY_PROVIDER_TEMPLATE = Template(""" +package $plugin_package.notification; /** * Provides ${plugin_name}EventRegistry. - * <br>The file was generated by notification_gen.py based on $inputfile - * <br>(python representation of api file generated by vppapigen). + * <br>The file was generated by notification_gen.py based on $json_filename. */ -public interface ${plugin_name}EventRegistryProvider extends $base_package.$notification_package.EventRegistryProvider { +public interface ${plugin_name}EventRegistryProvider extends io.fd.vpp.jvpp.notification.EventRegistryProvider { @Override public ${plugin_name}EventRegistry getEventRegistry(); @@ -123,93 +218,9 @@ public interface ${plugin_name}EventRegistryProvider extends $base_package.$noti """) -def generate_notification_registry(func_list, base_package, plugin_package, plugin_name, notification_package, - callback_package, dto_package, inputfile, logger): - """ Generates notification registry interface and implementation """ - logger.debug("Generating Notification interfaces and implementation for %s" % inputfile) +def _callback_name(msg): + return "%sCallback" % msg.java_name_upper - if not os.path.exists(notification_package): - os.mkdir(notification_package) - - callbacks = [] - register_callback_methods = [] - register_callback_methods_impl = [] - handler_methods = [] - for func in func_list: - camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) - - if util.is_control_ping(camel_case_name_with_suffix): - # Skip control ping managed by jvpp registry. - continue - if util.is_dump(func['name']) or util.is_request(func['name'], func_list): - continue - - # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client). - notification_dto = camel_case_name_with_suffix - callback_ifc = camel_case_name_with_suffix + callback_gen.callback_suffix - 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(plugin_package=plugin_package, - callback_package=callback_package, - dto_package=dto_package, - notification=camel_case_name_with_suffix, - 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, - notification_reply=camel_case_name_with_suffix, - callback=callback_ifc)) - - callback_file = open(os.path.join(notification_package, "%sEventRegistry.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%sEventCallback.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, "%sEventRegistryImpl.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, "%sEventRegistryProvider.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() +def _fqn_callback_name(plugin_package, callback_name): + return "%s.callback.%s" % (plugin_package, callback_name) diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py index c655933ddea..3767b530347 100644..100755 --- a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # -# Copyright (c) 2016 Cisco and/or its affiliates. +# Copyright (c) 2016,2018 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: @@ -12,220 +12,49 @@ # 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 -import jni_gen -import dto_gen +from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields +from jvpp_model import Class + + +def generate_types(work_dir, model, logger): + logger.debug("Generating custom types for %s " % model.json_api_files) + + for t in model.types: + if not isinstance(t, Class): + continue + logger.debug("Generating DTO for type %s", t) + type_class_name = t.java_name + fields = t.fields + type_class = _TYPE_TEMPLATE.substitute( + plugin_package=model.plugin_package, + c_type_name=t.name, + json_filename=model.json_api_files, + json_definition=t.doc, + java_type_name=type_class_name, + fields=generate_fields(fields), + hash_code=generate_hash_code(fields), + equals=generate_equals(type_class_name, fields), + to_string=generate_to_string(type_class_name, fields) + ) + with open("%s/%s.java" % (work_dir, type_class_name), "w") as f: + f.write(type_class) -type_template = Template(""" -package $plugin_package.$type_package; +_TYPE_TEMPLATE = Template(""" +package $plugin_package.types; /** * <p>This class represents $c_type_name type definition. - * <br>It was generated by types_gen.py based on $inputfile preparsed data: + * <br>It was generated by jvpp_types_gen.py based on $json_filename: * <pre> -$docs +$json_definition * </pre> */ public final class $java_type_name { $fields -$methods +$hash_code +$equals +$to_string } """) - -field_template = Template(""" public $type $name;\n""") - - -def generate_type_fields(type_definition): - """ - Generates fields for class representing typeonly definition - :param type_definition: python representation of typeonly definition - :return: string representing class fields - """ - fields = "" - for t in zip(type_definition['types'], type_definition['args']): - field_name = util.underscore_to_camelcase(t[1]) - fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]], - name=field_name) - return fields - -object_struct_setter_template = Template(""" - { - jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); - memset (&(mp->${c_name}), 0, sizeof (mp->${c_name})); - ${struct_initialization} - } -""") - -object_array_struct_setter_template = Template(""" - { - jclass ${field_reference_name}ArrayElementClass = (*env)->FindClass(env, "${class_FQN}"); - if (${field_reference_name}) { - size_t _i; - jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); - ${field_length_check} - for (_i = 0; _i < cnt; _i++) { - jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i); - memset (&(mp->${c_name}[_i]), 0, sizeof (mp->${c_name}[_i])); - ${struct_initialization} - } - } - } -""") - -object_dto_field_setter_template = Template(""" - { - jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); - jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V"); - jobject ${field_reference_name} = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); - ${type_initialization} - (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name}); - (*env)->DeleteLocalRef(env, ${field_reference_name}); - } -""") - -object_array_dto_field_setter_template = Template(""" - { - jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); - jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0); - jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V"); - unsigned int _i; - for (_i = 0; _i < ${field_length}; _i++) { - jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); - ${type_initialization} - (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement); - (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement); - } - (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name}); - (*env)->DeleteLocalRef(env, ${field_reference_name}); - } -""") - - -def generate_struct_initialization(type_def, c_name_prefix, object_name, indent): - struct_initialization = "" - # field identifiers - for t in zip(type_def['types'], type_def['args'], type_def['lengths']): - field_reference_name = "${c_name}" + util.underscore_to_camelcase_upper(t[1]) - field_name = util.underscore_to_camelcase(t[1]) - struct_initialization += jni_gen.jni_request_identifiers_for_type(field_type=t[0], - field_reference_name=field_reference_name, - field_name=field_name, - object_name=object_name) - struct_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=c_name_prefix + t[1], - field_reference_name=field_reference_name, - field_length=t[2][0], - is_variable_len_array=t[2][1]) - return indent + struct_initialization.replace('\n', '\n' + indent) - - -def generate_type_setter(handler_name, type_def, c_name_prefix, object_name, indent): - type_initialization = "" - for t in zip(type_def['types'], type_def['args'], type_def['lengths']): - field_length = t[2][0] - is_variable_len_array = t[2][1] - length_field_type = None - if is_variable_len_array: - length_field_type = type_def['types'][type_def['args'].index(field_length)] - type_initialization += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, - ref_name="${field_reference_name}", - field_type=t[0], c_name=c_name_prefix + t[1], - field_reference_name="${c_name}" + util.underscore_to_camelcase_upper(t[1]), - field_name=util.underscore_to_camelcase(t[1]), - field_length=field_length, - is_variable_len_array=is_variable_len_array, - length_field_type=length_field_type, - object_name=object_name) - return indent + type_initialization.replace('\n', '\n' + indent) - - -def generate_types(types_list, plugin_package, types_package, inputfile, logger): - """ - Generates Java representation of custom types defined in api file. - """ - - if not types_list: - logger.debug("Skipping custom types generation (%s does not define custom types)." % inputfile) - return - - logger.debug("Generating custom types for %s" % inputfile) - - if not os.path.exists(types_package): - os.mkdir(types_package) - - for type in types_list: - c_type_name = type['name'] - java_type_name = util.underscore_to_camelcase_upper(type['name']) - dto_path = os.path.join(types_package, java_type_name + ".java") - - fields = generate_type_fields(type) - - dto_file = open(dto_path, 'w') - dto_file.write(type_template.substitute(plugin_package=plugin_package, - type_package=types_package, - c_type_name=c_type_name, - inputfile=inputfile, - docs=util.api_message_to_javadoc(type), - java_type_name=java_type_name, - fields=fields, - methods=dto_gen.generate_dto_base_methods(java_type_name, type) - )) - - # update type mappings: - # todo fix vpe.api to use type_name instead of vl_api_type_name_t - type_name = "vl_api_" + c_type_name + "_t" - java_fqn = "%s.%s.%s" % (plugin_package, types_package, java_type_name) - util.vpp_2_jni_type_mapping[type_name] = "jobject" - util.vpp_2_jni_type_mapping[type_name + "[]"] = "jobjectArray" - util.jni_2_java_type_mapping[type_name] = java_fqn - util.jni_2_java_type_mapping[type_name + "[]"] = java_fqn + "[]" - jni_name = java_fqn.replace('.', "/") - jni_signature = "L" + jni_name + ";" - util.jni_2_signature_mapping[type_name] = "L" + jni_name + ";" - util.jni_2_signature_mapping[type_name + "[]"] = "[" + jni_signature - util.jni_field_accessors[type_name] = "ObjectField" - util.jni_field_accessors[type_name + "[]"] = "ObjectField" - - jni_gen.struct_setter_templates[type_name] = Template( - object_struct_setter_template.substitute( - c_name="${c_name}", - field_reference_name="${field_reference_name}", - class_FQN=jni_name, - struct_initialization=generate_struct_initialization(type, "${c_name}.", - "${field_reference_name}", ' ' * 4)) - ) - - jni_gen.struct_setter_templates[type_name+ "[]"] = Template( - object_array_struct_setter_template.substitute( - c_name="${c_name}", - field_reference_name="${field_reference_name}", - field_length_check="${field_length_check}", - class_FQN=jni_name, - struct_initialization=generate_struct_initialization(type, "${c_name}[_i].", - "${field_reference_name}ArrayElement", ' ' * 8)) - ) - - jni_gen.dto_field_setter_templates[type_name] = Template( - object_dto_field_setter_template.substitute( - field_reference_name="${field_reference_name}", - field_length="${field_length}", - class_FQN=jni_name, - type_initialization=generate_type_setter(c_type_name, type, "${c_name}.", - "${field_reference_name}", ' ' * 4)) - ) - - jni_gen.dto_field_setter_templates[type_name + "[]"] = Template( - object_array_dto_field_setter_template.substitute( - field_reference_name="${field_reference_name}", - field_length="${field_length}", - class_FQN=jni_name, - type_initialization=generate_type_setter(c_type_name, type, "${c_name}[_i].", - "${field_reference_name}ArrayElement", ' ' * 8)) - ) - - dto_file.flush() - dto_file.close() - diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/util.py b/src/vpp-api/java/jvpp/gen/jvppgen/util.py deleted file mode 100644 index 70ddc8ea8e7..00000000000 --- a/src/vpp-api/java/jvpp/gen/jvppgen/util.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/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_SUFFIX = "reply" -_DETAILS_SUFFIX = "details" -_REPLY_SUFFIXES = (_REPLY_SUFFIX, _DETAILS_SUFFIX) - - -def is_reply(name): - return name.lower().endswith(_REPLY_SUFFIXES) - - -def is_request(msg_name_underscore, all_messages): - """ - Checks if reply message is present in all_messages. - - :param msg_name_underscore name of vpp API message - :param all_messages: sequence of vpp message names - :returns: True if reply for the msg_name_underscore message is defined. - """ - reply_msg = msg_name_underscore + "_" + _REPLY_SUFFIX - return reply_msg in [m['name'] for m in all_messages] - - -def is_details(name): - return name.lower().endswith(_DETAILS_SUFFIX) - - -dump_suffix = "dump" - - -def is_dump(name): - return name.lower().endswith(dump_suffix) - - -def is_retval_field(name): - return name == 'retval' - - -def get_reply_suffix(name): - for reply_suffix in _REPLY_SUFFIXES: - if name.lower().endswith(reply_suffix): - return reply_suffix - -# Mapping according to: -# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html -# -# Unsigned types are converted to signed java types that have the same size. -# It is the API user responsibility to interpret them correctly. -jni_2_java_type_mapping = {'u8': 'byte', - 'u8[]': 'byte[]', - 'i8': 'byte', - 'i8[]': 'byte[]', - 'u16': 'short', - 'u16[]': 'short[]', - 'i16': 'short', - 'i16[]': 'short[]', - 'u32': 'int', - 'u32[]': 'int[]', - 'i32': 'int', - 'i32[]': 'int[]', - 'u64': 'long', - 'u64[]': 'long[]', - 'i64': 'long', - 'i64[]': 'long[]', - 'f64': 'double', - 'f64[]': 'double[]' - } - -vpp_2_jni_type_mapping = {'u8': 'jbyte', - 'u8[]': 'jbyteArray', - 'i8': 'jbyte', - 'i8[]': 'jbyteArray', - 'u16': 'jshort', - 'u16[]': 'jshortArray', - 'i16': 'jshort', - 'i16[]': 'jshortArray', - 'u32': 'jint', - 'u32[]': 'jintArray', - 'i32': 'jint', - 'i32[]': 'jintArray', - 'u64': 'jlong', - 'u64[]': 'jlongArray', - 'i64': 'jlong', - 'i64[]': 'jlongArray', - 'f64': 'jdouble', - 'f64[]': 'jdoubleArray' - } - -# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures -jni_2_signature_mapping = {'u8': 'B', - 'u8[]': '[B', - 'i8': 'B', - 'i8[]': '[B', - 'u16': 'S', - 'u16[]': '[S', - 'i16': 'S', - 'i16[]': '[S', - 'u32': 'I', - 'u32[]': '[I', - 'i32': 'I', - 'i32[]': '[I', - 'u64': 'J', - 'u64[]': '[J', - 'i64': 'J', - 'i64[]': '[J', - 'f64': 'D', - 'f64[]': '[D' - } - -# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines -jni_field_accessors = {'u8': 'ByteField', - 'u8[]': 'ObjectField', - 'i8': 'ByteField', - 'i8[]': 'ObjectField', - 'u16': 'ShortField', - 'u16[]': 'ObjectField', - 'i16': 'ShortField', - 'i16[]': 'ObjectField', - 'u32': 'IntField', - 'u32[]': 'ObjectField', - 'i32': 'IntField', - 'i32[]': 'ObjectField', - 'u64': 'LongField', - 'u64[]': 'ObjectField', - 'i64': 'LongField', - 'i64[]': 'ObjectField', - 'f64': 'DoubleField', - 'f64[]': 'ObjectField' - } - - -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): - if not suffix: - return camel_case_name_with_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 camel_case_name_with_suffix.lower().startswith("controlping") - - -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 * ") - - -def is_array(java_type_as_string): - return java_type_as_string.endswith("[]") |