private Database readWinnersDatabase(Branch winnersApplyBranch, List<File> remoteDatabases) throws IOException { // Make map 'short filename' -> 'full filename' Map<String, File> shortFilenameToFileMap = new HashMap<String, File>(); for (File remoteDatabase : remoteDatabases) { shortFilenameToFileMap.put(remoteDatabase.getName(), remoteDatabase); } // Load individual databases for branch ranges DatabaseDAO databaseDAO = new XmlDatabaseDAO(config.getTransformer()); Database winnerBranchDatabase = new Database(); // Database cannot be reused, since these might be different clients String clientName = null; VectorClock clientVersionFrom = null; VectorClock clientVersionTo = null; for (DatabaseVersionHeader databaseVersionHeader : winnersApplyBranch.getAll()) { // First of range for this client if (clientName == null || !clientName.equals(databaseVersionHeader.getClient())) { clientName = databaseVersionHeader.getClient(); clientVersionFrom = databaseVersionHeader.getVectorClock(); clientVersionTo = databaseVersionHeader.getVectorClock(); } // Still in range for this client else if (clientName.equals(databaseVersionHeader.getClient())) { clientVersionTo = databaseVersionHeader.getVectorClock(); } String potentialDatabaseShortFileNameForRange = "db-" + clientName + "-" + clientVersionTo.get(clientName); // TODO [medium] Naming stuff File databaseFileForRange = shortFilenameToFileMap.get(potentialDatabaseShortFileNameForRange); if (databaseFileForRange != null) { // Load database logger.log( Level.INFO, "- Loading " + databaseFileForRange + " (from " + clientVersionFrom + ", to " + clientVersionTo + ") ..."); databaseDAO.load( winnerBranchDatabase, databaseFileForRange, clientVersionFrom, clientVersionTo); // Reset range clientName = null; clientVersionFrom = null; clientVersionTo = null; } } return winnerBranchDatabase; }
private void applyWinnersBranch(Branch winnersBranch, List<File> unknownRemoteDatabasesInCache) throws Exception { Branch winnersApplyBranch = databaseReconciliator.findWinnersApplyBranch(localBranch, winnersBranch); logger.log(Level.INFO, "- Database versions to APPLY locally: " + winnersApplyBranch); if (winnersApplyBranch.size() == 0) { logger.log(Level.WARNING, " + Nothing to update. Nice!"); result.setResultCode(DownResultCode.OK_NO_REMOTE_CHANGES); } else { logger.log(Level.INFO, "- Loading winners database ..."); Database winnersDatabase = readWinnersDatabase(winnersApplyBranch, unknownRemoteDatabasesInCache); FileSystemActionReconciliator actionReconciliator = new FileSystemActionReconciliator(config, localDatabase, result); List<FileSystemAction> actions = actionReconciliator.determineFileSystemActions(winnersDatabase); Set<MultiChunkEntry> unknownMultiChunks = determineRequiredMultiChunks(actions, winnersDatabase); downloadAndDecryptMultiChunks(unknownMultiChunks); applyFileSystemActions(actions); // Add winners database to local database // Note: This must happen AFTER the file system stuff, because we compare the winners database // with the local database! for (DatabaseVersionHeader applyDatabaseVersionHeader : winnersApplyBranch.getAll()) { logger.log( Level.INFO, " + Applying database version " + applyDatabaseVersionHeader.getVectorClock()); DatabaseVersion applyDatabaseVersion = winnersDatabase.getDatabaseVersion(applyDatabaseVersionHeader.getVectorClock()); localDatabase.addDatabaseVersion(applyDatabaseVersion); } logger.log(Level.INFO, "- Saving local database to " + config.getDatabaseFile() + " ..."); saveLocalDatabase(localDatabase, config.getDatabaseFile()); result.setResultCode(DownResultCode.OK_WITH_REMOTE_CHANGES); } }
private void pruneConflictingLocalBranch(Branch winnersBranch) throws Exception { Branch localPruneBranch = databaseReconciliator.findLosersPruneBranch(localBranch, winnersBranch); logger.log(Level.INFO, "- Database versions to REMOVE locally: " + localPruneBranch); if (localPruneBranch.size() == 0) { logger.log(Level.INFO, " + Nothing to prune locally. No conflicts. Only updates. Nice!"); } else { // Load dirty database (if existent) logger.log(Level.INFO, " + Pruning databases locally ..."); Database dirtyDatabase = new Database(); for (DatabaseVersionHeader databaseVersionHeader : localPruneBranch.getAll()) { // Database version DatabaseVersion databaseVersion = localDatabase.getDatabaseVersion(databaseVersionHeader.getVectorClock()); dirtyDatabase.addDatabaseVersion(databaseVersion); // Remove database version locally logger.log(Level.INFO, " * Removing " + databaseVersionHeader + " ..."); localDatabase.removeDatabaseVersion(databaseVersion); DatabaseRemoteFile remoteFileToPrune = new DatabaseRemoteFile( "db-" + config.getMachineName() + "-" + databaseVersionHeader.getVectorClock().get(config.getMachineName())); logger.log(Level.INFO, " * Deleting remote database file " + remoteFileToPrune + " ..."); transferManager.delete(remoteFileToPrune); } logger.log( Level.INFO, " * Saving dirty database to " + config.getDirtyDatabaseFile() + " ..."); saveLocalDatabase(dirtyDatabase, config.getDirtyDatabaseFile()); } }
private Branches readUnknownDatabaseVersionHeaders(List<File> remoteDatabases) throws IOException { logger.log(Level.INFO, "Loading database headers, creating branches ..."); // Sort files (db-a-1 must be before db-a-2 !) Collections.sort( remoteDatabases); // TODO [medium] natural sort is a workaround, database file names should // be centrally managed, db-name-0000000009 avoids natural sort // Read database files Branches unknownRemoteBranches = new Branches(); DatabaseDAO dbDAO = new XmlDatabaseDAO(config.getTransformer()); for (File remoteDatabaseFileInCache : remoteDatabases) { Database remoteDatabase = new Database(); // Database cannot be reused, since these might be different clients RemoteDatabaseFile remoteDatabaseFile = new RemoteDatabaseFile(remoteDatabaseFileInCache); dbDAO.load( remoteDatabase, remoteDatabaseFile .getFile()); // TODO [medium] Performance: This is very, very, very inefficient, DB is // loaded and then discarded List<DatabaseVersion> remoteDatabaseVersions = remoteDatabase.getDatabaseVersions(); // Populate branches Branch remoteClientBranch = unknownRemoteBranches.getBranch(remoteDatabaseFile.getClientName(), true); for (DatabaseVersion remoteDatabaseVersion : remoteDatabaseVersions) { DatabaseVersionHeader header = remoteDatabaseVersion.getHeader(); remoteClientBranch.add(header); } } return unknownRemoteBranches; }