jvpp: make shm_prefix configurable (VPP-591)
[vpp.git] / src / vpp-api / java / jvpp-registry / io / fd / vpp / jvpp / JVppRegistryImpl.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.vpp.jvpp;
18
19 import static java.util.Objects.requireNonNull;
20
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;
26 import java.util.Map;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30
31 /**
32  * Default implementation of JVppRegistry.
33  */
34 public final class JVppRegistryImpl implements JVppRegistry, ControlPingCallback {
35
36     private static final Logger LOG = Logger.getLogger(JVppRegistryImpl.class.getName());
37
38     private final VppJNIConnection connection;
39     // Unguarded concurrent map, no race conditions expected on top of that
40     private final Map<String, JVppCallback> pluginRegistry;
41     // Guarded by self
42     private final Map<Integer, ControlPingCallback> pingCalls;
43
44     public JVppRegistryImpl(final String clientName) throws IOException {
45         connection = new VppJNIConnection(clientName);
46         connection.connect();
47         pluginRegistry = new ConcurrentHashMap<>();
48         pingCalls = new HashMap<>();
49     }
50
51     public JVppRegistryImpl(final String clientName, final String shmPrefix) throws IOException {
52         connection = new VppJNIConnection(clientName, shmPrefix);
53         connection.connect();
54         pluginRegistry = new ConcurrentHashMap<>();
55         pingCalls = new HashMap<>();
56     }
57
58     @Override
59     public VppConnection getConnection() {
60         return connection;
61     }
62
63     @Override
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));
71         }
72         jvpp.init(this, callback, connection.getConnectionInfo().queueAddress,
73             connection.getConnectionInfo().clientIndex);
74         pluginRegistry.put(name, callback);
75     }
76
77     @Override
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);
82     }
83
84     @Override
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);
89         return value;
90     }
91
92     private native int controlPing0() throws VppInvocationException;
93
94     @Override
95     public int controlPing(final Class<? extends JVpp> clazz) throws VppInvocationException {
96         connection.checkActive();
97         final String name = clazz.getName();
98
99         final ControlPingCallback callback = (ControlPingCallback) pluginRegistry.get(clazz.getName());
100         assertPluginWasRegistered(name, callback);
101
102         synchronized (pingCalls) {
103             int context = controlPing0();
104             if (context < 0) {
105                 throw new VppInvocationException("controlPing", context);
106             }
107
108             pingCalls.put(context, callback);
109             return context;
110         }
111     }
112
113     @Override
114     public void onControlPingReply(final ControlPingReply reply) {
115         final ControlPingCallback callback;
116         synchronized (pingCalls) {
117             callback = pingCalls.remove(reply.context);
118             if (callback == null) {
119                 LOG.log(Level.WARNING, "No callback was registered for reply context=" + reply.context + " Contexts waiting="
120                     + pingCalls.keySet());
121                 return;
122             }
123         }
124         // pass the reply to the callback registered by the ping caller
125         callback.onControlPingReply(reply);
126     }
127
128     @Override
129     public void onError(final VppCallbackException ex) {
130         final int ctxId = ex.getCtxId();
131         final ControlPingCallback callback;
132
133         synchronized (pingCalls) {
134             callback = pingCalls.get(ctxId);
135         }
136         if (callback == null) {
137             LOG.log(Level.WARNING, "No callback was registered for reply id={0} ", ctxId);
138             return;
139         }
140         // pass the error to the callback registered by the ping caller
141         callback.onError(ex);
142     }
143
144     private static void assertPluginWasRegistered(final String name, final JVppCallback value) {
145         if (value == null) {
146             throw new IllegalArgumentException(String.format("Callback for plugin %s is not registered", name));
147         }
148     }
149
150     @Override
151     public void close() throws Exception {
152         connection.close();
153     }
154 }