-
Notifications
You must be signed in to change notification settings - Fork 109
Expand file tree
/
Copy pathLoader.java
More file actions
360 lines (324 loc) · 10.3 KB
/
Loader.java
File metadata and controls
360 lines (324 loc) · 10.3 KB
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
/*
* Copyright (C) 2011 Klaus Reimer <[email protected]>
* See LICENSE.md for licensing information.
*/
package org.usb4java;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
/**
* Utility class to load native libraries from classpath.
*
* @author Klaus Reimer ([email protected])
*/
public final class Loader
{
/** Buffer size used for copying data. */
private static final int BUFFER_SIZE = 8192;
/** Constant for OS X operating system. */
private static final String OS_OSX = "osx";
/** Constant for OS X operating system. */
private static final String OS_MACOSX = "macosx";
/** Constant for Linux operating system. */
private static final String OS_LINUX = "linux";
/** Constant for Windows operating system. */
private static final String OS_WINDOWS = "windows";
/** Constant for FreeBSD operating system. */
private static final String OS_FREEBSD = "freebsd";
/** Constant for SunOS operating system. */
private static final String OS_SUNOS = "sunos";
/** Constant for i386 architecture. */
private static final String ARCH_I386 = "i386";
/** Constant for x86 architecture. */
private static final String ARCH_X86 = "x86";
/** Constant for x86_64 architecture. */
private static final String ARCH_X86_64 = "x86_64";
/** Constant for amd64 architecture. */
private static final String ARCH_AMD64 = "amd64";
/** Constant for so file extension. */
private static final String EXT_SO = "so";
/** Constant for dll file extension. */
private static final String EXT_DLL = "dll";
/** Constant for dylib file extension. */
private static final String EXT_DYLIB = "dylib";
/** The temporary directory for native libraries. */
private static File tmp;
/** If library is already loaded. */
private static boolean loaded = false;
/**
* Private constructor to prevent instantiation.
*/
private Loader()
{
// Nothing to do here
}
/**
* Returns the operating system name. This could be "linux", "windows" or
* "osx" or (for any other non-supported platform) the value of the
* "os.name" property converted to lower case and with removed space
* characters.
*
* @return The operating system name.
*/
private static String getOS()
{
final String os = System.getProperty("os.name").toLowerCase()
.replace(" ", "");
if (os.contains(OS_WINDOWS))
{
return OS_WINDOWS;
}
if (os.equals(OS_MACOSX))
{
return OS_OSX;
}
return os;
}
/**
* Returns the CPU architecture. This will be "x86" or "x86_64" (Platform
* names i386 und amd64 are converted accordingly) or (when platform is
* unsupported) the value of os.arch converted to lower-case and with
* removed space characters.
*
* @return The CPU architecture
*/
private static String getArch()
{
final String arch = System.getProperty("os.arch").toLowerCase()
.replace(" ", "");
if (arch.equals(ARCH_I386))
{
return ARCH_X86;
}
if (arch.equals(ARCH_AMD64))
{
return ARCH_X86_64;
}
return arch;
}
/**
* Returns the shared library extension name.
*
* @return The shared library extension name.
*/
private static String getExt()
{
final String os = getOS();
final String key = "usb4java.libext." + getOS();
final String ext = System.getProperty(key);
if (ext != null)
{
return ext;
}
if (os.equals(OS_LINUX) || os.equals(OS_FREEBSD) || os.equals(OS_SUNOS))
{
return EXT_SO;
}
if (os.equals(OS_WINDOWS))
{
return EXT_DLL;
}
if (os.equals(OS_OSX))
{
return EXT_DYLIB;
}
throw new LoaderException("Unable to determine the shared library "
+ "file extension for operating system '" + os
+ "'. Please specify Java parameter -D" + key
+ "=<FILE-EXTENSION>");
}
/**
* Creates the temporary directory used for unpacking the native libraries.
* This directory is marked for deletion on exit.
*
* @return The temporary directory for native libraries.
*/
private static File createTempDirectory()
{
// Return cached tmp directory when already created
if (tmp != null)
{
return tmp;
}
try
{
tmp = File.createTempFile("usb4java", null);
if (!tmp.delete())
{
throw new IOException("Unable to delete temporary file " + tmp);
}
if (!tmp.mkdirs())
{
throw new IOException("Unable to create temporary directory "
+ tmp);
}
tmp.deleteOnExit();
return tmp;
}
catch (final IOException e)
{
throw new LoaderException("Unable to create temporary directory "
+ "for usb4java natives: " + e, e);
}
}
/**
* Returns the platform name. This could be for example "linux-x86" or
* "windows-x86_64".
*
* @return The architecture name. Never null.
*/
private static String getPlatform()
{
return getOS() + "-" + getArch();
}
/**
* Returns the name of the usb4java native library. This could be
* "libusb4java.dll" for example.
*
* @return The usb4java native library name. Never null.
*/
private static String getLibName()
{
return "libusb4java." + getExt();
}
/**
* Returns the name of the libusb native library. This could be
* "libusb0.dll" for example or null if this library is not needed on the
* current platform (Because it is provided by the operating system).
*
* @return The libusb native library name or null if not needed.
*/
private static String getExtraLibName()
{
final String os = getOS();
if (os.equals(OS_WINDOWS))
{
return "libusb-1.0." + EXT_DLL;
}
return null;
}
/**
* Copies the specified input stream to the specified output file.
*
* @param input
* The input stream.
* @param output
* The output file.
* @throws IOException
* If copying failed.
*/
private static void copy(final InputStream input, final File output)
throws IOException
{
final byte[] buffer = new byte[BUFFER_SIZE];
final FileOutputStream stream = new FileOutputStream(output);
try
{
int read;
while ((read = input.read(buffer)) != -1)
{
stream.write(buffer, 0, read);
}
}
finally
{
stream.close();
}
}
/**
* Extracts a single library.
*
* @param platform
* The platform name (For example "linux-x86")
* @param lib
* The library name to extract (For example "libusb0.dll")
* @return The absolute path to the extracted library.
*/
private static String extractLibrary(final String platform,
final String lib)
{
// Extract the usb4java library
final String source = '/'
+ Loader.class.getPackage().getName().replace('.', '/') + '/'
+ platform + "/" + lib;
// Check if native library is present
final URL url = Loader.class.getResource(source);
if (url == null)
{
throw new LoaderException("Native library not found in classpath: "
+ source);
}
// If native library was found in an already extracted form then
// return this one without extracting it
if ("file".equals(url.getProtocol()))
{
try
{
return new File(url.toURI()).getAbsolutePath();
}
catch (final URISyntaxException e)
{
// Can't happen because we are not constructing the URI
// manually. But even when it happens then we fall back to
// extracting the library.
throw new LoaderException(e.toString(), e);
}
}
// Extract the library and return the path to the extracted file.
final File dest = new File(createTempDirectory(), lib);
try
{
final InputStream stream = Loader.class.getResourceAsStream(source);
if (stream == null)
{
throw new LoaderException("Unable to find " + source
+ " in the classpath");
}
try
{
copy(stream, dest);
}
finally
{
stream.close();
}
}
catch (final IOException e)
{
throw new LoaderException("Unable to extract native library "
+ source + " to " + dest + ": " + e, e);
}
// Mark usb4java library for deletion
dest.deleteOnExit();
return dest.getAbsolutePath();
}
/**
* Loads the libusb native wrapper library. Can be safely called multiple
* times. Duplicate calls are ignored. This method is automatically called
* when the {@link LibUsb} class is loaded. When you need to do it earlier
* (To catch exceptions for example) then simply call this method manually.
*
* @throws LoaderException
* When loading the native wrapper libraries failed.
*/
public static synchronized void load()
{
// Do nothing if already loaded (or still loading)
if (loaded)
{
return;
}
loaded = true;
final String platform = getPlatform();
final String lib = getLibName();
final String extraLib = getExtraLibName();
if (extraLib != null)
{
System.load(extractLibrary(platform, extraLib));
}
System.load(extractLibrary(platform, lib));
}
}