#!/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; /** *

Registry for notification callbacks defined in ${plugin_name}. *
It was generated by notification_gen.py based on $json_filename. */ public interface ${plugin_name}EventRegistry extends io.fd.jvpp.notification.EventRegistry { $register_callback_methods @Override void close(); } """) def _generate_event_registry_impl(work_dir, model, messages): plugin_name = model.plugin_java_name plugin_package = model.plugin_package register_callback_methods = [] handler_methods = [] for msg in messages: notification = msg.java_name_upper callback = "%sCallback" % notification register_callback_methods.append(_REGISTER_CALLBACK_IMPL_TEMPLATE.substitute( plugin_package=plugin_package, notification=notification, callback=callback )) handler_methods.append(_HANDLER_IMPL_TEMPLATE.substitute( plugin_package=plugin_package, notification=notification, callback=callback )) with open("%s/%sEventRegistryImpl.java" % (work_dir, plugin_name), "w") as f: f.write(_EVENT_REGISTRY_IMPL_TEMPLATE.substitute( plugin_package=plugin_package, plugin_name=plugin_name, json_filename=model.json_api_files, register_callback_methods="".join(register_callback_methods), handler_methods="".join(handler_methods) )) _REGISTER_CALLBACK_IMPL_TEMPLATE = Template(""" public java.lang.AutoCloseable register$callback(final $plugin_package.callback.$callback callback){ if(null != registeredCallbacks.putIfAbsent($plugin_package.dto.$notification.class, callback)){ throw new IllegalArgumentException("Callback for " + $plugin_package.dto.$notification.class + "notification already registered"); } return () -> registeredCallbacks.remove($plugin_package.dto.$notification.class); } """) _HANDLER_IMPL_TEMPLATE = Template(""" @Override public void on$notification( final $plugin_package.dto.$notification notification) { if (LOG.isLoggable(java.util.logging.Level.FINE)) { LOG.fine(java.lang.String.format("Received $notification event message: %s", notification)); } final io.fd.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; /** *

Notification registry delegating notification processing to registered callbacks. *
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, io.fd.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.jvpp.VppCallbackException ex) { java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName()); LOG.log(java.util.logging.Level.WARNING, java.lang.String.format("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()), ex); } } """) def _generate_global_event_callback(work_dir, model, messages): plugin_name = model.plugin_java_name plugin_package = model.plugin_package callbacks = "" callback_list = [] for msg in messages: fqn_name = _fqn_callback_name(plugin_package, _callback_name(msg)) callback_list.append(fqn_name) if callback_list: callbacks = " extends %s" % ", ".join(callback_list) with open("%s/Global%sEventCallback.java" % (work_dir, plugin_name), "w") as f: f.write(_GLOBAL_EVENT_CALLBACK_TEMPLATE.substitute( plugin_package=plugin_package, plugin_name=plugin_name, json_filename=model.json_api_files, callbacks=callbacks )) _GLOBAL_EVENT_CALLBACK_TEMPLATE = Template(""" package $plugin_package.notification; /** *

Aggregated callback interface for notifications only. *
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. *
The file was generated by notification_gen.py based on $json_filename. */ public interface ${plugin_name}EventRegistryProvider extends io.fd.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)