17940: Synchronize outgoing changes opens a connection per file
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTreeBuilder.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTreeBuilder.java
index d758cba..d287041 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTreeBuilder.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTreeBuilder.java
@@ -26,6 +26,7 @@
 import org.eclipse.team.internal.ccvs.core.ICVSFolder;
 import org.eclipse.team.internal.ccvs.core.ICVSRemoteResource;
 import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.ICVSRunnable;
 import org.eclipse.team.internal.ccvs.core.Policy;
 import org.eclipse.team.internal.ccvs.core.client.Command;
 import org.eclipse.team.internal.ccvs.core.client.Session;
@@ -169,22 +170,20 @@
 			monitor.beginTask(null, 100);
 	
 			Policy.checkCanceled(monitor);
-			Session session = new Session(repository, root, false);
-			session.open(Policy.subMonitorFor(monitor, 10));
-			try {
-				Policy.checkCanceled(monitor);
-				fetchDelta(session, Session.CURRENT_LOCAL_FOLDER, Policy.subMonitorFor(monitor, 50));
-				if (projectDoesNotExist) {
-					// We cannot handle the case where a project (i.e. the top-most CVS folder)
-					// has been deleted directly on the sever (i.e. deleted using rm -rf)
-					if (root.isCVSFolder() && ! root.isManaged()) {
-						throw new CVSException(Policy.bind("RemoteFolderTreeBuild.folderDeletedFromServer", root.getFolderSyncInfo().getRepository())); //$NON-NLS-1$
-					} else {
-						return null;
-					}
+			Session.run(repository, root, false, new ICVSRunnable() {
+				public void run(IProgressMonitor monitor) throws CVSException {
+					Policy.checkCanceled(monitor);
+					fetchDelta(new ICVSResource[] { root }, monitor);
 				}
-			} finally {
-				session.close();
+			}, Policy.subMonitorFor(monitor, 50));
+			if (projectDoesNotExist) {
+				// We cannot handle the case where a project (i.e. the top-most CVS folder)
+				// has been deleted directly on the sever (i.e. deleted using rm -rf)
+				if (root.isCVSFolder() && ! root.isManaged()) {
+					throw new CVSException(Policy.bind("RemoteFolderTreeBuild.folderDeletedFromServer", root.getFolderSyncInfo().getRepository())); //$NON-NLS-1$
+				} else {
+					return null;
+				}
 			}
 			// We need a second session because of the use of a different handle on the same remote resource
 			// Perhaps we could support the changing of a sessions root as long as
@@ -193,40 +192,36 @@
 				new RemoteFolderTree(null, root.getName(), repository,
 					new Path(root.getFolderSyncInfo().getRepository()),
 					tagForRemoteFolder(root, tag));
-			session = new Session(repository, remoteRoot, false);
-			session.open(Policy.subMonitorFor(monitor, 10));
-			try {
-				// Set up an infinite progress monitor for the recursive build
-				IProgressMonitor subProgress = Policy.infiniteSubMonitorFor(monitor, 30);
-				subProgress.beginTask(null, 512);
-				// Build the remote tree
-				buildRemoteTree(session, root, remoteRoot, Path.EMPTY, subProgress);
-				// we can only fecth the status for up to 1024 files in a single connection due to
-				// the server which has a limit on the number of "open" files.
-				if (!changedFiles.isEmpty() && changedFiles.size() <= MAX_REVISION_FETCHES_PER_CONNECTION) {
-					fetchFileRevisions(session, (String[])changedFiles.toArray(new String[changedFiles.size()]), Policy.subMonitorFor(monitor, 20));
+			Session.run(repository, remoteRoot, false, new ICVSRunnable() {
+				public void run(IProgressMonitor monitor) throws CVSException {
+					// Set up an infinite progress monitor for the recursive build
+					IProgressMonitor subProgress = Policy.infiniteSubMonitorFor(monitor, 30);
+					subProgress.beginTask(null, 512);
+					// Build the remote tree
+					buildRemoteTree(root, remoteRoot, Path.EMPTY, subProgress);
+					// we can only fetch the status for up to 1024 files in a single connection due to
+					// the server which has a limit on the number of "open" files.
+					if (!changedFiles.isEmpty() && changedFiles.size() <= MAX_REVISION_FETCHES_PER_CONNECTION) {
+						fetchFileRevisions((ICVSResource[])changedFiles.toArray(new ICVSResource[changedFiles.size()]), Policy.subMonitorFor(monitor, 20));
+					}
 				}
-			} finally {
-				session.close();
-			}
+			}, Policy.infiniteSubMonitorFor(monitor, 30));
 			
 			// If there were more than 1024 changed files, we need a connection per each 1024
 			if (!changedFiles.isEmpty() && changedFiles.size() > MAX_REVISION_FETCHES_PER_CONNECTION) {
-				String[] allChangedFiles = (String[])changedFiles.toArray(new String[changedFiles.size()]);
+				ICVSResource[] allChangedFiles = (ICVSResource[])changedFiles.toArray(new String[changedFiles.size()]);
 				int iterations = (allChangedFiles.length / MAX_REVISION_FETCHES_PER_CONNECTION) 
 					+ (allChangedFiles.length % MAX_REVISION_FETCHES_PER_CONNECTION == 0 ? 0 : 1);
 				for (int i = 0; i < iterations ; i++) {
 					int length = Math.min(MAX_REVISION_FETCHES_PER_CONNECTION, 
 						allChangedFiles.length - (MAX_REVISION_FETCHES_PER_CONNECTION * i));
-					String buffer[] = new String[length];
+					final ICVSResource buffer[] = new ICVSResource[length];
 					System.arraycopy(allChangedFiles, i * MAX_REVISION_FETCHES_PER_CONNECTION, buffer, 0, length);
-					session = new Session(repository, remoteRoot, false);
-					session.open(Policy.subMonitorFor(monitor, 1));
-					try {
-						fetchFileRevisions(session, buffer, Policy.subMonitorFor(monitor, 2));
-					} finally {
-						session.close();
-					}
+					Session.run(repository, remoteRoot, false, new ICVSRunnable() {
+						public void run(IProgressMonitor monitor) throws CVSException {
+							fetchFileRevisions(buffer, monitor);
+						}
+					}, Policy.subMonitorFor(monitor, 2));
 				}
 			}
 			
@@ -238,7 +233,7 @@
 		}
 	}
 	
-	private RemoteFile buildTree(ICVSFile file, IProgressMonitor monitor) throws CVSException {
+	private RemoteFile buildTree(final ICVSFile file, IProgressMonitor monitor) throws CVSException {
 		QuietOption quietness = CVSProviderPlugin.getPlugin().getQuietness();
 		try {
 			CVSProviderPlugin.getPlugin().setQuietness(Command.VERBOSE);
@@ -247,22 +242,21 @@
 	
 			// Query the server to see if there is a delta available
 			Policy.checkCanceled(monitor);
-			Session session = new Session(repository, root, false);
-			session.open(Policy.subMonitorFor(monitor, 10));
-			try {
-				Policy.checkCanceled(monitor);
-				fetchDelta(session, file.getName(), Policy.subMonitorFor(monitor, 50));
-				if (projectDoesNotExist) {
-					return null;
+			Session.run(repository, root, false, new ICVSRunnable() {
+				public void run(IProgressMonitor monitor) throws CVSException {
+					Policy.checkCanceled(monitor);
+					fetchDelta(new ICVSResource[] { file }, Policy.subMonitorFor(monitor, 50));
 				}
-			} finally {
-				session.close();
+			}, Policy.subMonitorFor(monitor, 50));
+			if (projectDoesNotExist) {
+				return null;
 			}
 			// Create a parent for the remote resource
 			remoteRoot =
 				new RemoteFolderTree(null, root.getName(), repository,
 					new Path(root.getFolderSyncInfo().getRepository()),
 					tagForRemoteFolder(root, tag));
+
 			// Create the remote resource (using the delta if there is one)
 			RemoteFile remoteFile;
 			Map deltas = (Map)fileDeltas.get(Path.EMPTY);
@@ -286,13 +280,11 @@
 			if (!changedFiles.isEmpty()) {
 				// Add the remote folder to the remote folder lookup table (used to update file revisions)
 				remoteFolderTable.put(new Path(remoteRoot.getFolderSyncInfo().getRemoteLocation()), remoteRoot);
-				session = new Session(repository, remoteRoot, false);
-				session.open(Policy.subMonitorFor(monitor, 10));
-				try {
-					fetchFileRevisions(session, (String[])changedFiles.toArray(new String[changedFiles.size()]), Policy.subMonitorFor(monitor, 20));
-				} finally {
-					session.close();
-				}
+				Session.run(repository, remoteRoot, false, new ICVSRunnable() {
+					public void run(IProgressMonitor monitor) throws CVSException {
+						fetchFileRevisions((ICVSResource[])changedFiles.toArray(new ICVSResource[changedFiles.size()]), monitor);
+					}
+				}, Policy.subMonitorFor(monitor, 20));
 			}
 			return remoteFile;
 			
@@ -363,7 +355,7 @@
 	 * 
 	 * Does 1 work for each file and folder delta processed
 	 */
-	private void buildRemoteTree(Session session, ICVSFolder local, RemoteFolderTree remote, IPath localPath, IProgressMonitor monitor) throws CVSException {
+	private void buildRemoteTree(ICVSFolder local, RemoteFolderTree remote, IPath localPath, IProgressMonitor monitor) throws CVSException {
 		
 		Policy.checkCanceled(monitor);
 		
@@ -375,7 +367,7 @@
 		
 		// If there's no corresponding local resource then we need to fetch its contents in order to populate the deltas
 		if (local == null) {
-			fetchNewDirectory(session, remote, localPath, monitor);
+			fetchNewDirectory(remote, monitor);
 		}
 		
 		// Fetch the delta's for the folder
@@ -390,7 +382,7 @@
 			for (int i=0;i<folders.length;i++) {
 				ICVSFolder folder = (ICVSFolder)folders[i];
 				DeltaNode d = (DeltaNode)deltas.get(folder.getName());
-				if (folder.isCVSFolder() && ! isOrphanedSubtree(session, folder) && (d==null || d.getRevision() != DELETED)) {
+				if (folder.isCVSFolder() && ! isOrphanedSubtree(folder) && (d==null || d.getRevision() != DELETED)) {
 					children.put(folders[i].getName(), 
 						new RemoteFolderTree(remote, folders[i].getName(), repository, 
 							new Path(folder.getFolderSyncInfo().getRepository()), 
@@ -469,7 +461,7 @@
 					localFolder = null;
 				else
 					localFolder = local.getFolder(name);
-				buildRemoteTree(session, localFolder, remoteFolder, localPath.append(name), monitor);
+				buildRemoteTree(localFolder, remoteFolder, localPath.append(name), monitor);
 				// Record any children that are empty
 				if (pruneEmptyDirectories() && remoteFolder.getChildren().length == 0) {
 					// Prune if the local folder is also empty.
@@ -501,11 +493,11 @@
 	 * 
 	 * Returns the list of changed files
 	 */
-	private List fetchDelta(Session session, String argument, final IProgressMonitor monitor) throws CVSException {
+	private List fetchDelta(final ICVSResource[] arguments, final IProgressMonitor monitor) throws CVSException {
 		
 		// Create an listener that will accumulate new and removed files and folders
 		final List newChildDirectories = new ArrayList();
-		IUpdateMessageListener listener = new IUpdateMessageListener() {
+		final IUpdateMessageListener listener = new IUpdateMessageListener() {
 			public void directoryInformation(ICVSFolder root, IPath path, boolean newDirectory) {
 				if (newDirectory) {
 					// Record new directory with parent so it can be retrieved when building the parent
@@ -543,7 +535,11 @@
 									break;
 					case Update.STATE_DELETED : // We have a locally removed file that still exists remotely
 					case Update.STATE_REMOTE_CHANGES : // We have an remote change to an unmodified local file
-								changedFiles.add(filename);
+								try {
+									changedFiles.add(root.getFile(filename));
+								} catch (CVSException e) {
+									CVSProviderPlugin.log(e.getStatus());
+								}
 								recordDelta(new Path(filename), UNKNOWN, type);
 								monitor.subTask(Policy.bind("RemoteFolderTreeBuilder.receivingDelta", filename)); //$NON-NLS-1$
 								break;
@@ -558,12 +554,16 @@
 		// Perform a "cvs -n update -d [-r tag] ." in order to get the
 		// messages from the server that will indicate what has changed on the 
 		// server.
-		IStatus status = Command.UPDATE.execute(session,
-			new GlobalOption[] { Command.DO_NOT_CHANGE },
-			updateLocalOptions,
-			new String[] { argument },
-			new UpdateListener(listener),
-			monitor);
+		Session.run(repository, root, false, new ICVSRunnable() {
+			public void run(IProgressMonitor monitor) throws CVSException {
+				IStatus status = Command.UPDATE.execute(
+					new GlobalOption[] { Command.DO_NOT_CHANGE },
+					updateLocalOptions,
+					arguments,
+					new UpdateListener(listener),
+					monitor);
+			}
+		}, monitor);
 		return changedFiles;
 	}
 	/*
@@ -571,10 +571,10 @@
 	 * 
 	 * The fetch may do up to 2 units of work in the provided monitor.
 	 */
-	private void fetchNewDirectory(Session session, RemoteFolderTree newFolder, IPath localPath, final IProgressMonitor monitor) throws CVSException {
+	private void fetchNewDirectory(final RemoteFolderTree newFolder, final IProgressMonitor monitor) throws CVSException {
 		
 		// Create an listener that will accumulate new files and folders
-		IUpdateMessageListener listener = new IUpdateMessageListener() {
+		final IUpdateMessageListener listener = new IUpdateMessageListener() {
 			public void directoryInformation(ICVSFolder root, IPath path, boolean newDirectory) {
 				if (newDirectory) {
 					// Record new directory with parent so it can be retrieved when building the parent
@@ -587,7 +587,11 @@
 			}
 			public void fileInformation(int type, ICVSFolder root, String filename) {
 				// NOTE: Check path prefix
-				changedFiles.add(filename);
+				try {
+					changedFiles.add(root.getFile(filename));
+				} catch (CVSException e) {
+					CVSProviderPlugin.log(e.getStatus());
+				}
 				recordDelta(new Path(filename), ADDED, type);
 				monitor.subTask(Policy.bind("RemoteFolderTreeBuilder.receivingDelta", filename)); //$NON-NLS-1$
 			}
@@ -595,42 +599,44 @@
 			}
 		};
 
-		// NOTE: Should use the path relative to the remoteRoot
-		IPath path = new Path(newFolder.getRepositoryRelativePath());
-		IStatus status = Command.UPDATE.execute(session,
-			new GlobalOption[] { Command.DO_NOT_CHANGE },
-			updateLocalOptions,
-			new String[] { localPath.toString() },
-			new UpdateListener(listener),
-			Policy.subMonitorFor(monitor, 1)); 
-		if (status.getCode() == CVSStatus.SERVER_ERROR) {
-			// FIXME: This should be refactored (maybe static methods on CVSException?)
-			CVSServerException e = new CVSServerException(status);
-			if ( ! e.isNoTagException() && e.containsErrors())
-				throw e;
-			// we now know that this is an exception caused by a cvs bug.
-			// if the folder has no files in it (just subfolders) cvs does not respond with the subfolders...
-			// workaround: retry the request with no tag to get the directory names (if any)
-			Policy.checkCanceled(monitor);
-			status = Command.UPDATE.execute(session,
-				new GlobalOption[] { Command.DO_NOT_CHANGE },
-				getOptionsWithoutTag(),
-				new String[] { localPath.toString() },
-				new UpdateListener(listener),
-				Policy.subMonitorFor(monitor, 1));
-			if (status.getCode() == CVSStatus.SERVER_ERROR) {
-				throw new CVSServerException(status);
+		Session.run(repository, remoteRoot, false, new ICVSRunnable() {
+			public void run(IProgressMonitor monitor) throws CVSException {
+				IStatus status = Command.UPDATE.execute(
+					new GlobalOption[] { Command.DO_NOT_CHANGE },
+					updateLocalOptions,
+					new ICVSResource[] { newFolder },
+					new UpdateListener(listener),
+					monitor); 
+				if (status.getCode() == CVSStatus.SERVER_ERROR) {
+					// FIXME: This should be refactored (maybe static methods on CVSException?)
+					CVSServerException e = new CVSServerException(status);
+					if ( ! e.isNoTagException() && e.containsErrors())
+						throw e;
+					// we now know that this is an exception caused by a cvs bug.
+					// if the folder has no files in it (just subfolders) cvs does not respond with the subfolders...
+					// workaround: retry the request with no tag to get the directory names (if any)
+					Policy.checkCanceled(monitor);
+					status = Command.UPDATE.execute(
+						new GlobalOption[] { Command.DO_NOT_CHANGE },
+						getOptionsWithoutTag(),
+						new ICVSResource[] { newFolder },
+						new UpdateListener(listener),
+						monitor);
+					if (status.getCode() == CVSStatus.SERVER_ERROR) {
+						throw new CVSServerException(status);
+					}
+				}
 			}
-		}
+		}, Policy.subMonitorFor(monitor, 1));
 	}
 	
 	// Get the file revisions for the given filenames
-	private void fetchFileRevisions(Session session, String[] fileNames, final IProgressMonitor monitor) throws CVSException {
+	private void fetchFileRevisions(final ICVSResource[] files, final IProgressMonitor monitor) throws CVSException {
 		
 		// Create a listener for receiving the revision info
 		final Map revisions = new HashMap();
 		final List exceptions = new ArrayList();
-		IStatusListener listener = new IStatusListener() {
+		final IStatusListener listener = new IStatusListener() {
 			public void fileStatus(ICVSFolder root, IPath path, String remoteRevision) {
 				try {
 					updateRevision(path, remoteRevision);
@@ -642,17 +648,22 @@
 		};
 			
 		// Perform a "cvs status..." with a custom message handler
-		IStatus status = Command.STATUS.execute(session,
-			Command.NO_GLOBAL_OPTIONS,
-			Command.NO_LOCAL_OPTIONS,
-			fileNames,
-			new StatusListener(listener),
-			monitor);
-		if (status.getCode() == CVSStatus.SERVER_ERROR) {
-			throw new CVSServerException(status);
-		}
+		Session.run(repository, remoteRoot, false, new ICVSRunnable() {
+			public void run(IProgressMonitor monitor) throws CVSException {
+				IStatus status = Command.STATUS.execute(
+					Command.NO_GLOBAL_OPTIONS,
+					Command.NO_LOCAL_OPTIONS,
+					files,
+					new StatusListener(listener),
+					monitor);
+				if (status.getCode() == CVSStatus.SERVER_ERROR) {
+					throw new CVSServerException(status);
+				}
+			}
+		}, monitor);
+
 		
-		// Report any exceptions that occured fecthing the revisions
+		// Report any exceptions that occured fetching the revisions
 		if ( ! exceptions.isEmpty()) {
 			if (exceptions.size() == 1) {
 				throw (CVSException)exceptions.get(0);
@@ -711,8 +722,8 @@
 		return tag == null ? folder.getFolderSyncInfo().getTag() : tag;
 	}
 	
-	private boolean isOrphanedSubtree(Session session, ICVSFolder mFolder) throws CVSException {
-		return mFolder.isCVSFolder() && ! mFolder.isManaged() && ! mFolder.equals(session.getLocalRoot()) && mFolder.getParent().isCVSFolder();
+	private boolean isOrphanedSubtree(ICVSFolder mFolder) throws CVSException {
+		return mFolder.isCVSFolder() && ! mFolder.isManaged() && ! mFolder.equals(remoteRoot) && mFolder.getParent().isCVSFolder();
 	}
 }