package esmska.update;
import esmska.Context;
import esmska.data.Icons;
import esmska.data.Log;
import esmska.data.Queue;
import esmska.data.Queue.Events;
import esmska.data.SMS;
import esmska.data.Tuple3;
import esmska.data.event.ValuedEvent;
import esmska.data.event.ValuedListener;
import esmska.utils.L10N;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;
/** Class for managing new gateway updates installation.
*/
public class UpdateInstaller {
private static UpdateInstaller instance;
private static final Logger logger = Logger.getLogger(UpdateInstaller.class.getName());
private static final ResourceBundle l10n = L10N.l10nBundle;
private static final Log log = Log.getInstance();
private final QueueListener queueListener = new QueueListener();
private ArrayList<Tuple3<GatewayUpdateInfo, String, byte[]>> updateFiles = new ArrayList<Tuple3<GatewayUpdateInfo, String, byte[]>>();
/** Disabled constructor */
private UpdateInstaller() {
}
/* Get program instance */
public static UpdateInstaller getInstance() {
if (instance == null) {
instance = new UpdateInstaller();
}
return instance;
}
/** Install all available gateway updates.
* This will ask UpdateChecker instance for the list of gateway updates.
* The update information must already be retrieved by it.
* The gateways will be downloaded and installed in a new thread.
*/
public void installNewGateways() {
logger.fine("Starting up update installation process...");
Set<GatewayUpdateInfo> updates = UpdateChecker.getInstance().getGatewayUpdates(true);
//download updates
final Downloader dl = new Downloader(updates);
dl.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state") &&
evt.getNewValue() == SwingWorker.StateValue.DONE) {
// everything downloaded, retrieve downloaded data
assert dl.isDone() : "Downloader should have finished";
boolean dlOk = dl.isFinishedOk();
try {
if (dl.get() != null) {
updateFiles = dl.get();
} else {
dlOk = false;
}
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not retrieve downloaded gateway information", ex);
dlOk = false;
}
if (!dlOk) {
log.addRecord(new Log.Record(l10n.getString("Update.downloadFailed"), null, Icons.STATUS_ERROR));
}
if (!updateFiles.isEmpty()) {
// let's install updates when queue is quiet (no sms being sent)
Queue.getInstance().addValuedListener(queueListener);
// simulate event to install updates immediately if possible
queueListener.eventOccured(null);
}
}
}
});
dl.execute();
}
/** Perform the gateway update installation (after all the data was downloaded) */
private void doInstallation() {
boolean installOk = true;
//save the data to harddisk
logger.log(Level.FINER, "Saving {0} updates to disk", updateFiles.size());
for (Tuple3<GatewayUpdateInfo, String, byte[]> script : updateFiles) {
try {
Context.persistenceManager.saveGateway(
script.get1().getFileName(), script.get2(), script.get3());
logger.log(Level.INFO, "Gateway updated: {0} ({1})",
new Object[]{script.get1().getName(), script.get1().getVersion()});
log.addRecord(new Log.Record(
MessageFormat.format(l10n.getString("Update.gwUpdated"), script.get1().getName()),
null, Icons.STATUS_UPDATE));
} catch (Exception ex) {
logger.log(Level.WARNING, "Could not save gateway", ex);
installOk = false;
}
}
//reload all gateways
logger.finer("Reloading gateways...");
try {
Context.persistenceManager.loadGateways();
Context.persistenceManager.loadGatewayProperties();
} catch (Exception ex) {
logger.log(Level.SEVERE, "Could not reload gateways", ex);
installOk = false;
}
//inform if something went wrong
if (!installOk) {
log.addRecord(new Log.Record(l10n.getString("Update.installFailed"), null, Icons.STATUS_ERROR));
}
}
/** Download all requested updates.
* Returns collection of [update info;script contents;icon]. */
private class Downloader extends SwingWorker<ArrayList<Tuple3<GatewayUpdateInfo,String,byte[]>>, Integer> {
private boolean finishedOk;
private Collection<GatewayUpdateInfo> infos;
private ArrayList<Tuple3<GatewayUpdateInfo,String,byte[]>> scripts = new ArrayList<Tuple3<GatewayUpdateInfo,String, byte[]>>();
int downloaded = 0;
/** Constructor.
* @param infos collection of infos for which to retrieve files
*/
public Downloader(Collection<GatewayUpdateInfo> infos) {
this.infos = infos;
}
@Override
protected ArrayList<Tuple3<GatewayUpdateInfo,String,byte[]>> doInBackground() throws Exception {
logger.log(Level.FINER, "Downloading {0} updates", infos.size());
for (GatewayUpdateInfo info : infos) {
try {
logger.log(Level.FINER, "Downloading gateway update: {0} {1}",
new Object[]{info.getName(), info.getVersion()});
//download script
HttpDownloader dl = new HttpDownloader(info.getDownloadUrl().toString(), false);
dl.execute();
String script = (String) dl.get();
if (!dl.isFinishedOk()) {
//if script is not downloaded, don't download the icon
continue;
}
byte[] icon = null;
if (info.getIconUrl() != null) {
//download icon
dl = new HttpDownloader(info.getIconUrl().toString(), true);
dl.execute();
icon = (byte[]) dl.get();
if (!dl.isFinishedOk()) {
continue;
}
}
Tuple3<GatewayUpdateInfo,String,byte[]> tuple =
new Tuple3<GatewayUpdateInfo,String,byte[]>(info, script, icon);
scripts.add(tuple);
} finally {
//publish how many updates are downloaded up to now
publish(++downloaded);
}
if (isCancelled()) {
logger.fine("Updates downloading cancelled");
finishedOk = false;
return null;
}
}
finishedOk = (infos.size() == scripts.size());
if (!finishedOk) {
logger.warning("Could not download all gateway updates");
}
return scripts;
}
/** Returns whether all requested updates were downloaded ok. */
public boolean isFinishedOk() {
return finishedOk;
}
}
/** Listen for queue changes and install new gateways when no SMS is being sent */
private class QueueListener implements ValuedListener<Queue.Events, SMS> {
@Override
public void eventOccured(ValuedEvent<Events, SMS> e) {
Queue queue = Queue.getInstance();
if (!queue.getAllWithStatus(SMS.Status.SENDING).isEmpty()) {
logger.finer("Messages are still being sent, postponing gateway update");
return;
}
// queue is quiet now, no SMS is being sent, let's update
doInstallation();
// and this listener is needed no more
queue.removeValuedListener(this);
}
}
}