/*
* Get Organized - Organize your schedule, course assignments, and grades
* Copyright © 2012 Alex Laird
* getorganized@alexlaird.com
* alexlaird.com
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
import com.apple.eawt.Application;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
/**
* The updater for the Get Organized application.
*
* @author Alex Laird
*/
public class GetOrganizedUpdater extends JFrame
{
public static final String DEFAULT_TITLE = "Get Organized Updater";
/** The name of the operating system being used.*/
public static final String OS_NAME = System.getProperty("os.name");
/** The unformatted path location of the updater.*/
public static final String UNFORMATTED_PATH = GetOrganizedUpdater.class.getProtectionDomain().getCodeSource().getLocation().toString().replaceAll("%20", " ");
/** The formatted path for the location of the updater and application.*/
public static String formattedPath;
/** A reference to the desktop model (if supported) for launching files and application.*/
public static Desktop desktop = Desktop.getDesktop();
/** The option pane which can be customized to have yes/no, ok/cancel, or just ok buttons in it.*/
public static final JOptionPane OPTION_PANE = new JOptionPane();
/** The OK button for the option dialog.*/
public static final JButton OK_OPTION_BUTTON = new JButton("Ok");
/** The object which contains only the ok button for the option pane.*/
public static final Object[] OK_CHOICE = new Object[]
{
OK_OPTION_BUTTON
};
/**
* Creates new frame for the application.
*/
public GetOrganizedUpdater()
{
if (OS_NAME.toLowerCase().contains("mac"))
{
Application macApp = Application.getApplication();
macApp.setDockIconImage(new ImageIcon(getClass().getResource("/images/go.png")).getImage());
}
UIManager.put("ProgressBar.selectionForeground", Color.BLACK);
UIManager.put("ProgressBar.selectionBackground", Color.BLACK);
UIManager.put("ProgressBar.foreground", new Color(185, 185, 185));
formattedPath = UNFORMATTED_PATH.substring(UNFORMATTED_PATH.indexOf(":") + 2, UNFORMATTED_PATH.lastIndexOf("/"));
if (!OS_NAME.toLowerCase().contains("windows"))
{
formattedPath = "/" + formattedPath;
}
initComponents();
initMyComponents();
setTitle (DEFAULT_TITLE);
}
/**
*
*/
private void initMyComponents()
{
setLocationRelativeTo(null);
updatesProgressBar.setMinimum(0);
updatesProgressBar.setMaximum(100);
updatesProgressBar.setValue(0);
updatesProgressBar.setString("");
statusLabel.setText("Initializing updater ...");
updatesProgressBar.setStringPainted(true);
OK_OPTION_BUTTON.setBackground(new Color(246, 245, 245));
OK_OPTION_BUTTON.setFont(new Font("Verdana", Font.PLAIN, 13));
OK_OPTION_BUTTON.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
OPTION_PANE.setValue(new Integer(JOptionPane.OK_OPTION));
}
});
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
updatesProgressBar = new javax.swing.JProgressBar();
statusLabel = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Get Organized Updater");
setIconImage(new ImageIcon (getClass ().getResource ("/images/go.png")).getImage ());
setResizable(false);
updatesProgressBar.setFont(new java.awt.Font("Verdana", 0, 12));
updatesProgressBar.setStringPainted(true);
updatesProgressBar.addAncestorListener(new javax.swing.event.AncestorListener() {
public void ancestorMoved(javax.swing.event.AncestorEvent evt) {
}
public void ancestorAdded(javax.swing.event.AncestorEvent evt) {
updatesProgressBarAncestorAdded(evt);
}
public void ancestorRemoved(javax.swing.event.AncestorEvent evt) {
}
});
statusLabel.setFont(new java.awt.Font("Verdana", 0, 12)); // NOI18N
statusLabel.setText("<html>Status Label</html>");
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
.add(org.jdesktop.layout.GroupLayout.LEADING, statusLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 299, Short.MAX_VALUE)
.add(org.jdesktop.layout.GroupLayout.LEADING, updatesProgressBar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 299, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.addContainerGap()
.add(updatesProgressBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(statusLabel)
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void updatesProgressBarAncestorAdded(javax.swing.event.AncestorEvent evt)//GEN-FIRST:event_updatesProgressBarAncestorAdded
{//GEN-HEADEREND:event_updatesProgressBarAncestorAdded
final JFrame frame = this;
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
statusLabel.setText("Connecting to update server ...");
updatesProgressBar.setIndeterminate(true);
String[] files = new File(formattedPath, "..").list();
String fileName = "";
for (int i = 0; i < files.length; ++i)
{
if ((files[i].endsWith(".jar") || files[i].endsWith(".exe"))
&& files[i].toLowerCase().replaceAll(" ", "").contains("getorganized"))
{
fileName = files[i];
}
}
double size = -1;
try
{
URL url = new URL("http://updates.alexlaird.com/get-organized/version.txt");
URLConnection conn = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// throw away the version number line
in.readLine();
if (formattedPath.replaceAll(" ", "").toLowerCase().contains("getorganizedportable")
|| fileName.replaceAll(" ", "").toLowerCase().contains("getorganizedportable"))
{
String line = in.readLine();
String value = line;
if (line.contains("="))
{
String[] split = line.split("=");
value = split[1];
}
size = Double.parseDouble(value);
}
else if (OS_NAME.toLowerCase().contains("windows"))
{
in.readLine();
String line = in.readLine();
String value = line;
if (line.contains("="))
{
String[] split = line.split("=");
value = split[1];
}
size = Double.parseDouble(value);
}
else if (OS_NAME.toLowerCase().contains("mac"))
{
in.readLine();
in.readLine();
String line = in.readLine();
String value = line;
if (line.contains("="))
{
String[] split = line.split("=");
value = split[1];
}
size = Double.parseDouble(value);
}
}
catch (Exception ex)
{
// if any exception occurs, just go get the update
}
updatesProgressBar.setIndeterminate(false);
updatesProgressBar.setString("0%");
setTitle (DEFAULT_TITLE + " (0%)");
statusLabel.setText("Downloading update ...");
// download the latest update over the current Get Organized application
File updateFile = null;
File releaseNotesFile = null;
File licenseFile = null;
InputStream in = null;
InputStream releaseNotesIn = null;
InputStream licenseIn = null;
if (formattedPath.replaceAll(" ", "").toLowerCase().contains("getorganizedportable")
|| fileName.replaceAll(" ", "").toLowerCase().contains("getorganizedportable"))
{
updateFile = new File(formattedPath, "Update.temp");
File oldLicenseFile = new File(formattedPath, "../license.txt");
if (oldLicenseFile.exists())
{
oldLicenseFile.delete();
}
File oldReleaseNotesFile = new File(formattedPath, "../releasenotes.rtf");
if (oldReleaseNotesFile.exists())
{
oldReleaseNotesFile.delete();
}
releaseNotesFile = new File(formattedPath, "../releasenotes.html");
licenseFile = new File(formattedPath, "../license.html");
if (fileName.endsWith(".exe"))
{
URL url = new URL("http://updates.alexlaird.com/get-organized/GetOrganizedPortable.exe");
url.openConnection();
in = url.openStream();
}
else
{
URL url = new URL("http://updates.alexlaird.com/get-organized/GetOrganizedPortable.jar");
url.openConnection();
in = url.openStream();
}
URL url = new URL("http://updates.alexlaird.com/get-organized/releasenotes.html");
url.openConnection();
releaseNotesIn = url.openStream();
url = new URL("http://updates.alexlaird.com/get-organized/license.html");
url.openConnection();
licenseIn = url.openStream();
}
else if (OS_NAME.toLowerCase().contains("windows"))
{
updateFile = new File(formattedPath, "GetOrganizedSetup.exe");
URL url = new URL("http://updates.alexlaird.com/get-organized/GetOrganizedSetup.exe");
url.openConnection();
in = url.openStream();
}
else if (OS_NAME.toLowerCase().contains("mac"))
{
updateFile = new File(formattedPath, "GetOrganizedSetup.pkg.zip");
URL url = new URL("http://updates.alexlaird.com/get-organized/GetOrganizedSetup.pkg.zip");
url.openConnection();
in = url.openStream();
}
if (updateFile != null)
{
if (updateFile.exists())
{
updateFile.delete();
}
FileOutputStream out = new FileOutputStream(updateFile);
byte[] buffer = new byte[1024];
int bytes = 0;
int totalBytes = 0;
while ((bytes = in.read (buffer)) > 0)
{
out.write(buffer, 0, bytes);
buffer = new byte[1024];
if (size != -1)
{
int value = (int) ((totalBytes / size) * 100);
updatesProgressBar.setValue(value);
updatesProgressBar.setString(value + "%");
if (value < 100)
{
setTitle(DEFAULT_TITLE + " (" + value + "%)");
}
else
{
setTitle(DEFAULT_TITLE + " (100%)");
}
}
totalBytes += bytes;
}
in.close();
out.close();
updatesProgressBar.setValue(0);
updatesProgressBar.setString("");
setTitle (DEFAULT_TITLE);
if (releaseNotesFile != null)
{
updatesProgressBar.setIndeterminate(true);
statusLabel.setText ("Downloading latest release notes ...");
if (releaseNotesFile.exists())
{
releaseNotesFile.delete();
}
out = new FileOutputStream(releaseNotesFile);
// download all bytes of the updater
bytes = 0;
while ((bytes = releaseNotesIn.read (buffer)) > 0)
{
out.write(buffer, 0, bytes);
buffer = new byte[153600];
}
releaseNotesIn.close();
out.close();
statusLabel.setText ("Downloading latest license ...");
if (licenseFile.exists())
{
licenseFile.delete();
}
out = new FileOutputStream(licenseFile);
// download all bytes of the updater
bytes = 0;
while ((bytes = releaseNotesIn.read (buffer)) > 0)
{
out.write(buffer, 0, bytes);
buffer = new byte[153600];
}
releaseNotesIn.close();
out.close();
}
else
{
updatesProgressBar.setIndeterminate(true);
}
if (formattedPath.replaceAll(" ", "").toLowerCase().contains("getorganizedportable")
|| fileName.replaceAll(" ", "").toLowerCase().contains("getorganizedportable"))
{
statusLabel.setText("Updating Get Organized ...");
File file = new File(formattedPath, "../" + fileName);
try
{
deleteDir (file);
// copy the new one in its place
copy(updateFile, file);
if (file.exists())
{
updateFile.delete();
}
file = new File (file.getCanonicalPath());
if (file.getCanonicalPath().endsWith(".exe"))
{
if (desktop != null)
{
if (OS_NAME.toLowerCase().contains("windows"))
{
// Since we're on Windows, straggling processes get left sometimes, so kill them
Process p = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String procLine;
while ((procLine = reader.readLine()) != null)
{
if (procLine.contains("Get Organized Portable.exe"))
{
Runtime.getRuntime().exec("taskkill /im \"Get Organized Portable.exe\" /f");
}
if (procLine.contains("GetOrganizedPortable.exe"))
{
Runtime.getRuntime().exec("taskkill /im \"GetOrganizedPortable.exe\" /f");
}
if (procLine.contains("GetOrganized Portable.exe"))
{
Runtime.getRuntime().exec("taskkill /im \"GetOrganized Portable.exe\" /f");
}
}
}
desktop.open(file);
}
else
{
updatesProgressBar.setIndeterminate(false);
OPTION_PANE.setOptions(OK_CHOICE);
OPTION_PANE.setMessage("Get Organized was successfully updated."
+ file.getCanonicalPath());
OPTION_PANE.setMessageType(JOptionPane.INFORMATION_MESSAGE);
JDialog optionDialog = OPTION_PANE.createDialog(frame, "Updated Successfully");
optionDialog.setVisible(true);
}
}
else
{
Runtime.getRuntime().exec(new String[]
{
"java", "-jar", file.getCanonicalPath()
}, null, null);
}
}
catch (IOException ex)
{
updatesProgressBar.setIndeterminate(false);
OPTION_PANE.setOptions(OK_CHOICE);
OPTION_PANE.setMessage("Get Organized was successfully updated."
+ file.getCanonicalPath());
OPTION_PANE.setMessageType(JOptionPane.INFORMATION_MESSAGE);
JDialog optionDialog = OPTION_PANE.createDialog(frame, "Updated Successfully");
optionDialog.setVisible(true);
}
catch (Exception ex)
{
updatesProgressBar.setIndeterminate(false);
OPTION_PANE.setOptions(OK_CHOICE);
OPTION_PANE.setMessage("The update was downloaded but could not be renamed and\n"
+ "moved into place. To apply the update, copy the Get Organized file located at\n"
+ updateFile.getCanonicalPath() + "\n"
+ "and use it to overwrite the existing application at\n"
+ file.getCanonicalPath() + "\n"
+ "More simply, the update can be installed automatically\n"
+ "from alexlaird.com/projects/get-organized.");
OPTION_PANE.setMessageType(JOptionPane.ERROR_MESSAGE);
JDialog optionDialog = OPTION_PANE.createDialog(frame, "Unable to Update Automatically");
optionDialog.setVisible(true);
}
}
else if (OS_NAME.toLowerCase().contains("windows")
|| OS_NAME.toLowerCase().contains("mac"))
{
statusLabel.setText("Launching Get Organized setup ...");
if (OS_NAME.toLowerCase().contains("windows"))
{
// Since we're on Windows, straggling processes get left sometimes, so kill them
Process p = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String procLine;
while ((procLine = reader.readLine()) != null)
{
if (procLine.contains("Get Organized.exe"))
{
Runtime.getRuntime().exec("taskkill /im \"Get Organized.exe\" /f");
}
if (procLine.contains("GetOrganized.exe"))
{
Runtime.getRuntime().exec("taskkill /im \"GetOrganized.exe\" /f");
}
}
}
if (desktop != null)
{
if (OS_NAME.toLowerCase().contains("mac"))
{
ZipFile zipFile = new ZipFile(updateFile);
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements())
{
ZipEntry entry = (ZipEntry) entries.nextElement();
if (entry.isDirectory())
{
// This is not robust, just for demonstration purposes.
(new File(formattedPath, entry.getName())).mkdir();
continue;
}
copyInputStream(zipFile.getInputStream(entry), new BufferedOutputStream(new FileOutputStream(new File(formattedPath, entry.getName()))));
}
zipFile.close();
updateFile.delete();
updateFile = new File(formattedPath, "GetOrganizedSetup.pkg");
}
desktop.open(updateFile);
}
else
{
updatesProgressBar.setIndeterminate(false);
OPTION_PANE.setOptions(OK_CHOICE);
OPTION_PANE.setMessage("The update was downloaded but the installer could not\n"
+ "be launched. Please launch the installer manually from the\n"
+ "following location:\n"
+ updateFile.getCanonicalPath());
OPTION_PANE.setMessageType(JOptionPane.ERROR_MESSAGE);
JDialog optionDialog = OPTION_PANE.createDialog(frame, "Unable to Update Automatically");
optionDialog.setVisible(true);
}
}
}
else
{
updatesProgressBar.setIndeterminate(false);
OPTION_PANE.setOptions(OK_CHOICE);
OPTION_PANE.setMessage("The update cannot be downloaded automatically for this\n"
+ "system. Please download the update manually from\n"
+ "alexlaird.com/projects/get-organized.");
OPTION_PANE.setMessageType(JOptionPane.ERROR_MESSAGE);
JDialog optionDialog = OPTION_PANE.createDialog(frame, "Unable to Update Automatically");
optionDialog.setVisible(true);
if (desktop != null)
{
try
{
desktop.browse(new URI("http://alexlaird.com/projects/get-organized"));
}
catch (Exception ex)
{
}
}
}
System.exit(0);
}
catch (Exception ex)
{
updatesProgressBar.setIndeterminate(false);
OPTION_PANE.setOptions(OK_CHOICE);
OPTION_PANE.setMessage("An unknown error has occured. Try updating the application manually\n"
+ "by going to alexlaird.com/projects/get-organized.");
OPTION_PANE.setMessageType(JOptionPane.ERROR_MESSAGE);
JDialog optionDialog = OPTION_PANE.createDialog(frame, "Unable to Update Automatically");
optionDialog.setVisible(true);
if (desktop != null)
{
try
{
desktop.browse(new URI("http://alexlaird.com/projects/get-organized"));
}
catch (Exception innerEx)
{
}
}
System.exit(0);
}
}
}).start();
}//GEN-LAST:event_updatesProgressBarAncestorAdded
/**
* Remove contents of a directory.
*
* @param dir The directory to be deleted.
* @return True if the directory was deleted, false otherwise.
*/
public static boolean deleteDir(File dir)
{
if (dir.isDirectory())
{
String[] children = dir.list();
for (int i = 0; i < children.length; i++)
{
boolean success = deleteDir(new File(dir, children[i]));
if (!success)
{
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
/**
* Copy the given file to the new file.
*
* @param fromFile The file to be copied from.
* @param toFile The file to be copied to.
* @throws IOException If any exception occurs.
*/
private void copy(File fromFile, File toFile) throws IOException
{
FileInputStream from = new FileInputStream(fromFile);
FileOutputStream to = new FileOutputStream(toFile);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = from.read(buffer)) != -1)
{
to.write(buffer, 0, bytesRead);
}
from.close();
to.close();
}
/**
* Copy data from the input stream to the output stream.
*
* @param in The input stream to copy from.
* @param out The output stream to copy to.
* @throws IOException
*/
public static void copyInputStream(InputStream in, OutputStream out) throws IOException
{
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) >= 0)
{
out.write(buffer, 0, len);
}
in.close();
out.close();
}
/**
* @param args the command line arguments
*/
public static void main(String args[])
{
// define UI characteristics before the applicaiton is instantiated
try
{
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
}
catch (ClassNotFoundException ex)
{
}
catch (InstantiationException ex)
{
}
catch (IllegalAccessException ex)
{
}
catch (UnsupportedLookAndFeelException ex)
{
}
java.awt.EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
new GetOrganizedUpdater().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel statusLabel;
private javax.swing.JProgressBar updatesProgressBar;
// End of variables declaration//GEN-END:variables
}