view src/name/blackcap/exifwasher/exiv2/Initialize.kt @ 60:d0b83fc1d62a default tip

Remember our input directory on a per-invocation basis.
author David Barts <n5jrn@me.com>
date Sun, 26 Jul 2020 15:14:03 -0700
parents cd2ca4727b7f
children
line wrap: on
line source

/*
 * Logic common to initializing all external (JNI) classes goes here.
 */
package name.blackcap.exifwasher.exiv2

import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.logging.Level
import java.util.logging.Logger
import javax.swing.*
import name.blackcap.exifwasher.Application
import name.blackcap.exifwasher.LF_DIR
import name.blackcap.exifwasher.LOGGER
import name.blackcap.exifwasher.OS

object Initialize {
    private var initialized = false

    public fun libraries() {
        /* no-op if already initialized */
        if (initialized) {
            return
        }

        /* use the appropriate binary for the system we're on */
        val (subdir, pfx, ext) = when(OS.type) {
            OS.UNIX -> Triple("linux", "lib", ".so")
            OS.MAC -> Triple("mac", "lib", ".dylib")
            OS.WINDOWS -> Triple("windows", "", ".dll")
            OS.OTHER -> throw RuntimeException("unsupported OS!")
        }

        /* extract to scratch files, if needed, then load */
        val klass = Initialize::class.java
        var myJar = JarFile(File(klass.getProtectionDomain().getCodeSource().getLocation().toURI()))
        for (base in arrayOf("exiv2", "jni")) {
            val eBase = "${pfx}${base}${ext}"
            val sPath = "name/blackcap/exifwasher/binaries/${subdir}/${eBase}"
            val source = myJar.getJarEntry(sPath)
            if (source == null) {
                die("${sPath} not found in jar")
            }
            val target = File(LF_DIR, eBase)
            val tPath = target.absolutePath
            try {
                if (!target.exists() || target.lastModified() < source.getTime()) {
                    myJar.getInputStream(source).use { sfp ->
                        FileOutputStream(target).use { tfp ->
                            sfp.copyTo(tfp)
                        }
                    }
                    target.setExecutable(true)
                    target.setLastModified(source.getTime())
                }
            } catch (e: IOException) {
                LOGGER.log(Level.SEVERE, "unable to create ${tPath}", e)
                val message = e.message ?: "I/O error"
                die("unable to create ${tPath}: ${message}")
            }
            try {
                System.load(tPath)
            } catch (e: UnsatisfiedLinkError) {
                LOGGER.log(Level.SEVERE, "unable to link ${tPath}", e)
                val message = e.message ?: "unsatisfied link"
                die("unable to link ${tPath}: ${message}")
            }
        }

        initialized = true
    }

    private fun die(message: String) {
        JOptionPane.showMessageDialog(Application.mainFrame,
            "A fatal error occurred in initialization:\n${message}",
            "Error", JOptionPane.ERROR_MESSAGE)
        LOGGER.log(Level.SEVERE, "aborted due to fatal error")
        System.exit(1)
    }
}