boolean (String[] patches, Path dest, UpdaterUI ui, boolean backup) { try { List<File> patchFiles = new ArrayList<>(patches.length); File destDir = dest.toFile(); File backupDir = null; String jarName = null; try { LOG.info("Extracting patch files..."); ui.startProcess(UpdaterUI.message("extracting.patch.files")); for (int i = 0; i < patches.length; i++) { jarName = new File(patches[i]).getName(); LOG.info("Unpacking " + jarName); ui.setProgress(100 * i / patches.length); File patchFile = Utils.getTempFile("patch" + i); patchFiles.add(patchFile); try (ZipFile zipFile = new ZipFile(patches[i]); InputStream in = Utils.getEntryInputStream(zipFile, PATCH_FILE_NAME); OutputStream out = new BufferedOutputStream(Files.newOutputStream(patchFile.toPath()))) { Utils.copyStream(in, out); } ui.checkCancelled(); } jarName = null; if (backup) { backupDir = Utils.getTempFile("backup"); if (!backupDir.mkdir()) throw new IOException("Cannot create a backup directory: " + backupDir); LOG.info("Backing up files..."); ui.startProcess(UpdaterUI.message("backing.up.files")); ui.setProgressIndeterminate(); Utils.copyDirectory(destDir.toPath(), backupDir.toPath()); } } catch (OperationCancelledException e) { LOG.log(Level.WARNING, "cancelled", e); return false; } catch (Throwable t) { LOG.log(Level.SEVERE, "prepare failed", t); String message = (jarName != null ? UpdaterUI.message("extract.failed", jarName, t.getClass().getSimpleName(), t.getMessage()) : UpdaterUI.message("prepare.failed", t.getClass().getSimpleName(), t.getMessage())) + "\n\n" + ui.bold(UpdaterUI.message("nothing.changed.retry")) + "\n\n" + UpdaterUI.message("see.log", logPath); ui.showError(message); return false; } boolean completed = false, needRestore = false; Throwable error = null; try { for (File patchFile : patchFiles) { PatchFileCreator.PreparationResult preparationResult = PatchFileCreator.prepareAndValidate(patchFile, destDir, ui); Map<String, ValidationResult.Option> resolutions = askForResolutions(preparationResult.validationResults, ui); PatchFileCreator.ApplicationResult applicationResult = PatchFileCreator.apply(preparationResult, resolutions, null, ui); needRestore |= !applicationResult.appliedActions.isEmpty(); if (!applicationResult.applied) { error = applicationResult.error; throw new OperationCancelledException(); } } completed = true; } catch (OperationCancelledException e) { LOG.log(Level.WARNING, "cancelled", e); } catch (Throwable t) { LOG.log(Level.SEVERE, "batch failed", t); error = t; } if (error != null) { String message = UpdaterUI.message("apply.failed", error.getClass().getSimpleName(), error.getMessage()) + "\n\n"; if (!needRestore) { message += ui.bold(UpdaterUI.message("nothing.changed.retry")); } else if (backupDir == null) { message += ui.bold(UpdaterUI.message("corrupted.reinstall")); } else { message += ui.bold(UpdaterUI.message("corrupted.reverting")); } message += "\n\n" + UpdaterUI.message("see.log", logPath); ui.showError(message); } ui.setDescription(""); if (!completed && needRestore && backupDir != null) { LOG.info("Reverting..."); ui.startProcess(UpdaterUI.message("reverting")); ui.setProgressIndeterminate(); try { Utils.delete(destDir); try { LOG.info("move: " + backupDir + " -> " + destDir); Files.move(backupDir.toPath(), destDir.toPath()); } catch (IOException e) { LOG.log(Level.SEVERE, "move failed", e); Utils.delete(destDir); Utils.copyDirectory(backupDir.toPath(), destDir.toPath()); } } catch (Throwable t) { LOG.log(Level.SEVERE, "revert failed", t); String message = UpdaterUI.message("revert.failed", t.getClass().getSimpleName(), t.getMessage()) + "\n\n" + ui.bold(UpdaterUI.message("corrupted.reinstall")) + "\n\n" + UpdaterUI.message("see.log", logPath); ui.showError(message); } } return completed; } finally { try { cleanup(ui); refreshApplicationIcon(dest.toString()); } catch (Throwable t) { LOG.log(Level.WARNING, "cleanup failed", t); } } }