aboutsummaryrefslogtreecommitdiffstats
path: root/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java
blob: 6e938ae313f9c97d65463837e4138764c88529b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Copyright (c) 2016 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.
 */

package io.fd.vpp.jvpp;

import static java.util.Objects.requireNonNull;

import io.fd.vpp.jvpp.callback.ControlPingCallback;
import io.fd.vpp.jvpp.callback.JVppCallback;
import io.fd.vpp.jvpp.dto.ControlPingReply;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Default implementation of JVppRegistry.
 */
public final class JVppRegistryImpl implements JVppRegistry, ControlPingCallback {

    private static final Logger LOG = Logger.getLogger(JVppRegistryImpl.class.getName());

    private final VppJNIConnection connection;
    // Unguarded concurrent map, no race conditions expected on top of that
    private final Map<String, JVppCallback> pluginRegistry;
    // Guarded by self
    private final Map<Integer, ControlPingCallback> pingCalls;

    public JVppRegistryImpl(final String clientName) throws IOException {
        connection = new VppJNIConnection(clientName);
        connection.connect();
        pluginRegistry = new ConcurrentHashMap<>();
        pingCalls = new HashMap<>();
    }

    public JVppRegistryImpl(final String clientName, final String shmPrefix) throws IOException {
        connection = new VppJNIConnection(clientName, shmPrefix);
        connection.connect();
        pluginRegistry = new ConcurrentHashMap<>();
        pingCalls = new HashMap<>();
    }

    @Override
    public VppConnection getConnection() {
        return connection;
    }

    @Override
    public void register(final JVpp jvpp, final JVppCallback callback) {
        requireNonNull(jvpp, "jvpp should not be null");
        requireNonNull(callback, "Callback should not be null");
        final String name = jvpp.getClass().getName();
        if (pluginRegistry.containsKey(name)) {
            throw new IllegalArgumentException(
                String.format("Callback for plugin %s was already registered", name));
        }
        jvpp.init(this, callback, connection.getConnectionInfo().queueAddress,
            connection.getConnectionInfo().clientIndex);
        pluginRegistry.put(name, callback);
    }

    @Override
    public void unregister(final String name) {
        requireNonNull(name, "Plugin name should not be null");
        final JVppCallback previous = pluginRegistry.remove(name);
        assertPluginWasRegistered(name, previous);
    }

    @Override
    public JVppCallback get(final String name) {
        requireNonNull(name, "Plugin name should not be null");
        JVppCallback value = pluginRegistry.get(name);
        assertPluginWasRegistered(name, value);
        return value;
    }

    private native int controlPing0() throws VppInvocationException;

    @Override
    public int controlPing(final Class<? extends JVpp> clazz) throws VppInvocationException {
        connection.checkActive();
        final String name = clazz.getName();

        final ControlPingCallback callback = (ControlPingCallback) pluginRegistry.get(clazz.getName());
        assertPluginWasRegistered(name, callback);

        synchronized (pingCalls) {
            int context = controlPing0();
            if (context < 0) {
                throw new VppInvocationException("controlPing", context);
            }

            pingCalls.put(context, callback);
            return context;
        }
    }

    @Override
    public void onControlPingReply(final ControlPingReply reply) {
        final ControlPingCallback callback;
        synchronized (pingCalls) {
            callback = pingCalls.remove(reply.context);
            if (callback == null) {
                LOG.log(Level.WARNING, "No callback was registered for reply context=" + reply.context + " Contexts waiting="
                    + pingCalls.keySet());
                return;
            }
        }
        // pass the reply to the callback registered by the ping caller
        callback.onControlPingReply(reply);
    }

    @Override
    public void onError(final VppCallbackException ex) {
        final int ctxId = ex.getCtxId();
        final ControlPingCallback callback;

        synchronized (pingCalls) {
            callback = pingCalls.get(ctxId);
        }
        if (callback == null) {
            LOG.log(Level.WARNING, "No callback was registered for reply id={0} ", ctxId);
            return;
        }
        // pass the error to the callback registered by the ping caller
        callback.onError(ex);
    }

    private static void assertPluginWasRegistered(final String name, final JVppCallback value) {
        if (value == null) {
            throw new IllegalArgumentException(String.format("Callback for plugin %s is not registered", name));
        }
    }

    @Override
    public void close() throws Exception {
        connection.close();
    }
}