I'm writing a program for several clients of mine to use from time to time, but it's still quite simple and I plan to add features as they use it. They know of this, and are fine with the plan, as it allows them to use the program earlier, even though it's more basic than the final version will be.
My problem is that, to update, I'd be downloading a newer version of the application (which is run as a JAR file), from a github repository, replacing the program as it's running, and then restarting it, as the new version. This design would also allow me to make changes to the updater if necessary. Is replacing the program's executable JAR while it's running possible, or should I take different approaches? If different approaches would work better, what would you guys recommend?
Also, I'm having trouble with my download class timing out often when it tries to download a file. Usually, it's only a problem the first time, as it typically works after the first try. However, it would be very inconvenient for a user to have to run the updater twice in order to even start the download. I've added the code for my Download class below.
class Download extends Observable implements Runnable {
// Max size of download buffer.
private static final int MAX_BUFFER_SIZE = 1024;
// These are the status codes.
public static final int DOWNLOADING = 0;
public static final int PAUSED = 1;
public static final int COMPLETE = 2;
public static final int CANCELLED = 3;
public static final int ERROR = 4;
private URL url; // download URL
private int size; // size of download in bytes
private int downloaded; // number of bytes downloaded
private int status; // current status of download
// Constructor for Download.
public Download(URL url) {
this.url = url;
size = -1;
downloaded = 0;
status = DOWNLOADING;
// Begin the download.
download();
}
// Get this download's URL.
public String getUrl() {
return url.toString();
}
// Get this download's final size.
public int getSize() {
return size;
}
// Get the download's current size
public int getDownloaded() {
return downloaded;
}
// Get this download's progress.
public float getProgress() {
return ((float) downloaded / size) * 100;
}
// Get this download's status.
public int getStatus() {
return status;
}
// Pause this download.
public void pause() {
status = PAUSED;
stateChanged();
}
// Resume this download.
public void resume() {
status = DOWNLOADING;
stateChanged();
download();
}
// Cancel this download.
public void cancel() {
status = CANCELLED;
stateChanged();
}
// Mark this download as having an error.
private void error() {
status = ERROR;
stateChanged();
}
// Start or resume downloading.
private void download() {
Thread thread = new Thread(this);
thread.start();
}
// Get file name portion of URL.
private String getFileName(URL url) {
String fileName = url.getFile();
return fileName.substring(fileName.lastIndexOf('/') + 1);
}
// Download file.
public void run() {
RandomAccessFile file = null;
InputStream stream = null;
try {
// Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
// Connect to server.
connection.connect();
// Make sure response code is in the 200 range.
if (connection.getResponseCode() / 100 != 2) {
error();
}
// Check for valid content length.
int contentLength = connection.getContentLength();
if (contentLength < 1) {
error();
}
/*
* Set the size for this download if it hasn't been already
* set.
*/
if (size == -1) {
size = contentLength;
stateChanged();
}
// Open file and seek to the end of it.
file = new RandomAccessFile(getFileName(url), "rw");
file.seek(downloaded);
stream = connection.getInputStream();
while (status == DOWNLOADING) {
/*
* Size buffer according to how much of the file is left
* to download.
*/
byte buffer[];
if (size - downloaded > MAX_BUFFER_SIZE) {
buffer = new byte[MAX_BUFFER_SIZE];
} else {
buffer = new byte[size - downloaded];
}
// Read from server into buffer.
int read = stream.read(buffer);
if (read == -1)
break;
// Write buffer to file.
file.write(buffer, 0, read);
downloaded += read;
stateChanged();
}
/*
* Change status to complete if this point was reached
* because downloading has finished.
*/
if (status == DOWNLOADING) {
status = COMPLETE;
stateChanged();
}
} catch (Exception e) {
error();
} finally {
// Close file.
if (file != null) {
try {
file.close();
} catch (Exception e) {
}
}
// Close connection to server.
if (stream != null) {
try {
stream.close();
} catch (Exception e) {
}
}
}
}
// Notify observers that this download's status has changed.
private void stateChanged() {
setChanged();
notifyObservers();
}
}
Any solutions to either of these problems are completely welcome. Thanks!