2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
32 package com.jme3.system;
35 import java.io.FileNotFoundException;
36 import java.io.FileOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.OutputStream;
40 import java.net.MalformedURLException;
42 import java.net.URLConnection;
43 import java.util.logging.Level;
44 import java.util.logging.Logger;
47 * Helper class for extracting the natives (dll, so) from the jars.
48 * This class should only be used internally.
50 public class Natives {
52 private static final Logger logger = Logger.getLogger(Natives.class.getName());
53 private static final byte[] buf = new byte[1024];
54 private static File workingDir = new File("").getAbsoluteFile();
56 public static void setExtractionDir(String name) {
57 workingDir = new File(name).getAbsoluteFile();
60 protected static void extractNativeLib(String sysName, String name) throws IOException {
61 extractNativeLib(sysName, name, false, true);
64 protected static void extractNativeLib(String sysName, String name, boolean load) throws IOException {
65 extractNativeLib(sysName, name, load, true);
68 protected static void extractNativeLib(String sysName, String name, boolean load, boolean warning) throws IOException {
69 String fullname = System.mapLibraryName(name);
71 String path = "native/" + sysName + "/" + fullname;
72 URL url = Thread.currentThread().getContextClassLoader().getResource(path);
75 if (sysName.equals("macosx")) {
76 fullname = fullname.replace(".dylib", ".jnilib");
77 String path2 = "native/" + sysName + "/" + fullname;
78 // logger.warning("retry "+path2);
79 url = Thread.currentThread().getContextClassLoader().getResource(path2);
82 logger.log(Level.WARNING, "Cannot locate native library: {0}/{1}",
83 new String[]{sysName, fullname});
89 logger.log(Level.WARNING, "Cannot locate native library: {0}/{1}",
90 new String[]{sysName, fullname});
96 URLConnection conn = url.openConnection();
97 InputStream in = conn.getInputStream();
98 File targetFile = new File(workingDir, fullname);
99 // logger.info("targetFile = "+targetFile.getPath());
100 if (targetFile.exists()){
101 // OK, compare last modified date of this file to
103 long targetLastModified = targetFile.lastModified();
104 long sourceLastModified = conn.getLastModified();
106 // Allow ~1 second range for OSes that only support low precision
107 if (targetLastModified + 1000 > sourceLastModified){
108 logger.log(Level.FINE, "Not copying library {0}. Latest already extracted.", fullname);
113 OutputStream out = new FileOutputStream(targetFile);
115 while ((len = in.read(buf)) > 0) {
116 out.write(buf, 0, len);
121 // NOTE: On OSes that support "Date Created" property,
122 // this will cause the last modified date to be lower than
123 // date created which makes no sense
124 targetFile.setLastModified(conn.getLastModified());
125 } catch (FileNotFoundException ex) {
126 if (ex.getMessage().contains("used by another process")) {
133 logger.log(Level.INFO, "Load native library {0}", targetFile.getAbsolutePath());
134 System.load(targetFile.getAbsolutePath());
137 logger.log(Level.FINE, "Copied {0} to {1}", new Object[]{fullname, targetFile});
140 private static String getExtractionDir() {
141 URL temp = Natives.class.getResource("");
143 StringBuilder sb = new StringBuilder(temp.toString());
144 if (sb.indexOf("jar:") == 0) {
146 sb.delete(sb.indexOf("!"), sb.length());
147 sb.delete(sb.lastIndexOf("/") + 1, sb.length());
150 return new URL(sb.toString()).toString();
151 } catch (MalformedURLException ex) {
158 protected static boolean isUsingNativeBullet(){
160 Class clazz = Class.forName("com.jme3.bullet.util.NativeMeshUtil");
161 return clazz != null;
162 } catch (ClassNotFoundException ex) {
167 protected static void extractNativeLibs(Platform platform, AppSettings settings) throws IOException {
168 String renderer = settings.getRenderer();
169 String audioRenderer = settings.getAudioRenderer();
170 boolean needLWJGL = false;
171 boolean needOAL = false;
172 boolean needJInput = false;
173 boolean needNativeBullet = isUsingNativeBullet();
174 if (renderer != null) {
175 if (renderer.startsWith("LWJGL")) {
179 if (audioRenderer != null) {
180 if (audioRenderer.equals("LWJGL")) {
185 needJInput = settings.useJoysticks();
188 logger.log(Level.INFO, "Extraction Directory #1: {0}", getExtractionDir());
189 logger.log(Level.INFO, "Extraction Directory #2: {0}", workingDir.toString());
190 // logger.log(Level.INFO, "Extraction Directory #3: {0}", System.getProperty("user.dir"));
191 // LWJGL supports this feature where
192 // it can load libraries from this path.
193 // This is a fallback method in case the OS doesn't load
194 // native libraries from the working directory (e.g Linux).
195 System.setProperty("org.lwjgl.librarypath", workingDir.toString());
201 extractNativeLib("windows", "lwjgl64");
204 extractNativeLib("windows", "OpenAL64");
207 extractNativeLib("windows", "jinput-dx8_64");
208 extractNativeLib("windows", "jinput-raw_64");
210 if (needNativeBullet) {
211 extractNativeLib("windows", "bulletjme64", true, false);
216 extractNativeLib("windows", "lwjgl");
219 extractNativeLib("windows", "OpenAL32");
222 extractNativeLib("windows", "jinput-dx8");
223 extractNativeLib("windows", "jinput-raw");
225 if (needNativeBullet) {
226 extractNativeLib("windows", "bulletjme", true, false);
231 extractNativeLib("linux", "lwjgl64");
234 extractNativeLib("linux", "jinput-linux64");
237 extractNativeLib("linux", "openal64");
239 if (needNativeBullet) {
240 extractNativeLib("linux", "bulletjme64", true, false);
245 extractNativeLib("linux", "lwjgl");
248 extractNativeLib("linux", "jinput-linux");
251 extractNativeLib("linux", "openal");
253 if (needNativeBullet) {
254 extractNativeLib("linux", "bulletjme", true, false);
259 extractNativeLib("solaris", "lwjgl64");
262 // extractNativeLib("solaris", "jinput-linux64");
265 extractNativeLib("solaris", "openal64");
267 if (needNativeBullet) {
268 extractNativeLib("solaris", "bulletjme64", true, false);
273 extractNativeLib("solaris", "lwjgl");
276 // extractNativeLib("solaris", "jinput-linux");
279 extractNativeLib("solaris", "openal");
281 if (needNativeBullet) {
282 extractNativeLib("solaris", "bulletjme", true, false);
290 extractNativeLib("macosx", "lwjgl");
293 extractNativeLib("macosx", "openal");
295 extractNativeLib("macosx", "jinput-osx");
297 if (needNativeBullet) {
298 extractNativeLib("macosx", "bulletjme", true, false);