com.samczsun.helios.bootloader.Bootloader.java Source code

Java tutorial

Introduction

Here is the source code for com.samczsun.helios.bootloader.Bootloader.java

Source

/*
 * Copyright 2016 Sam Sun <me@samczsun.com>
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.samczsun.helios.bootloader;

import com.samczsun.helios.Constants;
import com.samczsun.helios.Helios;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.zeroturnaround.zip.ZipUtil;

import javax.swing.JOptionPane;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.concurrent.Executor;

public class Bootloader {
    public static void main(String[] args) {
        try {
            if (!Constants.DATA_DIR.exists() && !Constants.DATA_DIR.mkdirs())
                throw new RuntimeException("Could not create data directory");
            if (!Constants.ADDONS_DIR.exists() && !Constants.ADDONS_DIR.mkdirs())
                throw new RuntimeException("Could not create addons directory");
            if (!Constants.SETTINGS_FILE.exists() && !Constants.SETTINGS_FILE.createNewFile())
                throw new RuntimeException("Could not create settings file");
            if (Constants.DATA_DIR.isFile())
                throw new RuntimeException("Data directory is file");
            if (Constants.ADDONS_DIR.isFile())
                throw new RuntimeException("Addons directory is file");
            if (Constants.SETTINGS_FILE.isDirectory())
                throw new RuntimeException("Settings file is directory");

            loadSWTLibrary();

            DisplayPumper displayPumper = new DisplayPumper();

            if (System.getProperty("os.name").toLowerCase().contains("mac")) {
                System.out.println("Attemting to force main thread");
                Executor executor;
                try {
                    Class<?> dispatchClass = Class.forName("com.apple.concurrent.Dispatch");
                    Object dispatchInstance = dispatchClass.getMethod("getInstance").invoke(null);
                    executor = (Executor) dispatchClass.getMethod("getNonBlockingMainQueueExecutor")
                            .invoke(dispatchInstance);
                } catch (Throwable throwable) {
                    throw new RuntimeException("Could not reflectively access Dispatch", throwable);
                }
                if (executor != null) {
                    executor.execute(displayPumper);
                } else {
                    throw new RuntimeException("Could not load executor");
                }
            } else {
                Thread pumpThread = new Thread(displayPumper);
                pumpThread.start();
            }
            while (!displayPumper.isReady())
                ;

            Display display = displayPumper.getDisplay();
            Shell shell = displayPumper.getShell();
            Splash splashScreen = new Splash(display);
            splashScreen.updateState(BootSequence.CHECKING_LIBRARIES);
            checkPackagedLibrary(splashScreen, "enjarify", Constants.ENJARIFY_VERSION,
                    BootSequence.CHECKING_ENJARIFY, BootSequence.CLEANING_ENJARIFY, BootSequence.MOVING_ENJARIFY);
            checkPackagedLibrary(splashScreen, "Krakatau", Constants.KRAKATAU_VERSION,
                    BootSequence.CHECKING_KRAKATAU, BootSequence.CLEANING_KRAKATAU, BootSequence.MOVING_KRAKATAU);
            Helios.main(args, shell, splashScreen);
            while (!displayPumper.isDone()) {
                Thread.sleep(100);
            }
        } catch (Throwable t) {
            displayError(t);
            System.exit(1);
        }
    }

    private static void checkPackagedLibrary(Splash splashScreen, String name, String version,
            BootSequence checking, BootSequence cleaning, BootSequence moving) {
        splashScreen.updateState(checking);
        File libFolder = new File(Constants.DATA_DIR, name);
        if (libFolder.isFile()) {
            if (!libFolder.delete()) {
                throw new IllegalArgumentException(name + " folder is file and could not be deleted");
            }
        }

        if (!libFolder.exists()) {
            if (!libFolder.mkdirs()) {
                throw new IllegalArgumentException("Could not create folder for " + name);
            }
        }

        File[] files = libFolder.listFiles();
        if (files == null) {
            throw new IllegalArgumentException(name + " folder is file");
        }

        for (File file : files) {
            if (!file.getName().equals(version)) {
                splashScreen.updateState(cleaning);
                FileUtils.deleteQuietly(file);
            }
        }

        File versionDirectory = new File(libFolder, version);
        if (!versionDirectory.exists()) {
            splashScreen.updateState(moving);
            InputStream inputStream = Bootloader.class.getResourceAsStream("/" + name + "-" + version + ".zip");
            if (inputStream == null) {
                throw new IllegalArgumentException(name + " was not packaged with this program");
            }
            ZipUtil.unpack(inputStream, libFolder);
        }
    }

    private static void loadSWTLibrary()
            throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        String name = getOSName();
        if (name == null)
            throw new IllegalArgumentException("Cannot determine OS");
        String arch = getArch();
        if (arch == null)
            throw new IllegalArgumentException("Cannot determine architecture");

        String swtLocation = "/swt/org.eclipse.swt." + name + "." + arch + "-" + Constants.SWT_VERSION + ".jar";

        System.out.println("Loading SWT version " + swtLocation.substring(5));

        InputStream swtIn = Bootloader.class.getResourceAsStream(swtLocation);
        if (swtIn == null)
            throw new IllegalArgumentException("SWT library not found");
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        IOUtils.copy(swtIn, outputStream);
        ByteArrayInputStream swt = new ByteArrayInputStream(outputStream.toByteArray());

        URL.setURLStreamHandlerFactory(protocol -> { //JarInJar!
            if (protocol.equals("swt")) {
                return new URLStreamHandler() {
                    protected URLConnection openConnection(URL u) {
                        return new URLConnection(u) {
                            public void connect() {
                            }

                            public InputStream getInputStream() {
                                return swt;
                            }
                        };
                    }

                    protected void parseURL(URL u, String spec, int start, int limit) {
                        // Don't parse or it's too slow
                    }
                };
            }
            return null;
        });

        ClassLoader classLoader = Bootloader.class.getClassLoader();
        Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        method.setAccessible(true);
        method.invoke(classLoader, new URL("swt://load"));

        System.out.println("Loaded SWT Library");
    }

    private static void displayError(Throwable t) {
        t.printStackTrace();
        StringWriter writer = new StringWriter();
        t.printStackTrace(new PrintWriter(writer));
        JOptionPane.showMessageDialog(null, writer.toString(), t.getClass().getSimpleName(),
                JOptionPane.INFORMATION_MESSAGE);
    }

    private static String getArch() {
        String arch = System.getProperty("sun.arch.data.model");
        if (arch == null)
            arch = System.getProperty("com.ibm.vm.bitmode");
        return "32".equals(arch) ? "x86" : "64".equals(arch) ? "x86_64" : null;
    }

    private static String getOSName() {
        String unparsedName = System.getProperty("os.name").toLowerCase();
        if (unparsedName.contains("win"))
            return "win32.win32";
        if (unparsedName.contains("mac"))
            return "cocoa.macosx";
        if (unparsedName.contains("linux"))
            return "gtk.linux";
        return null;
    }
}