Loader.java

package com.surrealdb;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.Locale;
import java.util.stream.Stream;

class Loader {

	static String SURREALDB = "surrealdb";
	static String SURREALDB_LIBNAME = System.mapLibraryName(SURREALDB);

	/** System property for explicit path to native lib (e.g. for tests). */
	private static final String NATIVE_PATH_PROP = "surrealdb.native.path";

	static void loadNative() throws RuntimeException {
		String explicitPath = System.getProperty(NATIVE_PATH_PROP);
		if (explicitPath != null && !explicitPath.isEmpty()) {
			File f = new File(explicitPath);
			if (f.exists()) {
				System.load(f.getAbsolutePath());
				return;
			}
		}
		try {
			System.loadLibrary(SURREALDB);
		} catch (final UnsatisfiedLinkError e) {
			try {
				System.load(extract(getPath()).getAbsolutePath());
			} catch (Throwable e2) {
				throw new RuntimeException("Couldn't load " + SURREALDB, e2);
			}
		}
	}

	private static String getPath() {
		final String vendor = System.getProperty("java.vendor").toLowerCase(Locale.ENGLISH);
		final String arch = System.getProperty("os.arch").toLowerCase(Locale.ENGLISH);
		final String name = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
		final boolean android = vendor.contains("android");
		final boolean linux = !android && name.contains("linux");
		final boolean windows = !linux && name.contains("win");
		final boolean osx = !windows && name.contains("mac");
		final boolean intel64 = arch.contains("x86_64") || arch.contains("amd64");
		final boolean intel32 = !intel64 && arch.contains("i386") || arch.contains("i686") || arch.contains("x86");
		final boolean arm64 = arch.contains("aarch64") || arch.contains("arm64");
		final boolean arm32 = !arm64 && (arch.contains("armv7") || arch.contains("arm"));
		if (android) {
			if (arm64)
				return "android_arm64";
			if (intel64)
				return "android_64";
			if (arm32)
				return "android_arm32";
			if (intel32)
				return "android_32";
		} else if (linux) {
			if (arm64)
				return "linux_arm64";
			if (intel64)
				return "linux_64";
			if (arm32)
				return "linux_arm32";
			if (intel32)
				return "linux_32";
		} else if (windows) {
			if (intel64)
				return "windows_64";
			if (intel32)
				return "windows_32";
		} else if (osx) {
			if (arm64)
				return "osx_arm64";
			else if (intel64)
				return "osx_64";
		}
		throw new RuntimeException("Unsupported architecture: " + arch + " - name: " + name);
	}

	private static void copy(InputStream input, OutputStream output) throws IOException {
		final byte[] buffer = new byte[8192];
		int bytesRead;
		while ((bytesRead = input.read(buffer)) != -1) {
			output.write(buffer, 0, bytesRead);
		}
	}

	private static File extract(String path) throws IOException {
		final String TargetsPath = "natives/" + path + "/" + SURREALDB_LIBNAME;
		final URL Targets = Surreal.class.getClassLoader().getResource(TargetsPath);
		if (Targets == null) {
			throw new RuntimeException("Couldn't find Targets: " + TargetsPath);
		}
		final Path tempDir = Files.createTempDirectory("surrealdb");

		// Add a hook to delete the temporary files on shutdown
		Runtime.getRuntime().addShutdownHook(new Thread(() -> {
			try {
				deleteDirectory(tempDir);
			} catch (IOException e) {
				// Safe to ignore
			}
		}));

		final URLConnection connection = Targets.openConnection();
		connection.setUseCaches(false);
		try (InputStream in = new BufferedInputStream(connection.getInputStream())) {
			final File outfile = new File(tempDir.toFile(), SURREALDB_LIBNAME);
			try (FileOutputStream out = new FileOutputStream(outfile)) {
				copy(in, out);
			}
			return outfile;
		}
	}

	private static void deleteDirectory(Path path) throws IOException {
		if (Files.isDirectory(path)) {
			try (Stream<Path> walker = Files.walk(path)) {
				walker.sorted(Comparator.reverseOrder()).forEach(p -> {
					try {
						Files.delete(p);
					} catch (IOException e) {
						// Safe to ignore
					}
				});
			}
		}
	}

}