NativeCodeLoader.java
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.commons.crypto.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import java.util.UUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A helper to load the native code i.e. libcommons-crypto.so.
* This handles the fallback to either the bundled libcommons-crypto-Linux-i386-32.so
* or the default java implementations where appropriate.
*/
public class NativeCodeLoader {
private static final Log LOG =
LogFactory.getLog(NativeCodeLoader.class);
private static boolean nativeCodeLoaded = false;
private NativeCodeLoader() {}
static {
// Try to load native library and set fallback flag appropriately
if(LOG.isDebugEnabled()) {
LOG.debug("Trying to load the custom-built native-commons-crypto library...");
}
try {
File nativeLibFile = findNativeLibrary();
if (nativeLibFile != null) {
// Load extracted or specified native library.
System.load(nativeLibFile.getAbsolutePath());
} else {
// Load preinstalled library (in the path -Djava.library.path)
System.loadLibrary("commons-crypto");
}
LOG.debug("Loaded the native library");
nativeCodeLoaded = true;
} catch (Throwable t) {
// Ignore failure to load
if(LOG.isDebugEnabled()) {
LOG.debug("Failed to load native library with error: " + t);
LOG.debug("java.library.path=" +
System.getProperty("java.library.path"));
}
}
if (!nativeCodeLoaded) {
LOG.warn("Unable to load native library for the platform... " +
"using builtin-java classes where applicable");
}
}
static File findNativeLibrary() {
// Try to load the library in commons-crypto.lib.path */
String nativeLibraryPath = Utils
.getLibPath();
String nativeLibraryName = Utils
.getLibName();
// Resolve the library file name with a suffix (e.g., dll, .so, etc.)
if (nativeLibraryName == null)
nativeLibraryName = System.mapLibraryName("commons-crypto");
if (nativeLibraryPath != null) {
File nativeLib = new File(nativeLibraryPath,
nativeLibraryName);
if (nativeLib.exists())
return nativeLib;
}
// Load an OS-dependent native library inside a jar file
nativeLibraryPath = "/org/apache/commons/crypto/native/"
+ OSInfo.getNativeLibFolderPathForCurrentOS();
boolean hasNativeLib = hasResource(nativeLibraryPath + "/"
+ nativeLibraryName);
if(!hasNativeLib) {
if (OSInfo.getOSName().equals("Mac")) {
// Fix for openjdk7 for Mac
String altName = "libcommons-crypto.jnilib";
if (hasResource(nativeLibraryPath + "/" + altName)) {
nativeLibraryName = altName;
hasNativeLib = true;
}
}
}
if (!hasNativeLib) {
String errorMessage = String.format(
"no native library is found for os.name=%s and os.arch=%s",
OSInfo.getOSName(), OSInfo.getArchName());
throw new RuntimeException(errorMessage);
}
// Temporary folder for the native lib. Use the value of
// commons-crypto.tempdir or java.io.tmpdir
String tempFolder = new File(Utils.getTmpDir())
.getAbsolutePath();
// Extract and load a native library inside the jar file
return extractLibraryFile(nativeLibraryPath,
nativeLibraryName, tempFolder);
}
/**
* Extracts the specified library file to the target folder.
*
* @param libFolderForCurrentOS the library in commons-crypto.lib.path.
* @param libraryFileName the library name.
* @param targetFolder Target folder for the native lib. Use the value of
* commons-crypto.tempdir or java.io.tmpdir.
* @return the library file.
*/
private static File extractLibraryFile(String libFolderForCurrentOS,
String libraryFileName, String targetFolder) {
String nativeLibraryFilePath = libFolderForCurrentOS + "/"
+ libraryFileName;
// Attach UUID to the native library file to ensure multiple class loaders
// can read the libcommons-crypto multiple times.
String uuid = UUID.randomUUID().toString();
String extractedLibFileName = String.format("commons-crypto-%s-%s-%s", getVersion(), uuid,
libraryFileName);
File extractedLibFile = new File(targetFolder, extractedLibFileName);
InputStream reader = null;
try {
// Extract a native library file into the target directory
reader = NativeCodeLoader.class.getResourceAsStream(nativeLibraryFilePath);
FileOutputStream writer = new FileOutputStream(extractedLibFile);
try {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, bytesRead);
}
} finally {
// Delete the extracted lib file on JVM exit.
extractedLibFile.deleteOnExit();
if (writer != null){
writer.close();
}
if (reader != null) {
reader.close();
reader = null;
}
}
// Set executable (x) flag to enable Java to load the native library
if (!extractedLibFile.setReadable(true) || !extractedLibFile.setExecutable(true)
|| !extractedLibFile.setWritable(true, true)) {
throw new RuntimeException("Invalid path for library path");
}
// Check whether the contents are properly copied from the resource folder
{
InputStream nativeIn = null;
InputStream extractedLibIn = null;
try {
nativeIn =
NativeCodeLoader.class.getResourceAsStream(nativeLibraryFilePath);
extractedLibIn = new FileInputStream(extractedLibFile);
if (!contentsEquals(nativeIn, extractedLibIn))
throw new RuntimeException(String.format(
"Failed to write a native library file at %s",
extractedLibFile));
} finally {
if (nativeIn != null)
nativeIn.close();
if (extractedLibIn != null)
extractedLibIn.close();
}
}
return new File(targetFolder, extractedLibFileName);
} catch (IOException e) {
e.printStackTrace(System.err);
return null;
} finally{
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Gets the version by reading pom.properties embedded in jar.
* This version data is used as a suffix of a dll file extracted from the
* jar.
*
* @return the version string
*/
public static String getVersion() {
URL versionFile = NativeCodeLoader.class
.getResource("/META-INF/maven/org.apache.commons.crypto/commons-crypto/pom.properties");
if (versionFile == null)
versionFile = NativeCodeLoader.class
.getResource("/org/apache/commons/crypto/VERSION");
String version = "unknown";
try {
if (versionFile != null) {
Properties versionData = new Properties();
versionData.load(versionFile.openStream());
version = versionData.getProperty("version", version);
if (version.equals("unknown"))
version = versionData.getProperty("VERSION", version);
version = version.trim().replaceAll("[^0-9M\\.]", "");
}
} catch (IOException e) {
System.err.println(e);
}
return version;
}
private static boolean contentsEquals(InputStream in1, InputStream in2)
throws IOException {
if (!(in1 instanceof BufferedInputStream)) {
in1 = new BufferedInputStream(in1);
}
if (!(in2 instanceof BufferedInputStream)) {
in2 = new BufferedInputStream(in2);
}
int ch = in1.read();
while (ch != -1) {
int ch2 = in2.read();
if (ch != ch2)
return false;
ch = in1.read();
}
int ch2 = in2.read();
return ch2 == -1;
}
private static boolean hasResource(String path) {
return NativeCodeLoader.class.getResource(path) != null;
}
/**
* Checks whether native code is loaded for this platform.
*
* @return <code>true</code> if native is loaded,
* else <code>false</code>.
*/
public static boolean isNativeCodeLoaded() {
return nativeCodeLoaded;
}
}