[syncany] 07/14: Imported Upstream version 0.4.7~alpha
Markus Koschany
apo-guest at moszumanska.debian.org
Wed Nov 11 22:58:48 UTC 2015
This is an automated email from the git hooks/post-receive script.
apo-guest pushed a commit to branch master
in repository syncany.
commit 0643c60c85d5bd5c44f2acfa42ecd974ffa4d4ff
Author: Markus Koschany <apo at debian.org>
Date: Wed Nov 11 22:51:40 2015 +0100
Imported Upstream version 0.4.7~alpha
---
.gitignore | 1 +
CHANGELOG.md | 11 +
build.gradle | 2 +-
gradle/arch/syncany/PKGBUILD | 6 +-
gradle/daemon/syncanyd.bat.skel | 8 +-
gradle/gradle/application.distribution.gradle | 6 +-
gradle/innosetup/innoinstall.sh | 5 +-
gradle/osx/syncany.rb | 11 +-
.../main/java/org/syncany/cli/PluginCommand.java | 2 +-
.../operations/down/DatabaseFileReader.java | 160 +++++++++++++
.../org/syncany/operations/down/DownOperation.java | 126 ++--------
.../down/FileSystemActionReconciliator.java | 15 +-
.../down/actions/ChangeFileSystemAction.java | 6 +-
.../down/actions/FileCreatingFileSystemAction.java | 11 +-
.../down/actions/NewFileSystemAction.java | 5 +-
.../syncany/operations/init/ConnectOperation.java | 2 +-
.../restore/RestoreFileSystemAction.java | 5 +-
.../operations/restore/RestoreOperation.java | 6 +-
.../org/syncany/operations/up/UpOperation.java | 256 +++++++++++----------
.../plugins/local/LocalTransferManager.java | 2 +-
.../TransactionAwareFeatureTransferManager.java | 11 +-
.../java/org/syncany/tests/ScenarioTestSuite.java | 4 +
.../operations/FileSystemActionComparatorTest.java | 2 +-
.../Issue520NoResumeOnCorruptXmlScenarioTest.java | 77 +++++++
24 files changed, 474 insertions(+), 266 deletions(-)
diff --git a/.gitignore b/.gitignore
index 7f49107..857f604 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ gradle/arch/syncany/pkg
gradle/arch/syncany/src
gradle/arch/syncany/syncany*.tar*
*.orig
+.recommenders
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4150ba8..69f76cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,17 @@
Change Log
==========
+### Syncany 0.4.7-alpha (Date: 7 Nov 2015)
+- Developer/alpha release (**We are now nearing the beta phase. Stay tuned!**)
+- Bugfixes and other things:
+ + Refactoring and simplification of UpOperation
+ + Refactoring DownOperation (better memory management)
+ + Refactoring of error handling (don't throw 'Exception')
+ + Fix GUI crashes in 'Add folder' wizard #497
+ + Fix OSX daemon start/stop to new style #281/#530
+ + Fix Windows spaces in path issue #522/#529
+ + Fix not resuming transactions if transaction files are corrupt #520
+
### Syncany 0.4.6-alpha (Date: 11 July 2015)
- Developer/alpha release (**We are now nearing the beta phase. Stay tuned!**)
- Features and significant changes:
diff --git a/build.gradle b/build.gradle
index b7b0a02..0b79219 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ apply from: 'gradle/gradle/helpers.gradle'
// Global Settings /////////////////////////////////////////////////////////////
project.ext {
- applicationVersion = "0.4.6-alpha"
+ applicationVersion = "0.4.7-alpha"
applicationVersionDebian = "1";
applicationRelease = isApplicationRelease()
diff --git a/gradle/arch/syncany/PKGBUILD b/gradle/arch/syncany/PKGBUILD
index f8ae744..02af931 100644
--- a/gradle/arch/syncany/PKGBUILD
+++ b/gradle/arch/syncany/PKGBUILD
@@ -1,7 +1,7 @@
# Maintainer: Pim Otte <otte dot pim at gmail dot com>
pkgname=syncany
-pkgver=0.4.5_alpha
-_realver=0.4.5-alpha
+pkgver=0.4.6_alpha
+_realver=0.4.6-alpha
pkgrel=1
pkgdesc="Cloud storage and filesharing application with a focus on security and abstraction of storage."
arch=(any)
@@ -10,7 +10,7 @@ license=('GPL3')
depends=('java-runtime>=7' 'bash-completion')
source=("http://syncany.org/dist/$pkgname-${_realver}.tar.gz"
)
-sha256sums=('7e8fbad0d9bf90369ad21fd1b3d54c7025738f2da9b1a364273f16cb76d59069')
+sha256sums=('9aab83cc336b898a48dde5e7799d703ef569255e5c6f24a7182a6983c0846bc8')
package() {
install -Dm644 "$srcdir/$pkgname-${_realver}/bash/syncany.bash-completion" "${pkgdir}/etc/bash_completion.d/syncany"
diff --git a/gradle/daemon/syncanyd.bat.skel b/gradle/daemon/syncanyd.bat.skel
index f972758..a3ef48d 100644
--- a/gradle/daemon/syncanyd.bat.skel
+++ b/gradle/daemon/syncanyd.bat.skel
@@ -1,10 +1,10 @@
rem Embedded Daemon script begins ################################SYNCANY_INCL_3#
set APP_NAME=syncanyd
-set APP_USER_DIR=%AppData%\Syncany
-set APP_DAEMON_CONTROL=%APP_USER_DIR%\daemon.ctrl
-set APP_DAEMON_PIDFILE=%APP_USER_DIR%\daemon.pid
-set APP_DAEMON_PIDFILE_TMP=%APP_USER_DIR%\daemon.pid.tmp
+set APP_USER_DIR="%AppData%\Syncany"
+set APP_DAEMON_CONTROL="%APP_USER_DIR%\daemon.ctrl"
+set APP_DAEMON_PIDFILE="%APP_USER_DIR%\daemon.pid"
+set APP_DAEMON_PIDFILE_TMP="%APP_USER_DIR%\daemon.pid.tmp"
if not exist "%APP_USER_DIR%" mkdir "%APP_USER_DIR%"
diff --git a/gradle/gradle/application.distribution.gradle b/gradle/gradle/application.distribution.gradle
index 3d47eeb..ea70a91 100644
--- a/gradle/gradle/application.distribution.gradle
+++ b/gradle/gradle/application.distribution.gradle
@@ -22,7 +22,7 @@ startScripts {
// - Read max memory (-Xmx) from userconfig.xml
String winMaxMemoryCommands = "@rem Read max memory from userconfig.xml #SYNCANY_INCL_1#\r\n"
- winMaxMemoryCommands += "set APP_USERCONFIG_FILE=%AppData%\\\\Syncany\\\\userconfig.xml\r\n";
+ winMaxMemoryCommands += "set APP_USERCONFIG_FILE=\"%AppData%\\\\Syncany\\\\userconfig.xml\"\r\n";
winMaxMemoryCommands += "\r\n";
winMaxMemoryCommands += "if exist \"%APP_USERCONFIG_FILE%\" (\r\n";
winMaxMemoryCommands += " if \"%OS%\"==\"Windows_NT\" setlocal ENABLEDELAYEDEXPANSION\r\n";
@@ -42,7 +42,7 @@ startScripts {
// - Post Java process commands: Delayed plugin JAR file deletion (Windows only)
String winPurgeFileDeletionCommands = "@rem Delete plugin JARs #SYNCANY_INCL_2#\r\n"
- winPurgeFileDeletionCommands += "SET PURGEFILE=%AppData%\\\\Syncany\\\\purgefile\r\n";
+ winPurgeFileDeletionCommands += "SET PURGEFILE=\"%AppData%\\\\Syncany\\\\purgefile\"\r\n";
winPurgeFileDeletionCommands += "if exist %PURGEFILE% (\r\n";
winPurgeFileDeletionCommands += " @for /f %%b in (%PURGEFILE%) do del /q \"%%b\" 2>NUL\r\n";
winPurgeFileDeletionCommands += " del /q %PURGEFILE% 2>NUL\r\n";
@@ -52,7 +52,7 @@ startScripts {
// - Post Java process commands: Delayed plugin JAR file install (Windows only)
String winUpdateFileCommands = "@rem Reinstall plugins after update removal #SYNCANY_INCL_4#\r\n"
- winUpdateFileCommands += "SET UPDATEFILE=%AppData%\\\\Syncany\\\\updatefile\r\n";
+ winUpdateFileCommands += "SET UPDATEFILE=\"%AppData%\\\\Syncany\\\\updatefile\"\r\n";
winUpdateFileCommands += "if exist %UPDATEFILE% (\r\n";
winUpdateFileCommands += " @for /f %%b in (%UPDATEFILE%) do (\r\n <nul set /p =Updating %%b... \r\n \"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %SYNCANY_OPTS% -classpath \"%CLASSPATH%\" org.syncany.Syncany plugin install %%b -m 2>NUL)\r\n";
winUpdateFileCommands += " del /q %UPDATEFILE% 2>NUL\r\n";
diff --git a/gradle/innosetup/innoinstall.sh b/gradle/innosetup/innoinstall.sh
index 44f4a8d..90a960f 100755
--- a/gradle/innosetup/innoinstall.sh
+++ b/gradle/innosetup/innoinstall.sh
@@ -4,11 +4,8 @@ rm -rf /tmp/inno
mkdir /tmp/inno
cd /tmp/inno
-wget -O is.exe http://www.jrsoftware.org/download.php/is.exe
+wget -O is.exe http://files.jrsoftware.org/is/5/isetup-5.5.5.exe
innoextract is.exe
mkdir -p ~/".wine/drive_c/inno"
cp -a app/* ~/".wine/drive_c/inno"
-#mkdir ~/".wine/drive_c/Program Files (x86)/Inno Setup 5"
-#cp -a app/* ~/".wine/drive_c/Program Files (x86)/Inno Setup 5"
-
diff --git a/gradle/osx/syncany.rb b/gradle/osx/syncany.rb
index 15a8fb4..e5b2952 100644
--- a/gradle/osx/syncany.rb
+++ b/gradle/osx/syncany.rb
@@ -2,9 +2,9 @@ require "formula"
class Syncany < Formula
homepage "https://www.syncany.org"
- url "https://codeload.github.com/syncany/syncany/tar.gz/v0.4.5-alpha"
- sha256 "957537a7177a5234e794d871043e67ac8e06ff3ff1ee059e599dfd477c5bb8e5"
- version "0.4.5-alpha"
+ url "https://codeload.github.com/syncany/syncany/tar.gz/v0.4.6-alpha"
+ sha256 "1dfd92e7618297eae6ee0d8acb7103f55c8d18500da409d032a5941147fbcd85"
+ version "0.4.6-alpha"
head "https://github.com/syncany/syncany.git", :branch => "develop"
depends_on :java => "1.7+"
@@ -26,7 +26,7 @@ class Syncany < Formula
bin.install_symlink Dir["#{libexec}/bin/sy"]
end
- plist_options :manual => "sy --daemon"
+ plist_options :manual => "sy daemon start"
def plist; <<-EOS.undent
<?xml version="1.0" encoding="UTF-8"?>
@@ -38,7 +38,8 @@ class Syncany < Formula
<key>ProgramArguments</key>
<array>
<string>#{prefix}/bin/sy</string>
- <string>--daemon</string>
+ <string>daemon</string>
+ <string>start</string>
</array>
<key>ProcessType</key>
<string>Background</string>
diff --git a/syncany-cli/src/main/java/org/syncany/cli/PluginCommand.java b/syncany-cli/src/main/java/org/syncany/cli/PluginCommand.java
index 1972c94..86c4d55 100644
--- a/syncany-cli/src/main/java/org/syncany/cli/PluginCommand.java
+++ b/syncany-cli/src/main/java/org/syncany/cli/PluginCommand.java
@@ -225,7 +225,7 @@ public class PluginCommand extends Command {
if (thirdPartyCount > 0) {
String pluginPlugins = (thirdPartyCount == 1) ? "plugin" : "plugins";
- out.printf("\nThird party plugins:\nPlease note that the Syncany Team does not take review or maintain the third-party %s\nlisted above. Please report issues to the corresponding plugin site.\n", pluginPlugins);
+ out.printf("\nThird party plugins:\nPlease note that the Syncany Team does not review or maintain the third-party %s\nlisted above. Please report issues to the corresponding plugin site.\n", pluginPlugins);
}
}
else {
diff --git a/syncany-lib/src/main/java/org/syncany/operations/down/DatabaseFileReader.java b/syncany-lib/src/main/java/org/syncany/operations/down/DatabaseFileReader.java
new file mode 100644
index 0000000..8d5363c
--- /dev/null
+++ b/syncany-lib/src/main/java/org/syncany/operations/down/DatabaseFileReader.java
@@ -0,0 +1,160 @@
+/*
+ * Syncany, www.syncany.org
+ * Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel at gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package org.syncany.operations.down;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.syncany.database.DatabaseVersion;
+import org.syncany.database.DatabaseVersionHeader;
+import org.syncany.database.MemoryDatabase;
+import org.syncany.database.VectorClock;
+import org.syncany.database.dao.DatabaseXmlSerializer;
+import org.syncany.database.dao.DatabaseXmlSerializer.DatabaseReadType;
+
+/**
+ * The DatabaseFileReader provides a way to read a series of database files
+ * in a memory-efficient way, by converting them to a series of MemoryDatabases,
+ * none of which are too large.
+ *
+ * @author Pim Otte
+ */
+public class DatabaseFileReader implements Iterator<MemoryDatabase> {
+ private static final int MAX_FILES = 9999;
+
+ private DatabaseXmlSerializer databaseSerializer;
+ private List<DatabaseVersionHeader> winnersApplyBranchList;
+ private Map<DatabaseVersionHeader, File> databaseVersionLocations;
+ private int branchIndex = 0;
+
+ public DatabaseFileReader(DatabaseXmlSerializer databaseSerializer, DatabaseBranch winnersApplyBranch,
+ Map<DatabaseVersionHeader, File> databaseVersionLocations) {
+
+ this.winnersApplyBranchList = winnersApplyBranch.getAll();
+ this.databaseVersionLocations = databaseVersionLocations;
+ this.databaseSerializer = databaseSerializer;
+ }
+
+ public boolean hasNext() {
+ return branchIndex < winnersApplyBranchList.size();
+ }
+
+ /**
+ * Loads the winner's database branch into the memory in a {@link MemoryDatabase} object, by using
+ * the already downloaded list of remote database files.
+ *
+ * <p>Because database files can contain multiple {@link DatabaseVersion}s per client, a range for which
+ * to load the database versions must be determined.
+ *
+ * <p><b>Example 1:</b><br />
+ * <pre>
+ * db-A-0001 (A1) Already known Not loaded
+ * db-A-0005 (A2) Already known Not loaded
+ * (A3) Already known Not loaded
+ * (A4) Part of winner's branch Loaded
+ * (A5) Purge database version Ignored (only DEFAULT)
+ * db-B-0001 (A5,B1) Part of winner's branch Loaded
+ * db-A-0006 (A6,B1) Part of winner's branch Loaded
+ * </pre>
+ *
+ * <p>In example 1, only (A4)-(A5) must be loaded from db-A-0005, and not all four database versions.
+ *
+ * <p><b>Other example:</b><br />
+ * <pre>
+ * db-A-0005 (A1) Part of winner's branch Loaded
+ * db-A-0005 (A2) Part of winner's branch Loaded
+ * db-B-0001 (A2,B1) Part of winner's branch Loaded
+ * db-A-0005 (A3,B1) Part of winner's branch Loaded
+ * db-A-0005 (A4,B1) Part of winner's branch Loaded
+ * db-A-0005 (A5,B1) Purge database version Ignored (only DEFAULT)
+ * </pre>
+ *
+ * <p>In example 2, (A1)-(A5,B1) [except (A2,B1)] are contained in db-A-0005 (after merging!), so
+ * db-A-0005 must be processed twice; each time loading separate parts of the file. In this case:
+ * First load (A1)-(A2) from db-A-0005, then load (A2,B1) from db-B-0001, then load (A3,B1)-(A4,B1)
+ * from db-A-0005, and ignore (A5,B1).
+ * @param databaseFileList
+ * @param ignoredMostRecentPurgeVersions
+ *
+ * @return Returns a loaded memory database containing all metadata from the winner's branch
+ */
+ @Override
+ public MemoryDatabase next() {
+ MemoryDatabase winnerBranchDatabase = new MemoryDatabase();
+ String rangeClientName = null;
+ VectorClock rangeVersionFrom = null;
+ VectorClock rangeVersionTo = null;
+
+ while (branchIndex < winnersApplyBranchList.size() && winnerBranchDatabase.getFileHistories().size() < MAX_FILES) {
+ DatabaseVersionHeader currentDatabaseVersionHeader = winnersApplyBranchList.get(branchIndex);
+ DatabaseVersionHeader nextDatabaseVersionHeader = (branchIndex + 1 < winnersApplyBranchList.size()) ? winnersApplyBranchList
+ .get(branchIndex + 1) : null;
+
+ // First of range for this client
+ if (rangeClientName == null) {
+ rangeClientName = currentDatabaseVersionHeader.getClient();
+ rangeVersionFrom = currentDatabaseVersionHeader.getVectorClock();
+ rangeVersionTo = currentDatabaseVersionHeader.getVectorClock();
+ }
+
+ // Still in range for this client
+ else {
+ rangeVersionTo = currentDatabaseVersionHeader.getVectorClock();
+ }
+
+ // Now load this stuff from the database file (or not)
+ // - If the database file exists, load the range and reset it
+ // - If not, only force a load if this is the range end
+
+ File databaseVersionFile = databaseVersionLocations.get(currentDatabaseVersionHeader);
+
+ if (databaseVersionFile == null) {
+ throw new RuntimeException("Could not find file corresponding to " + currentDatabaseVersionHeader
+ + ", while it is in the winners branch.");
+ }
+
+ boolean lastDatabaseVersionHeader = nextDatabaseVersionHeader == null;
+ boolean nextDatabaseVersionInSameFile = lastDatabaseVersionHeader
+ || databaseVersionFile.equals(databaseVersionLocations.get(nextDatabaseVersionHeader));
+ boolean rangeEnds = lastDatabaseVersionHeader || !nextDatabaseVersionInSameFile;
+
+ if (rangeEnds) {
+ try {
+ databaseSerializer.load(winnerBranchDatabase, databaseVersionFile, rangeVersionFrom, rangeVersionTo, DatabaseReadType.FULL);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ rangeClientName = null;
+ }
+ branchIndex++;
+ }
+
+ return winnerBranchDatabase;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("Removing a databaseversion is not supported");
+
+ }
+
+}
diff --git a/syncany-lib/src/main/java/org/syncany/operations/down/DownOperation.java b/syncany-lib/src/main/java/org/syncany/operations/down/DownOperation.java
index 2735aac..4849eca 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/down/DownOperation.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/down/DownOperation.java
@@ -457,119 +457,43 @@ public class DownOperation extends AbstractTransferOperation {
}
else {
logger.log(Level.INFO, "Loading winners database (DEFAULT) ...");
- MemoryDatabase winnersDatabase = readWinnersDatabase(winnersApplyBranch, databaseVersionLocations);
+ DatabaseFileReader databaseFileReader = new DatabaseFileReader(databaseSerializer, winnersApplyBranch, databaseVersionLocations);
- if (options.isApplyChanges()) {
- new ApplyChangesOperation(config, localDatabase, transferManager, winnersDatabase, result, cleanupOccurred,
- preDeleteFileHistoriesWithLastVersion).execute();
- }
+ boolean noDatabaseVersions = !databaseFileReader.hasNext();
+
+ if (noDatabaseVersions) {
+ applyChangesAndPersistDatabase(new MemoryDatabase(), cleanupOccurred, preDeleteFileHistoriesWithLastVersion);
+ }
else {
- logger.log(Level.INFO, "Doing nothing on the file system, because --no-apply switched on");
+ while (databaseFileReader.hasNext()) {
+ MemoryDatabase winnersDatabase = databaseFileReader.next();
+ applyChangesAndPersistDatabase(winnersDatabase, cleanupOccurred, preDeleteFileHistoriesWithLastVersion);
+ }
}
- persistDatabaseVersions(winnersApplyBranch, winnersDatabase);
-
result.setResultCode(DownResultCode.OK_WITH_REMOTE_CHANGES);
}
}
- /**
- * Loads the winner's database branch into the memory in a {@link MemoryDatabase} object, by using
- * the already downloaded list of remote database files.
- *
- * <p>Because database files can contain multiple {@link DatabaseVersion}s per client, a range for which
- * to load the database versions must be determined.
- *
- * <p><b>Example 1:</b><br />
- * <pre>
- * db-A-0001 (A1) Already known Not loaded
- * db-A-0005 (A2) Already known Not loaded
- * (A3) Already known Not loaded
- * (A4) Part of winner's branch Loaded
- * (A5) Purge database version Ignored (only DEFAULT)
- * db-B-0001 (A5,B1) Part of winner's branch Loaded
- * db-A-0006 (A6,B1) Part of winner's branch Loaded
- * </pre>
- *
- * <p>In example 1, only (A4)-(A5) must be loaded from db-A-0005, and not all four database versions.
- *
- * <p><b>Other example:</b><br />
- * <pre>
- * db-A-0005 (A1) Part of winner's branch Loaded
- * db-A-0005 (A2) Part of winner's branch Loaded
- * db-B-0001 (A2,B1) Part of winner's branch Loaded
- * db-A-0005 (A3,B1) Part of winner's branch Loaded
- * db-A-0005 (A4,B1) Part of winner's branch Loaded
- * db-A-0005 (A5,B1) Purge database version Ignored (only DEFAULT)
- * </pre>
- *
- * <p>In example 2, (A1)-(A5,B1) [except (A2,B1)] are contained in db-A-0005 (after merging!), so
- * db-A-0005 must be processed twice; each time loading separate parts of the file. In this case:
- * First load (A1)-(A2) from db-A-0005, then load (A2,B1) from db-B-0001, then load (A3,B1)-(A4,B1)
- * from db-A-0005, and ignore (A5,B1).
- * @param databaseFileList
- * @param ignoredMostRecentPurgeVersions
- *
- * @return Returns a loaded memory database containing all metadata from the winner's branch
- */
- private MemoryDatabase readWinnersDatabase(DatabaseBranch winnersApplyBranch, Map<DatabaseVersionHeader, File> databaseVersionLocations)
- throws IOException, StorageException {
-
- MemoryDatabase winnerBranchDatabase = new MemoryDatabase();
-
- List<DatabaseVersionHeader> winnersApplyBranchList = winnersApplyBranch.getAll();
-
- String rangeClientName = null;
- VectorClock rangeVersionFrom = null;
- VectorClock rangeVersionTo = null;
-
- for (int i = 0; i < winnersApplyBranchList.size(); i++) {
- DatabaseVersionHeader currentDatabaseVersionHeader = winnersApplyBranchList.get(i);
- DatabaseVersionHeader nextDatabaseVersionHeader = (i + 1 < winnersApplyBranchList.size()) ? winnersApplyBranchList.get(i + 1) : null;
-
- // First of range for this client
- if (rangeClientName == null) {
- rangeClientName = currentDatabaseVersionHeader.getClient();
- rangeVersionFrom = currentDatabaseVersionHeader.getVectorClock();
- rangeVersionTo = currentDatabaseVersionHeader.getVectorClock();
- }
-
- // Still in range for this client
- else {
- rangeVersionTo = currentDatabaseVersionHeader.getVectorClock();
- }
-
- // Now load this stuff from the database file (or not)
- // - If the database file exists, load the range and reset it
- // - If not, only force a load if this is the range end
-
- File databaseVersionFile = databaseVersionLocations.get(currentDatabaseVersionHeader);
-
- if (databaseVersionFile == null) {
- throw new StorageException("Could not find file corresponding to " + currentDatabaseVersionHeader
- + ", while it is in the winners branch.");
- }
-
- boolean lastDatabaseVersionHeader = nextDatabaseVersionHeader == null;
- boolean nextDatabaseVersionInSameFile = lastDatabaseVersionHeader
- || databaseVersionFile.equals(databaseVersionLocations.get(nextDatabaseVersionHeader));
- boolean rangeEnds = lastDatabaseVersionHeader || !nextDatabaseVersionInSameFile;
-
- if (rangeEnds) {
- databaseSerializer.load(winnerBranchDatabase, databaseVersionFile, rangeVersionFrom, rangeVersionTo, DatabaseReadType.FULL);
- rangeClientName = null;
- }
+ private void applyChangesAndPersistDatabase(MemoryDatabase winnersDatabase, boolean cleanupOccurred,
+ List<PartialFileHistory> preDeleteFileHistoriesWithLastVersion) throws Exception {
+
+ if (options.isApplyChanges()) {
+ new ApplyChangesOperation(config, localDatabase, transferManager, winnersDatabase, result, cleanupOccurred,
+ preDeleteFileHistoriesWithLastVersion).execute();
+ }
+ else {
+ logger.log(Level.INFO, "Doing nothing on the file system, because --no-apply switched on");
}
- if (logger.isLoggable(Level.FINE)) {
- logger.log(Level.FINE, "Winner Database Branch:");
-
- for (DatabaseVersion dbv : winnerBranchDatabase.getDatabaseVersions()) {
- logger.log(Level.FINE, "- " + dbv.getHeader());
- }
+ // We only persist the versions that we have already applied.
+ DatabaseBranch currentApplyBranch = new DatabaseBranch();
+ for (DatabaseVersion databaseVersion : winnersDatabase.getDatabaseVersions()) {
+ currentApplyBranch.add(databaseVersion.getHeader());
}
- return winnerBranchDatabase;
+ persistDatabaseVersions(currentApplyBranch, winnersDatabase);
+ localDatabase.commit();
}
/**
diff --git a/syncany-lib/src/main/java/org/syncany/operations/down/FileSystemActionReconciliator.java b/syncany-lib/src/main/java/org/syncany/operations/down/FileSystemActionReconciliator.java
index c01d2ef..093dc86 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/down/FileSystemActionReconciliator.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/down/FileSystemActionReconciliator.java
@@ -36,6 +36,7 @@ import org.syncany.database.MemoryDatabase;
import org.syncany.database.PartialFileHistory;
import org.syncany.database.PartialFileHistory.FileHistoryId;
import org.syncany.database.SqlDatabase;
+import org.syncany.operations.Assembler;
import org.syncany.operations.ChangeSet;
import org.syncany.operations.down.actions.ChangeFileSystemAction;
import org.syncany.operations.down.actions.DeleteFileSystemAction;
@@ -132,6 +133,7 @@ public class FileSystemActionReconciliator {
private ChangeSet changeSet;
private SqlDatabase localDatabase;
private FileVersionComparator fileVersionComparator;
+ private Assembler assembler;
public FileSystemActionReconciliator(Config config, ChangeSet changeSet) {
this.config = config;
@@ -147,6 +149,7 @@ public class FileSystemActionReconciliator {
public List<FileSystemAction> determineFileSystemActions(MemoryDatabase winnersDatabase, boolean cleanupOccurred,
List<PartialFileHistory> localFileHistoriesWithLastVersion) throws Exception {
+ this.assembler = new Assembler(config, localDatabase, winnersDatabase);
List<FileSystemAction> fileSystemActions = new ArrayList<FileSystemAction>();
@@ -230,7 +233,7 @@ public class FileSystemActionReconciliator {
logger.log(Level.INFO, " -> (1) Equals: Nothing to do, winning version equals winning file: "+winningLastVersion+" AND "+winningLastFile);
}
else if (winningFileToVersionComparison.getFileChanges().contains(FileChange.DELETED)) {
- FileSystemAction action = new NewFileSystemAction(config, winningLastVersion, winnersDatabase);
+ FileSystemAction action = new NewFileSystemAction(config, winnersDatabase, assembler, winningLastVersion);
outFileSystemActions.add(action);
logger.log(Level.INFO, " -> (2) Deleted: Local file does NOT exist, but it should, winning version not known: "+winningLastVersion+" AND "+winningLastFile);
@@ -272,7 +275,7 @@ public class FileSystemActionReconciliator {
throw new Exception("What happend here?");
}
else { // Content changed
- FileSystemAction action = new NewFileSystemAction(config, winningLastVersion, winnersDatabase);
+ FileSystemAction action = new NewFileSystemAction(config, winnersDatabase, assembler, winningLastVersion);
outFileSystemActions.add(action);
logger.log(Level.INFO, " -> (7) Content changed: Winning file differs from winning version: "+winningLastVersion+" AND "+winningLastFile);
@@ -294,7 +297,7 @@ public class FileSystemActionReconciliator {
logger.log(Level.INFO, " -> (8) Equals: Nothing to do, local file equals local version equals winning version: local file = "+localLastFile+", local version = "+localLastVersion+", winning version = "+winningLastVersion);
}
else if (winningVersionToLocalVersionComparison.getFileChanges().contains(FileChange.DELETED)) {
- FileSystemAction action = new ChangeFileSystemAction(config, localLastVersion, winningLastVersion, winnersDatabase);
+ FileSystemAction action = new ChangeFileSystemAction(config, winnersDatabase, assembler, localLastVersion, winningLastVersion);
fileSystemActions.add(action);
logger.log(Level.INFO, " -> (9) Content changed: Local file does not exist, but it should: local file = "+localLastFile+", local version = "+localLastVersion+", winning version = "+winningLastVersion);
@@ -333,7 +336,7 @@ public class FileSystemActionReconciliator {
changeSet.getChangedFiles().add(winningLastVersion.getPath());
}
else { // Content changed
- FileSystemAction action = new ChangeFileSystemAction(config, localLastVersion, winningLastVersion, winnersDatabase);
+ FileSystemAction action = new ChangeFileSystemAction(config, winnersDatabase, assembler, localLastVersion, winningLastVersion);
fileSystemActions.add(action);
logger.log(Level.INFO, " -> (13) Content changed: Local file differs from winning version: local file = "+localLastFile+", local version = "+localLastVersion+", winning version = "+winningLastVersion);
@@ -351,7 +354,7 @@ public class FileSystemActionReconciliator {
boolean winningLastVersionDeleted = winningLastVersion.getStatus() == FileStatus.DELETED;
if (!winningLastVersionDeleted) {
- FileSystemAction action = new ChangeFileSystemAction(config, localLastVersion, winningLastVersion, winnersDatabase);
+ FileSystemAction action = new ChangeFileSystemAction(config, winnersDatabase, assembler, localLastVersion, winningLastVersion);
fileSystemActions.add(action);
logger.log(Level.INFO, " -> (14) Content changed: Local file does NOT exist, and winning version changed: local file = "+localLastFile+", local version = "+localLastVersion+", winning version = "+winningLastVersion);
@@ -364,7 +367,7 @@ public class FileSystemActionReconciliator {
}
}
else {
- FileSystemAction action = new ChangeFileSystemAction(config, localLastVersion, winningLastVersion, winnersDatabase);
+ FileSystemAction action = new ChangeFileSystemAction(config, winnersDatabase, assembler, winningLastVersion, localLastVersion);
fileSystemActions.add(action);
logger.log(Level.INFO, " -> (16) Content changed: Local file differs from last version: local file = "+localLastFile+", local version = "+localLastVersion+", winning version = "+winningLastVersion);
diff --git a/syncany-lib/src/main/java/org/syncany/operations/down/actions/ChangeFileSystemAction.java b/syncany-lib/src/main/java/org/syncany/operations/down/actions/ChangeFileSystemAction.java
index f367b73..1acb363 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/down/actions/ChangeFileSystemAction.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/down/actions/ChangeFileSystemAction.java
@@ -23,10 +23,12 @@ import org.syncany.config.Config;
import org.syncany.database.FileVersion;
import org.syncany.database.FileVersion.FileStatus;
import org.syncany.database.MemoryDatabase;
+import org.syncany.operations.Assembler;
public class ChangeFileSystemAction extends FileCreatingFileSystemAction {
- public ChangeFileSystemAction(Config config, FileVersion fromFileVersion, FileVersion toFileVersion, MemoryDatabase winningDatabase) {
- super(config, winningDatabase, fromFileVersion, toFileVersion);
+ public ChangeFileSystemAction(Config config, MemoryDatabase winningDatabase, Assembler assembler, FileVersion fromFileVersion,
+ FileVersion toFileVersion) {
+ super(config, winningDatabase, assembler, fromFileVersion, toFileVersion);
}
@Override
diff --git a/syncany-lib/src/main/java/org/syncany/operations/down/actions/FileCreatingFileSystemAction.java b/syncany-lib/src/main/java/org/syncany/operations/down/actions/FileCreatingFileSystemAction.java
index 903ec0f..fddd5f9 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/down/actions/FileCreatingFileSystemAction.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/down/actions/FileCreatingFileSystemAction.java
@@ -24,13 +24,15 @@ import org.syncany.config.Config;
import org.syncany.database.FileVersion;
import org.syncany.database.FileVersion.FileType;
import org.syncany.database.MemoryDatabase;
-import org.syncany.database.SqlDatabase;
import org.syncany.operations.Assembler;
import org.syncany.util.NormalizedPath;
public abstract class FileCreatingFileSystemAction extends FileSystemAction {
- public FileCreatingFileSystemAction(Config config, MemoryDatabase winningDatabase, FileVersion file1, FileVersion file2) {
- super(config, winningDatabase, file1, file2);
+ protected Assembler assembler;
+
+ public FileCreatingFileSystemAction(Config config, MemoryDatabase winningDatabase, Assembler assembler, FileVersion file1, FileVersion file2) {
+ super(config, winningDatabase, file1, file2);
+ this.assembler = assembler;
}
protected void createFileFolderOrSymlink(FileVersion reconstructedFileVersion) throws Exception {
@@ -76,9 +78,6 @@ public abstract class FileCreatingFileSystemAction extends FileSystemAction {
}
protected File assembleFileToCache(FileVersion reconstructedFileVersion) throws Exception {
- SqlDatabase localDatabase = new SqlDatabase(config);
- Assembler assembler = new Assembler(config, localDatabase, winningDatabase);
-
File reconstructedFileInCache = assembler.assembleToCache(reconstructedFileVersion);
setFileAttributes(reconstructedFileVersion, reconstructedFileInCache);
diff --git a/syncany-lib/src/main/java/org/syncany/operations/down/actions/NewFileSystemAction.java b/syncany-lib/src/main/java/org/syncany/operations/down/actions/NewFileSystemAction.java
index 0f9f3d3..ad5fd5e 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/down/actions/NewFileSystemAction.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/down/actions/NewFileSystemAction.java
@@ -20,11 +20,12 @@ package org.syncany.operations.down.actions;
import org.syncany.config.Config;
import org.syncany.database.FileVersion;
import org.syncany.database.MemoryDatabase;
+import org.syncany.operations.Assembler;
public class NewFileSystemAction extends FileCreatingFileSystemAction {
- public NewFileSystemAction(Config config, FileVersion newFileVersion, MemoryDatabase winningDatabase) {
- super(config, winningDatabase, null, newFileVersion);
+ public NewFileSystemAction(Config config, MemoryDatabase winningDatabase, Assembler assembler, FileVersion newFileVersion) {
+ super(config, winningDatabase, assembler, null, newFileVersion);
}
@Override
diff --git a/syncany-lib/src/main/java/org/syncany/operations/init/ConnectOperation.java b/syncany-lib/src/main/java/org/syncany/operations/init/ConnectOperation.java
index 45f72d6..2d1de3b 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/init/ConnectOperation.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/init/ConnectOperation.java
@@ -315,7 +315,7 @@ public class ConnectOperation extends AbstractInitOperation {
throw new RuntimeException("Repository file is encrypted, but password cannot be queried (no listener).");
}
- return listener.onUserPassword(null, "Password: ");
+ return listener.onUserPassword(null, "Master Password: ");
}
else {
return options.getPassword();
diff --git a/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreFileSystemAction.java b/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreFileSystemAction.java
index 0cffdc7..61257c0 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreFileSystemAction.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreFileSystemAction.java
@@ -25,14 +25,15 @@ import org.syncany.database.FileVersion;
import org.syncany.database.FileVersion.FileStatus;
import org.syncany.database.FileVersion.FileType;
import org.syncany.database.MemoryDatabase;
+import org.syncany.operations.Assembler;
import org.syncany.operations.down.actions.FileCreatingFileSystemAction;
import org.syncany.util.NormalizedPath;
public class RestoreFileSystemAction extends FileCreatingFileSystemAction {
private String relativeTargetPath;
- public RestoreFileSystemAction(Config config, FileVersion fileVersion, String relativeTargetPath) {
- super(config, new MemoryDatabase(), null, fileVersion);
+ public RestoreFileSystemAction(Config config, Assembler assembler, FileVersion fileVersion, String relativeTargetPath) {
+ super(config, new MemoryDatabase(), assembler, null, fileVersion);
this.relativeTargetPath = relativeTargetPath;
}
diff --git a/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreOperation.java b/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreOperation.java
index 9536b5c..7d2f505 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreOperation.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/restore/RestoreOperation.java
@@ -32,6 +32,7 @@ import org.syncany.database.MultiChunkEntry.MultiChunkId;
import org.syncany.database.PartialFileHistory.FileHistoryId;
import org.syncany.database.SqlDatabase;
import org.syncany.operations.AbstractTransferOperation;
+import org.syncany.operations.Assembler;
import org.syncany.operations.Downloader;
import org.syncany.operations.restore.RestoreOperationResult.RestoreResultCode;
import org.syncany.plugins.transfer.StorageException;
@@ -45,6 +46,8 @@ public class RestoreOperation extends AbstractTransferOperation {
private SqlDatabase localDatabase;
private Downloader downloader;
+ private Assembler assembler;
+
public RestoreOperation(Config config) {
this(config, new RestoreOperationOptions());
}
@@ -55,6 +58,7 @@ public class RestoreOperation extends AbstractTransferOperation {
this.options = options;
this.localDatabase = new SqlDatabase(config);
this.downloader = new Downloader(config, transferManager);
+ this.assembler = new Assembler(config, localDatabase, null);
}
@Override
@@ -88,7 +92,7 @@ public class RestoreOperation extends AbstractTransferOperation {
// Restore file
logger.log(Level.INFO, "- Restoring: " + restoreFileVersion);
- RestoreFileSystemAction restoreAction = new RestoreFileSystemAction(config, restoreFileVersion, options.getRelativeTargetPath());
+ RestoreFileSystemAction restoreAction = new RestoreFileSystemAction(config, assembler, restoreFileVersion, options.getRelativeTargetPath());
RestoreFileSystemActionResult restoreResult = restoreAction.execute();
return new RestoreOperationResult(RestoreResultCode.ACK, restoreResult.getTargetFile());
diff --git a/syncany-lib/src/main/java/org/syncany/operations/up/UpOperation.java b/syncany-lib/src/main/java/org/syncany/operations/up/UpOperation.java
index 9247ce7..a8147fd 100644
--- a/syncany-lib/src/main/java/org/syncany/operations/up/UpOperation.java
+++ b/syncany-lib/src/main/java/org/syncany/operations/up/UpOperation.java
@@ -106,6 +106,11 @@ public class UpOperation extends AbstractTransferOperation {
private UpOperationResult result;
private SqlDatabase localDatabase;
+
+ private boolean resuming;
+ private TransactionRemoteFile transactionRemoteFileToResume;
+ private Collection<RemoteTransaction> remoteTransactionsToResume;
+ private BlockingQueue<DatabaseVersion> databaseVersionQueue;
public UpOperation(Config config) {
this(config, new UpOperationOptions());
@@ -117,6 +122,11 @@ public class UpOperation extends AbstractTransferOperation {
this.options = options;
this.result = new UpOperationResult();
this.localDatabase = new SqlDatabase(config);
+
+ this.resuming = false;
+ this.transactionRemoteFileToResume = null;
+ this.remoteTransactionsToResume = null;
+ this.databaseVersionQueue = new LinkedBlockingQueue<>();
}
@Override
@@ -135,82 +145,28 @@ public class UpOperation extends AbstractTransferOperation {
// Upload action file (lock for cleanup)
startOperation();
- TransactionRemoteFile transactionRemoteFileToResume = null;
- Collection<RemoteTransaction> remoteTransactionsToResume = null;
-
- BlockingQueue<DatabaseVersion> databaseVersionQueue = new LinkedBlockingQueue<>();
- boolean resuming = false;
-
- if (options.isResume()) {
- Collection<Long> versionsToResume = transferManager.loadPendingTransactionList();
- if (versionsToResume != null && versionsToResume.size() != 0) {
- logger.log(Level.INFO, "Found local transaction to resume.");
- logger.log(Level.INFO, "Attempting to find transactionRemoteFile");
-
- remoteTransactionsToResume = attemptResumeTransactions(versionsToResume);
- Collection<DatabaseVersion> remoteDatabaseVersionsToResume = attemptResumeDatabaseVersions(versionsToResume);
-
- if (remoteDatabaseVersionsToResume != null && remoteTransactionsToResume != null &&
- remoteDatabaseVersionsToResume.size() == remoteTransactionsToResume.size()) {
- databaseVersionQueue.addAll(remoteDatabaseVersionsToResume);
- resuming = true;
- }
- // Add stopping marker
- databaseVersionQueue.add(new DatabaseVersion());
-
- try {
- transactionRemoteFileToResume = attemptResumeTransactionRemoteFile();
- }
- catch (BlockingTransfersException e) {
- stopBecauseOfBlockingTransactions();
- return result;
- }
- }
- else {
- transferManager.clearResumableTransactions();
+ try {
+ if (options.isResume()) {
+ prepareResume();
}
- }
- if (!resuming) {
- // Get a list of files that have been updated
- ChangeSet localChanges = result.getStatusResult().getChangeSet();
- List<File> locallyUpdatedFiles = extractLocallyUpdatedFiles(localChanges);
- List<File> locallyDeletedFiles = extractLocallyDeletedFiles(localChanges);
- // Iterate over the changes, deduplicate, and feed DatabaseVersions into an iterator
- Deduper deduper = new Deduper(config.getChunker(), config.getMultiChunker(), config.getTransformer(), options.getTransactionSizeLimit(),
- options.getTransactionFileLimit());
-
- AsyncIndexer asyncIndexer = new AsyncIndexer(config, deduper, locallyUpdatedFiles, locallyDeletedFiles, databaseVersionQueue);
- new Thread(asyncIndexer).start();
- }
-
- // If we are not resuming from a remote transaction, we need to clean transactions.
- if (transactionRemoteFileToResume == null) {
- try {
- transferManager.cleanTransactions();
- }
- catch (BlockingTransfersException e) {
- stopBecauseOfBlockingTransactions();
- return result;
+ if (!resuming) {
+ startIndexerThread(databaseVersionQueue);
}
- }
- int numberOfPerformedTransactions = 0;
- if (resuming) {
- numberOfPerformedTransactions = executeTransactions(databaseVersionQueue, remoteTransactionsToResume.iterator(), transactionRemoteFileToResume);
+ // If we are not resuming from a remote transaction, we need to clean transactions.
+ if (transactionRemoteFileToResume == null) {
+ transferManager.cleanTransactions();
+ }
}
- else {
- numberOfPerformedTransactions = executeTransactions(databaseVersionQueue);
+ catch (BlockingTransfersException e) {
+ stopBecauseOfBlockingTransactions();
+ return result;
}
- // Check if anything has happened.
- if (numberOfPerformedTransactions == 0) {
- logger.log(Level.INFO, "Local database is up-to-date. NOTHING TO DO!");
- result.setResultCode(UpResultCode.OK_NO_CHANGES);
- } else {
- logger.log(Level.INFO, "Sync up done.");
- result.setResultCode(UpResultCode.OK_CHANGES_UPLOADED);
- }
+ // Go wild
+ int numberOfPerformedTransactions = executeTransactions();
+ updateResult(numberOfPerformedTransactions);
// Close database connection
localDatabase.finalize();
@@ -218,17 +174,62 @@ public class UpOperation extends AbstractTransferOperation {
// Finish 'up' before 'cleanup' starts
finishOperation();
fireEndEvent();
+
return result;
}
- /**
- * Transfers the given {@link DatabaseVersion} objects to the remote.
- * Each {@link DatabaseVersion} will be transferred in its own {@link RemoteTransaction} object.
- *
- * @param databaseVersions The {@link DatabaseVersion} objects to send to the remote.
- */
- private int executeTransactions(BlockingQueue<DatabaseVersion> databaseVersionQueue) throws Exception {
- return executeTransactions(databaseVersionQueue, null, null);
+ private void updateResult(int numberOfPerformedTransactions) {
+ if (numberOfPerformedTransactions == 0) {
+ logger.log(Level.INFO, "Local database is up-to-date. NOTHING TO DO!");
+ result.setResultCode(UpResultCode.OK_NO_CHANGES);
+ }
+ else {
+ logger.log(Level.INFO, "Sync up done.");
+ result.setResultCode(UpResultCode.OK_CHANGES_UPLOADED);
+ }
+ }
+
+ private void startIndexerThread(BlockingQueue<DatabaseVersion> databaseVersionQueue) {
+ // Get a list of files that have been updated
+ ChangeSet localChanges = result.getStatusResult().getChangeSet();
+ List<File> locallyUpdatedFiles = extractLocallyUpdatedFiles(localChanges);
+ List<File> locallyDeletedFiles = extractLocallyDeletedFiles(localChanges);
+
+ // Iterate over the changes, deduplicate, and feed DatabaseVersions into an iterator
+ Deduper deduper = new Deduper(config.getChunker(), config.getMultiChunker(), config.getTransformer(), options.getTransactionSizeLimit(),
+ options.getTransactionFileLimit());
+
+ AsyncIndexer asyncIndexer = new AsyncIndexer(config, deduper, locallyUpdatedFiles, locallyDeletedFiles, databaseVersionQueue);
+ new Thread(asyncIndexer, "AsyncI/" + config.getLocalDir().getName()).start();
+ }
+
+ private void prepareResume() throws Exception {
+ Collection<Long> versionsToResume = transferManager.loadPendingTransactionList();
+ boolean hasVersionsToResume = versionsToResume != null && versionsToResume.size() > 0;
+
+ if (hasVersionsToResume) {
+ logger.log(Level.INFO, "Found local transaction to resume.");
+ logger.log(Level.INFO, "Attempting to find transactionRemoteFile");
+
+ remoteTransactionsToResume = attemptResumeTransactions(versionsToResume);
+ Collection<DatabaseVersion> remoteDatabaseVersionsToResume = attemptResumeDatabaseVersions(versionsToResume);
+
+ resuming = remoteDatabaseVersionsToResume != null && remoteTransactionsToResume != null &&
+ remoteDatabaseVersionsToResume.size() == remoteTransactionsToResume.size();
+
+ if (resuming) {
+ databaseVersionQueue.addAll(remoteDatabaseVersionsToResume);
+ databaseVersionQueue.add(new DatabaseVersion()); // Empty database version is the stopping marker
+
+ transactionRemoteFileToResume = attemptResumeTransactionRemoteFile();
+ }
+ else {
+ transferManager.clearResumableTransactions();
+ }
+ }
+ else {
+ transferManager.clearResumableTransactions();
+ }
}
/**
@@ -246,16 +247,9 @@ public class UpOperation extends AbstractTransferOperation {
* @param remoteTransactionsToResume {@link RemoteTransaction} objects that correspond to the given {@link DatabaseVersion} objects.
* @param transactionRemoteFileToResume The file on the remote that was used for the specific transaction that was interrupted.
*/
- private int executeTransactions(BlockingQueue<DatabaseVersion> databaseVersionQueue, Iterator<RemoteTransaction> remoteTransactionsToResume,
- TransactionRemoteFile transactionRemoteFileToResume)
- throws Exception {
+ private int executeTransactions() throws Exception {
+ Iterator<RemoteTransaction> remoteTransactionsToResumeIterator = (resuming) ? remoteTransactionsToResume.iterator() : null;
- boolean resuming = true;
-
- if (remoteTransactionsToResume == null) {
- resuming = false;
- }
-
// At this point, if a failure occurs from which we can resume, new transaction files will be written
// Delete any old transaction files
transferManager.clearPendingTransactions();
@@ -267,14 +261,17 @@ public class UpOperation extends AbstractTransferOperation {
DatabaseVersion databaseVersion = databaseVersionQueue.take();
boolean noDatabaseVersions = databaseVersion.isEmpty();
+
// Add dirty data to first database
addDirtyData(databaseVersion);
//
while (!databaseVersion.isEmpty()) {
RemoteTransaction remoteTransaction = null;
+
if (!resuming) {
VectorClock newVectorClock = findNewVectorClock();
+
databaseVersion.setVectorClock(newVectorClock);
databaseVersion.setTimestamp(new Date());
databaseVersion.setClient(config.getMachineName());
@@ -283,11 +280,12 @@ public class UpOperation extends AbstractTransferOperation {
// Add multichunks to transaction
logger.log(Level.INFO, "Uploading new multichunks ...");
+
// This call adds newly changed chunks to a "RemoteTransaction", so they can be uploaded later.
addMultiChunksToTransaction(remoteTransaction, databaseVersion.getMultiChunks());
}
else {
- remoteTransaction = remoteTransactionsToResume.next();
+ remoteTransaction = remoteTransactionsToResumeIterator.next();
}
logger.log(Level.INFO, "Uploading database: " + databaseVersion);
@@ -395,6 +393,7 @@ public class UpOperation extends AbstractTransferOperation {
else {
transactionRemoteFile = transactions.get(0);
}
+
return transactionRemoteFile;
}
@@ -674,6 +673,7 @@ public class UpOperation extends AbstractTransferOperation {
private void addLocalDatabaseToTransaction(RemoteTransaction remoteTransaction, File localDatabaseFile, DatabaseRemoteFile remoteDatabaseFile)
throws InterruptedException,
StorageException {
+
logger.log(Level.INFO, "- Uploading " + localDatabaseFile + " to " + remoteDatabaseFile + " ...");
remoteTransaction.upload(localDatabaseFile, remoteDatabaseFile);
}
@@ -727,56 +727,70 @@ public class UpOperation extends AbstractTransferOperation {
return newVectorClock;
}
- private Collection<RemoteTransaction> attemptResumeTransactions(Collection<Long> versions) throws Exception {
- Collection<RemoteTransaction> remoteTransactions = new ArrayList<>();
- for (Long version : versions) {
- File transactionFile = config.getTransactionFile(version);
+ private Collection<RemoteTransaction> attemptResumeTransactions(Collection<Long> versions) {
+ try {
+ Collection<RemoteTransaction> remoteTransactions = new ArrayList<>();
- // If a single transaction file is missing, we should restart
- if (!transactionFile.exists()) {
- return null;
- }
+ for (Long version : versions) {
+ File transactionFile = config.getTransactionFile(version);
+
+ // If a single transaction file is missing, we should restart
+ if (!transactionFile.exists()) {
+ return null;
+ }
- TransactionTO transactionTO = TransactionTO.load(null, transactionFile);
+ TransactionTO transactionTO = TransactionTO.load(null, transactionFile);
- // Verify if all files needed are in cache.
- for (ActionTO action : transactionTO.getActions()) {
- if (action.getType() == ActionType.UPLOAD) {
- if (action.getStatus() == ActionStatus.UNSTARTED) {
- if (!action.getLocalTempLocation().exists()) {
- // Unstarted upload has no cached local copy, abort
- return null;
+ // Verify if all files needed are in cache.
+ for (ActionTO action : transactionTO.getActions()) {
+ if (action.getType() == ActionType.UPLOAD) {
+ if (action.getStatus() == ActionStatus.UNSTARTED) {
+ if (!action.getLocalTempLocation().exists()) {
+ // Unstarted upload has no cached local copy, abort
+ return null;
+ }
}
}
}
- }
- remoteTransactions.add(new RemoteTransaction(config, transferManager, transactionTO));
+ remoteTransactions.add(new RemoteTransaction(config, transferManager, transactionTO));
+ }
+
+ return remoteTransactions;
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Invalid transaction file. Cannot resume!");
+ return null;
}
- return remoteTransactions;
}
private Collection<DatabaseVersion> attemptResumeDatabaseVersions(Collection<Long> versions) throws Exception {
- Collection<DatabaseVersion> databaseVersions = new ArrayList<>();
- for (Long version : versions) {
- File databaseFile = config.getTransactionDatabaseFile(version);
+ try {
+ Collection<DatabaseVersion> databaseVersions = new ArrayList<>();
+
+ for (Long version : versions) {
+ File databaseFile = config.getTransactionDatabaseFile(version);
- // If a single database file is missing, we should restart
- if (!databaseFile.exists()) {
- return null;
- }
+ // If a single database file is missing, we should restart
+ if (!databaseFile.exists()) {
+ return null;
+ }
- DatabaseXmlSerializer databaseSerializer = new DatabaseXmlSerializer();
- MemoryDatabase memoryDatabase = new MemoryDatabase();
- databaseSerializer.load(memoryDatabase, databaseFile, null, null, DatabaseReadType.FULL);
+ DatabaseXmlSerializer databaseSerializer = new DatabaseXmlSerializer();
+ MemoryDatabase memoryDatabase = new MemoryDatabase();
+ databaseSerializer.load(memoryDatabase, databaseFile, null, null, DatabaseReadType.FULL);
- if (memoryDatabase.getDatabaseVersions().size() == 0) {
- return null;
- }
+ if (memoryDatabase.getDatabaseVersions().size() == 0) {
+ return null;
+ }
- databaseVersions.add(memoryDatabase.getLastDatabaseVersion());
+ databaseVersions.add(memoryDatabase.getLastDatabaseVersion());
+ }
+
+ return databaseVersions;
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Cannot load database versions from 'state'. Cannot resume.");
+ return null;
}
- return databaseVersions;
}
/**
diff --git a/syncany-lib/src/main/java/org/syncany/plugins/local/LocalTransferManager.java b/syncany-lib/src/main/java/org/syncany/plugins/local/LocalTransferManager.java
index 1422059..e7ee603 100644
--- a/syncany-lib/src/main/java/org/syncany/plugins/local/LocalTransferManager.java
+++ b/syncany-lib/src/main/java/org/syncany/plugins/local/LocalTransferManager.java
@@ -229,7 +229,7 @@ public class LocalTransferManager extends AbstractTransferManager {
}
catch (StorageException e) {
logger.log(Level.INFO, "Cannot create instance of " + remoteFileClass.getSimpleName() + " for file " + path
- + "; maybe invalid file name pattern. Ignoring file.", e);
+ + "; maybe invalid file name pattern. Ignoring file.");
}
}
}
diff --git a/syncany-lib/src/main/java/org/syncany/plugins/transfer/features/TransactionAwareFeatureTransferManager.java b/syncany-lib/src/main/java/org/syncany/plugins/transfer/features/TransactionAwareFeatureTransferManager.java
index bc5c582..b5be175 100644
--- a/syncany-lib/src/main/java/org/syncany/plugins/transfer/features/TransactionAwareFeatureTransferManager.java
+++ b/syncany-lib/src/main/java/org/syncany/plugins/transfer/features/TransactionAwareFeatureTransferManager.java
@@ -279,16 +279,25 @@ public class TransactionAwareFeatureTransferManager implements FeatureTransferMa
*/
public Collection<Long> loadPendingTransactionList() throws IOException {
Objects.requireNonNull(config, "Cannot read pending transaction list if config is null.");
+
Collection<Long> databaseVersionNumbers = new ArrayList<>();
File transactionListFile = config.getTransactionListFile();
+
if (!transactionListFile.exists()) {
return Collections.emptyList();
}
Collection<String> transactionLines = Files.readAllLines(transactionListFile.toPath(), Charset.forName("UTF-8"));
+
for (String transactionLine : transactionLines) {
- databaseVersionNumbers.add(Long.parseLong(transactionLine));
+ try {
+ databaseVersionNumbers.add(Long.parseLong(transactionLine));
+ } catch (NumberFormatException e) {
+ logger.log(Level.WARNING, "Cannot parse line in transaction list: " + transactionLine + ". Cannot resume.");
+ return Collections.emptyList();
+ }
}
+
return databaseVersionNumbers;
}
diff --git a/syncany-lib/src/test/integration/java/org/syncany/tests/ScenarioTestSuite.java b/syncany-lib/src/test/integration/java/org/syncany/tests/ScenarioTestSuite.java
index f24be0d..64d7d04 100644
--- a/syncany-lib/src/test/integration/java/org/syncany/tests/ScenarioTestSuite.java
+++ b/syncany-lib/src/test/integration/java/org/syncany/tests/ScenarioTestSuite.java
@@ -48,6 +48,8 @@ import org.syncany.tests.integration.scenarios.Issue288ScenarioTest;
import org.syncany.tests.integration.scenarios.Issue303ScenarioTest;
import org.syncany.tests.integration.scenarios.Issue316ScenarioTest;
import org.syncany.tests.integration.scenarios.Issue374Pre1965DateScenarioTest;
+import org.syncany.tests.integration.scenarios.Issue429ScenarioTest;
+import org.syncany.tests.integration.scenarios.Issue520NoResumeOnCorruptXmlScenarioTest;
import org.syncany.tests.integration.scenarios.ManyRenamesScenarioTest;
import org.syncany.tests.integration.scenarios.ManySyncUpsAndDatabaseFileCleanupScenarioTest;
import org.syncany.tests.integration.scenarios.ManySyncUpsAndOtherClientSyncDownScenarioTest;
@@ -94,6 +96,8 @@ import org.syncany.tests.integration.scenarios.SymlinkSyncScenarioTest;
Issue303ScenarioTest.class,
Issue316ScenarioTest.class,
Issue374Pre1965DateScenarioTest.class,
+ Issue429ScenarioTest.class,
+ Issue520NoResumeOnCorruptXmlScenarioTest.class,
ManyRenamesScenarioTest.class,
ManySyncUpsAndDatabaseFileCleanupScenarioTest.class,
ManySyncUpsLargeFileScenarioTest.class,
diff --git a/syncany-lib/src/test/integration/java/org/syncany/tests/integration/operations/FileSystemActionComparatorTest.java b/syncany-lib/src/test/integration/java/org/syncany/tests/integration/operations/FileSystemActionComparatorTest.java
index 1648abc..7267076 100644
--- a/syncany-lib/src/test/integration/java/org/syncany/tests/integration/operations/FileSystemActionComparatorTest.java
+++ b/syncany-lib/src/test/integration/java/org/syncany/tests/integration/operations/FileSystemActionComparatorTest.java
@@ -83,7 +83,7 @@ public class FileSystemActionComparatorTest {
private NewFileSystemAction createNewFileSystemAction(String path, FileType type) throws Exception {
FileVersion firstFileVersion = createFileVersion(path, type);
- return new NewFileSystemAction(createDummyConfig(), firstFileVersion, null);
+ return new NewFileSystemAction(createDummyConfig(), null, null, firstFileVersion);
}
private RenameFileSystemAction createRenameFileSystemAction(String fromPath, String toPath, FileType type) throws Exception {
diff --git a/syncany-lib/src/test/integration/java/org/syncany/tests/integration/scenarios/Issue520NoResumeOnCorruptXmlScenarioTest.java b/syncany-lib/src/test/integration/java/org/syncany/tests/integration/scenarios/Issue520NoResumeOnCorruptXmlScenarioTest.java
new file mode 100644
index 0000000..ed1737d
--- /dev/null
+++ b/syncany-lib/src/test/integration/java/org/syncany/tests/integration/scenarios/Issue520NoResumeOnCorruptXmlScenarioTest.java
@@ -0,0 +1,77 @@
+/*
+ * Syncany, www.syncany.org
+ * Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel at gmail.com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+package org.syncany.tests.integration.scenarios;
+
+import java.io.File;
+
+import org.junit.Test;
+import org.syncany.plugins.local.LocalTransferSettings;
+import org.syncany.tests.unit.util.TestFileUtil;
+import org.syncany.tests.util.TestAssertUtil;
+import org.syncany.tests.util.TestClient;
+import org.syncany.tests.util.TestConfigUtil;
+
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class Issue520NoResumeOnCorruptXmlScenarioTest {
+ @Test
+ public void testCorruptTransactionListFile() throws Exception {
+ // Setup
+ LocalTransferSettings testConnection = (LocalTransferSettings) TestConfigUtil.createTestLocalConnection();
+
+ TestClient clientA = new TestClient("A", testConnection);
+
+ clientA.createNewFile("file1.txt", 1024);
+ clientA.upWithForceChecksum();
+
+ TestFileUtil.createFileWithContent(new File(clientA.getConfig().getAppDir(), "/state/transaction-list.txt"), "INVALID");
+
+ clientA.createNewFile("file2.txt", 1024);
+ clientA.upWithForceChecksum(); // This did FAIL due to an XML parsing exception
+
+ assertEquals("There should be exactly two database files", 2, new File(testConnection.getPath() + "/databases").listFiles().length);
+ assertEquals("There should be exactly two multichunks", 2, new File(testConnection.getPath() + "/multichunks").listFiles().length);
+
+ // Tear down
+ clientA.deleteTestData();
+ }
+
+ @Test
+ public void testCorruptTransactionFile() throws Exception {
+ // Setup
+ LocalTransferSettings testConnection = (LocalTransferSettings) TestConfigUtil.createTestLocalConnection();
+
+ TestClient clientA = new TestClient("A", testConnection);
+
+ clientA.createNewFile("file1.txt", 1024);
+ clientA.upWithForceChecksum();
+
+ TestFileUtil.createFileWithContent(new File(clientA.getConfig().getAppDir(), "/state/transaction-list.txt"), "0000000001");
+ TestFileUtil.createFileWithContent(new File(clientA.getConfig().getAppDir(), "/state/transaction-database.0000000001.xml"), "invalid");
+
+ clientA.createNewFile("file2.txt", 1024);
+ clientA.upWithForceChecksum(); // This did FAIL due to an XML parsing exception
+
+ assertEquals("There should be exactly two database files", 2, new File(testConnection.getPath() + "/databases").listFiles().length);
+ assertEquals("There should be exactly two multichunks", 2, new File(testConnection.getPath() + "/multichunks").listFiles().length);
+
+ // Tear down
+ clientA.deleteTestData();
+ }
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/syncany.git
More information about the pkg-java-commits
mailing list