package org.smoothbuild.lang.function.nativ; import static java.nio.file.Files.list; import static java.util.stream.Collectors.toList; import static org.smoothbuild.SmoothConstants.SMOOTH_HOME_ENV_VARIABLE; import static org.smoothbuild.SmoothConstants.SMOOTH_HOME_LIB_DIR; import static org.smoothbuild.io.util.JarFile.jarFile; import static org.smoothbuild.lang.function.nativ.NativeFunctionFactory.nativeFunctions; import static org.smoothbuild.util.Classes.CLASS_FILE_EXTENSION; import static org.smoothbuild.util.Classes.binaryPathToBinaryName; import java.io.BufferedInputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.smoothbuild.io.util.JarFile; import org.smoothbuild.lang.function.Functions; import org.smoothbuild.lang.function.base.Function; import org.smoothbuild.lang.function.base.Name; import org.smoothbuild.util.ClassLoaders; public class NativeLibraryLoader { public static void loadBuiltinFunctions(Functions functions) { Path libsPath = Paths.get(smoothHomeDir(), SMOOTH_HOME_LIB_DIR); for (Function function : loadNativeModulesFromDir(libsPath)) { functions.add(function); } } private static String smoothHomeDir() { String smoothHomeDir = System.getenv(SMOOTH_HOME_ENV_VARIABLE); if (smoothHomeDir == null) { throw new RuntimeException("Environment variable '" + SMOOTH_HOME_ENV_VARIABLE + "' not set."); } return smoothHomeDir; } public static Collection<Function> loadNativeModulesFromDir(Path libsPath) { return loadNativeModules(listJars(libsPath)); } private static List<Path> listJars(Path libsPath) { try { return list(libsPath).filter(path -> path.toFile().isFile()).collect(toList()); } catch (IOException e) { throw new RuntimeException("IO error reading while reading from " + libsPath, e); } } static Collection<Function> loadNativeModules(List<Path> jars) { Map<Name, Function> result = new HashMap<>(); for (Path path : jars) { Collection<Function> functions = loadNativeModule(path); for (Function function : functions) { Name name = function.name(); if (result.containsKey(name)) { throw new IllegalArgumentException("Duplicate function " + name); } result.put(function.name(), function); } } return result.values(); } public static Collection<Function> loadNativeModule(Path jarPath) throws NativeFunctionImplementationException { try { return createNativeModuleImpl(jarFile(jarPath)); } catch (IOException e) { throw new RuntimeException(e); } } private static Collection<Function> createNativeModuleImpl(JarFile jar) throws IOException, NativeFunctionImplementationException { Map<Name, Function> result = new HashMap<>(); ClassLoader classLoader = classLoader(jar); try (JarInputStream jarInputStream = newJarInputStream(jar)) { JarEntry entry; while ((entry = jarInputStream.getNextJarEntry()) != null) { String fileName = entry.getName(); if (fileName.endsWith(CLASS_FILE_EXTENSION)) { Class<?> clazz = load(classLoader, binaryPathToBinaryName(fileName)); for (NativeFunction function : nativeFunctions(clazz, jar.hash())) { Name name = function.name(); if (result.containsKey(name)) { throw new IllegalArgumentException("Duplicate function " + name); } else { result.put(name, function); } } } } } return result.values(); } private static ClassLoader classLoader(JarFile jar) { ClassLoader parentClassLoader = NativeLibraryLoader.class.getClassLoader(); return ClassLoaders.jarClassLoader(parentClassLoader, jar.path()); } private static Class<?> load(ClassLoader classLoader, String binaryName) { try { return classLoader.loadClass(binaryName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } private static JarInputStream newJarInputStream(JarFile jar) throws IOException { return new JarInputStream(new BufferedInputStream(jar.openInputStream())); } }