BUG-634: Fix .so not being included in the .jar 01/6301/3
authorRobert Varga <rovarga@cisco.com>
Thu, 17 Apr 2014 13:24:55 +0000 (15:24 +0200)
committerRobert Varga <rovarga@cisco.com>
Tue, 22 Apr 2014 09:16:39 +0000 (11:16 +0200)
This patch ensures the native dynamic library is included in the
tcpmd5-jni jar file. Also changes packaging to bundle, implements proper
library loading and adds a unit test to validate basic functions.

Change-Id: I966ef49723de25ae05f75f719a2e0169d161f773
Signed-off-by: Robert Varga <rovarga@cisco.com>
tcp-md5/jni/.project
tcp-md5/jni/pom.xml
tcp-md5/jni/src/main/java/org/opendaylight/bgpcep/tcpmd5/jni/NativeKeyAccess.java
tcp-md5/jni/src/test/java/org/opendaylight/bgpcep/tcpmd5/jni/NativeKeyAccessTest.java [new file with mode: 0644]

index 31485997d9e48cfd1309e4fa85bfc0a85a9656f6..8e7a248ac1c44f3f4e04c54ef9e67cce8a698bbe 100644 (file)
@@ -24,6 +24,7 @@
                </buildCommand>
        </buildSpec>
        <natures>
+               <nature>org.eclipse.pde.PluginNature</nature>
                <nature>org.eclipse.jdt.core.javanature</nature>
                <nature>org.eclipse.m2e.core.maven2Nature</nature>
        </natures>
index bcf4e4c61baf97d91008ebe21d5867fb1e113b2f..1ae3356c5d615a0d6142a814daa925bb2f4abc6f 100644 (file)
@@ -19,7 +19,7 @@
     <modelVersion>4.0.0</modelVersion>
     <artifactId>tcpmd5-jni</artifactId>
     <description>Native support for RFC2385-enabled TCP sockets</description>
-    <packaging>jar</packaging>
+    <packaging>bundle</packaging>
     <name>${project.artifactId}</name>
 
     <prerequisites>
@@ -29,6 +29,7 @@
     <properties>
         <nar.groupId>com.github.maven-nar</nar.groupId>
         <nar.version>3.0.0</nar.version>
+        <copy.version>0.2.5</copy.version>
     </properties>
 
     <dependencies>
                         <Export-Package>
                             org.opendaylight.bgpcep.jni
                         </Export-Package>
+                        <Include-Resource>
+                            libtcpmd5-jni.so=target/nar/tcpmd5-jni-${project.version}-amd64-Linux-gpp-jni/lib/amd64-Linux-gpp/jni/libtcpmd5-jni-${project.version}.so
+                        </Include-Resource>
                     </instructions>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>com.github.goldin</groupId>
+                <artifactId>copy-maven-plugin</artifactId>
+                <version>0.2.5</version>
+                <executions>
+                    <execution>
+                        <id>copy-native</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>copy</goal>
+                        </goals>
+                        <configuration>
+                            <resources>
+                                <resource>
+                                    <targetPath>${project.build.outputDirectory}</targetPath>
+                                    <file>${project.build.directory}/nar/tcpmd5-jni-${project.version}-amd64-Linux-gpp-jni/lib/amd64-Linux-gpp/jni/libtcpmd5-jni-${project.version}.so</file>
+                                    <destFileName>libtcpmd5-jni.so</destFileName>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
             <plugin>
                 <!-- Let eclipse know about the generated sources -->
                 <groupId>org.codehaus.mojo</groupId>
                     </execution>
                 </executions>
             </plugin>
+
             <plugin>
                 <groupId>${nar.groupId}</groupId>
                 <artifactId>nar-maven-plugin</artifactId>
                             <goal>nar-resources</goal>
                             <goal>nar-javah</goal>
                             <goal>nar-compile</goal>
-                            <goal>nar-package</goal>
                         </goals>
                     </execution>
                 </executions>
                                         </goals>
                                     </pluginExecutionFilter>
                                     <action>
-                                        <ignore/>
+                                        <execute/>
+                                    </action>
+                                </pluginExecution>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>com.github.goldin</groupId>
+                                        <artifactId>copy-maven-plugin</artifactId>
+                                        <versionRange>${copy.version}</versionRange>
+                                        <goals>
+                                            <goal>copy</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <execute/>
                                     </action>
                                 </pluginExecution>
                             </pluginExecutions>
index e0fa715a68d2c2b594d47d52a0d0bb88e1126d79..1a6147e72d680c5a35b566a5e2e021b551d90a98 100644 (file)
@@ -8,7 +8,14 @@
 package org.opendaylight.bgpcep.tcpmd5.jni;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.channels.Channel;
+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 org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -20,16 +27,56 @@ import com.google.common.base.Preconditions;
  * directly to the underlying operating system.
  */
 public final class NativeKeyAccess implements KeyAccess {
+       private static final String LIBNAME = "libtcpmd5-jni.so";
        private static final Logger LOG = LoggerFactory.getLogger(NativeKeyAccess.class);
+       private static boolean AVAILABLE = false;
+
+       private static InputStream getLibraryStream() {
+               return Preconditions.checkNotNull(NativeKeyAccess.class.getResourceAsStream('/' + LIBNAME),
+                               String.format("Failed to open library resource %s", LIBNAME));
+       }
 
        static {
-               NarSystem.loadLibrary();
+               final Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
+
+               try (final InputStream is = getLibraryStream()) {
+                       try {
+                               final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms));
+
+                               LOG.info("Copying {} to {}", is, p);
 
-               int rt = NarSystem.runUnitTests();
-               if (rt == 0) {
-                       LOG.warn("Run-time initialization failed");
-               } else {
-                       LOG.debug("Run-time found {} supported channel classes", rt);
+                               try {
+                                       Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING);
+
+                                       try {
+                                               Runtime.getRuntime().load(p.toString());
+
+                                               LOG.info("Library {} loaded", p);
+
+                                               int rt = NarSystem.runUnitTests();
+                                               if (rt == 0) {
+                                                       LOG.warn("Run-time initialization failed");
+                                               } else {
+                                                       LOG.debug("Run-time found {} supported channel classes", rt);
+                                                       AVAILABLE = true;
+                                               }
+                                       } catch (RuntimeException e) {
+                                               LOG.error("Failed to load native library", e);
+                                       }
+                               } catch (IOException e) {
+                                       LOG.error("Failed to extract native library", e);
+                               } finally {
+                                       try {
+                                               Files.deleteIfExists(p);
+                                       } catch (IOException e) {
+                                               LOG.warn("Failed to remove temporary file", e);
+                                       }
+                               }
+                       } catch (IOException e2) {
+                               LOG.error("Failed to create temporary file {}", LIBNAME, e2);
+                       }
+               } catch (IOException e1) {
+                       LOG.error("Failed to find native library {}", LIBNAME, e1);
                }
        }
 
@@ -44,15 +91,31 @@ public final class NativeKeyAccess implements KeyAccess {
        }
 
        public static KeyAccess create(final Channel channel) {
-               if (isClassSupported0(channel.getClass())) {
-                       return new NativeKeyAccess(channel);
-               } else {
+               if (!AVAILABLE) {
+                       LOG.debug("Native library not available");
                        return null;
                }
+
+               if (!isClassSupported0(channel.getClass())) {
+                       LOG.debug("No support available for class {}", channel.getClass());
+                       return null;
+               }
+
+               return new NativeKeyAccess(channel);
        }
 
        public static boolean isAvailableForClass(final Class<?> clazz) {
-               return isClassSupported0(clazz);
+               if (!AVAILABLE) {
+                       LOG.debug("Native library not available");
+                       return false;
+               }
+
+               if (!isClassSupported0(clazz)) {
+                       LOG.debug("No support available for class {}", clazz);
+                       return false;
+               }
+
+               return true;
        }
 
        @Override
diff --git a/tcp-md5/jni/src/test/java/org/opendaylight/bgpcep/tcpmd5/jni/NativeKeyAccessTest.java b/tcp-md5/jni/src/test/java/org/opendaylight/bgpcep/tcpmd5/jni/NativeKeyAccessTest.java
new file mode 100644 (file)
index 0000000..9375263
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.bgpcep.tcpmd5.jni;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import org.junit.Test;
+
+public class NativeKeyAccessTest {
+
+       @Test
+       public void testAvailability() throws IOException {
+               final SocketChannel sc = SocketChannel.open();
+
+               assertTrue(NativeKeyAccess.isAvailableForClass(sc.getClass()));
+       }
+
+
+       @Test
+       public void testServerAvailability() throws IOException {
+               final ServerSocketChannel ssc = ServerSocketChannel.open();
+
+               assertTrue(NativeKeyAccess.isAvailableForClass(ssc.getClass()));
+       }
+}