1 /*******************************************************************************
2 * Copyright (c) 2015, 2016 Ericsson
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * Bernd Hufmann - Initial API and implementation
11 *******************************************************************************/
13 package org
.eclipse
.tracecompass
.internal
.tmf
.remote
.ui
.wizards
.fetch
.model
;
16 import java
.io
.FileNotFoundException
;
17 import java
.io
.FileOutputStream
;
18 import java
.io
.IOException
;
19 import java
.io
.InputStream
;
20 import java
.io
.OutputStream
;
21 import java
.lang
.reflect
.InvocationTargetException
;
23 import java
.nio
.file
.Files
;
24 import java
.nio
.file
.StandardCopyOption
;
25 import java
.util
.ArrayList
;
26 import java
.util
.Arrays
;
27 import java
.util
.List
;
29 import org
.eclipse
.core
.commands
.ExecutionException
;
30 import org
.eclipse
.core
.filesystem
.EFS
;
31 import org
.eclipse
.core
.filesystem
.IFileInfo
;
32 import org
.eclipse
.core
.filesystem
.IFileStore
;
33 import org
.eclipse
.core
.resources
.IFile
;
34 import org
.eclipse
.core
.resources
.IFolder
;
35 import org
.eclipse
.core
.resources
.IResource
;
36 import org
.eclipse
.core
.runtime
.CoreException
;
37 import org
.eclipse
.core
.runtime
.IPath
;
38 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
39 import org
.eclipse
.core
.runtime
.IStatus
;
40 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
41 import org
.eclipse
.core
.runtime
.OperationCanceledException
;
42 import org
.eclipse
.core
.runtime
.Path
;
43 import org
.eclipse
.core
.runtime
.Status
;
44 import org
.eclipse
.core
.runtime
.SubMonitor
;
45 import org
.eclipse
.core
.runtime
.URIUtil
;
46 import org
.eclipse
.jface
.operation
.ModalContext
;
47 import org
.eclipse
.osgi
.util
.NLS
;
48 import org
.eclipse
.swt
.widgets
.Shell
;
49 import org
.eclipse
.tracecompass
.internal
.tmf
.remote
.ui
.Activator
;
50 import org
.eclipse
.tracecompass
.internal
.tmf
.remote
.ui
.messages
.RemoteMessages
;
51 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.operations
.TmfWorkspaceModifyOperation
;
52 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ArchiveUtil
;
53 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.FileSystemObjectImportStructureProvider
;
54 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.IFileSystemObject
;
55 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ImportConfirmation
;
56 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ImportConflictHandler
;
57 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ImportTraceWizardPage
;
58 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.TraceFileSystemElement
;
59 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.TraceValidateAndImportOperation
;
60 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageElement
;
61 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageTraceElement
;
62 import org
.eclipse
.tracecompass
.tmf
.core
.TmfCommonConstants
;
63 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceCoreUtils
;
64 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceImportException
;
65 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
;
66 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TraceTypeHelper
;
67 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceFolder
;
68 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceTypeUIUtils
;
69 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTracesFolder
;
70 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TraceUtils
;
71 import org
.eclipse
.ui
.wizards
.datatransfer
.FileSystemStructureProvider
;
74 * Operation to import a set of traces from a remote node into a tracing
77 * @author Bernd Hufmann
79 public class RemoteImportTracesOperation
extends TmfWorkspaceModifyOperation
{
81 private static final String TRACE_IMPORT
= ".traceRemoteImport"; //$NON-NLS-1$
82 // ------------------------------------------------------------------------
84 // ------------------------------------------------------------------------
85 private static final int BUFFER_IN_KB
= 16;
86 private static final int BYTES_PER_KB
= 1024;
88 // ------------------------------------------------------------------------
90 // ------------------------------------------------------------------------
91 private IStatus fStatus
;
92 private final Shell fShell
;
93 private final TmfTraceFolder fDestination
;
94 private final Object
[] fTraceElements
;
95 private final ImportConflictHandler fConflictHandler
;
96 private final List
<IResource
> fImportedResources
= new ArrayList
<>();
98 // ------------------------------------------------------------------------
100 // ------------------------------------------------------------------------
102 * Operation to import a set of traces from a remote node into a tracing
106 * shell to display confirmation dialog
108 * The destination traces folder
110 * The trace model elements describing the traces to import
111 * @param overwriteAll
112 * Flag to indicate to overwrite all existing traces
114 public RemoteImportTracesOperation(Shell shell
, TmfTraceFolder destination
, Object
[] elements
, boolean overwriteAll
) {
117 fDestination
= destination
;
118 fTraceElements
= Arrays
.copyOf(elements
, elements
.length
);
120 fConflictHandler
= new ImportConflictHandler(fShell
, destination
, ImportConfirmation
.OVERWRITE_ALL
);
122 fConflictHandler
= new ImportConflictHandler(fShell
, destination
, ImportConfirmation
.SKIP
);
126 // ------------------------------------------------------------------------
128 // ------------------------------------------------------------------------
130 protected void execute(IProgressMonitor monitor
) throws CoreException
,
131 InvocationTargetException
, InterruptedException
{
135 setStatus(Status
.OK_STATUS
);
136 } catch (InterruptedException
| OperationCanceledException e
) {
137 setStatus(Status
.CANCEL_STATUS
);
139 } catch (Exception e
) {
140 setStatus(new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, RemoteMessages
.RemoteImportTracesOperation_ImportFailure
, e
));
141 throw new InvocationTargetException(e
);
145 // ------------------------------------------------------------------------
147 // ------------------------------------------------------------------------
148 private void doRun(IProgressMonitor monitor
) throws ExecutionException
, CoreException
, IOException
, InterruptedException
{
150 IFolder destinationFolder
= fDestination
.getResource();
151 if (!destinationFolder
.exists()) {
152 throw new ExecutionException(RemoteMessages
.RemoteImportTracesOperation_ImportDialogInvalidTracingProject
+ " (" + TmfTracesFolder
.TRACES_FOLDER_NAME
+ ")"); //$NON-NLS-1$//$NON-NLS-2$
155 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, fTraceElements
.length
* 4);
156 subMonitor
.beginTask(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
, fTraceElements
.length
* 4);
158 for (Object packageElement
: fTraceElements
) {
159 if (!(packageElement
instanceof TracePackageTraceElement
)) {
162 TracePackageTraceElement traceElement
= (TracePackageTraceElement
) packageElement
;
163 TracePackageElement parentElement
= traceElement
.getParent();
164 while (parentElement
!= null) {
165 if (parentElement
instanceof RemoteImportTraceGroupElement
) {
168 parentElement
= parentElement
.getParent();
171 if (parentElement
== null) {
175 RemoteImportTraceGroupElement traceGroup
= (RemoteImportTraceGroupElement
) parentElement
;
176 String rootPath
= traceGroup
.getRootImportPath();
178 // Create folder with node name in destination folder
179 RemoteImportConnectionNodeElement nodeElement
= (RemoteImportConnectionNodeElement
) traceGroup
.getParent();
180 String nodeName
= nodeElement
.getName();
181 IFolder nodeFolder
= destinationFolder
.getFolder(nodeName
);
183 TracePackageElement
[] children
= traceElement
.getChildren();
184 SubMonitor childMonitor
= subMonitor
.newChild(1);
185 TraceUtils
.createFolder(nodeFolder
, childMonitor
);
187 for (TracePackageElement element
: children
) {
188 ModalContext
.checkCanceled(monitor
);
190 if (element
instanceof RemoteImportTraceFilesElement
) {
191 RemoteImportTraceFilesElement traceFilesElement
= (RemoteImportTraceFilesElement
) element
;
193 IFileStore remoteFile
= traceFilesElement
.getRemoteFile();
195 // Preserve folder structure
196 IPath sessionParentPath
= TmfTraceCoreUtils
.newSafePath(rootPath
).removeLastSegments(1);
197 IPath traceParentPath
= TmfTraceCoreUtils
.newSafePath(remoteFile
.getParent().toURI().getPath());
198 IPath relativeTracePath
= Path
.EMPTY
;
199 if (sessionParentPath
.isPrefixOf(traceParentPath
)) {
200 relativeTracePath
= traceParentPath
.makeRelativeTo(sessionParentPath
);
203 String
[] segments
= relativeTracePath
.segments();
204 for (int i
= 0; i
< segments
.length
; i
++) {
205 String segment
= TmfTraceCoreUtils
.validateName(TmfTraceCoreUtils
.safePathToString(segments
[i
]));
207 relativeTracePath
= new Path(segment
);
209 relativeTracePath
= relativeTracePath
.append(segment
);
213 IFolder traceFolder
= nodeFolder
.getFolder(new Path(relativeTracePath
.toOSString()));
214 childMonitor
= subMonitor
.newChild(1);
215 TraceUtils
.createFolder(traceFolder
, childMonitor
);
219 IResource traceRes
= null;
220 IFileInfo info
= remoteFile
.fetchInfo();
221 if (info
.isDirectory()) {
222 traceRes
= downloadDirectoryTrace(remoteFile
, traceFolder
, subMonitor
.newChild(1));
224 traceRes
= downloadFileTrace(remoteFile
, traceFolder
, subMonitor
.newChild(1));
227 String traceName
= traceElement
.getText();
228 if (traceRes
== null || !traceRes
.exists()) {
233 TraceTypeHelper traceTypeHelper
= null;
234 String traceTypeStr
= traceElement
.getTraceType();
235 if (traceTypeStr
!= null) {
236 traceTypeHelper
= TmfTraceType
.getTraceType(traceTypeStr
);
239 // no specific trace type found
240 if (traceTypeHelper
== null) {
242 // Try to auto-detect the trace typ
243 childMonitor
= subMonitor
.newChild(1);
244 childMonitor
.setTaskName(NLS
.bind(RemoteMessages
.RemoteImportTracesOperation_DetectingTraceType
, traceName
));
246 traceTypeHelper
= TmfTraceTypeUIUtils
.selectTraceType(traceRes
.getLocation().toOSString(), null, null);
247 } catch (TmfTraceImportException e
) {
248 // Could not figure out the type
252 if (traceTypeHelper
!= null) {
253 TmfTraceTypeUIUtils
.setTraceType(traceRes
, traceTypeHelper
);
254 fImportedResources
.add(traceRes
);
257 // Set source location
258 URI uri
= remoteFile
.toURI();
259 String sourceLocation
= URIUtil
.toUnencodedString(uri
);
260 traceRes
.setPersistentProperty(TmfCommonConstants
.SOURCE_LOCATION
, sourceLocation
);
266 // Download a directory trace
267 private IResource
downloadDirectoryTrace(IFileStore trace
, IFolder traceFolder
, IProgressMonitor monitor
) throws CoreException
, IOException
, InterruptedException
{
269 IFileStore
[] sources
= trace
.childStores(EFS
.NONE
, monitor
);
271 // Don't import just the metadata file
272 if (sources
.length
> 1) {
273 String traceName
= trace
.getName();
275 traceName
= TmfTraceCoreUtils
.validateName(traceName
);
277 IFolder folder
= traceFolder
.getFolder(traceName
);
278 String newName
= fConflictHandler
.checkAndHandleNameClash(folder
.getFullPath(), monitor
);
279 if (newName
== null) {
283 folder
= traceFolder
.getFolder(newName
);
284 folder
.create(true, true, null);
286 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, sources
.length
);
287 subMonitor
.beginTask(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
, sources
.length
);
289 for (IFileStore source
: sources
) {
290 if (subMonitor
.isCanceled()) {
291 throw new InterruptedException();
294 IPath destination
= folder
.getLocation().addTrailingSeparator().append(source
.getName());
295 IFileInfo info
= source
.fetchInfo();
296 // TODO allow for downloading index directory and files
297 if (!info
.isDirectory()) {
298 SubMonitor childMonitor
= subMonitor
.newChild(1);
299 childMonitor
.setTaskName(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
+ ' ' + trace
.getName() + '/' + source
.getName());
300 try (InputStream in
= source
.openInputStream(EFS
.NONE
, new NullProgressMonitor())) {
301 copy(in
, folder
, destination
, childMonitor
, info
.getLength());
305 folder
.refreshLocal(IResource
.DEPTH_INFINITE
, null);
311 // Download file trace
312 private IResource
downloadFileTrace(IFileStore trace
, IFolder traceFolder
, IProgressMonitor monitor
) throws CoreException
, IOException
, InterruptedException
{
314 IFolder folder
= traceFolder
;
315 String traceName
= trace
.getName();
317 traceName
= TmfTraceCoreUtils
.validateName(traceName
);
319 IResource resource
= folder
.findMember(traceName
);
320 if ((resource
!= null) && resource
.exists()) {
321 String newName
= fConflictHandler
.checkAndHandleNameClash(resource
.getFullPath(), monitor
);
322 if (newName
== null) {
327 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, 1);
328 subMonitor
.beginTask(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
, 1);
330 IPath destination
= folder
.getLocation().addTrailingSeparator().append(traceName
);
331 IFileInfo info
= trace
.fetchInfo();
332 subMonitor
.setTaskName(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
+ ' ' + trace
.getName() + '/' + trace
.getName());
333 try (InputStream in
= trace
.openInputStream(EFS
.NONE
, new NullProgressMonitor())) {
334 copy(in
, folder
, destination
, subMonitor
, info
.getLength());
336 folder
.refreshLocal(IResource
.DEPTH_INFINITE
, null);
337 return folder
.findMember(traceName
);
340 private void copy(InputStream in
, IFolder destFolder
, IPath destination
, SubMonitor monitor
, long length
) throws IOException
{
341 IFolder intermediateTempFolder
= null;
342 IFile tempFile
= null;
343 File intermediateFile
= null;
345 intermediateTempFolder
= fDestination
.getProject().getResource().getFolder(TRACE_IMPORT
);
346 if (intermediateTempFolder
.exists()) {
347 intermediateTempFolder
.delete(true, SubMonitor
.convert(monitor
));
349 intermediateTempFolder
.create(true, true, SubMonitor
.convert(monitor
));
350 tempFile
= intermediateTempFolder
.getFile(destination
.lastSegment());
351 tempFile
.create(null, true, SubMonitor
.convert(monitor
));
352 intermediateFile
= tempFile
.getLocation().toFile();
353 intermediateFile
.createNewFile();
354 copy(in
, intermediateFile
, length
, monitor
);
355 if (ArchiveUtil
.isArchiveFile(intermediateFile
)) {
356 // Select all the elements in the archive
357 FileSystemObjectImportStructureProvider importProvider
= new FileSystemObjectImportStructureProvider(FileSystemStructureProvider
.INSTANCE
, null);
358 IFileSystemObject fileSystemObject
= importProvider
.getIFileSystemObject(intermediateFile
);
359 TraceFileSystemElement rootTraceFileElement
= TraceFileSystemElement
.createRootTraceFileElement(fileSystemObject
, importProvider
);
361 // Select all the elements in the archive
362 List
<TraceFileSystemElement
> list
= new ArrayList
<>();
363 rootTraceFileElement
.getAllChildren(list
);
364 if (!destFolder
.exists()) {
365 destFolder
.create(true, true, SubMonitor
.convert(monitor
));
367 final IFolder folder
= destFolder
.getFolder(destination
.lastSegment());
368 if (!folder
.exists()) {
369 folder
.create(true, true, SubMonitor
.convert(monitor
));
372 final TraceValidateAndImportOperation operation
= new TraceValidateAndImportOperation(
373 fShell
, list
, null, intermediateTempFolder
.getLocation(), destFolder
.getFullPath(), false,
374 ImportTraceWizardPage
.OPTION_PRESERVE_FOLDER_STRUCTURE
| ImportTraceWizardPage
.OPTION_IMPORT_UNRECOGNIZED_TRACES
,
376 operation
.setConflictHandler(fConflictHandler
);
377 operation
.run(SubMonitor
.convert(monitor
));
380 // should be lightning fast unless someone maps different files
381 // to different physical disks. In windows and linux, moves are
382 // super fast on the same drive
383 Files
.move(intermediateFile
.toPath(), destination
.toFile().toPath(), StandardCopyOption
.REPLACE_EXISTING
);
385 } catch (CoreException
| InvocationTargetException
| InterruptedException e
) {
386 Activator
.getDefault().logError(e
.getMessage(), e
);
388 if (intermediateFile
!= null && intermediateFile
.exists()) {
389 intermediateFile
.delete();
392 if (tempFile
!= null && tempFile
.exists()) {
393 tempFile
.delete(true, SubMonitor
.convert(monitor
));
395 if (intermediateTempFolder
!= null && intermediateTempFolder
.exists()) {
396 intermediateTempFolder
.delete(true, SubMonitor
.convert(monitor
));
398 } catch (CoreException e
) {
399 Activator
.getDefault().logError(e
.getMessage(), e
);
404 private static void copy(InputStream in
, File intermediateFile
, long length
, SubMonitor monitor
) throws IOException
, FileNotFoundException
{
405 try (OutputStream out
= new FileOutputStream(intermediateFile
)) {
406 monitor
.setWorkRemaining((int) (length
/ BYTES_PER_KB
));
407 byte[] buf
= new byte[BYTES_PER_KB
* BUFFER_IN_KB
];
410 int n
= in
.read(buf
);
414 out
.write(buf
, 0, n
);
415 counter
= (counter
% BYTES_PER_KB
) + n
;
416 monitor
.worked(counter
/ BYTES_PER_KB
);
422 * Set the result status for this operation
427 protected void setStatus(IStatus status
) {
432 * Gets the result of the operation.
434 * @return result status of operation
436 public IStatus
getStatus() {
441 * Get the list of resources that were imported by this operation. An
442 * example use case would be to use this to open traces that were imported
445 * Note this includes only valid traces and doesn'tinclude unrecognized
448 * @return the trace resources that were imported
450 public List
<IResource
> getImportedResources() {
451 return fImportedResources
;