package info.freelibrary.util; import static info.freelibrary.util.Constants.FREELIB_UTIL_MESSAGES; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Utility class to load a native library that lives in the current classpath. * * @author <a href="mailto:ksclarke@ksclarke.io">Kevin S. Clarke</a> */ public class NativeLibraryLoader { private static final Logger LOGGER = LoggerFactory.getLogger(NativeLibraryLoader.class, FREELIB_UTIL_MESSAGES); public static enum Architecture { UNKNOWN, LINUX_32, LINUX_64, LINUX_ARM, WINDOWS_32, WINDOWS_64, OSX_32, OSX_64, OSX_PPC }; private static enum Processor { UNKNOWN, INTEL_32, INTEL_64, PPC, ARM }; private static Architecture myArchitecture = Architecture.UNKNOWN; /** * Constructor for the NativeLibraryLoader. * * @throws IOException */ private NativeLibraryLoader() { } /** * Loads a native library from the classpath. * * @param aNativeLibrary A native library to load from the classpath * @throws IOException If there is trouble reading from the Jar file or file system */ public static void load(final String aNativeLibrary) throws IOException { final String libFileName = getPlatformLibraryName(aNativeLibrary); final File tmpDir = new File(System.getProperty("java.io.tmpdir")); final File libFile = new File(tmpDir, libFileName); // Check to see whether it already exists before we go creating it again? if (!libFile.exists() || libFile.length() == 0) { final URL url = ClasspathUtils.findFirst(libFileName); if (url == null) { throw new FileNotFoundException(LOGGER.getMessage(MessageCodes.UTIL_002, aNativeLibrary)); } final JarFile jarFile = new JarFile(url.getFile()); final JarEntry jarEntry = jarFile.getJarEntry(libFileName); final InputStream inStream = jarFile.getInputStream(jarEntry); final BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(libFile)); IOUtils.copyStream(inStream, outStream); IOUtils.closeQuietly(inStream); IOUtils.closeQuietly(outStream); IOUtils.closeQuietly(jarFile); } if (libFile.exists() && libFile.length() > 0) { System.load(libFile.getAbsolutePath()); } else { throw new IOException("Problem creating libfile"); } } /** * Gets the architecture of the machine running the JVM. * * @return The architecture of the machine running the JVM */ public static Architecture getArchitecture() { if (Architecture.UNKNOWN == myArchitecture) { final Processor processor = getProcessor(); if (Processor.UNKNOWN != processor) { final String name = System.getProperty("os.name").toLowerCase(); if (name.indexOf("nix") >= 0 || name.indexOf("nux") >= 0) { if (Processor.INTEL_32 == processor) { myArchitecture = Architecture.LINUX_32; } else if (Processor.INTEL_64 == processor) { myArchitecture = Architecture.LINUX_64; } else if (Processor.ARM == processor) { myArchitecture = Architecture.LINUX_ARM; } } else if (name.indexOf("win") >= 0) { if (Processor.INTEL_32 == processor) { myArchitecture = Architecture.WINDOWS_32; } else if (Processor.INTEL_64 == processor) { myArchitecture = Architecture.WINDOWS_64; } } else if (name.indexOf("mac") >= 0) { if (Processor.INTEL_32 == processor) { myArchitecture = Architecture.OSX_32; } else if (Processor.INTEL_64 == processor) { myArchitecture = Architecture.OSX_64; } else if (Processor.PPC == processor) { myArchitecture = Architecture.OSX_PPC; } } } } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Architecture is {} and os.name is {}", myArchitecture, System.getProperty("os.name") .toLowerCase()); } return myArchitecture; } private static Processor getProcessor() { Processor processor = Processor.UNKNOWN; int bits; // Note that this is actually the architecture of the installed JVM. final String arch = System.getProperty("os.arch").toLowerCase(); if (arch.indexOf("arm") >= 0) { processor = Processor.ARM; } else if (arch.indexOf("ppc") >= 0) { processor = Processor.PPC; } else if (arch.indexOf("86") >= 0 || arch.indexOf("amd") >= 0) { bits = 32; if (arch.indexOf("64") >= 0) { bits = 64; } processor = 32 == bits ? Processor.INTEL_32 : Processor.INTEL_64; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Processor is {} and os.arch is {}", processor, System.getProperty("os.arch").toLowerCase()); } return processor; } /** * Gets the library name for the current platform. * * @param aLibraryName The platform-independent library name * @return The library name for the current platform */ public static String getPlatformLibraryName(final String aLibraryName) { String libName = null; switch (getArchitecture()) { case LINUX_32: case LINUX_64: case LINUX_ARM: libName = "lib" + aLibraryName + ".so"; break; case WINDOWS_32: case WINDOWS_64: libName = aLibraryName + ".dll"; break; case OSX_32: case OSX_64: libName = "lib" + aLibraryName + ".dylib"; break; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Requested library name {}", libName); } return libName; } }