/
SnappyLoader.java
415 lines (375 loc) · 15 KB
/
SnappyLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
/*--------------------------------------------------------------------------
* Copyright 2011 Taro L. Saito
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*--------------------------------------------------------------------------*/
//--------------------------------------
// snappy-java Project
//
// SnappyLoader.java
// Since: 2011/03/29
//
// $URL$
// $Author$
//--------------------------------------
package org.xerial.snappy;
import org.xerial.snappy.pure.PureJavaSnappy;
import java.io.*;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;
import java.util.UUID;
/**
* <b>Internal only - Do not use this class.</b> This class loads a native
* library of snappy-java (snappyjava.dll, libsnappy.so, etc.) according to the
* user platform (<i>os.name</i> and <i>os.arch</i>). The natively compiled
* libraries bundled to snappy-java contain the codes of the original snappy and
* JNI programs to access Snappy.
* <p/>
* In default, no configuration is required to use snappy-java, but you can load
* your own native library created by 'make native' command.
* <p/>
* This SnappyLoader searches for native libraries (snappyjava.dll,
* libsnappy.so, etc.) in the following order:
* <ol>
* <li>If system property <i>org.xerial.snappy.use.systemlib</i> is set to true,
* lookup folders specified by <i>java.lib.path</i> system property (This is the
* default path that JVM searches for native libraries)
* <li>(System property: <i>org.xerial.snappy.lib.path</i>)/(System property:
* <i>org.xerial.lib.name</i>)
* <li>One of the libraries embedded in snappy-java-(version).jar extracted into
* (System property: <i>java.io.tempdir</i>). If
* <i>org.xerial.snappy.tempdir</i> is set, use this folder instead of
* <i>java.io.tempdir</i>.
* </ol>
* <p/>
* <p>
* If you do not want to use folder <i>java.io.tempdir</i>, set the System
* property <i>org.xerial.snappy.tempdir</i>. For example, to use
* <i>/tmp/leo</i> as a temporary folder to copy native libraries, use -D option
* of JVM:
* <p/>
* <pre>
* <code>
* java -Dorg.xerial.snappy.tempdir="/tmp/leo" ...
* </code>
* </pre>
* <p/>
* </p>
*
* @author leo
*/
public class SnappyLoader
{
public static final String SNAPPY_SYSTEM_PROPERTIES_FILE = "org-xerial-snappy.properties";
public static final String KEY_SNAPPY_LIB_PATH = "org.xerial.snappy.lib.path";
public static final String KEY_SNAPPY_LIB_NAME = "org.xerial.snappy.lib.name";
public static final String KEY_SNAPPY_PUREJAVA = "org.xerial.snappy.purejava";
public static final String KEY_SNAPPY_TEMPDIR = "org.xerial.snappy.tempdir";
public static final String KEY_SNAPPY_USE_SYSTEMLIB = "org.xerial.snappy.use.systemlib";
public static final String KEY_SNAPPY_DISABLE_BUNDLED_LIBS = "org.xerial.snappy.disable.bundled.libs"; // Depreciated, but preserved for backward compatibility
private static boolean isLoaded = false;
private static volatile SnappyApi snappyApi = null;
private static volatile BitShuffleNative bitshuffleApi = null;
private static File nativeLibFile = null;
static void cleanUpExtractedNativeLib()
{
if (nativeLibFile != null && nativeLibFile.exists()) {
boolean deleted = nativeLibFile.delete();
if (!deleted) {
// Deleting native lib has failed, but it's not serious so simply ignore it here
}
snappyApi = null;
bitshuffleApi = null;
}
}
/**
* Set the `snappyApi` instance.
*
* @param apiImpl
*/
static synchronized void setSnappyApi(SnappyApi apiImpl)
{
snappyApi = apiImpl;
}
/**
* load system properties when configuration file of the name
* {@link #SNAPPY_SYSTEM_PROPERTIES_FILE} is found
*/
private static void loadSnappySystemProperties()
{
try {
InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(SNAPPY_SYSTEM_PROPERTIES_FILE);
if (is == null) {
return; // no configuration file is found
}
// Load property file
Properties props = new Properties();
props.load(is);
is.close();
Enumeration<?> names = props.propertyNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
if (name.startsWith("org.xerial.snappy.")) {
if (System.getProperty(name) == null) {
System.setProperty(name, props.getProperty(name));
}
}
}
}
catch (Throwable ex) {
System.err.println("Could not load '" + SNAPPY_SYSTEM_PROPERTIES_FILE + "' from classpath: "
+ ex.toString());
}
}
static {
loadSnappySystemProperties();
}
static synchronized boolean isPureJava() {
return snappyApi != null && PureJavaSnappy.class.isAssignableFrom(snappyApi.getClass());
}
static synchronized SnappyApi loadSnappyApi()
{
if (snappyApi != null) {
return snappyApi;
}
try {
if(Boolean.parseBoolean(System.getProperty(KEY_SNAPPY_PUREJAVA, "false"))) {
// Use pure-java Snappy implementation
setSnappyApi(new PureJavaSnappy());
}
else {
loadNativeLibrary();
setSnappyApi(new SnappyNative());
}
}
catch(Throwable e) {
// Fall-back to pure-java Snappy implementation
setSnappyApi(new PureJavaSnappy());
}
return snappyApi;
}
static synchronized BitShuffleNative loadBitShuffleApi()
{
if (bitshuffleApi != null) {
return bitshuffleApi;
}
loadNativeLibrary();
bitshuffleApi = new BitShuffleNative();
return bitshuffleApi;
}
/**
* Load a native library of snappy-java
*/
private synchronized static void loadNativeLibrary()
{
if (!isLoaded) {
try {
nativeLibFile = findNativeLibrary();
if (nativeLibFile != null) {
// Load extracted or specified snappyjava native library.
System.load(nativeLibFile.getAbsolutePath());
} else {
// Load preinstalled snappyjava (in the path -Djava.library.path)
System.loadLibrary("snappyjava");
}
}
catch (Exception e) {
e.printStackTrace();
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, e.getMessage());
}
isLoaded = true;
}
}
private static boolean contentsEquals(InputStream in1, InputStream in2)
throws IOException
{
if (!(in1 instanceof BufferedInputStream)) {
in1 = new BufferedInputStream(in1);
}
if (!(in2 instanceof BufferedInputStream)) {
in2 = new BufferedInputStream(in2);
}
int ch = in1.read();
while (ch != -1) {
int ch2 = in2.read();
if (ch != ch2) {
return false;
}
ch = in1.read();
}
int ch2 = in2.read();
return ch2 == -1;
}
/**
* Extract the specified library file to the target folder
*
* @param libFolderForCurrentOS
* @param libraryFileName
* @param targetFolder
* @return
*/
private static File extractLibraryFile(String libFolderForCurrentOS, String libraryFileName, String targetFolder)
{
String nativeLibraryFilePath = libFolderForCurrentOS + "/" + libraryFileName;
// Attach UUID to the native library file to ensure multiple class loaders can read the libsnappy-java multiple times.
String uuid = UUID.randomUUID().toString();
String extractedLibFileName = String.format("snappy-%s-%s-%s", getVersion(), uuid, libraryFileName);
File extractedLibFile = new File(targetFolder, extractedLibFileName);
try {
// Extract a native library file into the target directory
InputStream reader = null;
FileOutputStream writer = null;
try {
reader = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
try {
writer = new FileOutputStream(extractedLibFile);
byte[] buffer = new byte[8192];
int bytesRead = 0;
while ((bytesRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, bytesRead);
}
}
finally {
if (writer != null) {
writer.close();
}
}
}
finally {
if (reader != null) {
reader.close();
}
// Delete the extracted lib file on JVM exit.
extractedLibFile.deleteOnExit();
}
// Set executable (x) flag to enable Java to load the native library
boolean success = extractedLibFile.setReadable(true) &&
extractedLibFile.setWritable(true, true) &&
extractedLibFile.setExecutable(true);
if (!success) {
// Setting file flag may fail, but in this case another error will be thrown in later phase
}
// Check whether the contents are properly copied from the resource folder
{
InputStream nativeIn = null;
InputStream extractedLibIn = null;
try {
nativeIn = SnappyLoader.class.getResourceAsStream(nativeLibraryFilePath);
extractedLibIn = new FileInputStream(extractedLibFile);
if (!contentsEquals(nativeIn, extractedLibIn)) {
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, String.format("Failed to write a native library file at %s", extractedLibFile));
}
}
finally {
if (nativeIn != null) {
nativeIn.close();
}
if (extractedLibIn != null) {
extractedLibIn.close();
}
}
}
return new File(targetFolder, extractedLibFileName);
}
catch (IOException e) {
e.printStackTrace(System.err);
return null;
}
}
static File findNativeLibrary()
{
boolean useSystemLib = Boolean.parseBoolean(System.getProperty(KEY_SNAPPY_USE_SYSTEMLIB, "false"));
boolean disabledBundledLibs = Boolean
.parseBoolean(System.getProperty(KEY_SNAPPY_DISABLE_BUNDLED_LIBS, "false"));
if (useSystemLib || disabledBundledLibs) {
return null; // Use a pre-installed libsnappyjava
}
// Try to load the library in org.xerial.snappy.lib.path */
String snappyNativeLibraryPath = System.getProperty(KEY_SNAPPY_LIB_PATH);
String snappyNativeLibraryName = System.getProperty(KEY_SNAPPY_LIB_NAME);
// Resolve the library file name with a suffix (e.g., dll, .so, etc.)
if (snappyNativeLibraryName == null) {
snappyNativeLibraryName = System.mapLibraryName("snappyjava");
}
if (snappyNativeLibraryPath != null) {
File nativeLib = new File(snappyNativeLibraryPath, snappyNativeLibraryName);
if (nativeLib.exists()) {
return nativeLib;
}
}
// Load an OS-dependent native library inside a jar file
snappyNativeLibraryPath = "/org/xerial/snappy/native/" + OSInfo.getNativeLibFolderPathForCurrentOS();
boolean hasNativeLib = hasResource(snappyNativeLibraryPath + "/" + snappyNativeLibraryName);
if (!hasNativeLib) {
if (OSInfo.getOSName().equals("Mac")) {
// Fix for openjdk7 for Mac
String altName = "libsnappyjava.dylib";
if (hasResource(snappyNativeLibraryPath + "/" + altName)) {
snappyNativeLibraryName = altName;
hasNativeLib = true;
}
}
}
if (!hasNativeLib) {
String errorMessage = String.format("no native library is found for os.name=%s and os.arch=%s", OSInfo.getOSName(), OSInfo.getArchName());
throw new SnappyError(SnappyErrorCode.FAILED_TO_LOAD_NATIVE_LIBRARY, errorMessage);
}
// Temporary folder for the native lib. Use the value of org.xerial.snappy.tempdir or java.io.tmpdir
File tempFolder = new File(System.getProperty(KEY_SNAPPY_TEMPDIR, System.getProperty("java.io.tmpdir")));
if (!tempFolder.exists()) {
boolean created = tempFolder.mkdirs();
if (!created) {
// if created == false, it will fail eventually in the later part
}
}
// Extract and load a native library inside the jar file
return extractLibraryFile(snappyNativeLibraryPath, snappyNativeLibraryName, tempFolder.getAbsolutePath());
}
private static boolean hasResource(String path)
{
return SnappyLoader.class.getResource(path) != null;
}
/**
* Get the snappy-java version by reading pom.properties embedded in jar.
* This version data is used as a suffix of a dll file extracted from the
* jar.
*
* @return the version string
*/
public static String getVersion()
{
URL versionFile = SnappyLoader.class
.getResource("/META-INF/maven/org.xerial.snappy/snappy-java/pom.properties");
if (versionFile == null) {
versionFile = SnappyLoader.class.getResource("/org/xerial/snappy/VERSION");
}
String version = "unknown";
try {
if (versionFile != null) {
Properties versionData = new Properties();
versionData.load(versionFile.openStream());
version = versionData.getProperty("version", version);
if (version.equals("unknown")) {
version = versionData.getProperty("SNAPPY_VERSION", version);
}
version = version.trim().replaceAll("[^0-9M\\.]", "");
}
}
catch (IOException e) {
System.err.println(e);
}
return version;
}
}