Package org.syncany.plugins.transfer

Source Code of org.syncany.plugins.transfer.RemoteTransaction$TransactionStats

/*
* Syncany, www.syncany.org
* Copyright (C) 2011-2014 Philipp C. Heckel <philipp.heckel@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.plugins.transfer;

import org.syncany.config.Config;
import org.syncany.config.LocalEventBus;
import org.syncany.operations.daemon.messages.UpUploadFileInTransactionSyncExternalEvent;
import org.syncany.operations.daemon.messages.UpUploadFileSyncExternalEvent;
import org.syncany.plugins.transfer.files.RemoteFile;
import org.syncany.plugins.transfer.files.TempRemoteFile;
import org.syncany.plugins.transfer.files.TransactionRemoteFile;
import org.syncany.plugins.transfer.to.ActionTO;
import org.syncany.plugins.transfer.to.TransactionTO;

import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* This class represents a transaction in a remote system. It will keep track of
* what files are to be added and ensures atomic operation.
*
* @author Pim Otte
*/
public class RemoteTransaction {
  private static final Logger logger = Logger.getLogger(RemoteTransaction.class.getSimpleName());

  private TransferManager transferManager;
  private Config config;
  private TransactionTO transactionTO;

  private LocalEventBus eventBus;

  public RemoteTransaction(Config config, TransferManager transferManager) {
    this.config = config;
    this.transferManager = transferManager;
    this.transactionTO = new TransactionTO(config.getMachineName());
    this.eventBus = LocalEventBus.getInstance();
  }

  /**
   * Returns whether the transaction is empty.
   */
  public boolean isEmpty() {
    return transactionTO.getActions().size() == 0;
  }

  /**
   * Adds a file to this transaction. Generates a temporary file to store it.
   */
  public void upload(File localFile, RemoteFile remoteFile) throws StorageException {
    TempRemoteFile temporaryRemoteFile = new TempRemoteFile(remoteFile);

    logger.log(Level.INFO, "- Adding file to TX for UPLOAD: " + localFile + " -> Temp. remote file: " + temporaryRemoteFile
        + ", final location: " + remoteFile);

    ActionTO action = new ActionTO();
    action.setType(ActionTO.TYPE_UPLOAD);
    action.setLocalTempLocation(localFile);
    action.setRemoteLocation(remoteFile);
    action.setRemoteTempLocation(temporaryRemoteFile);

    transactionTO.addAction(action);
  }

  /**
   * Adds the deletion of a file to this transaction. Generates a temporary file
   * to store it while the transaction is being finalized.
   */
  public void delete(RemoteFile remoteFile) throws StorageException {
    TempRemoteFile temporaryRemoteFile = new TempRemoteFile(remoteFile);

    logger.log(Level.INFO, "- Adding file to TX for DELETE: " + remoteFile + "-> Temp. remote file: " + temporaryRemoteFile);

    ActionTO action = new ActionTO();
    action.setType(ActionTO.TYPE_DELETE);
    action.setRemoteLocation(remoteFile);
    action.setRemoteTempLocation(temporaryRemoteFile);

    transactionTO.addAction(action);
  }

  /**
   * Commits this transaction by performing the required upload and
   * delete operations. The method first moves all files to the temporary
   * remote location. If no errors occur, all files are moved to their
   * final location.
   *
   * <p>The method first writes a {@link TransactionRemoteFile} containing
   * all actions to be performed and then uploads this file. Then it uploads
   * new files (added by {@link #upload(File, RemoteFile) upload()} and moves
   * deleted files to a temporary location (deleted by {@link #delete(RemoteFile) delete()}.
   *
   * <p>If this was successful, the transaction file is deleted and the
   * temporary files. After deleting the transaction file, the transaction
   * is successfully committed.
   */
  public void commit() throws StorageException {
    logger.log(Level.INFO, "Starting TX.commit() ...");

    if (isEmpty()) {
      logger.log(Level.INFO, "- Empty transaction, not committing anything.");
      return;
    }

    File localTransactionFile = writeLocalTransactionFile();
    TransactionRemoteFile remoteTransactionFile = uploadTransactionFile(localTransactionFile);

    uploadAndMoveToTempLocation();
    moveToFinalLocation();

    deleteTransactionFile(localTransactionFile, remoteTransactionFile);
    deleteTempRemoteFiles();
  }

  private File writeLocalTransactionFile() throws StorageException {
    try {
      File localTransactionFile = config.getCache().createTempFile("transaction");

      transactionTO.save(config.getTransformer(), localTransactionFile);
      logger.log(Level.INFO, "Wrote transaction manifest to temporary file: " + localTransactionFile);

      return localTransactionFile;
    }
    catch (Exception e) {
      throw new StorageException("Could not create temporary file for transaction", e);
    }
  }

  private TransactionRemoteFile uploadTransactionFile(File localTransactionFile) throws StorageException {
    TransactionRemoteFile remoteTransactionFile = new TransactionRemoteFile(this);

    eventBus.post(new UpUploadFileSyncExternalEvent(config.getLocalDir().getAbsolutePath(), remoteTransactionFile.getName()));

    logger.log(Level.INFO, "- Uploading remote transaction file {0} ...", remoteTransactionFile);
    transferManager.upload(localTransactionFile, remoteTransactionFile);

    return remoteTransactionFile;
  }

  private void uploadAndMoveToTempLocation() throws StorageException {
    TransactionStats stats = gatherTransactionStats();
    int uploadFileIndex = 0;

    for (ActionTO action : transactionTO.getActions()) {
      RemoteFile tempRemoteFile = action.getTempRemoteFile();

      if (action.getType().equals(ActionTO.TYPE_UPLOAD)) {
        File localFile = action.getLocalTempLocation();
        long localFileSize = localFile.length();

        eventBus.post(new UpUploadFileInTransactionSyncExternalEvent(config.getLocalDir().getAbsolutePath(), ++uploadFileIndex,
            stats.totalUploadFileCount, localFileSize, stats.totalUploadSize));

        logger.log(Level.INFO, "- Uploading {0} to temp. file {1} ...", new Object[] { localFile, tempRemoteFile });
        transferManager.upload(localFile, tempRemoteFile);
      }
      else if (action.getType().equals(ActionTO.TYPE_DELETE)) {
        RemoteFile remoteFile = action.getRemoteFile();

        try {
          logger.log(Level.INFO, "- Moving {0} to temp. file {1} ...", new Object[] { remoteFile, tempRemoteFile });
          transferManager.move(remoteFile, tempRemoteFile);
        }
        catch (StorageMoveException e) {
          logger.log(Level.INFO, "  -> FAILED (don't care!), because the remoteFile does not exist: " + remoteFile);
        }
      }
    }
  }

  private TransactionStats gatherTransactionStats() {
    TransactionStats stats = new TransactionStats();

    for (ActionTO action : transactionTO.getActions()) {
      if (action.getType().equals(ActionTO.TYPE_UPLOAD)) {
        stats.totalUploadFileCount++;
        stats.totalUploadSize += action.getLocalTempLocation().length();
      }
    }

    return stats;
  }

  private void moveToFinalLocation() throws StorageException {
    for (ActionTO action : transactionTO.getActions()) {
      if (action.getType().equals(ActionTO.TYPE_UPLOAD)) {
        RemoteFile tempRemoteFile = action.getTempRemoteFile();
        RemoteFile finalRemoteFile = action.getRemoteFile();

        logger.log(Level.INFO, "- Moving temp. file {0} to final location {1} ...", new Object[] { tempRemoteFile, finalRemoteFile });
        transferManager.move(tempRemoteFile, finalRemoteFile);
      }
    }
  }

  private void deleteTransactionFile(File localTransactionFile, TransactionRemoteFile remoteTransactionFile) throws StorageException {
    // After this deletion, the transaction is final!
    logger.log(Level.INFO, "- Deleting remote transaction file {0} ...", remoteTransactionFile);

    transferManager.delete(remoteTransactionFile);
    localTransactionFile.delete();

    logger.log(Level.INFO, "END of TX.commmit(): Succesfully committed transaction.");
  }

  private void deleteTempRemoteFiles() throws StorageException {
    // Actually deleting remote files is done after finishing the transaction, because
    // it cannot be rolled back! If this fails, the temporary files will eventually
    // be cleaned up by CleanUp and download will not download these, because
    // they are not in any transaction file.

    for (ActionTO action : transactionTO.getActions()) {
      if (action.getType().equals(ActionTO.TYPE_DELETE)) {
        RemoteFile tempRemoteFile = action.getTempRemoteFile();

        logger.log(Level.INFO, "- Deleting temp. file {0}  ...", new Object[] { tempRemoteFile });
        transferManager.delete(tempRemoteFile);
      }
    }

    logger.log(Level.INFO, "END of TX.delTemp(): Sucessfully deleted final files.");
  }

  private class TransactionStats {
    private long totalUploadSize;
    private int totalUploadFileCount;
  }
}
TOP

Related Classes of org.syncany.plugins.transfer.RemoteTransaction$TransactionStats

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.