// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: Installer.java,v 1.20 2008/02/28 15:59:50 spyromus Exp $
//
package com.salas.bb.installation;
import com.jgoodies.binding.value.ValueHolder;
import com.jgoodies.uif.application.Application;
import com.salas.bb.core.GlobalModel;
import com.salas.bb.core.actions.guide.ImportGuidesAction;
import com.salas.bb.installation.wizard.InstallationSettings;
import com.salas.bb.installation.wizard.InstallationWizard;
import com.salas.bb.service.ServicePreferences;
import com.salas.bb.service.sync.SyncIn;
import com.salas.bb.utils.CommonUtils;
import com.salas.bb.utils.ResourceID;
import com.salas.bb.utils.StringUtils;
import com.salas.bb.utils.feedscollections.CollectionItem;
import com.salas.bb.utils.i18n.Strings;
import com.salas.bb.utils.uif.UifUtilities;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.prefs.Preferences;
/**
* Installer of new version. When application detects that this is the first run it
* calls installer to perform user-friendly installation. Installer in turn uses
* Wizard-like dialog box to gather required information about user's preferences and
* then runs the installation.
*/
public class Installer extends Thread
{
private static final String MSG_INST_PROGRESS_DIALOG_ERROR = "Failed to create installation progress dialog.";
/** Path to fresh database script. */
private static final String FRESH_SCRIPT_RESOURCE = "resources/blogbridge.script";
private static final String FRESH_PROPERTIES_RESOURCE = "resources/blogbridge.properties";
private InstallationProgressDialog progressDialog;
private InstallationSettings installationSettings;
private GlobalModel model;
private String workingPath;
private final Object lock = new Object();
/**
* Performs installation by displaying wizard dialog and then running main process.
*
* @param aWorkingPath path to working directory.
*
* @return prepared global model. Null if installation cancelled for some reason.
*/
public GlobalModel perform(String aWorkingPath)
{
workingPath = aWorkingPath;
InstallationSettings settings = getInstallationSettings();
// user entered all necessary information
if (settings != null) install(settings);
return model;
}
/**
* Displays wizard-like dialog box with several pages to get information from user.
*
* @return installation settings or <code>null</code> if user cancelled.
*/
private InstallationSettings getInstallationSettings()
{
final ValueHolder settingsHolder = new ValueHolder();
UifUtilities.invokeAndWait(new Runnable()
{
public void run()
{
InstallationWizard wizard = new InstallationWizard();
InstallationSettings settings = wizard.openDialog();
settingsHolder.setValue(settings);
}
}, "Failed to get installation settings.", Level.SEVERE);
return (InstallationSettings)settingsHolder.getValue();
}
/**
* Perform installation using provided settings.
*
* @param aSettings settings to use in installation.
*
* @return <code>true</code> if installation was successful.
*/
private boolean install(InstallationSettings aSettings)
{
if (aSettings == null) return false;
// Setup progress dialog
final String[] steps = enumerateInstallationSteps(aSettings);
UifUtilities.invokeAndWait(new Runnable()
{
public void run()
{
progressDialog = new InstallationProgressDialog(steps, lock);
}
}, MSG_INST_PROGRESS_DIALOG_ERROR, Level.SEVERE);
boolean result = true;
installationSettings = aSettings;
synchronized (lock)
{
start();
try
{
lock.wait();
} catch (InterruptedException e)
{
// Job exited or canceled
}
if (this.isAlive()) this.interrupt();
}
return result;
}
/**
* Enumerates all steps necessary to finish installation.
*
* @param settings settings to analyze.
*
* @return list of steps to do.
*/
private static String[] enumerateInstallationSteps(InstallationSettings settings)
{
String serviceAccountEmail = settings.getServiceAccountEmail();
String serviceAccountPassword = settings.getServiceAccountPassword();
ArrayList<String> steps = new ArrayList<String>();
steps.add(Strings.message("installation.step.creating.empty.database"));
if (StringUtils.isNotEmpty(serviceAccountEmail) &&
StringUtils.isNotEmpty(serviceAccountPassword) &&
!settings.isServiceAccountExists())
{
steps.add(Strings.message("installation.step.initializing.service.account"));
}
if (settings.getDataInitMode() == InstallationSettings.DATA_INIT_POINTS)
{
steps.add(Strings.message("installation.step.loading.guides.from.selected.starting.points"));
} else if (settings.getDataInitMode() == InstallationSettings.DATA_INIT_SERVICE)
{
steps.add(Strings.message("installation.step.synchronizing.with.service.account"));
} else
{
steps.add(Strings.message("installation.step.creating.clean.list.of.guides"));
}
steps.add(Strings.message("installation.step.applying.settings.locally"));
return steps.toArray(new String[steps.size()]);
}
/**
* Performs installation.
*/
public void run()
{
synchronized (lock)
{
progressDialog.open();
}
// Create empty model and read in all preferences we have
model = new GlobalModel(null, false);
model.restorePreferences(Application.getUserPreferences());
// Create empty database
progressDialog.succeedStep();
cleanDatabase(workingPath);
if (installationSettings.isUseAccountSelected())
{
// Initialize account
initializeServiceAccount();
}
progressDialog.succeedStep();
// Import data
if (importData())
{
progressDialog.succeedStep();
} else
{
progressDialog.failedStep();
}
// Apply local settings
applyLocalSettings();
progressDialog.succeedStep();
synchronized (lock)
{
progressDialog.finish();
}
}
private void applyLocalSettings()
{
// Set acccepted license
Preferences prefs = Application.getUserPreferences();
String version = Application.getDescription().getVersion();
prefs.put(ResourceID.VERSION_ACCEPTED_LICENSE, version);
}
/**
* Initializes main properties of the service account.
*/
private void initializeServiceAccount()
{
ServicePreferences sp = model.getServicePreferences();
sp.setRegDate(new Date());
sp.setEmail(installationSettings.getServiceAccountEmail());
sp.setPassword(installationSettings.getServiceAccountPassword());
// Since we copy everything from the installation model to the working model in
// OpenDBInBackground.run(), we don't need this any more
// Preferences prefs = Application.getUserPreferences();
// prefs.putLong(ServicePreferences.KEY_REG_DATE, System.currentTimeMillis());
// prefs.put(ServicePreferences.KEY_EMAIL, installationSettings.getServiceAccountEmail());
// prefs.put(ServicePreferences.KEY_PASSWORD, installationSettings.getServiceAccountPassword());
}
private boolean importData()
{
boolean result = true;
switch (installationSettings.getDataInitMode())
{
case InstallationSettings.DATA_INIT_POINTS:
importDataFromPoints();
break;
case InstallationSettings.DATA_INIT_SERVICE:
result = importDataFromService();
break;
default:
// clean installation
break;
}
return result;
}
private boolean importDataFromService()
{
String email = installationSettings.getServiceAccountEmail();
String password = installationSettings.getServiceAccountPassword();
SyncIn syncIn = new SyncIn(model, false);
return !syncIn.doSynchronization(null, false, email, password).hasFailed();
}
private void importDataFromPoints()
{
CollectionItem[] points = installationSettings.getSelectedStartingPoints();
for (CollectionItem point : points)
{
ImportGuidesAction.importAndAppend(model, point.getXmlURL());
}
}
/**
* Replaces database with a fresh copy from resources.
*
* @param workingPath working folder path.
*/
public static void cleanDatabase(String workingPath)
{
CommonUtils.copyResourceToFile(FRESH_SCRIPT_RESOURCE, workingPath + "blogbridge.script");
CommonUtils.copyResourceToFile(FRESH_PROPERTIES_RESOURCE, workingPath + "blogbridge.properties");
}
}