com.skcraft.launcher.launch.JavaRuntimeFetcher.java Source code

Java tutorial

Introduction

Here is the source code for com.skcraft.launcher.launch.JavaRuntimeFetcher.java

Source

package com.skcraft.launcher.launch;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.skcraft.concurrency.SettableProgress;
import com.skcraft.launcher.Launcher;
import com.skcraft.launcher.util.Closer;
import com.skcraft.launcher.util.Environment;
import com.skcraft.launcher.util.Platform;
import com.skcraft.launcher.util.SharedLocale;
import lombok.Getter;
import lombok.extern.java.Log;
import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author dags <dags@dags.me>
 */
@Log
public class JavaRuntimeFetcher {

    private static final String LAUNCHER_DATA = "http://launchermeta.mojang.com/mc-staging/launcher.json";

    @Getter
    private final SettableProgress progress;
    private final File installDir;

    public JavaRuntimeFetcher(Launcher launcher, SettableProgress progress) {
        this.progress = progress;
        this.installDir = launcher.getBaseDir();
    }

    public File findJRE() {
        File runtime = getRuntimeDir();
        if (runtime == null) {
            log.log(Level.INFO, "Could not detect runtime dir on platform {0}",
                    Environment.getInstance().getPlatform());
            return null;
        }

        log.log(Level.INFO, "Detected runtime dir: {0}, exists: {1}", new Object[] { runtime, runtime.exists() });

        File x64 = resolve(runtime, "jre-x64");
        File jre = getLatestJRE(x64);
        if (jre == null || !jre.isDirectory()) {
            installJRE();
        }

        jre = getLatestJRE(x64);
        if (jre == null || !jre.isDirectory()) {
            log.log(Level.WARNING, "Could not locate JRE in: {0}, exists: {1}", new Object[] { x64, x64.exists() });
            return null;
        }

        boolean root = containsAll(jre, "license", "welcome");
        boolean bin = containsAll(new File(jre, "bin"), "java");
        boolean lib = containsAll(new File(jre, "lib"), "rt", "resources", "charsets");
        if (!root || !bin || !lib) {
            log.log(Level.WARNING, "Incomplete JRE installation detected in: {0}", new Object[] { jre });
            installJRE();
            jre = getLatestJRE(x64);
            if (jre == null) {
                log.log(Level.WARNING, "Could not locate JRE in: {0}, exists: {1}",
                        new Object[] { x64, x64.exists() });
                return null;
            }
        }

        log.log(Level.INFO, "Detected JRE: {0}", jre);

        if (!resolve(jre, "bin", "java").setExecutable(true)) {
            log.log(Level.WARNING, "Unable to make 'java' executable!");
        }

        if (!resolve(jre, "bin", "javaw").setExecutable(true)) {
            log.log(Level.WARNING, "Unable to make 'javaw' executable!");
        }

        return resolve(jre, "bin");
    }

    private File getRuntimeDir() {
        return resolve(installDir.getAbsoluteFile(), "runtime");
    }

    private void installJRE() {
        try {
            File x64 = resolve(getRuntimeDir(), "jre-x64");

            JsonNode jreMeta = getJRE();
            if (jreMeta == null) {
                return;
            }

            // download the jre to the version dir
            // decompress from lzma -> zip
            // extract zip to version dir

            String jreURL = jreMeta.get("url").asText();
            String jreVersion = jreMeta.get("version").asText();

            File jreDir = resolve(x64, jreVersion);
            File zip = new File(jreDir, jreVersion + ".zip");
            File lzma = new File(jreDir, jreVersion + ".lzma");

            download(jreURL, lzma);
            decompress(lzma, zip);
            extract(zip, zip.getParentFile());

            if (lzma.delete()) {
                log.log(Level.INFO, "Removing installation file: {0}", lzma);
            }

            if (zip.delete()) {
                log.log(Level.INFO, "Removing installation file: {0}", zip);
            }
        } catch (Throwable ignore) {
            log.log(Level.WARNING, "Something went wrong whilst try to install the jre locally");
        }
    }

    private File getLatestJRE(File dir) {
        File[] jres = dir.listFiles();
        if (jres == null || jres.length == 0) {
            return null;
        }

        File latest = null;
        for (File jre : jres) {
            // ignore files and hidden folders
            if (!jre.isDirectory() || jre.getName().startsWith(".")) {
                continue;
            }
            if (latest == null || jre.lastModified() > latest.lastModified()) {
                latest = jre;
            }
        }

        return latest;
    }

    private File resolve(File parent, String... path) {
        File file = parent;
        for (String s : path) {
            file = new File(file, s);
        }
        return file;
    }

    private JsonNode getJRE() {
        Platform system = Environment.getInstance().getPlatform();
        String os = system == Platform.WINDOWS ? "windows" : system == Platform.MAC_OS_X ? "osx" : "linux";

        if (os.equals("linux")) {
            throw new UnsupportedOperationException("Mojang do not support a linux JRE :[");
        }

        InputStream inputStream = null;
        JsonNode jre = null;

        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(LAUNCHER_DATA).openConnection();
            inputStream = connection.getInputStream();
            JsonNode root = new ObjectMapper().readTree(inputStream);
            JsonNode platform = root.get(os);
            JsonNode x64 = platform.get("64");
            jre = x64.get("jre");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Closer.close(inputStream);
        }

        if (jre == null) {
            throw new UnsupportedOperationException("Cannot retrieve JRE url for system " + os + ":x64");
        }

        return jre;
    }

    private void download(String url, File destination) {
        if (destination.exists() && !destination.delete()) {
            log.log(Level.INFO, "Unable to clean up existing file {0} before downloading", destination);
            return;
        }

        int attempts = 0;
        while (attempts++ < 5) {
            InputStream inputStream = null;

            try {
                HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
                String length = connection.getHeaderField("Content-Length");
                if (length == null) {
                    log.log(Level.INFO, "Download attempt {0} failed", attempts);
                    Thread.sleep(500);
                    continue;
                }

                long len = Long.parseLong(length);
                if (len <= 0) {
                    log.log(Level.INFO, "Download attempt {0} failed", attempts);
                    Thread.sleep(500);
                    continue;
                }

                inputStream = connection.getInputStream();
                copyFile(inputStream, destination, SharedLocale.tr("runtimeFetcher.download"), len);
                return;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                Closer.close(inputStream);
            }
        }
    }

    private void decompress(File lzma, File zip) {
        if (!lzma.exists()) {
            throw new UnsupportedOperationException("Attempted to decompress non-existent file: " + lzma);
        }

        if (zip.exists() && !zip.delete()) {
            log.log(Level.INFO, "Unable to clean up existing file {0} before unzipping", zip);
            return;
        }

        BufferedInputStream inputStream = null;
        LZMACompressorInputStream lzmaInputStream = null;

        try {
            inputStream = new BufferedInputStream(new FileInputStream(lzma));
            lzmaInputStream = new LZMACompressorInputStream(inputStream);
            copyFile(lzmaInputStream, zip, SharedLocale.tr("runtimeFetcher.decompress"), -1);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Closer.close(lzmaInputStream);
            Closer.close(inputStream);
        }
    }

    private void extract(File zip, File destination) {
        if (!zip.exists()) {
            throw new UnsupportedOperationException("Attempted to extract non-existent file: " + zip);
        }

        if (destination.mkdirs()) {
            log.log(Level.INFO, "Creating dir: {0}", destination);
        }

        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(zip);
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (!entry.isDirectory()) {
                    InputStream inputStream = zipFile.getInputStream(entry);
                    File file = new File(destination, entry.getName());
                    copyFile(inputStream, file, SharedLocale.tr("runtimeFetcher.extract", file.getName()), -1);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Closer.close(zipFile);
        }
    }

    private void copyFile(InputStream source, File destination, String status, long length) {
        ReadableByteChannel byteChannel = null;
        FileOutputStream outputStream = null;
        try {
            if (destination.getParentFile().mkdirs()) {
                log.log(Level.INFO, "Creating dir: {0}", destination.getParentFile());
            }

            if (destination.createNewFile()) {
                log.log(Level.INFO, "Creating file: {0}", destination);
            }

            byteChannel = Channels.newChannel(source);
            outputStream = new FileOutputStream(destination);

            if (length < 0) {
                getProgress().set(status, -1);
                outputStream.getChannel().transferFrom(byteChannel, 0, Long.MAX_VALUE);
            } else {
                long position = 0;
                long increment = length / 100L;
                while (position < length) {
                    outputStream.getChannel().transferFrom(byteChannel, position, increment);
                    position += increment;

                    double progress = (double) position / (double) length;
                    getProgress().set(status, progress);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Closer.close(byteChannel);
            Closer.close(outputStream);
        }
    }

    private boolean containsAll(File dir, String... lookups) {
        String[] files = dir.list();
        if (files != null) {
            outer: for (String lookup : lookups) {
                for (String file : files) {
                    if (file.toLowerCase().contains(lookup.toLowerCase())) {
                        continue outer;
                    }
                }
                return false;
            }
        }
        return true;
    }
}