/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package install; import java.awt.Desktop; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.swing.JOptionPane; /** * Performs the tasks necessary to initiate a BIRCH installation or update ** * @author Graham Alvare * @author Dale Hamel * @author Brian Fristensky */ public class Install { /** * The "meat" of the GetBIRCH process. This method performs all of the * tasks necessary to install or update BIRCH. * * This method also performs the update-specific operations, which are * part of the BIRCH update process. Note that the current BIRCH update * format is to uninstall BIRCH, then reinstall BIRCH with the a new version. ** * @param update whether this method is supposed to update an existing BIRCH installation (if true), or install a fresh copy of BIRCH (if false) * @param installDir the target location to install BIRCH * @param frameType the version of the BIRCH framework to install * @param binaries the set of BIRCH binaries to install * @param devel whether to install the development version of BIRCH * @param mini whether to install miniBIRCH * @param adminEmail the administrator's email address to use for the installation * @param createBackup whether to backup an existing BIRCH installation (for updates only!) * @param logger the output log/console to use to display progress information * @param keep_binaries whether an update should remove and install new binaries (false = remove and install new binaries) */ public static void installBIRCH( boolean update, File installDir, String frameType, Set binaries, boolean devel, boolean mini, String adminEmail, boolean createBackup, OutputConsole logger, boolean keep_binaries, boolean stats) { // set local parameters long time = System.currentTimeMillis(); boolean has_depends = true; final File BIRCHDOC = new File(new File(installDir, "public_html"), "index.html"); File installBIRCH = new File(installDir, "install-birch"); File installScripts = new File(installDir, "install-scripts"); File[] filesToExtract; try { PrintStream logstream = logger.getLogStream(); // this will produce a crash: must be called as module // run_main(cwd) logger.println("Running GetBIRCH installer process"); if (update) { logger.println("Updating an existing BIRCH installation"); } if (Main.IS_WINDOWS) { logger.println("This is a windows install"); } if (!has_depends) { logger.println("A required dependancy is not present on system path, BIRCH cannot be installed"); } else { if (update) { //success = update_birch(); logger.println("Updating BIRCH"); logger.setProgressTask("Updating BIRCH"); // update the log logger.println("The selected directory " + installDir + " contains a BIRCH installation, it will be updated"); // if desired by the user, back up the old version of BIRCH if (createBackup) { try { Update.compress_old_birch(installDir, logger); } catch (IOException ioe) { logger.exception(ioe); } } } if (update || !clobber_check(installDir, logger)) { logger.println("********************Starting the install process********************"); logger.setProgressTask("Starting BIRCH installation process"); if (!devel) { logger.println("Installing BIRCH version " + frameType); } else { logger.println("Installing the development version of BIRCH"); } logger.println("Using: " + installDir + " as installation directory"); if (!installDir.exists()) { logger.println("Creating the install directory: " + installDir); installDir.mkdirs(); } if (Main.DISC_INSTALL) { filesToExtract = disc_prepare(installDir, logger, keep_binaries, update); } else { filesToExtract = download_birch(installDir, frameType, binaries, devel, mini, logger, keep_binaries, update); } if (update) { Update.uninstallBIRCH(installDir, logger, keep_binaries); } extract_tarballs(installDir, filesToExtract, logger); String pyexec = (Main.IS_WINDOWS ? "python3.exe" : "python3"); logger.setProgressTask("Running " + Main.PYTHON_PATH + " birch_install.py..."); logger.println("Running " + Main.PYTHON_PATH + " birch_install.py"); BIRCHIO.runCommand(new String[] { pyexec, new File(new File(installDir, "install-scripts"), "birch_install.py").getCanonicalPath(), Main.PLATFORM.toString(), installDir.getCanonicalPath(), adminEmail, (update ? "N" : "Y"), (mini ? "Y" : "N") }, null, installDir.getCanonicalFile(), logstream, logstream); } } // determine the total length of time taken by the installation time = Math.round((System.currentTimeMillis() - time) / 60000.); logger.println("Installation took " + time + " minutes."); if (update) { logger.println("Update completed successfully"); JOptionPane.showMessageDialog(logger, "Update completed successfully!\n" + "Click OK to launch the BIRCH documentation homepage in your browser.\n" + "Please bookmark the BIRCH documentation page for future reference."); JOptionPane.showMessageDialog(logger, "For best results,\n" + "logout and log back in before using BIRCH.\n"); } else { logger.println("Installation completed successfully"); JOptionPane.showMessageDialog(logger, "Installation completed successfully!\n" + "Click OK to launch the BIRCH documentation homepage in your browser.\n" + "Please bookmark the BIRCH documentation page for future reference."); JOptionPane.showMessageDialog(logger, "For best results,\n" + "logout and log back in before using BIRCH.\n"); } // Post data on this install to the BIRCH server for download statistics if (stats) { SendStatsToBirch(devel, Main.PLATFORM.toString(), update); } if (Desktop.isDesktopSupported()) { // Platform independent (Java 1.6) Desktop.getDesktop().browse(BIRCHDOC.toURI()); /* - HOW TO ALTERNATIVELY OPEN A BROWSER - */ /* // Windows BIRCHIO.runCommand(new String[] { "cmd", "/k", "start", url }, null, null, System.out, System.err); OR "rundll32 url.dll,FileProtocolHandler " + url // OS X BIRCHIO.runCommand(new String[] { "open", url }, null, null, System.out, System.err); // Linux BIRCHIO.runCommand(new String[] { "/bin/sh", "-c", "xdg-open", url }, null, null, System.out, System.err); */ } else { // Python way BIRCHIO.runCommand(new String [] { Main.PYTHON_PATH, new File(new File(installDir, "script"), "browser.py").getCanonicalPath(), BIRCHDOC.toURL().toString() }, null, installDir.getCanonicalFile(), logstream, logstream); } } catch (Exception ex) { if (logger != null) { logger.exception(ex); } else { ex.printStackTrace(System.err); } JOptionPane.showMessageDialog(null, "Installation failed, please try again and submit install log as a bug report."); } finally { logger.println("Installation finished"); } } /** * Checks whether we are clobbering an existing BIRCH installation. ** * This method will start by search for a local directory or an * admin/BIRCH.properties file in the current installation directory. If * either of these two files exist, this method will return false, and * display an error message to the user. * * If the installation directory does NOT exist, this method will search * for the BIRCH environment variable. If the BIRCH environment variable * is set, this method will prompt the user as to whether they wish to * create a new installation, which may "clobber" their existing BIRCH * installation. If they wish to do so, this method will pretend that * the existing BIRCH installation does not exist (i.e. return false). * If the user does not wish to create a new installation because of an * existing BIRCH installation, this method will display an error message * and return true ** * @param installDir the destination directory to install BIRCH * @param logger the output log/console to use to display progress information * @return true if the installation will clobber an existing BIRCH installation */ public static boolean clobber_check(File installDir, OutputConsole logger) { boolean clobber = false; File localDir = new File(installDir, "local"); File adminProperties = new File(new File(installDir, "admin"), "BIRCH.properties"); String birchenv = System.getenv("BIRCH"); if (installDir.exists()) { clobber = localDir.exists() || adminProperties.exists(); if (clobber) { logger.println("ERROR! The directory specified already contains a BIRCH installation! Aborting"); JOptionPane.showMessageDialog(null, "Installation cancelled to prevent clobbering existing installation.\n" + "Please restart installer and select a different directory, or remove old installation"); } } else if (birchenv != null) { File birchDir = new File(birchenv); if (birchDir.exists()) { logger.println("An existing BIRCH installation was found at: \"" + birchDir.getAbsolutePath() + "\""); if (installDir.equals(birchDir)) { int reload = JOptionPane.showConfirmDialog(null, "The installation directory that you specified:\n" + installDir + "\nAlready contains a BIRCH installation. Are you sure you wish to proceed?(NOT recommended)", "Input", JOptionPane.YES_NO_OPTION); clobber = (reload == JOptionPane.NO_OPTION); if (clobber) { logger.println("User aborted install."); } else { logger.println("User decided to install over old installation."); } } } } return clobber; } /** * Prepare for a disc installation of BIRCH. ** * Copy all of the relevant .tar.gz files from the disc to the installation * directory. Then, extract the files to the destination. ** * @param installDir the destination directory to install BIRCH * @param logger the output log/console to use to display progress information * @param keep_binaries whether an update should remove and install new binaries (false = remove and install new binaries) * @param update whether this method is supposed to update an existing BIRCH installation (if true), or install a fresh copy of BIRCH (if false) * @return the array of files to extract (i.e. all of the files copied from the install disc by GetBIRCH) */ private static File[] disc_prepare(File installDir, OutputConsole logger, boolean keep_binaries, boolean update) throws IOException { File framework_src = new File(BIRCHIO.JAR_PATH, "framework.tar.gz"); File binaries_src = new File(BIRCHIO.JAR_PATH, "binaries.tar.gz"); File framework_dest = new File(installDir, "framework.tar.gz"); File binaries_dest = new File(installDir, "binaries.tar.gz"); PrintStream logstream = logger.getLogStream(); logger.println("This is a disc install, must decompress binaries to install directory"); // copy the framework to the destination logger.setProgressTask("Copying framework to installation directory"); logger.println("Copying framework to installation directory..."); BIRCHIO.copy(framework_dest, framework_src, logger, true); // decompress the framework logger.println("Decompressing framework..."); logger.setProgressTask("Decompressing framework..."); // only extract the binaries if necessary if (!keep_binaries) { // copy the binaries to the destination directory logger.setProgressTask("Copying binaries to installation directory"); logger.println("Copying binaries to installation directory..."); BIRCHIO.copy(binaries_dest, binaries_src, logger, true); } return new File[] {framework_dest, binaries_dest}; } /** * Download and extract the files required to install BIRCH ** * @param installDir the target location to install BIRCH * @param version the version of the BIRCH framework to install * @param binaries the set of BIRCH binaries to install * @param development whether to install the development version of BIRCH * @param minibirch whether to install miniBIRCH * @param logger the output log/console to use to display progress information * @param keep_binaries whether an update should remove and install new binaries (false = remove and install new binaries) * @param update whether this method is supposed to update an existing BIRCH installation (if true), or install a fresh copy of BIRCH (if false) * @return the array of files to extract (i.e. all of the files downloaded by GetBIRCH) */ private static File[] download_birch(File installDir, String version, Set binaries, boolean development, boolean minibirch, OutputConsole logger, boolean keep_binaries, boolean update) throws IOException { URL framework = null; File framework_tar = File.createTempFile("framework", ".tar.gz", installDir); List files = new ArrayList(); framework_tar.deleteOnExit(); logger.println("********************Fetching archives********************"); if (!minibirch && !development) { framework = new URL(Main.BASE_URL + "CURRENT/framework_" + version + ".tar.gz"); logger.println("Downloading BIRCH framework to " + installDir + " from " + framework); } else if (minibirch && !development) { framework = new URL(Main.BASE_URL + "minibirch/framework.mini_" + version + ".tar.gz"); logger.println("Downloading miniBirch framework to " + installDir + " from " + framework); } else if (!minibirch && development) { framework = new URL(Main.BASE_URL + "Development/framework_D.tar.gz"); logger.println("Downloading development framework to " + installDir + " from " + framework); } else { framework = new URL(Main.BASE_URL + "minibirch/framework.mini_D.tar.gz"); logger.println("Downloading miniBirch development framework to " + installDir + " from " + framework); } logger.setProgressTask("Downloading framework"); BIRCHIO.wget(framework, framework_tar, logger, true); files.add(framework_tar); if (!keep_binaries) { for (Main.ArchSys platform : binaries) { URL location = null; File binfile = File.createTempFile(platform.toString(), ".tar.gz", installDir); binfile.deleteOnExit(); if (development) { location = new URL(Main.BASE_URL + "Development/bin-" + platform + "_D.tar.gz"); } else { location = new URL(Main.BASE_URL + "CURRENT/bin-" + platform + "_" + version + ".tar.gz"); } logger.println("Downloading binaries for platform \"" + platform + "\" to " + installDir + " from " + location); logger.setProgressTask("Downloading binaries (" + platform + ")"); BIRCHIO.wget(location, binfile, logger, true); files.add(binfile); } } return files.toArray(new File[0]); } /** * Extract the downloaded (or disc) BIRCH archives ** * @param installDir the target location to install BIRCH * @param the list of files to extract * @return the array of files to extract (i.e. all of the files downloaded or copied by GetBIRCH) */ private static void extract_tarballs(File installDir, File[] files, OutputConsole logger) throws IOException { PrintStream logstream = logger.getLogStream(); for (File f : files) { logger.println("Decompressing " + f + "..."); logger.setProgressTask("Decompressing framework..."); if (f.exists()) { BIRCHIO.untar(f, installDir, logstream, logstream, true); } else { logger.println("The required file framework.tar.gz was not found"); } } } /** * Send a line of install info to the BIRCH server for download statistics ** * @param version BIRCH version being installed * @param platform platform on which BIRCH is being installed * @param update whether this method is supposed to update an existing BIRCH installation (if true), or install a fresh copy of BIRCH (if false) * * SendStatsToBirch calles birchtally.cgi at STAT_URL_BASE, which appends an output line to * /var/www/birch/birchtally.tsv. This is a tab-separated file with the following on each line: * Date/TimePlatformVersionInstall-type * */ private static void SendStatsToBirch(boolean devel, String platform, boolean update) { final String STAT_URL_BASE = "http://flamingo.plants.umanitoba.ca/cgi-bin/birch/birchtally.cgi"; // Set value of version string String version; if (devel) { version = "D"; // Development version } else { version = Main.VERSION; // current stable version } // Create URL String insttype; if (update) { insttype = "update"; } else { insttype = "new"; } // I tried using java.net.URLEncoder, and discovered that there is a lot of disagreement // regarding the best way to encode parameters to send to a script. Basically, non-standard // ASCII characters, along with white space characters, need to be sent as codes. //The one-parameter method is deprecated and won't compile. // The two parameter method has a 2nd parameter for a coding scheme. The Javadoc advises // against UTF-8 encoding, but other writers say that many servers ONLY use UTF-8. // Since this script is intended for just a single server, and for this purpose we // don't need any special characters. //If I ever decide to write a general purpose method, I'll do a lot more // research on this than I want to do right now. String Params = "version=" + version + "&" + "platform=" + platform + "&" + "insttype=" + insttype; // Post data to URL. See example at // http://www.rgagnon.com/javadetails/java-0082.html try { // Post data to the CGI script URL CGIurl; CGIurl = new URL(STAT_URL_BASE); URLConnection c = CGIurl.openConnection(); c.setDoOutput(true); c.setUseCaches(false); c.setRequestProperty("content-type","application/x-www-form-urlencoded"); DataOutputStream out = new DataOutputStream(c.getOutputStream()); out.writeBytes(Params); out.flush(); out.close(); // Receive and print output BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream())); String aLine; while ((aLine = in.readLine()) != null) { // data from the CGI System.out.println(aLine); } } catch (Exception ex) { System.out.println("Cannot send stats to" + STAT_URL_BASE); } } }