| /******************************************************************************* |
| * Copyright (c) 2015, 2016 Ericsson |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Bernd Hufmann - Initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.internal.tmf.remote.ui.wizards.fetch.model; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.lang.reflect.InvocationTargetException; |
| import java.net.URI; |
| import java.nio.file.Files; |
| import java.nio.file.StandardCopyOption; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.eclipse.core.commands.ExecutionException; |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileInfo; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.core.runtime.URIUtil; |
| import org.eclipse.jface.operation.ModalContext; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.tracecompass.internal.tmf.remote.ui.Activator; |
| import org.eclipse.tracecompass.internal.tmf.remote.ui.messages.RemoteMessages; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.operations.TmfWorkspaceModifyOperation; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.ArchiveUtil; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.FileSystemObjectImportStructureProvider; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.IFileSystemObject; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.ImportConfirmation; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.ImportConflictHandler; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.ImportTraceWizardPage; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TraceFileSystemElement; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.importtrace.TraceValidateAndImportOperation; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageElement; |
| import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageTraceElement; |
| import org.eclipse.tracecompass.tmf.core.TmfCommonConstants; |
| import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceCoreUtils; |
| import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceImportException; |
| import org.eclipse.tracecompass.tmf.core.project.model.TmfTraceType; |
| import org.eclipse.tracecompass.tmf.core.project.model.TraceTypeHelper; |
| import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceFolder; |
| import org.eclipse.tracecompass.tmf.ui.project.model.TmfTraceTypeUIUtils; |
| import org.eclipse.tracecompass.tmf.ui.project.model.TraceUtils; |
| import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; |
| |
| /** |
| * Operation to import a set of traces from a remote node into a tracing |
| * project. |
| * |
| * @author Bernd Hufmann |
| */ |
| public class RemoteImportTracesOperation extends TmfWorkspaceModifyOperation { |
| |
| private static final String TRACE_IMPORT = ".traceRemoteImport"; //$NON-NLS-1$ |
| // ------------------------------------------------------------------------ |
| // Constants |
| // ------------------------------------------------------------------------ |
| private static final int BUFFER_IN_KB = 16; |
| private static final int BYTES_PER_KB = 1024; |
| |
| // ------------------------------------------------------------------------ |
| // Attributes |
| // ------------------------------------------------------------------------ |
| private IStatus fStatus; |
| private final Shell fShell; |
| private final TmfTraceFolder fDestination; |
| private final Object[] fTraceElements; |
| private final ImportConflictHandler fConflictHandler; |
| private final List<IResource> fImportedResources = new ArrayList<>(); |
| |
| // ------------------------------------------------------------------------ |
| // Constructor(s) |
| // ------------------------------------------------------------------------ |
| /** |
| * Operation to import a set of traces from a remote node into a tracing |
| * project. |
| * |
| * @param shell |
| * shell to display confirmation dialog |
| * @param destination |
| * The destination traces folder |
| * @param elements |
| * The trace model elements describing the traces to import |
| * @param overwriteAll |
| * Flag to indicate to overwrite all existing traces |
| */ |
| public RemoteImportTracesOperation(Shell shell, TmfTraceFolder destination, Object[] elements, boolean overwriteAll) { |
| super(); |
| fShell = shell; |
| fDestination = destination; |
| fTraceElements = Arrays.copyOf(elements, elements.length); |
| if (overwriteAll) { |
| fConflictHandler = new ImportConflictHandler(fShell, destination, ImportConfirmation.OVERWRITE_ALL); |
| } else { |
| fConflictHandler = new ImportConflictHandler(fShell, destination, ImportConfirmation.SKIP); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Operations |
| // ------------------------------------------------------------------------ |
| @Override |
| protected void execute(IProgressMonitor monitor) throws CoreException, |
| InvocationTargetException, InterruptedException { |
| |
| try { |
| doRun(monitor); |
| setStatus(Status.OK_STATUS); |
| } catch (InterruptedException | OperationCanceledException e) { |
| setStatus(Status.CANCEL_STATUS); |
| throw e; |
| } catch (Exception e) { |
| setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, RemoteMessages.RemoteImportTracesOperation_ImportFailure, e)); |
| throw new InvocationTargetException(e); |
| } |
| } |
| |
| // ------------------------------------------------------------------------ |
| // Helper methods |
| // ------------------------------------------------------------------------ |
| private void doRun(IProgressMonitor monitor) throws ExecutionException, CoreException, IOException, InterruptedException { |
| |
| IFolder destinationFolder = fDestination.getResource(); |
| if (!destinationFolder.exists()) { |
| throw new ExecutionException(RemoteMessages.RemoteImportTracesOperation_ImportDialogInvalidTracingProject + " (" + destinationFolder + ")"); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| |
| SubMonitor subMonitor = SubMonitor.convert(monitor, fTraceElements.length * 4); |
| subMonitor.beginTask(RemoteMessages.RemoteImportTracesOperation_DownloadTask, fTraceElements.length * 4); |
| |
| for (Object packageElement : fTraceElements) { |
| if (!(packageElement instanceof TracePackageTraceElement)) { |
| continue; |
| } |
| TracePackageTraceElement traceElement = (TracePackageTraceElement) packageElement; |
| TracePackageElement parentElement = traceElement.getParent(); |
| while (parentElement != null) { |
| if (parentElement instanceof RemoteImportTraceGroupElement) { |
| break; |
| } |
| parentElement = parentElement.getParent(); |
| } |
| |
| if (parentElement == null) { |
| continue; |
| } |
| |
| RemoteImportTraceGroupElement traceGroup = (RemoteImportTraceGroupElement) parentElement; |
| String rootPath = traceGroup.getRootImportPath(); |
| |
| // Create folder with node name in destination folder |
| RemoteImportConnectionNodeElement nodeElement = (RemoteImportConnectionNodeElement) traceGroup.getParent(); |
| String nodeName = nodeElement.getName(); |
| IFolder nodeFolder = destinationFolder.getFolder(nodeName); |
| |
| TracePackageElement[] children = traceElement.getChildren(); |
| SubMonitor childMonitor = subMonitor.newChild(1); |
| TraceUtils.createFolder(nodeFolder, childMonitor); |
| |
| for (TracePackageElement element : children) { |
| ModalContext.checkCanceled(monitor); |
| |
| if (element instanceof RemoteImportTraceFilesElement) { |
| RemoteImportTraceFilesElement traceFilesElement = (RemoteImportTraceFilesElement) element; |
| |
| IFileStore remoteFile = traceFilesElement.getRemoteFile(); |
| |
| // Preserve folder structure |
| IPath sessionParentPath = TmfTraceCoreUtils.newSafePath(rootPath).removeLastSegments(1); |
| IPath traceParentPath = TmfTraceCoreUtils.newSafePath(remoteFile.getParent().toURI().getPath()); |
| IPath relativeTracePath = Path.EMPTY; |
| if (sessionParentPath.isPrefixOf(traceParentPath)) { |
| relativeTracePath = traceParentPath.makeRelativeTo(sessionParentPath); |
| } |
| |
| String[] segments = relativeTracePath.segments(); |
| for (int i = 0; i < segments.length; i++) { |
| String segment = TmfTraceCoreUtils.validateName(TmfTraceCoreUtils.safePathToString(segments[i])); |
| if (i == 0) { |
| relativeTracePath = new Path(segment); |
| } else { |
| relativeTracePath = relativeTracePath.append(segment); |
| } |
| } |
| |
| IFolder traceFolder = nodeFolder.getFolder(new Path(relativeTracePath.toOSString())); |
| childMonitor = subMonitor.newChild(1); |
| TraceUtils.createFolder(traceFolder, childMonitor); |
| childMonitor.done(); |
| |
| // Import trace |
| IResource traceRes = null; |
| IFileInfo info = remoteFile.fetchInfo(); |
| if (info.isDirectory()) { |
| traceRes = downloadDirectoryTrace(remoteFile, traceFolder, subMonitor.newChild(1)); |
| } else { |
| traceRes = downloadFileTrace(remoteFile, traceFolder, subMonitor.newChild(1)); |
| } |
| |
| String traceName = traceElement.getText(); |
| if (traceRes == null || !traceRes.exists()) { |
| continue; |
| } |
| |
| // Select trace type |
| TraceTypeHelper traceTypeHelper = null; |
| String traceTypeStr = traceElement.getTraceType(); |
| if (traceTypeStr != null) { |
| traceTypeHelper = TmfTraceType.getTraceType(traceTypeStr); |
| } |
| |
| // no specific trace type found |
| if (traceTypeHelper == null) { |
| try { |
| // Try to auto-detect the trace typ |
| childMonitor = subMonitor.newChild(1); |
| childMonitor.setTaskName(NLS.bind(RemoteMessages.RemoteImportTracesOperation_DetectingTraceType, traceName)); |
| childMonitor.done(); |
| traceTypeHelper = TmfTraceTypeUIUtils.selectTraceType(traceRes.getLocation().toOSString(), null, null); |
| } catch (TmfTraceImportException e) { |
| // Could not figure out the type |
| } |
| } |
| |
| if (traceTypeHelper != null) { |
| TmfTraceTypeUIUtils.setTraceType(traceRes, traceTypeHelper); |
| fImportedResources.add(traceRes); |
| } |
| |
| // Set source location |
| URI uri = remoteFile.toURI(); |
| String sourceLocation = URIUtil.toUnencodedString(uri); |
| traceRes.setPersistentProperty(TmfCommonConstants.SOURCE_LOCATION, sourceLocation); |
| } |
| } |
| } |
| } |
| |
| // Download a directory trace |
| private IResource downloadDirectoryTrace(IFileStore trace, IFolder traceFolder, IProgressMonitor monitor) throws CoreException, IOException, InterruptedException { |
| |
| IFileStore[] sources = trace.childStores(EFS.NONE, monitor); |
| |
| // Don't import just the metadata file |
| if (sources.length > 1) { |
| String traceName = trace.getName(); |
| |
| traceName = TmfTraceCoreUtils.validateName(traceName); |
| |
| IFolder folder = traceFolder.getFolder(traceName); |
| String newName = fConflictHandler.checkAndHandleNameClash(folder.getFullPath(), monitor); |
| if (newName == null) { |
| return null; |
| } |
| |
| folder = traceFolder.getFolder(newName); |
| folder.create(true, true, null); |
| |
| SubMonitor subMonitor = SubMonitor.convert(monitor, sources.length); |
| subMonitor.beginTask(RemoteMessages.RemoteImportTracesOperation_DownloadTask, sources.length); |
| |
| for (IFileStore source : sources) { |
| if (subMonitor.isCanceled()) { |
| throw new InterruptedException(); |
| } |
| |
| IPath destination = folder.getLocation().addTrailingSeparator().append(source.getName()); |
| IFileInfo info = source.fetchInfo(); |
| // TODO allow for downloading index directory and files |
| if (!info.isDirectory()) { |
| SubMonitor childMonitor = subMonitor.newChild(1); |
| childMonitor.setTaskName(RemoteMessages.RemoteImportTracesOperation_DownloadTask + ' ' + trace.getName() + '/' + source.getName()); |
| try (InputStream in = source.openInputStream(EFS.NONE, new NullProgressMonitor())) { |
| copy(in, folder, destination, childMonitor, info.getLength()); |
| } |
| } |
| } |
| folder.refreshLocal(IResource.DEPTH_INFINITE, null); |
| return folder; |
| } |
| return null; |
| } |
| |
| // Download file trace |
| private IResource downloadFileTrace(IFileStore trace, IFolder traceFolder, IProgressMonitor monitor) throws CoreException, IOException, InterruptedException { |
| |
| IFolder folder = traceFolder; |
| String traceName = trace.getName(); |
| |
| traceName = TmfTraceCoreUtils.validateName(traceName); |
| |
| IResource resource = folder.findMember(traceName); |
| if ((resource != null) && resource.exists()) { |
| String newName = fConflictHandler.checkAndHandleNameClash(resource.getFullPath(), monitor); |
| if (newName == null) { |
| return null; |
| } |
| traceName = newName; |
| } |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 1); |
| subMonitor.beginTask(RemoteMessages.RemoteImportTracesOperation_DownloadTask, 1); |
| |
| IPath destination = folder.getLocation().addTrailingSeparator().append(traceName); |
| IFileInfo info = trace.fetchInfo(); |
| subMonitor.setTaskName(RemoteMessages.RemoteImportTracesOperation_DownloadTask + ' ' + trace.getName() + '/' + trace.getName()); |
| try (InputStream in = trace.openInputStream(EFS.NONE, new NullProgressMonitor())) { |
| copy(in, folder, destination, subMonitor, info.getLength()); |
| } |
| folder.refreshLocal(IResource.DEPTH_INFINITE, null); |
| return folder.findMember(traceName); |
| } |
| |
| private void copy(InputStream in, IFolder destFolder, IPath destination, SubMonitor monitor, long length) throws IOException { |
| IFolder intermediateTempFolder = null; |
| IFile tempFile = null; |
| File intermediateFile = null; |
| try { |
| intermediateTempFolder = fDestination.getProject().getResource().getFolder(TRACE_IMPORT); |
| if (intermediateTempFolder.exists()) { |
| intermediateTempFolder.delete(true, SubMonitor.convert(monitor)); |
| } |
| intermediateTempFolder.create(true, true, SubMonitor.convert(monitor)); |
| tempFile = intermediateTempFolder.getFile(destination.lastSegment()); |
| tempFile.create(null, true, SubMonitor.convert(monitor)); |
| intermediateFile = tempFile.getLocation().toFile(); |
| Files.createFile(intermediateFile.toPath()); |
| copy(in, intermediateFile, length, monitor); |
| if (ArchiveUtil.isArchiveFile(intermediateFile)) { |
| // Select all the elements in the archive |
| FileSystemObjectImportStructureProvider importProvider = new FileSystemObjectImportStructureProvider(FileSystemStructureProvider.INSTANCE, null); |
| IFileSystemObject fileSystemObject = importProvider.getIFileSystemObject(intermediateFile); |
| TraceFileSystemElement rootTraceFileElement = TraceFileSystemElement.createRootTraceFileElement(fileSystemObject, importProvider); |
| |
| // Select all the elements in the archive |
| List<TraceFileSystemElement> list = new ArrayList<>(); |
| rootTraceFileElement.getAllChildren(list); |
| if (!destFolder.exists()) { |
| destFolder.create(true, true, SubMonitor.convert(monitor)); |
| } |
| final IFolder folder = destFolder.getFolder(destination.lastSegment()); |
| if (!folder.exists()) { |
| folder.create(true, true, SubMonitor.convert(monitor)); |
| } |
| |
| final TraceValidateAndImportOperation operation = new TraceValidateAndImportOperation( |
| fShell, list, null, intermediateTempFolder.getLocation(), destFolder.getFullPath(), false, |
| ImportTraceWizardPage.OPTION_PRESERVE_FOLDER_STRUCTURE | ImportTraceWizardPage.OPTION_IMPORT_UNRECOGNIZED_TRACES, |
| fDestination, null, null, null, false); |
| operation.setConflictHandler(fConflictHandler); |
| operation.run(SubMonitor.convert(monitor)); |
| monitor.done(); |
| } else { |
| // should be lightning fast unless someone maps different files |
| // to different physical disks. In windows and linux, moves are |
| // super fast on the same drive |
| Files.move(intermediateFile.toPath(), destination.toFile().toPath(), StandardCopyOption.REPLACE_EXISTING); |
| } |
| } catch (CoreException | InvocationTargetException e) { |
| Activator.getDefault().logError(e.getMessage(), e); |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } finally { |
| try { |
| if (intermediateFile != null && intermediateFile.exists()) { |
| Files.delete(intermediateFile.toPath()); |
| } |
| if (tempFile != null && tempFile.exists()) { |
| tempFile.delete(true, SubMonitor.convert(monitor)); |
| } |
| if (intermediateTempFolder != null && intermediateTempFolder.exists()) { |
| intermediateTempFolder.delete(true, SubMonitor.convert(monitor)); |
| } |
| } catch (CoreException e) { |
| Activator.getDefault().logError(e.getMessage(), e); |
| } |
| } |
| } |
| |
| private static void copy(InputStream in, File intermediateFile, long length, SubMonitor monitor) throws IOException { |
| try (OutputStream out = new FileOutputStream(intermediateFile)) { |
| monitor.setWorkRemaining((int) (length / BYTES_PER_KB)); |
| byte[] buf = new byte[BYTES_PER_KB * BUFFER_IN_KB]; |
| int counter = 0; |
| for (;;) { |
| int n = in.read(buf); |
| if (n <= 0) { |
| break; |
| } |
| out.write(buf, 0, n); |
| counter = (counter % BYTES_PER_KB) + n; |
| monitor.worked(counter / BYTES_PER_KB); |
| } |
| } |
| } |
| |
| /** |
| * Set the result status for this operation |
| * |
| * @param status |
| * the status |
| */ |
| protected void setStatus(IStatus status) { |
| fStatus = status; |
| } |
| |
| /** |
| * Gets the result of the operation. |
| * |
| * @return result status of operation |
| */ |
| public IStatus getStatus() { |
| return fStatus; |
| } |
| |
| /** |
| * Get the list of resources that were imported by this operation. An |
| * example use case would be to use this to open traces that were imported |
| * by this operation. |
| * |
| * Note this includes only valid traces and doesn'tinclude unrecognized |
| * files. |
| * |
| * @return the trace resources that were imported |
| */ |
| public List<IResource> getImportedResources() { |
| return fImportedResources; |
| } |
| } |