2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.vpp.jvpp;
19 import static java.util.Objects.requireNonNull;
21 import io.fd.vpp.jvpp.callback.ControlPingCallback;
22 import io.fd.vpp.jvpp.callback.JVppCallback;
23 import io.fd.vpp.jvpp.dto.ControlPingReply;
24 import java.io.IOException;
25 import java.util.HashMap;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
32 * Default implementation of JVppRegistry.
34 public final class JVppRegistryImpl implements JVppRegistry, ControlPingCallback {
36 private static final Logger LOG = Logger.getLogger(JVppRegistryImpl.class.getName());
38 private final VppJNIConnection connection;
39 // Unguarded concurrent map, no race conditions expected on top of that
40 private final Map<String, JVppCallback> pluginRegistry;
42 private final Map<Integer, ControlPingCallback> pingCalls;
44 public JVppRegistryImpl(final String clientName) throws IOException {
45 connection = new VppJNIConnection(clientName);
47 pluginRegistry = new ConcurrentHashMap<>();
48 pingCalls = new HashMap<>();
51 public JVppRegistryImpl(final String clientName, final String shmPrefix) throws IOException {
52 connection = new VppJNIConnection(clientName, shmPrefix);
54 pluginRegistry = new ConcurrentHashMap<>();
55 pingCalls = new HashMap<>();
59 public VppConnection getConnection() {
64 public void register(final JVpp jvpp, final JVppCallback callback) {
65 requireNonNull(jvpp, "jvpp should not be null");
66 requireNonNull(callback, "Callback should not be null");
67 final String name = jvpp.getClass().getName();
68 if (pluginRegistry.containsKey(name)) {
69 throw new IllegalArgumentException(
70 String.format("Callback for plugin %s was already registered", name));
72 jvpp.init(this, callback, connection.getConnectionInfo().queueAddress,
73 connection.getConnectionInfo().clientIndex);
74 pluginRegistry.put(name, callback);
78 public void unregister(final String name) {
79 requireNonNull(name, "Plugin name should not be null");
80 final JVppCallback previous = pluginRegistry.remove(name);
81 assertPluginWasRegistered(name, previous);
85 public JVppCallback get(final String name) {
86 requireNonNull(name, "Plugin name should not be null");
87 JVppCallback value = pluginRegistry.get(name);
88 assertPluginWasRegistered(name, value);
92 private native int controlPing0() throws VppInvocationException;
95 public int controlPing(final Class<? extends JVpp> clazz) throws VppInvocationException {
96 connection.checkActive();
97 final String name = clazz.getName();
99 final ControlPingCallback callback = (ControlPingCallback) pluginRegistry.get(clazz.getName());
100 assertPluginWasRegistered(name, callback);
102 // controlPing0 is sending function and can go to waiting in case of e. g. full queue
103 // because of that it cant be in same synchronization block as used by reply handler function
104 int context = controlPing0();
106 throw new VppInvocationException("controlPing", context);
109 synchronized (pingCalls) {
110 // if callback is in map it's because reply was already received
111 EarlyControlPingReply earlyReplyCallback = (EarlyControlPingReply) pingCalls.remove(context);
112 if(earlyReplyCallback == null) {
113 pingCalls.put(context, callback);
115 callback.onControlPingReply(earlyReplyCallback.getReply());
123 public void onControlPingReply(final ControlPingReply reply) {
124 final ControlPingCallback callback;
125 synchronized (pingCalls) {
126 callback = pingCalls.remove(reply.context);
127 if (callback == null) {
128 // reply received early, because we don't know callback to call
129 // we wrap the reply and let the sender to call it
130 pingCalls.put(reply.context, new EarlyControlPingReply(reply));
134 // pass the reply to the callback registered by the ping caller
135 callback.onControlPingReply(reply);
139 public void onError(final VppCallbackException ex) {
140 final int ctxId = ex.getCtxId();
141 final ControlPingCallback callback;
143 synchronized (pingCalls) {
144 callback = pingCalls.get(ctxId);
146 if (callback == null) {
147 LOG.log(Level.WARNING, "No callback was registered for reply id={0} ", ctxId);
150 // pass the error to the callback registered by the ping caller
151 callback.onError(ex);
154 private static void assertPluginWasRegistered(final String name, final JVppCallback value) {
156 throw new IllegalArgumentException(String.format("Callback for plugin %s is not registered", name));
161 public void close() throws Exception {
165 private static class EarlyControlPingReply implements ControlPingCallback {
167 private final ControlPingReply reply;
169 public EarlyControlPingReply(final ControlPingReply reply) {
173 public ControlPingReply getReply() {
178 public void onError(VppCallbackException ex) {
179 throw new IllegalStateException("Calling onError in EarlyControlPingReply");
183 public void onControlPingReply(ControlPingReply reply) {
184 throw new IllegalStateException("Calling onControlPingReply in EarlyControlPingReply");