/* * The tracker package defines a set of video/image analysis tools * built on the Open Source Physics framework by Wolfgang Christian. * * Copyright (c) 2019 Douglas Brown * * Tracker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Tracker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tracker; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA * or view the license online at <http://www.gnu.org/copyleft/gpl.html> * * For additional Tracker information and documentation, please see * <http://physlets.org/tracker/>. */ package org.opensourcephysics.cabrillo.tracker; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import org.opensourcephysics.cabrillo.tracker.deploy.TrackerStarter; import org.opensourcephysics.controls.OSPLog; import org.opensourcephysics.controls.XML; import org.opensourcephysics.desktop.OSPDesktop; import org.opensourcephysics.display.OSPRuntime; import org.opensourcephysics.tools.FontSizer; import org.opensourcephysics.tools.ResourceLoader; import org.opensourcephysics.tools.ToolsRes; /** * A class to upgrade Tracker. * * @author Douglas Brown */ public class Upgrader { TFrame frame; JDialog upgradeDialog; JLabel downloadLabel, relaunchLabel; public Upgrader(TFrame tFrame) { frame = tFrame; } public void upgrade() { // get upgrade dialog getUpgradeDialog(); // initialize boolean[] failed = new boolean[] {false}; int responseCode = 0; // code 200 = "OK" // look for upgrade tracker.jar final String jarFileName = "tracker-"+Tracker.newerVersion+".jar"; //$NON-NLS-1$ //$NON-NLS-2$ final String upgradeURL = ResourceLoader.getString("https://physlets.org/tracker/upgradeURL.txt"); //$NON-NLS-1$ if (upgradeURL!=null && Tracker.trackerHome!=null) { String upgradeFile = upgradeURL.trim()+jarFileName; try { URL url = new URL(upgradeFile); HttpURLConnection huc = (HttpURLConnection)url.openConnection(); responseCode = huc.getResponseCode(); } catch (Exception ex) { } } if (responseCode!=200) { // jar file not found failed[0] = true; } else if (OSPRuntime.isWindows()) { upgradeWindows(failed); } else if (OSPRuntime.isMac()) { // OSX upgradeOSX(failed); } else if (OSPRuntime.isLinux()) { upgradeLinux(failed); } if (failed[0]) { // close upgrade dialog and display Tracker web site closeUpgradeDialog(); String websiteurl = "https://"+Tracker.trackerWebsite; //$NON-NLS-1$ OSPDesktop.displayURL(websiteurl); } } private void upgradeWindows(final boolean[] failed) { // check for upgrade installer final String jarFileName = "tracker-"+Tracker.newerVersion+".jar"; //$NON-NLS-1$ //$NON-NLS-2$ final String upgradeURL = ResourceLoader.getString("https://physlets.org/tracker/upgradeURL.txt"); //$NON-NLS-1$ final String upgradeInstallerName = "TrackerUpgrade-"+Tracker.newerVersion+"-windows-installer.exe"; //$NON-NLS-1$ //$NON-NLS-2$ final String upgradeInstallerURL = upgradeURL.trim()+upgradeInstallerName; int responseCode = 0; try { URL url = new URL(upgradeInstallerURL); HttpURLConnection huc = (HttpURLConnection)url.openConnection(); responseCode = huc.getResponseCode(); } catch (Exception ex) { } if (responseCode==200) { // upgrade installer exists File downloadDir = OSPRuntime.getDownloadDir(); // let user choose downloadDir = chooseDownloadDirectory(frame, downloadDir); if (downloadDir==null) { // user cancelled return; } if (!downloadDir.exists()) { // failed to specify valid download directory OSPLog.warning("download directory does not exist: "+downloadDir); //$NON-NLS-1$ failed[0] = true; } else { // download and launch installer in separate thread final File downloads = downloadDir; Runnable runner = new Runnable() { public void run() { File installer = new File(downloads, upgradeInstallerName); // show relaunching dialog during download downloadLabel.setText((TrackerRes.getString("TTrackBar.Dialog.Relaunch.DownloadLabel.Upgrade.Text") //$NON-NLS-1$ +" "+installer.getPath()+".")); //$NON-NLS-1$ //$NON-NLS-2$ relaunchLabel.setText((TrackerRes.getString("TTrackBar.Dialog.Relaunch.RelaunchLabel.Upgrade.Text"))); //$NON-NLS-1$ upgradeDialog.pack(); // center on TFrame upgradeDialog.setLocationRelativeTo(frame); upgradeDialog.setVisible(true); // download upgrade installer installer = ResourceLoader.download(upgradeInstallerURL, installer, true); // close dialog when done downloading closeUpgradeDialog(); if (installer!=null && installer.exists()) { // get OK to run installer and close Tracker int ans = JOptionPane.showConfirmDialog(frame, TrackerRes.getString("Upgrader.Dialog.Downloaded.Message1")+" "+installer.getPath()+"." //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ +XML.NEW_LINE+TrackerRes.getString("Upgrader.Dialog.Downloaded.Message2")+XML.NEW_LINE, //$NON-NLS-1$ TrackerRes.getString("TTrackBar.Dialog.Download.Title"), //$NON-NLS-1$ JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); if (ans!=JOptionPane.OK_OPTION) { return; } // launch the upgrade installer and close Tracker try { // assemble command: pass tracker home as parameter ArrayList<String> cmd = new ArrayList<String>(); cmd.add("cmd"); //$NON-NLS-1$ cmd.add("/c"); //$NON-NLS-1$ cmd.add(installer.getPath()); cmd.add("--tracker-home"); //$NON-NLS-1$ cmd.add(Tracker.trackerHome); // log command String message = ""; //$NON-NLS-1$ for (String next: cmd) { message += next + " "; //$NON-NLS-1$ } OSPLog.info("executing command: " + message); //$NON-NLS-1$ ProcessBuilder builder = new ProcessBuilder(cmd); Process p = builder.start(); if (isAlive(p)) { // set preferred tracker to default Tracker.preferredTrackerJar = null; Tracker.savePreferences(); // exit Tracker TrackerPanel trackerPanel = frame.getTrackerPanel(frame.getSelectedTab()); if (trackerPanel!=null) { Action exit = TActions.getAction("exit", trackerPanel); //$NON-NLS-1$ exit.actionPerformed(null); } else { System.exit(0); } } else { // upgrade installer launch failure OSPLog.warning("failed to launch upgrade installer"); //$NON-NLS-1$ failed[0] = true; } } catch (Exception ex) { OSPLog.warning("exception: "+ex); //$NON-NLS-1$ failed[0] = true; } } else { // failed to download upgrade installer OSPLog.warning("failed to download upgrade installer"); //$NON-NLS-1$ failed[0] = true; } if (failed[0]) { // close upgrade dialog and display Tracker web site closeUpgradeDialog(); String websiteurl = "https://"+Tracker.trackerWebsite; //$NON-NLS-1$ OSPDesktop.displayURL(websiteurl); } } }; // end runnable new Thread(runner).start(); } } // end upgrade installer else { // no upgrade installer so download tracker.jar // inform user of intended action and ask permission int ans = JOptionPane.showConfirmDialog(frame, TrackerRes.getString("TTrackBar.Dialog.Download.Message1")+" "+Tracker.trackerHome+"." //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ +XML.NEW_LINE+TrackerRes.getString("TTrackBar.Dialog.Download.Message2")+XML.NEW_LINE, //$NON-NLS-1$ TrackerRes.getString("TTrackBar.Dialog.Download.Title"), //$NON-NLS-1$ JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); if (ans!=JOptionPane.OK_OPTION) { // user cancelled return; } Runnable runner = new Runnable() { public void run() { // show relaunching dialog during downloads downloadLabel.setText((TrackerRes.getString("TTrackBar.Dialog.Relaunch.DownloadLabel.Text") //$NON-NLS-1$ +" "+Tracker.trackerHome+".")); //$NON-NLS-1$ //$NON-NLS-2$ relaunchLabel.setText((TrackerRes.getString("TTrackBar.Dialog.Relaunch.RelaunchLabel.Text"))); //$NON-NLS-1$ upgradeDialog.pack(); // center on TFrame upgradeDialog.setLocationRelativeTo(frame); upgradeDialog.setVisible(true); // download new tracker jar File jarFile = new File(Tracker.trackerHome, jarFileName); String jarURL = upgradeURL.trim()+jarFileName; jarFile = ResourceLoader.download(jarURL, jarFile, true); // also download new Tracker.exe if available String starterName = "Tracker.exe"; //$NON-NLS-1$ String starterURL = upgradeURL.trim()+starterName; int responseCode = 0; try { URL url = new URL(starterURL); HttpURLConnection huc = (HttpURLConnection)url.openConnection(); responseCode = huc.getResponseCode(); } catch (Exception ex) { } if (responseCode==200) { // Tracker.exe is available File starterTarget = new File(Tracker.trackerHome, starterName); ResourceLoader.download(starterURL, starterTarget, true); } if (jarFile!=null && jarFile.exists()) { // new jar successfully downloaded // launch new Tracker version ArrayList<String> filenames = new ArrayList<String>(); for (int i = 0; i<frame.getTabCount(); i++) { TrackerPanel next = frame.getTrackerPanel(i); if (!next.save()) { // user aborted the relaunch closeUpgradeDialog(); return; } File datafile = next.getDataFile(); if (datafile!=null) { filenames.add(datafile.getAbsolutePath()); } } String[] args = filenames.isEmpty()? null: filenames.toArray(new String[0]); System.setProperty(TrackerStarter.PREFERRED_TRACKER_JAR, jarFile.getAbsolutePath()); System.setProperty(TrackerStarter.TRACKER_NEW_VERSION, jarURL); TrackerStarter.relaunch(args, false); } else { OSPLog.warning("failed to download new version"); //$NON-NLS-1$ failed[0] = true; } if (failed[0]) { // close upgrade dialog and display Tracker web site closeUpgradeDialog(); String websiteurl = "https://"+Tracker.trackerWebsite; //$NON-NLS-1$ OSPDesktop.displayURL(websiteurl); } } }; // end runnable new Thread(runner).start(); } // end new tracker.jar } private void upgradeOSX(final boolean[] failed) { // see if a TrackerUpgrade zip is available final String upgradeURL = ResourceLoader.getString("https://physlets.org/tracker/upgradeURL.txt"); //$NON-NLS-1$ final String dmgFileName = "TrackerUpgrade-"+Tracker.newerVersion+"-osx-installer.dmg"; //$NON-NLS-1$ //$NON-NLS-2$ final String dmgURL = upgradeURL.trim()+dmgFileName; int responseCode = 0; try { URL url = new URL(dmgURL); HttpURLConnection huc = (HttpURLConnection)url.openConnection(); responseCode = huc.getResponseCode(); } catch (Exception ex) { } if (responseCode==200) { // upgrade installer exists File downloadDir = OSPRuntime.getDownloadDir(); // let user choose downloadDir = chooseDownloadDirectory(frame, downloadDir); if (downloadDir==null) { // user cancelled return; } if (!downloadDir.exists()) { // OSX chooser is weird--sometimes returns desired folder as filename also File parent = downloadDir.getParentFile(); if (parent!=null && parent.getName().equals(downloadDir.getName())) { downloadDir = parent; } } if (!downloadDir.exists()) { // failed to specify valid download directory OSPLog.warning("download directory does not exist: "+downloadDir); //$NON-NLS-1$ failed[0] = true; } else { final File downloads = downloadDir; // download, mount dmg and run installer in separate thread Runnable runner = new Runnable() { public void run() { File dmgFile = new File(downloads, dmgFileName); String appName = "TrackerUpgrade-"+Tracker.newerVersion+"-osx-installer.app"; //$NON-NLS-1$ //$NON-NLS-2$ // show relaunching dialog during download downloadLabel.setText((TrackerRes.getString("TTrackBar.Dialog.Relaunch.DownloadLabel.Upgrade.Text") //$NON-NLS-1$ +" "+dmgFile.getPath()+".")); //$NON-NLS-1$ //$NON-NLS-2$ relaunchLabel.setText(""); //$NON-NLS-1$ upgradeDialog.pack(); // center on TFrame upgradeDialog.setLocationRelativeTo(frame); upgradeDialog.setVisible(true); // download dmg file dmgFile = ResourceLoader.download(dmgURL, dmgFile, true); if (dmgFile!=null && dmgFile.exists()) { // use hdiutil to mount String path = dmgFile.getPath(); ArrayList<String> cmd = new ArrayList<String>(); cmd.add("hdiutil"); //$NON-NLS-1$ cmd.add("attach"); //$NON-NLS-1$ cmd.add("-noverify"); //$NON-NLS-1$ cmd.add("-autoopen"); //$NON-NLS-1$ cmd.add(path); try { ProcessBuilder builder = new ProcessBuilder(cmd); Process p = builder.start(); // read output of the process BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())); StringBuilder blder = new StringBuilder(); String line = null; while ( (line = reader.readLine()) != null) { blder.append(line); } String output = blder.toString(); // output is /dev node, tab, mount point String[] chunks = output.split("\t"); //$NON-NLS-1$ // wait for process to finish p.waitFor(); // dmgFile.delete(); // upgrade installer dmg should now be mounted closeUpgradeDialog(); if (chunks.length>1) { String volume = chunks[chunks.length-1]; File installer = new File(volume, appName); // // get OK to run installer // int ans = JOptionPane.showConfirmDialog(frame, // TrackerRes.getString("Upgrader.Dialog.Downloaded.Message1") //$NON-NLS-1$ // +XML.NEW_LINE+TrackerRes.getString("Upgrader.Dialog.Downloaded.Message2")+XML.NEW_LINE, //$NON-NLS-1$ // TrackerRes.getString("TTrackBar.Dialog.Download.Title"), //$NON-NLS-1$ // JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); // if (ans!=JOptionPane.OK_OPTION) { // return; // } // run upgrade installer cmd = new ArrayList<String>(); cmd.add("open"); //$NON-NLS-1$ cmd.add(installer.getCanonicalPath()); try { builder = new ProcessBuilder(cmd); p = builder.start(); } catch (Exception ex) { OSPLog.warning("exception: "+ex); //$NON-NLS-1$ failed[0] = true; } p.waitFor(); // exit Tracker TrackerPanel trackerPanel = frame.getTrackerPanel(frame.getSelectedTab()); if (trackerPanel!=null) { Action exit = TActions.getAction("exit", trackerPanel); //$NON-NLS-1$ exit.actionPerformed(null); } else { System.exit(0); } } else { OSPLog.warning("failed to mount upgrade installer"); //$NON-NLS-1$ failed[0] = true; } } catch (Exception ex) { OSPLog.warning("exception: "+ex); //$NON-NLS-1$ failed[0] = true; } } else { OSPLog.warning("failed to download upgrade installer"); //$NON-NLS-1$ failed[0] = true; } if (failed[0]) { // close upgrade dialog and display Tracker web site closeUpgradeDialog(); String websiteurl = "https://"+Tracker.trackerWebsite; //$NON-NLS-1$ OSPDesktop.displayURL(websiteurl); } } }; // end runnable new Thread(runner).start(); } } else { OSPLog.warning("no upgrade installer found on server"); //$NON-NLS-1$ failed[0] = true; } } private void upgradeLinux(final boolean[] failed) { final String upgradeURL = ResourceLoader.getString("https://physlets.org/tracker/upgradeURL.txt"); //$NON-NLS-1$ // see if a TrackerUpgrade file is available int bitness = OSPRuntime.getVMBitness(); // see if a TrackerUpgrade zip is available final String upgradeFileName = "TrackerUpgrade-"+Tracker.newerVersion+"-linux-"+bitness+"bit-installer.run"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ final String fileURL = upgradeURL.trim()+upgradeFileName; int responseCode = 0; try { URL url = new URL(fileURL); HttpURLConnection huc = (HttpURLConnection)url.openConnection(); responseCode = huc.getResponseCode(); } catch (Exception ex) { } if (responseCode==200) { // upgrade installer exists File downloadDir = OSPRuntime.getDownloadDir(); // let user choose downloadDir = chooseDownloadDirectory(frame, downloadDir); if (downloadDir==null) { // user cancelled return; } if (!downloadDir.exists()) { // failed to specify valid download directory OSPLog.warning("download directory does not exist: "+downloadDir); //$NON-NLS-1$ failed[0] = true; } else { final File downloads = downloadDir; // download and run installer in separate thread Runnable runner = new Runnable() { public void run() { // show relaunching dialog during download downloadLabel.setText((TrackerRes.getString("TTrackBar.Dialog.Relaunch.DownloadLabel.Upgrade.Text") //$NON-NLS-1$ +" "+downloads.getPath()+"/"+upgradeFileName+".")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // show relaunching dialog during download relaunchLabel.setText(""); //$NON-NLS-1$ FontSizer.setFonts(upgradeDialog, FontSizer.getLevel()); upgradeDialog.pack(); // center on TFrame upgradeDialog.setLocationRelativeTo(frame); upgradeDialog.setVisible(true); // download upgrade installer File installer = new File(downloads, upgradeFileName); installer = ResourceLoader.download(fileURL, installer, true); closeUpgradeDialog(); if (installer!=null && installer.exists()) { installer.setExecutable(true, false); // create text field to display copy-able command for Terminal final JTextField field = new JTextField(10); field.setBackground(Color.white); field.setEditable(false); field.addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { field.selectAll(); } }); // assemble command: pass tracker home as parameter String cmd = "sudo "+installer.getPath(); //$NON-NLS-1$ cmd += " --tracker-home "+Tracker.trackerHome; //$NON-NLS-1$ // log command OSPLog.info("execution command: " + cmd); //$NON-NLS-1$ field.setText(cmd); JPanel panel = new JPanel(new BorderLayout()); JLabel label1 = new JLabel(TrackerRes.getString("Upgrader.Dialog.Downloaded.Message1") //$NON-NLS-1$ +" "+installer.getPath()+"."); //$NON-NLS-1$ //$NON-NLS-2$ label1.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 4)); JLabel label2 = new JLabel(TrackerRes.getString("Upgrader.Dialog.Downloaded.Linux.Message2")); //$NON-NLS-1$ label2.setBorder(BorderFactory.createEmptyBorder(4, 0, 10, 4)); JLabel label3 = new JLabel(TrackerRes.getString("Upgrader.Dialog.Downloaded.Linux.Message3")); //$NON-NLS-1$ label3.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 4)); Box box = Box.createVerticalBox(); box.add(label1); box.add(label2); box.add(field); box.add(label3); panel.add(box, BorderLayout.NORTH); // inform user of required action and intent to close Tracker int ans = JOptionPane.showConfirmDialog(frame, panel, TrackerRes.getString("TTrackBar.Dialog.Download.Title"), //$NON-NLS-1$ JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE); if (ans!=JOptionPane.OK_OPTION) { return; } // copy command to the clipboard // // following lines don't work on Ubuntu (known issue as of Jan 2018) // StringSelection data = new StringSelection(cmd); // Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); // clipboard.setContents(data, data); TrackerPanel trackerPanel = frame.getTrackerPanel(frame.getSelectedTab()); if (trackerPanel!=null) { Action exit = TActions.getAction("exit", trackerPanel); //$NON-NLS-1$ exit.actionPerformed(null); } else { System.exit(0); } } else { OSPLog.warning("failed to download upgrade installer"); //$NON-NLS-1$ failed[0] = true; } if (failed[0]) { // close upgrade dialog and display Tracker web site closeUpgradeDialog(); String websiteurl = "https://"+Tracker.trackerWebsite; //$NON-NLS-1$ OSPDesktop.displayURL(websiteurl); } } }; // end runnable new Thread(runner).start(); } } else { OSPLog.warning("no upgrade installer found on server"); //$NON-NLS-1$ failed[0] = true; } } private void closeUpgradeDialog() { if (upgradeDialog!=null) { upgradeDialog.setVisible(false); upgradeDialog.dispose(); upgradeDialog = null; } } /** * Uses a JFileChooser to select a download directory. * * @param parent a component to own the file chooser * @param likely the default likely directory * @return the chosen directory file */ private File chooseDownloadDirectory(Component parent, File likely) { JFileChooser chooser = new JFileChooser() { public void approveSelection() { if (getSelectedFile().isFile()) { return; } else super.approveSelection(); } }; chooser.setDialogTitle(TrackerRes.getString("TTrackBar.Chooser.DownloadDirectory")); //$NON-NLS-1$ chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); javax.swing.filechooser.FileFilter folderFilter = new javax.swing.filechooser.FileFilter() { // accept directories only public boolean accept(File f) { if (f==null) return false; if (f.getName().endsWith(".app")) return false; //$NON-NLS-1$ return f.isDirectory(); } public String getDescription() { return ToolsRes.getString("LibraryTreePanel.FolderFileFilter.Description"); //$NON-NLS-1$ } }; chooser.setAcceptAllFileFilterUsed(false); chooser.addChoosableFileFilter(folderFilter); chooser.setCurrentDirectory(new File(likely.getParent())); chooser.setSelectedFile(likely); if (OSPRuntime.isMac()) { chooser.updateUI(); } FontSizer.setFonts(chooser, FontSizer.getLevel()); int result = chooser.showDialog(parent, TrackerRes.getString("Dialog.Button.OK")); //$NON-NLS-1$ if (result==JFileChooser.APPROVE_OPTION) { return chooser.getSelectedFile(); } return null; } private JDialog getUpgradeDialog() { // create relaunching dialog if (upgradeDialog==null) { upgradeDialog = new JDialog(frame, false); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEtchedBorder()); upgradeDialog.setContentPane(panel); upgradeDialog.setTitle(TrackerRes.getString("TTrackBar.Dialog.Relaunch.Title.Text")); //$NON-NLS-1$ Box box = Box.createVerticalBox(); upgradeDialog.getContentPane().add(box); downloadLabel = new JLabel(); downloadLabel.setBorder(BorderFactory.createEmptyBorder(10, 6, 6, 6)); box.add(downloadLabel); relaunchLabel = new JLabel(); relaunchLabel.setBorder(BorderFactory.createEmptyBorder(6, 6, 16, 6)); } return upgradeDialog; } private boolean isAlive(Process process) { try { process.exitValue(); return false; } catch (Exception e) { return true; } } }