summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/.gitignore1
-rw-r--r--java/CMakeLists.txt6
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/JVppClientStatsImpl.java90
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/JVppStats.java31
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/JVppStatsImpl.java121
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/JVppStatsRegistryImpl.java96
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/VppStatsJNIConnection.java142
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/callback/InterfaceStatisticsDetailsCallback.java25
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/callback/JVppStatsGlobalCallback.java23
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStats.java28
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacade.java61
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacadeCallback.java67
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatistics.java101
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetails.java71
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetailsReplyDump.java54
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDump.java51
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/future/AbstractFutureJVppInvoker.java170
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppInvoker.java48
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStats.java28
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacade.java53
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacadeCallback.java81
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/test/FutureApiTest.java62
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/test/JvppStatsApiTest.java44
-rw-r--r--java/jvpp-stats/io/fd/jvpp/stats/test/Readme.txt9
-rw-r--r--java/jvpp-stats/jvpp_interface_stats.h236
-rw-r--r--java/jvpp-stats/jvpp_stats.c161
-rw-r--r--java/jvpp-stats/jvpp_stats.h44
-rw-r--r--java/jvpp-stats/jvpp_stats_registry.c214
28 files changed, 1807 insertions, 311 deletions
diff --git a/java/.gitignore b/java/.gitignore
index c94b6e4..92c3656 100644
--- a/java/.gitignore
+++ b/java/.gitignore
@@ -20,3 +20,4 @@ hs_err_pid*
/*/*.files
/*/io_fd_jvpp_*Impl.h
/jvpp-registry/io_fd_jvpp_VppJNIConnection.h
+/jvpp-stats/io_fd_jvpp_stats_VppStatsJNIConnection.h
diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt
index 7270aee..a7be1aa 100644
--- a/java/CMakeLists.txt
+++ b/java/CMakeLists.txt
@@ -209,15 +209,19 @@ add_custom_command(TARGET jvpp-stats-classes
)
add_library(jvpp_stats SHARED jvpp-stats/jvpp_stats.c)
+add_library(jvpp_stats_registry SHARED jvpp-stats/jvpp_stats_registry.c)
target_link_libraries(jvpp_stats ${JVPP_LIBS} vppapiclient vppinfra)
+target_link_libraries(jvpp_stats_registry ${JVPP_LIBS})
include_directories(jvpp-stats)
add_dependencies(jvpp_stats jvpp_common jvpp_registry jvpp-stats-classes)
+add_dependencies(jvpp_stats_registry jvpp_common jvpp_registry jvpp-stats-classes)
add_custom_target(jvpp-stats ALL)
-add_dependencies(jvpp-stats jvpp_stats jvpp-stats-classes)
+add_dependencies(jvpp-stats jvpp_stats jvpp_stats_registry jvpp-stats-classes)
add_custom_command(TARGET jvpp-stats
PRE_BUILD
COMMAND cp ${CMAKE_BINARY_DIR}/build-root/lib/libjvpp_stats.so jvpp-stats/target
+ COMMAND cp ${CMAKE_BINARY_DIR}/build-root/lib/libjvpp_stats_registry.so jvpp-stats/target
COMMAND ${Java_JAR_EXECUTABLE} ARGS cf
${CMAKE_CURRENT_BINARY_DIR}/jvpp-stats-${JAPI_LIB_VERSION}.jar
-C jvpp-stats/target .
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/JVppClientStatsImpl.java b/java/jvpp-stats/io/fd/jvpp/stats/JVppClientStatsImpl.java
deleted file mode 100644
index 32d2345..0000000
--- a/java/jvpp-stats/io/fd/jvpp/stats/JVppClientStatsImpl.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech.
- *
- * 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.jvpp.stats;
-
-import static io.fd.jvpp.NativeLibraryLoader.loadLibrary;
-import static java.lang.String.format;
-
-import io.fd.jvpp.stats.dto.InterfaceStatistics;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.LinkedList;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class JVppClientStatsImpl implements AutoCloseable {
-
- private boolean connected;
-
- private static final Logger LOG = Logger.getLogger(JVppClientStatsImpl.class.getName());
-
- static {
- final String libName = "libjvpp_stats.so";
- try {
- loadLibrary(libName, JVppClientStatsImpl.class);
- } catch (IOException e) {
- LOG.log(Level.SEVERE, format("Can't find vpp jni library: %s", libName), e);
- throw new ExceptionInInitializerError(e);
- }
- }
-
- private static native InterfaceStatistics[] interfaceStatisticsDump();
- private static native int statSegmentConnect();
- private static native void statSegmentDisconnect();
-
- public synchronized List<InterfaceStatistics> getInterfaceStatistics() {
- if (!this.connected) {
- LOG.severe("Unable to dump statistics. Client isn't connected. Try reconnecting.");
- return null;
- }
- InterfaceStatistics[] statDump = interfaceStatisticsDump();
- List<InterfaceStatistics> statistics = new LinkedList();
- if (statDump != null) {
- statistics = Arrays.asList(statDump);
- }
- return statistics;
- }
-
- public JVppClientStatsImpl() {
- connectClient();
- }
-
- public boolean reconnect() {
- connectClient();
- return this.connected;
- }
-
- private void connectClient() {
- if (!this.connected) {
- if (statSegmentConnect() == 0) {
- this.connected = true;
- }
- }
- }
-
- public boolean isConnected() {
- return connected;
- }
-
- public void close() {
- if (this.connected) {
- statSegmentDisconnect();
- this.connected = false;
- }
- }
-}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/JVppStats.java b/java/jvpp-stats/io/fd/jvpp/stats/JVppStats.java
new file mode 100644
index 0000000..c4f22dc
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/JVppStats.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats;
+
+/**
+ * <p>Java representation of plugin's api file.
+ */
+public interface JVppStats extends io.fd.jvpp.JVpp {
+ /**
+ * Generic dispatch method for sending requests to VPP
+ *
+ * @throws io.fd.jvpp.VppInvocationException if send request had failed
+ */
+ int send(io.fd.jvpp.dto.JVppRequest request) throws io.fd.jvpp.VppInvocationException;
+
+ int interfaceStatisticsDump() throws io.fd.jvpp.VppInvocationException;
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/JVppStatsImpl.java b/java/jvpp-stats/io/fd/jvpp/stats/JVppStatsImpl.java
new file mode 100644
index 0000000..fdee289
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/JVppStatsImpl.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats;
+
+import io.fd.jvpp.JVppRegistry;
+import io.fd.jvpp.VppConnection;
+import io.fd.jvpp.VppInvocationException;
+import io.fd.jvpp.callback.JVppCallback;
+import io.fd.jvpp.dto.ControlPing;
+import io.fd.jvpp.dto.JVppRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Set;
+import java.util.logging.Logger;
+
+public class JVppStatsImpl implements io.fd.jvpp.stats.JVppStats {
+
+ private final static Logger LOG = Logger.getLogger(JVppStatsImpl.class.getName());
+ private static final java.lang.String LIBNAME = "libjvpp_stats.so";
+
+ static {
+ try {
+ loadLibrary();
+ } catch (Exception e) {
+ LOG.severe("Can't find jvpp jni library: " + LIBNAME);
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ private VppConnection connection;
+ private JVppRegistry registry;
+
+ private static void loadStream(final InputStream is) throws IOException {
+ final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
+ final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
+ try {
+ Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
+
+ try {
+ Runtime.getRuntime().load(p.toString());
+ } catch (UnsatisfiedLinkError e) {
+ throw new IOException("Failed to load library " + p, e);
+ }
+ } finally {
+ try {
+ Files.deleteIfExists(p);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ private static void loadLibrary() throws IOException {
+ try (final InputStream is = JVppStatsImpl.class.getResourceAsStream('/' + LIBNAME)) {
+ if (is == null) {
+ throw new IOException("Failed to open library resource " + LIBNAME);
+ }
+ loadStream(is);
+ }
+ }
+
+ private static native void init0(final JVppCallback callback, final long queueAddress, final int clientIndex);
+
+ private static native int interfaceStatisticsDump0() throws io.fd.jvpp.VppInvocationException;
+
+ private static native void close0();
+
+ @Override
+ public int send(final JVppRequest request) throws VppInvocationException {
+ return request.send(this);
+ }
+
+ @Override
+ public void init(final JVppRegistry registry, final JVppCallback callback, final long queueAddress,
+ final int clientIndex) {
+ this.registry = java.util.Objects.requireNonNull(registry, "registry should not be null");
+ this.connection = java.util.Objects.requireNonNull(registry.getConnection(), "connection should not be null");
+ connection.checkActive();
+ init0(callback, queueAddress, clientIndex);
+ }
+
+ @Override
+ public int controlPing(final ControlPing controlPing) {
+ return 1;
+ }
+
+ @Override
+ public int interfaceStatisticsDump() throws VppInvocationException {
+ connection.checkActive();
+ LOG.fine("Sending interfaceStatisticsDump event message");
+ int result = interfaceStatisticsDump0();
+ if (result < 0) {
+ throw new io.fd.jvpp.VppInvocationException("interfaceStatisticsDump", result);
+ }
+ return result;
+ }
+
+ @Override
+ public void close() {
+ close0();
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/JVppStatsRegistryImpl.java b/java/jvpp-stats/io/fd/jvpp/stats/JVppStatsRegistryImpl.java
new file mode 100644
index 0000000..a94a77f
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/JVppStatsRegistryImpl.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats;
+
+import io.fd.jvpp.JVpp;
+import io.fd.jvpp.JVppRegistry;
+import io.fd.jvpp.VppInvocationException;
+import io.fd.jvpp.callback.JVppCallback;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Stats implementation of JVppRegistry.
+ */
+public final class JVppStatsRegistryImpl implements JVppRegistry {
+
+ private final VppStatsJNIConnection connection;
+ // Unguarded concurrent map, no race conditions expected on top of that
+ private final Map<String, JVppCallback> pluginRegistry;
+
+ public JVppStatsRegistryImpl(final String clientName) throws IOException {
+ connection = new VppStatsJNIConnection(clientName);
+ connection.connect();
+ pluginRegistry = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public VppStatsJNIConnection 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.getStatsConnectionInfo().queueAddress,
+ connection.getStatsConnectionInfo().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(Class<? extends JVpp> clazz) throws VppInvocationException {
+ return controlPing0();
+ }
+
+ 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();
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/VppStatsJNIConnection.java b/java/jvpp-stats/io/fd/jvpp/stats/VppStatsJNIConnection.java
new file mode 100644
index 0000000..164a553
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/VppStatsJNIConnection.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats;
+
+import io.fd.jvpp.VppConnection;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static io.fd.jvpp.NativeLibraryLoader.loadLibrary;
+import static java.lang.String.format;
+
+/**
+ * JNI based representation of a management connection to VPP.
+ */
+public final class VppStatsJNIConnection implements VppConnection {
+ private static final Logger LOG = Logger.getLogger(VppStatsJNIConnection.class.getName());
+
+ private StatsConnectionInfo connectionInfo;
+
+ private final String clientName;
+ private volatile boolean disconnected = false;
+
+ static {
+ final String libName = "libjvpp_stats_registry.so";
+ try {
+ loadLibrary(libName, VppStatsJNIConnection.class);
+ } catch (IOException e) {
+ LOG.log(Level.SEVERE, format("Can't find vpp jni library: %s", libName), e);
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ /**
+ * Create VPPStatsJNIConnection instance for client connecting to VPP.
+ *
+ * @param clientName client name instance to be used for communication. Single connection per clientName is
+ * allowed.
+ */
+ public VppStatsJNIConnection(final String clientName) {
+ this.clientName = Objects.requireNonNull(clientName, "Null clientName");
+ }
+
+ /**
+ * Guarded by VppStatsJNIConnection.class
+ */
+ private static final Map<String, VppStatsJNIConnection> connections = new HashMap<>();
+
+ /**
+ * Initiate VPP connection for current instance
+ * <p>
+ * Multiple instances are allowed since this class is not a singleton (VPP allows multiple management connections).
+ * <p>
+ * However only a single connection per clientName is allowed.
+ *
+ * @throws IOException in case the connection could not be established
+ */
+
+ @Override
+ public void connect() throws IOException {
+ _connect();
+ }
+
+ private void _connect() throws IOException {
+
+ synchronized (VppStatsJNIConnection.class) {
+ if (connections.containsKey(clientName)) {
+ throw new IOException("Client " + clientName + " already connected");
+ }
+
+ connectionInfo = statsConnect(clientName);
+ if (connectionInfo.status != 0) {
+ throw new IOException("Connection returned error " + connectionInfo.status);
+ }
+ connections.put(clientName, this);
+ }
+ }
+
+ @Override
+ public final void checkActive() {
+ if (disconnected) {
+ throw new IllegalStateException("Disconnected client " + clientName);
+ }
+ }
+
+ @Override
+ public final synchronized void close() {
+ if (!disconnected) {
+ disconnected = true;
+ try {
+ statsDisconnect();
+ } finally {
+ synchronized (VppStatsJNIConnection.class) {
+ connections.remove(clientName);
+ }
+ }
+ }
+ }
+
+ public StatsConnectionInfo getStatsConnectionInfo() {
+ return connectionInfo;
+ }
+
+ /**
+ * VPP connection information used by plugins to reuse the connection.
+ */
+ public static final class StatsConnectionInfo {
+ public final long queueAddress;
+ public final int clientIndex;
+ public final int status; // FIXME throw exception instead
+ public final int pid;
+
+ public StatsConnectionInfo(long queueAddress, int clientIndex, int status, int pid) {
+ this.queueAddress = queueAddress;
+ this.clientIndex = clientIndex;
+ this.status = status;
+ this.pid = pid;
+ }
+ }
+
+ private static native StatsConnectionInfo statsConnect(String clientName);
+
+ private static native void statsDisconnect();
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/callback/InterfaceStatisticsDetailsCallback.java b/java/jvpp-stats/io/fd/jvpp/stats/callback/InterfaceStatisticsDetailsCallback.java
new file mode 100644
index 0000000..86fe189
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/callback/InterfaceStatisticsDetailsCallback.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.callback;
+
+/**
+ * <p>Represents callback for plugin's api message.
+ */
+public interface InterfaceStatisticsDetailsCallback extends io.fd.jvpp.callback.JVppCallback {
+
+ void onInterfaceStatisticsDetails(io.fd.jvpp.stats.dto.InterfaceStatisticsDetails reply);
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/callback/JVppStatsGlobalCallback.java b/java/jvpp-stats/io/fd/jvpp/stats/callback/JVppStatsGlobalCallback.java
new file mode 100644
index 0000000..794a645
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/callback/JVppStatsGlobalCallback.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.callback;
+
+/**
+ * Global aggregated callback interface.
+ */
+public interface JVppStatsGlobalCallback extends io.fd.jvpp.stats.callback.InterfaceStatisticsDetailsCallback {
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStats.java b/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStats.java
new file mode 100644
index 0000000..307e24d
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStats.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.callfacade;
+
+import io.fd.jvpp.stats.callback.InterfaceStatisticsDetailsCallback;
+
+/**
+ * <p>Callback Java API representation of io.fd.jvpp.stats plugin.
+ */
+public interface CallbackJVppStats extends io.fd.jvpp.notification.EventRegistryProvider, AutoCloseable {
+
+ // TODO add send
+ void interfaceStatisticsDump(InterfaceStatisticsDetailsCallback callback) throws io.fd.jvpp.VppInvocationException;
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacade.java b/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacade.java
new file mode 100644
index 0000000..f936dc1
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacade.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.callfacade;
+
+import io.fd.jvpp.notification.EventRegistry;
+
+/**
+ * <p>Default implementation of CallbackStatsJVpp interface.
+ */
+public final class CallbackJVppStatsFacade implements CallbackJVppStats {
+
+ private final io.fd.jvpp.stats.JVppStats jvpp;
+ private final java.util.Map<Integer, io.fd.jvpp.callback.JVppCallback> callbacks;
+
+ /**
+ * <p>Create CallbackJVppStatsFacade object for provided JVpp instance.
+ * Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks
+ * and then connects to provided JVpp instance
+ *
+ * @param jvpp provided io.fd.jvpp.JVpp instance
+ * @throws java.io.IOException in case instance cannot connect to JVPP
+ */
+ public CallbackJVppStatsFacade(final io.fd.jvpp.JVppRegistry registry, final io.fd.jvpp.stats.JVppStats jvpp)
+ throws java.io.IOException {
+ this.jvpp = java.util.Objects.requireNonNull(jvpp, "jvpp is null");
+ this.callbacks = new java.util.HashMap<>();
+ java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
+ registry.register(jvpp, new CallbackJVppStatsFacadeCallback(this.callbacks));
+ }
+
+ @Override
+ public void close() throws Exception {
+ jvpp.close();
+ }
+
+ public final void interfaceStatisticsDump(io.fd.jvpp.stats.callback.InterfaceStatisticsDetailsCallback callback)
+ throws io.fd.jvpp.VppInvocationException {
+ synchronized (callbacks) {
+ callbacks.put(jvpp.interfaceStatisticsDump(), callback);
+ }
+ }
+
+ @Override
+ public EventRegistry getEventRegistry() {
+ return null;
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacadeCallback.java b/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacadeCallback.java
new file mode 100644
index 0000000..ec1feb2
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacadeCallback.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.callfacade;
+
+/**
+ * <p>Implementation of JVppGlobalCallback interface for Java Callback API.
+ */
+public final class CallbackJVppStatsFacadeCallback implements io.fd.jvpp.stats.callback.JVppStatsGlobalCallback {
+
+ private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(
+ CallbackJVppStatsFacadeCallback.class.getName());
+ private final java.util.Map<Integer, io.fd.jvpp.callback.JVppCallback> requests;
+
+ public CallbackJVppStatsFacadeCallback(final java.util.Map<Integer, io.fd.jvpp.callback.JVppCallback> requestMap) {
+ this.requests = requestMap;
+ }
+
+ @Override
+ public void onError(io.fd.jvpp.VppCallbackException reply) {
+
+ io.fd.jvpp.callback.JVppCallback failedCall;
+ synchronized (requests) {
+ failedCall = requests.remove(reply.getCtxId());
+ }
+
+ if (failedCall != null) {
+ try {
+ failedCall.onError(reply);
+ } catch (RuntimeException ex) {
+ ex.addSuppressed(reply);
+ LOG.log(java.util.logging.Level.WARNING,
+ String.format("Callback: %s failed while handling exception: %s", failedCall, reply), ex);
+ }
+ }
+ }
+
+ @Override
+ public void onInterfaceStatisticsDetails(final io.fd.jvpp.stats.dto.InterfaceStatisticsDetails reply) {
+
+ io.fd.jvpp.stats.callback.InterfaceStatisticsDetailsCallback callback;
+ final int replyId = reply.context;
+ if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+ LOG.fine(String.format("Received InterfaceStatisticsDetails event message: %s", reply));
+ }
+ synchronized (requests) {
+ callback = (io.fd.jvpp.stats.callback.InterfaceStatisticsDetailsCallback) requests.remove(replyId);
+ }
+
+ if (callback != null) {
+ callback.onInterfaceStatisticsDetails(reply);
+ }
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatistics.java b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatistics.java
index 49486a1..e831e35 100644
--- a/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatistics.java
+++ b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatistics.java
@@ -16,25 +16,27 @@
package io.fd.jvpp.stats.dto;
+import java.util.Objects;
+
public class InterfaceStatistics {
- private int swIfIndex;
- private int outErrors;
- private int outMulticastPkts;
- private int outUnicastPkts;
- private int outBroadcastPkts;
- private int outBytes;
- private int inErrors;
- private int inMulticastPkts;
- private int inUnicastPkts;
- private int inBroadcastPkts;
- private int inBytes;
+ public int swIfIndex;
+ public int outErrors;
+ public int outMulticastPkts;
+ public int outUnicastPkts;
+ public int outBroadcastPkts;
+ public int outBytes;
+ public int inErrors;
+ public int inMulticastPkts;
+ public int inUnicastPkts;
+ public int inBroadcastPkts;
+ public int inBytes;
public InterfaceStatistics(final int swIfIndex, final int outErrors, final int outMulticastPkts,
- final int outUnicastPkts,
- final int outBroadcastPkts, final int outBytes, final int inErrors,
- final int inMulticastPkts, final int inUnicastPkts,
- final int inBroadcastPkts, final int inBytes) {
+ final int outUnicastPkts, final int outBroadcastPkts,
+ final int outBytes, final int inErrors, final int inMulticastPkts,
+ final int inUnicastPkts, final int inBroadcastPkts,
+ final int inBytes) {
this.swIfIndex = swIfIndex;
this.outErrors = outErrors;
this.outMulticastPkts = outMulticastPkts;
@@ -48,52 +50,37 @@ public class InterfaceStatistics {
this.inBytes = inBytes;
}
- public int getSwIfIndex() {
- return swIfIndex;
- }
-
- public int getOutErrors() {
- return outErrors;
- }
-
- public int getOutMulticastPkts() {
- return outMulticastPkts;
- }
-
- public int getOutUnicastPkts() {
- return outUnicastPkts;
- }
-
- public int getOutBroadcastPkts() {
- return outBroadcastPkts;
- }
-
- public int getOutBytes() {
- return outBytes;
- }
-
- public int getInErrors() {
- return inErrors;
- }
-
- public int getInMulticastPkts() {
- return inMulticastPkts;
- }
-
- public int getInUnicastPkts() {
- return inUnicastPkts;
- }
-
- public int getInBroadcastPkts() {
- return inBroadcastPkts;
+ @Override
+ public boolean equals(final Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+ final InterfaceStatistics otherIfcStats = (InterfaceStatistics) other;
+ return swIfIndex == otherIfcStats.swIfIndex &&
+ outErrors == otherIfcStats.outErrors &&
+ outMulticastPkts == otherIfcStats.outMulticastPkts &&
+ outUnicastPkts == otherIfcStats.outUnicastPkts &&
+ outBroadcastPkts == otherIfcStats.outBroadcastPkts &&
+ outBytes == otherIfcStats.outBytes &&
+ inErrors == otherIfcStats.inErrors &&
+ inMulticastPkts == otherIfcStats.inMulticastPkts &&
+ inUnicastPkts == otherIfcStats.inUnicastPkts &&
+ inBroadcastPkts == otherIfcStats.inBroadcastPkts &&
+ inBytes == otherIfcStats.inBytes;
}
- public int getInBytes() {
- return inBytes;
+ @Override
+ public int hashCode() {
+ return Objects
+ .hash(swIfIndex, outErrors, outMulticastPkts, outUnicastPkts, outBroadcastPkts, outBytes, inErrors,
+ inMulticastPkts, inUnicastPkts, inBroadcastPkts, inBytes);
}
- @java.lang.Override
- public java.lang.String toString() {
+ @Override
+ public String toString() {
return "InterfaceStatistics{" +
"swIfIndex=" + swIfIndex +
", outErrors=" + outErrors +
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetails.java b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetails.java
new file mode 100644
index 0000000..997dc70
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetails.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.dto;
+
+/**
+ * <p>This class represents reply DTO for InterfaceStatisticsDetails.
+ */
+public final class InterfaceStatisticsDetails implements io.fd.jvpp.dto.JVppReply<InterfaceStatisticsDump> {
+ public int context;
+ public InterfaceStatistics[] interfaceStatistics;
+ public int length;
+
+ public InterfaceStatisticsDetails(int length, int context) {
+ this.context = context;
+ this.length = length;
+ this.interfaceStatistics = new InterfaceStatistics[length];
+ }
+
+ @Override
+ @io.fd.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+ public int hashCode() {
+ return java.util.Objects.hash(context, interfaceStatistics, length);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final InterfaceStatisticsDetails other = (InterfaceStatisticsDetails) o;
+
+ if (!java.util.Objects.equals(this.context, other.context)) {
+ return false;
+ }
+ if (!java.util.Arrays.equals(this.interfaceStatistics, other.interfaceStatistics)) {
+ return false;
+ }
+ if (!java.util.Objects.equals(this.length, other.length)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public java.lang.String toString() {
+ return "InterfaceStatisticsDetails{" +
+ "context=" + context +
+ ", interfaceStatistics=" + java.util.Arrays.toString(interfaceStatistics) +
+ ", length=" + length +
+ '}';
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetailsReplyDump.java b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetailsReplyDump.java
new file mode 100644
index 0000000..53ef30f
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetailsReplyDump.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.dto;
+
+/**
+ * <p>This class represents dump reply wrapper for InterfaceStatisticsDetails.
+ */
+public final class InterfaceStatisticsDetailsReplyDump
+ implements io.fd.jvpp.dto.JVppReplyDump<InterfaceStatisticsDump, InterfaceStatisticsDetails> {
+
+ public InterfaceStatisticsDetails interfaceStatisticsDetails;
+
+ @Override
+ @io.fd.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+ public int hashCode() {
+ return java.util.Objects.hash(interfaceStatisticsDetails);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final InterfaceStatisticsDetailsReplyDump
+ other = (InterfaceStatisticsDetailsReplyDump) o;
+
+ return java.util.Objects.equals(this.interfaceStatisticsDetails, other.interfaceStatisticsDetails);
+ }
+
+ @Override
+ public String toString() {
+ return "InterfaceStatisticsDetailsReplyDump{" +
+ "interfaceStatisticsDetails=" + interfaceStatisticsDetails +
+ '}';
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDump.java b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDump.java
new file mode 100644
index 0000000..e51936a
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDump.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.dto;
+
+/**
+ * <p>This class represents request DTO.
+ */
+public final class InterfaceStatisticsDump implements io.fd.jvpp.dto.JVppDump {
+
+ @Override
+ @io.fd.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+ public int hashCode() {
+ return java.util.Objects.hash();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "InterfaceStatisticsDump{}";
+ }
+
+ @Override
+ public int send(final io.fd.jvpp.JVpp jvpp) throws io.fd.jvpp.VppInvocationException {
+ return ((io.fd.jvpp.stats.JVppStats) jvpp).interfaceStatisticsDump();
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/future/AbstractFutureJVppInvoker.java b/java/jvpp-stats/io/fd/jvpp/stats/future/AbstractFutureJVppInvoker.java
new file mode 100644
index 0000000..e66cfd8
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/future/AbstractFutureJVppInvoker.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.future;
+
+
+import io.fd.jvpp.JVpp;
+import io.fd.jvpp.JVppRegistry;
+import io.fd.jvpp.VppInvocationException;
+import io.fd.jvpp.dto.JVppDump;
+import io.fd.jvpp.dto.JVppReply;
+import io.fd.jvpp.dto.JVppReplyDump;
+import io.fd.jvpp.dto.JVppRequest;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+/**
+ * Future facade on top of JVpp
+ */
+public abstract class AbstractFutureJVppInvoker implements FutureJVppInvoker {
+
+ private final JVpp jvpp;
+ private final JVppRegistry registry;
+
+ /**
+ * Guarded by self
+ */
+ private final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requests;
+
+ protected AbstractFutureJVppInvoker(final JVpp jvpp, final JVppRegistry registry,
+ final Map<Integer, CompletableFuture<? extends JVppReply<?>>> requestMap) {
+ this.jvpp = Objects.requireNonNull(jvpp, "jvpp should not be null");
+ this.registry = Objects.requireNonNull(registry, "registry should not be null");
+ // Request map represents the shared state between this facade and it's callback
+ // where facade puts futures in and callback completes + removes them
+ this.requests = Objects.requireNonNull(requestMap, "Null requestMap");
+ }
+
+ protected final Map<Integer, CompletableFuture<? extends JVppReply<?>>> getRequests() {
+ synchronized (requests) {
+ return requests;
+ }
+ }
+
+ // TODO use Optional in Future, java8
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <REQ extends JVppRequest, REPLY extends JVppReply<REQ>> CompletionStage<REPLY> send(REQ req) {
+ try {
+ // jvpp.send() can go to waiting state if sending queue is full, putting it into same
+ // synchronization block as used by receiving part (synchronized(requests)) can lead
+ // to deadlock between these two sides or at least slowing sending process by slow
+ // reader
+ final CompletableFuture<REPLY> replyCompletableFuture;
+ final int contextId = jvpp.send(req);
+
+ if(req instanceof JVppDump) {
+ throw new IllegalArgumentException("Send with empty reply dump has to be used in case of dump calls");
+ }
+
+ synchronized(requests) {
+ CompletableFuture<? extends JVppReply<?>> replyFuture = requests.get(contextId);
+ if (replyFuture == null) {
+ // reply not yet received, put new future into map
+ replyCompletableFuture = new CompletableFuture<>();
+ requests.put(contextId, replyCompletableFuture);
+ } else {
+ // reply already received (should be completed by reader),
+ // remove future from map and return it to caller
+ replyCompletableFuture = (CompletableFuture<REPLY>) replyFuture;
+ requests.remove(contextId);
+ }
+ }
+
+ // TODO in case of timeouts/missing replies, requests from the map are not removed
+ // consider adding cancel method, that would remove requests from the map and cancel
+ // associated replyCompletableFuture
+
+ return replyCompletableFuture;
+ } catch (VppInvocationException ex) {
+ final CompletableFuture<REPLY> replyCompletableFuture = new CompletableFuture<>();
+ replyCompletableFuture.completeExceptionally(ex);
+ return replyCompletableFuture;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <REQ extends JVppRequest, REPLY extends JVppReply<REQ>, DUMP extends JVppReplyDump<REQ, REPLY>> CompletionStage<DUMP> send(
+ REQ req, DUMP emptyReplyDump) {
+ try {
+ // jvpp.send() can go to waiting state if sending queue is full,
+ // putting it into same synchronization block as used by receiving part (synchronized(requests))
+ // can lead to deadlock between these two sides or at least slowing sending process by slow reader
+ final CompletableDumpFuture<DUMP> replyDumpFuture;
+ final int contextId = jvpp.send(req);
+
+ if(!(req instanceof JVppDump)) {
+ throw new IllegalArgumentException("Send without empty reply dump has to be used in case of regular calls");
+ }
+
+ synchronized(requests) {
+ CompletableFuture<? extends JVppReply<?>> replyFuture = requests.get(contextId);
+ if (replyFuture == null) {
+ // reply not received yet, put new future to map
+ replyDumpFuture = new CompletableDumpFuture<>(contextId, emptyReplyDump);
+ requests.put(contextId, replyDumpFuture);
+ } else {
+ // reply already received, save existing future
+ replyDumpFuture = (CompletableDumpFuture<DUMP>) replyFuture;
+ }
+ }
+
+ synchronized (requests) {
+ // reply already received, complete future
+ replyDumpFuture.complete(replyDumpFuture.getReplyDump());
+ requests.remove(contextId);
+ }
+
+ // TODO in case of timeouts/missing replies, requests from the map are not removed
+ // consider adding cancel method, that would remove requests from the map and cancel
+ // associated replyCompletableFuture
+
+ return replyDumpFuture;
+ } catch (VppInvocationException ex) {
+ final CompletableFuture<DUMP> replyCompletableFuture = new CompletableFuture<>();
+ replyCompletableFuture.completeExceptionally(ex);
+ return replyCompletableFuture;
+ }
+ }
+
+ public static final class CompletableDumpFuture<T extends JVppReplyDump<?, ?>> extends CompletableFuture<T> {
+ private final T replyDump;
+ private final int contextId;
+
+ public CompletableDumpFuture(final int contextId, final T emptyDump) {
+ this.contextId = contextId;
+ this.replyDump = emptyDump;
+ }
+
+ public int getContextId() {
+ return contextId;
+ }
+
+ public T getReplyDump() {
+ return replyDump;
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ jvpp.close();
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppInvoker.java b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppInvoker.java
new file mode 100644
index 0000000..c93c332
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppInvoker.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.future;
+
+
+import io.fd.jvpp.dto.JVppReply;
+import io.fd.jvpp.dto.JVppReplyDump;
+import io.fd.jvpp.dto.JVppRequest;
+import io.fd.jvpp.notification.EventRegistryProvider;
+import java.util.concurrent.CompletionStage;
+
+/**
+* Future facade on top of JVpp
+*/
+public interface FutureJVppInvoker extends EventRegistryProvider, AutoCloseable {
+
+ /**
+ * Invoke asynchronous operation on VPP
+ *
+ * @return CompletionStage with future result of an async VPP call
+ * @throws io.fd.jvpp.VppInvocationException when send request failed with details
+ */
+ <REQ extends JVppRequest, REPLY extends JVppReply<REQ>> CompletionStage<REPLY> send(REQ req);
+
+
+ /**
+ * Invoke asynchronous dump operation on VPP
+ *
+ * @return CompletionStage with aggregated future result of an async VPP dump call
+ * @throws io.fd.jvpp.VppInvocationException when send request failed with details
+ */
+ <REQ extends JVppRequest, REPLY extends JVppReply<REQ>, DUMP extends JVppReplyDump<REQ, REPLY>> CompletionStage<DUMP> send(
+ REQ req, DUMP emptyReplyDump);
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStats.java b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStats.java
new file mode 100644
index 0000000..288b556
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStats.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.future;
+
+import io.fd.jvpp.stats.dto.InterfaceStatisticsDetailsReplyDump;
+
+/**
+ * <p>Async facade extension adding specific methods for each request invocation
+ */
+public interface FutureJVppStats extends io.fd.jvpp.stats.future.FutureJVppInvoker {
+
+ java.util.concurrent.CompletionStage<InterfaceStatisticsDetailsReplyDump> interfaceStatisticsDump(
+ io.fd.jvpp.stats.dto.InterfaceStatisticsDump request);
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacade.java b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacade.java
new file mode 100644
index 0000000..470a2fd
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacade.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.future;
+
+import io.fd.jvpp.notification.EventRegistry;
+import io.fd.jvpp.stats.dto.InterfaceStatisticsDetailsReplyDump;
+
+/**
+ * <p>Implementation of FutureJVpp based on AbstractFutureJVppInvoker
+ */
+public class FutureJVppStatsFacade extends io.fd.jvpp.stats.future.AbstractFutureJVppInvoker
+ implements FutureJVppStats {
+
+ /**
+ * <p>Create FutureJVppStatsFacade object for provided JVpp instance.
+ * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks
+ * and then connects to provided JVpp instance
+ *
+ * @param jvpp provided io.fd.jvpp.JVpp instance
+ * @throws java.io.IOException in case instance cannot connect to JVPP
+ */
+ public FutureJVppStatsFacade(final io.fd.jvpp.JVppRegistry registry, final io.fd.jvpp.JVpp jvpp)
+ throws java.io.IOException {
+ super(jvpp, registry, new java.util.HashMap<>());
+ java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
+ registry.register(jvpp, new FutureJVppStatsFacadeCallback(getRequests()));
+ }
+
+ @Override
+ public java.util.concurrent.CompletionStage<InterfaceStatisticsDetailsReplyDump> interfaceStatisticsDump(
+ io.fd.jvpp.stats.dto.InterfaceStatisticsDump request) {
+ return send(request, new InterfaceStatisticsDetailsReplyDump());
+ }
+
+ @Override
+ public EventRegistry getEventRegistry() {
+ return null;
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacadeCallback.java b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacadeCallback.java
new file mode 100644
index 0000000..a215b89
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacadeCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.future;
+
+import io.fd.jvpp.stats.dto.InterfaceStatisticsDetailsReplyDump;
+
+/**
+ * <p>Async facade callback setting values to future objects
+ */
+public final class FutureJVppStatsFacadeCallback implements io.fd.jvpp.stats.callback.JVppStatsGlobalCallback {
+
+ private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(
+ FutureJVppStatsFacadeCallback.class.getName());
+ private final java.util.Map<Integer, java.util.concurrent.CompletableFuture<? extends io.fd.jvpp.dto.JVppReply<?>>>
+ requests;
+
+ public FutureJVppStatsFacadeCallback(
+ final java.util.Map<Integer, java.util.concurrent.CompletableFuture<? extends io.fd.jvpp.dto.JVppReply<?>>> requestMap) {
+ this.requests = requestMap;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void onError(io.fd.jvpp.VppCallbackException reply) {
+ final java.util.concurrent.CompletableFuture<io.fd.jvpp.dto.JVppReply<?>> completableFuture;
+
+ synchronized (requests) {
+ completableFuture = (java.util.concurrent.CompletableFuture<io.fd.jvpp.dto.JVppReply<?>>) requests
+ .get(reply.getCtxId());
+ }
+
+ if (completableFuture != null) {
+ completableFuture.completeExceptionally(reply);
+
+ synchronized (requests) {
+ requests.remove(reply.getCtxId());
+ }
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void onInterfaceStatisticsDetails(final io.fd.jvpp.stats.dto.InterfaceStatisticsDetails reply) {
+ io.fd.jvpp.stats.future.AbstractFutureJVppInvoker.CompletableDumpFuture<InterfaceStatisticsDetailsReplyDump>
+ completableFuture;
+ final int replyId = reply.context;
+ if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+ LOG.fine(String.format("Received InterfaceStatisticsDetails event message: %s", reply));
+ }
+ synchronized (requests) {
+ completableFuture =
+ (io.fd.jvpp.stats.future.AbstractFutureJVppInvoker.CompletableDumpFuture<InterfaceStatisticsDetailsReplyDump>) requests
+ .get(replyId);
+
+ if (completableFuture == null) {
+ // reply received before writer created future,
+ // create new future, and put into map to notify sender that reply is already received,
+ // following details replies will add information to this future
+ completableFuture =
+ new io.fd.jvpp.stats.future.AbstractFutureJVppInvoker.CompletableDumpFuture<>(replyId,
+ new InterfaceStatisticsDetailsReplyDump());
+ requests.put(replyId, completableFuture);
+ }
+ completableFuture.getReplyDump().interfaceStatisticsDetails = reply;
+ }
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/test/FutureApiTest.java b/java/jvpp-stats/io/fd/jvpp/stats/test/FutureApiTest.java
new file mode 100644
index 0000000..2b28915
--- /dev/null
+++ b/java/jvpp-stats/io/fd/jvpp/stats/test/FutureApiTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2019 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.jvpp.stats.test;
+
+
+import io.fd.jvpp.JVppRegistry;
+import io.fd.jvpp.stats.JVppStatsImpl;
+import io.fd.jvpp.stats.JVppStatsRegistryImpl;
+import io.fd.jvpp.stats.dto.InterfaceStatisticsDetailsReplyDump;
+import io.fd.jvpp.stats.dto.InterfaceStatisticsDump;
+import io.fd.jvpp.stats.future.FutureJVppStatsFacade;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+ private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName());
+
+ public static void main(String[] args) throws Exception {
+ testCallbackApi(args);
+ }
+
+ private static void testCallbackApi(String[] args) throws Exception {
+ LOG.info("Testing Java callback API for stats plugin");
+ try (final JVppRegistry registry = new JVppStatsRegistryImpl("FutureApiTest");
+ final FutureJVppStatsFacade jvpp = new FutureJVppStatsFacade(registry, new JVppStatsImpl())) {
+ LOG.info("Successfully connected to VPP");
+ testinterfaceStatisticsDump(jvpp);
+
+ LOG.info("Disconnecting...");
+ }
+ }
+
+ private static void testinterfaceStatisticsDump(FutureJVppStatsFacade jvpp) throws Exception {
+ LOG.info("Sending InterfaceStatisticsDump request...");
+ final InterfaceStatisticsDump request = new InterfaceStatisticsDump();
+
+ final Future<InterfaceStatisticsDetailsReplyDump> replyFuture =
+ jvpp.interfaceStatisticsDump(request).toCompletableFuture();
+ final InterfaceStatisticsDetailsReplyDump reply = replyFuture.get();
+
+ if (reply == null || reply.interfaceStatisticsDetails == null) {
+ throw new IllegalStateException("Received null response for empty dump: " + reply);
+ } else {
+ LOG.info(String.format("Received interface statistics reply: %s \n", reply.interfaceStatisticsDetails));
+ }
+ }
+}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/test/JvppStatsApiTest.java b/java/jvpp-stats/io/fd/jvpp/stats/test/JvppStatsApiTest.java
deleted file mode 100644
index 2f9cf9f..0000000
--- a/java/jvpp-stats/io/fd/jvpp/stats/test/JvppStatsApiTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (c) 2019 PANTHEON.tech.
- *
- * 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.jvpp.stats.test;
-
-import io.fd.jvpp.stats.JVppClientStatsImpl;
-import io.fd.jvpp.stats.dto.InterfaceStatistics;
-import java.util.List;
-
-class JvppStatsApiTest {
-
- public static void main(String[] args) {
-
- JVppClientStatsImpl myImpl = new JVppClientStatsImpl();
- List<InterfaceStatistics> statistics = myImpl.getInterfaceStatistics();
-
- if (statistics == null) {
- System.out.println("in java - dump was null");
- System.exit(1);
- }
-
- if (statistics.size() == 0) {
- System.out.println("in java - dump was empty");
- System.exit(2);
- }
-
- for (InterfaceStatistics stat: statistics) {
- System.out.println(stat.toString());
- }
- }
-}
diff --git a/java/jvpp-stats/io/fd/jvpp/stats/test/Readme.txt b/java/jvpp-stats/io/fd/jvpp/stats/test/Readme.txt
index cda9ed2..9fb22cc 100644
--- a/java/jvpp-stats/io/fd/jvpp/stats/test/Readme.txt
+++ b/java/jvpp-stats/io/fd/jvpp/stats/test/Readme.txt
@@ -2,4 +2,11 @@ This package contains basic tests for jvpp. To run the tests:
- Make sure VPP is running
- From JVPP's java/ folder execute:
- - sudo java -cp jvpp-registry-19.04.jar:jvpp-stats-19.04.jar io.fd.jvpp.stats.test.JvppStatsApiTest
+ - "sudo java -cp jvpp-registry-19.08.jar:jvpp-stats-19.08.jar io.fd.jvpp.stats.test.FutureApiTest"
+- For debug use:
+ - "sudo java -verbose:jni -Xcheck:jni -cp jvpp-registry-19.08.jar:jvpp-stats-19.08.jar io.fd.jvpp.stats.test.FutureApiTest"
+ - in the case there is a core dump generated use GDB for backtrace:
+ - "sudo gdb java core"
+ - then in GDB console use "bt full" command to print backtrace.
+ - if libraries are missing use "info sharedlibrary" in GDB to find out path and name of missing libraries.
+ You can copy missing libraries to desired location (usually /tmp/, because the libs are extracted from jars on startup) \ No newline at end of file
diff --git a/java/jvpp-stats/jvpp_interface_stats.h b/java/jvpp-stats/jvpp_interface_stats.h
new file mode 100644
index 0000000..01b8cdb
--- /dev/null
+++ b/java/jvpp-stats/jvpp_interface_stats.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+/**
+ * This file contains JNI bindings for jvpp Java API.
+ */
+#include <jvpp-common/jvpp_common.h>
+#include <vpp-api/client/stat_client.h>
+
+// JAVA class reference cache
+jclass interfaceStatisticsDumpClass;
+jclass interfaceStatisticsClass;
+jclass interfaceStatisticsDetailsClass;
+jclass callbackExceptionClass;
+
+typedef struct interface_statistics {
+ int context;
+ int sw_if_index;
+ int rx_errors;
+ int rx_bytes;
+ int rx_unicast_pkts;
+ int rx_broadcast_pkts;
+ int rx_multicast_pkts;
+ int tx_errors;
+ int tx_bytes;
+ int tx_unicast_pkts;
+ int tx_broadcast_pkts;
+ int tx_multicast_pkts;
+
+} vl_api_interface_statistics_details_t;
+
+static int cache_class_references(JNIEnv *env) {
+
+ interfaceStatisticsDumpClass = (jclass) (*env)->NewGlobalRef(env, (*env)->FindClass(env,
+ "io/fd/jvpp/stats/dto/InterfaceStatisticsDump"));
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ return JNI_ERR;
+ }
+ interfaceStatisticsDetailsClass = (jclass) (*env)->NewGlobalRef(env, (*env)->FindClass(env,
+ "io/fd/jvpp/stats/dto/InterfaceStatisticsDetails"));
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ return JNI_ERR;
+ }
+ interfaceStatisticsClass = (jclass) (*env)->NewGlobalRef(env, (*env)->FindClass(env,
+ "io/fd/jvpp/stats/dto/InterfaceStatistics"));
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ return JNI_ERR;
+ }
+ callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env,
+ (*env)->FindClass(env, "io/fd/jvpp/VppCallbackException"));
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ return JNI_ERR;
+ }
+ return 0;
+}
+
+static void delete_class_references(JNIEnv *env) {
+
+ if (interfaceStatisticsDumpClass) {
+ (*env)->DeleteGlobalRef(env, interfaceStatisticsDumpClass);
+ }
+ if (interfaceStatisticsDetailsClass) {
+ (*env)->DeleteGlobalRef(env, interfaceStatisticsDetailsClass);
+ }
+ if (interfaceStatisticsClass) {
+ (*env)->DeleteGlobalRef(env, interfaceStatisticsClass);
+ }
+ if (callbackExceptionClass) {
+ (*env)->DeleteGlobalRef(env, callbackExceptionClass);
+ }
+}
+
+/**
+ * Handler for interface_statistics_details message.
+ */
+static void
+interface_statistics_details_handler(JNIEnv *env, vl_api_interface_statistics_details_t *ifc_stats, int ifc_count) {
+ stats_main_t *plugin_main = &stats_main;
+ jthrowable exc;
+ if (CLIB_DEBUG > 1)
+ clib_warning ("Received interface_statistics_details event message");
+
+ jmethodID constructor = (*env)->GetMethodID(env, interfaceStatisticsDetailsClass, "<init>", "(II)V");
+
+ // User does not have to provide callbacks for all VPP messages.
+ // We are ignoring messages that are not supported by user.
+ (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared
+ jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "onInterfaceStatisticsDetails",
+ "(Lio/fd/jvpp/stats/dto/InterfaceStatisticsDetails;)V");
+ exc = (*env)->ExceptionOccurred(env);
+ if (exc) {
+ clib_warning(
+ "Unable to extract onInterfaceStatisticsDetails method reference from stats plugin's callbackClass. Ignoring message.\n");
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ return;
+ }
+ jobject dto = (*env)->NewObject(env, interfaceStatisticsDetailsClass, constructor, ifc_count, ifc_stats->context);
+ jfieldID interfaceStatisticsId = (*env)->GetFieldID(env, interfaceStatisticsDetailsClass, "interfaceStatistics",
+ "[Lio/fd/jvpp/stats/dto/InterfaceStatistics;");
+ jobject stats_array = (*env)->GetObjectField(env, dto, interfaceStatisticsId);
+
+ jmethodID ifc_stats_constructor = (*env)->GetMethodID(env, interfaceStatisticsClass, "<init>", "(IIIIIIIIIII)V");
+ for (int i = 0; i < ifc_count; i++) {
+
+ jobject element = (*env)->NewObject(env, interfaceStatisticsClass, ifc_stats_constructor,
+ ifc_stats[i].sw_if_index,
+ ifc_stats[i].tx_errors,
+ ifc_stats[i].tx_multicast_pkts,
+ ifc_stats[i].tx_unicast_pkts,
+ ifc_stats[i].tx_broadcast_pkts,
+ ifc_stats[i].tx_bytes,
+ ifc_stats[i].rx_errors,
+ ifc_stats[i].rx_multicast_pkts,
+ ifc_stats[i].rx_unicast_pkts,
+ ifc_stats[i].rx_broadcast_pkts,
+ ifc_stats[i].rx_bytes);
+
+ (*env)->SetObjectArrayElement(env, stats_array, i, element);
+ if ((*env)->ExceptionOccurred(env)) {
+ break;
+ }
+ (*env)->DeleteLocalRef(env, element);
+ }
+ (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
+ if ((*env)->ExceptionOccurred(env)) {
+ clib_warning("Unable to call callback for stats plugin's callbackClass.\n");
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ return;
+ }
+ (*env)->DeleteLocalRef(env, stats_array);
+ (*env)->DeleteLocalRef(env, dto);
+}
+
+static void
+set_field(vl_api_interface_statistics_details_t *stats, int index, const char *field_name, int packets, int bytes) {
+ if (strcmp(field_name, "/if/rx-error") == 0) {
+ stats[index].rx_errors = packets;
+ } else if (strcmp(field_name, "/if/tx-error") == 0) {
+ stats[index].tx_errors = packets;
+ } else if (strcmp(field_name, "/if/tx") == 0) {
+ stats[index].tx_bytes = bytes;
+ } else if (strcmp(field_name, "/if/tx-multicast") == 0) {
+ stats[index].tx_multicast_pkts = packets;
+ } else if (strcmp(field_name, "/if/tx-unicast") == 0) {
+ stats[index].tx_unicast_pkts = packets;
+ } else if (strcmp(field_name, "/if/tx-broadcast") == 0) {
+ stats[index].tx_broadcast_pkts = packets;
+ } else if (strcmp(field_name, "/if/rx") == 0) {
+ stats[index].rx_bytes = bytes;
+ } else if (strcmp(field_name, "/if/rx-multicast") == 0) {
+ stats[index].rx_multicast_pkts = packets;
+ } else if (strcmp(field_name, "/if/rx-unicast") == 0) {
+ stats[index].rx_unicast_pkts = packets;
+ } else if (strcmp(field_name, "/if/rx-broadcast") == 0) {
+ stats[index].rx_broadcast_pkts = packets;
+ }
+ stats[index].sw_if_index = index;
+}
+
+static stat_segment_data_t *get_ifc_statistics_dump() {
+ u8 **patterns = 0;
+ u32 *dir;
+ vec_add1(patterns, (u8 *) "/if/rx");
+ vec_add1(patterns, (u8 *) "/if/tx");
+ dir = stat_segment_ls(patterns);
+ return stat_segment_dump(dir);
+}
+
+static jint getInterfaceStatisticsDump(JNIEnv *env) {
+ stat_segment_data_t *res;
+ int i, j, k, interface_count = 0;
+ u32 my_context_id = vppjni_get_context_id(&jvpp_main);
+ res = get_ifc_statistics_dump();
+ if (res == NULL) {
+ clib_warning("Interface Statistics dump failed.\n");
+ return -1;
+ }
+
+ if (vec_len (res) > 0) {
+ if ((res[0].simple_counter_vec != 0) && (vec_len (res[0].simple_counter_vec) > 0)) {
+ interface_count = vec_len (res[0].simple_counter_vec[0]);
+ } else if ((res[0].combined_counter_vec != 0) && (vec_len (res[0].combined_counter_vec) > 0)) {
+ interface_count = vec_len (res[0].combined_counter_vec[0]);
+ }
+ }
+ vl_api_interface_statistics_details_t ifc_stats[interface_count];
+ memset(ifc_stats, 0, interface_count * sizeof(vl_api_interface_statistics_details_t));
+ ifc_stats->context = my_context_id;
+
+ for (i = 0; i < vec_len (res); i++) {
+ switch (res[i].type) {
+ case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
+ if (res[i].simple_counter_vec == 0)
+ continue;
+ for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++) {
+ set_field(ifc_stats, j, res[i].name, res[i].simple_counter_vec[k][j], 0);
+ }
+ break;
+
+ case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
+ if (res[i].combined_counter_vec == 0)
+ continue;
+ for (k = 0; k < vec_len (res[i].combined_counter_vec); k++)
+ for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++) {
+ set_field(ifc_stats, j, res[i].name, res[i].combined_counter_vec[k][j].packets,
+ res[i].combined_counter_vec[k][j].bytes);
+ }
+ break;
+
+ default:;
+ }
+ }
+ stat_segment_data_free(res);
+ interface_statistics_details_handler(env, ifc_stats, interface_count);
+ return ifc_stats->context;
+} \ No newline at end of file
diff --git a/java/jvpp-stats/jvpp_stats.c b/java/jvpp-stats/jvpp_stats.c
index 78d69f5..d3a0b18 100644
--- a/java/jvpp-stats/jvpp_stats.c
+++ b/java/jvpp-stats/jvpp_stats.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 PANTHEON.tech.
+ * Copyright (c) 2019 PANTHEON.tech., 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.
@@ -14,123 +14,80 @@
* limitations under the License.
*/
-#include "io_fd_jvpp_stats_JVppClientStatsImpl.h"
+#include "io_fd_jvpp_stats_JVppStatsImpl.h"
#include "jvpp_stats.h"
-#include <vpp-api/client/stat_client.h>
+#include "jvpp_interface_stats.h"
#include <vppinfra/vec.h>
#include <vppinfra/format.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+#include <vnet/vnet.h>
+#include <jvpp-common/jvpp_common.h>
+#include <vpp/api/vpe_msg_enum.h>
+#define vl_typedefs /* define message structures */
-void set_field(int_stats_t* stats, int index, const char* field_name, int packets, int bytes) {
- if (strcmp(field_name, "/if/rx-error") == 0) {
- stats[index].rx_errors = packets;
- } else if (strcmp(field_name, "/if/tx-error") == 0) {
- stats[index].tx_errors = packets;
- } else if (strcmp(field_name, "/if/tx") == 0) {
- stats[index].tx_bytes = bytes;
- } else if (strcmp(field_name, "/if/tx-multicast") == 0) {
- stats[index].tx_multicast_pkts= packets;
- } else if (strcmp(field_name, "/if/tx-unicast") == 0) {
- stats[index].tx_unicast_pkts= packets;
- } else if (strcmp(field_name, "/if/tx-broadcast") == 0) {
- stats[index].tx_broadcast_pkts= packets;
- } else if (strcmp(field_name, "/if/rx") == 0) {
- stats[index].rx_bytes = bytes;
- } else if (strcmp(field_name, "/if/rx-multicast") == 0) {
- stats[index].rx_multicast_pkts= packets;
- } else if (strcmp(field_name, "/if/rx-unicast") == 0) {
- stats[index].rx_unicast_pkts= packets;
- } else if (strcmp(field_name, "/if/rx-broadcast") == 0) {
- stats[index].rx_broadcast_pkts= packets;
- }
-}
-stat_segment_data_t* get_statistics_dump() {
- u8 **patterns = 0;
- u32 *dir;
- vec_add1(patterns, (u8*)"/if/rx");
- vec_add1(patterns, (u8*)"/if/tx");
- dir = stat_segment_ls(patterns);
- return stat_segment_dump(dir);
-}
+#include <vpp/api/vpe_all_api_h.h>
-JNIEXPORT jobjectArray JNICALL Java_io_fd_jvpp_stats_JVppClientStatsImpl_interfaceStatisticsDump(JNIEnv *env,
- jclass jclazz) {
+#undef vl_typedefs
- stat_segment_data_t *res;
- int i, j, k, interface_count;
+JNIEXPORT void JNICALL Java_io_fd_jvpp_stats_JVppStatsImpl_init0
+ (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
+ stats_main_t *plugin_main = &stats_main;
+ clib_warning ("Java_io_fd_jvpp_stats_JVppStatsImpl_init0");
- res = get_statistics_dump();
- if (res == NULL) {
- fprintf (stderr, "Interface Statistics dump failed.\n");
- return NULL;
- }
+ plugin_main->my_client_index = my_client_index;
+ plugin_main->vl_input_queue = uword_to_pointer (queue_address, svm_queue_t *);
- if (vec_len (res) > 0) {
- if ((res[0].simple_counter_vec != 0) && (vec_len (res[0].simple_counter_vec) > 0)) {
- interface_count = vec_len (res[0].simple_counter_vec[0]);
- } else if ((res[0].combined_counter_vec != 0) && (vec_len (res[0].combined_counter_vec) > 0)) {
- interface_count = vec_len (res[0].combined_counter_vec[0]);
- }
- }
- int_stats_t ifc_stats[interface_count];
- memset(ifc_stats, 0, interface_count*sizeof(int_stats_t));
+ plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
+ plugin_main->callbackClass = (jclass) (*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
- for (i = 0; i < vec_len (res); i++)
- {
- switch (res[i].type)
- {
- case STAT_DIR_TYPE_COUNTER_VECTOR_SIMPLE:
- if (res[i].simple_counter_vec == 0)
- continue;
- for (k = 0; k < vec_len (res[i].simple_counter_vec); k++)
- for (j = 0; j < vec_len (res[i].simple_counter_vec[k]); j++) {
- set_field(ifc_stats, j, res[i].name, res[i].simple_counter_vec[k][j], 0);
- }
- break;
+ int rv = stat_segment_connect(STAT_SEGMENT_SOCKET_FILE);
+ if (rv < 0) {
+ clib_warning("Couldn't connect to %s. Check if socket exists/permissions.(ret stat: %d)\n",
+ STAT_SEGMENT_SOCKET_FILE, rv);
+ return;
+ }
+}
- case STAT_DIR_TYPE_COUNTER_VECTOR_COMBINED:
- if (res[i].combined_counter_vec == 0)
- continue;
- for (k = 0; k < vec_len (res[i].combined_counter_vec); k++)
- for (j = 0; j < vec_len (res[i].combined_counter_vec[k]); j++) {
- set_field(ifc_stats, j, res[i].name, res[i].combined_counter_vec[k][j].packets,
- res[i].combined_counter_vec[k][j].bytes);
- }
- break;
+JNIEXPORT void JNICALL Java_io_fd_jvpp_stats_JVppStatsImpl_close0
+ (JNIEnv *env, jclass clazz) {
+ stats_main_t *plugin_main = &stats_main;
+ stat_segment_disconnect();
+ // cleanup:
+ (*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
+ (*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
- default:
- ;
- }
- }
- stat_segment_data_free (res);
+ plugin_main->callbackClass = NULL;
+ plugin_main->callbackObject = NULL;
+}
- jobjectArray retArray;
- jclass ifcStatsClass = (*env)->FindClass(env, "io/fd/jvpp/stats/dto/InterfaceStatistics");
- retArray= (jobjectArray)(*env)->NewObjectArray(env, interface_count,ifcStatsClass, NULL);
- jmethodID constructor = (*env)->GetMethodID(env, ifcStatsClass, "<init>", "(IIIIIIIIIII)V");
+JNIEXPORT jint JNICALL Java_io_fd_jvpp_stats_JVppStatsImpl_interfaceStatisticsDump0(JNIEnv *env, jclass jclazz) {
+ return getInterfaceStatisticsDump(env);
+}
- for (int i = 0; i < interface_count; i++) {
- jobject newClientObj = (*env)->NewObject(env, ifcStatsClass, constructor, i,
- ifc_stats[i].tx_errors, ifc_stats[i].tx_multicast_pkts, ifc_stats[i].tx_unicast_pkts,
- ifc_stats[i].tx_broadcast_pkts, ifc_stats[i].tx_bytes, ifc_stats[i].rx_errors, ifc_stats[i].rx_multicast_pkts,
- ifc_stats[i].rx_unicast_pkts, ifc_stats[i].rx_broadcast_pkts, ifc_stats[i].rx_bytes);
- (*env)->SetObjectArrayElement(env,retArray,i,newClientObj);
+/* Attach thread to JVM and cache class references when initiating JVPP Stats */
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ JNIEnv *env;
+ if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_8) != JNI_OK) {
+ return JNI_EVERSION;
}
- return retArray;
-}
-JNIEXPORT jint JNICALL Java_io_fd_jvpp_stats_JVppClientStatsImpl_statSegmentConnect(JNIEnv *env, jclass jclazz) {
- u8 * stat_segment_name = (u8 *) STAT_SEGMENT_SOCKET_FILE;
- int rv = stat_segment_connect((char*)stat_segment_name);
- if (rv < 0) {
- fprintf(stderr, "Couldn't connect to %s. Check if socket exists/permissions.(ret stat: %d)\n",
- stat_segment_name, rv);
- return -1;
+ if (cache_class_references(env) != 0) {
+ clib_warning ("Failed to cache class references\n");
+ return JNI_ERR;
}
- return 0;
-}
-JNIEXPORT void JNICALL Java_io_fd_jvpp_stats_JVppClientStatsImpl_statSegmentDisconnect(JNIEnv *env, jclass jclazz) {
- stat_segment_disconnect();
- return;
+ return JNI_VERSION_1_8;
}
+
+/* Clean up cached references when disposing JVPP Stats */
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+ JNIEnv *env;
+ if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_8) != JNI_OK) {
+ return;
+ }
+ delete_class_references(env);
+} \ No newline at end of file
diff --git a/java/jvpp-stats/jvpp_stats.h b/java/jvpp-stats/jvpp_stats.h
index 7014101..d38414b 100644
--- a/java/jvpp-stats/jvpp_stats.h
+++ b/java/jvpp-stats/jvpp_stats.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 PANTHEON.tech.
+ * Copyright (c) 2019 PANTHEON.tech., 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.
@@ -17,20 +17,32 @@
#ifndef PROJECT_JVPP_STATS_H
#define PROJECT_JVPP_STATS_H
-#endif //PROJECT_JVPP_STATS_H
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/api_errno.h>
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <jni.h>
+
+/* Global state for JVPP-STATS */
+typedef struct {
+ /* Pointer to shared memory queue */
+ svm_queue_t *vl_input_queue;
+
+ /* VPP api client index */
+ u32 my_client_index;
+
+ /* Callback object and class references enabling asynchronous Java calls */
+ jobject callbackObject;
+ jclass callbackClass;
+} stats_main_t;
+stats_main_t stats_main __attribute__((aligned (64)));
-typedef struct interface_statistics{
- int rx_errors;
- int rx_bytes;
- int rx_unicast_pkts;
- int rx_broadcast_pkts;
- int rx_multicast_pkts;
- int tx_errors;
- int tx_bytes;
- int tx_unicast_pkts;
- int tx_broadcast_pkts;
- int tx_multicast_pkts;
-} int_stats_t;
-
-int_stats_t interface_statistics __attribute__((aligned (64)));
+JNIEXPORT void JNICALL Java_io_fd_jvpp_stats_JVppStatsImpl_init0(JNIEnv *, jclass, jobject, jlong, jint);
+
+JNIEXPORT void JNICALL Java_io_fd_jvpp_stats_JVppStatsImpl_close0(JNIEnv *, jclass);
+
+JNIEXPORT jint JNICALL Java_io_fd_jvpp_stats_JVppStatsImpl_interfaceStatisticsDump0(JNIEnv *, jclass);
+
+#endif //PROJECT_JVPP_STATS_H
diff --git a/java/jvpp-stats/jvpp_stats_registry.c b/java/jvpp-stats/jvpp_stats_registry.c
new file mode 100644
index 0000000..3b2db38
--- /dev/null
+++ b/java/jvpp-stats/jvpp_stats_registry.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#include <vnet/vnet.h>
+
+#include <jni.h>
+#include "io_fd_jvpp_stats_VppStatsJNIConnection.h"
+#include "io_fd_jvpp_stats_JVppStatsRegistryImpl.h"
+#include <jvpp-common/jvpp_common.h>
+
+/*
+ * The Java runtime isn't compile w/ -fstack-protector,
+ * so we have to supply missing external references for the
+ * regular vpp libraries.
+ */
+void __stack_chk_guard(void) __attribute__((weak));
+void __stack_chk_guard(void) {
+}
+
+typedef struct {
+ jobject registryObject;
+ jclass registryClass;
+ jclass callbackExceptionClass;
+
+ /* Connected indication */
+ volatile u8 is_connected;
+ u32 vpe_pid;
+} jvpp_stats_registry_main_t;
+
+jvpp_stats_registry_main_t jvpp_stats_registry_main __attribute__((aligned (64)));
+
+// JAVA class reference cache
+jclass connectionInfoClass;
+jclass callbackExceptionClass;
+
+JNIEXPORT jint JNICALL Java_io_fd_jvpp_stats_JVppStatsRegistryImpl_controlPing0(JNIEnv *env, jobject registryObject) {
+ u32 my_context_id = vppjni_get_context_id(&jvpp_main);
+ jvpp_stats_registry_main_t *rm = &jvpp_stats_registry_main;
+
+ if (rm->registryObject == 0) {
+ rm->registryObject = (*env)->NewGlobalRef(env, registryObject);
+ }
+ if (rm->registryClass == 0) {
+ rm->registryClass = (jclass) (*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, registryObject));
+ }
+
+ rm->is_connected = 1;
+ return my_context_id;
+}
+
+JNIEXPORT jobject JNICALL
+Java_io_fd_jvpp_stats_VppStatsJNIConnection_statsConnect(JNIEnv *env, jclass obj, jstring clientName) {
+ // int rv;
+ const char *client_name;
+ jvpp_main_t * jm = &jvpp_main;
+ jobject con_info;
+ jthrowable exc;
+ jvpp_stats_registry_main_t * rm = &jvpp_stats_registry_main;
+
+ jmethodID connectionInfoConstructor = (*env)->GetMethodID(env, connectionInfoClass, "<init>", "(JIII)V");
+ exc = (*env)->ExceptionOccurred(env);
+ if (exc) {
+ clib_warning(
+ "Unable to extract connectionInfoClass constructor. Ignoring message.\n");
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+
+ con_info = (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, 0, 0,
+ VNET_API_ERROR_INVALID_REGISTRATION, 0);
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ clib_warning("StatsConnectionInfo: Invalid Registration!\n");
+ }
+ return con_info;
+ }
+
+ if (rm->is_connected) {
+ con_info = (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, 0, 0,
+ VNET_API_ERROR_ALREADY_CONNECTED, 0);
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ clib_warning("StatsConnectionInfo: Already connected.\n");
+ }
+ return con_info;
+ }
+
+ client_name = (*env)->GetStringUTFChars(env, clientName, 0);
+
+ if (!client_name) {
+ con_info = (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, 0, 0,
+ VNET_API_ERROR_INVALID_VALUE, 0);
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ clib_warning("StatsConnectionInfo: Invalid Client name!\n");
+ }
+ return con_info;
+ }
+
+ (*env)->ReleaseStringUTFChars(env, clientName, client_name);
+
+ con_info = (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor,
+ (jlong) pointer_to_uword(jm->vl_input_queue), (jint) jm->my_client_index, (jint) 0,
+ (jint) rm->vpe_pid);
+
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ clib_warning("StatsConnectionInfo: Unspecified Error! Exiting.\n");
+ return NULL;
+ }
+
+ return con_info;
+}
+
+
+JNIEXPORT void JNICALL Java_io_fd_jvpp_stats_VppStatsJNIConnection_statsDisconnect(
+ JNIEnv *env, jclass clazz) {
+ jvpp_stats_registry_main_t * rm = &jvpp_stats_registry_main;
+ rm->is_connected = 0; // TODO make thread safe
+ // cleanup:
+ if (rm->registryObject) {
+ (*env)->DeleteGlobalRef(env, rm->registryObject);
+ rm->registryObject = 0;
+ }
+ if (rm->registryClass) {
+ (*env)->DeleteGlobalRef(env, rm->registryClass);
+ rm->registryClass = 0;
+ }
+}
+
+static int cache_class_references(JNIEnv *env) {
+ connectionInfoClass =
+ (jclass) (*env)->NewGlobalRef(env, (*env)->FindClass(env,
+ "io/fd/jvpp/stats/VppStatsJNIConnection$StatsConnectionInfo"));
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ return JNI_ERR;
+ }
+
+ callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env,
+ (*env)->FindClass(env, "io/fd/jvpp/VppCallbackException"));
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ return JNI_ERR;
+ }
+ return 0;
+}
+
+static void delete_class_references(JNIEnv *env) {
+
+ if (connectionInfoClass) {
+ (*env)->DeleteGlobalRef(env, connectionInfoClass);
+ }
+ if (callbackExceptionClass) {
+ (*env)->DeleteGlobalRef(env, callbackExceptionClass);
+ }
+}
+
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+ jvpp_main_t * jm = &jvpp_main;
+ jvpp_stats_registry_main_t * rm = &jvpp_stats_registry_main;
+ JNIEnv* env;
+
+ if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+ return JNI_EVERSION;
+ }
+
+ if (cache_class_references(env) != 0) {
+ clib_warning ("Failed to cache class references\n");
+ return JNI_ERR;
+ }
+
+ rm->callbackExceptionClass = connectionInfoClass;
+ jm->jvm = vm;
+
+ if (cache_class_references(env) != 0) {
+ clib_warning ("Failed to cache class references\n");
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_8;
+}
+
+void JNI_OnUnload(JavaVM *vm, void *reserved) {
+ jvpp_main_t * jm = &jvpp_main;
+ jvpp_stats_registry_main_t * rm = &jvpp_stats_registry_main;
+
+ JNIEnv* env;
+ if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
+ return;
+ }
+
+ delete_class_references(env);
+ rm->callbackExceptionClass = NULL;
+
+ jm->jenv = NULL;
+ jm->jvm = NULL;
+}