Extend to load binaries as well

This commit is contained in:
2016-08-17 20:24:42 +01:00
parent 79ecf1f008
commit e3b807d23a
13 changed files with 219 additions and 193 deletions

View File

@@ -7,7 +7,7 @@ task wrapper(type: Wrapper) {
group = 'com.github.boukefalos'
archivesBaseName = 'jlibloader'
version = '0.2'
version = '0.3'
uploadArchives {
repositories.mavenDeployer {

View File

@@ -1,29 +1,17 @@
/*
* Copyright 2012 Adam Murdoch
*
* 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 com.github.boukefalos.jlibloader;
import java.io.File;
import com.github.boukefalos.jlibloader.internal.NativeBinaryLoader;
import com.github.boukefalos.jlibloader.internal.NativeBinaryLocator;
import com.github.boukefalos.jlibloader.internal.NativeLibraryLoader;
import com.github.boukefalos.jlibloader.internal.NativeLibraryLocator;
import com.github.boukefalos.jlibloader.internal.Platform;
public class Native {
private static NativeLibraryLoader loader;
private static Platform platform;
private static NativeLibraryLoader libraryLoader;
private static NativeBinaryLoader binaryLoader;
private Native() {
}
@@ -37,12 +25,13 @@ public class Native {
* @throws NativeLibraryUnavailableException When the native library is not available on the current machine.
* @throws NativeException On failure to load the native library.
*/
static public void init(File extractDir) throws NativeLibraryUnavailableException, NativeException {
static public void init(File extractDir) throws NativeException {
synchronized (Native.class) {
if (loader == null) {
Platform platform = Platform.current();
if (platform == null) {
platform = Platform.current();
try {
loader = new NativeLibraryLoader(platform, new NativeLibraryLocator(extractDir));
libraryLoader = new NativeLibraryLoader(platform, new NativeLibraryLocator(extractDir));
binaryLoader = new NativeBinaryLoader(platform, new NativeBinaryLocator(extractDir));
} catch (NativeException e) {
throw e;
} catch (Throwable t) {
@@ -59,12 +48,26 @@ public class Native {
public static void load(String group, String name, String file) {
init(null);
try {
loader.load(group, name, Platform.current().getLibraryName(file));
libraryLoader.load(group, name, Platform.current().getLibraryName(file));
} catch (NativeException e) {
throw e;
throw e;
} catch (Throwable t) {
throw new NativeException("Failed to load native library.");
}
throw new NativeException("Failed to load native library.");
}
}
public static void binary(String group, String name) {
binary(group, name, name);
}
public static String binary(String group, String name, String file) {
init(null);
try {
return binaryLoader.load(group, name, Platform.current().getBinaryName(file));
} catch (NativeException e) {
throw e;
} catch (Throwable t) {
throw new NativeException("Failed to load native binary.");
}
}
}

View File

@@ -0,0 +1,12 @@
package com.github.boukefalos.jlibloader;
/**
* Thrown when a given integration is not available for the current machine.
*/
public class NativeBinaryUnavailableException extends NativeException {
private static final long serialVersionUID = 1L;
public NativeBinaryUnavailableException(String message) {
super(message);
}
}

View File

@@ -1,19 +1,3 @@
/*
* Copyright 2012 Adam Murdoch
*
* 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 com.github.boukefalos.jlibloader;
public class NativeException extends RuntimeException {

View File

@@ -1,19 +1,3 @@
/*
* Copyright 2012 Adam Murdoch
*
* 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 com.github.boukefalos.jlibloader;
/**

View File

@@ -0,0 +1,37 @@
package com.github.boukefalos.jlibloader.internal;
import java.io.File;
import java.util.HashMap;
import com.github.boukefalos.jlibloader.NativeBinaryUnavailableException;
import com.github.boukefalos.jlibloader.NativeException;
public class NativeBinaryLoader extends NativeLoader {
private HashMap<String,String> binaryFileMap = new HashMap<String,String>();
private NativeBinaryLocator nativeBinaryLocator;
public NativeBinaryLoader(Platform platform, NativeBinaryLocator nativeBinaryLocator) {
this.platform = platform;
this.nativeBinaryLocator = nativeBinaryLocator;
}
public String load(String binaryGroupName, String binaryName, String binaryFileName) {
if (binaryFileMap.containsKey(binaryFileName)) {
return binaryFileMap.get(binaryFileName);
}
try {
File binFile = nativeBinaryLocator.find(new NativeDef(binaryGroupName, binaryName, binaryFileName, platform.getId()));
if (binFile == null) {
throw new NativeBinaryUnavailableException(String.format("Native binary '%s' is not available for %s.", binaryFileName, platform));
}
String binaryPath = binFile.getAbsolutePath();
binaryFileMap.put(binaryFileName, binaryPath);
return binaryPath;
} catch (NativeException e) {
throw e;
} catch (Throwable t) {
t.printStackTrace();
throw new NativeException(String.format("Failed to load native library '%s' for %s.", binaryFileName, platform), t);
}
}
}

View File

@@ -0,0 +1,9 @@
package com.github.boukefalos.jlibloader.internal;
import java.io.File;
public class NativeBinaryLocator extends NativeLocator {
public NativeBinaryLocator(File extractDir) {
super(extractDir);
}
}

View File

@@ -1,12 +1,12 @@
package com.github.boukefalos.jlibloader.internal;
public class LibraryDef {
public class NativeDef {
final String group;
final String name;
final String file;
final String platform;
public LibraryDef(String group, String name, String file, String platform) {
public NativeDef(String group, String name, String file, String platform) {
this.group = group;
this.name = name;
this.file = file;
@@ -21,7 +21,7 @@ public class LibraryDef {
if (obj == null || obj.getClass() != getClass()) {
return false;
}
LibraryDef other = (LibraryDef) obj;
NativeDef other = (NativeDef) obj;
return name.equals(other.name) && platform.equals(other.platform);
}
@@ -33,4 +33,4 @@ public class LibraryDef {
public String getGroupPath() {
return group.replace(".", "/");
}
}
}

View File

@@ -1,19 +1,3 @@
/*
* Copyright 2012 Adam Murdoch
*
* 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 com.github.boukefalos.jlibloader.internal;
import java.io.File;
@@ -23,9 +7,8 @@ import java.util.Set;
import com.github.boukefalos.jlibloader.NativeException;
import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException;
public class NativeLibraryLoader {
private final Set<String> loaded = new HashSet<String>();
private final Platform platform;
public class NativeLibraryLoader extends NativeLoader {
private final Set<String> loaded = new HashSet<String>();
private final NativeLibraryLocator nativeLibraryLocator;
public NativeLibraryLoader(Platform platform, NativeLibraryLocator nativeLibraryLocator) {
@@ -38,7 +21,7 @@ public class NativeLibraryLoader {
return;
}
try {
File libFile = nativeLibraryLocator.find(new LibraryDef(libraryGroupName, libraryName, libraryFileName, platform.getId()));
File libFile = nativeLibraryLocator.find(new NativeDef(libraryGroupName, libraryName, libraryFileName, platform.getId()));
if (libFile == null) {
throw new NativeLibraryUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform));
}
@@ -51,4 +34,4 @@ public class NativeLibraryLoader {
}
loaded.add(libraryFileName);
}
}
}

View File

@@ -1,118 +1,30 @@
/*
* Copyright 2012 Adam Murdoch
*
* 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 com.github.boukefalos.jlibloader.internal;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import com.github.boukefalos.jlibloader.NativeException;
public class NativeLibraryLocator {
private final File extractDir;
public class NativeLibraryLocator extends NativeLocator {
public NativeLibraryLocator(File extractDir) {
this.extractDir = extractDir;
}
super(extractDir);
}
public File find(LibraryDef libraryDef) throws IOException {
String resourceName = String.format("%s/%s/%s/%s", libraryDef.getGroupPath(), libraryDef.name, libraryDef.platform, libraryDef.file);
if (extractDir != null) {
File libFile = new File(extractDir, String.format("%s/%s", libraryDef.platform, libraryDef.file));
File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock");
lockFile.getParentFile().mkdirs();
lockFile.createNewFile();
RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw");
try {
if (lockFile.length() > 0 && lockFileAccess.readBoolean()) {
// Library has been extracted
return libFile;
}
URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) {
// Extract library and write marker to lock file
libFile.getParentFile().mkdirs();
copy(resource, libFile);
lockFileAccess.seek(0);
lockFileAccess.writeBoolean(true);
return libFile;
}
} finally {
// Also releases lock
lockFileAccess.close();
}
} else {
URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) {
File libFile;
File libDir = File.createTempFile(libraryDef.file, "dir");
libDir.delete();
libDir.mkdirs();
libFile = new File(libDir, libraryDef.file);
libFile.deleteOnExit();
libDir.deleteOnExit();
copy(resource, libFile);
return libFile;
}
}
String componentName = libraryDef.file.replaceFirst("^lib", "").replaceFirst("\\.\\w+$", "");
int pos = componentName.indexOf("-");
while (pos >= 0) {
componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2);
pos = componentName.indexOf("-", pos);
}
File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, libraryDef.platform.replace("-", "_"), libraryDef.file));
if (libFile.isFile()) {
return libFile;
}
libFile = new File(String.format("build/binaries/mainSharedLibrary/%s/%s", libraryDef.platform.replace("-", "_"), libraryDef.file));
if (libFile.isFile()) {
return libFile;
}
public File find(NativeDef nativeDef) throws IOException {
if (super.find(nativeDef) == null) {
String componentName = nativeDef.file.replaceFirst("^lib", "").replaceFirst("\\.\\w+$", "");
int pos = componentName.indexOf("-");
while (pos >= 0) {
componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2);
pos = componentName.indexOf("-", pos);
}
File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, nativeDef.platform.replace("-", "_"), nativeDef.file));
if (libFile.isFile()) {
return libFile;
}
libFile = new File(String.format("build/binaries/mainSharedLibrary/%s/%s", nativeDef.platform.replace("-", "_"), nativeDef.file));
if (libFile.isFile()) {
return libFile;
}
}
return null;
}
private static void copy(URL source, File dest) {
try {
InputStream inputStream = source.openStream();
try {
OutputStream outputStream = new FileOutputStream(dest);
try {
byte[] buffer = new byte[4096];
while (true) {
int nread = inputStream.read(buffer);
if (nread < 0) {
break;
}
outputStream.write(buffer, 0, nread);
}
} finally {
outputStream.close();
}
} finally {
inputStream.close();
}
} catch (IOException e) {
throw new NativeException(String.format("Could not extract native JNI library."), e);
}
}
}

View File

@@ -0,0 +1,5 @@
package com.github.boukefalos.jlibloader.internal;
public abstract class NativeLoader {
protected Platform platform;
}

View File

@@ -0,0 +1,87 @@
package com.github.boukefalos.jlibloader.internal;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import com.github.boukefalos.jlibloader.NativeException;
public class NativeLocator {
protected final File extractDir;
public NativeLocator(File extractDir) {
this.extractDir = extractDir;
}
public File find(NativeDef nativeDef) throws IOException {
String resourceName = String.format("%s/%s/%s/%s", nativeDef.getGroupPath(), nativeDef.name, nativeDef.platform, nativeDef.file);
if (extractDir != null) {
File libFile = new File(extractDir, String.format("%s/%s", nativeDef.platform, nativeDef.file));
File lockFile = new File(libFile.getParentFile(), libFile.getName() + ".lock");
lockFile.getParentFile().mkdirs();
lockFile.createNewFile();
RandomAccessFile lockFileAccess = new RandomAccessFile(lockFile, "rw");
try {
if (lockFile.length() > 0 && lockFileAccess.readBoolean()) {
// Library has been extracted
return libFile;
}
URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) {
// Extract library and write marker to lock file
libFile.getParentFile().mkdirs();
copy(resource, libFile);
lockFileAccess.seek(0);
lockFileAccess.writeBoolean(true);
return libFile;
}
} finally {
// Also releases lock
lockFileAccess.close();
}
} else {
URL resource = getClass().getClassLoader().getResource(resourceName);
if (resource != null) {
File libFile;
File libDir = File.createTempFile(nativeDef.file, "dir");
libDir.delete();
libDir.mkdirs();
libFile = new File(libDir, nativeDef.file);
libFile.deleteOnExit();
libDir.deleteOnExit();
copy(resource, libFile);
return libFile;
}
}
return null;
}
protected static void copy(URL source, File dest) {
try {
InputStream inputStream = source.openStream();
try {
OutputStream outputStream = new FileOutputStream(dest);
try {
byte[] buffer = new byte[4096];
while (true) {
int nread = inputStream.read(buffer);
if (nread < 0) {
break;
}
outputStream.write(buffer, 0, nread);
}
} finally {
outputStream.close();
}
} finally {
inputStream.close();
}
} catch (IOException e) {
throw new NativeException(String.format("Could not extract native file."), e);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.github.boukefalos.jlibloader.internal;
import com.github.boukefalos.jlibloader.NativeBinaryUnavailableException;
import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException;
public abstract class Platform {
@@ -64,7 +65,7 @@ public abstract class Platform {
}
}
public boolean isWindows() {
public boolean isWindows() {
return false;
}
@@ -77,6 +78,10 @@ public abstract class Platform {
throw new NativeLibraryUnavailableException(String.format("Native library is not available for %s.", toString()));
}
public String getBinaryName(String name) {
throw new NativeBinaryUnavailableException(String.format("Native binary is not available for %s.", toString()));
}
public abstract String getId();
private static String getOperatingSystem() {
@@ -97,6 +102,11 @@ public abstract class Platform {
public String getLibraryName(String name) {
return String.format("%s.dll", name);
}
@Override
public String getBinaryName(String name) {
return String.format("%s.exe", name);
}
}
private static class Window32Bit extends Windows {