9e07cc768c407f370890d4213c5d0aec775eddea
[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.Set;
29 import java.util.logging.Logger;
30 import org.openvpp.jvpp.callback.JVppCallback;
31
32 /**
33  * JNI based representation of a management connection to VPP
34  */
35 public final class VppJNIConnection implements VppConnection {
36     private final static Logger LOG = Logger.getLogger(VppJNIConnection.class.getName());
37     private static final String LIBNAME = "libjvpp.so.0.0.0";
38
39     static {
40         try {
41             loadLibrary();
42         } catch (Exception e) {
43             LOG.severe("Can't find vpp jni library: " + LIBNAME);
44             throw new ExceptionInInitializerError(e);
45         }
46     }
47
48     private static void loadStream(final InputStream is) throws IOException {
49         final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
50         final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
51         try {
52             Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
53
54             try {
55                 Runtime.getRuntime().load(p.toString());
56             } catch (UnsatisfiedLinkError e) {
57                 throw new IOException("Failed to load library " + p, e);
58             }
59         } finally {
60             try {
61                 Files.deleteIfExists(p);
62             } catch (IOException e) {
63             }
64         }
65     }
66
67     private static void loadLibrary() throws IOException {
68         try (final InputStream is = VppJNIConnection.class.getResourceAsStream('/' + LIBNAME)) {
69             if (is == null) {
70                 throw new IOException("Failed to open library resource " + LIBNAME);
71             }
72             loadStream(is);
73         }
74     }
75
76     private final String clientName;
77     private volatile boolean disconnected = false;
78
79     private VppJNIConnection(final String clientName) {
80         if (clientName == null) {
81             throw new NullPointerException("Null clientName");
82         }
83         this.clientName = clientName;
84     }
85
86     /**
87      * Guarded by VppJNIConnection.class
88      */
89     private static final Map<String, VppJNIConnection> connections = new HashMap<>();
90
91     /**
92      * Create a new Vpp connection identified by clientName parameter.
93      *
94      * Multiple instances are allowed since this class is not a singleton
95      * (VPP allows multiple management connections).
96      *
97      * However only a single connection per clientName is allowed.
98      *
99      * @param clientName identifier of vpp connection
100      * @param callback global callback to receive response calls from vpp
101      *
102      * @return new Vpp connection
103      * @throws IOException in case the connection could not be established, or there already is a connection with the same name
104      */
105     public static VppJNIConnection create(final String clientName, final JVppCallback callback) throws IOException {
106         synchronized (VppJNIConnection.class) {
107             if(connections.containsKey(clientName)) {
108                 throw new IOException("Client " + clientName + " already connected");
109             }
110
111             final VppJNIConnection vppJNIConnection = new VppJNIConnection(clientName);
112             final int ret = clientConnect(clientName, callback);
113             if (ret != 0) {
114                 throw new IOException("Connection returned error " + ret);
115             }
116             connections.put(clientName, vppJNIConnection);
117             return vppJNIConnection;
118         }
119     }
120
121     @Override
122     public final void checkActive() {
123         if (disconnected) {
124             throw new IllegalStateException("Disconnected client " + clientName);
125         }
126     }
127
128     @Override
129     public synchronized final void close() {
130         if (!disconnected) {
131             disconnected = true;
132             try {
133                 clientDisconnect();
134             } finally {
135                 synchronized (VppJNIConnection.class) {
136                     connections.remove(clientName);
137                 }
138             }
139         }
140     }
141
142     private static native int clientConnect(String clientName, JVppCallback callback);
143     private static native void clientDisconnect();
144 }