/**
* Aptana Studio
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions).
* Please see the license.html included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.syncing.core.old;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.CRC32;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.internal.filesystem.Policy;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import com.aptana.core.io.efs.EFSUtils;
import com.aptana.core.io.efs.SyncUtils;
import com.aptana.core.io.vfs.IExtendedFileInfo;
import com.aptana.core.io.vfs.IExtendedFileStore;
import com.aptana.core.logging.IdeLog;
import com.aptana.core.util.FileUtil;
import com.aptana.filewatcher.FileWatcher;
import com.aptana.ide.core.io.IConnectionPoint;
import com.aptana.ide.core.io.preferences.PermissionDirection;
import com.aptana.ide.core.io.preferences.PreferenceUtils;
import com.aptana.ide.syncing.core.SyncingPlugin;
/**
* @author Kevin Lindsey
*/
@SuppressWarnings("restriction")
public class Synchronizer implements ILoggable
{
public static final QualifiedName SYNC_IN_PROGRESS = new QualifiedName(Synchronizer.class.getPackage().getName(),
"SYNC_IN_PROGRESS"); //$NON-NLS-1$
private static final int DEFAULT_TIME_TOLERANCE = 1000;
private boolean _useCRC;
private boolean _includeCloakedFiles = false;
private long _timeTolerance;
private int _clientDirectoryCreatedCount;
private int _clientDirectoryDeletedCount;
private int _clientFileDeletedCount;
private int _clientFileTransferedCount;
private int _serverDirectoryCreatedCount;
private int _serverDirectoryDeletedCount;
private int _serverFileDeletedCount;
private int _serverFileTransferedCount;
private IConnectionPoint _clientFileManager;
private IConnectionPoint _serverFileManager;
private IFileStore _clientFileRoot;
private IFileStore _serverFileRoot;
private ISyncEventHandler _eventHandler;
private ILogger logger;
private List<IFileStore> _newFilesDownloaded;
private List<IFileStore> _newFilesUploaded;
/**
* Constructs a Synchronizer with default parameters.
*/
public Synchronizer()
{
this(false, DEFAULT_TIME_TOLERANCE, false);
}
/**
* Constructs a Synchronizer with specified CRC flag and tolerance time.
*
* @param calculateCrc
* A flag indicating whether two files should be compared by their CRC when their modification times
* match
* @param timeTolerance
* The number of milliseconds a client and server file can differ in their modification times to still be
* considered equal
*/
public Synchronizer(boolean calculateCrc, int timeTolerance)
{
this(calculateCrc, timeTolerance, false);
}
/**
* Constructs a Synchronizer with specified CRC flag and tolerance time.
*
* @param calculateCrc
* A flag indicating whether two files should be compared by their CRC when their modification times
* match
* @param timeTolerance
* The number of milliseconds a client and server file can differ in their modification times to still be
* considered equal
* @param includeCloakedFiles
* Do we synchronize files marked as cloaked?
*/
public Synchronizer(boolean calculateCrc, int timeTolerance, boolean includeCloakedFiles)
{
if (timeTolerance < 0)
{
// makes sure time is positive
timeTolerance = -timeTolerance;
}
this._useCRC = calculateCrc;
this._includeCloakedFiles = includeCloakedFiles;
this._timeTolerance = timeTolerance;
_newFilesDownloaded = new ArrayList<IFileStore>();
_newFilesUploaded = new ArrayList<IFileStore>();
}
/**
* Logs a message.
*
* @param message
* the message to be logged
*/
protected void log(String message)
{
if (this.logger != null)
{
this.logger.logInfo(message, null);
}
}
/**
* Convert the full path of the specified file into a canonical form. This will remove the base directory set by the
* file's server and it will convert all '\' characters to '/' characters.
*
* @param file
* The file to use when computing the canonical path
* @return the file's canonical path
*/
public static String getCanonicalPath(IFileStore root, IFileStore file)
{
String basePath = null;
String result = null;
try
{
basePath = EFSUtils.getAbsolutePath(root);
if (basePath == null)
{
return null;
}
String filePath = EFSUtils.getAbsolutePath(file);
if (filePath == null)
{
return null;
}
result = filePath.substring(basePath.length());
if (result.indexOf('\\') != -1)
{
result = result.replace('\\', '/');
}
if (result.startsWith("/")) //$NON-NLS-1$
{
result = result.substring(1);
}
}
catch (StringIndexOutOfBoundsException e)
{
throw new IllegalArgumentException(MessageFormat.format(Messages.Synchronizer_FileNotContained,
EFSUtils.getAbsolutePath(file), basePath));
}
return result;
}
/**
* Returns the number of directories that were created on the client.
*
* @return the number of directories created on the client
*/
public int getClientDirectoryCreatedCount()
{
return this._clientDirectoryCreatedCount;
}
/**
* Returns the number of directories that were deleted from the client.
*
* @return the number of directories deleted from the client
*/
public int getClientDirectoryDeletedCount()
{
return this._clientDirectoryDeletedCount;
}
/**
* Returns the number of files that were deleted from the client.
*
* @return the number of files deleted from the client
*/
public int getClientFileDeletedCount()
{
return this._clientFileDeletedCount;
}
/**
* Returns the number of files that were transferred to the server.
*
* @return the number of files transferred to the server
*/
public int getClientFileTransferedCount()
{
return this._clientFileTransferedCount;
}
/**
* Gets the current sync event handler.
*
* @return the event handler for syncing
*/
public ISyncEventHandler getEventHandler()
{
return this._eventHandler;
}
/**
* Sets the current sync event handler
*
* @param eventHandler
* the event handler for syncing
*/
public void setEventHandler(ISyncEventHandler eventHandler)
{
this._eventHandler = eventHandler;
}
/**
* Returns the number of directories that were created on the server.
*
* @return the number of directories created on the server
*/
public int getServerDirectoryCreatedCount()
{
return this._serverDirectoryCreatedCount;
}
/**
* Returns the number of directories that were deleted from the server.
*
* @return the number of directories deleted from the server
*/
public int getServerDirectoryDeletedCount()
{
return this._serverDirectoryDeletedCount;
}
/**
* Returns the number of files that were deleted from the server.
*
* @return the number of files deleted from the server
*/
public int getServerFileDeletedCount()
{
return this._serverFileDeletedCount;
}
/**
* Returns the number of files that were transferred to the client.
*
* @return the number of files that were transferred to the client
*/
public int getServerFileTransferedCount()
{
return this._serverFileTransferedCount;
}
public IFileStore[] getNewFilesDownloaded()
{
return _newFilesDownloaded.toArray(new IFileStore[_newFilesDownloaded.size()]);
}
public IFileStore[] getNewFilesUploaded()
{
return _newFilesUploaded.toArray(new IFileStore[_newFilesUploaded.size()]);
}
public void setClientFileRoot(IFileStore client)
{
_clientFileRoot = client;
}
public void setServerFileRoot(IFileStore server)
{
_serverFileRoot = server;
}
/**
* Gets the list of items to sync.
*
* @param client
* the client
* @param server
* the server
* @return an array of item pairs to sync
* @throws IOException
* @throws VirtualFileManagerException
* @throws ConnectionException
* @throws CoreException
*/
public VirtualFileSyncPair[] getSyncItems(IConnectionPoint clientPoint, IConnectionPoint serverPoint,
IFileStore client, IFileStore server, IProgressMonitor monitor) throws CoreException
{
// store references to file managers
setClientFileManager(clientPoint);
setServerFileManager(serverPoint);
setClientFileRoot(client);
setServerFileRoot(server);
IFileStore[] clientFiles = new IFileStore[0];
IFileStore[] serverFiles = new IFileStore[0];
IFileInfo clientInfo = client.fetchInfo();
if (!clientInfo.exists())
{
throw new CoreException(new Status(IStatus.ERROR, SyncingPlugin.PLUGIN_ID, MessageFormat.format(
Messages.Synchronizer_ERR_RootNotExist, client.toString())));
}
IFileInfo serverInfo = server.fetchInfo();
if (!serverInfo.exists())
{
throw new CoreException(new Status(IStatus.ERROR, SyncingPlugin.PLUGIN_ID, MessageFormat.format(
Messages.Synchronizer_ERR_RootNotExist, server.toString())));
}
try
{
setClientEventHandler(client, server);
if (!clientInfo.isDirectory() || !serverInfo.isDirectory())
{
if (clientInfo.exists())
{
clientFiles = new IFileStore[] { client };
}
if (serverInfo.exists())
{
serverFiles = new IFileStore[] { server };
}
}
else
{
// get the complete file listings for the client and server
log(FileUtil.NEW_LINE);
log(MessageFormat.format(Messages.Synchronizer_Gathering_Source, new Object[] { client.toString() }));
long start = System.currentTimeMillis();
clientFiles = EFSUtils.getFiles(client, true, _includeCloakedFiles, monitor);
log(MessageFormat.format(Messages.Synchronizer_Completed, System.currentTimeMillis() - start));
start = System.currentTimeMillis();
log(FileUtil.NEW_LINE);
log(MessageFormat.format(Messages.Synchronizer_Gathering_Destination,
new Object[] { server.toString() }));
serverFiles = EFSUtils.getFiles(server, true, _includeCloakedFiles, monitor);
log(MessageFormat.format(Messages.Synchronizer_Completed, System.currentTimeMillis() - start));
log(FileUtil.NEW_LINE);
log(Messages.Synchronizer_Listing_Complete);
}
}
finally
{
// we just throw exceptions back up the tree
removeClientEventHandler(client, server);
}
if (!syncContinue(monitor))
{
return null;
}
return createSyncItems(clientFiles, serverFiles, monitor);
}
/**
* @param clientFiles
* @param serverFiles
* @param monitor
* TODO
* @return VirtualFileSyncPair[]
* @throws ConnectionException
* @throws VirtualFileManagerException
* @throws IOException
* @throws CoreException
*/
public VirtualFileSyncPair[] createSyncItems(IFileStore[] clientFiles, IFileStore[] serverFiles,
IProgressMonitor monitor) throws CoreException
{
log(FileUtil.NEW_LINE + Messages.Synchronizer_Generating_Comparison);
Map<String, VirtualFileSyncPair> fileList = new HashMap<String, VirtualFileSyncPair>();
// reset statistics and clear lists
this.reset();
monitor = Policy.monitorFor(monitor);
Policy.checkCanceled(monitor);
// add all client files by default
for (int i = 0; i < clientFiles.length; i++)
{
if (!syncContinue(monitor))
return null;
Policy.checkCanceled(monitor);
monitor.worked(1);
IFileStore clientFile = clientFiles[i];
if (clientFile.fetchInfo().getAttribute(EFS.ATTRIBUTE_SYMLINK))
continue;
String relativePath = getCanonicalPath(_clientFileRoot, clientFile);
VirtualFileSyncPair item = new VirtualFileSyncPair(clientFile, null, relativePath, SyncState.ClientItemOnly);
fileList.put(item.getRelativePath(), item);
}
// remove matching server files with the same modification date/time
for (int i = 0; i < serverFiles.length; i++)
{
if (!syncContinue(monitor))
return null;
Policy.checkCanceled(monitor);
monitor.worked(1);
IFileStore serverFile = serverFiles[i];
IFileInfo serverFileInfo = serverFile.fetchInfo(IExtendedFileStore.DETAILED, null);
String relativePath = getCanonicalPath(_serverFileRoot, serverFile);
logDebug(FileUtil.NEW_LINE);
logDebug(MessageFormat.format(Messages.Synchronizer_Comparing_Files, new Object[] { relativePath }));
if (!fileList.containsKey(relativePath)) // Server only
{
if (serverFileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK))
continue;
VirtualFileSyncPair item = new VirtualFileSyncPair(null, serverFile, relativePath,
SyncState.ServerItemOnly);
fileList.put(relativePath, item);
logDebug(Messages.Synchronizer_Item_Not_On_Destination);
continue;
}
// Client and server
// get client sync item already in our file list
VirtualFileSyncPair item = fileList.get(relativePath);
// associate this server file with that sync item
item.setDestinationFile(serverFile);
IFileInfo clientFileInfo = item.getSourceFileInfo(monitor);
if (clientFileInfo == null && item.getSyncState() == SyncState.ServerItemOnly)
{
// This is an item we've seen already. Continue on.
continue;
}
if (clientFileInfo.isDirectory() != serverFileInfo.isDirectory())
{
// this only occurs if one file is a directory and the other
// is not a directory
item.setSyncState(SyncState.IncompatibleFileTypes);
logDebug(Messages.Synchronizer_Incompatible_Types);
continue;
}
if (serverFileInfo.isDirectory())
{
fileList.remove(relativePath);
logDebug(Messages.Synchronizer_Directory);
continue;
}
// calculate modification time difference, taking server
// offset into account
long serverFileTime = serverFileInfo.getLastModified();
long clientFileTime = clientFileInfo.getLastModified();
long timeDiff = serverFileTime - clientFileTime;
logDebug(MessageFormat.format(Messages.Synchronizer_Times_Modified, new long[] { clientFileTime,
serverFileTime }));
// check modification date
if (-this._timeTolerance <= timeDiff && timeDiff <= this._timeTolerance)
{
if (this._useCRC && !serverFileInfo.isDirectory())
{
item.setSyncState(this.compareCRC(item));
}
else
{
item.setSyncState(SyncState.ItemsMatch);
logDebug(Messages.Synchronizer_Items_Identical);
}
}
else
{
if (timeDiff < 0)
{
item.setSyncState(SyncState.ClientItemIsNewer);
logDebug(MessageFormat.format(Messages.Synchronizer_Source_Newer,
new long[] { Math.round(Math.abs(timeDiff / 1000)) }));
}
else
{
item.setSyncState(SyncState.ServerItemIsNewer);
logDebug(MessageFormat.format(Messages.Synchronizer_Destination_Newer,
new long[] { Math.round(Math.abs(timeDiff / 1000)) }));
}
}
}
// sort items
Set<String> keySet = fileList.keySet();
String[] keys = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keys);
// create modifiable list
VirtualFileSyncPair[] syncItems = new VirtualFileSyncPair[keys.length];
for (int i = 0; i < keys.length; i++)
{
syncItems[i] = fileList.get(keys[i]);
}
// long end = System.currentTimeMillis();
// System.out.println(end - start);
// return results
return syncItems;
}
/**
* @param client
* @param server
*/
private void setClientEventHandler(IFileStore client, IFileStore server)
{
// client.getFileManager().setEventHandler(this._eventHandler);
// server.getFileManager().setEventHandler(this._eventHandler);
}
/**
* @param client
* @param server
*/
private void removeClientEventHandler(IFileStore client, IFileStore server)
{
// client.getFileManager().setEventHandler(null);
// server.getFileManager().setEventHandler(null);
}
/**
* getTimeTolerance
*
* @return Returns the timeTolerance.
*/
public long getTimeTolerance()
{
return this._timeTolerance;
}
/**
* setTimeTolerance
*
* @param timeTolerance
* The timeTolerance to set.
*/
public void setTimeTolerance(int timeTolerance)
{
this._timeTolerance = timeTolerance;
}
/**
* setCalculateCrc
*
* @param calculateCrc
* The calculateCrc to set.
*/
public void setUseCRC(boolean calculateCrc)
{
this._useCRC = calculateCrc;
}
/**
* isCalculateCrc
*
* @return Returns the calculateCrc.
*/
public boolean getUseCRC()
{
return this._useCRC;
}
/**
* compareCRC
*
* @param item
* @return SyncState
* @throws CoreException
*/
private int compareCRC(VirtualFileSyncPair item) throws CoreException
{
InputStream clientStream = item.getSourceInputStream();
InputStream serverStream = item.getDestinationInputStream();
int result;
if (clientStream != null && serverStream != null)
{
// get individual CRC's
long clientCRC = getCRC(clientStream);
long serverCRC = getCRC(serverStream);
// close streams
try
{
clientStream.close();
serverStream.close();
}
catch (IOException e)
{
IdeLog.logError(SyncingPlugin.getDefault(),
MessageFormat.format(Messages.Synchronizer_ErrorClosingStreams, item.getRelativePath()), e);
}
result = (clientCRC == serverCRC) ? SyncState.ItemsMatch : SyncState.CRCMismatch;
}
else
{
// NOTE: clientStream can only equal serverStream if both are null,
// so we assume the files match in that case
result = (clientStream == serverStream) ? SyncState.ItemsMatch : SyncState.CRCMismatch;
}
return result;
}
/**
* getCRC
*
* @param stream
* @return CRC
*/
private long getCRC(InputStream stream)
{
CRC32 crc = new CRC32();
try
{
byte[] buffer = new byte[1024];
int length;
while ((length = stream.read(buffer)) != -1)
{
crc.update(buffer, 0, length);
}
}
catch (IOException e)
{
IdeLog.logError(SyncingPlugin.getDefault(), Messages.Synchronizer_ErrorRetrievingCRC, e);
}
return crc.getValue();
}
// public void cancelAllOperations()
// {
// if (this._clientFileManager != null)
// {
// this._clientFileManager.cancel();
// }
// if (this._serverFileManager != null)
// {
// this._serverFileManager.cancel();
// }
// }
/**
* Download to the client all files on the server that are newer or that only exist on the server
*
* @param fileList
* @return success
* @throws ConnectionException
* @throws VirtualFileManagerException
*/
public boolean download(VirtualFileSyncPair[] fileList, IProgressMonitor monitor)
{
return downloadAndDelete(fileList, false, monitor);
}
/**
* Download to the client all files on the server that are newer or delete files on the client that don't exist on
* the server
*
* @param fileList
* @return success
* @throws ConnectionException
* @throws VirtualFileManagerException
*/
public boolean downloadAndDelete(VirtualFileSyncPair[] fileList, IProgressMonitor monitor)
{
return downloadAndDelete(fileList, true, monitor);
}
/**
* downloadAndDelete
*
* @param fileList
* @param delete
* @return success
* @throws CoreException
* @throws ConnectionException
* @throws VirtualFileManagerException
*/
public boolean downloadAndDelete(VirtualFileSyncPair[] fileList, boolean delete, IProgressMonitor monitor)
{
FileWatcher.avoidNotify();
try
{
checkFileManagers();
logBeginDownloading();
boolean result = true;
int totalItems = fileList.length;
// IConnectionPoint client = getClientFileManager();
this.reset();
SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.Synchronizer_Downloading_Files,
fileList.length);
Policy.checkCanceled(subMonitor);
FILE_LOOP: for (int i = 0; i < fileList.length; i++)
{
final VirtualFileSyncPair item = fileList[i];
final IFileStore clientFile = item.getSourceFile();
final IFileStore serverFile = item.getDestinationFile();
setSyncItemDirection(item, false, true);
SubMonitor childMonitor = subMonitor.newChild(1);
childMonitor.setTaskName(getSyncStatus(item));
try
{
final IFileInfo clientFileInfo = item.getSourceFileInfo();
final IFileInfo serverFileInfo = item.getDestinationFileInfo();
// fire event
if (!syncEvent(item, i, totalItems, childMonitor))
{
delete = false;
break;
}
Policy.checkCanceled(childMonitor);
switch (item.getSyncState())
{
case SyncState.ClientItemOnly:
// only exists on client; checks if it needs to be deleted
if (delete)
{
// Need to query first because deletion makes isDirectory always return false
boolean wasDirectory = clientFileInfo.isDirectory();
clientFile.delete(EFS.NONE, null);
if (wasDirectory)
{
this._clientDirectoryDeletedCount++;
}
else
{
this._clientFileDeletedCount++;
}
}
syncDone(item, childMonitor);
break;
case SyncState.ServerItemOnly:
IFileStore targetClientFile = EFSUtils.createFile(_serverFileRoot,
item.getDestinationFile(), _clientFileRoot);
boolean exists = targetClientFile.fetchInfo().exists();
if (serverFileInfo.isDirectory())
{
logCreatedDirectory(targetClientFile);
if (!exists)
{
targetClientFile.mkdir(EFS.NONE, null);
this._clientDirectoryCreatedCount++;
_newFilesDownloaded.add(targetClientFile);
// update permissions for the newly created directory
updatePermissions(serverFile, targetClientFile, false,
PermissionDirection.DOWNLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
logDownloading(serverFile);
try
{
SyncUtils
.copy(serverFile, serverFileInfo, targetClientFile, EFS.NONE, childMonitor);
Synchronizer.this._serverFileTransferedCount++;
_newFilesDownloaded.add(targetClientFile);
// update permissions for the newly created file
if (!exists)
{
updatePermissions(serverFile, targetClientFile, true,
PermissionDirection.DOWNLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
break;
case SyncState.ServerItemIsNewer:
case SyncState.CRCMismatch:
// exists on both sides, but the server item is newer
logDownloading(serverFile);
if (serverFileInfo.isDirectory())
{
try
{
EFSUtils.setModificationTime(serverFileInfo.getLastModified(), clientFile);
}
catch (CoreException e)
{
logError(e);
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
try
{
SyncUtils.copy(serverFile, serverFileInfo, clientFile, EFS.NONE, childMonitor);
Synchronizer.this._serverFileTransferedCount++;
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
break;
default:
syncDone(item, childMonitor);
break;
}
}
catch (Exception ex)
{
IdeLog.logError(SyncingPlugin.getDefault(), Messages.Synchronizer_ErrorDuringSync, ex);
result = false;
if (!syncError(item, ex, childMonitor))
{
break FILE_LOOP;
}
}
}
return result;
}
finally
{
FileWatcher.resumeNotify();
}
}
/**
* Returns a string describing what's going on during the synchronization
*
* @param item
* @return
*/
private String getSyncStatus(VirtualFileSyncPair item)
{
if (item.getSyncDirection() == VirtualFileSyncPair.Direction_ClientToServer)
{
return MessageFormat.format(Messages.Synchronizer_Uploading, item.getRelativePath());
}
if (item.getSyncDirection() == VirtualFileSyncPair.Direction_ServerToClient)
{
return MessageFormat.format(Messages.Synchronizer_Downloading, item.getRelativePath());
}
return MessageFormat.format(Messages.Synchronizer_Skipping_File, item.getRelativePath());
}
/**
* fullSync
*
* @param fileList
* @return success
*/
public boolean fullSync(VirtualFileSyncPair[] fileList, IProgressMonitor monitor)
{
return this.fullSyncAndDelete(fileList, false, false, monitor);
}
/**
* fullSyncAndDelete
*
* @param fileList
* @return success
*/
public boolean fullSyncAndDelete(VirtualFileSyncPair[] fileList, IProgressMonitor monitor)
{
return this.fullSyncAndDelete(fileList, true, true, monitor);
}
/**
* fullSyncAndDelete
*
* @param fileList
* @param delete
* @return success
*/
public boolean fullSyncAndDelete(VirtualFileSyncPair[] fileList, boolean deleteLocal, boolean deleteRemote,
IProgressMonitor monitor)
{
FileWatcher.avoidNotify();
try
{
logBeginFullSyncing();
// assume we'll be successful
boolean result = true;
int totalItems = fileList.length;
// reset stats
this.reset();
SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.Synchronizer_Synchronizing, fileList.length);
Policy.checkCanceled(subMonitor);
// process all items in our list
FILE_LOOP: for (int i = 0; i < fileList.length; i++)
{
final VirtualFileSyncPair item = fileList[i];
final IFileStore clientFile = item.getSourceFile();
final IFileStore serverFile = item.getDestinationFile();
setSyncItemDirection(item, false, true);
SubMonitor childMonitor = subMonitor.newChild(1);
childMonitor.setTaskName(getSyncStatus(item));
try
{
final IFileInfo clientFileInfo = item.getSourceFileInfo(childMonitor);
final IFileInfo serverFileInfo = item.getDestinationFileInfo(childMonitor);
// fire event
if (!syncEvent(item, i, totalItems, childMonitor))
{
result = false;
break FILE_LOOP;
}
Policy.checkCanceled(childMonitor);
switch (item.getSyncState())
{
case SyncState.ClientItemIsNewer:
// item exists on both ends, but the client one is newer
logUploading(serverFile);
if (clientFileInfo.isDirectory())
{
EFSUtils.setModificationTime(clientFileInfo.getLastModified(), serverFile);
logSuccess();
syncDone(item, childMonitor);
}
else
{
try
{
SyncUtils.copy(clientFile, clientFileInfo, serverFile, EFS.NONE, childMonitor);
Synchronizer.this._clientFileTransferedCount++;
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
break;
case SyncState.ClientItemOnly:
// only exists on client
if (deleteLocal)
{
// need to query first because deletion causes isDirectory to always return false
boolean wasDirectory = clientFileInfo.isDirectory();
// deletes the item
clientFile.delete(EFS.NONE, null);
if (wasDirectory)
{
this._clientDirectoryDeletedCount++;
}
else
{
this._clientFileDeletedCount++;
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
// creates the item on server
IFileStore targetServerFile = EFSUtils.createFile(_clientFileRoot,
item.getSourceFile(), _serverFileRoot);
boolean exists = targetServerFile.fetchInfo().exists();
if (clientFileInfo.isDirectory())
{
logCreatedDirectory(targetServerFile);
if (!exists)
{
targetServerFile.mkdir(EFS.NONE, null);
this._serverDirectoryCreatedCount++;
_newFilesUploaded.add(targetServerFile);
// update permissions for the newly created directory
updatePermissions(clientFile, targetServerFile, false,
PermissionDirection.UPLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
logUploading(clientFile);
try
{
SyncUtils.copy(clientFile, clientFileInfo, targetServerFile, EFS.NONE,
childMonitor);
Synchronizer.this._clientFileTransferedCount++;
_newFilesUploaded.add(targetServerFile);
// update permissions for the newly created file
if (!exists)
{
updatePermissions(clientFile, targetServerFile, true,
PermissionDirection.UPLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
}
break;
case SyncState.ServerItemIsNewer:
// item exists on both ends, but the server one is newer
logDownloading(clientFile);
if (serverFileInfo.isDirectory())
{
// just needs to set the modification time for directory
EFSUtils.setModificationTime(serverFileInfo.getLastModified(), clientFile);
logSuccess();
syncDone(item, childMonitor);
}
else
{
try
{
SyncUtils.copy(serverFile, serverFileInfo, clientFile, EFS.NONE, childMonitor);
Synchronizer.this._serverFileTransferedCount++;
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
break;
case SyncState.ServerItemOnly:
// only exists on client
if (deleteRemote)
{
// need to query first because deletion causes isDirectory to always return false
boolean wasDirectory = serverFileInfo.isDirectory();
// deletes the item
serverFile.delete(EFS.NONE, null); // server.deleteFile(serverFile);
if (wasDirectory)
{
this._serverDirectoryDeletedCount++;
}
else
{
this._serverFileDeletedCount++;
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
// creates the item on client
IFileStore targetClientFile = EFSUtils.createFile(_serverFileRoot,
item.getDestinationFile(), _clientFileRoot);
boolean exists = targetClientFile.fetchInfo().exists();
if (serverFileInfo.isDirectory())
{
logCreatedDirectory(targetClientFile);
if (!exists)
{
targetClientFile.mkdir(EFS.NONE, null);
this._clientDirectoryCreatedCount++;
_newFilesDownloaded.add(targetClientFile);
// update permissions for the newly created directory
updatePermissions(serverFile, targetClientFile, false,
PermissionDirection.DOWNLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
logDownloading(targetClientFile);
try
{
SyncUtils.copy(serverFile, serverFileInfo, targetClientFile, EFS.NONE,
childMonitor);
Synchronizer.this._serverFileTransferedCount++;
_newFilesDownloaded.add(targetClientFile);
// update permissions for the newly created file
if (!exists)
{
updatePermissions(serverFile, targetClientFile, true,
PermissionDirection.DOWNLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
}
break;
case SyncState.CRCMismatch:
result = false;
IdeLog.logError(
SyncingPlugin.getDefault(),
MessageFormat.format(Messages.Synchronizer_FullSyncCRCMismatches,
item.getRelativePath()), (Throwable) null);
if (!syncError(item, null, childMonitor))
{
break FILE_LOOP;
}
break;
case SyncState.Ignore:
// ignore this file
break;
default:
break;
}
}
catch (Exception ex)
{
IdeLog.logError(SyncingPlugin.getDefault(), Messages.Synchronizer_ErrorDuringSync, ex);
result = false;
if (!syncError(item, ex, childMonitor))
{
break FILE_LOOP;
}
}
}
return result;
}
finally
{
FileWatcher.resumeNotify();
}
}
/**
* resetStats
*/
private void reset()
{
this._clientDirectoryCreatedCount = 0;
this._clientDirectoryDeletedCount = 0;
this._clientFileDeletedCount = 0;
this._clientFileTransferedCount = 0;
this._serverDirectoryCreatedCount = 0;
this._serverDirectoryDeletedCount = 0;
this._serverFileDeletedCount = 0;
this._serverFileTransferedCount = 0;
this._newFilesDownloaded.clear();
this._newFilesUploaded.clear();
}
/**
* Upload to the server all files on the client that are newer or that only exist on the client
*
* @param fileList
* @return success
* @throws ConnectionException
* @throws VirtualFileManagerException
*/
public boolean upload(VirtualFileSyncPair[] fileList, IProgressMonitor monitor)
{
return uploadAndDelete(fileList, false, monitor);
}
/**
* Upload to the server all files on the client that are newer or delete files on the server that don't exist on the
* client
*
* @param fileList
* @return success
* @throws ConnectionException
* @throws VirtualFileManagerException
*/
public boolean uploadAndDelete(VirtualFileSyncPair[] fileList, IProgressMonitor monitor)
{
return uploadAndDelete(fileList, true, monitor);
}
/**
* uploadAndDelete
*
* @param fileList
* @param delete
* @return success
* @throws ConnectionException
* @throws VirtualFileManagerException
*/
public boolean uploadAndDelete(VirtualFileSyncPair[] fileList, boolean delete, IProgressMonitor monitor)
{
FileWatcher.avoidNotify();
try
{
checkFileManagers();
logBeginUploading();
// IConnectionPoint server = getServerFileManager();
boolean result = true;
int totalItems = fileList.length;
this.reset();
SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.Synchronizer_Uploading_Files, fileList.length);
Policy.checkCanceled(subMonitor);
FILE_LOOP: for (int i = 0; i < fileList.length; i++)
{
final VirtualFileSyncPair item = fileList[i];
final IFileStore clientFile = item.getSourceFile();
final IFileStore serverFile = item.getDestinationFile();
setSyncItemDirection(item, false, true);
SubMonitor childMonitor = subMonitor.newChild(1);
childMonitor.setTaskName(getSyncStatus(item));
try
{
final IFileInfo clientFileInfo = item.getSourceFileInfo(childMonitor);
final IFileInfo serverFileInfo = item.getDestinationFileInfo(childMonitor);
// fire event
if (!syncEvent(item, i, totalItems, childMonitor))
{
result = false;
break;
}
Policy.checkCanceled(childMonitor);
switch (item.getSyncState())
{
case SyncState.ClientItemOnly:
// only exists on client; creates the item on server
IFileStore targetServerFile = EFSUtils.createFile(_clientFileRoot, item.getSourceFile(),
_serverFileRoot);
boolean exists = targetServerFile.fetchInfo().exists();
if (clientFileInfo.isDirectory())
{
if (!exists)
{
targetServerFile.mkdir(EFS.NONE, null);
this._serverDirectoryCreatedCount++;
_newFilesUploaded.add(targetServerFile);
// update permissions for the newly created directory
updatePermissions(clientFile, targetServerFile, false, PermissionDirection.UPLOAD,
childMonitor);
}
syncDone(item, childMonitor);
}
else
{
logUploading(clientFile);
try
{
SyncUtils
.copy(clientFile, clientFileInfo, targetServerFile, EFS.NONE, childMonitor);
Synchronizer.this._clientFileTransferedCount++;
_newFilesUploaded.add(targetServerFile);
// update permissions for the newly created file
if (!exists)
{
updatePermissions(clientFile, targetServerFile, true,
PermissionDirection.UPLOAD, childMonitor);
}
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
break;
case SyncState.ServerItemOnly:
// only exists on server; checks if it needs to be deleted
if (delete)
{
// Need to query if directory first because deletion makes isDirectory always return
// false.
boolean wasDirectory = serverFileInfo.isDirectory();
serverFile.delete(EFS.NONE, childMonitor);
if (wasDirectory)
{
this._serverDirectoryDeletedCount++;
}
else
{
this._serverFileDeletedCount++;
}
}
syncDone(item, childMonitor);
break;
case SyncState.ClientItemIsNewer:
case SyncState.CRCMismatch:
// exists on both sides, but the client item is newer
logUploading(clientFile);
if (clientFileInfo.isDirectory())
{
// just needs to set the modification time for directory
try
{
EFSUtils.setModificationTime(clientFileInfo.getLastModified(), serverFile);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
logSuccess();
syncDone(item, childMonitor);
}
else
{
try
{
SyncUtils.copy(clientFile, clientFileInfo, serverFile, EFS.NONE, childMonitor);
Synchronizer.this._clientFileTransferedCount++;
logSuccess();
syncDone(item, childMonitor);
}
catch (CoreException e)
{
logError(e);
if (!syncError(item, e, childMonitor))
{
result = false;
break FILE_LOOP;
}
}
}
break;
default:
syncDone(item, childMonitor);
break;
}
}
catch (Exception ex)
{
IdeLog.logError(SyncingPlugin.getDefault(), Messages.Synchronizer_ErrorDuringSync, ex);
result = false;
if (!syncError(item, ex, childMonitor))
{
break FILE_LOOP;
}
}
}
return result;
}
finally
{
FileWatcher.resumeNotify();
}
}
private static void setSyncItemDirection(VirtualFileSyncPair item, boolean upload, boolean full)
{
int direction = VirtualFileSyncPair.Direction_None;
switch (item.getSyncState())
{
case SyncState.Unknown:
case SyncState.Ignore:
case SyncState.ItemsMatch:
case SyncState.IncompatibleFileTypes:
break;
case SyncState.CRCMismatch:
if (upload)
{
direction = VirtualFileSyncPair.Direction_ClientToServer;
}
else if (!full)
{
direction = VirtualFileSyncPair.Direction_ServerToClient;
}
break;
case SyncState.ClientItemIsNewer:
case SyncState.ClientItemOnly:
if (upload || full)
{
direction = VirtualFileSyncPair.Direction_ClientToServer;
}
break;
case SyncState.ServerItemIsNewer:
case SyncState.ServerItemOnly:
if (!upload || full)
{
direction = VirtualFileSyncPair.Direction_ServerToClient;
}
break;
}
item.setSyncDirection(direction);
}
private static void updatePermissions(IFileStore sourceFileStore, IFileStore targetFileStore, boolean isFile,
PermissionDirection direction, IProgressMonitor monitor)
{
if (PreferenceUtils.getUpdatePermissions(direction))
{
IFileInfo targetFileInfo = getFileInfo(targetFileStore, monitor);
long permissions = 0;
if (PreferenceUtils.getSpecificPermissions(direction))
{
// use specified permissions from preferences
permissions = isFile ? PreferenceUtils.getFilePermissions(direction) : PreferenceUtils
.getFolderPermissions(direction);
}
else
{
// uses source's permissions
IFileInfo sourceFileInfo = getFileInfo(sourceFileStore, monitor);
if (sourceFileInfo != null)
{
permissions = getPermissions(sourceFileInfo);
}
}
if (permissions > 0)
{
if (targetFileInfo instanceof IExtendedFileInfo)
{
((IExtendedFileInfo) targetFileInfo).setPermissions(permissions);
}
else
{
targetFileInfo.setAttribute(EFS.ATTRIBUTE_OWNER_READ,
(permissions & IExtendedFileInfo.PERMISSION_OWNER_READ) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_OWNER_WRITE,
(permissions & IExtendedFileInfo.PERMISSION_OWNER_WRITE) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_OWNER_EXECUTE,
(permissions & IExtendedFileInfo.PERMISSION_OWNER_EXECUTE) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_READ,
(permissions & IExtendedFileInfo.PERMISSION_GROUP_READ) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_WRITE,
(permissions & IExtendedFileInfo.PERMISSION_GROUP_WRITE) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE,
(permissions & IExtendedFileInfo.PERMISSION_GROUP_EXECUTE) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_READ,
(permissions & IExtendedFileInfo.PERMISSION_OTHERS_READ) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_WRITE,
(permissions & IExtendedFileInfo.PERMISSION_OTHERS_WRITE) != 0);
targetFileInfo.setAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE,
(permissions & IExtendedFileInfo.PERMISSION_OTHERS_EXECUTE) != 0);
}
}
try
{
if (targetFileInfo instanceof IExtendedFileInfo)
{
targetFileStore.putInfo(targetFileInfo, IExtendedFileInfo.SET_PERMISSIONS, monitor);
}
else
{
targetFileStore.putInfo(targetFileInfo, EFS.SET_ATTRIBUTES, monitor);
}
}
catch (CoreException e)
{
IdeLog.logWarning(SyncingPlugin.getDefault(), "Failed to update permissions for " + targetFileStore, e); //$NON-NLS-1$
}
}
}
private static IFileInfo getFileInfo(IFileStore fileStore, IProgressMonitor monitor)
{
IFileInfo fileInfo = (IFileInfo) fileStore.getAdapter(IFileInfo.class);
if (fileInfo != null)
{
return fileInfo;
}
try
{
return fileStore.fetchInfo(EFS.NONE, monitor);
}
catch (CoreException e)
{
// ignores the exception
}
return null;
}
private static long getPermissions(IFileInfo fileInfo)
{
if (fileInfo instanceof IExtendedFileInfo)
{
return ((IExtendedFileInfo) fileInfo).getPermissions();
}
long permissions = 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_OWNER_READ) ? IExtendedFileInfo.PERMISSION_OWNER_READ : 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_OWNER_WRITE) ? IExtendedFileInfo.PERMISSION_OWNER_WRITE : 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_OWNER_EXECUTE) ? IExtendedFileInfo.PERMISSION_OWNER_EXECUTE
: 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_GROUP_READ) ? IExtendedFileInfo.PERMISSION_GROUP_READ : 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_GROUP_WRITE) ? IExtendedFileInfo.PERMISSION_GROUP_WRITE : 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE) ? IExtendedFileInfo.PERMISSION_GROUP_EXECUTE
: 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_OTHER_READ) ? IExtendedFileInfo.PERMISSION_OTHERS_READ : 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_OTHER_WRITE) ? IExtendedFileInfo.PERMISSION_OTHERS_WRITE : 0;
permissions |= fileInfo.getAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE) ? IExtendedFileInfo.PERMISSION_OTHERS_EXECUTE
: 0;
return permissions;
}
/**
* @return Returns the clientFileManager.
*/
public IConnectionPoint getClientFileManager()
{
return this._clientFileManager;
}
/**
* @param fileManager
* The clientFileManager to set.
*/
public void setClientFileManager(IConnectionPoint fileManager)
{
this._clientFileManager = fileManager;
}
/**
* @return Returns the serverFileManager.
*/
public IConnectionPoint getServerFileManager()
{
return this._serverFileManager;
}
/**
* @param fileManager
* The serverFileManager to set.
*/
public void setServerFileManager(IConnectionPoint fileManager)
{
this._serverFileManager = fileManager;
}
/**
* Resets time tolerance.
*/
public void resetTimeTolerance()
{
this._timeTolerance = DEFAULT_TIME_TOLERANCE;
}
/**
* @see com.com.aptana.ide.syncing.core.old.ILoggable#getLogger()
*/
public ILogger getLogger()
{
return this.logger;
}
/**
* @see com.com.aptana.ide.syncing.core.old.ILoggable#setLogger(com.com.aptana.ide.syncing.core.old.ILogger)
*/
public void setLogger(ILogger logger)
{
this.logger = logger;
}
private void checkFileManagers()
{
if (getClientFileManager() == null)
{
throw new NullPointerException(Messages.Synchronizer_ClientFileManagerCannotBeNull);
}
if (getServerFileManager() == null)
{
throw new NullPointerException(Messages.Synchronizer_ServerFileManagerCannotBeNull);
}
}
private void logBeginDownloading()
{
log(FileUtil.NEW_LINE + FileUtil.NEW_LINE
+ MessageFormat.format(Messages.Synchronizer_BeginningDownload, getTimestamp()));
}
private void logBeginFullSyncing()
{
log(FileUtil.NEW_LINE + FileUtil.NEW_LINE
+ MessageFormat.format(Messages.Synchronizer_BeginningFullSync, getTimestamp()));
}
private void logBeginUploading()
{
log(FileUtil.NEW_LINE + FileUtil.NEW_LINE
+ MessageFormat.format(Messages.Synchronizer_BeginningUpload, getTimestamp()));
}
private void logCreatedDirectory(IFileStore file)
{
log(FileUtil.NEW_LINE
+ MessageFormat.format(Messages.Synchronizer_CreatedDirectory, EFSUtils.getAbsolutePath(file)));
}
private void logDownloading(IFileStore file)
{
log(FileUtil.NEW_LINE + MessageFormat.format(Messages.Synchronizer_Downloading, EFSUtils.getAbsolutePath(file)));
}
private void logDebug(String message)
{
// log(message);
}
private void logError(Exception e)
{
IdeLog.logError(SyncingPlugin.getDefault(), e);
if (this.logger != null)
{
if (e.getCause() != null)
{
log(MessageFormat.format(Messages.Synchronizer_Error_Extended, new Object[] { e.getLocalizedMessage(),
e.getCause().getLocalizedMessage() }));
}
else
{
log(MessageFormat.format(Messages.Synchronizer_Error, e.getLocalizedMessage()));
}
}
}
private void logSuccess()
{
log(Messages.Synchronizer_Success);
}
private void logUploading(IFileStore file)
{
log(FileUtil.NEW_LINE + MessageFormat.format(Messages.Synchronizer_Uploading, EFSUtils.getAbsolutePath(file)));
}
private void syncDone(VirtualFileSyncPair item, IProgressMonitor monitor)
{
if (this._eventHandler != null)
{
this._eventHandler.syncDone(item, monitor);
}
if (monitor != null)
{
monitor.worked(1);
}
}
private boolean syncError(VirtualFileSyncPair item, Exception e, IProgressMonitor monitor)
{
return this._eventHandler == null || this._eventHandler.syncErrorEvent(item, e, monitor);
}
private boolean syncEvent(VirtualFileSyncPair item, int index, int totalItems, IProgressMonitor monitor)
{
return this._eventHandler == null || this._eventHandler.syncEvent(item, index, totalItems, monitor);
}
private boolean syncContinue(IProgressMonitor monitor)
{
return this._eventHandler == null || this._eventHandler.syncContinue(monitor);
}
private static String getTimestamp()
{
Date d = new Date();
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
return df.format(d);
}
public void disconnect()
{
try
{
getClientFileManager().disconnect(null);
getServerFileManager().disconnect(null);
}
catch (CoreException e)
{
// TODO Auto-generated catch block
IdeLog.logError(SyncingPlugin.getDefault(), e);
}
}
}