Skip to content

Discussion: Adaptable NativeLibraryLoader #32

Description

@BjoernAkAManf

Hi,

as a User of this Library i want to be able to control access of IO. In particular i want to be able to specify the path the native library is beeing written to.

For example i attached the following Class "Native" that emulates the behavior i want to implement.
Unfortunately this requires alot of copy-paste and hackish workarounds.

I would propose extracting the necessary functionality into atleast two parts:

  1. General Utility Class providing init() and load() delegating to implementation
  2. Strategy API -> Allows to implement a System.load() strategy

By default i suggest implementing the following Strategies:

  1. tryLoadFromLibraryPath -> Same as the corresponding method
  2. loadFromTempFile -> Same as current Implementation provided by libraryPath()

Order is provided by each Strategy. As such tryLoadFromLibraryPath would return -1 and loadFromTempfile 1. Other Implementations can then use 0 easily. ServiceLoader would allow to reduce implementation.

The API should provide access to atleast fileName and extension in order to allow writing correctly.

I did not yet start working on a Pull Request, because i feel like this RfC is disruptive to the current implementation and as such I feel @kawamuray you as a Maintainer should first give your Opinion.

Alternatively one could use an Environment Variable, but i feel that is too restrictive for other use cases. Especially when one wants to validate DLL using an out of band signing process.

import io.github.kawamuray.wasmtime.NativeLibraryLoader;
import lombok.AllArgsConstructor;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.Properties;

/**
 * This is a copy of {@link NativeLibraryLoader} that is needed to extract to a well known location instead of a temp file.
 */
@Slf4j
@UtilityClass
public final class Native {
    private static final String SANDBOX_NATIVE_OVERRIDE = "SANDBOX_NATIVE_OVERRIDE";
    private static final String LOCATION = ".native";
    private boolean loaded = false;

    public void load() {
        if (loaded) {
            return;
        }

        if (Native.isAutoLoadDisabled()) {
            log.error("Please set Environment Variable {} to a Value", DISABLE_AUTO_LOAD_ENV);
            System.exit(1);
        }

        try {
            final var nativeLibraryDir = Paths.get(LOCATION);
            if (!Files.exists(nativeLibraryDir)) {
                Files.createDirectory(nativeLibraryDir);
            }
            final var libraryPath = libraryPath(nativeLibraryDir);
            log.debug("Loading Wasmtime JNI library from {}", libraryPath);
            System.load(libraryPath);
            loaded = true;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean isAutoLoadDisabled() {
        return System.getenv(DISABLE_AUTO_LOAD_ENV) == null;
    }

    private boolean isOverridingNative() {
        return System.getenv(SANDBOX_NATIVE_OVERRIDE) != null;
    }

    // Copied and changed
    private static String libraryPath(final Path root) throws IOException {
        final var platform = detectPlatform();
        String version = libVersion();
        String ext = platform.ext;
        String fileName = platform.prefix + NATIVE_LIBRARY_NAME + '_' + version + '_' + platform.classifier;
        final var name = fileName + ext;
        final var p = root.resolve(name);
        final var ovr = isOverridingNative();
        if (ovr) {
            log.warn("Overriding existing stuff yadda yaadda");
        }
        if (ovr || !Files.exists(p)) {
            try (final var in = NativeLibraryLoader.class.getResourceAsStream('/' + name)) {
                // Added to copied struct
                Objects.requireNonNull(in, "Could not find Library");
                final var options = ovr
                    ? new CopyOption[]{StandardCopyOption.REPLACE_EXISTING}
                    : new CopyOption[]{};
                Files.copy(in, p, options);
            }
        }
        return p.toRealPath().toAbsolutePath().toString();
    }

    // Rest is copied from Version 0.9.0
    private static final String DISABLE_AUTO_LOAD_ENV = "WASMTIME_JNI_LOAD_DISABLED";
    private static final String NATIVE_LIBRARY_NAME = "wasmtime_jni";
    private static final String META_PROPS_FILE = "wasmtime-java-meta.properties";

    private static final String JNI_LIB_VERSION_PROP = "jnilib.version";

    @AllArgsConstructor
    private enum Platform {
        LINUX("linux", "lib", ".so"),
        MACOS("macos", "lib", ".dylib"),
        WINDOWS("windows", "", ".dll");
        final String classifier;
        final String prefix;
        final String ext;

    }

    private static Platform detectPlatform() {
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("linux")) {
            return Platform.LINUX;
        }
        if (os.contains("mac os") || os.contains("darwin")) {
            return Platform.MACOS;
        }
        if (os.toLowerCase().contains("windows")) {
            return Platform.WINDOWS;
        }
        throw new RuntimeException("platform not supported: " + os);
    }

    private static String libVersion() throws IOException {
        final Properties props;
        try (InputStream in = NativeLibraryLoader.class.getResourceAsStream('/' + META_PROPS_FILE)) {
            props = new Properties();
            props.load(in);
        }
        return props.getProperty(JNI_LIB_VERSION_PROP);
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions