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' group = 'com.github.boukefalos'
archivesBaseName = 'jlibloader' archivesBaseName = 'jlibloader'
version = '0.2' version = '0.3'
uploadArchives { uploadArchives {
repositories.mavenDeployer { 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; package com.github.boukefalos.jlibloader;
import java.io.File; 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.NativeLibraryLoader;
import com.github.boukefalos.jlibloader.internal.NativeLibraryLocator; import com.github.boukefalos.jlibloader.internal.NativeLibraryLocator;
import com.github.boukefalos.jlibloader.internal.Platform; import com.github.boukefalos.jlibloader.internal.Platform;
public class Native { public class Native {
private static NativeLibraryLoader loader; private static Platform platform;
private static NativeLibraryLoader libraryLoader;
private static NativeBinaryLoader binaryLoader;
private Native() { private Native() {
} }
@@ -37,12 +25,13 @@ public class Native {
* @throws NativeLibraryUnavailableException When the native library is not available on the current machine. * @throws NativeLibraryUnavailableException When the native library is not available on the current machine.
* @throws NativeException On failure to load the native library. * @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) { synchronized (Native.class) {
if (loader == null) { if (platform == null) {
Platform platform = Platform.current(); platform = Platform.current();
try { 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) { } catch (NativeException e) {
throw e; throw e;
} catch (Throwable t) { } catch (Throwable t) {
@@ -59,12 +48,26 @@ public class Native {
public static void load(String group, String name, String file) { public static void load(String group, String name, String file) {
init(null); init(null);
try { try {
loader.load(group, name, Platform.current().getLibraryName(file)); libraryLoader.load(group, name, Platform.current().getLibraryName(file));
} catch (NativeException e) { } catch (NativeException e) {
throw e; throw e;
} catch (Throwable t) { } 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; package com.github.boukefalos.jlibloader;
public class NativeException extends RuntimeException { 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; 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; package com.github.boukefalos.jlibloader.internal;
public class LibraryDef { public class NativeDef {
final String group; final String group;
final String name; final String name;
final String file; final String file;
final String platform; 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.group = group;
this.name = name; this.name = name;
this.file = file; this.file = file;
@@ -21,7 +21,7 @@ public class LibraryDef {
if (obj == null || obj.getClass() != getClass()) { if (obj == null || obj.getClass() != getClass()) {
return false; return false;
} }
LibraryDef other = (LibraryDef) obj; NativeDef other = (NativeDef) obj;
return name.equals(other.name) && platform.equals(other.platform); return name.equals(other.name) && platform.equals(other.platform);
} }

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; package com.github.boukefalos.jlibloader.internal;
import java.io.File; import java.io.File;
@@ -23,9 +7,8 @@ import java.util.Set;
import com.github.boukefalos.jlibloader.NativeException; import com.github.boukefalos.jlibloader.NativeException;
import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException; import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException;
public class NativeLibraryLoader { public class NativeLibraryLoader extends NativeLoader {
private final Set<String> loaded = new HashSet<String>(); private final Set<String> loaded = new HashSet<String>();
private final Platform platform;
private final NativeLibraryLocator nativeLibraryLocator; private final NativeLibraryLocator nativeLibraryLocator;
public NativeLibraryLoader(Platform platform, NativeLibraryLocator nativeLibraryLocator) { public NativeLibraryLoader(Platform platform, NativeLibraryLocator nativeLibraryLocator) {
@@ -38,7 +21,7 @@ public class NativeLibraryLoader {
return; return;
} }
try { 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) { if (libFile == null) {
throw new NativeLibraryUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform)); throw new NativeLibraryUnavailableException(String.format("Native library '%s' is not available for %s.", libraryFileName, platform));
} }

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; package com.github.boukefalos.jlibloader.internal;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; 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) { public NativeLibraryLocator(File extractDir) {
this.extractDir = extractDir; super(extractDir);
} }
public File find(LibraryDef libraryDef) throws IOException { public File find(NativeDef nativeDef) throws IOException {
String resourceName = String.format("%s/%s/%s/%s", libraryDef.getGroupPath(), libraryDef.name, libraryDef.platform, libraryDef.file); if (super.find(nativeDef) == null) {
if (extractDir != null) { String componentName = nativeDef.file.replaceFirst("^lib", "").replaceFirst("\\.\\w+$", "");
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("-"); int pos = componentName.indexOf("-");
while (pos >= 0) { while (pos >= 0) {
componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2); componentName = componentName.substring(0, pos) + Character.toUpperCase(componentName.charAt(pos + 1)) + componentName.substring(pos + 2);
pos = componentName.indexOf("-", pos); pos = componentName.indexOf("-", pos);
} }
File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, libraryDef.platform.replace("-", "_"), libraryDef.file)); File libFile = new File(String.format("build/binaries/%sSharedLibrary/%s/%s", componentName, nativeDef.platform.replace("-", "_"), nativeDef.file));
if (libFile.isFile()) { if (libFile.isFile()) {
return libFile; return libFile;
} }
libFile = new File(String.format("build/binaries/mainSharedLibrary/%s/%s", libraryDef.platform.replace("-", "_"), libraryDef.file)); libFile = new File(String.format("build/binaries/mainSharedLibrary/%s/%s", nativeDef.platform.replace("-", "_"), nativeDef.file));
if (libFile.isFile()) { if (libFile.isFile()) {
return libFile; return libFile;
} }
}
return null; 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; package com.github.boukefalos.jlibloader.internal;
import com.github.boukefalos.jlibloader.NativeBinaryUnavailableException;
import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException; import com.github.boukefalos.jlibloader.NativeLibraryUnavailableException;
public abstract class Platform { public abstract class Platform {
@@ -77,6 +78,10 @@ public abstract class Platform {
throw new NativeLibraryUnavailableException(String.format("Native library is not available for %s.", toString())); 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(); public abstract String getId();
private static String getOperatingSystem() { private static String getOperatingSystem() {
@@ -97,6 +102,11 @@ public abstract class Platform {
public String getLibraryName(String name) { public String getLibraryName(String name) {
return String.format("%s.dll", 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 { private static class Window32Bit extends Windows {