1+ /*
2+ * Class NativeUtils is published under the The MIT License:
3+ *
4+ * Copyright (c) 2012 Adam Heinrich <adam@adamh.cz>
5+ *
6+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7+ * of this software and associated documentation files (the "Software"), to deal
8+ * in the Software without restriction, including without limitation the rights
9+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+ * copies of the Software, and to permit persons to whom the Software is
11+ * furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in all
14+ * copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+ * SOFTWARE.
23+ */
24+ package org .nethergames .proxytransport .utils ;
25+
26+ import java .io .*;
27+ import java .nio .file .FileSystemNotFoundException ;
28+ import java .nio .file .FileSystems ;
29+ import java .nio .file .Files ;
30+ import java .nio .file .ProviderNotFoundException ;
31+ import java .nio .file .StandardCopyOption ;
32+
33+ /**
34+ * A simple library class which helps with loading dynamic libraries stored in the
35+ * JAR archive. These libraries usually contain implementation of some methods in
36+ * native code (using JNI - Java Native Interface).
37+ *
38+ * @see <a href="http://adamheinrich.com/blog/2012/how-to-load-native-jni-library-from-jar">http://adamheinrich.com/blog/2012/how-to-load-native-jni-library-from-jar</a>
39+ * @see <a href="https://github.com/adamheinrich/native-utils">https://github.com/adamheinrich/native-utils</a>
40+ *
41+ */
42+ public class NativeUtils {
43+
44+ /**
45+ * The minimum length a prefix for a file has to have according to {@link File#createTempFile(String, String)}}.
46+ */
47+ private static final int MIN_PREFIX_LENGTH = 3 ;
48+ public static final String NATIVE_FOLDER_PATH_PREFIX = "nativeutils" ;
49+
50+ /**
51+ * Temporary directory which will contain the DLLs.
52+ */
53+ private static File temporaryDir ;
54+
55+ /**
56+ * Private constructor - this class will never be instanced
57+ */
58+ private NativeUtils () {
59+ }
60+
61+ /**
62+ * Loads library from current JAR archive
63+ *
64+ * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after
65+ * exiting.
66+ * Method uses String as filename because the pathname is "abstract", not system-dependent.
67+ *
68+ * @param path The path of file inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext
69+ * @throws IOException If temporary file creation or read/write operation fails
70+ * @throws IllegalArgumentException If source file (param path) does not exist
71+ * @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters
72+ * (restriction of {@link File#createTempFile(java.lang.String, java.lang.String)}).
73+ * @throws FileNotFoundException If the file could not be found inside the JAR.
74+ */
75+ public static void loadLibraryFromJar (String path ) throws IOException {
76+
77+ if (null == path || !path .startsWith ("/" )) {
78+ throw new IllegalArgumentException ("The path has to be absolute (start with '/')." );
79+ }
80+
81+ // Obtain filename from path
82+ String [] parts = path .split ("/" );
83+ String filename = (parts .length > 1 ) ? parts [parts .length - 1 ] : null ;
84+
85+ // Check if the filename is okay
86+ if (filename == null || filename .length () < MIN_PREFIX_LENGTH ) {
87+ throw new IllegalArgumentException ("The filename has to be at least 3 characters long." );
88+ }
89+
90+ // Prepare temporary file
91+ if (temporaryDir == null ) {
92+ temporaryDir = createTempDirectory (NATIVE_FOLDER_PATH_PREFIX );
93+ temporaryDir .deleteOnExit ();
94+ }
95+
96+ File temp = new File (temporaryDir , filename );
97+
98+ try (InputStream is = NativeUtils .class .getResourceAsStream (path )) {
99+ Files .copy (is , temp .toPath (), StandardCopyOption .REPLACE_EXISTING );
100+ } catch (IOException e ) {
101+ temp .delete ();
102+ throw e ;
103+ } catch (NullPointerException e ) {
104+ temp .delete ();
105+ throw new FileNotFoundException ("File " + path + " was not found inside JAR." );
106+ }
107+
108+ try {
109+ System .load (temp .getAbsolutePath ());
110+ } finally {
111+ if (isPosixCompliant ()) {
112+ // Assume POSIX compliant file system, can be deleted after loading
113+ temp .delete ();
114+ } else {
115+ // Assume non-POSIX, and don't delete until last file descriptor closed
116+ temp .deleteOnExit ();
117+ }
118+ }
119+ }
120+
121+ private static boolean isPosixCompliant () {
122+ try {
123+ return FileSystems .getDefault ()
124+ .supportedFileAttributeViews ()
125+ .contains ("posix" );
126+ } catch (FileSystemNotFoundException
127+ | ProviderNotFoundException
128+ | SecurityException e ) {
129+ return false ;
130+ }
131+ }
132+
133+ private static File createTempDirectory (String prefix ) throws IOException {
134+ String tempDir = System .getProperty ("java.io.tmpdir" );
135+ File generatedDir = new File (tempDir , prefix + System .nanoTime ());
136+
137+ if (!generatedDir .mkdir ())
138+ throw new IOException ("Failed to create temp directory " + generatedDir .getName ());
139+
140+ return generatedDir ;
141+ }
142+ }
0 commit comments