/* * Copyright (C) 2014 University of Dundee & Open Microscopy Environment. * All rights reserved. * * 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 2 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package ome.formats.importer.transfers; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import omero.ServerError; import omero.api.RawFileStorePrx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base {@link FileTransfer} implementation primarily providing the * {@link #start(TransferState)} and {@link #finish(TransferState, long)} * methods. Also used as the factory for {@link FileTransfer} implementations * via {@link #createTransfer(String)}. * * @since 5.0 */ public abstract class AbstractFileTransfer implements FileTransfer { /** * Enum of well-known {@link FileTransfer} names. * Note: these values are also in use in the fs.py * CLI plugin. */ public enum Transfers { ln(HardlinkFileTransfer.class), ln_rm(MoveFileTransfer.class), ln_s(SymlinkFileTransfer.class), cp(CopyFileTransfer.class), cp_rm(CopyMoveFileTransfer.class), upload(UploadFileTransfer.class), upload_rm(UploadRmFileTransfer.class); Class<?> kls; Transfers(Class<?> kls) { this.kls = kls; } } /** * Factory method for instantiating {@link FileTransfer} objects from * a string. Supported values can be found in the {@link Transfers} enum. * Otherwise, a FQN for a class on the classpath should be passed in. * @param arg a type of {@link FileTransfer} instance as named among {@link Transfers} * @return the new {@link FileTransfer} instance of the requested type */ public static FileTransfer createTransfer(String arg) { Logger tmp = LoggerFactory.getLogger(AbstractFileTransfer.class); tmp.debug("Loading file transfer class {}", arg); try { try { return (FileTransfer) Transfers.valueOf(arg).kls.newInstance(); } catch (Exception e) { // Assume not in the enum } Class<?> c = Class.forName(arg); return (FileTransfer) c.newInstance(); } catch (Exception e) { tmp.error("Failed to load file transfer class " + arg); throw new RuntimeException(e); } } protected final Logger log = LoggerFactory.getLogger(getClass()); /** * Minimal start method which logs the file, calls * {@link TransferState#start()}, and loads the {@link RawFileStorePrx} * which any implementation will need. * * @param state the transfer state * @return a raw file store proxy for the upload * @throws ServerError if the uploader could not be obtained */ protected RawFileStorePrx start(TransferState state) throws ServerError { log.info("Transferring {}...", state.getFile()); state.start(); return state.getUploader(); } /** * Save the current state to disk and finish all timing and logging. * * @param state non-null * @param offset total length transferred. * @return client-side digest string. * @throws ServerError if the upload could not be completed and checksummed */ protected String finish(TransferState state, long offset) throws ServerError { state.start(); state.save(); state.stop(); state.uploadComplete(offset); return state.getChecksum(); } /** * Utility method for closing resources. * * @param rawFileStore possibly null * @param stream possibly null * @throws ServerError presently not at all as errors are simply logged, but possibly in the future */ protected void cleanupUpload(RawFileStorePrx rawFileStore, FileInputStream stream) throws ServerError { try { if (rawFileStore != null) { try { rawFileStore.close(); } catch (Exception e) { log.debug("error in closing raw file store", e); } } } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { log.error("I/O in error closing stream", e); } } } } /** * Uses os.name to determine whether or not this JVM is running * under Windows. This is mostly used for determining which executables * to run. */ protected boolean isWindows() { return System.getProperty("os.name").startsWith("Windows"); } protected void printLine() { log.error("*******************************************"); } /** * Method used by subclasses during {@link FileTransfer#afterTransfer(int, List)} * if they would like to remove all the files transferred in the set. */ protected void deleteTransferredFiles(int errors, List<String> srcFiles) throws CleanupFailure { if (errors > 0) { printLine(); log.error("{} error(s) found.", errors); log.error("{} cleanup not performed!", getClass().getSimpleName()); log.error("The following files will *not* be deleted:"); for (String srcFile : srcFiles) { log.error("\t{}", srcFile); } printLine(); return; } List<File> failedFiles = new ArrayList<File>(); for (String path : srcFiles) { File srcFile = new File(path); try { log.info("Deleting source file {}...", srcFile); if (!srcFile.delete()) { throw new RuntimeException("Failed to delete."); } } catch (Exception e) { log.error("Failed to remove source file {}", srcFile); failedFiles.add(srcFile); } } if (!failedFiles.isEmpty()) { printLine(); log.error("Cleanup failed!"); log.error("{} files could not be removed and will need to " + "be handled manually", failedFiles.size()); for (File failedFile : failedFiles) { log.error("\t{}", failedFile.getAbsolutePath()); } printLine(); throw new CleanupFailure(failedFiles); } } }