From 9c6ac1db63e54e753ebdc2aa3fc73c68ef2a2247 Mon Sep 17 00:00:00 2001 From: Michal Cmarada Date: Fri, 3 May 2019 11:29:04 +0200 Subject: Implement callbacks and registry for stats Change-Id: I9e562482195fcd5f54634cfff29df4358b78ed28 Signed-off-by: Michal Cmarada --- .../io/fd/jvpp/stats/JVppClientStatsImpl.java | 90 ----------- java/jvpp-stats/io/fd/jvpp/stats/JVppStats.java | 31 ++++ .../jvpp-stats/io/fd/jvpp/stats/JVppStatsImpl.java | 121 +++++++++++++++ .../io/fd/jvpp/stats/JVppStatsRegistryImpl.java | 96 ++++++++++++ .../io/fd/jvpp/stats/VppStatsJNIConnection.java | 142 +++++++++++++++++ .../InterfaceStatisticsDetailsCallback.java | 25 +++ .../stats/callback/JVppStatsGlobalCallback.java | 23 +++ .../jvpp/stats/callfacade/CallbackJVppStats.java | 28 ++++ .../stats/callfacade/CallbackJVppStatsFacade.java | 61 ++++++++ .../CallbackJVppStatsFacadeCallback.java | 67 ++++++++ .../io/fd/jvpp/stats/dto/InterfaceStatistics.java | 101 ++++++------ .../jvpp/stats/dto/InterfaceStatisticsDetails.java | 71 +++++++++ .../dto/InterfaceStatisticsDetailsReplyDump.java | 54 +++++++ .../fd/jvpp/stats/dto/InterfaceStatisticsDump.java | 51 +++++++ .../stats/future/AbstractFutureJVppInvoker.java | 170 +++++++++++++++++++++ .../io/fd/jvpp/stats/future/FutureJVppInvoker.java | 48 ++++++ .../io/fd/jvpp/stats/future/FutureJVppStats.java | 28 ++++ .../jvpp/stats/future/FutureJVppStatsFacade.java | 53 +++++++ .../future/FutureJVppStatsFacadeCallback.java | 81 ++++++++++ .../io/fd/jvpp/stats/test/FutureApiTest.java | 62 ++++++++ .../io/fd/jvpp/stats/test/JvppStatsApiTest.java | 44 ------ java/jvpp-stats/io/fd/jvpp/stats/test/Readme.txt | 9 +- 22 files changed, 1264 insertions(+), 192 deletions(-) delete mode 100644 java/jvpp-stats/io/fd/jvpp/stats/JVppClientStatsImpl.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/JVppStats.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/JVppStatsImpl.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/JVppStatsRegistryImpl.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/VppStatsJNIConnection.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/callback/InterfaceStatisticsDetailsCallback.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/callback/JVppStatsGlobalCallback.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStats.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacade.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/callfacade/CallbackJVppStatsFacadeCallback.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetails.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDetailsReplyDump.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/dto/InterfaceStatisticsDump.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/future/AbstractFutureJVppInvoker.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppInvoker.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStats.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacade.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/future/FutureJVppStatsFacadeCallback.java create mode 100644 java/jvpp-stats/io/fd/jvpp/stats/test/FutureApiTest.java delete mode 100644 java/jvpp-stats/io/fd/jvpp/stats/test/JvppStatsApiTest.java (limited to 'java/jvpp-stats/io/fd/jvpp') 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 getInterfaceStatistics() { - if (!this.connected) { - LOG.severe("Unable to dump statistics. Client isn't connected. Try reconnecting."); - return null; - } - InterfaceStatistics[] statDump = interfaceStatisticsDump(); - List 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; + +/** + *

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 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 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 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 connections = new HashMap<>(); + + /** + * Initiate VPP connection for current instance + *

+ * Multiple instances are allowed since this class is not a singleton (VPP allows multiple management connections). + *

+ * 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; + +/** + *

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; + +/** + *

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; + +/** + *

Default implementation of CallbackStatsJVpp interface. + */ +public final class CallbackJVppStatsFacade implements CallbackJVppStats { + + private final io.fd.jvpp.stats.JVppStats jvpp; + private final java.util.Map callbacks; + + /** + *

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; + +/** + *

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 requests; + + public CallbackJVppStatsFacadeCallback(final java.util.Map 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; + +/** + *

This class represents reply DTO for InterfaceStatisticsDetails. + */ +public final class InterfaceStatisticsDetails implements io.fd.jvpp.dto.JVppReply { + 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; + +/** + *

This class represents dump reply wrapper for InterfaceStatisticsDetails. + */ +public final class InterfaceStatisticsDetailsReplyDump + implements io.fd.jvpp.dto.JVppReplyDump { + + 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; + +/** + *

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>> requests; + + protected AbstractFutureJVppInvoker(final JVpp jvpp, final JVppRegistry registry, + final Map>> 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>> getRequests() { + synchronized (requests) { + return requests; + } + } + + // TODO use Optional in Future, java8 + + @Override + @SuppressWarnings("unchecked") + public > CompletionStage 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 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> 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) 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 replyCompletableFuture = new CompletableFuture<>(); + replyCompletableFuture.completeExceptionally(ex); + return replyCompletableFuture; + } + } + + @Override + @SuppressWarnings("unchecked") + public , DUMP extends JVppReplyDump> CompletionStage 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 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> 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) 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 replyCompletableFuture = new CompletableFuture<>(); + replyCompletableFuture.completeExceptionally(ex); + return replyCompletableFuture; + } + } + + public static final class CompletableDumpFuture> extends CompletableFuture { + 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 + */ + > CompletionStage 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 + */ + , DUMP extends JVppReplyDump> CompletionStage 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; + +/** + *

Async facade extension adding specific methods for each request invocation + */ +public interface FutureJVppStats extends io.fd.jvpp.stats.future.FutureJVppInvoker { + + java.util.concurrent.CompletionStage 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; + +/** + *

Implementation of FutureJVpp based on AbstractFutureJVppInvoker + */ +public class FutureJVppStatsFacade extends io.fd.jvpp.stats.future.AbstractFutureJVppInvoker + implements FutureJVppStats { + + /** + *

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 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; + +/** + *

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>> + requests; + + public FutureJVppStatsFacadeCallback( + final java.util.Map>> requestMap) { + this.requests = requestMap; + } + + @Override + @SuppressWarnings("unchecked") + public void onError(io.fd.jvpp.VppCallbackException reply) { + final java.util.concurrent.CompletableFuture> completableFuture; + + synchronized (requests) { + completableFuture = (java.util.concurrent.CompletableFuture>) 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 + 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) 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 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 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 -- cgit 1.2.3-korg