All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jnr.ffi.LibraryLoader Maven / Gradle / Ivy

The newest version!
package jnr.ffi;

import jnr.ffi.mapper.*;
import jnr.ffi.provider.FFIProvider;
import jnr.ffi.provider.LoadedLibrary;
import jnr.ffi.provider.NativeInvocationHandler;

import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

/**
 * Loads a native library and maps it to a java interface.
 *
 * 

Example usage

*
 * {@code
 *
 * public interface LibC {
 *    int puts(String str);
 * }
 *
 * LibC libc = LibraryLoader.create(LibC.class).load("c");
 *
 * libc.puts("Hello, World");
 *
 * }
 * 

*/ public abstract class LibraryLoader { private final List searchPaths = new ArrayList(); private final List libraryNames = new ArrayList(); private final List typeMappers = new ArrayList(); private final List functionMappers = new ArrayList(); private final Map optionMap = new EnumMap(LibraryOption.class); private final Class interfaceClass; private boolean failImmediately = false; /** * Creates a new {@code LibraryLoader} instance. * * @param interfaceClass the interface that describes the native library functions * @return A {@code LibraryLoader} instance. */ public static LibraryLoader create(Class interfaceClass) { return FFIProvider.getSystemProvider().createLibraryLoader(interfaceClass); } protected LibraryLoader(Class interfaceClass) { this.interfaceClass = interfaceClass; } /** * Adds a library to be loaded. Multiple libraries can be specified using additional calls * to this method, and all libraries will be searched to resolve symbols (e.g. functions, variables). * * @param libraryName The name or path of library to load. * @return The {@code LibraryLoader} instance. */ public LibraryLoader library(String libraryName) { this.libraryNames.add(libraryName); return this; } /** * Adds a path to search for libraries. Multiple paths can be specified using multiple calls * to this method, and all paths will be searched.. * * @param path A directory to search. * @return The {@code LibraryLoader} instance. */ public LibraryLoader search(String path) { searchPaths.add(path); return this; } /** * Sets an option when loading libraries. * * @see LibraryOption * * @param option The option to set * @param value The value for the option. * @return The {@code LibraryLoader} instance. */ public LibraryLoader option(LibraryOption option, Object value) { switch (option) { case TypeMapper: if (value instanceof SignatureTypeMapper) { mapper((SignatureTypeMapper) value); } else if (value instanceof TypeMapper) { mapper((TypeMapper) value); } else if (value != null) { throw new IllegalArgumentException("invalid TypeMapper: " + value.getClass()); } break; case FunctionMapper: mapper((FunctionMapper) value); break; default: optionMap.put(option, value); } return this; } /** * Adds a type mapper to use when resolving method parameter and result types. * * Multiple type mappers can be specified by additional calls to this method, and * each mapper will be tried in order until one is successful. * * @param typeMapper The type mapper to use. * @return The {@code LibraryLoader} instance. */ public LibraryLoader mapper(TypeMapper typeMapper) { typeMappers.add(new SignatureTypeMapperAdapter(typeMapper)); return this; } /** * Adds a type mapper to use when resolving method parameter and result types. * * Multiple type mappers can be specified by additional calls to this method, and * each mapper will be tried in order until one is successful. * * @param typeMapper The type mapper to use. * @return The {@code LibraryLoader} instance. */ public LibraryLoader mapper(SignatureTypeMapper typeMapper) { typeMappers.add(typeMapper); return this; } /** * Adds a function mapper to use when resolving symbols in this library. * * Multiple function mappers can be specified by additional calls to this method, and * each mapper will be tried in order, until one is successful. * * @param typeMapper The function mapper to use. * @return The {@code LibraryLoader} instance. */ public LibraryLoader mapper(FunctionMapper typeMapper) { optionMap.put(LibraryOption.FunctionMapper, typeMapper); return this; } /** * Sets the native function calling convention. * *

This is only needed on windows platforms - unless explicitly specified, all platforms assume * {@link CallingConvention#DEFAULT} as the calling convention. * * @return The {@code LibraryLoader} instance. */ public LibraryLoader convention(CallingConvention convention) { optionMap.put(LibraryOption.CallingConvention, convention); return this; } /** * Sets the calling convention of the library to the Windows stdcall calling convention * * @return This {@code LibraryLoader} instance. */ public final LibraryLoader stdcall() { return convention(CallingConvention.STDCALL); } /** * Turns off lazy propagation of load failures. By default, {@link jnr.ffi.LibraryLoader#load()} will not fail * immediately if any libraries cannot be loaded - instead, it will create an instance of the library interface * that re-throws any load errors when invoked. * * Calling this method will make {@link jnr.ffi.LibraryLoader#load()} throw errors immediately. * * @return This {@code LibraryLoader} instance. */ public final LibraryLoader failImmediately() { failImmediately = true; return this; } /** * Loads a native library and links the methods defined in {@code interfaceClass} * to native methods in the library. * * @param libraryName The name or path of library to load. * @return an implementation of the interface provided to {@link #create(Class)} that will call the native methods. */ public T load(String libraryName) { return library(libraryName).load(); } /** * Loads a native library and links the methods defined in {@code interfaceClass} * to native methods in the library. * * @return an implementation of the interface provided to {@link #create(Class)} that will call the native methods. */ public T load() { if (libraryNames.isEmpty()) { throw new UnsatisfiedLinkError("no library names specified"); } if (!typeMappers.isEmpty()) { SignatureTypeMapper typeMapper = typeMappers.size() > 1 ? new CompositeTypeMapper(typeMappers) : typeMappers.get(0); optionMap.put(LibraryOption.TypeMapper, typeMapper); } if (!functionMappers.isEmpty()) { FunctionMapper functionMapper = functionMappers.size() > 1 ? new CompositeFunctionMapper(functionMappers) : functionMappers.get(0); optionMap.put(LibraryOption.FunctionMapper, functionMapper); } try { return loadLibrary(interfaceClass, Collections.unmodifiableList(libraryNames), getSearchPaths(), Collections.unmodifiableMap(optionMap)); } catch (LinkageError error) { if (failImmediately) throw error; return createErrorProxy(error); } catch (Exception ex) { RuntimeException re = ex instanceof RuntimeException ? (RuntimeException) ex : new RuntimeException(ex); if (failImmediately) throw re; return createErrorProxy(re); } } private T createErrorProxy(final Throwable ex) { return interfaceClass.cast(Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass, LoadedLibrary.class }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { throw ex; } }) ); } private Collection getSearchPaths() { List paths = new ArrayList(searchPaths); paths.addAll(StaticDataHolder.USER_LIBRARY_PATH); return Collections.unmodifiableList(paths); } /** * Implemented by FFI providers to load the actual library. * * @param interfaceClass The java class that describes the functions to be mapped. * @param libraryNames A list of libraries to load & search for symbols * @param searchPaths The paths to search for libraries to be loaded * @param options The options to apply when loading the library * @return an instance of {@code interfaceClass} that will call the native methods. */ protected abstract T loadLibrary(Class interfaceClass, Collection libraryNames, Collection searchPaths, Map options); private static final class StaticDataHolder { private static final List USER_LIBRARY_PATH; static { List paths = new ArrayList(); try { paths.addAll(getPropertyPaths("jnr.ffi.library.path")); paths.addAll(getPropertyPaths("jaffl.library.path")); // Add JNA paths for compatibility paths.addAll(getPropertyPaths("jna.library.path")); paths.addAll(getPropertyPaths("java.library.path")); } catch (Exception ignored) {} USER_LIBRARY_PATH = Collections.unmodifiableList(new ArrayList(paths)); } } private static List getPropertyPaths(String propName) { String value = System.getProperty(propName); if (value != null) { String[] paths = value.split(File.pathSeparator); return new ArrayList(Arrays.asList(paths)); } return Collections.emptyList(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy