Move java api to extras/

Change-Id: Ibd5cbbdfb22a235442cddaebc9eae9a3c4e35ec9
Signed-off-by: Damjan Marion <damarion@cisco.com>
This commit is contained in:
Damjan Marion
2018-07-30 16:10:14 +02:00
committed by Dave Barach
parent a14c166740
commit cc4a5e8089
126 changed files with 77 additions and 54 deletions

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import argparse
import logging
import os
import sys
from jvppgen.types_gen import generate_types
from jvppgen.enums_gen import generate_enums
from jvppgen.unions_gen import generate_unions
from jvppgen.dto_gen import generate_dtos
from jvppgen.jvpp_ifc_gen import generate_java_ifc
from jvppgen.jvpp_impl_gen import generate_java_impl
from jvppgen.callback_gen import generate_callbacks
from jvppgen.jni_gen import generate_jni
from jvppgen.notification_gen import generate_notifications
from jvppgen.jvpp_future_facade_gen import generate_future_facade
from jvppgen.jvpp_callback_facade_gen import generate_callback_facade
from jvppgen.jvpp_model import JVppModel
def generate_jvpp(root_dir, model, logger):
base_dir = "%s/target/%s" % (root_dir, model.plugin_package.replace(".", "/"))
generate_types(_work_dir(base_dir, "types"), model, logger)
generate_enums(_work_dir(base_dir, "types"), model, logger)
generate_unions(_work_dir(base_dir, "types"), model, logger)
generate_dtos(_work_dir(base_dir, "dto"), model, logger)
generate_java_ifc(_work_dir(base_dir), model, logger)
generate_java_impl(_work_dir(base_dir), model, logger)
generate_callbacks(_work_dir(base_dir, "callback"), model, logger)
generate_jni(root_dir, model, logger)
generate_notifications(_work_dir(base_dir, "notification"), model, logger)
generate_future_facade(_work_dir(base_dir, "future"), model, logger)
generate_callback_facade(_work_dir(base_dir, "callfacade"), model, logger)
def _work_dir(work_dir, sub_dir=None):
if sub_dir:
work_dir = "%s/%s" % (work_dir, sub_dir)
try:
os.makedirs(work_dir)
except OSError:
if not os.path.isdir(work_dir):
raise
return work_dir
def _init_logger():
try:
verbose = int(os.getenv("V", 0))
except:
verbose = 0
log_level = logging.WARNING
if verbose == 1:
log_level = logging.INFO
elif verbose >= 2:
log_level = logging.DEBUG
logging.basicConfig(stream=sys.stdout, level=log_level)
logger = logging.getLogger("JVPP GEN")
logger.setLevel(log_level)
return logger
if __name__ == '__main__':
logger = _init_logger()
argparser = argparse.ArgumentParser(description="VPP Java API generator")
argparser.add_argument('-i', nargs='+', metavar='api_file.json', help="json vpp api file(s)")
argparser.add_argument('--plugin_name')
argparser.add_argument('--root_dir')
args = argparser.parse_args()
logger.info("Generating Java API for %s" % args.i)
logger.debug("plugin_name: %s" % args.plugin_name)
logger.debug("root_dir: %s" % args.root_dir)
model = JVppModel(logger, args.i, args.plugin_name)
generate_jvpp(args.root_dir, model, logger)

View File

@ -0,0 +1,73 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from string import Template
from jvpp_model import is_request, is_dump, is_control_ping, is_control_ping_reply
def generate_callbacks(work_dir, model, logger):
json_api_files = model.json_api_files
logger.debug("Generating Callback interfaces for %s" % json_api_files)
plugin_package = model.plugin_package
callbacks = []
for msg in model.messages:
name = msg.java_name_upper
if is_control_ping(msg) or is_control_ping_reply(msg):
# Skip control_ping managed by jvpp registry.
continue
if is_dump(msg) or is_request(msg):
continue
callbacks.append("%s.callback.%sCallback" % (plugin_package, name))
callback = _CALLBACK_TEMPLATE.substitute(
plugin_package=plugin_package,
json_filename=json_api_files,
name=name)
with open("%s/%sCallback.java" % (work_dir, name), "w") as f:
f.write(callback)
plugin_name = model.plugin_java_name
with open("%s/JVpp%sGlobalCallback.java" % (work_dir, plugin_name), "w") as f:
f.write(_GLOBAL_CALLBACK_TEMPLATE.substitute(
plugin_package=plugin_package,
json_filename=json_api_files,
plugin_name=plugin_name,
callbacks=", ".join(callbacks)
))
_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback;
/**
* <p>Represents callback for plugin's api message.
* <br>It was generated by jvpp_callback_gen.py based on $json_filename.
*/
public interface ${name}Callback extends io.fd.vpp.jvpp.callback.JVppCallback {
void on${name}(${plugin_package}.dto.${name} reply);
}
""")
_GLOBAL_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback;
/**
* <p>Global aggregated callback interface.
* <br>It was generated by jvpp_callback_gen.py based on $json_filename.
*/
public interface JVpp${plugin_name}GlobalCallback extends io.fd.vpp.jvpp.callback.ControlPingCallback, $callbacks {
}
""")

View File

@ -0,0 +1,231 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
from jvpp_model import is_request, is_reply, is_retval, is_dump, is_details, is_event, is_control_ping, \
is_control_ping_reply
def generate_dtos(work_dir, model, logger):
logger.debug("Generating DTOs for %s " % model.json_api_files)
_generate_message_dtos(work_dir, model, logger)
_generate_dump_reply_wrappers(work_dir, model, logger)
def _generate_message_dtos(work_dir, model, logger):
for msg in model.messages:
logger.debug("Generating DTO for message %s", msg)
class_name = msg.java_name_upper
if is_control_ping(msg) or is_control_ping_reply(msg):
# Skip control_ping managed by jvpp registry.
continue
if is_request(msg):
dto = _generate_request_dto(msg, model, base_type="JVppRequest")
elif is_dump(msg):
dto = _generate_request_dto(msg, model, base_type="JVppDump")
elif is_reply(msg) or is_details(msg):
dto = _generate_reply_dto(msg, model)
elif is_event(msg):
dto = _generate_event_dto(msg, model)
else:
logger.warn("Failed to generate DTO for: %s. Message type is not supported." % msg)
continue
with open("%s/%s.java" % (work_dir, class_name), "w") as f:
f.write(dto)
def _generate_request_dto(msg, model, base_type):
msg_java_name_upper = msg.java_name_upper
fields = msg.fields
return _REQUEST_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
json_definition=msg.doc,
class_name=msg_java_name_upper,
base_type=base_type,
fields=generate_fields(fields),
hash_code=generate_hash_code(fields),
equals=generate_equals(msg_java_name_upper, fields),
to_string=generate_to_string(msg_java_name_upper, fields),
send=_generate_send(model, msg))
_REQUEST_TEMPLATE = Template("""
package $plugin_package.dto;
/**
* <p>This class represents request DTO.
* <br>It was generated by dto_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public final class $class_name implements io.fd.vpp.jvpp.dto.$base_type {
$fields
$hash_code
$equals
$to_string
$send
}
""")
def _generate_send(model, msg):
return _SEND_TEMPLATE.substitute(
plugin_package=model.plugin_package,
plugin_name=model.plugin_java_name,
method_name=msg.java_name_lower,
args="this" if msg.has_fields else ""
)
_SEND_TEMPLATE = Template("""
@Override
public int send(final io.fd.vpp.jvpp.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException {
return (($plugin_package.JVpp${plugin_name})jvpp).$method_name($args);
}""")
def _generate_reply_dto(msg, model):
msg_java_name_upper = msg.java_name_upper
# Negative retval is mapped to java exception, so filter it out:
fields = filter(lambda field: not is_retval(field), msg.fields)
return _REPLY_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
json_definition=msg.doc,
class_name=msg_java_name_upper,
request_name=msg.request_java,
fields=generate_fields(fields),
hash_code=generate_hash_code(fields),
equals=generate_equals(msg_java_name_upper, fields),
to_string=generate_to_string(msg_java_name_upper, fields))
_REPLY_TEMPLATE = Template("""
package $plugin_package.dto;
/**
* <p>This class represents reply DTO.
* <br>It was generated by jvpp_dto_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public final class $class_name implements io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_name> {
$fields
$hash_code
$equals
$to_string
}
""")
def _generate_event_dto(msg, model):
msg_java_name_upper = msg.java_name_upper
# Negative retval is mapped to java exception, so filter it out:
fields = filter(lambda field: not is_retval(field), msg.fields)
return _EVENT_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
json_definition=msg.doc,
class_name=msg_java_name_upper,
fields=generate_fields(fields),
hash_code=generate_hash_code(fields),
equals=generate_equals(msg_java_name_upper, fields),
to_string=generate_to_string(msg_java_name_upper, fields))
_EVENT_TEMPLATE = Template("""
package $plugin_package.dto;
/**
* <p>This class represents event DTO.
* <br>It was generated by jvpp_dto_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public final class $class_name {
$fields
$hash_code
$equals
$to_string
}
""")
def _generate_dump_reply_wrappers(work_dir, model, logger):
for msg in model.messages:
if is_details(msg):
logger.debug("Generating ReplyDump DTO for message %s", msg)
details_class = msg.java_name_upper
dto = _REPLY_DUMP_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
json_definition=msg.doc,
details_class=details_class,
details_field=msg.java_name_lower,
dump_class=msg.request_java
)
with open("%s/%sReplyDump.java" % (work_dir, details_class), "w") as f:
f.write(dto)
_REPLY_DUMP_TEMPLATE = Template("""
package $plugin_package.dto;
/**
* <p>This class represents dump reply wrapper.
* <br>It was generated by jvpp_dto_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public final class ${details_class}ReplyDump implements io.fd.vpp.jvpp.dto.JVppReplyDump<${plugin_package}.dto.${dump_class}, ${plugin_package}.dto.${details_class}> {
public java.util.List<${details_class}> ${details_field} = new java.util.ArrayList<>();
@Override
@io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
public int hashCode() {
return java.util.Objects.hash(${details_field});
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final ${details_class}ReplyDump other = (${details_class}ReplyDump) o;
if (!java.util.Objects.equals(this.${details_field}, other.${details_field})) {
return false;
}
return true;
}
@Override
public String toString() {
return "${details_class}ReplyDump{" +
"${details_field}=" + ${details_field} + "}";
}
}
""")

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_model import Enum
def generate_enums(work_dir, model, logger):
logger.debug("Generating enums for %s " % model.json_api_files)
for t in model.types:
if not isinstance(t, Enum):
continue
logger.debug("Generating DTO for enum %s", t)
type_class_name = t.java_name
type_class = _ENUM_TEMPLATE.substitute(
plugin_package=model.plugin_package,
c_type_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
java_enum_name=type_class_name,
constants=_generate_constants(t.constants),
value_type=t.value.type.java_name
)
with open("%s/%s.java" % (work_dir, type_class_name), "w") as f:
f.write(type_class)
_ENUM_TEMPLATE = Template("""
package $plugin_package.types;
/**
* <p>This class represents $c_type_name enum definition.
* <br>It was generated by enums_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public enum $java_enum_name {
$constants;
public final $value_type value;
$java_enum_name(final $value_type value) {
this.value = value;
}
public static $java_enum_name forValue(final $value_type value) {
for ($java_enum_name enumeration : $java_enum_name.values()) {
if (value == enumeration.value) {
return enumeration;
}
}
return null;
}
}
""")
def _generate_constants(constants):
return ",\n".join(_CONSTANT_TEMPLATE.substitute(name=c['name'], value=c['value']) for c in constants)
_CONSTANT_TEMPLATE = Template(""" $name($value)""")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from string import Template
from jni_impl_gen import generate_jni_impl
from jni_msg_handlers_gen import generate_jni_handlers
from jni_type_handlers_gen import generate_type_handlers
from jvpp_model import is_control_ping, is_dump, is_request, is_control_ping_reply
def generate_jni(work_dir, model, logger):
logger.debug("Generating jvpp C for %s" % model.json_api_files)
plugin_name = model.plugin_name
messages = model.messages
with open("%s/jvpp_%s_gen.h" % (work_dir, plugin_name), "w") as f:
f.write(_JVPP_C_TEMPLATE.substitute(
json_filename=model.json_api_files,
class_cache=_generate_class_cache(plugin_name, messages),
api_verification=_generate_api_verification(messages),
type_handlers=generate_type_handlers(model, logger),
jni_implementations=generate_jni_impl(model),
msg_handlers=generate_jni_handlers(model),
handler_registration=_generate_handler_registration(messages)))
_JVPP_C_TEMPLATE = Template("""/**
* This file contains JNI bindings for jvpp Java API.
* It was generated by jvpp_jni_gen.py based on $json_filename.
*/
$class_cache
$api_verification
// Type handlers
$type_handlers
// JNI bindings
$jni_implementations
// Message handlers
$msg_handlers
$handler_registration
""")
def _generate_class_cache(plugin_name, messages):
references = []
for msg in messages:
if is_control_ping(msg) or is_control_ping_reply(msg):
# Skip control_ping managed by jvpp registry.
continue
references.append((
msg.java_name_lower,
'io/fd/vpp/jvpp/%s/dto/%s' % (plugin_name, msg.java_name_upper)
))
references.append(('callbackException', 'io/fd/vpp/jvpp/VppCallbackException'))
return _CLASS_CACHE_TEMPLATE.substitute(
class_references=_generate_class_references(references),
create_references=_generate_create_references(references),
delete_references=_generate_delete_references(references)
)
_CLASS_CACHE_TEMPLATE = Template("""
// JAVA class reference cache
$class_references
static int cache_class_references(JNIEnv* env) {
$create_references
return 0;
}
static void delete_class_references(JNIEnv* env) {
$delete_references
}""")
def _generate_class_references(references):
return "\n".join("jclass %sClass;" % r[0] for r in references)
def _generate_create_references(references):
items = []
for r in references:
items.append(_CREATE_GLOBAL_REF_TEMPLATE.substitute(
ref_name=r[0],
fqn_name=r[1]
))
return "".join(items)
_CREATE_GLOBAL_REF_TEMPLATE = Template("""
${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${fqn_name}"));
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
return JNI_ERR;
}""")
def _generate_delete_references(references):
items = []
for r in references:
items.append(_DELETE_CLASS_INVOCATION_TEMPLATE.substitute(ref_name=r[0]))
return "".join(items)
_DELETE_CLASS_INVOCATION_TEMPLATE = Template("""
if (${ref_name}Class) {
(*env)->DeleteGlobalRef(env, ${ref_name}Class);
}""")
def _generate_api_verification(messages):
items = []
for msg in messages:
items.append("_(%s_%s) \\" % (msg.name, msg.crc))
return _API_VERIFICATION_TEMPLATE.substitute(messages="\n".join(items))
_API_VERIFICATION_TEMPLATE = Template("""
// List of supported API messages used for verification
#define foreach_supported_api_message \\
$messages
""")
def _generate_handler_registration(messages):
"""
Generates msg handler registration for all messages except for dumps and requests.
:param messages: collection of VPP API messages.
"""
handlers = []
for msg in messages:
if is_control_ping(msg) or is_control_ping_reply(msg):
# Skip control_ping managed by jvpp registry.
continue
if is_dump(msg) or is_request(msg):
continue
name = msg.name
crc = msg.crc
handlers.append("_(%s_%s, %s) \\" % (name, crc, name))
return _HANDLER_REGISTRATION_TEMPLATE.substitute(handlers="\n".join(handlers))
_HANDLER_REGISTRATION_TEMPLATE = Template("""
// Registration of message handlers in vlib
#define foreach_api_reply_handler \\
$handlers
""")

View File

@ -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)

View File

@ -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;
}""")

View File

@ -0,0 +1,222 @@
#!/usr/bin/env python2
#
# Copyright (c) 2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from string import Template
from jni_common_gen import generate_j2c_swap, generate_j2c_field_swap, generate_j2c_identifiers, generate_c2j_swap
from jvpp_model import Class, Enum, Union
def generate_type_handlers(model, logger):
"""
Generates host-to-net and net-to-host functions for all custom types defined in the VPP API
:param model: meta-model of VPP API used for jVPP generation.
:param logger: jVPP logger
"""
type_handlers = []
for t in model.types:
#TODO(VPP-1186): move the logic to JNI generators
if isinstance(t, Class):
_generate_class(model, t, type_handlers)
elif isinstance(t, Enum):
_generate_enum(model, t, type_handlers)
elif isinstance(t, Union):
_generate_union(model, t, type_handlers)
else:
logger.debug("Skipping custom JNI type handler generation for %s", t)
return "\n".join(type_handlers)
def _generate_class(model, t, type_handlers):
ref_name = t.java_name_lower
type_handlers.append(_TYPE_HOST_TO_NET_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
type_reference_name=ref_name,
class_FQN=t.jni_name,
jni_identifiers=generate_j2c_identifiers(t, class_ref_name="%sClass" % ref_name, object_ref_name="_host"),
type_swap=generate_j2c_swap(t, struct_ref_name="_net")
))
type_handlers.append(_TYPE_NET_TO_HOST_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
type_reference_name=ref_name,
class_FQN=t.jni_name,
type_swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
))
_TYPE_HOST_TO_NET_TEMPLATE = Template("""
/**
* Host to network byte order conversion for ${c_name} type.
* Generated based on $json_filename:
$json_definition
*/
static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
{
jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
$jni_identifiers
$type_swap
}""")
_TYPE_NET_TO_HOST_TEMPLATE = Template("""
/**
* Network to host byte order conversion for ${c_name} type.
* Generated based on $json_filename:
$json_definition
*/
static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host)
{
jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
$type_swap
}""")
def _generate_enum(model, t, type_handlers):
value_type = t.value.type
type_handlers.append(_ENUM_NET_TO_HOST_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
class_FQN=t.jni_name,
jni_signature=value_type.jni_signature,
jni_type=value_type.jni_type,
jni_accessor=value_type.jni_accessor,
swap=_generate_scalar_host_to_net_swap(t.value)
))
type_handlers.append(_ENUM_HOST_TO_NET_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
class_FQN=t.jni_name,
jni_type=value_type.jni_type,
type_swap=_generate_scalar_net_to_host_swap(t.value)
))
_ENUM_NET_TO_HOST_TEMPLATE = Template("""
/**
* Host to network byte order conversion for ${c_name} enum.
* Generated based on $json_filename:
$json_definition
*/
static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
{
jclass enumClass = (*env)->FindClass(env, "${class_FQN}");
jfieldID valueFieldId = (*env)->GetStaticFieldID(env, enumClass, "value", "${jni_signature}");
${jni_type} value = (*env)->GetStatic${jni_accessor}Field(env, enumClass, valueFieldId);
${swap};
}""")
_ENUM_HOST_TO_NET_TEMPLATE = Template("""
/**
* Network to host byte order conversion for ${c_name} type.
* Generated based on $json_filename:
$json_definition
*/
static inline ${jni_type} _net_to_host_${c_name}(vl_api_${c_name}_t _net)
{
return (${jni_type}) $type_swap
}""")
def _generate_scalar_host_to_net_swap(field):
field_type = field.type
if field_type.is_swap_needed:
return field_type.get_host_to_net_function(field.java_name, "*_net")
else:
return "*_net = %s" % field.java_name
def _generate_scalar_net_to_host_swap(field):
field_type = field.type
if field_type.is_swap_needed:
return "%s((%s) _net);" % (field_type.net_to_host_function, field_type.name)
else:
return "_net"
def _generate_union(model, t, type_handlers):
type_handlers.append(_generate_union_host_to_net(model, t))
type_handlers.append(_generate_union_net_to_host(model, t))
def _generate_union_host_to_net(model, t):
swap = []
for i, field in enumerate(t.fields):
field_type = field.type
swap.append(_UNION_FIELD_HOST_TO_NET_TEMPLATE.substitute(
field_index=i,
java_name=field.java_name,
jni_signature=field_type.jni_signature,
jni_type=field_type.jni_type,
jni_accessor=field_type.jni_accessor,
swap=generate_j2c_field_swap(field, struct_ref_name="_net")
))
return _UNION_HOST_TO_NET_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
class_FQN=t.jni_name,
swap="".join(swap)
)
_UNION_FIELD_HOST_TO_NET_TEMPLATE = Template("""
if (_activeMember == ${field_index}) {
jfieldID fieldId = (*env)->GetFieldID(env, _class, "${java_name}", "${jni_signature}");
${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, _host, fieldId);
${swap}
}""")
_UNION_HOST_TO_NET_TEMPLATE = Template("""
/**
* Host to network byte order conversion for ${c_name} union.
* Generated based on $json_filename:
$json_definition
*/
static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
{
jclass _class = (*env)->FindClass(env, "${class_FQN}");
jfieldID _activeMemberFieldId = (*env)->GetFieldID(env, _class, "_activeMember", "I");
jint _activeMember = (*env)->GetIntField(env, _host, _activeMemberFieldId);
$swap
}""")
def _generate_union_net_to_host(model, t):
return _UNION_NET_TO_HOST_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
type_reference_name=t.java_name_lower,
class_FQN=t.jni_name,
swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
)
_UNION_NET_TO_HOST_TEMPLATE = Template("""
/**
* Network to host byte order conversion for ${c_name} union.
* Generated based on $json_filename:
$json_definition
*/
static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host)
{
jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
$swap
}""")

View File

@ -0,0 +1,289 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_model import is_control_ping, is_dump, is_request, is_event, is_control_ping_reply
def generate_callback_facade(work_dir, model, logger):
""" Generates callback facade """
logger.debug("Generating JVpp callback facade for %s" % model.json_api_files)
_generate_ifc(work_dir, model),
_generate_impl(work_dir, model)
_generate_callback(work_dir, model)
def _generate_ifc(work_dir, model):
with open("%s/CallbackJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f:
f.write(_IFC_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
plugin_name=model.plugin_java_name,
methods=_generate_ifc_methods(model)
))
_IFC_TEMPLATE = Template("""
package $plugin_package.callfacade;
/**
* <p>Callback Java API representation of $plugin_package plugin.
* <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
*/
public interface CallbackJVpp${plugin_name} extends io.fd.vpp.jvpp.notification.EventRegistryProvider, java.lang.AutoCloseable {
// TODO add send
$methods
}
""")
def _generate_ifc_methods(model):
plugin_package = model.plugin_package
methods = []
for msg in model.messages:
if is_control_ping(msg):
# Skip control ping managed by jvpp registry.
continue
if not (is_dump(msg) or is_request(msg)):
# Skip replies and messages that do not not have replies (e.g events/counters).
continue
template = _IFC_NO_ARG_METHOD_TEMPLATE
if msg.has_fields:
template = _IFC_METHOD_TEMPLATE
methods.append(template.substitute(
name=msg.java_name_lower,
plugin_package=plugin_package,
request=msg.java_name_upper,
reply=msg.reply_java
))
return "\n".join(methods)
_IFC_NO_ARG_METHOD_TEMPLATE = Template(
""" void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""")
_IFC_METHOD_TEMPLATE = Template(
""" void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""")
def _generate_impl(work_dir, model):
with open("%s/CallbackJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f:
f.write(_IMPL_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
plugin_name=model.plugin_java_name,
methods=_generate_impl_methods(model)
))
_IMPL_TEMPLATE = Template("""
package $plugin_package.callfacade;
/**
* <p>Default implementation of Callback${plugin_name}JVpp interface.
* <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
*/
public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plugin_name} {
private final $plugin_package.JVpp${plugin_name} jvpp;
private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> callbacks;
private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl();
/**
* <p>Create CallbackJVpp${plugin_name}Facade object for provided JVpp instance.
* Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks
* and then connects to provided JVpp instance
*
* @param jvpp provided io.fd.vpp.jvpp.JVpp instance
*
* @throws java.io.IOException in case instance cannot connect to JVPP
*/
public CallbackJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException {
this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null");
this.callbacks = new java.util.HashMap<>();
java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
registry.register(jvpp, new CallbackJVpp${plugin_name}FacadeCallback(this.callbacks, eventRegistry));
}
@Override
public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() {
return eventRegistry;
}
@Override
public void close() throws Exception {
jvpp.close();
}
// TODO add send()
$methods
}
""")
def _generate_impl_methods(model):
plugin_package = model.plugin_package
methods = []
for msg in model.messages:
if is_control_ping(msg):
# Skip control ping managed by jvpp registry.
continue
if not (is_dump(msg) or is_request(msg)):
# Skip replies and messages that do not not have replies (e.g events/counters).
continue
template = _IMPL_NO_ARG_METHOD_TEMPLATE
if msg.has_fields:
template = _IMPL_METHOD_TEMPLATE
methods.append(template.substitute(
name=msg.java_name_lower,
plugin_package=plugin_package,
request=msg.java_name_upper,
reply=msg.reply_java
))
return "\n".join(methods)
_IMPL_NO_ARG_METHOD_TEMPLATE = Template(
""" public final void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException {
synchronized (callbacks) {
callbacks.put(jvpp.$name(), callback);
}
}
""")
_IMPL_METHOD_TEMPLATE = Template(""" public final void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException {
synchronized (callbacks) {
callbacks.put(jvpp.$name(request), callback);
}
}
""")
def _generate_callback(work_dir, model):
with open("%s/CallbackJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f:
f.write(_CALLBACK_TEMPLATE.substitute(
plugin_package=model.plugin_package,
json_filename=model.json_api_files,
plugin_name=model.plugin_java_name,
methods=_generate_callback_methods(model)
))
_CALLBACK_TEMPLATE = Template("""
package $plugin_package.callfacade;
/**
* <p>Implementation of JVppGlobalCallback interface for Java Callback API.
* <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
*/
public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback {
private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requests;
private final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback;
private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVpp${plugin_name}FacadeCallback.class.getName());
public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requestMap,
final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback) {
this.requests = requestMap;
this.eventCallback = eventCallback;
}
@Override
public void onError(io.fd.vpp.jvpp.VppCallbackException reply) {
io.fd.vpp.jvpp.callback.JVppCallback failedCall;
synchronized(requests) {
failedCall = requests.remove(reply.getCtxId());
}
if(failedCall != null) {
try {
failedCall.onError(reply);
} catch(RuntimeException ex) {
ex.addSuppressed(reply);
LOG.log(java.util.logging.Level.WARNING, String.format("Callback: %s failed while handling exception: %s", failedCall, reply), ex);
}
}
}
@Override
@SuppressWarnings("unchecked")
public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) {
io.fd.vpp.jvpp.callback.ControlPingCallback callback;
final int replyId = reply.context;
synchronized(requests) {
callback = (io.fd.vpp.jvpp.callback.ControlPingCallback) requests.remove(replyId);
}
if(callback != null) {
callback.onControlPingReply(reply);
}
}
$methods
}
""")
def _generate_callback_methods(model):
plugin_package = model.plugin_package
methods = []
for msg in model.messages:
if is_dump(msg) or is_request(msg):
continue
if is_control_ping_reply(msg):
# Skip control ping managed by jvpp registry.
continue
# Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
template = _CALLBACK_METHOD_TEMPLATE
if is_event(msg):
template = _CALLBACK_EVENT_METHOD_TEMPLATE
msg_name = msg.java_name_upper
methods.append(template.substitute(
message=msg_name,
callback="%sCallback" % msg_name,
plugin_package=plugin_package
))
return "\n".join(methods)
_CALLBACK_METHOD_TEMPLATE = Template("""
@Override
@SuppressWarnings("unchecked")
public void on${message}(final $plugin_package.dto.${message} reply) {
$plugin_package.callback.$callback callback;
final int replyId = reply.context;
if (LOG.isLoggable(java.util.logging.Level.FINE)) {
LOG.fine(String.format("Received ${message} event message: %s", reply));
}
synchronized(requests) {
callback = ($plugin_package.callback.$callback) requests.remove(replyId);
}
if(callback != null) {
callback.on${message}(reply);
}
}
""")
_CALLBACK_EVENT_METHOD_TEMPLATE = Template("""
@Override
@SuppressWarnings("unchecked")
public void on${message}($plugin_package.dto.${message} notification) {
if (LOG.isLoggable(java.util.logging.Level.FINE)) {
LOG.fine(String.format("Received ${message} event message: %s", notification));
}
eventCallback.on${message}(notification);
}
""")

View File

@ -0,0 +1,114 @@
#!/usr/bin/env python2
#
# Copyright (c) 2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_model import is_array
def generate_fields(fields, access_modifier="public"):
return "\n".join(_FIELD_TEMPLATE
.substitute(access_modifier=access_modifier, type=f.type.java_name_fqn, name=f.java_name)
for f in fields)
_FIELD_TEMPLATE = Template(""" ${access_modifier} ${type} ${name};""")
def generate_hash_code(fields):
if len(fields) == 1 and is_array(fields[0]):
return _HASH_CODE_SINGLE_ARRAY_TEMPLATE.substitute(array_field=fields[0].java_name)
return _HASH_CODE_TEMPLATE.substitute(fields=", ".join(f.java_name for f in fields))
_HASH_CODE_TEMPLATE = Template("""
@Override
@io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
public int hashCode() {
return java.util.Objects.hash($fields);
}""")
_HASH_CODE_SINGLE_ARRAY_TEMPLATE = Template("""
@Override
@io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
public int hashCode() {
return java.util.Arrays.hashCode($array_field);
}""")
def generate_equals(class_name, fields):
comparisons = []
for f in fields:
if is_array(f):
comparisons.append(_EQUALS_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name))
else:
comparisons.append(_EQUALS_FIELD_TEMPLATE.substitute(field_name=f.java_name))
if comparisons:
comparisons.insert(0, _EQUALS_OTHER_TEMPLATE.substitute(cls_name=class_name))
return _EQUALS_TEMPLATE.substitute(comparisons="\n".join(comparisons))
_EQUALS_TEMPLATE = Template("""
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
$comparisons
return true;
}""")
_EQUALS_OTHER_TEMPLATE = Template("""
final $cls_name other = ($cls_name) o;
""")
_EQUALS_FIELD_TEMPLATE = Template(""" if (!java.util.Objects.equals(this.$field_name, other.$field_name)) {
return false;
}""")
_EQUALS_ARRAY_FIELD_TEMPLATE = Template(""" if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) {
return false;
}""")
def generate_to_string(class_name, fields):
to_string = []
for f in fields:
if is_array(f):
to_string.append(_TO_STRING_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name))
else:
to_string.append(_TO_STRING_FIELD_TEMPLATE.substitute(field_name=f.java_name))
to_string_fields = " \"}\";"
if to_string:
to_string_fields = " + \", \" +\n".join(to_string) + " + \"}\";"
return _TO_STRING_TEMPLATE.substitute(
class_name=class_name,
to_string_fields=to_string_fields
)
_TO_STRING_TEMPLATE = Template("""
@Override
public String toString() {
return "$class_name{" +
$to_string_fields
}""")
_TO_STRING_FIELD_TEMPLATE = Template(""" \"$field_name=\" + $field_name""")
_TO_STRING_ARRAY_FIELD_TEMPLATE = Template(
""" \"$field_name=\" + java.util.Arrays.toString($field_name)""")

File diff suppressed because it is too large Load Diff

View File

@ -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
}
""")

View File

@ -0,0 +1,173 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from string import Template
from jvpp_model import is_request, is_dump, is_event
def generate_java_impl(work_dir, model, logger):
logger.debug("Generating JVpp implementation for %s" % model.json_api_files)
messages = filter(_jvpp_impl_filter, model.messages)
plugin_package = model.plugin_package
methods = []
for msg in messages:
if msg.has_fields:
methods.append(_JVPP_IMPL_METHOD_TEMPLATE.substitute(
name=msg.java_name_lower,
plugin_package=plugin_package,
type=msg.java_name_upper))
else:
methods.append(_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE.substitute(
name=msg.java_name_lower,
type=msg.java_name_upper))
plugin_name = model.plugin_java_name
jvpp_impl = _JVPP_IMPL_TEMPLATE.substitute(
plugin_package=plugin_package,
json_filename=model.json_api_files,
plugin_name=model.plugin_java_name,
plugin_name_underscore=model.plugin_name,
methods="\n".join(methods))
with open("%s/JVpp%sImpl.java" % (work_dir, plugin_name), "w") as f:
f.write(jvpp_impl)
def _jvpp_impl_filter(msg):
return is_request(msg) or is_dump(msg) or is_event(msg)
_JVPP_IMPL_TEMPLATE = Template("""package $plugin_package;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
import java.util.logging.Logger;
import java.util.logging.Level;
import io.fd.vpp.jvpp.callback.JVppCallback;
import io.fd.vpp.jvpp.VppConnection;
import io.fd.vpp.jvpp.JVppRegistry;
/**
* <p>Default implementation of JVpp interface.
* <br>It was generated by jvpp_impl_gen.py based on $json_filename.
* <br>(python representation of api file generated by vppapigen)
*/
public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugin_name} {
private final static Logger LOG = Logger.getLogger(JVpp${plugin_name}Impl.class.getName());
private static final String LIBNAME = "libjvpp_${plugin_name_underscore}.so";
// FIXME using NativeLibraryLoader makes load fail could not find (WantInterfaceEventsReply).
static {
try {
loadLibrary();
} catch (Exception e) {
LOG.severe("Can't find jvpp jni library: " + LIBNAME);
throw new ExceptionInInitializerError(e);
}
}
private static void loadStream(final InputStream is) throws IOException {
final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
try {
Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
try {
Runtime.getRuntime().load(p.toString());
} catch (UnsatisfiedLinkError e) {
throw new IOException("Failed to load library " + p, e);
}
} finally {
try {
Files.deleteIfExists(p);
} catch (IOException e) {
}
}
}
private static void loadLibrary() throws IOException {
try (final InputStream is = JVpp${plugin_name}Impl.class.getResourceAsStream('/' + LIBNAME)) {
if (is == null) {
throw new IOException("Failed to open library resource " + LIBNAME);
}
loadStream(is);
}
}
private VppConnection connection;
private JVppRegistry registry;
private static native void init0(final JVppCallback callback, final long queueAddress, final int clientIndex);
@Override
public void init(final JVppRegistry registry, final JVppCallback callback, final long queueAddress, final int clientIndex) {
this.registry = java.util.Objects.requireNonNull(registry, "registry should not be null");
this.connection = java.util.Objects.requireNonNull(registry.getConnection(), "connection should not be null");
connection.checkActive();
init0(callback, queueAddress, clientIndex);
}
private static native void close0();
@Override
public void close() {
close0();
}
@Override
public int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException {
return request.send(this);
}
@Override
public final int controlPing(final io.fd.vpp.jvpp.dto.ControlPing controlPing) throws io.fd.vpp.jvpp.VppInvocationException {
return registry.controlPing(JVpp${plugin_name}Impl.class);
}
$methods
}
""")
_JVPP_IMPL_METHOD_TEMPLATE = Template("""
private static native int ${name}0($plugin_package.dto.$type request);
public final int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException {
java.util.Objects.requireNonNull(request, "Null request object");
connection.checkActive();
if (LOG.isLoggable(Level.FINE)) {
LOG.fine(String.format("Sending $type event message: %s", request));
}
int result=${name}0(request);
if (result<0){
throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result);
}
return result;
}""")
_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE = Template("""
private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException;
public final int $name() throws io.fd.vpp.jvpp.VppInvocationException {
connection.checkActive();
LOG.fine("Sending $type event message");
int result=${name}0();
if(result<0){
throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result);
}
return result;
}""")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,226 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request
def generate_notifications(work_dir, model, logger):
""" Generates notification registry interface and implementation """
logger.debug("Generating Notification interfaces and implementation for %s" % model.json_api_files)
messages = filter(_notification_filter, model.messages)
_generate_global_event_callback(work_dir, model, messages)
_generate_event_registry(work_dir, model, messages)
_generate_event_registry_impl(work_dir, model, messages)
_generate_event_registry_provider(work_dir, model)
def _notification_filter(msg):
# Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
# Also skip control ping managed by jvpp registry.
return (not is_control_ping(msg)) and \
(not is_control_ping_reply(msg)) and \
(not is_dump(msg)) and \
(not is_request(msg))
def _generate_event_registry(work_dir, model, messages):
plugin_name = model.plugin_java_name
plugin_package = model.plugin_package
register_callback_methods = []
for msg in messages:
name = _callback_name(msg)
fqn_name = _fqn_callback_name(plugin_package, name)
# TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate
# that the registration should be closed
register_callback_methods.append(" java.lang.AutoCloseable register%s(%s callback);" % (name, fqn_name))
with open("%s/%sEventRegistry.java" % (work_dir, plugin_name), "w") as f:
f.write(_EVENT_REGISTRY_TEMPLATE.substitute(
plugin_package=plugin_package,
plugin_name=plugin_name,
json_filename=model.json_api_files,
register_callback_methods="\n".join(register_callback_methods)
))
_EVENT_REGISTRY_TEMPLATE = Template("""
package $plugin_package.notification;
/**
* <p>Registry for notification callbacks defined in ${plugin_name}.
* <br>It was generated by notification_gen.py based on $json_filename.
*/
public interface ${plugin_name}EventRegistry extends io.fd.vpp.jvpp.notification.EventRegistry {
$register_callback_methods
@Override
void close();
}
""")
def _generate_event_registry_impl(work_dir, model, messages):
plugin_name = model.plugin_java_name
plugin_package = model.plugin_package
register_callback_methods = []
handler_methods = []
for msg in messages:
notification = msg.java_name_upper
callback = "%sCallback" % notification
register_callback_methods.append(_REGISTER_CALLBACK_IMPL_TEMPLATE.substitute(
plugin_package=plugin_package,
notification=notification,
callback=callback
))
handler_methods.append(_HANDLER_IMPL_TEMPLATE.substitute(
plugin_package=plugin_package,
notification=notification,
callback=callback
))
with open("%s/%sEventRegistryImpl.java" % (work_dir, plugin_name), "w") as f:
f.write(_EVENT_REGISTRY_IMPL_TEMPLATE.substitute(
plugin_package=plugin_package,
plugin_name=plugin_name,
json_filename=model.json_api_files,
register_callback_methods="".join(register_callback_methods),
handler_methods="".join(handler_methods)
))
_REGISTER_CALLBACK_IMPL_TEMPLATE = Template("""
public java.lang.AutoCloseable register$callback(final $plugin_package.callback.$callback callback){
if(null != registeredCallbacks.putIfAbsent($plugin_package.dto.$notification.class, callback)){
throw new IllegalArgumentException("Callback for " + $plugin_package.dto.$notification.class +
"notification already registered");
}
return () -> registeredCallbacks.remove($plugin_package.dto.$notification.class);
}
""")
_HANDLER_IMPL_TEMPLATE = Template("""
@Override
public void on$notification(
final $plugin_package.dto.$notification notification) {
if (LOG.isLoggable(java.util.logging.Level.FINE)) {
LOG.fine(String.format("Received $notification event message: %s", notification));
}
final io.fd.vpp.jvpp.callback.JVppCallback jVppCallback = registeredCallbacks.get($plugin_package.dto.$notification.class);
if (null != jVppCallback) {
(($plugin_package.callback.$callback) registeredCallbacks
.get($plugin_package.dto.$notification.class))
.on$notification(notification);
}
}
""")
_EVENT_REGISTRY_IMPL_TEMPLATE = Template("""
package $plugin_package.notification;
/**
* <p>Notification registry delegating notification processing to registered callbacks.
* <br>It was generated by notification_gen.py based on $json_filename.
*/
public final class ${plugin_name}EventRegistryImpl implements ${plugin_name}EventRegistry, Global${plugin_name}EventCallback {
// TODO add a special NotificationCallback interface and only allow those to be registered
private final java.util.concurrent.ConcurrentMap<Class<?>, io.fd.vpp.jvpp.callback.JVppCallback> registeredCallbacks =
new java.util.concurrent.ConcurrentHashMap<>();
private static java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName());
$register_callback_methods
$handler_methods
@Override
public void close() {
registeredCallbacks.clear();
}
@Override
public void onError(io.fd.vpp.jvpp.VppCallbackException ex) {
java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName());
LOG.log(java.util.logging.Level.WARNING, String.format("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(),
ex.getCtxId(), ex.getErrorCode()), ex);
}
}
""")
def _generate_global_event_callback(work_dir, model, messages):
plugin_name = model.plugin_java_name
plugin_package = model.plugin_package
callbacks = ""
callback_list = []
for msg in messages:
fqn_name = _fqn_callback_name(plugin_package, _callback_name(msg))
callback_list.append(fqn_name)
if callback_list:
callbacks = " extends %s" % ", ".join(callback_list)
with open("%s/Global%sEventCallback.java" % (work_dir, plugin_name), "w") as f:
f.write(_GLOBAL_EVENT_CALLBACK_TEMPLATE.substitute(
plugin_package=plugin_package,
plugin_name=plugin_name,
json_filename=model.json_api_files,
callbacks=callbacks
))
_GLOBAL_EVENT_CALLBACK_TEMPLATE = Template("""
package $plugin_package.notification;
/**
* <p>Aggregated callback interface for notifications only.
* <br>It was generated by notification_gen.py based on $json_filename.
*/
public interface Global${plugin_name}EventCallback$callbacks {
}
""")
def _generate_event_registry_provider(work_dir, model):
plugin_name = model.plugin_java_name
with open("%s/%sEventRegistryProvider.java" % (work_dir, plugin_name), "w") as f:
f.write(_EVENT_REGISTRY_PROVIDER_TEMPLATE.substitute(
plugin_package=model.plugin_package,
plugin_name=plugin_name,
json_filename=model.json_api_files
))
_EVENT_REGISTRY_PROVIDER_TEMPLATE = Template("""
package $plugin_package.notification;
/**
* Provides ${plugin_name}EventRegistry.
* <br>The file was generated by notification_gen.py based on $json_filename.
*/
public interface ${plugin_name}EventRegistryProvider extends io.fd.vpp.jvpp.notification.EventRegistryProvider {
@Override
public ${plugin_name}EventRegistry getEventRegistry();
}
""")
def _callback_name(msg):
return "%sCallback" % msg.java_name_upper
def _fqn_callback_name(plugin_package, callback_name):
return "%s.callback.%s" % (plugin_package, callback_name)

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
from jvpp_model import Class
def generate_types(work_dir, model, logger):
logger.debug("Generating custom types for %s " % model.json_api_files)
for t in model.types:
if not isinstance(t, Class):
continue
logger.debug("Generating DTO for type %s", t)
type_class_name = t.java_name
fields = t.fields
type_class = _TYPE_TEMPLATE.substitute(
plugin_package=model.plugin_package,
c_type_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
java_type_name=type_class_name,
fields=generate_fields(fields),
hash_code=generate_hash_code(fields),
equals=generate_equals(type_class_name, fields),
to_string=generate_to_string(type_class_name, fields)
)
with open("%s/%s.java" % (work_dir, type_class_name), "w") as f:
f.write(type_class)
_TYPE_TEMPLATE = Template("""
package $plugin_package.types;
/**
* <p>This class represents $c_type_name type definition.
* <br>It was generated by jvpp_types_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public final class $java_type_name {
$fields
$hash_code
$equals
$to_string
}
""")

View File

@ -0,0 +1,98 @@
#!/usr/bin/env python2
#
# Copyright (c) 2018 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from string import Template
from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
from jvpp_model import Union
def generate_unions(work_dir, model, logger):
logger.debug("Generating unions for %s " % model.json_api_files)
for t in model.types:
if not isinstance(t, Union):
continue
logger.debug("Generating DTO for union %s", t)
java_union_name = t.java_name
fields = t.fields
type_class = _UNION_TEMPLATE.substitute(
plugin_package=model.plugin_package,
c_type_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
java_union_name=java_union_name,
fields=generate_fields(fields, access_modifier="private"),
constructors=_generate_constructors(java_union_name, fields),
getters=_generate_getters(fields),
hash_code=generate_hash_code(fields),
equals=generate_equals(java_union_name, fields),
to_string=generate_to_string(java_union_name, fields)
)
with open("%s/%s.java" % (work_dir, java_union_name), "w") as f:
f.write(type_class)
_UNION_TEMPLATE = Template("""
package ${plugin_package}.types;
/**
* <p>This class represents ${c_type_name} union definition.
* <br>It was generated by unions_gen.py based on ${json_filename}:
* <pre>
${json_definition}
* </pre>
*/
public class ${java_union_name} {
private final int _activeMember;
${fields}
private ${java_union_name}() {
// Constructor for JNI usage. All members can be read.
_activeMember = -1;
}
${constructors}
${getters}
${hash_code}
${equals}
${to_string}
}
""")
def _generate_constructors(union_name, fields):
return "".join(
_CONSTRUCTOR_TEMPLATE
.substitute(union_name=union_name,
field_type=f.type.java_name_fqn,
field_name=f.java_name,
field_index=i) for i, f in enumerate(fields))
_CONSTRUCTOR_TEMPLATE = Template("""
public ${union_name}(${field_type} ${field_name}) {
this.${field_name} = java.util.Objects.requireNonNull(${field_name}, "${field_name} should not be null");
_activeMember = $field_index;
}""")
def _generate_getters(fields):
return "".join(_GETTER_TEMPLATE.substitute(
type=f.type.java_name_fqn,
getter_name=f.java_name_upper,
field_name=f.java_name
) for f in fields)
_GETTER_TEMPLATE = Template("""
public ${type} get${getter_name}() {
return ${field_name};
}""")