/** * 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.models.EUpdateStatus.*; import static com.autoupdater.commons.error.codes.EErrorCode.SUCCESS; import static com.google.common.base.Throwables.propagate; import static java.util.regex.Pattern.compile; import java.io.IOException; import java.util.SortedSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import net.jsdpu.process.executors.ExecutionQueueReader; import net.jsdpu.process.executors.InvalidCommandException; import com.autoupdater.client.environment.ClientEnvironmentException; import com.autoupdater.client.environment.EnvironmentData; import com.autoupdater.client.models.EUpdateStatus; import com.autoupdater.client.models.Update; import com.autoupdater.client.utils.enums.Enums; import com.autoupdater.commons.error.codes.EErrorCode; import com.autoupdater.commons.messages.EInstallerMessage; import com.google.common.base.Objects; /** * Helper responsible for reading data from installer's execution log, and * obtaining information about e.g. success, failure, backup completion. * * <p> * Used by InstallationRunnable. * </p> * * @see com.autoupdater.client.installation.runnable.InstallationRunnable */ class InstallersOutputParser { private static Pattern INFO_PATTERN = compile("^\\[info\\] ([^:]+): (.+)$"); private static Pattern ERROR_PATTERN = compile("^\\[error\\] ([^:]+): (.+)$"); private final EnvironmentData environmentData; /** * Initializes output parser. */ InstallersOutputParser() { environmentData = null; } /** * Initializes output parser. * * @param environmentData * EnvironmentData that can be used for saving current state of * installed Update */ InstallersOutputParser(EnvironmentData environmentData) { this.environmentData = environmentData; } /** * Parses information from installer's log into information about current * state of installation. * * @param updates * set of updates * @param reader * source of installer's log output */ public void parseInstallersOutput(SortedSet<Update> updates, ExecutionQueueReader reader) { String result; while (true) { try { if (isInstallationFinished(updates)) { reader.killCurrentProcess(); return; } if ((result = reader.getNextOutput()) == null) return; Matcher infoMatcher = INFO_PATTERN.matcher(result); if (infoMatcher.find()) { parseInfoResult(updates, infoMatcher); continue; } Matcher errorMatcher = ERROR_PATTERN.matcher(result); if (errorMatcher.find()) { parseErrorResult(updates, errorMatcher); continue; } } catch (InvalidCommandException e) { continue; } } } /** * Parses log's entry containing INFO message. * * @param updates * set of updates * @param infoMatcher * matcher that found INFO */ private void parseInfoResult(SortedSet<Update> updates, Matcher infoMatcher) { EInstallerMessage installerMessage; String updateID = infoMatcher.group(1); String message = infoMatcher.group(2); Update update = findUpdateByID(updates, updateID); EUpdateStatus updateStatus; if (update != null) try { if ((installerMessage = Enums.parseMessage(EInstallerMessage.class, message)) != null && (updateStatus = Enums.parseField(EUpdateStatus.class, "installerMessage", installerMessage)) != null) update.setStatus(updateStatus); else if (Enums.parseField(EErrorCode.class, "description", message) == SUCCESS) update.setStatus(INSTALLED); } catch (NoSuchFieldException e) { propagate(e); } } /** * Parses log's entry containing ERROR message. * * @param updates * set of updates * @param errorMatcher * matcher that found ERROR */ private void parseErrorResult(SortedSet<Update> updates, Matcher errorMatcher) { EInstallerMessage installerMessage; String updateID = errorMatcher.group(1); String message = errorMatcher.group(2); Update update = findUpdateByID(updates, updateID); EUpdateStatus updateStatus; if (update != null) { try { if ((installerMessage = Enums.parseMessage(EInstallerMessage.class, message)) != null && (updateStatus = Enums.parseField(EUpdateStatus.class, "installerMessage", installerMessage)) != null) { update.setStatus(updateStatus); if (updateStatus == INSTALLED && environmentData != null) try { environmentData.save(); } catch (ClientEnvironmentException | IOException e) { } } else if (update.getStatus().isIntendedToBeChanged()) { update.setStatusMessage(message); update.setStatus(FAILED); } } catch (NoSuchFieldException e) { throw new RuntimeException("Wrong installer message field name"); } } } /** * Whether there is any unfinished update attempt. * * @param updates * updates to check * @return whether update is finished */ private boolean isInstallationFinished(SortedSet<Update> updates) { for (Update update : updates) if (update.getStatus() != NOT_SELECTED && !update.getStatus().isUpdateAttemptFinished()) return false; return true; } /** * Finds Update in set by its ID. * * @param updates * set of updates * @param updateID * searched Update's ID * @return found update, null otherwise */ private static Update findUpdateByID(SortedSet<Update> updates, String updateID) { for (Update update : updates) if (Objects.equal(update.getUniqueIdentifer(), updateID)) return update; return null; } }