/***************************************************************************** * Copyright (c) 2008 g-Eclipse Consortium * 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 * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Szymon Mueller - initial API and implementation *****************************************************************************/ package eu.geclipse.core.filesystem; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.List; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IContainer; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; 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.SubProgressMonitor; import eu.geclipse.core.ExtensionManager; import eu.geclipse.core.filesystem.internal.Activator; import eu.geclipse.core.filesystem.internal.filesystem.ConnectionElement; import eu.geclipse.core.filesystem.internal.filesystem.GEclipseFileStore; import eu.geclipse.core.model.GridModel; import eu.geclipse.core.model.IGridElement; import eu.geclipse.core.model.ITransferInformation; import eu.geclipse.core.model.ITransferManager; import eu.geclipse.core.model.ITransferService; import eu.geclipse.core.reporting.ProblemException; /** * Manager class for handling all the transfers. */ public class TransferManager implements ITransferManager { private static final int INF = 10; private static final int HIGH = 1; private static final int MID = 2; private static final int LOW = 3; private static TransferManager singleton; int counter = 1; private List<TransferInformation> pendingTransfers; private final boolean useRepo = true; /** * Constructor of TransferManager class. It checks repository for unfinished * transfer operations and tries to resume them. */ private TransferManager() { TransferRepository repo = TransferRepository.getTransferRepository(); this.pendingTransfers = repo.getOperations(); } /** * Get singleton of the TransferManager * * @return singleton of this manager */ public static TransferManager getManager() { if( singleton == null ) { singleton = new TransferManager(); } return singleton; } public List<TransferInformation> getPendingTransfers() { return this.pendingTransfers; } public IStatus resumeTransfer( final ITransferInformation transfer, final IProgressMonitor monitor ) { IStatus status = new Status( IStatus.OK, Activator.PLUGIN_ID, "Transfer status", null ); ITransferService service = findService( transfer.getSource() .toURI() .getScheme(), transfer.getDestination().toURI().getScheme() ); status = service.transfer( transfer, transfer.isMove(), monitor ); try { IGridElement[] children = GridModel.getConnectionManager() .getChildren( monitor ); IContainer[] findContainersForLocationURI = GridModel.getRoot() .getResource() .getWorkspace() .getRoot() .findContainersForLocationURI( transfer.getDestination().toURI() ); for( IContainer cont : findContainersForLocationURI ) { cont.refreshLocal( IContainer.DEPTH_ONE, monitor ); } if( status.isOK() ) { for( IGridElement elem : children ) { if( elem instanceof ConnectionElement ) { recursiveRefreshChild( transfer.getDestination().toURI(), ( ConnectionElement )elem, monitor ); } } } } catch( ProblemException e ) { // TODO Auto-generated catch block Activator.logException( e ); } catch( CoreException e ) { // TODO Auto-generated catch block Activator.logException( e ); } if( this.useRepo ) { TransferRepository repo = TransferRepository.getTransferRepository(); repo.delete( transfer.getId() ); } return status; } /** * Method used to refresh all connection elements containing the transferred * element. * * @param uri of the transferred element. * @param element element on a connection tree which should be checked * @param monitor progress monitor */ private void recursiveRefreshChild( final URI uri, final ConnectionElement element, final IProgressMonitor monitor ) { ConnectionElement resultElement = null; try { String slaveScheme = new GEclipseURI( element.getConnectionFileStore().toURI() ).toSlaveURI().getScheme().toString(); if( uri.getScheme().equalsIgnoreCase( slaveScheme ) && element.hasChildren() ) { IGridElement[] children = element.getChildren( monitor ); for( IGridElement child : children ) { if( child instanceof ConnectionElement ) { URI slaveURI = new GEclipseURI( ( ( ConnectionElement )child ).getConnectionFileStore() .toURI() ).toSlaveURI(); if( slaveURI.equals( uri ) ) { resultElement = ( ConnectionElement )child; resultElement.refresh( monitor ); } else if( ( ( ConnectionElement )child ).hasChildren() ) { Path uriPath = new Path( uri.getPath() ); Path slavePath = new Path( slaveURI.getPath() ); boolean continueSearch = true; for( int i = 0; i < slavePath.segmentCount() && i < uriPath.segmentCount(); i++ ) { if( !slavePath.segment( i ).equals( uriPath.segment( i ) ) ) { continueSearch = false; } } if( continueSearch ) { recursiveRefreshChild( uri, ( ConnectionElement )child, monitor ); } } } } } } catch( ProblemException e ) { //TODO better error handling Activator.logException( e ); } catch( CoreException e ) { //TODO better error handling Activator.logException( e ); } } public IStatus startTransfer( final IFileStore sourceGecl, final IFileStore destinationGecl, final boolean moveFlag, final IProgressMonitor monitor ) { IStatus status = new Status( IStatus.OK, Activator.PLUGIN_ID, "Transfer status", null ); IFileStore source; IFileStore destination; if ( sourceGecl instanceof GEclipseFileStore ) { source = ((GEclipseFileStore) sourceGecl).getSlave(); } else { source = sourceGecl; } if ( destinationGecl instanceof GEclipseFileStore ) { destination = ((GEclipseFileStore) destinationGecl).getSlave(); } else { destination = destinationGecl; } ITransferService service = findService( source.toURI().getScheme(), destination.toURI().getScheme() ); Integer transferId = getNextKey(); TransferInformation op = new TransferInformation( transferId, source, destination, "", source.fetchInfo().getLength() ); // Should never happen, all transfer types should at least have basic // transfer operation available Assert.isNotNull( service, "No operation defined for this type of transfer" ); TransferRepository repo = TransferRepository.getTransferRepository(); if( this.useRepo ) { repo.save( op ); } if ( monitor.isCanceled() ) { throw new OperationCanceledException(); } status = service.transfer( op, moveFlag, monitor ); if( this.useRepo ) { repo.delete( transferId ); } return status; } private ITransferService findService( final String scheme1, final String scheme2 ) { ITransferService operation = null; ExtensionManager manager = new ExtensionManager(); List<IConfigurationElement> transfers = manager.getConfigurationElements( "eu.geclipse.core.filesystem.transferService", //$NON-NLS-1$ "transfer" ); //$NON-NLS-1$ int priority = TransferManager.INF; //search extensions for operation with highest priority for ( IConfigurationElement provider: transfers ) { if ( provider.getAttribute( "source" ).equalsIgnoreCase( scheme1 ) //$NON-NLS-1$ && provider.getAttribute( "target" ).equalsIgnoreCase( scheme2 ) //$NON-NLS-1$ && Integer.valueOf( provider.getAttribute( "priority" ) ).intValue() < priority ) { //$NON-NLS-1$ try { operation = (ITransferService) provider.createExecutableExtension( "class" ); //$NON-NLS-1$ priority = Integer.valueOf( provider.getAttribute( "priority" ) ).intValue(); //$NON-NLS-1$ } catch( CoreException e ) { //TODO better error handling Activator.logException( e ); } } } if( operation == null ) { operation = new DefaultTransferService(); } return operation; } private class DefaultTransferService implements ITransferService { /** * Copy the content of the specified file store to the destination file * store. If destination IFileStore exists, then it will be deleted before * the operation. * * @param transfer information about the transfer * @param isMove flag set to true if transfer is a move operation * @param monitor A progress monitor used to track the transfer. * @return A status object tracking errors of the transfer. */ public Status transfer( final ITransferInformation transfer, final boolean isMove, final IProgressMonitor monitor ) { Status status = new Status( IStatus.OK, Activator.PLUGIN_ID, "Transfer status", null ); boolean transferStatus = true; // Prepare copy operation IFileStore from = transfer.getSource(); IFileStore to = transfer.getDestination(); IFileStore targetFile = null; monitor.beginTask( Messages.getString( "TransferManager.copying_progress" ) + from.getName(), 100 ); //$NON-NLS-1$ monitor.setTaskName( Messages.getString( "TransferManager.copying_progress" ) + from.getName() ); //$NON-NLS-1$ InputStream inStream = null; OutputStream outStream = null; if( from instanceof GEclipseFileStore ) { ( ( GEclipseFileStore )from ).setActive( GEclipseFileStore.FETCH_INFO_ACTIVE_POLICY ); } try { IFileInfo sourceFileInfo = from.fetchInfo( EFS.NONE, new NullProgressMonitor() ); if( sourceFileInfo.exists() ) { if( !sourceFileInfo.isDirectory() ) { // File long length = sourceFileInfo.getLength(); // Open input stream if( transferStatus ) { try { inStream = from.openInputStream( EFS.NONE, new SubProgressMonitor( monitor, 8 ) ); } catch( CoreException cExc ) { transferStatus = false; status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, String.format( "Error opening input stream for %s", from.toURI() ), cExc ); } } // Open output stream if( transferStatus ) { try { IFileInfo fileInfo = to.fetchInfo(); if( fileInfo.exists() ) { if( fileInfo.isDirectory() ) { targetFile = to.getChild( from.getName() ); } else { targetFile = to.getParent().getChild( from.getName() ); } outStream = targetFile.openOutputStream( EFS.NONE, new SubProgressMonitor( monitor, 8 ) ); } else { transferStatus = false; status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, String.format( "Error opening input stream for %s", from.toURI() ), null ); } // TODO possible solution to possible bug >_< // if( targetFile.fetchInfo().exists() ) { // targetFile.delete( EFS.NONE, monitor ); // } } catch( CoreException cExc ) { transferStatus = false; status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, String.format( "Error opening output stream for %s", targetFile.toURI() ), cExc ); } } // Prepare copy operation if( transferStatus && ( inStream != null ) && ( outStream != null ) ) { byte[] buffer = new byte[ 1024 * 1024 ]; Integer totalKb = Integer.valueOf( ( int )Math.ceil( ( double )length / 1024 ) ); SubProgressMonitor subMonitor = new SubProgressMonitor( monitor, 84 ); subMonitor.beginTask( Messages.getString( "TransferManager.copying_progress" ) + from.getName(), ( int )length ); //$NON-NLS-1$ subMonitor.subTask( Messages.getString( "TransferManager.copying_progress" ) + from.getName() ); //$NON-NLS-1$ // Copy the data long byteCounter = 0; while( !subMonitor.isCanceled() && transferStatus ) { int bytesRead = -1; // Read data from source try { bytesRead = inStream.read( buffer ); } catch( IOException ioExc ) { transferStatus = false; status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, String.format( "Error reading from %s", from.toURI() ), ioExc ); } // Check if there is still data available if( bytesRead == -1 ) { break; } // Write data to target try { outStream.write( buffer, 0, bytesRead ); } catch( IOException ioExc ) { transferStatus = false; status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, String.format( "Error writing to %s", to.toURI() ), ioExc ); } byteCounter += bytesRead; subMonitor.worked( bytesRead ); subMonitor.subTask( String.format( Messages.getString( "TransferManager.transfer_progress_format" ), //$NON-NLS-1$ sourceFileInfo.getName(), Integer.valueOf( ( int )( 100. * byteCounter / length ) ), Integer.valueOf( ( int )( byteCounter / 1024 ) ), totalKb ) ); } subMonitor.done(); } } else { // Directory copyDirectory( from, to, isMove, monitor ); } } } catch( Exception exc ) { status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, String.format( "Error during transfering %s", from.toURI() ), exc ); } finally { monitor.subTask( Messages.getString( "TransferManager.closing_streams" ) ); //$NON-NLS-1$ // Close output stream if( outStream != null ) { try { outStream.close(); } catch( IOException ioExc ) { // just ignore this } } // Close input stream if( inStream != null ) { try { inStream.close(); } catch( IOException e ) { // just ignore this } } monitor.done(); } return status; } /** * Copies this specified directory file store and its children recursively to the * destination file store. * * @param from The source of the transfer. * @param to The target of the transfer. * @param monitor A progress monitor used to track the transfer. * @return A status object tracking errors of the transfer. */ private IStatus copyDirectory( final IFileStore from, final IFileStore to, final boolean isMove, final IProgressMonitor monitor ) { // Prepare operation IStatus status = Status.OK_STATUS; monitor.beginTask( Messages.getString("TransferManager.copying_progress") + from.getName(), 10 ); //$NON-NLS-1$ try { IFileStore[] children = null; try { GEclipseFileSystem.assureFileStoreIsActive( from ); children = from.childStores( EFS.NONE, new SubProgressMonitor( monitor, 1 ) ); } catch( CoreException cExc ) { status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, Messages.getString("TransferManager.fetching_children_error") + from.getName(), //$NON-NLS-1$ cExc ); } if ( status.isOK() && ( children != null ) ) { IFileStore newTo = null; if( to.fetchInfo().isDirectory() ) { newTo = to.getChild( from.getName() ); } else { newTo = to.getParent().getChild( from.getName() ); } IFileInfo newToInfo = newTo.fetchInfo(); boolean mkdir = !newToInfo.exists(); SubProgressMonitor subMonitor = new SubProgressMonitor( monitor, 9 ); subMonitor.beginTask( Messages.getString("TransferManager.copying_progress") //$NON-NLS-1$ + from.getName(), mkdir ? children.length + 1 : children.length ); if ( mkdir ) { try { newTo.mkdir( EFS.NONE, new SubProgressMonitor( subMonitor, 1 ) ); } catch( CoreException cExc ) { status = new Status( IStatus.ERROR, Activator.PLUGIN_ID, IStatus.OK, Messages.getString("TransferManager.create_dir_error") + newTo.getName(), //$NON-NLS-1$ cExc ); } } if ( status.isOK() ) { MultiStatus mStatus = new MultiStatus( Activator.PLUGIN_ID, IStatus.OK, Messages.getString("TransferManager.copying_members_status") //$NON-NLS-1$ + from.getName(), null ); for ( IFileStore child : children ) { // IStatus tempStatus = copy( child, newTo, new SubProgressMonitor( subMonitor, 1) ); TransferInformation childTransfer = new TransferInformation( 0, child, newTo, "", 0 ); status = transfer( childTransfer, isMove, new SubProgressMonitor( subMonitor, 1 ) ); if ( subMonitor.isCanceled() ) { break; } } if ( !mStatus.isOK() ) { status = mStatus; } } } } finally { monitor.done(); } return status; } } private synchronized Integer getNextKey() { return Integer.valueOf( this.counter++ ); } public int registerTransfer( final IFileStore source, final IFileStore destination ) { // TODO Auto-generated method stub TransferRepository repo = TransferRepository.getTransferRepository(); Integer transferId = getNextKey(); TransferInformation op = new TransferInformation( transferId, source, destination, "", 0 ); repo.save( op ); return transferId.intValue(); } public void unregisterTransfer( final Integer transferID ) { TransferRepository repo = TransferRepository.getTransferRepository(); repo.delete( transferID ); } }