1 /*******************************************************************************
2 * Copyright (c) 2015 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
.Path
;
42 import org
.eclipse
.core
.runtime
.Status
;
43 import org
.eclipse
.core
.runtime
.SubMonitor
;
44 import org
.eclipse
.core
.runtime
.URIUtil
;
45 import org
.eclipse
.jface
.operation
.ModalContext
;
46 import org
.eclipse
.osgi
.util
.NLS
;
47 import org
.eclipse
.swt
.widgets
.Shell
;
48 import org
.eclipse
.tracecompass
.internal
.tmf
.remote
.ui
.Activator
;
49 import org
.eclipse
.tracecompass
.internal
.tmf
.remote
.ui
.messages
.RemoteMessages
;
50 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.operations
.TmfWorkspaceModifyOperation
;
51 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ArchiveUtil
;
52 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.FileSystemObjectImportStructureProvider
;
53 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.IFileSystemObject
;
54 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ImportConfirmation
;
55 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ImportConflictHandler
;
56 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.ImportTraceWizardPage
;
57 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.TraceFileSystemElement
;
58 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
.TraceValidateAndImportOperation
;
59 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageElement
;
60 import org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.tracepkg
.TracePackageTraceElement
;
61 import org
.eclipse
.tracecompass
.tmf
.core
.TmfCommonConstants
;
62 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceCoreUtils
;
63 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceImportException
;
64 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TmfTraceType
;
65 import org
.eclipse
.tracecompass
.tmf
.core
.project
.model
.TraceTypeHelper
;
66 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceFolder
;
67 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTraceTypeUIUtils
;
68 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TmfTracesFolder
;
69 import org
.eclipse
.tracecompass
.tmf
.ui
.project
.model
.TraceUtils
;
70 import org
.eclipse
.ui
.wizards
.datatransfer
.FileSystemStructureProvider
;
73 * Operation to import a set of traces from a remote node into a tracing
76 * @author Bernd Hufmann
78 public class RemoteImportTracesOperation
extends TmfWorkspaceModifyOperation
{
80 private static final String TRACE_IMPORT
= ".traceRemoteImport"; //$NON-NLS-1$
81 // ------------------------------------------------------------------------
83 // ------------------------------------------------------------------------
84 private static final int BUFFER_IN_KB
= 16;
85 private static final int BYTES_PER_KB
= 1024;
87 // ------------------------------------------------------------------------
89 // ------------------------------------------------------------------------
90 private IStatus fStatus
;
91 private final Shell fShell
;
92 private final TmfTraceFolder fDestination
;
93 private final Object
[] fTraceElements
;
94 private final ImportConflictHandler fConflictHandler
;
95 private final List
<IResource
> fImportedResources
= new ArrayList
<>();
97 // ------------------------------------------------------------------------
99 // ------------------------------------------------------------------------
101 * Operation to import a set of traces from a remote node into a tracing
105 * shell to display confirmation dialog
107 * The destination traces folder
109 * The trace model elements describing the traces to import
110 * @param overwriteAll
111 * Flag to indicate to overwrite all existing traces
113 public RemoteImportTracesOperation(Shell shell
, TmfTraceFolder destination
, Object
[] elements
, boolean overwriteAll
) {
116 fDestination
= destination
;
117 fTraceElements
= Arrays
.copyOf(elements
, elements
.length
);
119 fConflictHandler
= new ImportConflictHandler(fShell
, destination
, ImportConfirmation
.OVERWRITE_ALL
);
121 fConflictHandler
= new ImportConflictHandler(fShell
, destination
, ImportConfirmation
.SKIP
);
125 // ------------------------------------------------------------------------
127 // ------------------------------------------------------------------------
129 protected void execute(IProgressMonitor monitor
) throws CoreException
,
130 InvocationTargetException
, InterruptedException
{
134 setStatus(Status
.OK_STATUS
);
135 } catch (InterruptedException e
) {
136 setStatus(Status
.CANCEL_STATUS
);
138 } catch (Exception e
) {
139 setStatus(new Status(IStatus
.ERROR
, Activator
.PLUGIN_ID
, RemoteMessages
.RemoteImportTracesOperation_ImportFailure
, e
));
140 throw new InvocationTargetException(e
);
144 // ------------------------------------------------------------------------
146 // ------------------------------------------------------------------------
147 private void doRun(IProgressMonitor monitor
) throws ExecutionException
, CoreException
, IOException
, InterruptedException
{
149 IFolder destinationFolder
= fDestination
.getResource();
150 if (!destinationFolder
.exists()) {
151 throw new ExecutionException(RemoteMessages
.RemoteImportTracesOperation_ImportDialogInvalidTracingProject
+ " (" + TmfTracesFolder
.TRACES_FOLDER_NAME
+ ")"); //$NON-NLS-1$//$NON-NLS-2$
154 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, fTraceElements
.length
* 4);
155 subMonitor
.beginTask(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
, fTraceElements
.length
* 4);
157 for (Object packageElement
: fTraceElements
) {
158 if (!(packageElement
instanceof TracePackageTraceElement
)) {
161 TracePackageTraceElement traceElement
= (TracePackageTraceElement
) packageElement
;
162 TracePackageElement parentElement
= traceElement
.getParent();
163 while (parentElement
!= null) {
164 if (parentElement
instanceof RemoteImportTraceGroupElement
) {
167 parentElement
= parentElement
.getParent();
170 if (parentElement
== null) {
174 RemoteImportTraceGroupElement traceGroup
= (RemoteImportTraceGroupElement
) parentElement
;
175 String rootPath
= traceGroup
.getRootImportPath();
177 // Create folder with node name in destination folder
178 RemoteImportConnectionNodeElement nodeElement
= (RemoteImportConnectionNodeElement
) traceGroup
.getParent();
179 String nodeName
= nodeElement
.getName();
180 IFolder nodeFolder
= destinationFolder
.getFolder(nodeName
);
182 TracePackageElement
[] children
= traceElement
.getChildren();
183 SubMonitor childMonitor
= subMonitor
.newChild(1);
184 TraceUtils
.createFolder(nodeFolder
, childMonitor
);
186 for (TracePackageElement element
: children
) {
187 ModalContext
.checkCanceled(monitor
);
189 if (element
instanceof RemoteImportTraceFilesElement
) {
190 RemoteImportTraceFilesElement traceFilesElement
= (RemoteImportTraceFilesElement
) element
;
192 IFileStore remoteFile
= traceFilesElement
.getRemoteFile();
194 // Preserve folder structure
195 IPath sessionParentPath
= TmfTraceCoreUtils
.newSafePath(rootPath
).removeLastSegments(1);
196 IPath traceParentPath
= TmfTraceCoreUtils
.newSafePath(remoteFile
.getParent().toURI().getPath());
197 IPath relativeTracePath
= Path
.EMPTY
;
198 if (sessionParentPath
.isPrefixOf(traceParentPath
)) {
199 relativeTracePath
= traceParentPath
.makeRelativeTo(sessionParentPath
);
202 String
[] segments
= relativeTracePath
.segments();
203 for (int i
= 0; i
< segments
.length
; i
++) {
204 String segment
= TmfTraceCoreUtils
.validateName(TmfTraceCoreUtils
.safePathToString(segments
[i
]));
206 relativeTracePath
= new Path(segment
);
208 relativeTracePath
= relativeTracePath
.append(segment
);
212 IFolder traceFolder
= nodeFolder
.getFolder(new Path(relativeTracePath
.toOSString()));
213 childMonitor
= subMonitor
.newChild(1);
214 TraceUtils
.createFolder(traceFolder
, childMonitor
);
218 IResource traceRes
= null;
219 IFileInfo info
= remoteFile
.fetchInfo();
220 if (info
.isDirectory()) {
221 traceRes
= downloadDirectoryTrace(remoteFile
, traceFolder
, subMonitor
.newChild(1));
223 traceRes
= downloadFileTrace(remoteFile
, traceFolder
, subMonitor
.newChild(1));
226 String traceName
= traceElement
.getText();
227 if (traceRes
== null || !traceRes
.exists()) {
232 TraceTypeHelper traceTypeHelper
= null;
233 String traceTypeStr
= traceElement
.getTraceType();
234 if (traceTypeStr
!= null) {
235 traceTypeHelper
= TmfTraceType
.getTraceType(traceTypeStr
);
238 // no specific trace type found
239 if (traceTypeHelper
== null) {
241 // Try to auto-detect the trace typ
242 childMonitor
= subMonitor
.newChild(1);
243 childMonitor
.setTaskName(NLS
.bind(RemoteMessages
.RemoteImportTracesOperation_DetectingTraceType
, traceName
));
245 traceTypeHelper
= TmfTraceTypeUIUtils
.selectTraceType(traceRes
.getLocation().toOSString(), null, null);
246 } catch (TmfTraceImportException e
) {
247 // Could not figure out the type
251 if (traceTypeHelper
!= null) {
252 TmfTraceTypeUIUtils
.setTraceType(traceRes
, traceTypeHelper
);
253 fImportedResources
.add(traceRes
);
256 // Set source location
257 URI uri
= remoteFile
.toURI();
258 String sourceLocation
= URIUtil
.toUnencodedString(uri
);
259 traceRes
.setPersistentProperty(TmfCommonConstants
.SOURCE_LOCATION
, sourceLocation
);
265 // Download a directory trace
266 private IResource
downloadDirectoryTrace(IFileStore trace
, IFolder traceFolder
, IProgressMonitor monitor
) throws CoreException
, IOException
, InterruptedException
{
268 IFileStore
[] sources
= trace
.childStores(EFS
.NONE
, monitor
);
270 // Don't import just the metadata file
271 if (sources
.length
> 1) {
272 String traceName
= trace
.getName();
274 traceName
= TmfTraceCoreUtils
.validateName(traceName
);
276 IFolder folder
= traceFolder
.getFolder(traceName
);
277 String newName
= fConflictHandler
.checkAndHandleNameClash(folder
.getFullPath(), monitor
);
278 if (newName
== null) {
282 folder
= traceFolder
.getFolder(newName
);
283 folder
.create(true, true, null);
285 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, sources
.length
);
286 subMonitor
.beginTask(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
, sources
.length
);
288 for (IFileStore source
: sources
) {
289 if (subMonitor
.isCanceled()) {
290 throw new InterruptedException();
293 IPath destination
= folder
.getLocation().addTrailingSeparator().append(source
.getName());
294 IFileInfo info
= source
.fetchInfo();
295 // TODO allow for downloading index directory and files
296 if (!info
.isDirectory()) {
297 SubMonitor childMonitor
= subMonitor
.newChild(1);
298 childMonitor
.setTaskName(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
+ ' ' + trace
.getName() + '/' + source
.getName());
299 try (InputStream in
= source
.openInputStream(EFS
.NONE
, new NullProgressMonitor())) {
300 copy(in
, folder
, destination
, childMonitor
, info
.getLength());
304 folder
.refreshLocal(IResource
.DEPTH_INFINITE
, null);
310 // Download file trace
311 private IResource
downloadFileTrace(IFileStore trace
, IFolder traceFolder
, IProgressMonitor monitor
) throws CoreException
, IOException
, InterruptedException
{
313 IFolder folder
= traceFolder
;
314 String traceName
= trace
.getName();
316 traceName
= TmfTraceCoreUtils
.validateName(traceName
);
318 IResource resource
= folder
.findMember(traceName
);
319 if ((resource
!= null) && resource
.exists()) {
320 String newName
= fConflictHandler
.checkAndHandleNameClash(resource
.getFullPath(), monitor
);
321 if (newName
== null) {
326 SubMonitor subMonitor
= SubMonitor
.convert(monitor
, 1);
327 subMonitor
.beginTask(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
, 1);
329 IPath destination
= folder
.getLocation().addTrailingSeparator().append(traceName
);
330 IFileInfo info
= trace
.fetchInfo();
331 subMonitor
.setTaskName(RemoteMessages
.RemoteImportTracesOperation_DownloadTask
+ ' ' + trace
.getName() + '/' + trace
.getName());
332 try (InputStream in
= trace
.openInputStream(EFS
.NONE
, new NullProgressMonitor())) {
333 copy(in
, folder
, destination
, subMonitor
, info
.getLength());
335 folder
.refreshLocal(IResource
.DEPTH_INFINITE
, null);
336 return folder
.findMember(traceName
);
339 private void copy(InputStream in
, IFolder destFolder
, IPath destination
, SubMonitor monitor
, long length
) throws IOException
{
340 IFolder intermediateTempFolder
= null;
341 IFile tempFile
= null;
342 File intermediateFile
= null;
344 intermediateTempFolder
= fDestination
.getProject().getResource().getFolder(TRACE_IMPORT
);
345 if (intermediateTempFolder
.exists()) {
346 intermediateTempFolder
.delete(true, SubMonitor
.convert(monitor
));
348 intermediateTempFolder
.create(true, true, SubMonitor
.convert(monitor
));
349 tempFile
= intermediateTempFolder
.getFile(destination
.lastSegment());
350 tempFile
.create(null, true, SubMonitor
.convert(monitor
));
351 intermediateFile
= tempFile
.getLocation().toFile();
352 intermediateFile
.createNewFile();
353 copy(in
, intermediateFile
, length
, monitor
);
354 if (ArchiveUtil
.isArchiveFile(intermediateFile
)) {
355 // Select all the elements in the archive
356 FileSystemObjectImportStructureProvider importProvider
= new FileSystemObjectImportStructureProvider(FileSystemStructureProvider
.INSTANCE
, null);
357 IFileSystemObject fileSystemObject
= importProvider
.getIFileSystemObject(intermediateFile
);
358 TraceFileSystemElement rootTraceFileElement
= TraceFileSystemElement
.createRootTraceFileElement(fileSystemObject
, importProvider
);
360 // Select all the elements in the archive
361 List
<TraceFileSystemElement
> list
= new ArrayList
<>();
362 rootTraceFileElement
.getAllChildren(list
);
363 if (!destFolder
.exists()) {
364 destFolder
.create(true, true, SubMonitor
.convert(monitor
));
366 final IFolder folder
= destFolder
.getFolder(destination
.lastSegment());
367 if (!folder
.exists()) {
368 folder
.create(true, true, SubMonitor
.convert(monitor
));
371 final TraceValidateAndImportOperation operation
= new TraceValidateAndImportOperation(
372 fShell
, list
, null, intermediateTempFolder
.getLocation(), destFolder
.getFullPath(), false,
373 ImportTraceWizardPage
.OPTION_PRESERVE_FOLDER_STRUCTURE
| ImportTraceWizardPage
.OPTION_IMPORT_UNRECOGNIZED_TRACES
,
375 operation
.setConflictHandler(fConflictHandler
);
376 operation
.run(SubMonitor
.convert(monitor
));
379 // should be lightning fast unless someone maps different files
380 // to different physical disks. In windows and linux, moves are
381 // super fast on the same drive
382 Files
.move(intermediateFile
.toPath(), destination
.toFile().toPath(), StandardCopyOption
.REPLACE_EXISTING
);
384 } catch (CoreException
| InvocationTargetException
| InterruptedException e
) {
385 Activator
.getDefault().logError(e
.getMessage(), e
);
387 if (intermediateFile
!= null && intermediateFile
.exists()) {
388 intermediateFile
.delete();
391 if (tempFile
!= null && tempFile
.exists()) {
392 tempFile
.delete(true, SubMonitor
.convert(monitor
));
394 if (intermediateTempFolder
!= null && intermediateTempFolder
.exists()) {
395 intermediateTempFolder
.delete(true, SubMonitor
.convert(monitor
));
397 } catch (CoreException e
) {
398 Activator
.getDefault().logError(e
.getMessage(), e
);
403 private static void copy(InputStream in
, File intermediateFile
, long length
, SubMonitor monitor
) throws IOException
, FileNotFoundException
{
404 try (OutputStream out
= new FileOutputStream(intermediateFile
)) {
405 monitor
.setWorkRemaining((int) (length
/ BYTES_PER_KB
));
406 byte[] buf
= new byte[BYTES_PER_KB
* BUFFER_IN_KB
];
409 int n
= in
.read(buf
);
413 out
.write(buf
, 0, n
);
414 counter
= (counter
% BYTES_PER_KB
) + n
;
415 monitor
.worked(counter
/ BYTES_PER_KB
);
421 * Set the result status for this operation
426 protected void setStatus(IStatus status
) {
431 * Gets the result of the operation.
433 * @return result status of operation
435 public IStatus
getStatus() {
440 * Get the list of resources that were imported by this operation. An
441 * example use case would be to use this to open traces that were imported
444 * Note this includes only valid traces and doesn'tinclude unrecognized
447 * @return the trace resources that were imported
449 public List
<IResource
> getImportedResources() {
450 return fImportedResources
;