package com.onionnetworks.util; import java.io.*; import java.util.*; import java.net.URL; /** * This class is used for deploying native libraries that are stored inside * jar files. * * For each jar that contains native libraries there must be a file called * "lib/native.properties" that has a format similar to the following: * <pre> * com.onionnetworks.native.keys=fec8-linux-x86,fec16-linux-x86,fec8-win32,fec16-win32 * * com.onionnetworks.native.fec8-linux-x86.name=fec8 * com.onionnetworks.native.fec8-linux-x86.osarch=linux-x86 * com.onionnetworks.native.fec8-linux-x86.path=lib/linux/x86/libfec8.so * * com.onionnetworks.native.fec16-linux-x86.name=fec16 * com.onionnetworks.native.fec16-linux-x86.osarch=linux-x86 * com.onionnetworks.native.fec16-linux-x86.path=lib/linux/x86/libfec16.so * * com.onionnetworks.native.fec8-win32.name=fec8 * com.onionnetworks.native.fec8-win32.osarch=win32 * com.onionnetworks.native.fec8-win32.path=lib/win32/fec8.dll * * com.onionnetworks.native.fec16-win32.name=fec16 * com.onionnetworks.native.fec16-win32.osarch=win32 * com.onionnetworks.native.fec16-win32.path=lib/win32/fec16.dll * </pre> * * * For the "osarch" property note that Sun's VM uses 'i386' and IBM's uses * 'x86' so we convert all to 'x86'. * For now, we map 'Windows 95', 'Windows 98', 'Windows NT', and * 'Windows 2000', no matter what the architecture, all to 'win32'. * Depending on what native libraries are added in the future, this may have * to be made more flexible; for example, if a library depends on Windows 2000 * features or is tuned for Pentium processors. * We will just the "os.name" and "os.arch" properties to retrieve this * information on all other systems not explicitly mentioned above. * * * @author Justin F. Chapweske * */ public class NativeDeployer { public final static String OS_ARCH; static { final String OS = System.getProperty("os.name").startsWith("Windows ") ? "win32" : System.getProperty("os.name").toLowerCase(); if(System.getProperty("os.arch").toLowerCase().matches("(i?[x0-9]86_64|amd64)")) OS_ARCH=OS+"-x86_64"; else if(System.getProperty("os.arch").toLowerCase().indexOf("86") != -1) OS_ARCH=OS+"-x86"; else OS_ARCH=OS+"-"+System.getProperty("os.arch").toLowerCase(); } public final static String NATIVE_PROPERTIES_PATH = "lib/native.properties"; public synchronized final static String getLibraryPath(ClassLoader cl, String libName) { long t = System.currentTimeMillis(); IOException iox = null; /* this code avoids try {} finally {} idiom for the sake of GCJ 3.0 */ try { String libPath = (String) findLibraries(cl).get(libName); if (libPath == null) { return null; } try { return getLocalResourcePath(cl, libPath); } catch (IOException ex) { iox = ex; } System.out.println("It took "+(System.currentTimeMillis()-t)+ " millis to extract "+libName); if (iox != null) { iox.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } return null; } // Since the local copy of the resource isn't stored in a temporary // directory, at some point we'll presumably implement version-based // caching rather than extracting the file every time the code is run.... public synchronized final static String getLocalResourcePath (ClassLoader cl, String resourcePath) throws IOException { File f = File.createTempFile("libfec",".tmp"); URL url = cl.getResource(resourcePath); if (url == null) { return null; } InputStream is = url.openStream(); f.delete(); // VERY VERY important, VM crashes w/o this :P OutputStream os = new FileOutputStream(f); byte[] b = new byte[1024]; int c; while ((c = is.read(b)) != -1) { os.write(b,0,c); } is.close(); os.flush(); os.close(); return f.toString(); } /** * @return A HashMap mapping library names to paths for this os/arch. */ private final static HashMap findLibraries(ClassLoader cl) throws IOException { HashMap libMap = new HashMap(); // loop through all of the properties files. for (Enumeration en=cl.getResources(NATIVE_PROPERTIES_PATH); en.hasMoreElements();){ Properties p = new Properties(); p.load(((URL) en.nextElement()).openStream()); // Extract the keys and loop through all of the libs. for (StringTokenizer st = new StringTokenizer (p.getProperty("com.onionnetworks.native.keys"),","); st.hasMoreTokens();) { String key = st.nextToken().trim(); // If it matches the os and arch then add it. if (p.getProperty("com.onionnetworks.native."+key+".osarch"). trim().equals(OS_ARCH)) { libMap.put(p.getProperty ("com.onionnetworks.native."+key+".name").trim(), p.getProperty ("com.onionnetworks.native."+key+".path").trim()); } } } return libMap; } }