VPP-330 Track pending map-requests with a fifo
[vpp.git] / vpp-api / java / jvpp / org / openvpp / jvpp / VppJNIConnection.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.openvpp.jvpp;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.nio.file.Files;
22 import java.nio.file.Path;
23 import java.nio.file.StandardCopyOption;
24 import java.nio.file.attribute.PosixFilePermission;
25 import java.nio.file.attribute.PosixFilePermissions;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Set;
30 import java.util.logging.Logger;
31 import org.openvpp.jvpp.callback.JVppCallback;
32
33 /**
34  * JNI based representation of a management connection to VPP
35  */
36 public final class VppJNIConnection implements VppConnection {
37     private final static Logger LOG = Logger.getLogger(VppJNIConnection.class.getName());
38     private static final String LIBNAME = "libjvpp.so.0.0.0";
39
40     static {
41         try {
42             loadLibrary();
43         } catch (Exception e) {
44             LOG.severe("Can't find vpp jni library: " + LIBNAME);
45             throw new ExceptionInInitializerError(e);
46         }
47     }
48
49     private static void loadStream(final InputStream is) throws IOException {
50         final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
51         final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
52         try {
53             Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
54
55             try {
56                 Runtime.getRuntime().load(p.toString());
57             } catch (UnsatisfiedLinkError e) {
58                 throw new IOException("Failed to load library " + p, e);
59             }
60         } finally {
61             try {
62                 Files.deleteIfExists(p);
63             } catch (IOException e) {
64             }
65         }
66     }
67
68     private static void loadLibrary() throws IOException {
69         try (final InputStream is = VppJNIConnection.class.getResourceAsStream('/' + LIBNAME)) {
70             if (is == null) {
71                 throw new IOException("Failed to open library resource " + LIBNAME);
72             }
73             loadStream(is);
74         }
75     }
76
77     private final String clientName;
78     private volatile boolean disconnected = false;
79
80     /**
81      * Create VPPJNIConnection instance for client connecting to VPP.
82      *
83      * @param clientName client name instance to be used for communication. Single connection per clientName is allowed.
84      */
85     public VppJNIConnection(final String clientName) {
86         this.clientName = Objects.requireNonNull(clientName,"Null clientName");
87     }
88
89     /**
90      * Guarded by VppJNIConnection.class
91      */
92     private static final Map<String, VppJNIConnection> connections = new HashMap<>();
93
94     /**
95      * Initiate VPP connection for current instance
96      *
97      * Multiple instances are allowed since this class is not a singleton
98      * (VPP allows multiple management connections).
99      *
100      * However only a single connection per clientName is allowed.
101      *
102      * @param callback global callback to receive response calls from vpp
103      *
104      * @throws IOException in case the connection could not be established
105      */
106     public void connect(final JVppCallback callback) throws IOException {
107         synchronized (VppJNIConnection.class) {
108             if(connections.containsKey(clientName)) {
109                 throw new IOException("Client " + clientName + " already connected");
110             }
111
112             final int ret = clientConnect(clientName, callback);
113             if (ret != 0) {
114                 throw new IOException("Connection returned error " + ret);
115             }
116             connections.put(clientName, this);
117         }
118     }
119
120     @Override
121     public final void checkActive() {
122         if (disconnected) {
123             throw new IllegalStateException("Disconnected client " + clientName);
124         }
125     }
126
127     @Override
128     public synchronized final void close() {
129         if (!disconnected) {
130             disconnected = true;
131             try {
132                 clientDisconnect();
133             } finally {
134                 synchronized (VppJNIConnection.class) {
135                     connections.remove(clientName);
136                 }
137             }
138         }
139     }
140
141     private static native int clientConnect(String clientName, JVppCallback callback);
142     private static native void clientDisconnect();
143 }