|
1 | 1 | package org.rapidyaml; |
2 | 2 |
|
3 | | -import java.io.*; |
4 | | -import java.nio.file.FileSystemNotFoundException; |
5 | | -import java.nio.file.FileSystems; |
| 3 | +import java.io.File; |
| 4 | +import java.io.InputStream; |
| 5 | +import java.io.IOException; |
| 6 | + |
6 | 7 | import java.nio.file.Files; |
7 | | -import java.nio.file.ProviderNotFoundException; |
8 | 8 | import java.nio.file.StandardCopyOption; |
9 | 9 |
|
10 | | -import java.net.URL; |
11 | | -import java.net.URLConnection; |
12 | | -import java.nio.file.Path; |
13 | | -import java.nio.file.Paths; |
14 | | -import java.util.UUID; |
15 | | -import java.util.stream.Stream; |
16 | | -import java.text.MessageFormat; |
17 | | - |
18 | | -public class NativeLibLoader { |
19 | | - private static File tempdir; |
20 | | - private static final String LOCK_EXT = ".lck"; |
21 | | - |
22 | | - private NativeLibLoader() {} |
23 | | - |
24 | | - |
25 | | - /** |
26 | | - * Extracts and loads the specified library file to the target folder |
27 | | - * |
28 | | - * @param libFolderForCurrentOS Library path. |
29 | | - * @param libraryFileName Library name. |
30 | | - * @param targetFolder Target folder. |
31 | | - * @return |
32 | | - * -Dorg.sqlite.lib.path=. |
33 | | - * -Dorg.sqlite.lib.name=sqlite_cryption_support.dll |
34 | | - */ |
35 | | - |
36 | | - public static boolean extractAndLoadLibraryFile(String libraryFileName) |
37 | | - throws Exception |
| 10 | +class NativeLibLoader { |
| 11 | + public static void loadLibraryFromResource(String libraryName) |
| 12 | + throws IOException |
38 | 13 | { |
39 | | - System.out.printf(">>> libraryFileName = %s\n", libraryFileName); |
40 | | - String libFolderForCurrentOS = ""; |
41 | | - String targetFolder = "rapidyamllibloader"; |
42 | | - // String targetFolder = System.getProperty("java.io.tmpdir"); |
43 | | - |
44 | | - String nativeLibraryFilePath = |
45 | | - libFolderForCurrentOS + "/" + libraryFileName; |
46 | | - System.out.printf(">>> FOO nativeLibraryFilePath = %s\n", nativeLibraryFilePath); |
47 | | - // Include architecture name in temporary filename in order to avoid |
48 | | - // conflicts when multiple JVMs with different architectures running at |
49 | | - // the same time |
50 | | - String uuid = UUID.randomUUID().toString(); |
51 | | - String extractedLibFileName = String.format( |
52 | | - "librapidyaml-%s-%s-%s", |
53 | | - "0.8.0", |
54 | | - uuid, |
55 | | - libraryFileName); |
56 | | - String extractedLckFileName = extractedLibFileName + LOCK_EXT; |
57 | | - |
58 | | - Path extractedLibFile = Paths.get(targetFolder, extractedLibFileName); |
59 | | - Path extractedLckFile = Paths.get(targetFolder, extractedLckFileName); |
60 | | - |
61 | | - try { |
62 | | - // Extract a native library file into the target directory |
63 | | - try (InputStream reader = getResourceAsStream(nativeLibraryFilePath)) { |
64 | | - if (Files.notExists(extractedLckFile)) { |
65 | | - Files.createFile(extractedLckFile); |
66 | | - } |
67 | | - |
68 | | - Files.copy(reader, extractedLibFile, StandardCopyOption.REPLACE_EXISTING); |
69 | | - } finally { |
70 | | - // Delete the extracted lib file on JVM exit. |
71 | | - extractedLibFile.toFile().deleteOnExit(); |
72 | | - extractedLckFile.toFile().deleteOnExit(); |
73 | | - } |
74 | | - |
75 | | - // Set executable (x) flag to enable Java to load the native library |
76 | | - extractedLibFile.toFile().setReadable(true); |
77 | | - extractedLibFile.toFile().setWritable(true, true); |
78 | | - extractedLibFile.toFile().setExecutable(true); |
79 | | - |
80 | | - // Check whether the contents are properly copied from the resource folder |
81 | | - { |
82 | | - try (InputStream nativeIn = getResourceAsStream(nativeLibraryFilePath); |
83 | | - InputStream extractedLibIn = Files.newInputStream(extractedLibFile)) { |
84 | | - if (!contentsEquals(nativeIn, extractedLibIn)) { |
85 | | - throw new Exception( |
86 | | - String.format( |
87 | | - "Failed to write a native library file at %s", |
88 | | - extractedLibFile)); |
89 | | - } |
90 | | - } |
91 | | - } |
92 | | - return loadNativeLibrary(targetFolder, extractedLibFileName); |
93 | | - } catch (IOException e) { |
94 | | - // logger.error(() -> "Unexpected IOException", e); |
95 | | - return false; |
96 | | - } |
97 | | - } |
98 | | - |
99 | | - // Replacement of java.lang.Class#getResourceAsStream(String) to disable sharing the resource |
100 | | - // stream |
101 | | - // in multiple class loaders and specifically to avoid |
102 | | - // https://bugs.openjdk.java.net/browse/JDK-8205976 |
103 | | - private static InputStream getResourceAsStream(String name) { |
104 | | - // Remove leading '/' since all our resource paths include a leading directory |
105 | | - // See: |
106 | | - // https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Class.java#L3054 |
107 | | - String resolvedName = name.substring(1); |
108 | | - ClassLoader cl = NativeLibLoader.class.getClassLoader(); |
109 | | - URL url = cl.getResource(resolvedName); |
110 | | - if (url == null) { |
111 | | - return null; |
112 | | - } |
113 | | - try { |
114 | | - URLConnection connection = url.openConnection(); |
115 | | - connection.setUseCaches(false); |
116 | | - return connection.getInputStream(); |
117 | | - } catch (IOException e) { |
118 | | - // logger.error(() -> "Could not connect", e); |
119 | | - return null; |
120 | | - } |
121 | | - } |
122 | | - |
123 | | - |
124 | | - /** |
125 | | - * Loads native library using the given path and name of the library. |
126 | | - * |
127 | | - * @param path Path of the native library. |
128 | | - * @param name Name of the native library. |
129 | | - * @return True for successfully loading; false otherwise. |
130 | | - */ |
131 | | - private static boolean loadNativeLibrary(String path, String name) { |
132 | | - File libPath = new File(path, name); |
133 | | - if (libPath.exists()) { |
134 | | - |
135 | | - try { |
136 | | - System.load(new File(path, name).getAbsolutePath()); |
137 | | - return true; |
138 | | - } catch (UnsatisfiedLinkError e) { |
139 | | - |
140 | | -// logger.error( |
141 | | -// () -> |
142 | | -// MessageFormat.format( |
143 | | -// "Failed to load native library: {0}. osinfo: {1}", |
144 | | -// name, OSInfo.getNativeLibFolderPathForCurrentOS()), |
145 | | -// e); |
146 | | - return false; |
147 | | - } |
148 | | - |
149 | | - } else { |
150 | | - return false; |
151 | | - } |
152 | | - } |
153 | | - |
154 | | - private static boolean contentsEquals(InputStream in1, InputStream in2) throws IOException { |
155 | | - if (!(in1 instanceof BufferedInputStream)) { |
156 | | - in1 = new BufferedInputStream(in1); |
157 | | - } |
158 | | - if (!(in2 instanceof BufferedInputStream)) { |
159 | | - in2 = new BufferedInputStream(in2); |
160 | | - } |
161 | | - |
162 | | - int ch = in1.read(); |
163 | | - while (ch != -1) { |
164 | | - int ch2 = in2.read(); |
165 | | - if (ch != ch2) { |
166 | | - return false; |
167 | | - } |
168 | | - ch = in1.read(); |
169 | | - } |
170 | | - int ch2 = in2.read(); |
171 | | - return ch2 == -1; |
172 | | - } |
| 14 | +System.out.println("libraryName: " + libraryName); |
| 15 | + ClassLoader classLoader = |
| 16 | + Thread.currentThread().getContextClassLoader(); |
173 | 17 |
|
| 18 | + InputStream inputStream = |
| 19 | + classLoader.getResourceAsStream(libraryName); |
174 | 20 |
|
175 | | - public static void loadLibraryFromJar(String path) throws IOException { |
176 | | -// if (null == path || !path.startsWith("/")) { |
177 | | -// throw new IllegalArgumentException( |
178 | | -// "The path has to be absolute (start with '/')."); |
179 | | -// } |
180 | | - |
181 | | -// String[] parts = path.split("/"); |
182 | | -// String filename = (parts.length > 1) ? parts[parts.length - 1] : null; |
183 | | -// System.out.printf("jar path file = %s\n", filename); |
184 | | - |
185 | | - if (tempdir == null) { |
186 | | - tempdir = createTempDirectory("rapidyamllibloader"); |
187 | | - // tempdir.deleteOnExit(); |
188 | | - } |
| 21 | + if (inputStream == null) |
| 22 | + throw new IOException( |
| 23 | + "Failed to load library resource '" + |
| 24 | + libraryName + "' from NativeLibLoader"); |
189 | 25 |
|
190 | | - System.out.printf("TEMPDIR = %s\n", tempdir.getAbsolutePath()); |
191 | | - System.out.printf("%s\n", System.getProperty("java.library.path")); |
192 | | - System.setProperty("java.library.path", tempdir.getAbsolutePath()); |
193 | | - System.out.printf("%s\n", System.getProperty("java.library.path")); |
| 26 | +System.out.println("inputStream: " + inputStream); |
194 | 27 |
|
| 28 | + File tempDir = createTempDir(); |
| 29 | + // tempDir.deleteOnExit(); |
| 30 | + File tempFile = new File(tempDir, libraryName); |
| 31 | +System.out.println("tempFile: " + tempFile); |
195 | 32 |
|
196 | | -// File temp = new File(tempdir, filename); |
197 | | - File temp = new File(tempdir, path); |
| 33 | + Files.copy( |
| 34 | + inputStream, |
| 35 | + tempFile.toPath(), |
| 36 | + StandardCopyOption.REPLACE_EXISTING); |
198 | 37 |
|
199 | | - try (InputStream is = NativeLibLoader.class.getResourceAsStream(path)) { |
200 | | - System.out.printf("InputStream: >>>%s<<<\n", is); |
201 | | - Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); |
202 | | - is.close(); |
203 | | - } |
204 | | - catch (IOException e) { |
205 | | - // temp.delete(); |
206 | | - throw e; |
207 | | - } |
208 | | - catch (NullPointerException e) { |
209 | | - // temp.delete(); |
210 | | - throw new FileNotFoundException( |
211 | | - "File '" + path + "' was not found inside JAR."); |
212 | | - } |
213 | | - finally { |
214 | | - } |
| 38 | + inputStream.close(); |
215 | 39 |
|
216 | 40 | try { |
217 | | - System.load(temp.getAbsolutePath()); |
| 41 | + System.load(tempFile.getAbsolutePath()); |
218 | 42 | } |
219 | 43 | finally { |
220 | | - if (isPosixCompliant()) { |
221 | | - // temp.delete(); |
222 | | - } |
223 | | - else { |
224 | | - // temp.deleteOnExit(); |
225 | | - } |
| 44 | + // tempFile.delete(); |
226 | 45 | } |
227 | | - } |
228 | 46 |
|
229 | | - private static boolean isPosixCompliant() { |
230 | | - try { |
231 | | - return FileSystems.getDefault() |
232 | | - .supportedFileAttributeViews() |
233 | | - .contains("posix"); |
234 | | - } |
235 | | - catch (FileSystemNotFoundException |
236 | | - | ProviderNotFoundException |
237 | | - | SecurityException e) { |
238 | | - return false; |
239 | | - } |
| 47 | +System.out.println("Loaded library from temp file"); |
240 | 48 | } |
241 | 49 |
|
242 | | - private static File createTempDirectory(String prefix) throws IOException { |
243 | | - String tempDir = System.getProperty("java.io.tmpdir"); |
244 | | - File generatedDir = new File(tempDir, prefix + System.nanoTime()); |
245 | | - |
246 | | - if (!generatedDir.mkdir()) |
| 50 | + private static File createTempDir() throws IOException { |
| 51 | + String tempBase = System.getProperty("java.io.tmpdir"); |
| 52 | + File tempDir = new File(tempBase, "" + System.nanoTime()); |
| 53 | + if (! tempDir.mkdir()) |
247 | 54 | throw new IOException( |
248 | | - "Failed to create temp directory " + generatedDir.getName()); |
249 | | - |
250 | | - return generatedDir; |
| 55 | + "Failed to create temp directory " + |
| 56 | + tempDir.getName()); |
| 57 | + return tempDir; |
251 | 58 | } |
252 | 59 | } |
0 commit comments