/** * Copyright 2012-2013 Maciej Jaworski, Mariusz Kapcia, Paweł Kędzia, Mateusz Kubuszok * * <p>Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at</p> * * <p>http://www.apache.org/licenses/LICENSE-2.0</p> * * <p>Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.</p> */ package com.autoupdater.client.installation.runnable; import static com.autoupdater.client.installation.EInstallationStatus.*; import static com.autoupdater.client.models.EUpdateStatus.*; import java.io.IOException; import java.util.List; import java.util.SortedSet; import net.jsdpu.process.executors.ExecutionQueueReader; import net.jsdpu.process.executors.InvalidCommandException; import net.jsdpu.process.killers.ProcessKillerException; import com.autoupdater.client.environment.ClientEnvironmentException; import com.autoupdater.client.environment.EnvironmentData; import com.autoupdater.client.environment.ProgramSettingsNotFoundException; import com.autoupdater.client.installation.EInstallationStatus; import com.autoupdater.client.installation.InstallationServiceMessage; import com.autoupdater.client.models.Update; import com.autoupdater.client.utils.executions.RunnableWithErrors; import com.autoupdater.client.utils.services.ObservableService; /** * Runnable responsible for calling installer and obtaining results of its * execution. * * <p> * Used by InstallationAggregatedService. * </p> * * @see com.autoupdater.client.installation.aggregated.services.AggregatedInstallationService */ public class InstallationRunnable extends ObservableService<InstallationServiceMessage> implements RunnableWithErrors { private final EnvironmentData environmentData; private final CommandGenerationHelper commandGenerationHelper; private final ProcessShutdownHelper processHelper; private final SortedSet<Update> updates; private EInstallationStatus state = PREPARING_INSTALLATION; private List<String[]> updateExecutionCommands; private ExecutionQueueReader reader; private Throwable thrownException = null; /** * Creates runnable that will attempt to install required Updates. * * @param environmentData * environmentData instance * @param updates * Updates to install */ public InstallationRunnable(EnvironmentData environmentData, SortedSet<Update> updates) { this.environmentData = environmentData; this.commandGenerationHelper = new CommandGenerationHelper(environmentData); this.processHelper = new ProcessShutdownHelper(environmentData); this.updates = updates; } /** * Starts installation process. */ @Override public void run() { state = PREPARING_INSTALLATION; try { clearPossibleFailureMessages(); killOpenedUpdatedPrograms(); prepareUpdateCommands(); installUpdates(); } catch (ProgramSettingsNotFoundException | IOException | InterruptedException | ProcessKillerException | InvalidCommandException e) { setThrownException(e); reportError(e.getMessage()); } finally { updateEnvironmentDataAndPackagesIfNeccessary(); } } /** * Returns current state of installation. * * @return current state of installation. */ public EInstallationStatus getState() { return state; } @Override public Throwable getThrownException() { return thrownException; } @Override public void setThrownException(Throwable throwable) { thrownException = throwable; } @Override public void throwExceptionIfErrorOccured() throws Throwable { if (thrownException != null) throw thrownException; } /** * For each Update clears failure messages if any is set. */ private void clearPossibleFailureMessages() { for (Update update : updates) update.setStatusMessage(null); } /** * Kills open updated programs. * * @throws ProgramSettingsNotFoundException * thrown if ProgramSettings for some Update isn't found * @throws ProcessKillerException * thrown if ProcessKiller cannot kill some program * @throws IOException * thrown when error occurs in system dependent process * @throws InterruptedException * thrown when thread is interrupted during waiting for system * dependent process to finish */ private void killOpenedUpdatedPrograms() throws ProgramSettingsNotFoundException, ProcessKillerException, IOException, InterruptedException { reportChange("Shutting down applications before update...", EInstallationStatus.KILLING_UPDATED_APPLICATIONS); processHelper.killProcesses(updates); reportChange("Closed all applications", EInstallationStatus.KILLED_UPDATED_APPLICATIONS); } /** * Prepares commands used during update. * * @throws ProgramSettingsNotFoundException * thrown if ProgramSettings for some Update isn't found * @throws InvalidCommandException * thrown if command passed into ProcessExecutor is invalid * @throws IOException * thrown when error occurs in system dependent process */ private void prepareUpdateCommands() throws ProgramSettingsNotFoundException, InvalidCommandException, IOException { reportChange("Preparing installation commands...", INSTALLING_UPDATES); updateExecutionCommands = commandGenerationHelper.getUpdateExecutionCommands(updates); reader = environmentData.getSystem().getProcessExecutor() .executeRoot(updateExecutionCommands); } /** * Installs updates. */ private void installUpdates() { reportChange("Installing updates...", INSTALLING_UPDATES); new InstallersOutputParser(environmentData).parseInstallersOutput(updates, reader); } /** * Updates information inside Packages (changes version number if * installation finished successfully) and saves EnvironmentData if any of * version numbers changed. */ private void updateEnvironmentDataAndPackagesIfNeccessary() { boolean errorOccured = false; boolean someUpdateInstalled = false; for (Update update : updates) if (update != null) { if (update.getStatus() == INSTALLED) someUpdateInstalled = true; else if (update.getStatus().isInstallationFailed()) { update.setStatusMessage("Uknown Installer failure"); update.setStatus(FAILED); errorOccured = true; } } if (errorOccured) reportError("Not all updates were successfully installed"); else reportChange("Updates installed successfully", INSTALLATION_SUCCEEDED); if (someUpdateInstalled) try { environmentData.save(); } catch (ClientEnvironmentException | IOException e) { reportError("Couldn't save information about newly installed updates"); } } /** * Reports change together with new installation status. * * @param message * message to pass * @param state * new installation status */ private void reportChange(String message, EInstallationStatus state) { this.state = state; hasChanged(); notifyObservers(new InstallationServiceMessage(message)); } /** * Reports error in installation process. * * @param message * message to pass */ private void reportError(String message) { state = INSTALLATION_FAILED; hasChanged(); notifyObservers(new InstallationServiceMessage(message, true)); } }