Initial release

This commit is contained in:
Norberto Lopez
2013-04-06 01:58:46 +00:00
parent 032594eb35
commit ab6a50e29c
7 changed files with 611 additions and 0 deletions

Binary file not shown.

Binary file not shown.

BIN
lib/miglayout-3.7.2.jar Normal file

Binary file not shown.

View File

@@ -0,0 +1,267 @@
package distMaker;
import glum.gui.panel.task.FullTaskPanel;
import glum.io.IoUtil;
import glum.net.Credential;
import glum.reflect.FunctionRunnable;
import glum.task.Task;
import glum.unit.DateUnit;
import glum.util.ThreadUtil;
import java.io.*;
import java.net.URL;
import java.util.List;
import javax.swing.JFrame;
import distMaker.gui.PickReleasePanel;
public class DistMakerEngine
{
// State vars
private URL updateUrl;
private Release currRelease;
private Credential refCredential;
// Gui vars
private JFrame parentFrame;
private PickReleasePanel pickVersionPanel;
public DistMakerEngine(JFrame aParentFrame, URL aUpdateUrl)
{
updateUrl = aUpdateUrl;
refCredential = null;
parentFrame = aParentFrame;
initialize();
}
/**
* Method that will notify the user that updates are being checked for
*/
public void checkForUpdates()
{
FullTaskPanel taskPanel;
String appName;
appName = currRelease.getName();
// Setup our TaskPanel
taskPanel = new FullTaskPanel(parentFrame, true, false);
taskPanel.setTitle(appName + ": Checking for updates...");
taskPanel.setSize(640, taskPanel.getPreferredSize().height);
taskPanel.setVisible(true);
// Launch the actual checking of updates in a separate worker thread
ThreadUtil.launchRunnable(new FunctionRunnable(this, "checkForUpdatesWorker", taskPanel), "thread-checkForUpdates");
}
/**
* Notification that the corresponding application has been fully initialized.
*/
public void markSystemFullyStarted()
{
// TODO: Flush this out
}
/**
* Sets in the credentials used to access the update site. If either argument is null, then the credentials will be
* cleared out.
*/
public void setCredentials(String aUsername, char[] aPassword)
{
refCredential = null;
if (aUsername == null || aPassword == null)
return;
refCredential = new Credential(aUsername, aPassword);
}
/**
* Helper method to fully set up this object
*/
private void initialize()
{
File appPath, cfgFile;
BufferedReader br;
DateUnit dateUnit;
String currInstr, strLine;
String appName, verName, deployStr;
long deployTime;
currRelease = null;
appName = null;
verName = null;
deployStr = null;
// Locate the official DistMaker configuration file associated with this release
appPath = DistUtils.getAppPath();
cfgFile = new File(appPath, "app.cfg");
// Bail if there is no configuration file
if (cfgFile.isFile() == false)
return;
// Read in the configuration file
br = null;
try
{
br = new BufferedReader(new InputStreamReader(new FileInputStream(cfgFile)));
// Read the lines
currInstr = "None";
while (true)
{
strLine = br.readLine();
// Bail once we get to the end of the file
if (strLine == null)
break;
// Update the current instruction, if one specified
if (strLine.startsWith("-") == true)
currInstr = strLine;
// Skip empty lines / comments
else if (strLine.isEmpty() == true || strLine.startsWith("#") == true)
; // Nothing to do
// Process the name instruction
else if (currInstr.equals("-name") == true)
appName = strLine;
else if (currInstr.equals("-version") == true)
verName = strLine;
else if (currInstr.equals("-deployDate") == true)
deployStr = strLine;
}
}
catch (IOException aExp)
{
aExp.printStackTrace();
}
finally
{
IoUtil.forceClose(br);
}
if (appName == null || verName == null || deployStr == null)
{
System.out.println("Failed to properly parse DistMaker config file: " + cfgFile);
return;
}
// Form the installed Release
dateUnit = new DateUnit("", "yyyyMMMdd HH:mm:ss");
deployTime = dateUnit.parseString(deployStr, 0);
currRelease = new Release(appName, verName, deployTime);
// Form the PickReleasePanel
pickVersionPanel = new PickReleasePanel(parentFrame, currRelease);
}
/**
* Helper method that does the heavy lifting of the checking for updates.
* <P>
* This method will be called via reflection.
*/
@SuppressWarnings("unused")
private void checkForUpdatesWorker(Task aTask)
{
List<Release> fullList;
Release chosenItem;
File destPath;
String appName;
boolean isPass;
// Determine the destination where to drop the release
destPath = new File(DistUtils.getAppPath().getParentFile(), "delta");
// Status info
appName = currRelease.getName();
aTask.infoAppendln("Application: " + appName + " - " + currRelease.getVersion() + '\n');
// Retrieve the list of available releases
aTask.infoAppendln("Checking for updates...\n");
fullList = DistUtils.getAvailableReleases(aTask, updateUrl, appName, refCredential);
if (fullList == null)
return;
// Prompt the user for the Release
aTask.infoAppendln("Please select the release to install...");
pickVersionPanel.setConfiguration(fullList);
pickVersionPanel.setVisibleAsModal();
chosenItem = pickVersionPanel.getChosenItem();
if (chosenItem == null)
{
aTask.infoAppendln("No release specified. Update has been aborted.");
aTask.abort();
return;
}
// Log the user chosen action
aTask.infoAppendln("\tRelease chosen: " + chosenItem.getVersion());
if (currRelease.getDeployTime() < chosenItem.getDeployTime())
aTask.infoAppendln("\t" + appName + " will be updated...");
else
aTask.infoAppendln("\t" + appName + " will be reverted...");
// Download the release
isPass = downloadRelease(aTask, chosenItem, destPath);
if (isPass == false || aTask.isActive() == false)
{
IoUtil.deleteDirectory(destPath);
aTask.infoAppendln("Application update aborted.");
return;
}
// Notify the user of success
aTask.infoAppendln(appName + " has been updated to version: " + chosenItem.getDeployTime() + ".");
aTask.infoAppendln("These updates will become active when " + appName + " is restarted.");
}
/**
* Helper method to download the specified release.
* <P>
* Returns true if the release was downloaded properly.
*/
private boolean downloadRelease(Task aTask, Release aRelease, File destPath)
{
List<File> fileList;
String baseUrlStr, srcUrlStr;
URL srcUrl;
int clipLen;
boolean isPass;
// Retrieve the list of files to download
fileList = DistUtils.getFileListForRelease(aTask, updateUrl, aRelease, destPath, refCredential);
// Compute some baseline vars
baseUrlStr = updateUrl.toString() + "/" + aRelease.getVersion() + "/";
clipLen = destPath.getAbsolutePath().length();
// Download the individual files
for (File aFile : fileList)
{
// Bail if we have been aborted
if (aTask.isActive() == false)
return false;
srcUrlStr = baseUrlStr + aFile.getAbsolutePath().substring(clipLen);
srcUrl = IoUtil.createURL(srcUrlStr);
aTask.infoAppendln(srcUrlStr + " -> " + aFile);
aFile.getParentFile().mkdirs();
isPass = IoUtil.copyUrlToFile(srcUrl, aFile);// , Username, Password);
if (isPass == false)
{
aFile.delete();
aTask.infoAppendln("Failed to download resource: " + srcUrl);
aTask.infoAppendln("\tSource: " + srcUrlStr);
aTask.infoAppendln("\tDest: " + aFile);
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,261 @@
package distMaker;
import glum.gui.GuiUtil;
import glum.io.IoUtil;
import glum.net.*;
import glum.reflect.ReflectUtil;
import glum.task.Task;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import com.google.common.collect.Lists;
public class DistUtils
{
/**
* Utility method to determine the path where the application is installed.
* <P>
* If this application is not a formal DistMaker application, then the parent folder corresponding to the folder
* which contains this jar will be returned.
*/
public static File getAppPath()
{
File jarPath, currPath, testPath;
jarPath = ReflectUtil.getInstalledRootDir(DistUtils.class);
currPath = jarPath;
while (currPath != null)
{
currPath = currPath.getParentFile();
testPath = new File(currPath, "app.cfg");
if (testPath.isFile() == true)
return testPath;
}
// Return default location
return jarPath.getParentFile().getParentFile();
}
/**
* Returns the list of available releases.
*/
public static List<Release> getAvailableReleases(Task aTask, URL aUpdateUrl, String appName, Credential aCredential)
{
List<Release> fullList;
URL catUrl;
URLConnection connection;
InputStream inStream;
BufferedReader bufReader;
String errMsg;
errMsg = null;
fullList = Lists.newArrayList();
catUrl = IoUtil.createURL(aUpdateUrl.toString() + "/" + appName + "/" + "releaseInfo.txt");
connection = null;
inStream = null;
bufReader = null;
try
{
String[] tokens;
String strLine, verName;
long deployTime;
// Read the contents of the file
connection = catUrl.openConnection();
inStream = NetUtil.getInputStream(connection, aCredential);
bufReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(inStream)));
// Read the lines
while (true)
{
strLine = bufReader.readLine();
// Bail once we are done
if (strLine == null)
break;
tokens = strLine.split(",");
if (strLine.isEmpty() == true || strLine.startsWith("#") == true)
; // Nothing to do
else if (tokens.length == 2 && tokens[0].equals("name") == true)
; // Nothing to do
else if (tokens.length != 2)
aTask.infoAppendln("Unreconized line: " + strLine);
else
// if (tokens.length == 2)
{
verName = tokens[0];
deployTime = GuiUtil.readLong(tokens[1], 0);
fullList.add(new Release(appName, verName, deployTime));
}
}
}
catch (IOException aExp)
{
Result aResult;
aExp.printStackTrace();
errMsg = "The update site, " + aUpdateUrl + ", is not available.\n\t";
aResult = NetUtil.getResult(aExp, connection);
switch (aResult)
{
case BadCredentials:
errMsg += "The update site is password protected and bad credentials were provided.";
break;
case ConnectFailure:
case UnreachableHost:
case UnsupportedConnection:
errMsg += "The update site appears to be unreachable.";
break;
case Interrupted:
errMsg += "The retrival of the catalog file has been interrupted.";
break;
case InvalidResource:
errMsg += "The remote catalog file, releaseInfo.txt, does not appear to be valid.";
break;
default:
errMsg += "An undefined error occurred while retrieving the remote catalog file.";
break;
}
}
finally
{
IoUtil.forceClose(inStream);
IoUtil.forceClose(bufReader);
}
// See if we are in a valid state
if (errMsg != null)
; // Nothing to do, as an earlier error has occured
else if (fullList.size() == 0)
errMsg = "The update URL appears to be invalid.";
else if (fullList.size() == 1)
errMsg = "There are no updates of " + appName + ". Only one release has been made.";
// Bail if there were issues
if (errMsg != null)
{
aTask.infoAppendln(errMsg);
return null;
}
return fullList;
}
/**
* Returns the list of files (relative to destPath) that are needed for the specified release.
*/
public static List<File> getFileListForRelease(Task aTask, URL aUpdateUrl, Release aRelease, File destPath, Credential aCredential)
{
List<File> fullList;
URL md5sumUrl;
URLConnection connection;
InputStream inStream;
BufferedReader bufReader;
String errMsg;
errMsg = null;
fullList = Lists.newArrayList();
md5sumUrl = IoUtil.createURL(aUpdateUrl.toString() + "/" + aRelease.getName() + "/" + aRelease.getVersion() + "/delta/md5sum.txt");
connection = null;
inStream = null;
bufReader = null;
try
{
String[] tokens;
String strLine, filename;
// Read the contents of the file
connection = md5sumUrl.openConnection();
inStream = NetUtil.getInputStream(connection, aCredential);
bufReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(inStream)));
// Read the lines
while (true)
{
strLine = bufReader.readLine();
// Bail once we are done
if (strLine == null)
break;
tokens = strLine.split(",");
if (strLine.isEmpty() == true || strLine.startsWith("#") == true)
; // Nothing to do
else if (tokens.length != 2)
aTask.infoAppendln("Unreconized line: " + strLine);
else
// if (tokens.length == 2)
{
filename = tokens[1];
fullList.add(new File(destPath, filename));
}
}
}
catch (IOException aExp)
{
Result aResult;
aExp.printStackTrace();
errMsg = "The update site, " + aUpdateUrl + ", is not available.\n\t";
aResult = NetUtil.getResult(aExp, connection);
switch (aResult)
{
case BadCredentials:
errMsg += "The update site is password protected and bad credentials were provided.";
break;
case ConnectFailure:
case UnreachableHost:
case UnsupportedConnection:
errMsg += "The update site appears to be unreachable.";
break;
case Interrupted:
errMsg += "The retrival of the md5sum file has been interrupted.";
break;
case InvalidResource:
errMsg += "The remote md5sum file, md5sumInfo.txt, does not appear to be valid.";
break;
default:
errMsg += "An undefined error occurred while retrieving the remote md5sum file.";
break;
}
}
finally
{
IoUtil.forceClose(inStream);
IoUtil.forceClose(bufReader);
}
// See if we are in a valid state
if (errMsg != null)
; // Nothing to do, as an earlier error has occured
else if (fullList.size() == 0)
errMsg = "The md5sum URL appears to be invalid.";
// Bail if there were issues
if (errMsg != null)
{
aTask.infoAppendln(errMsg);
return null;
}
return fullList;
}
}

View File

@@ -0,0 +1,7 @@
package distMaker;
public enum LookUp
{
VersionName,
DeployTime,
}

View File

@@ -0,0 +1,76 @@
package distMaker;
import glum.database.QueryItem;
public class Release implements Comparable<Release>, QueryItem<LookUp>
{
private String appName;
private String verName;
private long deployTime;
public Release(String aAppName, String aVerName, long aDeployTime)
{
appName = aAppName;
verName = aVerName;
deployTime = aDeployTime;
}
/**
* Returns the formal name of the application
*/
public String getName()
{
return appName;
}
/**
* Returns the version name corresponding to this distribution
*/
public String getVersion()
{
return verName;
}
/**
* Returns the time at which this version was deployed (made available to the public/customer)
*/
public long getDeployTime()
{
return deployTime;
}
@Override
public int compareTo(Release o)
{
if (deployTime < o.deployTime)
return -1;
else if (deployTime > o.deployTime)
return 1;
return 0;
}
@Override
public Object getValue(LookUp aEnum)
{
switch (aEnum)
{
case DeployTime:
return deployTime;
case VersionName:
return verName;
default:
return null;
}
}
@Override
public void setValue(LookUp aEnum, Object aObj)
{
throw new RuntimeException("Unsupported operation");
}
}