diff options
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.tcf.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java')
-rw-r--r-- | target_explorer/plugins/org.eclipse.tcf.te.tcf.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java | 594 |
1 files changed, 231 insertions, 363 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.tcf.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java b/target_explorer/plugins/org.eclipse.tcf.te.tcf.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java index 54e20fcd9..47223fec8 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.tcf.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.tcf.filesystem.core/src/org/eclipse/tcf/te/tcf/filesystem/core/internal/operations/OpUpload.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2012 Wind River Systems, Inc. and others. All rights reserved. + * Copyright (c) 2011, 2015 Wind River Systems, Inc. and others. All rights reserved. * This program and the accompanying materials are made available under the terms * of the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html @@ -9,423 +9,291 @@ *******************************************************************************/ package org.eclipse.tcf.te.tcf.filesystem.core.internal.operations; +import static java.text.MessageFormat.format; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URL; +import java.io.InputStream; +import java.io.OutputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.LinkedList; import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.ISafeRunnable; -import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.osgi.util.NLS; +import org.eclipse.tcf.protocol.IToken; +import org.eclipse.tcf.protocol.Protocol; +import org.eclipse.tcf.services.IFileSystem; +import org.eclipse.tcf.services.IFileSystem.DoneOpen; +import org.eclipse.tcf.services.IFileSystem.DoneStat; +import org.eclipse.tcf.services.IFileSystem.FileAttrs; +import org.eclipse.tcf.services.IFileSystem.FileSystemException; +import org.eclipse.tcf.services.IFileSystem.IFileHandle; import org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IConfirmCallback; -import org.eclipse.tcf.te.tcf.filesystem.core.internal.url.TcfURLConnection; -import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.CacheManager; +import org.eclipse.tcf.te.tcf.filesystem.core.internal.FSTreeNode; import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.FileState; import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.PersistenceManager; -import org.eclipse.tcf.te.tcf.filesystem.core.model.FSTreeNode; +import org.eclipse.tcf.te.tcf.filesystem.core.internal.utils.StatusHelper; import org.eclipse.tcf.te.tcf.filesystem.core.nls.Messages; +import org.eclipse.tcf.util.TCFFileOutputStream; /** - * Upload multiple files from local system to a remote system. + * Upload multiple files from local system to a remote system. */ -public class OpUpload extends OpStreamOp { - // The source files to be uploaded. - File[] srcFiles; - // The destination URLs to be uploaded to. - URL[] dstURLs; - // The confirm callback - IConfirmCallback confirmCallback; - // The parent folder map to search files that have same names. - Map<File, FSTreeNode> parentFolders; - // The files that are to be committed to its target file system. - FSTreeNode[] nodes; - - /** - * Constructor. - * - * @param srcFile The source file to be uploaded. - * @param dstURL The destination URL. - */ - public OpUpload(File srcFile, URL dstURL) { - this(new File[]{srcFile}, new URL[]{dstURL}); - } - - /** - * Constructor. - * - * @param srcFiles The source files to be uploaded. - * @param dstURLs The destination URLs. - */ - public OpUpload(File[] srcFiles, URL[] dstURLs) { - this(srcFiles, dstURLs, null); +public class OpUpload extends AbstractOperation { + private static class WorkItem { + final File fSource; + final FSTreeNode fDestination; + final boolean fDropToDestination; + WorkItem(File source, FSTreeNode destination, boolean isDrop) { + fSource = source; + fDestination = destination; + fDropToDestination = isDrop; + } } - - /** - * Constructor. - * - * @param sourceFiles The source files in the native file system to be uploaded. - * @param targetFolder The taret parent folder to upload these files to. - */ - public OpUpload(String[]sourceFiles, FSTreeNode targetFolder) { - this(sourceFiles, targetFolder, null); + + IConfirmCallback fConfirmCallback; + + LinkedList<WorkItem> fWork = new LinkedList<WorkItem>(); + private long fStartTime; + + public OpUpload(IConfirmCallback confirm) { + fConfirmCallback = confirm; } - - /** - * Constructor. - * - * @param sourceFiles The source files in the native file system to be uploaded. - * @param targetFolder The target parent folder to upload these files to. - * @param confirmCallback the confirmation callback to confirm overwriting. - */ - public OpUpload(File[] srcFiles, URL[] dstURLs, IConfirmCallback confirmCallback) { - this.srcFiles = srcFiles; - this.dstURLs = dstURLs; - this.confirmCallback = confirmCallback; + + public void addUpload(File source, FSTreeNode destinationFile) { + fWork.add(new WorkItem(source, destinationFile, false)); } - - /** - * Constructor that upload the local cache files of the specified nodes. - * - * @param nodes The nodes to be uploaded. - */ - public OpUpload(FSTreeNode... nodes) { - srcFiles = new File[nodes.length]; - dstURLs = new URL[nodes.length]; - for (int i = 0; i < nodes.length; i++) { - srcFiles[i] = CacheManager.getCacheFile(nodes[i]); - dstURLs[i] = nodes[i].getLocationURL(); - } - this.nodes = nodes; + + public void addDrop(File source, FSTreeNode destiniationFolder) { + fWork.add(new WorkItem(source, destiniationFolder, true)); } - - /** - * Create an instance with specified files, target folder and a callback. - * - * @param sourceFiles the source files being uploaded. - * @param targetFolder the target folder to upload the files to. - * @param callback the callback that is invoked after uploading. - */ - public OpUpload(String[] sourceFiles, FSTreeNode targetFolder, IConfirmCallback confirmCallback) { - this.confirmCallback = confirmCallback; - List<File> fileList = new ArrayList<File>(); - List<URL> urlList = new ArrayList<URL>(); - prepareDirStruct(sourceFiles, fileList, urlList, targetFolder); - srcFiles = fileList.toArray(new File[fileList.size()]); - dstURLs = urlList.toArray(new URL[urlList.size()]); - } - /* - * (non-Javadoc) - * @see org.eclipse.tcf.te.tcf.filesystem.internal.operations.FSOperation#run(org.eclipse.core.runtime.IProgressMonitor) - */ @Override - public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { - super.run(monitor); - try { - uploadFiles(srcFiles, dstURLs); - if(monitor.isCanceled()) throw new InterruptedException(); - } catch (MalformedURLException e) { - throw new InvocationTargetException(e); - } catch (IOException e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); + public IStatus doRun(IProgressMonitor monitor) { + fStartTime = System.currentTimeMillis(); + monitor.beginTask(getName(), IProgressMonitor.UNKNOWN); + while (!fWork.isEmpty()) { + IStatus s = runWorkItem(fWork.remove(), monitor); + if (!s.isOK()) + return s; } - } - /** - * Test if the specified file should be confirmed. - * - * @param file The file to be confirmed. - * @return true if it is. - */ - private boolean requireConfirmation(File file) { - return parentFolders != null && confirmCallback != null && !yes2All && confirmCallback.requires(file) && findNode(file) != null; + return Status.OK_STATUS; } - - /** - * Upload the specified file list to the specified locations, reporting the progress - * using the specified monitor. - * - * @param files The file list to be uploaded. - * @param urls The - * @param monitor - * @throws IOException - */ - private void uploadFiles(File[] files, URL[] urls) throws IOException { - // The buffer used to download the file. - byte[] data = new byte[DEFAULT_CHUNK_SIZE]; - // Calculate the total size. - long totalSize = 0; - for (File file:files) { - totalSize += file.length(); - } - // Calculate the chunk size of one percent. - int chunk_size = (int) totalSize / 100; - // The current reading percentage. - int percentRead = 0; - // The current length of read bytes. - long bytesRead = 0; - for (int i = 0; i < files.length && !monitor.isCanceled(); i++) { - File file = files[i]; - if (requireConfirmation(file)) { - int result = confirmCallback.confirms(file); - switch (result) { - case IConfirmCallback.YES: - break; - case IConfirmCallback.YES_TO_ALL: - yes2All = true; - break; - case IConfirmCallback.NO: - bytesRead += file.length(); - if (chunk_size != 0) { - int percent = (int) bytesRead / chunk_size; - if (percent != percentRead) { // Update the progress. - monitor.worked(percent - percentRead); - percentRead = percent; // Remember the percentage. - // Report the progress. - monitor.subTask(NLS - .bind(Messages.OpUpload_UploadingProgress, new Object[] { file - .getName(), formatSize(bytesRead), formatSize(file - .length()) })); - } + + protected IStatus runWorkItem(final WorkItem item, IProgressMonitor monitor) { + final String path; + final FSTreeNode destination = item.fDestination; + FSTreeNode existing; + final String name; + final File source = item.fSource; + if (item.fDropToDestination) { + IStatus status = refresh(destination, fStartTime, monitor); + if (!status.isOK()) + return status; + + name = item.fSource.getName(); + existing = destination.findChild(name); + + if (source.isDirectory()) { + if (existing != null) { + if (!existing.isDirectory()) { + return StatusHelper.createStatus(format(Messages.OpCopy_error_noDirectory, existing.getLocation()), null); } - continue; - case IConfirmCallback.CANCEL: - monitor.setCanceled(true); - continue; - } - } - BufferedInputStream input = null; - BufferedOutputStream output = null; - MessageDigest digest = null; - try { - URL url = urls[i]; - TcfURLConnection connection = (TcfURLConnection) url.openConnection(); - connection.setDoInput(false); - connection.setDoOutput(true); - if (nodes != null) { - try { - digest = MessageDigest.getInstance(MD_ALG); - input = new BufferedInputStream(new DigestInputStream(new FileInputStream(file), digest)); - } - catch (NoSuchAlgorithmException e) { - input = new BufferedInputStream(new FileInputStream(file)); - } - } - else { - input = new BufferedInputStream(new FileInputStream(file)); + int replace = confirmCallback(existing, fConfirmCallback); + if (replace == IConfirmCallback.NO) { + return Status.OK_STATUS; + } + if (replace != IConfirmCallback.YES) { + return Status.CANCEL_STATUS; + } + } else { + status = destination.operationNewFolder(name).run(new SubProgressMonitor(monitor, 0)); + if (!status.isOK()) + return status; + existing = destination.findChild(name); } - output = new BufferedOutputStream(connection.getOutputStream()); - // Total size displayed on the progress dialog. - String fileLength = formatSize(file.length()); - int length; - while ((length = input.read(data)) >= 0 && !monitor.isCanceled()) { - output.write(data, 0, length); - output.flush(); - bytesRead += length; - if (chunk_size != 0) { - int percent = (int) bytesRead / chunk_size; - if (percent != percentRead) { // Update the progress. - monitor.worked(percent - percentRead); - percentRead = percent; // Remember the percentage. - // Report the progress. - monitor.subTask(NLS.bind(Messages.OpUpload_UploadingProgress, new Object[]{file.getName(), formatSize(bytesRead), fileLength})); - } - } + for (File child : source.listFiles()) { + fWork.addFirst(new WorkItem(child, existing, true)); } - } finally { - if (output != null) { - try { - output.close(); - } catch (Exception e) { + return Status.OK_STATUS; + } else if (source.isFile()) { + if (existing != null) { + if (!existing.isFile()) { + return StatusHelper.createStatus(format(Messages.OpCopy_error_noFile, existing.getLocation()), null); } - } - if (input != null) { - try { - input.close(); - } catch (Exception e) { + int replace = confirmCallback(existing, fConfirmCallback); + if (replace == IConfirmCallback.NO) { + return Status.OK_STATUS; + } + if (replace != IConfirmCallback.YES) { + return Status.CANCEL_STATUS; } } - if(digest != null && nodes != null) { - FileState filedigest = PersistenceManager.getInstance().getFileDigest(nodes[i]); - filedigest.reset(digest.digest()); - } + path = getPath(destination, name); + } else { + return Status.OK_STATUS; } + } else { + name = destination.getName(); + existing = destination; + path = destination.getLocation(true); } - } - /** - * Prepare the directory structure on the remote target, creating necessary intermediate directories - * and found all files that should be uploaded. The resulting files to be uploaded should be stored - * to the file list. The resulting corresponding target file URLs should be stored in the url list. - * - * @param fileList The file list to store the files that should be uploaded. - * @param urlList The list containing the corresponding urls. - */ - private void prepareDirStruct(String[] sourceFiles, List<File> fileList, List<URL> urlList, FSTreeNode targetFolder) { - parentFolders = Collections.synchronizedMap(new HashMap<File, FSTreeNode>()); - List<File> files = new ArrayList<File>(); - for(String path: sourceFiles) { - files.add(new File(path)); - } - // Find the root nodes of these files. - List<File> topFiles = getTopFiles(files); - for(File topFile : topFiles) { - appendFile(topFile, fileList, urlList, targetFolder); - } - } - - /** - * Append the specified file object to the file list and url list. If the file object is a file - * then append it to the two lists. If the file object is a directory, then recursively - * add its children and grand children to the two list. During this process, the parents of - * these files and directories traversed should be put into the parent folders map so that - * it could be queried to check if it has a file/directory with a same name. - * - * @param file The file to be added - * @param fileList The file list - * @param urlList The url list - * @param parent The current parent node - */ - private void appendFile(final File file, final List<File> fileList, final List<URL> urlList, final FSTreeNode parent) { - parentFolders.put(file, parent); - if(file.isFile()) { - SafeRunner.run(new ISafeRunnable(){ - @Override - public void run() throws Exception { - URL folderURL = parent.getLocationURL(); - URL url = new URL(folderURL, file.getName()); - fileList.add(file); - urlList.add(url); - } - @Override - public void handleException(Throwable exception) { - // Ignore on purpose - }}); - } else if(file.isDirectory()) { - FSTreeNode node = findNode(file); - if(node == null) { - OpCreateFolder create = new OpCreateFolder(parent, file.getName()); - new NullOpExecutor().execute(create); - node = create.getNode(); - } - File[] children = file.listFiles(); - for(File child : children) { - appendFile(child, fileList, urlList, node); + final TCFResult<OutputStream> result = new TCFResult<OutputStream>(); + monitor.subTask(NLS.bind(Messages.OpUpload_UploadSingleFile, item.fSource)); + Protocol.invokeLater(new Runnable() { + @Override + public void run() { + IFileSystem fs = destination.getRuntimeModel().getFileSystem(); + if (fs == null) { + result.setCancelled(); + } else { + tcfGetOutputStream(fs, path, result); + } } - } - } + }); + IStatus status = result.waitDone(monitor); + if (!status.isOK()) + return status; - /** - * Get the root files of the specified files/folders in the list. - * - * @param files The files to be checked. - * @return Root nodes of these files that has no parent. - */ - private List<File> getTopFiles(List<File>files) { - List<File> result = new ArrayList<File>(); - for(File file : files) { - if(!hasFileAncestor(file, files)) { - result.add(file); + OutputStream out = new BufferedOutputStream(result.getValue()); + try { + IStatus s = uploadFile(item.fSource, existing, out, new SubProgressMonitor(monitor, 0)); + if (!s.isOK()) + return s; + } finally { + try { + out.close(); + } catch (IOException e) { } } - return result; - } - /** - * Check if the target file has an ancestral parent in the specified list. - * - * @param target The target file to be checked. - * @param files The file list to be searched. - * @return true if it has an ancestral parent. - */ - private boolean hasFileAncestor(File target, List<File> files) { - for(File file : files) { - if(isFileAncestor(file, target)) { - return true; + return updateNode(path, name, destination, existing, monitor); + } + + private IStatus updateNode(final String path, final String name, + final FSTreeNode destination, final FSTreeNode existing, IProgressMonitor monitor) { + final TCFResult<?> r2 = new TCFResult<Object>(); + Protocol.invokeLater(new Runnable() { + @Override + public void run() { + IFileSystem fs = destination.getRuntimeModel().getFileSystem(); + if (fs == null) { + r2.setCancelled(); + } else if (!r2.checkCancelled()) { + fs.stat(path, new DoneStat() { + @Override + public void doneStat(IToken token, FileSystemException error, FileAttrs attrs) { + if (error != null) { + r2.setError(format(Messages.OpUpload_error_upload, name), error); + } else if (!r2.checkCancelled()) { + if (existing != null) { + existing.setAttributes(attrs, true); + } else { + destination.addNode(new FSTreeNode(destination, name, false, attrs), true); + } + r2.setDone(null); + } + } + }); + } } + }); + return r2.waitDone(monitor); + } + + protected void tcfGetOutputStream(IFileSystem fileSystem, final String path, final TCFResult<OutputStream> result) { + int flags = IFileSystem.TCF_O_WRITE | IFileSystem.TCF_O_CREAT | IFileSystem.TCF_O_TRUNC; + if (!result.checkCancelled()) { + fileSystem.open(path, flags, null, new DoneOpen() { + @Override + public void doneOpen(IToken token, FileSystemException error, IFileHandle handle) { + if (error != null) { + result.setError(format(Messages.OpUpload_error_openFile, path), error); + } else { + result.setDone(new TCFFileOutputStream(handle)); + } + } + }); } - return false; - } + } - /** - * Check if the specified "file" is an ancestral parent of the "target" file. - * - * @param file The ancestral file. - * @param target The target file. - * @return true if "file" is an ancestral parent of "target" - */ - private boolean isFileAncestor(File file, File target) { - if(target == null) return false; - File parent = target.getParentFile(); - if(file.equals(parent)) return true; - return isFileAncestor(file, parent); - } + private IStatus uploadFile(File source, FSTreeNode existing, OutputStream output, IProgressMonitor monitor) { + byte[] data = new byte[DEFAULT_CHUNK_SIZE]; + // Calculate the total size. + long totalSize = source.length(); + // Calculate the chunk size of one percent. + int chunk_size = (int) totalSize / 100; + // The current reading percentage. + int percentRead = 0; + // The current length of read bytes. + long bytesRead = 0; + MessageDigest digest = null; + InputStream input = null; + try { + input = new BufferedInputStream(new FileInputStream(source)); + if (existing != null) { + try { + digest = MessageDigest.getInstance(MD_ALG); + input = new DigestInputStream(input, digest); + } catch (NoSuchAlgorithmException e) { + digest = null; + } + } - /** - * Check if the specified file has a same-named file under its corresponding - * parent folder. - * - * @param file The file to checked. - * @return the node that has the same name with the file. - */ - private FSTreeNode findNode(File file) { - final FSTreeNode parent = parentFolders.get(file); - if (parent != null) { - final List<FSTreeNode> targetChildren = new ArrayList<FSTreeNode>(); - SafeRunner.run(new ISafeRunnable() { - @Override - public void run() throws Exception { - targetChildren.addAll(getChildren(parent)); + // Total size displayed on the progress dialog. + String fileLength = formatSize(totalSize); + int length; + while ((length = input.read(data)) >= 0) { + output.write(data, 0, length); + bytesRead += length; + if (chunk_size != 0) { + int percent = (int) bytesRead / chunk_size; + if (percent != percentRead) { // Update the progress. + monitor.worked(percent - percentRead); + percentRead = percent; // Remember the percentage. + // Report the progress. + if (fWork.size() == 0) + monitor.subTask(NLS.bind(Messages.OpUpload_UploadingProgress, new Object[]{source.getName(), formatSize(bytesRead), fileLength})); + } } + if (monitor.isCanceled()) + return Status.CANCEL_STATUS; + } - @Override - public void handleException(Throwable exception) { - // Ignore on purpose + if (digest != null && existing != null) { + FileState filedigest = PersistenceManager.getInstance().getFileDigest(existing); + filedigest.reset(digest.digest()); + } + return Status.OK_STATUS; + } catch (IOException e) { + return StatusHelper.createStatus(format(Messages.OpUpload_error_upload, source), e); + } finally { + if (input != null) { + try { + input.close(); + } catch (Exception e) { } - }); - String name = file.getName(); - for (FSTreeNode child : targetChildren) { - if (name.equals(child.name)) return child; } } - return null; - } + } - /* - * (non-Javadoc) - * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getName() - */ @Override public String getName() { String message; - if(srcFiles.length==1) - message = NLS.bind(Messages.OpUpload_UploadSingleFile, srcFiles[0].getName()); + if(fWork.size()==1) + message = NLS.bind(Messages.OpUpload_UploadSingleFile, fWork.element().fSource); else - message = NLS.bind(Messages.OpUpload_UploadNFiles, Long.valueOf(srcFiles.length)); + message = NLS.bind(Messages.OpUpload_UploadNFiles, Long.valueOf(fWork.size())); return message; } - - /* - * (non-Javadoc) - * @see org.eclipse.tcf.te.tcf.filesystem.core.interfaces.IOperation#getTotalWork() - */ - @Override - public int getTotalWork() { - return 100; - } } |