/src/com/sun/jna/Native.java
Java | 2414 lines | 1410 code | 203 blank | 801 comment | 337 complexity | 8c7944b63e7c7813d66fcf2b4782f8df MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
Large files files are truncated, but you can click here to view the full file
- /* Copyright (c) 2007-2015 Timothy Wall, All Rights Reserved
- *
- * The contents of this file is dual-licensed under 2
- * alternative Open Source/Free licenses: LGPL 2.1 or later and
- * Apache License 2.0. (starting with JNA version 4.0.0).
- *
- * You can freely decide which license you want to apply to
- * the project.
- *
- * You may obtain a copy of the LGPL License at:
- *
- * http://www.gnu.org/licenses/licenses.html
- *
- * A copy is also included in the downloadable source code package
- * containing JNA, in file "LGPL2.1".
- *
- * You may obtain a copy of the Apache License at:
- *
- * http://www.apache.org/licenses/
- *
- * A copy is also included in the downloadable source code package
- * containing JNA, in file "AL2.0".
- */
- package com.sun.jna;
- import java.awt.Component;
- import java.awt.GraphicsEnvironment;
- import java.awt.HeadlessException;
- import java.awt.Window;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.FilenameFilter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.lang.ref.Reference;
- import java.lang.ref.WeakReference;
- import java.lang.reflect.Array;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.lang.reflect.Proxy;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.nio.Buffer;
- import java.nio.ByteBuffer;
- import java.nio.charset.Charset;
- import java.nio.charset.IllegalCharsetNameException;
- import java.nio.charset.UnsupportedCharsetException;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.StringTokenizer;
- import java.util.WeakHashMap;
- import com.sun.jna.Callback.UncaughtExceptionHandler;
- import com.sun.jna.Structure.FFIType;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- /** Provides generation of invocation plumbing for a defined native
- * library interface. Also provides various utilities for native operations.
- * <p>
- * {@link #getTypeMapper} and {@link #getStructureAlignment} are provided
- * to avoid having to explicitly pass these parameters to {@link Structure}s,
- * which would require every {@link Structure} which requires custom mapping
- * or alignment to define a constructor and pass parameters to the superclass.
- * To avoid lots of boilerplate, the base {@link Structure} constructor
- * figures out these properties based on its enclosing interface.<p>
- * <a name=library_loading></a>
- * <h2>Library Loading</h2>
- * <p>When JNA classes are loaded, the native shared library (jnidispatch) is
- * loaded as well. An attempt is made to load it from the any paths defined
- * in <code>jna.boot.library.path</code> (if defined), then the system library
- * path using {@link System#loadLibrary}, unless <code>jna.nosys=true</code>.
- * If not found, the appropriate library will be extracted from the class path
- * (into a temporary directory if found within a jar file) and loaded from
- * there, unless <code>jna.noclasspath=true</code>. If your system has
- * additional security constraints regarding execution or load of files
- * (SELinux, for example), you should probably install the native library in
- * an accessible location and configure your system accordingly, rather than
- * relying on JNA to extract the library from its own jar file.</p>
- * <p>To avoid the automatic unpacking (in situations where you want to force a
- * failure if the JNA native library is not properly installed on the system),
- * set the system property <code>jna.nounpack=true</code>.
- * </p>
- * <p>While this class and its corresponding native library are loaded, the
- * system property <code>jna.loaded</code> will be set. The property will be
- * cleared when native support has been unloaded (i.e. the Native class and
- * its underlying native support has been GC'd).</p>
- * <p>NOTE: all native functions are provided within this class to ensure that
- * all other JNA-provided classes and objects are GC'd and/or
- * finalized/disposed before this class is disposed and/or removed from
- * memory (most notably Memory and any other class which by default frees its
- * resources in a finalizer).</p>
- * <a name=native_library_loading></a>
- * <h2>Native Library Loading</h2>
- * Native libraries loaded via {@link #load(Class)} may be found in
- * <a href="NativeLibrary.html#library_search_paths">several locations</a>.
- * @see Library
- * @author Todd Fast, todd.fast@sun.com
- * @author twall@users.sf.net
- */
- public final class Native implements Version {
- private static final Logger LOG = Logger.getLogger(Native.class.getName());
- public static final Charset DEFAULT_CHARSET;
- public static final String DEFAULT_ENCODING;
- static {
- // JNA used the defaultCharset to determine which encoding to use when
- // converting strings to native char*. The defaultCharset is set from
- // the system property file.encoding. Up to JDK 17 its value defaulted
- // to the system default encoding. From JDK 18 onwards its default value
- // changed to UTF-8.
- // JDK 18+ exposes the native encoding as the new system property
- // native.encoding, prior versions don't have that property and will
- // report NULL for it.
- // The algorithm is simple: If native.encoding is set, it will be used
- // else the original implementation of Charset#defaultCharset is used
- String nativeEncoding = System.getProperty("native.encoding");
- Charset nativeCharset = null;
- if (nativeEncoding != null) {
- try {
- nativeCharset = Charset.forName(nativeEncoding);
- } catch (Exception ex) {
- LOG.log(Level.WARNING, "Failed to get charset for native.encoding value : '" + nativeEncoding + "'", ex);
- }
- }
- if (nativeCharset == null) {
- nativeCharset = Charset.defaultCharset();
- }
- DEFAULT_CHARSET = nativeCharset;
- DEFAULT_ENCODING = nativeCharset.name();
- }
- public static final boolean DEBUG_LOAD = Boolean.getBoolean("jna.debug_load");
- public static final boolean DEBUG_JNA_LOAD = Boolean.getBoolean("jna.debug_load.jna");
- private final static Level DEBUG_JNA_LOAD_LEVEL = DEBUG_JNA_LOAD ? Level.INFO : Level.FINE;
- // Used by tests, do not remove
- static String jnidispatchPath = null;
- private static final Map<Class<?>, Map<String, Object>> typeOptions = Collections.synchronizedMap(new WeakHashMap<Class<?>, Map<String, Object>>());
- private static final Map<Class<?>, Reference<?>> libraries = Collections.synchronizedMap(new WeakHashMap<Class<?>, Reference<?>>());
- private static final String _OPTION_ENCLOSING_LIBRARY = "enclosing-library";
- private static final UncaughtExceptionHandler DEFAULT_HANDLER =
- new UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Callback c, Throwable e) {
- LOG.log(Level.WARNING, "JNA: Callback " + c + " threw the following exception", e);
- }
- };
- private static UncaughtExceptionHandler callbackExceptionHandler = DEFAULT_HANDLER;
- /** The size of a native pointer (<code>void*</code>) on the current
- * platform, in bytes.
- */
- public static final int POINTER_SIZE;
- /** Size of a native <code>long</code> type, in bytes. */
- public static final int LONG_SIZE;
- /** Size of a native <code>wchar_t</code> type, in bytes. */
- public static final int WCHAR_SIZE;
- /** Size of a native <code>size_t</code> type, in bytes. */
- public static final int SIZE_T_SIZE;
- /** Size of a native <code>bool</code> type (C99 and later), in bytes. */
- public static final int BOOL_SIZE;
- /** Size of a native <code>long double</code> type (C99 and later), in bytes. */
- public static final int LONG_DOUBLE_SIZE;
- private static final int TYPE_VOIDP = 0;
- private static final int TYPE_LONG = 1;
- private static final int TYPE_WCHAR_T = 2;
- private static final int TYPE_SIZE_T = 3;
- private static final int TYPE_BOOL = 4;
- private static final int TYPE_LONG_DOUBLE = 5;
- static final int MAX_ALIGNMENT;
- static final int MAX_PADDING;
- /**
- * Version string must have the structure <major>.<minor>.<revision>
- * a bugfix change in the native code increments revision, the minor is
- * incremented for backwards compatible changes and the major version
- * is changed for backwards incompatbile changes.
- *
- * @param expectedVersion
- * @param nativeVersion
- * @return true if nativeVersion describes a version compatible to expectedVersion
- */
- static boolean isCompatibleVersion(String expectedVersion, String nativeVersion) {
- String[] expectedVersionParts = expectedVersion.split("\\.");
- String[] nativeVersionParts = nativeVersion.split("\\.");
- if(expectedVersionParts.length < 3 || nativeVersionParts.length < 3) {
- return false;
- }
- int expectedMajor = Integer.parseInt(expectedVersionParts[0]);
- int nativeMajor = Integer.parseInt(nativeVersionParts[0]);
- int expectedMinor = Integer.parseInt(expectedVersionParts[1]);
- int nativeMinor = Integer.parseInt(nativeVersionParts[1]);
- if(expectedMajor != nativeMajor) {
- return false;
- }
- if(expectedMinor > nativeMinor) {
- return false;
- }
- return true;
- }
- static {
- loadNativeDispatchLibrary();
- if (! isCompatibleVersion(VERSION_NATIVE, getNativeVersion())) {
- String LS = System.getProperty("line.separator");
- throw new Error(LS + LS
- + "There is an incompatible JNA native library installed on this system" + LS
- + "Expected: " + VERSION_NATIVE + LS
- + "Found: " + getNativeVersion() + LS
- + (jnidispatchPath != null
- ? "(at " + jnidispatchPath + ")" : System.getProperty("java.library.path"))
- + "." + LS
- + "To resolve this issue you may do one of the following:" + LS
- + " - remove or uninstall the offending library" + LS
- + " - set the system property jna.nosys=true" + LS
- + " - set jna.boot.library.path to include the path to the version of the " + LS
- + " jnidispatch library included with the JNA jar file you are using" + LS);
- }
- POINTER_SIZE = sizeof(TYPE_VOIDP);
- LONG_SIZE = sizeof(TYPE_LONG);
- WCHAR_SIZE = sizeof(TYPE_WCHAR_T);
- SIZE_T_SIZE = sizeof(TYPE_SIZE_T);
- BOOL_SIZE = sizeof(TYPE_BOOL);
- LONG_DOUBLE_SIZE = sizeof(TYPE_LONG_DOUBLE);
- // Perform initialization of other JNA classes until *after*
- // initializing the above final fields
- initIDs();
- if (Boolean.getBoolean("jna.protected")) {
- setProtected(true);
- }
- MAX_ALIGNMENT = Platform.isSPARC() || Platform.isWindows()
- || (Platform.isLinux() && (Platform.isARM() || Platform.isPPC() || Platform.isMIPS()))
- || Platform.isAIX()
- || (Platform.isAndroid() && !Platform.isIntel())
- ? 8 : LONG_SIZE;
- MAX_PADDING = (Platform.isMac() && Platform.isPPC()) ? 8 : MAX_ALIGNMENT;
- System.setProperty("jna.loaded", "true");
- }
- /** Force a dispose when the Native class is GC'd. */
- private static final Object finalizer = new Object() {
- @Override
- protected void finalize() throws Throwable {
- dispose();
- super.finalize();
- }
- };
- /** Properly dispose of JNA functionality.
- Called when this class is finalized and also from JNI when
- JNA's native shared library is unloaded.
- */
- private static void dispose() {
- CallbackReference.disposeAll();
- Memory.disposeAll();
- NativeLibrary.disposeAll();
- unregisterAll();
- jnidispatchPath = null;
- System.setProperty("jna.loaded", "false");
- }
- /** Remove any automatically unpacked native library.
- This will fail on windows, which disallows removal of any file that is
- still in use, so an alternative is required in that case. Mark
- the file that could not be deleted, and attempt to delete any
- temporaries on next startup.
- Do NOT force the class loader to unload the native library, since
- that introduces issues with cleaning up any extant JNA bits
- (e.g. Memory) which may still need use of the library before shutdown.
- */
- static boolean deleteLibrary(File lib) {
- if (lib.delete()) {
- return true;
- }
- // Couldn't delete it, mark for later deletion
- markTemporaryFile(lib);
- return false;
- }
- private Native() { }
- private static native void initIDs();
- /** Set whether native memory accesses are protected from invalid
- * accesses. This should only be set true when testing or debugging,
- * and should not be considered reliable or robust for applications
- * where JNA native calls are occurring on multiple threads.
- * Protected mode will be automatically set if the
- * system property <code>jna.protected</code> has a value of "true"
- * when the JNA library is first loaded.<p>
- * If not supported by the underlying platform, this setting will
- * have no effect.<p>
- * NOTE: On platforms which support signals (non-Windows), JNA uses
- * signals to trap errors. This may interfere with the JVM's own use of
- * signals. When protected mode is enabled, you should make use of the
- * jsig library, if available (see <a href="http://download.oracle.com/javase/6/docs/technotes/guides/vm/signal-chaining.html">Signal Chaining</a>).
- * In short, set the environment variable <code>LD_PRELOAD</code> to the
- * path to <code>libjsig.so</code> in your JRE lib directory
- * (usually ${java.home}/lib/${os.arch}/libjsig.so) before launching your
- * Java application.
- */
- public static synchronized native void setProtected(boolean enable);
- /** Returns whether protection is enabled. Check the result of this method
- * after calling {@link #setProtected setProtected(true)} to determine
- * if this platform supports protecting memory accesses.
- */
- public static synchronized native boolean isProtected();
- /** Utility method to get the native window ID for a Java {@link Window}
- * as a <code>long</code> value.
- * This method is primarily for X11-based systems, which use an opaque
- * <code>XID</code> (usually <code>long int</code>) to identify windows.
- * @throws HeadlessException if the current VM is running headless
- */
- public static long getWindowID(Window w) throws HeadlessException {
- return AWT.getWindowID(w);
- }
- /** Utility method to get the native window ID for a heavyweight Java
- * {@link Component} as a <code>long</code> value.
- * This method is primarily for X11-based systems, which use an opaque
- * <code>XID</code> (usually <code>long int</code>) to identify windows.
- * @throws HeadlessException if the current VM is running headless
- */
- public static long getComponentID(Component c) throws HeadlessException {
- return AWT.getComponentID(c);
- }
- /** Utility method to get the native window pointer for a Java
- * {@link Window} as a {@link Pointer} value. This method is primarily for
- * w32, which uses the <code>HANDLE</code> type (actually
- * <code>void *</code>) to identify windows.
- * @throws HeadlessException if the current VM is running headless
- */
- public static Pointer getWindowPointer(Window w) throws HeadlessException {
- return new Pointer(AWT.getWindowID(w));
- }
- /** Utility method to get the native window pointer for a heavyweight Java
- * {@link Component} as a {@link Pointer} value. This method is primarily
- * for w32, which uses the <code>HWND</code> type (actually
- * <code>void *</code>) to identify windows.
- * @throws HeadlessException if the current VM is running headless
- */
- public static Pointer getComponentPointer(Component c) throws HeadlessException {
- return new Pointer(AWT.getComponentID(c));
- }
- static native long getWindowHandle0(Component c);
- /** Convert a direct {@link Buffer} into a {@link Pointer}.
- * @throws IllegalArgumentException if the buffer is not direct.
- */
- public static Pointer getDirectBufferPointer(Buffer b) {
- long peer = _getDirectBufferPointer(b);
- return peer == 0 ? null : new Pointer(peer);
- }
- private static native long _getDirectBufferPointer(Buffer b);
- /**
- * Gets the charset belonging to the given {@code encoding}.
- * @param encoding The encoding - if {@code null} then the default platform
- * encoding is used.
- * @return The charset belonging to the given {@code encoding} or the platform default.
- * Never {@code null}.
- */
- private static Charset getCharset(String encoding) {
- Charset charset = null;
- if (encoding != null) {
- try {
- charset = Charset.forName(encoding);
- }
- catch(IllegalCharsetNameException e) {
- LOG.log(Level.WARNING, "JNA Warning: Encoding ''{0}'' is unsupported ({1})",
- new Object[]{encoding, e.getMessage()});
- }
- catch(UnsupportedCharsetException e) {
- LOG.log(Level.WARNING, "JNA Warning: Encoding ''{0}'' is unsupported ({1})",
- new Object[]{encoding, e.getMessage()});
- }
- }
- if (charset == null) {
- LOG.log(Level.WARNING, "JNA Warning: Using fallback encoding {0}", Native.DEFAULT_CHARSET);
- charset = Native.DEFAULT_CHARSET;
- }
- return charset;
- }
- /**
- * Obtain a Java String from the given native byte array. If there is
- * no NUL terminator, the String will comprise the entire array. The
- * encoding is obtained from {@link #getDefaultStringEncoding()}.
- *
- * @param buf The buffer containing the encoded bytes
- * @see #toString(byte[], String)
- */
- public static String toString(byte[] buf) {
- return toString(buf, getDefaultStringEncoding());
- }
- /**
- * Obtain a Java String from the given native byte array, using the given
- * encoding. If there is no NUL terminator, the String will comprise the
- * entire array.
- *
- * <p><strong>Usage note</strong>: This function assumes, that {@code buf}
- * holds a {@code char} array. This means only single-byte encodings are
- * supported.</p>
- *
- * @param buf The buffer containing the encoded bytes. Must not be {@code null}.
- * @param encoding The encoding name - if {@code null} then the platform
- * default encoding will be used
- */
- public static String toString(byte[] buf, String encoding) {
- return Native.toString(buf, Native.getCharset(encoding));
- }
- /**
- * Obtain a Java String from the given native byte array, using the given
- * encoding. If there is no NUL terminator, the String will comprise the
- * entire array.
- *
- * <p><strong>Usage note</strong>: This function assumes, that {@code buf}
- * holds a {@code char} array. This means only single-byte encodings are
- * supported.</p>
- *
- * @param buf The buffer containing the encoded bytes. Must not be {@code null}.
- * @param charset The charset to decode {@code buf}. Must not be {@code null}.
- */
- public static String toString(byte[] buf, Charset charset) {
- int len = buf.length;
- // find out the effective length
- for (int index = 0; index < len; index++) {
- if (buf[index] == 0) {
- len = index;
- break;
- }
- }
- if (len == 0) {
- return "";
- }
- return new String(buf, 0, len, charset);
- }
- /**
- * Obtain a Java String from the given native wchar_t array. If there is
- * no NUL terminator, the String will comprise the entire array.
- *
- * @param buf The buffer containing the characters
- */
- public static String toString(char[] buf) {
- int len = buf.length;
- for (int index = 0; index < len; index++) {
- if (buf[index] == '\0') {
- len = index;
- break;
- }
- }
- if (len == 0) {
- return "";
- } else {
- return new String(buf, 0, len);
- }
- }
- /**
- * Converts a "list" of strings each null terminated
- * into a {@link List} of {@link String} values. The end of the
- * list is signaled by an extra NULL value at the end or by the
- * end of the buffer.
- * @param buf The buffer containing the strings
- * @return A {@link List} of all the strings in the buffer
- * @see #toStringList(char[], int, int)
- */
- public static List<String> toStringList(char[] buf) {
- return toStringList(buf, 0, buf.length);
- }
- /**
- * Converts a "list" of strings each null terminated
- * into a {@link List} of {@link String} values. The end of the
- * list is signaled by an extra NULL value at the end or by the
- * end of the data.
- * @param buf The buffer containing the strings
- * @param offset Offset to start parsing
- * @param len The total characters to parse
- * @return A {@link List} of all the strings in the buffer
- */
- public static List<String> toStringList(char[] buf, int offset, int len) {
- List<String> list = new ArrayList<String>();
- int lastPos = offset;
- int maxPos = offset + len;
- for (int curPos = offset; curPos < maxPos; curPos++) {
- if (buf[curPos] != '\0') {
- continue;
- }
- // check if found the extra null terminator
- if (lastPos == curPos) {
- return list;
- }
- String value = new String(buf, lastPos, curPos - lastPos);
- list.add(value);
- lastPos = curPos + 1; // skip the '\0'
- }
- // This point is reached if there is no double null terminator
- if (lastPos < maxPos) {
- String value = new String(buf, lastPos, maxPos - lastPos);
- list.add(value);
- }
- return list;
- }
- /** Map a library interface to the current process, providing
- * the explicit interface class.
- * Native libraries loaded via this method may be found in
- * <a href="NativeLibrary.html#library_search_paths">several locations</a>.
- * @param <T> Type of expected wrapper
- * @param interfaceClass The implementation wrapper interface
- * @return an instance of the requested interface, mapped to the current
- * process.
- * @throws UnsatisfiedLinkError if the library cannot be found or
- * dependent libraries are missing.
- */
- public static <T extends Library> T load(Class<T> interfaceClass) {
- return load(null, interfaceClass);
- }
- /** Map a library interface to the current process, providing
- * the explicit interface class. Any options provided for the library are
- * cached and associated with the library and any of its defined
- * structures and/or functions.
- * Native libraries loaded via this method may be found in
- * <a href="NativeLibrary.html#library_search_paths">several locations</a>.
- * @param <T> Type of expected wrapper
- * @param interfaceClass The implementation wrapper interface
- * @param options Map of library options
- * @return an instance of the requested interface, mapped to the current
- * process.
- * @throws UnsatisfiedLinkError if the library cannot be found or
- * dependent libraries are missing.
- * @see #load(String, Class, Map)
- */
- public static <T extends Library> T load(Class<T> interfaceClass, Map<String, ?> options) {
- return load(null, interfaceClass, options);
- }
- /** Map a library interface to the given shared library, providing
- * the explicit interface class.
- * If <code>name</code> is null, attempts to map onto the current process.
- * Native libraries loaded via this method may be found in
- * <a href="NativeLibrary.html#library_search_paths">several locations</a>.
- * @param <T> Type of expected wrapper
- * @param name Library base name
- * @param interfaceClass The implementation wrapper interface
- * @return an instance of the requested interface, mapped to the indicated
- * native library.
- * @throws UnsatisfiedLinkError if the library cannot be found or
- * dependent libraries are missing.
- * @see #load(String, Class, Map)
- */
- public static <T extends Library> T load(String name, Class<T> interfaceClass) {
- return load(name, interfaceClass, Collections.<String, Object>emptyMap());
- }
- /** Load a library interface from the given shared library, providing
- * the explicit interface class and a map of options for the library.
- * If no library options are detected the map is interpreted as a map
- * of Java method names to native function names.<p>
- * If <code>name</code> is null, attempts to map onto the current process.
- * Native libraries loaded via this method may be found in
- * <a href="NativeLibrary.html#library_search_paths">several locations</a>.
- * @param <T> Type of expected wrapper
- * @param name Library base name
- * @param interfaceClass The implementation wrapper interface
- * @param options Map of library options
- * @return an instance of the requested interface, mapped to the indicated
- * native library.
- * @throws UnsatisfiedLinkError if the library cannot be found or
- * dependent libraries are missing.
- */
- public static <T extends Library> T load(String name, Class<T> interfaceClass, Map<String, ?> options) {
- if (!Library.class.isAssignableFrom(interfaceClass)) {
- // Maybe still possible if the caller is not using generics?
- throw new IllegalArgumentException("Interface (" + interfaceClass.getSimpleName() + ")"
- + " of library=" + name + " does not extend " + Library.class.getSimpleName());
- }
- Library.Handler handler = new Library.Handler(name, interfaceClass, options);
- ClassLoader loader = interfaceClass.getClassLoader();
- Object proxy = Proxy.newProxyInstance(loader, new Class[] {interfaceClass}, handler);
- cacheOptions(interfaceClass, options, proxy);
- return interfaceClass.cast(proxy);
- }
- /**
- * Provided for improved compatibility between JNA 4.X and 5.X
- *
- * @see Native#load(java.lang.Class)
- */
- @Deprecated
- public static <T> T loadLibrary(Class<T> interfaceClass) {
- return loadLibrary(null, interfaceClass);
- }
- /**
- * Provided for improved compatibility between JNA 4.X and 5.X
- *
- * @see Native#load(java.lang.Class, java.util.Map)
- */
- @Deprecated
- public static <T> T loadLibrary(Class<T> interfaceClass, Map<String, ?> options) {
- return loadLibrary(null, interfaceClass, options);
- }
- /**
- * Provided for improved compatibility between JNA 4.X and 5.X
- *
- * @see Native#load(java.lang.String, java.lang.Class)
- */
- @Deprecated
- public static <T> T loadLibrary(String name, Class<T> interfaceClass) {
- return loadLibrary(name, interfaceClass, Collections.<String, Object>emptyMap());
- }
- /**
- * Provided for improved compatibility between JNA 4.X and 5.X
- *
- * @see Native#load(java.lang.String, java.lang.Class, java.util.Map)
- */
- @Deprecated
- public static <T> T loadLibrary(String name, Class<T> interfaceClass, Map<String, ?> options) {
- if (!Library.class.isAssignableFrom(interfaceClass)) {
- // Maybe still possible if the caller is not using generics?
- throw new IllegalArgumentException("Interface (" + interfaceClass.getSimpleName() + ")"
- + " of library=" + name + " does not extend " + Library.class.getSimpleName());
- }
- Library.Handler handler = new Library.Handler(name, interfaceClass, options);
- ClassLoader loader = interfaceClass.getClassLoader();
- Object proxy = Proxy.newProxyInstance(loader, new Class[] {interfaceClass}, handler);
- cacheOptions(interfaceClass, options, proxy);
- return interfaceClass.cast(proxy);
- }
- /** Attempts to force initialization of an instance of the library interface
- * by loading a public static field of the requisite type.
- * Returns whether an instance variable was instantiated.
- * Expects that lock on libraries is already held
- */
- private static void loadLibraryInstance(Class<?> cls) {
- if (cls != null && !libraries.containsKey(cls)) {
- try {
- Field[] fields = cls.getFields();
- for (int i=0;i < fields.length;i++) {
- Field field = fields[i];
- if (field.getType() == cls
- && Modifier.isStatic(field.getModifiers())) {
- // Ensure the field gets initialized by reading it
- field.setAccessible(true); // interface might be private
- libraries.put(cls, new WeakReference<Object>(field.get(null)));
- break;
- }
- }
- }
- catch (Exception e) {
- throw new IllegalArgumentException("Could not access instance of "
- + cls + " (" + e + ")");
- }
- }
- }
- /**
- * Find the library interface corresponding to the given class. Checks
- * all ancestor classes and interfaces for a declaring class which
- * implements {@link Library}.
- * @param cls The given class
- * @return The enclosing class
- */
- static Class<?> findEnclosingLibraryClass(Class<?> cls) {
- if (cls == null) {
- return null;
- }
- // Check for direct-mapped libraries, which won't necessarily
- // implement com.sun.jna.Library.
- Map<String, ?> libOptions = typeOptions.get(cls);
- if (libOptions != null) {
- Class<?> enclosingClass = (Class<?>)libOptions.get(_OPTION_ENCLOSING_LIBRARY);
- if (enclosingClass != null) {
- return enclosingClass;
- }
- return cls;
- }
- if (Library.class.isAssignableFrom(cls)) {
- return cls;
- }
- if (Callback.class.isAssignableFrom(cls)) {
- cls = CallbackReference.findCallbackClass(cls);
- }
- Class<?> declaring = cls.getDeclaringClass();
- Class<?> fromDeclaring = findEnclosingLibraryClass(declaring);
- if (fromDeclaring != null) {
- return fromDeclaring;
- }
- return findEnclosingLibraryClass(cls.getSuperclass());
- }
- /** Return the preferred native library configuration options for the given
- * class. First attempts to load any field of the interface type within
- * the interface mapping, then checks the cache for any specified library
- * options. If none found, a set of library options will be generated
- * from the fields (by order of precedence) <code>OPTIONS</code> (a {@link
- * Map}), <code>TYPE_MAPPER</code> (a {@link TypeMapper}),
- * <code>STRUCTURE_ALIGNMENT</code> (an {@link Integer}), and
- * <code>STRING_ENCODING</code> (a {@link String}).
- *
- * @param type The type class
- * @return The options map
- */
- public static Map<String, Object> getLibraryOptions(Class<?> type) {
- Map<String, Object> libraryOptions;
- // cached already ?
- libraryOptions = typeOptions.get(type);
- if (libraryOptions != null) {
- return libraryOptions;
- }
- Class<?> mappingClass = findEnclosingLibraryClass(type);
- if (mappingClass != null) {
- loadLibraryInstance(mappingClass);
- } else {
- mappingClass = type;
- }
- libraryOptions = typeOptions.get(mappingClass);
- if (libraryOptions != null) {
- typeOptions.put(type, libraryOptions); // cache for next time
- return libraryOptions;
- }
- try {
- Field field = mappingClass.getField("OPTIONS");
- field.setAccessible(true);
- libraryOptions = (Map<String, Object>) field.get(null);
- if (libraryOptions == null) {
- throw new IllegalStateException("Null options field");
- }
- } catch (NoSuchFieldException e) {
- libraryOptions = Collections.<String, Object>emptyMap();
- } catch (Exception e) {
- throw new IllegalArgumentException("OPTIONS must be a public field of type java.util.Map (" + e + "): " + mappingClass);
- }
- // Make a clone of the original options
- libraryOptions = new HashMap<String, Object>(libraryOptions);
- if (!libraryOptions.containsKey(Library.OPTION_TYPE_MAPPER)) {
- libraryOptions.put(Library.OPTION_TYPE_MAPPER, lookupField(mappingClass, "TYPE_MAPPER", TypeMapper.class));
- }
- if (!libraryOptions.containsKey(Library.OPTION_STRUCTURE_ALIGNMENT)) {
- libraryOptions.put(Library.OPTION_STRUCTURE_ALIGNMENT, lookupField(mappingClass, "STRUCTURE_ALIGNMENT", Integer.class));
- }
- if (!libraryOptions.containsKey(Library.OPTION_STRING_ENCODING)) {
- libraryOptions.put(Library.OPTION_STRING_ENCODING, lookupField(mappingClass, "STRING_ENCODING", String.class));
- }
- libraryOptions = cacheOptions(mappingClass, libraryOptions, null);
- // Store the original lookup class, if different from the mapping class
- if (type != mappingClass) {
- typeOptions.put(type, libraryOptions);
- }
- return libraryOptions;
- }
- private static Object lookupField(Class<?> mappingClass, String fieldName, Class<?> resultClass) {
- try {
- Field field = mappingClass.getField(fieldName);
- field.setAccessible(true);
- return field.get(null);
- }
- catch (NoSuchFieldException e) {
- return null;
- }
- catch (Exception e) {
- throw new IllegalArgumentException(fieldName + " must be a public field of type "
- + resultClass.getName() + " ("
- + e + "): " + mappingClass);
- }
- }
- /** Return the preferred {@link TypeMapper} for the given native interface.
- * See {@link com.sun.jna.Library#OPTION_TYPE_MAPPER}.
- */
- public static TypeMapper getTypeMapper(Class<?> cls) {
- Map<String, ?> options = getLibraryOptions(cls);
- return (TypeMapper) options.get(Library.OPTION_TYPE_MAPPER);
- }
- /**
- * @param cls The native interface type
- * @return The preferred string encoding for the given native interface.
- * If there is no setting, defaults to the {@link #getDefaultStringEncoding()}.
- * @see com.sun.jna.Library#OPTION_STRING_ENCODING
- */
- public static String getStringEncoding(Class<?> cls) {
- Map<String, ?> options = getLibraryOptions(cls);
- String encoding = (String) options.get(Library.OPTION_STRING_ENCODING);
- return encoding != null ? encoding : getDefaultStringEncoding();
- }
- /**
- * @return The default string encoding. Returns the value of the system
- * property <code>jna.encoding</code> or {@link Native#DEFAULT_ENCODING}.
- */
- public static String getDefaultStringEncoding() {
- return System.getProperty("jna.encoding", DEFAULT_ENCODING);
- }
- /**
- * @param cls The native interface type
- * @return The preferred structure alignment for the given native interface.
- * @see com.sun.jna.Library#OPTION_STRUCTURE_ALIGNMENT
- */
- public static int getStructureAlignment(Class<?> cls) {
- Integer alignment = (Integer)getLibraryOptions(cls).get(Library.OPTION_STRUCTURE_ALIGNMENT);
- return alignment == null ? Structure.ALIGN_DEFAULT : alignment;
- }
- /**
- * @param s The input string
- * @return A byte array corresponding to the given String. The encoding
- * used is obtained from {@link #getDefaultStringEncoding()}.
- */
- static byte[] getBytes(String s) {
- return getBytes(s, getDefaultStringEncoding());
- }
- /**
- * @param s The string. Must not be {@code null}.
- * @param encoding The encoding - if {@code null} then the default platform
- * encoding is used
- * @return A byte array corresponding to the given String, using the given
- * encoding. If the encoding is not found default to the platform native
- * encoding.
- */
- static byte[] getBytes(String s, String encoding) {
- return Native.getBytes(s, Native.getCharset(encoding));
- }
- /**
- * @param s The string. Must not be {@code null}.
- * @param charset The charset used to encode {@code s}. Must not be {@code null}.
- * @return A byte array corresponding to the given String, using the given
- * charset.
- */
- static byte[] getBytes(String s, Charset charset) {
- return s.getBytes(charset);
- }
- /**
- * @param s The string
- * @return A NUL-terminated byte buffer equivalent to the given String,
- * using the encoding returned by {@link #getDefaultStringEncoding()}.
- * @see #toByteArray(String, String)
- */
- public static byte[] toByteArray(String s) {
- return toByteArray(s, getDefaultStringEncoding());
- }
- /**
- * @param s The string. Must not be {@code null}.
- * @param encoding The encoding - if {@code null} then the default platform
- * encoding is used
- * @return A NUL-terminated byte buffer equivalent to the given String,
- * using the given encoding.
- * @see #getBytes(String, String)
- */
- public static byte[] toByteArray(String s, String encoding) {
- return Native.toByteArray(s, Native.getCharset(encoding));
- }
- /**
- * @param s The string. Must not be {@code null}.
- * @param charset The charset used to encode {@code s}. Must not be {@code null}.
- * @return A NUL-terminated byte buffer equivalent to the given String,
- * using the given charset.
- * @see #getBytes(String, String)
- */
- public static byte[] toByteArray(String s, Charset charset) {
- byte[] bytes = Native.getBytes(s, charset);
- byte[] buf = new byte[bytes.length+1];
- System.arraycopy(bytes, 0, buf, 0, bytes.length);
- return buf;
- }
- /**
- * @param s The string
- * @return A NUL-terminated wide character buffer equivalent to the given string.
- */
- public static char[] toCharArray(String s) {
- char[] chars = s.toCharArray();
- char[] buf = new char[chars.length+1];
- System.arraycopy(chars, 0, buf, 0, chars.length);
- return buf;
- }
- /**
- * Loads the JNA stub library.
- * First tries jna.boot.library.path, then the system path, then from the
- * jar file.
- */
- private static void loadNativeDispatchLibrary() {
- if (!Boolean.getBoolean("jna.nounpack")) {
- try {
- removeTemporaryFiles();
- }
- catch(IOException e) {
- LOG.log(Level.WARNING, "JNA Warning: IOException removing temporary files", e);
- }
- }
- String libName = System.getProperty("jna.boot.library.name", "jnidispatch");
- String bootPath = System.getProperty("jna.boot.library.path");
- if (bootPath != null) {
- // String.split not available in 1.4
- StringTokenizer dirs = new StringTokenizer(bootPath, File.pathSeparator);
- while (dirs.hasMoreTokens()) {
- String dir = dirs.nextToken();
- File file = new File(new File(dir), System.mapLibraryName(libName).replace(".dylib", ".jnilib"));
- String path = file.getAbsolutePath();
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Looking in {0}", path);
- if (file.exists()) {
- try {
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", path);
- System.setProperty("jnidispatch.path", path);
- System.load(path);
- jnidispatchPath = path;
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", path);
- return;
- } catch (UnsatisfiedLinkError ex) {
- // Not a problem if already loaded in anoteher class loader
- // Unfortunately we can't distinguish the difference...
- //System.out.println("File found at " + file + " but not loadable: " + ex.getMessage());
- }
- }
- if (Platform.isMac()) {
- String orig, ext;
- if (path.endsWith("dylib")) {
- orig = "dylib";
- ext = "jnilib";
- } else {
- orig = "jnilib";
- ext = "dylib";
- }
- path = path.substring(0, path.lastIndexOf(orig)) + ext;
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Looking in {0}", path);
- if (new File(path).exists()) {
- try {
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", path);
- System.setProperty("jnidispatch.path", path);
- System.load(path);
- jnidispatchPath = path;
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", path);
- return;
- } catch (UnsatisfiedLinkError ex) {
- LOG.log(Level.WARNING, "File found at " + path + " but not loadable: " + ex.getMessage(), ex);
- }
- }
- }
- }
- }
- String jnaNosys = System.getProperty("jna.nosys", "true");
- if ((!Boolean.parseBoolean(jnaNosys)) || Platform.isAndroid()) {
- try {
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying (via loadLibrary) {0}", libName);
- System.loadLibrary(libName);
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch on system path");
- return;
- }
- catch(UnsatisfiedLinkError e) {
- }
- }
- if (!Boolean.getBoolean("jna.noclasspath")) {
- loadNativeDispatchLibraryFromClasspath();
- }
- else {
- throw new UnsatisfiedLinkError("Unable to locate JNA native support library");
- }
- }
- static final String JNA_TMPLIB_PREFIX = "jna";
- /**
- * Attempts to load the native library resource from the filesystem,
- * extracting the JNA stub library from jna.jar if not already available.
- */
- private static void loadNativeDispatchLibraryFromClasspath() {
- try {
- String mappedName = System.mapLibraryName("jnidispatch").replace(".dylib", ".jnilib");
- if(Platform.isAIX()) {
- // OpenJDK is reported to map to .so -- this works around the
- // difference between J9 and OpenJDK
- mappedName = "libjnidispatch.a";
- }
- String libName = "/com/sun/jna/" + Platform.RESOURCE_PREFIX + "/" + mappedName;
- File lib = extractFromResourcePath(libName, Native.class.getClassLoader());
- if (lib == null) {
- if (lib == null) {
- throw new UnsatisfiedLinkError("Could not find JNA native support");
- }
- }
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Trying {0}", lib.getAbsolutePath());
- System.setProperty("jnidispatch.path", lib.getAbsolutePath());
- System.load(lib.getAbsolutePath());
- jnidispatchPath = lib.getAbsolutePath();
- LOG.log(DEBUG_JNA_LOAD_LEVEL, "Found jnidispatch at {0}", jnidispatchPath);
- // Attempt to delete immediately once jnidispatch is successfully
- // loaded. This avoids the complexity of trying to do so on "exit",
- // which point can vary under different circumstances (native
- // compilation, dynamically loaded modules, normal application, etc).
- if (isUnpacked(lib)
- && !Boolean.getBoolean("jnidispatch.preserve")) {
- deleteLibrary(lib);
- }
- }
- catch(IOException e) {
- throw new UnsatisfiedLinkError(e.getMessage());
- }
- }
- /** Identify temporary files unpacked from classpath jar files. */
- static boolean isUnpacked(File file) {
- return file.getName().startsWith(JNA_TMPLIB_PREFIX);
- }
- /** Attempt to extract a native library from the current resource path,
- * using the current thread context class loader.
- * @param name Base name of native library to extract. May also be an
- * absolute resource path (i.e. starts with "/"), in which case the
- * no transformations of the library name are performed. If only the base
- * name is given, the resource path is attempted both with and without
- * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via
- * {@link NativeLibrary#mapSharedLibraryName(String)}.
- * @return File indicating extracted resource on disk
- * @throws IOException if resource not found
- */
- public static File extractFromResourcePath(String name) throws IOException {
- return extractFromResourcePath(name, null);
- }
- /** Attempt to extract a native library from the resource path using the
- * given class loader.
- * @param name Base name of native library to extract. May also be an
- * absolute resource path (i.e. starts with "/"), in which case the
- * no transformations of the library name are performed. If only the base
- * name is given, the resource path is attempted both with and without
- * {@link Platform#RESOURCE_PREFIX}, after mapping the library name via
- * {@link NativeLibrary#mapSharedLibraryName(String)}.
- * @param loader Class loader to use to load resources
- * @return File indicating extracted resource on disk
- * @throws IOException if resource not found
- */
- public static File extractFromResourcePath(String name, ClassLoader loader) throws IOException {
- final Level DEBUG = (DEBUG_LOAD
- || (DEBUG_JNA_LOAD && name.contains("jnidispatch"))) ? Level.INFO : Level.FINE;
- if (loader == null) {
- loader = Thread.currentThread().getContextClassLoader();
- // Context class loader is not guaranteed to be set
- if (loader == null) {
- loader = Native.class.getClassLoader();
- }
- }
- LOG.log(DEBUG, "Looking in classpath from {0} for {1}", new Object[]{loader, name});
- String libname = name.startsWith("/") ? name : NativeLibrary.mapSharedLibraryName(name);
- String resourcePath = name.startsWith("/") ? name : Platform.RESOURCE_PREFIX + "/" + libname;
- if (resourcePath.startsWith("/")) {
- resourcePath = resourcePath.substring(1);
- }
- URL url = loader.getResource(resourcePath);
- if (url == null) {
- if (resourcePath.startsWith(Platform.RESOURCE_PREFIX)) {
- // Fallback for legacy darwin behaviour: darwin was in the past
- // special cased in that all architectures were mapped to the same
- // prefix and it was expected, that a fat binary was present at that
- // point, that contained all architectures.
- if(Platform.RESOURCE_PREFIX.startsWith("darwin")) {
- url = loader.getResource("darwin/" + resourcePath.substring(Platform.RESOURCE_PREFIX.length() + 1));
- }
- if (url == null) {
- // If not found with the standard resource prefix, try without it
- url = loader.getResource(libname);
- }
- } else if (resourcePath.startsWith("com/sun/jna/" + Platform.RESOURCE_PREFIX + "/")) {
- // Fallback for legacy darwin behaviour: darwin was in the past
- // special cased in that all architectures were mapped to the same
- // prefix and it was expected, that a fat binary was present at that
- // point, that contained all architectures.
- if(Platform.RESOURCE_PREFIX.startsWith("com/sun/jna/darwin")) {
- url = loader.getResource("com/sun/jna/darwin" + resourcePath.substring(("com/sun/jna/" + Platform.RESOURCE_PREFIX).length() + 1));
- }
- if (url == null) {
- // If not found with the standard resource prefix, try without it
- url = loader.getResource(libname);
- }
- }
- }
- if (url == null) {
- String path = System.getProperty("java.class.path");
- if (loader instanceof URLClass…
Large files files are truncated, but you can click here to view the full file