Refactor Native to try and load other architectures.
This commit is contained in:
		@@ -18,14 +18,14 @@
 | 
			
		||||
 | 
			
		||||
package com.lyndir.masterpassword.impl;
 | 
			
		||||
 | 
			
		||||
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
 | 
			
		||||
 | 
			
		||||
import com.google.common.base.Joiner;
 | 
			
		||||
import com.google.common.collect.ImmutableList;
 | 
			
		||||
import com.google.common.collect.ImmutableSet;
 | 
			
		||||
import com.google.common.io.ByteStreams;
 | 
			
		||||
import com.lyndir.lhunath.opal.system.logging.Logger;
 | 
			
		||||
import java.io.*;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
import java.util.function.Predicate;
 | 
			
		||||
import javax.annotation.Nonnull;
 | 
			
		||||
import javax.annotation.Nullable;
 | 
			
		||||
 | 
			
		||||
@@ -43,71 +43,161 @@ public final class Native {
 | 
			
		||||
    private static final String NATIVES_PATH        = "lib";
 | 
			
		||||
 | 
			
		||||
    @SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" })
 | 
			
		||||
    public static void load(final Class<?> context, final String name) {
 | 
			
		||||
    public static boolean load(final Class<?> context, final String name) {
 | 
			
		||||
 | 
			
		||||
        // Try to load the library using the native system.
 | 
			
		||||
        try {
 | 
			
		||||
            System.loadLibrary( name );
 | 
			
		||||
            return;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Try to find and open a stream to the packaged library resource.
 | 
			
		||||
        try {
 | 
			
		||||
            String      library          = System.mapLibraryName( name );
 | 
			
		||||
            int         libraryDot       = library.lastIndexOf( EXTENSION_SEPARATOR );
 | 
			
		||||
            String      libraryName      = (libraryDot > 0)? library.substring( 0, libraryDot ): library;
 | 
			
		||||
            String      libraryExtension = (libraryDot > 0)? library.substring( libraryDot ): ".lib";
 | 
			
		||||
            String      libraryResource  = getLibraryResource( library );
 | 
			
		||||
            InputStream libraryStream    = context.getResourceAsStream( libraryResource );
 | 
			
		||||
            if (libraryStream == null)
 | 
			
		||||
                throw new IllegalStateException(
 | 
			
		||||
                        "Library: " + name + " (" + libraryResource + "), not found in class loader for: " + context );
 | 
			
		||||
        String             library          = System.mapLibraryName( name );
 | 
			
		||||
        int                libraryDot       = library.lastIndexOf( EXTENSION_SEPARATOR );
 | 
			
		||||
        String             libraryName      = (libraryDot > 0)? library.substring( 0, libraryDot ): library;
 | 
			
		||||
        String             libraryExtension = (libraryDot > 0)? library.substring( libraryDot ): ".lib";
 | 
			
		||||
 | 
			
		||||
            // Write the library resource to a temporary file.
 | 
			
		||||
            File             libraryFile       = File.createTempFile( libraryName, libraryExtension );
 | 
			
		||||
            FileOutputStream libraryFileStream = new FileOutputStream( libraryFile );
 | 
			
		||||
        @Nullable
 | 
			
		||||
        File libraryFile = null;
 | 
			
		||||
        Set<String> libraryResources = getLibraryResources( library );
 | 
			
		||||
        for (final String libraryResource : libraryResources) {
 | 
			
		||||
            try {
 | 
			
		||||
                libraryFile.deleteOnExit();
 | 
			
		||||
                ByteStreams.copy( libraryStream, libraryFileStream );
 | 
			
		||||
            }
 | 
			
		||||
            finally {
 | 
			
		||||
                libraryFileStream.close();
 | 
			
		||||
                libraryStream.close();
 | 
			
		||||
            }
 | 
			
		||||
                InputStream libraryStream = context.getResourceAsStream( libraryResource );
 | 
			
		||||
                if (libraryStream == null) {
 | 
			
		||||
                    logger.dbg( "No resource for library: %s", libraryResource );
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            // Load the library from the temporary file.
 | 
			
		||||
            System.load( libraryFile.getAbsolutePath() );
 | 
			
		||||
        }
 | 
			
		||||
        catch (final IOException e) {
 | 
			
		||||
            throw new IllegalStateException( "Couldn't extract library: " + name, e );
 | 
			
		||||
                // Write the library resource to a temporary file.
 | 
			
		||||
                libraryFile = File.createTempFile( libraryName, libraryExtension );
 | 
			
		||||
                FileOutputStream libraryFileStream = new FileOutputStream( libraryFile );
 | 
			
		||||
                try {
 | 
			
		||||
                    libraryFile.deleteOnExit();
 | 
			
		||||
                    ByteStreams.copy( libraryStream, libraryFileStream );
 | 
			
		||||
                }
 | 
			
		||||
                finally {
 | 
			
		||||
                    libraryFileStream.close();
 | 
			
		||||
                    libraryStream.close();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Load the library from the temporary file.
 | 
			
		||||
                System.load( libraryFile.getAbsolutePath() );
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            catch (@SuppressWarnings("ErrorNotRethrown") final IOException | UnsatisfiedLinkError e) {
 | 
			
		||||
                logger.dbg( e, "Couldn't load library: %s", libraryResource );
 | 
			
		||||
 | 
			
		||||
                if (libraryFile != null)
 | 
			
		||||
                    if (libraryFile.exists() && !libraryFile.delete())
 | 
			
		||||
                        logger.wrn( "Couldn't clean up library file: %s", libraryFile );
 | 
			
		||||
                libraryFile = null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nonnull
 | 
			
		||||
    private static String getLibraryResource(final String library) {
 | 
			
		||||
        String system       = ifNotNullElse( System.getProperty( "os.name" ), "linux" ).toLowerCase( Locale.ROOT );
 | 
			
		||||
        String architecture = ifNotNullElse( System.getProperty( "os.arch" ), "x86" ).toLowerCase( Locale.ROOT );
 | 
			
		||||
 | 
			
		||||
    private static Set<String> getLibraryResources(final String library) {
 | 
			
		||||
        // Standardize system naming in accordance with masterpassword-core.
 | 
			
		||||
        if (system.contains( "windows" ))
 | 
			
		||||
            system = "windows";
 | 
			
		||||
        else if (system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" ))
 | 
			
		||||
            system = "macos";
 | 
			
		||||
        else
 | 
			
		||||
            system = "linux";
 | 
			
		||||
        Sys system = Sys.findCurrent();
 | 
			
		||||
 | 
			
		||||
        // Standardize architecture naming in accordance with masterpassword-core.
 | 
			
		||||
        if (ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture ))
 | 
			
		||||
            architecture = "arm";
 | 
			
		||||
        else if (architecture.startsWith( "arm" ))
 | 
			
		||||
            architecture = "arm64";
 | 
			
		||||
        else if (ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture ))
 | 
			
		||||
            architecture = "x86_64";
 | 
			
		||||
        else
 | 
			
		||||
            architecture = "x86";
 | 
			
		||||
        Collection<Arch> architectures = new LinkedHashSet<>();
 | 
			
		||||
        architectures.add( Arch.findCurrent() );
 | 
			
		||||
        architectures.addAll( Arrays.asList( Arch.values() ) );
 | 
			
		||||
 | 
			
		||||
        return Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, architecture, library );
 | 
			
		||||
        ImmutableSet.Builder<String> resources = ImmutableSet.builder();
 | 
			
		||||
        for (final Arch arch : architectures)
 | 
			
		||||
            resources.add( Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, arch, library ) );
 | 
			
		||||
 | 
			
		||||
        return resources.build();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private enum Sys implements Predicate<String> {
 | 
			
		||||
        windows {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String system) {
 | 
			
		||||
                return system.contains( "windows" );
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        macos {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String system) {
 | 
			
		||||
                return system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" );
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        linux {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String system) {
 | 
			
		||||
                return system.contains( "linux" );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public static Sys findCurrent() {
 | 
			
		||||
            return find( System.getProperty( "os.name" ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public static Sys find(@Nullable String name) {
 | 
			
		||||
            if (name != null) {
 | 
			
		||||
                name = name.toLowerCase( Locale.ROOT );
 | 
			
		||||
 | 
			
		||||
                for (final Sys sys : values())
 | 
			
		||||
                    if (sys.test( name ))
 | 
			
		||||
                        return sys;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return linux;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private enum Arch implements Predicate<String> {
 | 
			
		||||
        arm {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String architecture) {
 | 
			
		||||
                return ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture );
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        arm64 {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String architecture) {
 | 
			
		||||
                return architecture.startsWith( "arm" ) && !arm.test( architecture );
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        x86_64 {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String architecture) {
 | 
			
		||||
                return ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture );
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        x86 {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean test(final String architecture) {
 | 
			
		||||
                return ImmutableList.of( "x86", "i386", "i686" ).contains( architecture );
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public static Arch findCurrent() {
 | 
			
		||||
            return find( System.getProperty( "os.arch" ) );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Nonnull
 | 
			
		||||
        public static Arch find(@Nullable String name) {
 | 
			
		||||
            if (name != null) {
 | 
			
		||||
                name = name.toLowerCase( Locale.ROOT );
 | 
			
		||||
 | 
			
		||||
                for (final Arch arch : values())
 | 
			
		||||
                    if (arch.test( name ))
 | 
			
		||||
                        return arch;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return x86;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user