• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 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.io.*;
35 import java.net.MalformedURLException;
36 import java.net.URL;
37 import java.net.URLConnection;
38 import java.util.logging.Level;
39 import java.util.logging.Logger;
40 
41 /**
42  * Helper class for extracting the natives (dll, so) from the jars.
43  * This class should only be used internally.
44  */
45 public final class Natives {
46 
47     private static final Logger logger = Logger.getLogger(Natives.class.getName());
48     private static final byte[] buf = new byte[1024];
49     private static File extractionDirOverride = null;
50     private static File extractionDir = null;
51 
setExtractionDir(String name)52     public static void setExtractionDir(String name) {
53         extractionDirOverride = new File(name).getAbsoluteFile();
54     }
55 
getExtractionDir()56     public static File getExtractionDir() {
57         if (extractionDirOverride != null) {
58             return extractionDirOverride;
59         }
60         if (extractionDir == null) {
61             File workingFolder = new File("").getAbsoluteFile();
62             if (!workingFolder.canWrite()) {
63                 setStorageExtractionDir();
64             } else {
65                 try {
66                     File file = new File(workingFolder.getAbsolutePath() + File.separator + ".jmetestwrite");
67                     file.createNewFile();
68                     file.delete();
69                     extractionDir = workingFolder;
70                 } catch (Exception e) {
71                     setStorageExtractionDir();
72                 }
73             }
74         }
75         return extractionDir;
76     }
77 
setStorageExtractionDir()78     private static void setStorageExtractionDir() {
79         logger.log(Level.WARNING, "Working directory is not writable. Using home directory instead.");
80         extractionDir = new File(JmeSystem.getStorageFolder(),
81                 "natives_" + Integer.toHexString(computeNativesHash()));
82         if (!extractionDir.exists()) {
83             extractionDir.mkdir();
84         }
85     }
86 
computeNativesHash()87     private static int computeNativesHash() {
88         try {
89             String classpath = System.getProperty("java.class.path");
90             URL url = Thread.currentThread().getContextClassLoader().getResource("com/jme3/system/Natives.class");
91 
92             StringBuilder sb = new StringBuilder(url.toString());
93             if (sb.indexOf("jar:") == 0) {
94                 sb.delete(0, 4);
95                 sb.delete(sb.indexOf("!"), sb.length());
96                 sb.delete(sb.lastIndexOf("/") + 1, sb.length());
97             }
98             try {
99                 url = new URL(sb.toString());
100             } catch (MalformedURLException ex) {
101                 throw new UnsupportedOperationException(ex);
102             }
103 
104             URLConnection conn = url.openConnection();
105             int hash = classpath.hashCode() ^ (int) conn.getLastModified();
106             return hash;
107         } catch (IOException ex) {
108             throw new UnsupportedOperationException(ex);
109         }
110     }
111 
extractNativeLib(String sysName, String name)112     public static void extractNativeLib(String sysName, String name) throws IOException {
113         extractNativeLib(sysName, name, false, true);
114     }
115 
extractNativeLib(String sysName, String name, boolean load)116     public static void extractNativeLib(String sysName, String name, boolean load) throws IOException {
117         extractNativeLib(sysName, name, load, true);
118     }
119 
extractNativeLib(String sysName, String name, boolean load, boolean warning)120     public static void extractNativeLib(String sysName, String name, boolean load, boolean warning) throws IOException {
121         String fullname = System.mapLibraryName(name);
122 
123         String path = "native/" + sysName + "/" + fullname;
124         URL url = Thread.currentThread().getContextClassLoader().getResource(path);
125 
126         if (url == null) {
127             if (!warning) {
128                 logger.log(Level.WARNING, "Cannot locate native library: {0}/{1}",
129                         new String[]{sysName, fullname});
130             }
131             return;
132         }
133 
134         URLConnection conn = url.openConnection();
135         InputStream in = conn.getInputStream();
136         File targetFile = new File(getExtractionDir(), fullname);
137         OutputStream out = null;
138         try {
139             if (targetFile.exists()) {
140                 // OK, compare last modified date of this file to
141                 // file in jar
142                 long targetLastModified = targetFile.lastModified();
143                 long sourceLastModified = conn.getLastModified();
144 
145                 // Allow ~1 second range for OSes that only support low precision
146                 if (targetLastModified + 1000 > sourceLastModified) {
147                     logger.log(Level.FINE, "Not copying library {0}. Latest already extracted.", fullname);
148                     return;
149                 }
150             }
151 
152             out = new FileOutputStream(targetFile);
153             int len;
154             while ((len = in.read(buf)) > 0) {
155                 out.write(buf, 0, len);
156             }
157             in.close();
158             in = null;
159             out.close();
160             out = null;
161 
162             // NOTE: On OSes that support "Date Created" property,
163             // this will cause the last modified date to be lower than
164             // date created which makes no sense
165             targetFile.setLastModified(conn.getLastModified());
166         } catch (FileNotFoundException ex) {
167             if (ex.getMessage().contains("used by another process")) {
168                 return;
169             }
170 
171             throw ex;
172         } finally {
173             if (load) {
174                 System.load(targetFile.getAbsolutePath());
175             }
176             if(in != null){
177                 in.close();
178             }
179             if(out != null){
180                 out.close();
181             }
182         }
183         logger.log(Level.FINE, "Copied {0} to {1}", new Object[]{fullname, targetFile});
184     }
185 
isUsingNativeBullet()186     protected static boolean isUsingNativeBullet() {
187         try {
188             Class clazz = Class.forName("com.jme3.bullet.util.NativeMeshUtil");
189             return clazz != null;
190         } catch (ClassNotFoundException ex) {
191             return false;
192         }
193     }
194 
extractNativeLibs(Platform platform, AppSettings settings)195     public static void extractNativeLibs(Platform platform, AppSettings settings) throws IOException {
196         String renderer = settings.getRenderer();
197         String audioRenderer = settings.getAudioRenderer();
198         boolean needLWJGL = false;
199         boolean needOAL = false;
200         boolean needJInput = false;
201         boolean needNativeBullet = isUsingNativeBullet();
202 
203         if (renderer != null) {
204             if (renderer.startsWith("LWJGL")) {
205                 needLWJGL = true;
206             }
207         }
208         if (audioRenderer != null) {
209             if (audioRenderer.equals("LWJGL")) {
210                 needLWJGL = true;
211                 needOAL = true;
212             }
213         }
214         needJInput = settings.useJoysticks();
215 
216         String libraryPath = getExtractionDir().toString();
217         if (needLWJGL) {
218             logger.log(Level.INFO, "Extraction Directory: {0}", getExtractionDir().toString());
219 
220             // LWJGL supports this feature where
221             // it can load libraries from this path.
222             System.setProperty("org.lwjgl.librarypath", libraryPath);
223         }
224         if (needJInput) {
225             // AND Luckily enough JInput supports the same feature.
226             System.setProperty("net.java.games.input.librarypath", libraryPath);
227         }
228 
229         switch (platform) {
230             case Windows64:
231                 if (needLWJGL) {
232                     extractNativeLib("windows", "lwjgl64");
233                 }
234                 if (needOAL) {
235                     extractNativeLib("windows", "OpenAL64");
236                 }
237                 if (needJInput) {
238                     extractNativeLib("windows", "jinput-dx8_64");
239                     extractNativeLib("windows", "jinput-raw_64");
240                 }
241                 if (needNativeBullet) {
242                     extractNativeLib("windows", "bulletjme64", true, false);
243                 }
244                 break;
245             case Windows32:
246                 if (needLWJGL) {
247                     extractNativeLib("windows", "lwjgl");
248                 }
249                 if (needOAL) {
250                     extractNativeLib("windows", "OpenAL32");
251                 }
252                 if (needJInput) {
253                     extractNativeLib("windows", "jinput-dx8");
254                     extractNativeLib("windows", "jinput-raw");
255                 }
256                 if (needNativeBullet) {
257                     extractNativeLib("windows", "bulletjme", true, false);
258                 }
259                 break;
260             case Linux64:
261                 if (needLWJGL) {
262                     extractNativeLib("linux", "lwjgl64");
263                 }
264                 if (needJInput) {
265                     extractNativeLib("linux", "jinput-linux64");
266                 }
267                 if (needOAL) {
268                     extractNativeLib("linux", "openal64");
269                 }
270                 if (needNativeBullet) {
271                     extractNativeLib("linux", "bulletjme64", true, false);
272                 }
273                 break;
274             case Linux32:
275                 if (needLWJGL) {
276                     extractNativeLib("linux", "lwjgl");
277                 }
278                 if (needJInput) {
279                     extractNativeLib("linux", "jinput-linux");
280                 }
281                 if (needOAL) {
282                     extractNativeLib("linux", "openal");
283                 }
284                 if (needNativeBullet) {
285                     extractNativeLib("linux", "bulletjme", true, false);
286                 }
287                 break;
288             case MacOSX_PPC32:
289             case MacOSX32:
290             case MacOSX_PPC64:
291             case MacOSX64:
292                 if (needLWJGL) {
293                     extractNativeLib("macosx", "lwjgl");
294                 }
295 //                if (needOAL)
296 //                    extractNativeLib("macosx", "openal");
297                 if (needJInput) {
298                     extractNativeLib("macosx", "jinput-osx");
299                 }
300                 if (needNativeBullet) {
301                     extractNativeLib("macosx", "bulletjme", true, false);
302                 }
303                 break;
304         }
305     }
306 }
307