/******************************************************************************* * Copyright (c) 2012 GigaSpaces Technologies Ltd. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. *******************************************************************************/ package org.cloudifysource.esc.installer.filetransfer; import java.io.File; import java.util.List; import java.util.Set; import java.util.concurrent.TimeoutException; import org.apache.commons.vfs2.AllFileSelector; import org.apache.commons.vfs2.FileDepthSelector; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSelectInfo; import org.apache.commons.vfs2.FileSelector; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.FileSystemOptions; import org.apache.commons.vfs2.FileType; import org.cloudifysource.domain.cloud.CloudTemplateInstallerConfiguration; import org.cloudifysource.esc.installer.InstallationDetails; import org.cloudifysource.esc.installer.InstallerException; /********* * A base class for commons-vfs based file transfer. * * @author barakme * @since 2.5.0 * */ public abstract class VfsFileTransfer implements FileTransfer { protected static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(VfsFileTransfer.class .getName()); protected FileSystemManager fileSystemManager; protected FileObject localDir; protected FileObject remoteDir; protected String host; protected String targetURI; protected FileSystemOptions opts; protected boolean deleteRemoteDirectoryContents = false; protected CloudTemplateInstallerConfiguration installerConfiguration; /****** * Checks if the specified end time has reached. * * @param endTimeMillis * the end time. * @throws TimeoutException * if the target time has passed. */ protected void checkTimeout(final long endTimeMillis) throws TimeoutException { if (System.currentTimeMillis() > endTimeMillis) { throw new TimeoutException("File transfer operation exceeded timeout"); } } /**** * Closes the local and remote file system. */ public void shutdown() { fileSystemManager.closeFileSystem(remoteDir.getFileSystem()); fileSystemManager.closeFileSystem(localDir.getFileSystem()); } @Override public void copyFiles(final InstallationDetails details, final Set<String> excludedFiles, final List<File> additionalFiles, final long endTimeMillis) throws TimeoutException, InstallerException { logger.fine("Copying files to: " + host + " from local dir: " + localDir.getName().getPath() + " excluding " + excludedFiles.toString()); try { if (remoteDir.exists()) { FileType type = remoteDir.getType(); if (!type.equals(FileType.FOLDER)) { throw new InstallerException("The remote location: " + remoteDir.getName().getFriendlyURI() + " exists but is not a directory"); } if (deleteRemoteDirectoryContents) { logger.info("Deleting contents of remote directory: " + remoteDir.getName().getFriendlyURI()); remoteDir.delete(new FileDepthSelector(1, Integer.MAX_VALUE)); } FileObject[] children = remoteDir.getChildren(); if (children.length > 0) { throw new InstallerException("The remote directory: " + remoteDir.getName().getFriendlyURI() + " is not empty"); } } remoteDir.copyFrom(localDir, new FileSelector() { @Override public boolean includeFile(final FileSelectInfo fileInfo) throws Exception { if (excludedFiles.contains(fileInfo.getFile().getName().getBaseName())) { logger.fine(fileInfo.getFile().getName().getBaseName() + " excluded"); return false; } final FileObject remoteFile = fileSystemManager.resolveFile(remoteDir, localDir.getName().getRelativeName(fileInfo.getFile().getName())); if (!remoteFile.exists()) { logger.fine(fileInfo.getFile().getName().getBaseName() + " missing on server"); return true; } if (fileInfo.getFile().getType() == FileType.FILE) { final long remoteSize = remoteFile.getContent().getSize(); final long localSize = fileInfo.getFile().getContent().getSize(); final boolean res = localSize != remoteSize; if (res) { logger.fine(fileInfo.getFile().getName().getBaseName() + " different on server"); } return res; } return false; } @Override public boolean traverseDescendents(final FileSelectInfo fileInfo) throws Exception { return true; } }); for (final File file : additionalFiles) { logger.fine("copying file: " + file.getAbsolutePath() + " to remote directory"); final FileObject fileObject = fileSystemManager.resolveFile("file:" + file.getAbsolutePath()); final FileObject remoteFile = remoteDir.resolveFile(file.getName()); remoteFile.copyFrom(fileObject, new AllFileSelector()); } logger.fine("Copying files to: " + host + " completed."); } catch (final FileSystemException e) { throw new InstallerException("Failed to copy files to remote host " + host + ": " + e.getMessage(), e); } checkTimeout(endTimeMillis); } @Override public void initialize(final InstallationDetails details, final long endTimeMillis) throws TimeoutException, InstallerException { this.installerConfiguration = details.getInstallerConfiguration(); this.deleteRemoteDirectoryContents = details.isDeleteRemoteDirectoryContents(); if (details.isConnectedToPrivateIp()) { host = details.getPrivateIp(); } else { host = details.getPublicIp(); } checkTimeout(endTimeMillis); initVFSManager(details, endTimeMillis); createTargetURI(details); final FileSystemManager mng = fileSystemManager; mng.setLogger(org.apache.commons.logging.LogFactory.getLog(logger.getName())); // when bootstrapping a management machine, pass all of the cloud // configuration, including all template // for an agent machine, just pass the upload dir fot the specific // template. String localDirPath = details.getLocalDir(); if (details.isManagement()) { if (details.getCloudFile() == null) { throw new IllegalArgumentException("While bootstrapping a management machine, cloud file is null"); } localDirPath = details.getCloudFile().getParentFile().getAbsolutePath(); } try { localDir = mng.resolveFile("file:" + localDirPath); remoteDir = resolveTargetDirectory(opts, targetURI, mng); } catch (final FileSystemException e) { throw new InstallerException("Failed to set up file transfer: " + e.getMessage(), e); } } private FileObject resolveTargetDirectory(final FileSystemOptions opts, final String target, final FileSystemManager mng) throws FileSystemException { FileSystemException lastException = null; for (int i = 0; i < installerConfiguration.getFileTransferRetries(); ++i) { try { final FileObject targetDirectory = mng.resolveFile(target, opts); logger.fine("Remote directory resolved successfully."); return targetDirectory; } catch (final FileSystemException fse) { logger.fine("Attempt number: " + (i + 1) + " to reslve remote directory failed." + " This may be a temporary issue while remote machine is starting up."); try { Thread.sleep(installerConfiguration.getFileTransferConnectionRetryIntervalMillis()); } catch (final InterruptedException e) { // ignore } lastException = fse; } } throw lastException; } /********* * Initialize the VFS manager with the required settings. * * @param details * the installation details. * @param endTimeMillis * max end time for this operation. * @throws InstallerException * if the operation failed. */ protected abstract void initVFSManager(final InstallationDetails details, final long endTimeMillis) throws InstallerException; /****** * Creates the required URI so it will be available for use later. * * @param details * the installation details. * @throws InstallerException * if there was a problem. */ protected abstract void createTargetURI(InstallationDetails details) throws InstallerException; }