Home | History | Annotate | Download | only in system
      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 
     52     public static void setExtractionDir(String name) {
     53         extractionDirOverride = new File(name).getAbsoluteFile();
     54     }
     55 
     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 
     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 
     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 
    112     public static void extractNativeLib(String sysName, String name) throws IOException {
    113         extractNativeLib(sysName, name, false, true);
    114     }
    115 
    116     public static void extractNativeLib(String sysName, String name, boolean load) throws IOException {
    117         extractNativeLib(sysName, name, load, true);
    118     }
    119 
    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 
    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 
    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