/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.arakhne.afc.vmutil;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import org.eclipse.xtext.xbase.lib.Inline;
import org.eclipse.xtext.xbase.lib.Pure;
/**
* This class provides more generic means for loading
* dynamical libraries.
*
* <p>The library loader may be enabled or not.
* When library loader is enable, it is able to
* retreive and load native libraries. When it is
* disable, it ignore all the loading queries.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public final class LibraryLoader {
private static final int BUFFER_SIZE = 4096;
private static volatile boolean disable;
private LibraryLoader() {
//
}
/** Replies if this library loader is enable.
*
* <p>The library loader is able to load native libraries
* when it is enable. Otherwise it ignore all the loading
* queries.
*
* @return <code>true</code> if the library loader is enable,
* otherwise <code>false</code>
* @since 5.0
*/
@Pure
public static boolean isEnable() {
return !disable;
}
/** Replies if this library loader is enable.
*
* <p>The library loader is able to load native libraries
* when it is enable. Otherwise it ignore all the loading
* queries.
*
* @param enable is <code>true</code> to allow this loader
* to retreive native libraries, or <code>false</code> to
* ignore all the loading queries.
* @since 5.0
*/
public static void setEnable(boolean enable) {
disable = !enable;
}
/** Loads a code file with the specified filename from the local file
* system as a dynamic library. The filename
* argument must be a complete path name.
*
* <p>The call <code>LibraryLoader.load(name)</code> is effectively equivalent
* to the call:
* <blockquote><pre>
* System.load(name)
* </pre></blockquote>
*
* @param filename is the file to load.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the file does not exist.
* @throws NullPointerException if <code>filename</code> is
* <code>null</code>
* @see java.lang.System#load(java.lang.String)
*/
public static void load(String filename) {
// Silently ignore loading query
if (disable) {
return;
}
Runtime.getRuntime().load(filename);
}
/**
* Loads a code file with the specified filename from the local file
* system as a dynamic library. The filename
* argument must be a complete path name.
*
* @param filename is the file to load.
* @throws IOException when reading error occurs.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the file does not exist.
* @throws NullPointerException if <code>filename</code> is
* <code>null</code>
* @see java.lang.System#load(java.lang.String)
*/
public static void load(URL filename) throws IOException {
// Silently ignore loading query
if (disable) {
return;
}
if (URISchemeType.FILE.isURL(filename)) {
try {
load(new File(filename.toURI()));
} catch (URISyntaxException e) {
throw new FileNotFoundException(filename.toExternalForm());
}
} else {
// Create a tmp file to receive the library code.
final String libName = System.mapLibraryName("javaDynLib"); //$NON-NLS-1$
String suffix = ".dll"; //$NON-NLS-1$
String prefix = "javaDynLib"; //$NON-NLS-1$
final int pos = libName.lastIndexOf('.');
if (pos >= 0) {
suffix = libName.substring(pos);
prefix = libName.substring(0, pos);
}
final File file = File.createTempFile(prefix, suffix);
// Copy the library code into the local file
try (FileOutputStream outs = new FileOutputStream(file)) {
try (InputStream ins = filename.openStream()) {
final byte[] buffer = new byte[BUFFER_SIZE];
int lu;
while ((lu = ins.read(buffer)) > 0) {
outs.write(buffer, 0, lu);
}
}
}
// Load the library from the local file
load(file);
// Delete local file
file.deleteOnExit();
}
}
/** Loads a code file with the specified filename from the local file
* system as a dynamic library. The filename
* argument must be a complete path name.
*
* <p>The call <code>LibraryLoader.load(name)</code> is effectively equivalent
* to the call:
* <blockquote><pre>
* System.load(name.getAbsolutePath())
* </pre></blockquote>
*
* @param filename is the file to load.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the file does not exist.
* @throws NullPointerException if <code>filename</code> is
* <code>null</code>
* @see java.lang.System#load(java.lang.String)
*/
public static void load(File filename) {
// Silently ignore loading query
if (disable) {
return;
}
Runtime.getRuntime().load(filename.getAbsolutePath());
}
/**
* Loads the system library specified by the <code>libname</code>
* argument. The manner in which a library name is mapped to the
* actual system library is system dependent.
*
* <p>The call <code>LibraryLoader.loadLibrary(name)</code> is effectively
* equivalent to the call
* <blockquote><pre>
* System.loadLibrary(name)
* </pre></blockquote>
*
* @param libname the name of the library.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the library does not exist.
* @throws NullPointerException if <code>libname</code> is
* <code>null</code>
* @see java.lang.System#loadLibrary(java.lang.String)
*/
public static void loadLibrary(String libname) {
// Silently ignore loading query
if (disable) {
return;
}
Runtime.getRuntime().loadLibrary(libname);
}
/** Replies the URL for the specified library.
*
* @param libName is the name of the library
* @return the URL where the specified library was located.
*/
@Pure
public static URL findLibraryURL(String libName) {
return findLibraryURL(null, libName, null, null);
}
/** Replies the URL for the specified library.
*
* <p>The call <code>LibraryLoader.findLibraryURL(path,name)</code> is effectively equivalent
* to the call:
* <blockquote><pre>
* getClassLoader().getResource(path+System.mapLibraryName(name))
* </pre></blockquote>
*
* @param path is the resource's path where the library was located.
* @param libName is the name of the library
* @return the URL where the specified library was located.
*/
@Pure
public static URL findLibraryURL(String path, String libName) {
return findLibraryURL(path, libName, null, null);
}
private static URL findLibraryURL(String path, String libName, String platform, String arch) {
final ClassLoader cl = ClassLoaderFinder.findClassLoader();
assert cl != null;
String resourcePath = path;
if (resourcePath == null) {
resourcePath = ""; //$NON-NLS-1$
} else if ((resourcePath.length() > 0) && (!resourcePath.endsWith("/"))) { //$NON-NLS-1$
resourcePath += "/"; //$NON-NLS-1$
}
// Find the 64bits version of the DLL
String realLibName;
if (platform != null) {
final StringBuilder buf = new StringBuilder(libName);
buf.append("-"); //$NON-NLS-1$
buf.append(platform);
if (arch != null) {
buf.append(arch);
}
realLibName = System.mapLibraryName(buf.toString());
final int idx = realLibName.indexOf(libName);
if (idx > 0) {
realLibName = realLibName.substring(idx);
}
} else {
final StringBuilder buf = new StringBuilder(libName);
if (arch != null) {
buf.append(arch);
}
realLibName = System.mapLibraryName(buf.toString());
}
final URL libRes = Resources.getResource(cl, resourcePath + realLibName);
if (libRes != null) {
return libRes;
}
return Resources.getResource(cl, realLibName);
}
/** Replies the data model of the current operating system: 32 or 64 bits.
*
* @return the integer which is corresponding to the data model, or <code>0</code> if
* it could not be determined.
*/
@Pure
static int getOperatingSystemArchitectureDataModel() {
final String arch = System.getProperty("sun.arch.data.model"); //$NON-NLS-1$
if (arch != null) {
try {
return Integer.parseInt(arch);
} catch (AssertionError e) {
throw e;
} catch (Throwable exception) {
//
}
}
return 0;
}
@SuppressWarnings("checkstyle:magicnumber")
private static URL getPlatformDependentLibrary(String[] paths, String libname, String platform) {
URL url;
final int dataModel = getOperatingSystemArchitectureDataModel();
for (final String path : paths) {
if (dataModel == 64) {
// Load the 64 library
url = findLibraryURL(path, libname, platform, "64"); //$NON-NLS-1$
if (url != null) {
return url;
}
} else if (dataModel == 32) {
// Load the 32 library
url = findLibraryURL(path, libname, platform, "32"); //$NON-NLS-1$
if (url != null) {
return url;
}
}
// Load the multi-platform library
url = findLibraryURL(path, libname, platform, null);
if (url != null) {
return url;
}
}
return null;
}
/**
* Search and load the dynamic library which is fitting the
* current operating system (32 or 64bits operating system...).
* A 64 bits library is assumed to be named <code>libname64.dll</code>
* on Windows® and <code>liblibname64.so</code> on Unix.
* A 32 bits library is assumed to be named <code>libname32.dll</code>
* on Windows® and <code>liblibname32.so</code> on Unix.
* A library which could be ran either on 32 and 64 platforms is assumed
* to be named <code>libname.dll</code> on Windows® and
* <code>liblibname.so</code> on Unix.
*
* @param libname is the name of the library.
* @throws IOException when reading error occurs.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the file does not exist.
* @throws NullPointerException if <code>filename</code> is
* <code>null</code>
* @see java.lang.System#load(java.lang.String)
*/
@Inline(value = "LibraryLoader.loadPlatformDependentLibrary(null, $1)", imported = {LibraryLoader.class},
statementExpression = true)
public static void loadPlatformDependentLibrary(String libname) throws IOException {
loadPlatformDependentLibrary(null, libname);
}
/**
* Search and load the dynamic library which is fitting the
* current operating system (32 or 64bits operating system...).
* A 64 bits library is assumed to be named <code>libname64.dll</code>
* on Windows® and <code>liblibname64.so</code> on Unix.
* A 32 bits library is assumed to be named <code>libname32.dll</code>
* on Windows® and <code>liblibname32.so</code> on Unix.
* A library which could be ran either on 32 and 64 platforms is assumed
* to be named <code>libname.dll</code> on Windows® and
* <code>liblibname.so</code> on Unix.
*
* @param path is the resource's path where the library was located.
* @param libname is the name of the library.
* @throws IOException when reading error occurs.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the file does not exist.
* @throws NullPointerException if <code>filename</code> is
* <code>null</code>
* @see java.lang.System#load(java.lang.String)
*/
public static void loadPlatformDependentLibrary(String path, String libname) throws IOException {
loadPlatformDependentLibrary(libname, null, path);
}
/**
* Search and load the dynamic library which is fitting the
* current operating system (32 or 64bits operating system...).
* A 64 bits library is assumed to be named <code>libname64.dll</code>
* on Windows® and <code>liblibname64.so</code> on Unix.
* A 32 bits library is assumed to be named <code>libname32.dll</code>
* on Windows® and <code>liblibname32.so</code> on Unix.
* A library which could be ran either on 32 and 64 platforms is assumed
* to be named <code>libname.dll</code> on Windows® and
* <code>liblibname.so</code> on Unix.
*
* @param libname is the name of the library.
* @param platform is the name of the current OS platform.
* @param paths are the resource's paths where the library was located.
* @throws IOException when reading error occurs.
* @throws SecurityException if a security manager exists and its
* <code>checkLink</code> method doesn't allow
* loading of the specified dynamic library
* @throws UnsatisfiedLinkError if the file does not exist.
* @throws NullPointerException if <code>filename</code> is
* <code>null</code>
* @see java.lang.System#load(java.lang.String)
*/
static void loadPlatformDependentLibrary(String libname, String platform, String... paths) throws IOException {
URL url;
// Package version (according to Maven module)
url = getPlatformDependentLibrary(paths, libname, null);
if (url != null) {
try {
load(url);
// library loaded
return;
} catch (AssertionError e) {
throw e;
} catch (Throwable e) {
//
}
}
// Eclipse version (according to Maven module)
if (platform != null) {
url = getPlatformDependentLibrary(paths, libname, platform);
if (url != null) {
try {
load(url);
// library loaded
return;
} catch (AssertionError e) {
throw e;
} catch (Throwable e) {
//
}
}
}
// System-based loading
loadLibrary(libname);
}
}