Skip to content

Commit 44a50de

Browse files
authored
Improve NativeLibraryLoader (#1973)
* Improve NativeLibraryLoader. * Add javadoc. * Moved library extraction requirement check into a separate method. * Fix javadoc. * Refactor library extraction check method. * Extract natives to system temp directory retrieved by System.getProperty("java.io.tmpdir") instead of working directory. * Renamed enum "Openal" to "OpenAL" and added javadoc on NativeLibraries. * Update comments.
1 parent 72ffe5e commit 44a50de

File tree

7 files changed

+412
-221
lines changed

7 files changed

+412
-221
lines changed

jme3-desktop/src/main/java/com/jme3/system/JmeDesktopSystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ public void initialize(AppSettings settings) {
285285
logger.log(Level.INFO, getBuildInfo());
286286
if (!lowPermissions) {
287287
if (NativeLibraryLoader.isUsingNativeBullet()) {
288-
NativeLibraryLoader.loadNativeLibrary("bulletjme", true);
288+
NativeLibraryLoader.loadNativeLibrary(NativeLibraries.BulletJme.getName(), true);
289289
}
290290
}
291291
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Copyright (c) 2009-2023 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package com.jme3.system;
33+
34+
import java.nio.file.Paths;
35+
import java.util.ArrayList;
36+
import java.util.List;
37+
import java.util.function.Consumer;
38+
39+
/**
40+
* Defines default native libraries that are loaded by
41+
* {@link NativeLibraryLoader}.
42+
*
43+
* @author Ali-RS
44+
*/
45+
public enum NativeLibraries {
46+
47+
// Note: LWJGL 3 handles its native library extracting & loading using
48+
// its own SharedLibraryLoader.
49+
50+
/**
51+
* Native lwjgl libraries for LWJGL 2 required by jme3-lwjgl backend.
52+
*/
53+
Lwjgl(new LibraryInfo("lwjgl", libPath ->
54+
// Delegate loading to lwjgl.
55+
System.setProperty("org.lwjgl.librarypath",
56+
Paths.get(libPath).getParent().toAbsolutePath().toString()))
57+
.addNativeVariant(Platform.Windows32, "lwjgl.dll")
58+
.addNativeVariant(Platform.Windows64, "lwjgl64.dll")
59+
.addNativeVariant(Platform.Linux32, "liblwjgl.so")
60+
.addNativeVariant(Platform.Linux64, "liblwjgl64.so")
61+
.addNativeVariant(Platform.MacOSX32, "liblwjgl.dylib")
62+
.addNativeVariant(Platform.MacOSX64, "liblwjgl.dylib")
63+
),
64+
65+
// OpenAL for LWJGL 2
66+
// For OSX: Need to add lib prefix when extracting
67+
/**
68+
* Native OpenAL audio libraries for LWJGL 2 required by jme3-lwjgl backend.
69+
*/
70+
OpenAL(new LibraryInfo("openal")
71+
.addNativeVariant(Platform.Windows32, "OpenAL32.dll")
72+
.addNativeVariant(Platform.Windows64, "OpenAL64.dll")
73+
.addNativeVariant(Platform.Linux32, "libopenal.so")
74+
.addNativeVariant(Platform.Linux64, "libopenal64.so")
75+
.addNativeVariant(Platform.MacOSX32, "openal.dylib", "libopenal.dylib")
76+
.addNativeVariant(Platform.MacOSX64, "openal.dylib", "libopenal.dylib")
77+
),
78+
79+
/**
80+
* Native bullet physics libraries required by Minie library.
81+
*/
82+
BulletJme(new LibraryInfo("bulletjme")
83+
.addNativeVariant(Platform.Windows32, "native/windows/x86/bulletjme.dll", "bulletjme-x86.dll")
84+
.addNativeVariant(Platform.Windows64, "native/windows/x86_64/bulletjme.dll", "bulletjme-x86_64.dll")
85+
.addNativeVariant(Platform.Windows_ARM64, "native/windows/arm64/bulletjme.dll", "bulletjme-arm64.dll")
86+
.addNativeVariant(Platform.Linux32, "native/linux/x86/libbulletjme.so", "libbulletjme-x86.so")
87+
.addNativeVariant(Platform.Linux64, "native/linux/x86_64/libbulletjme.so", "libbulletjme-x86_64.so")
88+
.addNativeVariant(Platform.Linux_ARM32, "native/linux/arm32/libbulletjme.so", "libbulletjme-arm32.so")
89+
.addNativeVariant(Platform.Linux_ARM64, "native/linux/arm64/libbulletjme.so", "libbulletjme-arm64.so")
90+
.addNativeVariant(Platform.MacOSX32, "native/osx/x86/libbulletjme.dylib", "libbulletjme-x86.dylib")
91+
.addNativeVariant(Platform.MacOSX64, "native/osx/x86_64/libbulletjme.dylib", "libbulletjme-x86_64.dylib")
92+
.addNativeVariant(Platform.MacOSX_ARM64, "native/osx/arm64/libbulletjme.dylib", "libbulletjme-arm64.dylib")
93+
),
94+
95+
// For OSX: Need to rename extension jnilib -> dylib when extracting
96+
/**
97+
* Native JInput joystick libraries required by jme3-lwjgl backend.
98+
*/
99+
JInput(new LibraryInfo("jinput", libPath ->
100+
// Delegate loading to jinput.
101+
System.setProperty("net.java.games.input.librarypath",
102+
Paths.get(libPath).getParent().toAbsolutePath().toString()))
103+
.addNativeVariant(Platform.Windows32, "jinput-raw.dll")
104+
.addNativeVariant(Platform.Windows64, "jinput-raw_64.dll")
105+
.addNativeVariant(Platform.Linux32, "libjinput-linux.so")
106+
.addNativeVariant(Platform.Linux64, "libjinput-linux64.so")
107+
.addNativeVariant(Platform.MacOSX32, "libjinput-osx.jnilib", "libjinput-osx.dylib")
108+
.addNativeVariant(Platform.MacOSX64, "libjinput-osx.jnilib", "libjinput-osx.dylib")
109+
),
110+
111+
/**
112+
* Native JInput DirectX 8 auxiliary libraries required by jme3-lwjgl backend.
113+
* (only required on Windows)
114+
*/
115+
JInputDX8(new LibraryInfo("jinput-dx8")
116+
.addNativeVariant(Platform.Windows32, "jinput-dx8.dll", null)
117+
.addNativeVariant(Platform.Windows64, "jinput-dx8_64.dll", null)
118+
.addNativeVariant(Platform.Linux32, null)
119+
.addNativeVariant(Platform.Linux64, null)
120+
.addNativeVariant(Platform.MacOSX32, null)
121+
.addNativeVariant(Platform.MacOSX64, null)
122+
);
123+
124+
private final LibraryInfo library;
125+
126+
127+
NativeLibraries(LibraryInfo library) {
128+
this.library = library;
129+
}
130+
131+
/**
132+
* Register native libraries on {@link NativeLibraryLoader} so we can load them
133+
* later on via {@link NativeLibraryLoader#loadNativeLibrary(String, boolean)}.
134+
*/
135+
public static void registerDefaultLibraries() {
136+
Lwjgl.registerLibrary();
137+
OpenAL.registerLibrary();
138+
BulletJme.registerLibrary();
139+
JInput.registerLibrary();
140+
JInputDX8.registerLibrary();
141+
}
142+
143+
public LibraryInfo getLibrary() {
144+
return library;
145+
}
146+
147+
/**
148+
* @return the library name. This is effectively equivalent to the
149+
* call {@link LibraryInfo#getName()}
150+
*/
151+
public String getName() {
152+
return library.getName();
153+
}
154+
155+
/**
156+
* Registers this library's native variants into {@link NativeLibraryLoader} that can
157+
* be loaded later via {@link NativeLibraryLoader#loadNativeLibrary(String, boolean)}.
158+
*/
159+
private void registerLibrary() {
160+
library.getNativeVariants().forEach(NativeLibraryLoader::registerNativeLibrary);
161+
}
162+
163+
/**
164+
* A helper class that defines a native library by name, list of its native variants
165+
* for target platforms and a load function used to load library from an absolute
166+
* path after extracted by {@link NativeLibraryLoader}.
167+
*/
168+
public static class LibraryInfo {
169+
170+
private final String name;
171+
private final List<NativeLibrary> nativeVariants = new ArrayList<>();
172+
private final Consumer<String> loadFunction;
173+
174+
/**
175+
* Define a library by the specified name and a default load function
176+
* that uses {@link System#load(String)} to load extracted native from
177+
* absolute path.
178+
* @param name The library name. (not null)
179+
*/
180+
public LibraryInfo(String name) {
181+
this(name, System::load);
182+
}
183+
184+
/**
185+
* Define a library by the specified name and specified load function
186+
* that is used to load extracted native from an absolute path string.
187+
*
188+
* @param name The library name (not null)
189+
* @param loadFunction The load function for loading library from
190+
* an absolute path string. (not null)
191+
*/
192+
public LibraryInfo(String name, Consumer<String> loadFunction) {
193+
this.name = name;
194+
this.loadFunction = loadFunction;
195+
}
196+
197+
/**
198+
* @return the library name.
199+
*/
200+
public String getName() {
201+
return name;
202+
}
203+
204+
/**
205+
* @return the list of native variants, each targeting a specific platform.
206+
*/
207+
public List<NativeLibrary> getNativeVariants() {
208+
return nativeVariants;
209+
}
210+
211+
/**
212+
* Adds a new native library that targets specified platform.
213+
*
214+
* @param platform The platform this library targets
215+
* @param pathInNativesJar The path of native file inside library jar
216+
* @return this
217+
*/
218+
public LibraryInfo addNativeVariant(Platform platform, String pathInNativesJar) {
219+
return addNativeVariant(platform, pathInNativesJar, null);
220+
}
221+
222+
/**
223+
* Adds a new native library that targets specified platform.
224+
*
225+
* @param platform The platform this library targets
226+
* @param pathInNativesJar The path of native file inside library jar
227+
* @param extractedAsFileName The filename that the library should be extracted as
228+
* @return this
229+
*/
230+
public LibraryInfo addNativeVariant(Platform platform, String pathInNativesJar, String extractedAsFileName) {
231+
nativeVariants.add(new NativeLibrary(name, platform, pathInNativesJar, extractedAsFileName, loadFunction));
232+
return this;
233+
}
234+
}
235+
}

jme3-desktop/src/main/java/com/jme3/system/NativeLibrary.java

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2012 jMonkeyEngine
2+
* Copyright (c) 2009-2023 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,8 @@
3131
*/
3232
package com.jme3.system;
3333

34+
import java.util.function.Consumer;
35+
3436
/**
3537
* Holds information about a native library for a particular platform.
3638
*
@@ -42,6 +44,7 @@ final class NativeLibrary {
4244
private final Platform platform;
4345
private final String pathInNativesJar;
4446
private final String extractedAsFileName;
47+
private final Consumer<String> loadFunction;
4548

4649
/**
4750
* Key for map to find a library for a name and platform.
@@ -76,6 +79,55 @@ public boolean equals(Object obj) {
7679
return true;
7780
}
7881
}
82+
83+
/**
84+
* Create a new NativeLibrary. The extracted file name will be the same as
85+
* in jar and will be loaded via {@link System#load(String)}.
86+
*
87+
* @param name The name of the library (not null)
88+
* @param platform The platform associated to this native library (not null)
89+
* @param pathInNativesJar The path to the library in the classpath (if it is null,
90+
* the library loading will be ignored on this platform)
91+
**/
92+
public NativeLibrary(String name, Platform platform, String pathInNativesJar) {
93+
this(name, platform, pathInNativesJar, null);
94+
}
95+
96+
/**
97+
* Create a new NativeLibrary. The extracted file will be loaded
98+
* via {@link System#load(String)}.
99+
*
100+
* @param name The name of the library (not null)
101+
* @param platform The platform associated to this native library (not null)
102+
* @param pathInNativesJar The path to the library in the classpath (if it is null,
103+
* the library loading will be ignored on this platform)
104+
* @param extractedAsFileName The name that should be given to the extracted file
105+
* (if set to null, then the filename in the natives
106+
* jar shall be used)
107+
*/
108+
public NativeLibrary(String name, Platform platform, String pathInNativesJar, String extractedAsFileName) {
109+
this(name, platform, pathInNativesJar, extractedAsFileName, System::load);
110+
}
111+
112+
/**
113+
* Create a new NativeLibrary.
114+
*
115+
* @param name The name of the library (not null)
116+
* @param platform The platform associated to this native library (not null)
117+
* @param pathInNativesJar The path to the library in the classpath (if it is null,
118+
* the library loading will be ignored on this platform)
119+
* @param extractedAsFileName The name that should be given to the extracted file
120+
* (if set to null, then the filename in the natives
121+
* jar shall be used)
122+
* @param loadFunction The function used to load the library from absolute path (not null)
123+
*/
124+
public NativeLibrary(String name, Platform platform, String pathInNativesJar, String extractedAsFileName, Consumer<String> loadFunction) {
125+
this.name = name;
126+
this.platform = platform;
127+
this.pathInNativesJar = pathInNativesJar;
128+
this.extractedAsFileName = extractedAsFileName;
129+
this.loadFunction = loadFunction;
130+
}
79131

80132
/**
81133
* The name of the library.
@@ -90,7 +142,7 @@ public String getName() {
90142
/**
91143
* The OS + architecture combination for which this library
92144
* should be extracted.
93-
*
145+
*
94146
* @return platform associated to this native library
95147
*/
96148
public Platform getPlatform() {
@@ -99,12 +151,12 @@ public Platform getPlatform() {
99151

100152
/**
101153
* The filename that the library should be extracted as.
102-
*
154+
*
103155
* In some cases, this differs from the {@link #getPathInNativesJar() path in the natives jar},
104156
* since the names of the libraries specified in the jars are often incorrect.
105157
* If set to <code>null</code>, then the filename in the
106158
* natives jar shall be used.
107-
*
159+
*
108160
* @return the name that should be given to the extracted file.
109161
*/
110162
public String getExtractedAsName() {
@@ -113,30 +165,29 @@ public String getExtractedAsName() {
113165

114166
/**
115167
* Path inside the natives jar or classpath where the library is located.
116-
*
168+
*
117169
* This library must be compatible with the {@link #getPlatform() platform}
118170
* which this library is associated with.
119-
*
171+
*
120172
* @return path to the library in the classpath
121173
*/
122174
public String getPathInNativesJar() {
123175
return pathInNativesJar;
124176
}
125177

126178
/**
127-
* Create a new NativeLibrary.
179+
* @return the load function used for loading this native library.
180+
* It loads the native library from absolute path on disk.
181+
* By default, it loads with {@link System#load(java.lang.String) }.
128182
*/
129-
public NativeLibrary(String name, Platform platform, String pathInNativesJar, String extractedAsFileName) {
130-
this.name = name;
131-
this.platform = platform;
132-
this.pathInNativesJar = pathInNativesJar;
133-
this.extractedAsFileName = extractedAsFileName;
183+
public Consumer<String> getLoadFunction() {
184+
return loadFunction;
134185
}
135186

136187
/**
137-
* Create a new NativeLibrary.
188+
* @return key for map to find a library for a name and platform.
138189
*/
139-
public NativeLibrary(String name, Platform platform, String pathInNativesJar) {
140-
this(name, platform, pathInNativesJar, null);
190+
public Key getKey() {
191+
return new NativeLibrary.Key(getName(), getPlatform());
141192
}
142193
}

0 commit comments

Comments
 (0)