Move java api to extras/
[vpp.git] / extras / japi / 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         // 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();
105         if (context < 0) {
106             throw new VppInvocationException("controlPing", context);
107         }
108
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);
114             } else {
115                 callback.onControlPingReply(earlyReplyCallback.getReply());
116             }
117         }
118
119         return context;
120     }
121
122     @Override
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));
131                 return;
132             }
133         }
134         // pass the reply to the callback registered by the ping caller
135         callback.onControlPingReply(reply);
136     }
137
138     @Override
139     public void onError(final VppCallbackException ex) {
140         final int ctxId = ex.getCtxId();
141         final ControlPingCallback callback;
142
143         synchronized (pingCalls) {
144             callback = pingCalls.get(ctxId);
145         }
146         if (callback == null) {
147             LOG.log(Level.WARNING, "No callback was registered for reply id={0} ", ctxId);
148             return;
149         }
150         // pass the error to the callback registered by the ping caller
151         callback.onError(ex);
152     }
153
154     private static void assertPluginWasRegistered(final String name, final JVppCallback value) {
155         if (value == null) {
156             throw new IllegalArgumentException(String.format("Callback for plugin %s is not registered", name));
157         }
158     }
159
160     @Override
161     public void close() throws Exception {
162         connection.close();
163     }
164
165     private static class EarlyControlPingReply implements ControlPingCallback {
166
167         private final ControlPingReply reply;
168
169         public EarlyControlPingReply(final ControlPingReply reply) {
170             this.reply = reply;
171         }
172
173         public ControlPingReply getReply() {
174             return reply;
175         }
176
177         @Override
178         public void onError(VppCallbackException ex) {
179             throw new IllegalStateException("Calling onError in EarlyControlPingReply");
180         }
181
182         @Override
183         public void onControlPingReply(ControlPingReply reply) {
184             throw new IllegalStateException("Calling onControlPingReply in EarlyControlPingReply");
185         }
186     }
187 }