Package eu.mihosoft.vrlstudio.io

Source Code of eu.mihosoft.vrlstudio.io.StudioBundleUpdater

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package eu.mihosoft.vrlstudio.io;

import eu.mihosoft.vrl.io.IOUtil;
import eu.mihosoft.vrl.io.SynchronizedFileAccess;
import eu.mihosoft.vrl.io.TextSaver;
import eu.mihosoft.vrl.system.VRL;
import eu.mihosoft.vrl.system.VSysUtil;
import eu.mihosoft.vrl.visual.ProceedRequest;
import eu.mihosoft.vrl.visual.VSwingUtil;
import eu.mihosoft.vrlstudio.main.Studio;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;

/**
* This class is responsible for managing a complete VRL-Studio update.
*
* Some methods are called from the Studio process. Others must be called from
* the updater process (see method documentation for details).
*
* @author Michael Hoffer <info@michaelhoffer.de>
*/
public class StudioBundleUpdater {

    /**
     * Java Bean for command-line options.
     */
    private static CmdOptions options;
    /**
     * The update logger.
     */
    private static Logger logger =
            Logger.getLogger(StudioBundleUpdater.class.getName());
    /**
     * The process object of the updater
     */
    private static Process studioUpdaterProcess;
    /**
     * Indicates whether the updater process is running, i.e., if this instance
     * is from the updater process's JVM.
     */
    private static boolean runningUpdater;
    public static final String PREV_VERSION_EXTENSION = "-PREV-VER";

    /**
     * Initializes the updater logger.
     */
    private static void initLogger() {
        try {
            FileHandler fileHandler = new FileHandler("VRL-Studio-Updater.log",
                    1024 * 1024 * 1 /*
                     * MB
                     */, 5);
            fileHandler.setFormatter(new SimpleFormatter());
            logger.addHandler(fileHandler);
        } catch (IOException ex) {
            Logger.getLogger(StudioBundleUpdater.class.getName()).
                    log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(StudioBundleUpdater.class.getName()).
                    log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Main method of the update process. It will be delegated from
     * {@link Studio#main(java.lang.String[])} if it has been called with the
     * <code>-updater</code> command-line option (must be the first option).
     *
     * @param args update arguments (see {@link CmdOptions} for possible
     * options)
     */
    public static void main(String[] args) {

        runningUpdater = true;

        initLogger();

        logger.info(">> updater running:");
        options = new CmdOptions();
        CmdLineParser parser = new CmdLineParser(options);
        try {
            parser.parseArgument(args);
        } catch (CmdLineException e) {
            logger.log(Level.SEVERE, null, e);
            System.err.println(e.getMessage());
            printUsage(parser);
            return;
        }

        boolean wrongOptions = false;

        if (!options.getSourceFolder().isDirectory()) {
            String msg = "-i: specified value is no directory: "
                    + options.getSourceFolder();
            logger.log(Level.SEVERE, msg);
            printUsage(parser);
            wrongOptions = true;
        }

        if (!options.getTargetFolder().isDirectory()) {
            String msg = "-o: specified value is no directory: "
                    + options.getTargetFolder();
            logger.log(Level.SEVERE, msg);
            printUsage(parser);
            wrongOptions = true;
        }

        if (!options.getUpdateFolder().isDirectory()) {
            String msg = "-update-folder: specified value is no directory: "
                    + options.getUpdateFolder();
            logger.log(Level.SEVERE, msg);
            printUsage(parser);
            wrongOptions = true;
        }

        if (!options.getPropertyFolder().isDirectory()) {
            String msg = "-property-folder: specified value is no directory: "
                    + options.getPropertyFolder();
            logger.log(Level.SEVERE, msg);
            printUsage(parser);
            wrongOptions = true;
        }

        if (wrongOptions) {
            logger.log(Level.SEVERE, "UPDATER CALLED WITH WRONG OPTIONS");
            return;
        }

        System.out.println(">> updater waiting...");

//        exitFile = new File(options.getUpdateFolder(),"exit-updater");

        while (parentProcessStillRunning()) {
            logger.log(Level.INFO, ">> watching pid: " + options.getPid()
                    + " because of strange Windows file locking we have to wait :( \n"
                    + "   (we are lazy and use the same code on unix as well)");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
            }

//            // stop update
//            if (exitFile.exists()) {
//                logger.log(Level.INFO, ">> stopping update (requested by host process)");
//                IOUtil.deleteDirectory(exitFile);
//                return;
//            }
        }

        // if no delta-update, move VRL-Studio folder to VRL-Studio-PREV-VER
        File prevVersion = new File(options.getTargetFolder().getAbsolutePath()
                + PREV_VERSION_EXTENSION);
        // delete prevVersion if it already exists
        IOUtil.deleteDirectory(prevVersion);
        // move current (old) version to PREV-Version
        IOUtil.move(options.getTargetFolder(), prevVersion);
        //if (VSysUtil.isWindows()) {
        //    IOUtil.copyDirectory(options.getTargetFolder(), prevVersion);
        //} else {
        //    IOUtil.move(options.getTargetFolder(), prevVersion);
        //}

//        IOUtil.deleteDirectory(options.getTargetFolder());

        // if update bundle cannot be copied to final bundle location
        // silently quit
        if (!copyUpdateToFinalBundle()) {
            logger.log(Level.SEVERE, ">> cannot copy update to final bundle");
            return;
        }

        // if running new studio bundle is successful then delete the
        // updater bundle files
        // - we keep them otherwise to allow debugging
        if (runNewStudio()) {

            // delete contents of updates folder
            for (File f : options.getUpdateFolder().listFiles()) {
                IOUtil.deleteTmpFilesOnExit(f);
            }

        }
    }

    /**
     * Determines if the parent (Studio) process is still running.
     *
     * <p><b>Note:</b> only call this from update process.</p>
     *
     * @return <code>true</code> if the Studio process is running;
     * <code>false</code> otherwise
     */
    private static boolean parentProcessStillRunning() {

        throwIfNotCallingFromUpdaterProcess();

        if (!VSysUtil.isWindows()) {
            return VSysUtil.isRunning(options.getPid());
        } else {
            File lockFile = new File(options.getPropertyFolder(), ".lock");
            return SynchronizedFileAccess.isLocked(lockFile);
        }
    }

    /**
     * @throws IllegalStateException if not running from the updater process
     */
    private static void throwIfNotCallingFromUpdaterProcess() {
        if (!isRunningUpdater()) {
            throw new IllegalStateException(
                    "This method must not be called from the Studio process!");
        }
    }

    /**
     * @throws IllegalStateException if not running from the studio process
     */
    private static void throwIfNotCallingFromStudioProcess() {
        if (isRunningUpdater()) {
            throw new IllegalStateException(
                    "This method must not be called from the Updater process!");
        }
    }

    /**
     * Creates the update bundle in a temporary folder.
     *
     * <p><b>Note:</b> only call this from Studio process.</p>
     *
     * @param input the input/source (.zip) file that shall be unzipped
     * @return the tmp folder containing the updater bundle or <code>null</code>
     * if the updater bundle cannot be created
     */
    private static File createUpdaterBundle(File input) {

        throwIfNotCallingFromStudioProcess();

        Studio.logger.info(" --> creating update-bundle-folder");
        File tmpFolder = null;
        try {
            tmpFolder = IOUtil.createTempDir(
                    VRL.getPropertyFolderManager().getUpdatesFolder());
        } catch (IOException ex) {
            Logger.getLogger(StudioBundleUpdater.class.getName()).
                    log(Level.SEVERE, null, ex);
            Studio.logger.severe(
                    " --> StudioBundleUpdater:"
                    + " cannot create tmp dir for update bundle");
            return null;
        }

        if (!VSysUtil.isWindows()) {
            try {
                Studio.logger.info(
                        " --> unzip: " + input + " -> " + tmpFolder);
                IOUtil.copyFile(
                        input, new File(tmpFolder + "/" + input.getName()));

                Process p = Runtime.getRuntime().exec(
                        new String[]{"unzip", input.getName()},
                        null, tmpFolder);

                BufferedReader inputS = new BufferedReader(
                        new InputStreamReader(p.getInputStream()));

                String line = null;

                while ((line = inputS.readLine()) != null) {
                    System.out.println(" --> unzip: " + line);
                }

            } catch (IOException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                Studio.logger.severe(
                        " --> StudioBundleUpdater:"
                        + " cannot unpack update bundle");
                return null;
            }
        } else {
            try {
                Studio.logger.info(
                        " --> unzip: " + input + " -> " + tmpFolder);
                IOUtil.copyFile(input,
                        new File(tmpFolder + "/" + input.getName()));
                IOUtil.unzip(input, tmpFolder);
            } catch (IOException ex) {
                Logger.getLogger(
                        StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                Studio.logger.severe(
                        " --> StudioBundleUpdater:"
                        + " cannot unpack update bundle");
            }
        }

        Studio.logger.info(" --> update-bundle-folder: " + tmpFolder);

        return tmpFolder;
    }

    /**
     * Runs the updater process that updates the studio.
     *
     * <p><b>Note:</b> only call this from Studio process.</p>
     *
     * @return <code>true</code> if the updater process could be executed;
     * <code>false</code> otherwise
     */
    public static boolean runStudioUpdater(File source) {

        throwIfNotCallingFromStudioProcess();

        Studio.logger.info(">> running studio update");

        File target = Studio.APP_FOLDER;

        File bundleFolder = createUpdaterBundle(source);

        if (bundleFolder == null) {
            Studio.logger.severe(" --> no update-bundle");
            return false;
        }

        if (!VSysUtil.isWindows()) {

            String runPath = "/VRL-Studio/.application/updater/run-update";
            String inPath = "/VRL-Studio";

            if (VSysUtil.isMacOSX()) {
                runPath = "/VRL-Studio.app/Contents/Resources/.application/updater/run-update";
                inPath = "/VRL-Studio.app";
            }

            Studio.logger.info(">> updater run path: " + runPath);
            Studio.logger.info(">> updater in path: " + inPath);

            String[] command = new String[]{"nohup",
                bundleFolder.getAbsolutePath() + runPath,
                "-i", bundleFolder.getAbsolutePath() + inPath,
                "-o", target.getAbsolutePath(),
                "-pid", "" + VSysUtil.getPID(),
                "-update-folder",
                VRL.getPropertyFolderManager().getUpdatesFolder().getAbsolutePath(),
                "-property-folder",
                VRL.getPropertyFolderManager().getPropertyFolder().getAbsolutePath()};

            String finalCommandAsString = "";

            for (String s : command) {
                finalCommandAsString += s + " ";
            }

            Studio.logger.info(">> final command: " + finalCommandAsString);

            try {
                System.out.println(" --> running Unix install");
                studioUpdaterProcess = Runtime.getRuntime().exec(command);

                final long timeStamp = System.currentTimeMillis();

                // Date: 30.01.2013
                // TODO we need to check whether waiting til nohup process has
                // started is sufficient
                // Reason: on Linux update process did not always start
                VSwingUtil.newWaitController().requestWait(new ProceedRequest() {
                    @Override
                    public boolean proceed() {
                        return System.currentTimeMillis() - timeStamp > 3000;
                    }
                });

//                BufferedReader inputS = new BufferedReader(
//                        new InputStreamReader(studioUpdaterProcess.getInputStream()));
//
//                String line = null;
//
//                while ((line = inputS.readLine()) != null) {
//                    System.out.println(" --> run-update: " + line);
//                }

            } catch (IOException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                Studio.logger.severe(">> cannot run update-bundle: "
                        + bundleFolder);
                return false;
            }
        } else {
            String runPath = "updater\\run-update.bat";
            String inPath = "\\VRL-Studio";

            Studio.logger.info(">> updater run path: " + runPath);
            Studio.logger.info(">> updater in path: " + inPath);

            String[] command = new String[]{"cmd", "/C", "start",
                runPath,
                "-i", bundleFolder + inPath,
                "-o", target.getAbsolutePath(),
                "-pid", "" + VSysUtil.getPID(),
                "-update-folder", VRL.getPropertyFolderManager().getUpdatesFolder().getAbsolutePath(),
                "-property-folder", VRL.getPropertyFolderManager().getPropertyFolder().getAbsolutePath()};

            String finalCommandAsString = "";

            for (String s : command) {
                finalCommandAsString += s + " ";
            }

            Studio.logger.info(">> final command: " + finalCommandAsString);

            try {
                System.out.println(" --> running Unix install");
                studioUpdaterProcess = Runtime.getRuntime().exec(
                        command, null, new File(bundleFolder,
                        "VRL-Studio\\.application"));

            } catch (IOException ex) {

                Studio.logger.severe(">> cannot run update-bundle: "
                        + bundleFolder);

                Studio.logger.log(Level.SEVERE, null, ex);
                return false;
            }
        }

        return true;
    }

    /**
     * Runs the updated Studio process.
     *
     * <p><b>Note:</b> only call this from update process.</p>
     *
     * @return <code>true</code> if the Studio process could be executed;
     * <code>false</code> otherwise
     */
    private static boolean runNewStudio() {

        throwIfNotCallingFromUpdaterProcess();

        logger.info(">> running new studio");

        File bundleFolder = options.getTargetFolder();

        if (VSysUtil.isLinux()) {

            try {
                System.out.println(" --> running Unix install");
                Process p = Runtime.getRuntime().exec(new String[]{"nohup",
                            bundleFolder.getAbsolutePath()
                            + "/run", "-updated"}, null, bundleFolder.getAbsoluteFile());

            } catch (IOException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                logger.severe(">> cannot run studio-bundle: "
                        + bundleFolder);

                return false;
            }
        } else if (VSysUtil.isMacOSX()) {

            logger.info(">> osx: " + bundleFolder.getAbsolutePath());
            try {
                System.out.println(" --> running Mac install");
                Process p = Runtime.getRuntime().exec(new String[]{"open",
                            bundleFolder.getAbsolutePath(), "--args", "-updated"});

            } catch (IOException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                logger.severe(">> cannot run studio-bundle: "
                        + bundleFolder);

                return false;
            }
        } else {
            logger.info(">> windows: " + bundleFolder.getAbsolutePath());
            try {
                System.out.println(" --> running Windows install");
                String[] cmd = new String[]{"cmd", "/C", "start", "run.bat", "-updated"};

                String finalCommandAsString = "";

                for (String s : cmd) {
                    finalCommandAsString += s + " ";
                }

                logger.info(">> windows: " + finalCommandAsString);
                Process p = Runtime.getRuntime().exec(
                        cmd, null,
                        bundleFolder.getAbsoluteFile());

            } catch (IOException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                logger.severe(">> cannot run studio-bundle: "
                        + bundleFolder);

                return false;
            }
        }

        return true;
    }

    /**
     * Prints a usage message if the updater process has been started with wrong
     * options.
     *
     * @param parser parser used for argument parsing
     */
    private static void printUsage(CmdLineParser parser) {
        System.err.println("Don't call this programm manually!");
        parser.printUsage(System.err);
    }

    /**
     * Copies the update to the final Studio bundle location.
     *
     * <p><b>Note:</b> only call this from update process.</p>
     *
     * @return <code>true</code> if successful; <code>false</code> otherwise
     */
    private static boolean copyUpdateToFinalBundle() {

        throwIfNotCallingFromUpdaterProcess();

        if (!VSysUtil.isWindows()) {
            try {
                logger.info(" --> cp: "
                        + options.getSourceFolder()
                        + " -> " + options.getTargetFolder());

                Process p = Runtime.getRuntime().exec(new String[]{
                        "cp", "-rv", options.getSourceFolder().getAbsolutePath(),
                        options.getTargetFolder().getAbsoluteFile().
                        getParentFile().getAbsolutePath()});

                // TODO why does waitFor() hang? (29.01.2013)
//                try {
//                    p.waitFor();
//                } catch (InterruptedException ex) {
//                    Logger.getLogger(StudioBundleUpdater.class.getName()).
//                log(Level.SEVERE, null, ex);
//                }

                BufferedReader input = new BufferedReader(
                        new InputStreamReader(p.getInputStream()));

                String line = null;

                while ((line = input.readLine()) != null) {
                    logger.info(" --> updater: " + line);
                }


            } catch (IOException ex) {
                Logger.getLogger(StudioBundleUpdater.class.getName()).
                        log(Level.SEVERE, null, ex);
                logger.severe("cannot copy update bundle");
                return false;
            }
        } else {
            try {
                IOUtil.copyDirectory(options.getSourceFolder(),
                        options.getTargetFolder().getAbsoluteFile());
            } catch (IOException ex) {
                logger.log(Level.SEVERE, null, ex);
            }
        }

        logger.info(
                " --> cp: "
                + options.getSourceFolder()
                + " -> " + options.getTargetFolder());

        return true;
    }

    /**
     * @return <code>true</code> if this instance of StudioBundleUpdater belongs
     * to the updater process; <code>false</code> otherwise
     */
    public static boolean isRunningUpdater() {
        return runningUpdater;
    }
}
TOP

Related Classes of eu.mihosoft.vrlstudio.io.StudioBundleUpdater

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.