From 49aa9ac936e07937b7e08b17759f904e2f10d3fb Mon Sep 17 00:00:00 2001
From: kenneth_suter <kenneth_suter@localhost>
Date: Thu, 20 Sep 2007 19:31:21 +0000
Subject: [PATCH] Fix for issue 2227 in which unpredictable behavior results from an upgrade or reversion process replacing the upgrade script while the upgrade process is running on Windows. This code will compare the running version of the script with the new version to see whether or not the script actually needs replacing. If so the script is copied with an extension NEW. When the script starts it checks for the existence of upgrade.bat.NEW and if exists prints a message informing the user that they must replace the old version of the script with the new version before continuing.
---
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java | 51 ++++++--
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java | 27 ++++
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/quicksetup/util/FileManagerTest.java | 33 +++++
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Stage.java | 127 +++++++++++++++++++++
opendj-sdk/opends/src/messages/messages/quicksetup.properties | 3
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Installation.java | 10 +
opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Reverter.java | 60 ++++++---
opendj-sdk/opends/resource/upgrade.bat | 10 +
8 files changed, 288 insertions(+), 33 deletions(-)
diff --git a/opendj-sdk/opends/resource/upgrade.bat b/opendj-sdk/opends/resource/upgrade.bat
index 36908c5..a585001 100644
--- a/opendj-sdk/opends/resource/upgrade.bat
+++ b/opendj-sdk/opends/resource/upgrade.bat
@@ -30,6 +30,16 @@
set INSTANCE_ROOT=%DIR_HOME%
+:checkNewVersion
+if exist "upgrade.bat.NEW" goto newVersion
+goto checkJavaBin
+
+:newVersion
+echo A new version of this script was made available by the last upgrade
+echo operation. Delete this old version and rename file 'upgrade.bat.NEW'
+echo to 'upgrade.bat' before continuing.
+goto end
+
:checkJavaBin
if "%JAVA_BIN%" == "" goto noJavaBin
goto callExtractor
diff --git a/opendj-sdk/opends/src/messages/messages/quicksetup.properties b/opendj-sdk/opends/src/messages/messages/quicksetup.properties
index cda0998..9dac254 100644
--- a/opendj-sdk/opends/src/messages/messages/quicksetup.properties
+++ b/opendj-sdk/opends/src/messages/messages/quicksetup.properties
@@ -1188,3 +1188,6 @@
INFO_ZIP_FILES_DESCRIPTION=OpenDS Installation Package (.zip)
SEVERE_ERR_COULD_NOT_FIND_REPLICATIONID=Could not find a remote peer to \
initialize the contents of local base DN: %s.
+INFO_NEW_UPGRADE_SCRIPT_AVAILABLE=A new version of '%s' has been made \
+ available. After this operation you should delete this script and rename \
+ '%s' to '%1$s'.
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Installation.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Installation.java
index 3f3e0e2..742d070 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Installation.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/Installation.java
@@ -164,6 +164,16 @@
public static final String WINDOWS_UPGRADE_FILE_NAME = "upgrade.bat";
/**
+ * Newly upgraded Windows upgrade batch file name. When the upgrade
+ * batch file requires upgrade it is not done during execution of the
+ * upgrade utility itself since replacing a running script on Windows
+ * with a different version leads to unpredictable results. Instead
+ * this new name is used for the upgraded version and the user is
+ * expected to manually rename the file following the upgrade.
+ */
+ public static final String WINDOWS_UPGRADE_FILE_NAME_NEW = "upgrade.bat.NEW";
+
+ /**
* The UNIX start script file name.
*/
public static final String UNIX_START_FILE_NAME = "start-ds";
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Reverter.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Reverter.java
index 8c95ed4..8fb4214 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Reverter.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Reverter.java
@@ -46,6 +46,7 @@
import org.opends.quicksetup.HistoricalRecord;
import org.opends.quicksetup.UserInteraction;
import org.opends.quicksetup.CliUserInteraction;
+import static org.opends.quicksetup.Installation.*;
import org.opends.quicksetup.event.ProgressUpdateListener;
import org.opends.quicksetup.util.ProgressMessageFormatter;
import org.opends.quicksetup.util.Utils;
@@ -90,6 +91,7 @@
private BuildInformation archiveBuildInfo;
private boolean abort = false;
private boolean restartServer = false;
+ private Stage stage = null;
/**
* {@inheritDoc}
@@ -100,7 +102,7 @@
if (launcher instanceof UpgradeLauncher) {
ud = new ReverterUserData();
UpgradeLauncher rl = (UpgradeLauncher)launcher;
- File filesDir = null;
+ File filesDir;
if (rl.isInteractive()) {
if (rl.isNoPrompt()) {
StringBuilder sb = new StringBuilder()
@@ -219,7 +221,7 @@
if (historyDir.exists()) {
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
- return !Installation.HISTORY_LOG_FILE_NAME.equals(name);
+ return !HISTORY_LOG_FILE_NAME.equals(name);
}
};
String[] childNames = historyDir.list(filter);
@@ -233,7 +235,7 @@
Arrays.sort(childNames);
for (String childName : childNames) {
File b = new File(historyDir, childName);
- File d = new File(b, Installation.HISTORY_BACKUP_FILES_DIR_NAME);
+ File d = new File(b, HISTORY_BACKUP_FILES_DIR_NAME);
if (isReversionFilesDirectory(d)) {
ud.setReversionArchiveDirectory(d);
break;
@@ -330,7 +332,7 @@
if (f.getParentFile() != null &&
f.getParentFile().getParentFile() != null &&
new File(f.getParentFile().getParentFile(),
- Installation.LOCKS_PATH_RELATIVE).exists()) {
+ LOCKS_PATH_RELATIVE).exists()) {
installationPath = Utils.getPath(f.getParentFile().getParentFile());
} else {
installationPath = path;
@@ -546,7 +548,15 @@
FileFilter filter = new UpgradeFileFilter(root);
for (String fileName : root.list()) {
File f = new File(root, fileName);
- //fm.copyRecursively(f, filesBackupDirectory,
+
+ // Replacing a Windows bat file while it is running with a different
+ // version leads to unpredictable behavior so we make a special case
+ // here and during the upgrade components step.
+ if (Utils.isWindows() &&
+ fileName.equals(WINDOWS_UPGRADE_FILE_NAME)) {
+ continue;
+ }
+
fm.move(f, filesBackupDirectory, filter);
}
LOG.log(Level.INFO, "Finished backing up filesystem");
@@ -564,16 +574,10 @@
private void revertComponents() throws ApplicationException {
try {
- File stageDir = getReversionFilesDirectory();
+ Stage stage = getStage();
Installation installation = getInstallation();
File root = installation.getRootDirectory();
- FileManager fm = new FileManager();
- for (String fileName : stageDir.list()) {
- File f = new File(stageDir, fileName);
- fm.copyRecursively(f, root,
- new UpgradeFileFilter(stageDir),
- /*overwrite=*/true);
- }
+ stage.move(root);
// The bits should now be of the new version. Have
// the installation update the build information so
@@ -616,8 +620,8 @@
Set<String> cs = new HashSet<String>(Arrays.asList(children));
// TODO: more testing of file dir
- isFilesDir = cs.contains(Installation.CONFIG_PATH_RELATIVE) &&
- cs.contains(Installation.LIBRARIES_PATH_RELATIVE);
+ isFilesDir = cs.contains(CONFIG_PATH_RELATIVE) &&
+ cs.contains(LIBRARIES_PATH_RELATIVE);
}
return isFilesDir;
}
@@ -674,6 +678,17 @@
INFO_GENERAL_SEE_FOR_HISTORY.get(
Utils.getPath(getInstallation().getHistoryLogFile())))
.append(getLineBreak()).toMessage());
+
+ try {
+ Stage stage = getStage();
+ List<Message> stageMessages = stage.getMessages();
+ for (Message m : stageMessages) {
+ notifyListeners(m);
+ }
+ } catch (IOException e) {
+ LOG.log(Level.INFO, "failed to access stage", e);
+ }
+
} catch (ApplicationException e) {
notifyListeners(getFormattedDoneWithLineBreak());
LOG.log(Level.INFO, "Error cleaning up after reversion.", e);
@@ -828,7 +843,7 @@
{
if (tempBackupDir == null) {
tempBackupDir = new File(getInstallation().getTemporaryDirectory(),
- Installation.HISTORY_BACKUP_FILES_DIR_NAME);
+ HISTORY_BACKUP_FILES_DIR_NAME);
if (tempBackupDir.exists()) {
FileManager fm = new FileManager();
fm.deleteRecursively(tempBackupDir);
@@ -866,7 +881,7 @@
}
} else {
- Installation archiveInstall = null;
+ Installation archiveInstall;
try {
archiveInstall = getArchiveInstallation();
archiveBuildInfo = archiveInstall.getBuildInformation();
@@ -962,12 +977,19 @@
if (archiveDir != null) {
// Automatically append the 'filesDir' subdirectory if necessary
if (!archiveDir.getName().
- endsWith(Installation.HISTORY_BACKUP_FILES_DIR_NAME)) {
+ endsWith(HISTORY_BACKUP_FILES_DIR_NAME)) {
archiveDir = new File(archiveDir,
- Installation.HISTORY_BACKUP_FILES_DIR_NAME);
+ HISTORY_BACKUP_FILES_DIR_NAME);
}
}
return archiveDir;
}
+ private Stage getStage() throws IOException, ApplicationException {
+ if (this.stage == null) {
+ this.stage = new Stage(getReversionFilesDirectory());
+ }
+ return stage;
+ }
+
}
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Stage.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Stage.java
new file mode 100644
index 0000000..8a491c4
--- /dev/null
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Stage.java
@@ -0,0 +1,127 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
+ */
+
+package org.opends.quicksetup.upgrader;
+
+import static org.opends.quicksetup.Installation.*;
+import static org.opends.messages.QuickSetupMessages.*;
+import org.opends.messages.Message;
+import org.opends.quicksetup.ApplicationException;
+import org.opends.quicksetup.util.Utils;
+import org.opends.quicksetup.util.FileManager;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Collections;
+
+/**
+ * Represents a directory used to stage files for upgrade or reversion and
+ * is intended to handle special case operations.
+ */
+public class Stage {
+
+ private static final Logger LOG =
+ Logger.getLogger(Stage.class.getName());
+
+ private File root;
+
+ private FileManager fm;
+
+ private List<Message> messages = new LinkedList<Message>();
+
+ /**
+ * Creates a parameterized instance.
+ *
+ * @param root of the stage directory
+ */
+ public Stage(File root) {
+ this.root = root;
+ this.fm = new FileManager();
+ }
+
+ /**
+ * Moves the files in the staging area to a destination directory.
+ *
+ * @param destination for the staged files
+ * @throws ApplicationException if something goes wrong
+ */
+ public void move(File destination) throws ApplicationException {
+ UpgradeFileFilter ff = new UpgradeFileFilter(root);
+ for (String fileName : root.list()) {
+ File dest = new File(destination, fileName);
+ File src = getSourceForCopy(fileName, dest);
+ fm.copyRecursively(src, destination, ff, /*overwrite=*/true);
+ }
+ }
+
+ /**
+ * Returns a list of messages to be displayed to the user following
+ * this operation.
+ *
+ * @return list of messages
+ */
+ public List<Message> getMessages() {
+ return Collections.unmodifiableList(messages);
+ }
+
+ private File getSourceForCopy(String fileName, File dest) {
+
+ File src = new File(root, fileName);
+
+ // If this is the running script on Windows, see if it is actually
+ // different than the new version. If not don't do anything but if
+ // so copy it over under a different name so that the running script
+ // is not disturbed
+ if (WINDOWS_UPGRADE_FILE_NAME.equals(fileName) &&
+ Utils.isWindows()) {
+ try {
+ if (fm.filesDiffer(src, dest)) {
+ File renamedUpgradeBatFile = new File(root,
+ WINDOWS_UPGRADE_FILE_NAME_NEW);
+ if (src.renameTo(renamedUpgradeBatFile)) {
+ src = renamedUpgradeBatFile;
+ messages.add(INFO_NEW_UPGRADE_SCRIPT_AVAILABLE.get(
+ WINDOWS_UPGRADE_FILE_NAME,
+ WINDOWS_UPGRADE_FILE_NAME_NEW));
+ } else {
+ LOG.log(Level.INFO, "Failed to rename new version of " +
+ "'" + WINDOWS_UPGRADE_FILE_NAME + "' to '" +
+ WINDOWS_UPGRADE_FILE_NAME_NEW + "'");
+ }
+ }
+ } catch (IOException e) {
+ LOG.log(Level.INFO, "Exception comparing files " + e.getMessage());
+ }
+ }
+ return src;
+ }
+
+}
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
index d9d8a5a..0c39f3d 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/upgrader/Upgrader.java
@@ -148,11 +148,6 @@
private ApplicationException runWarning = null;
/**
- * Helps with CLI specific tasks.
- */
- private UpgraderCliHelper cliHelper = null;
-
- /**
* Directory where backup is kept in case the upgrade needs reversion.
*/
private File backupDirectory = null;
@@ -178,8 +173,16 @@
*/
private Installation stagedInstallation = null;
+ // TODO: remove dead code
private RemoteBuildManager remoteBuildManager = null;
+
+ /**
+ * Represents staged files for upgrade.
+ */
+ private Stage stage = null;
+
+
/** Set to true if the user decides to close the window while running. */
private boolean abort = false;
@@ -1026,6 +1029,17 @@
Utils.getPath(getInstallation().getHistoryLogFile())))
.append(formatter.getLineBreak())
.toMessage());
+
+ try {
+ Stage stage = getStage();
+ List<Message> stageMessages = stage.getMessages();
+ for (Message m : stageMessages) {
+ notifyListeners(m);
+ }
+ } catch (IOException e) {
+ LOG.log(Level.INFO, "failed to access stage", e);
+ }
+
} catch (ApplicationException e) {
notifyListeners(getFormattedErrorWithLineBreak());
LOG.log(Level.INFO, "Error cleaning up after upgrade.", e);
@@ -1164,18 +1178,19 @@
}
+ private Stage getStage() throws IOException, ApplicationException {
+ if (this.stage == null) {
+ this.stage = new Stage(getStageDirectory());
+ }
+ return this.stage;
+ }
+
private void upgradeComponents() throws ApplicationException {
try {
- File stageDir = getStageDirectory();
+ Stage stage = getStage();
Installation installation = getInstallation();
File root = installation.getRootDirectory();
- FileManager fm = new FileManager();
- for (String fileName : stageDir.list()) {
- File f = new File(stageDir, fileName);
- fm.copyRecursively(f, root,
- new UpgradeFileFilter(stageDir),
- /*overwrite=*/true);
- }
+ stage.move(root);
// The bits should now be of the new version. Have
// the installation update the build information so
@@ -1223,7 +1238,15 @@
FileFilter filter = new UpgradeFileFilter(root);
for (String fileName : root.list()) {
File f = new File(root, fileName);
- //fm.copyRecursively(f, filesBackupDirectory,
+
+ // Replacing a Windows bat file while it is running with a different
+ // version leads to unpredictable behavior so we make a special case
+ // here and during the upgrade components step.
+ if (Utils.isWindows() &&
+ fileName.equals(Installation.WINDOWS_UPGRADE_FILE_NAME)) {
+ continue;
+ }
+
fm.move(f, filesBackupDirectory, filter);
}
} catch (ApplicationException ae) {
diff --git a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
index 8379c04..78813cd 100644
--- a/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
+++ b/opendj-sdk/opends/src/quicksetup/org/opends/quicksetup/util/FileManager.java
@@ -353,6 +353,33 @@
filter);
}
+ /**
+ * Determines whether or not two files differ in content.
+ *
+ * @param f1 file to compare
+ * @param f2 file to compare
+ * @return boolean where true indicates that two files differ
+ * @throws IOException if there is a problem reading the files' conents
+ */
+ public boolean filesDiffer(File f1, File f2) throws IOException {
+ boolean differ = false;
+ FileReader fr1 = new FileReader(f1);
+ FileReader fr2 = new FileReader(f2);
+ try {
+ boolean done = false;
+ while (!differ && !done) {
+ int c1 = fr1.read();
+ int c2 = fr2.read();
+ differ = c1 != c2;
+ done = c1 == -1 || c2 == -1;
+ }
+ } finally {
+ fr1.close();
+ fr2.close();
+ }
+ return differ;
+ }
+
private void operateRecursively(FileOperation op, FileFilter filter)
throws ApplicationException {
File file = op.getObjectFile();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/quicksetup/util/FileManagerTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/quicksetup/util/FileManagerTest.java
index 345ca8a..1a33cca 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/quicksetup/util/FileManagerTest.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/quicksetup/util/FileManagerTest.java
@@ -498,6 +498,39 @@
assertTrue(ORIGINAL.equals(contentsOf(copiedF2b1)));
}
+ @DataProvider(name = "differTestData")
+ public Object[][] differTestData() {
+ return new Object[][] {
+ new Object[] { "abc", "abc" },
+ new Object[] { "abc", "xyz" },
+ new Object[] { "abc", "abc\n" },
+ new Object[] { "abc\n", "abc\n" },
+ new Object[] { "abc\nabc", "abc\nabc" },
+ new Object[] { "abc\nabc\nabc", "abc\nabc\nabc" }
+ };
+ }
+
+ @Test(dataProvider = "differTestData")
+ public void testFilesDiffer(String contents1, String contents2)
+ throws Exception
+ {
+ File f1 = new File(fmWorkspace, "f1");
+ File f2 = new File(fmWorkspace, "f2");
+ writeContents(f1, contents1);
+ writeContents(f2, contents2);
+ if (!contents1.equals(contents2)) {
+ assertTrue(fileManager.filesDiffer(f1, f2),
+ "File contents '" + contents1 + "' and '" + contents2 + "' are " +
+ "not equal despite what FileManager claims");
+ } else {
+ assertFalse(fileManager.filesDiffer(f1, f2),
+ "File contents '" + contents1 + "' and '" + contents2 + "' are " +
+ "equal despite what FileManager claims");
+ }
+ f1.delete();
+ f2.delete();
+ }
+
/**
* Creates a set of file for testing.
* @param parent of the files.
--
Gitblit v1.10.0