Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCache.java277
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCacheEntry.java232
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java2
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java64
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSWorkspaceSubscriberTest.java41
5 files changed, 415 insertions, 201 deletions
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCache.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCache.java
index 67b985c0c..210ca912b 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCache.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCache.java
@@ -10,15 +10,7 @@
*******************************************************************************/
package org.eclipse.team.core.sync;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@@ -27,8 +19,8 @@ import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.jobs.ILock;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.core.Policy;
import org.eclipse.team.internal.core.TeamPlugin;
@@ -47,10 +39,12 @@ public class RemoteContentsCache {
private static Map caches = new HashMap(); // String (local name) > RemoteContentsCache
private String name;
- private Map cacheFileNames;
- private Map cacheFileTimes;
+ private Map cacheEntries;
private long lastCacheCleanup;
private int cacheDirSize;
+
+ // Lock used to serialize the writting of cache contents
+ private ILock lock = Platform.getJobManager().newLock();
/**
* Enables the use of remote contents caching for the given cacheId. The cache ID must be unique.
@@ -89,31 +83,19 @@ public class RemoteContentsCache {
* @param cacheId the unique Id of the cache
* @throws TeamException if the cached contents could not be deleted from disk
*/
- public static void disableCache(String cacheId) throws TeamException {
+ public static void disableCache(String cacheId) {
RemoteContentsCache cache = getCache(cacheId);
if (cache == null) {
- // There is no cahce to dispose of
+ // There is no cache to dispose of
return;
}
- cache.deleteCacheDirectory();
caches.remove(cacheId);
- }
-
- /**
- * Return the <code>java.io.File</code> that contains the same contents as the remote resource
- * identified by the <local name, qualified name> pair. It is up to the owner of the cache to use
- * an appropriate qualified name that uniquely identified remote versions of a file.
- *
- * @param id
- * @return
- * @throws TeamException if the cache is not enabled
- */
- public static synchronized File getFile(QualifiedName id) throws TeamException {
- RemoteContentsCache cache = getCache(id.getLocalName());
- if (cache == null) {
- throw new TeamException(Policy.bind("RemoteContentsCache.cacheNotEnabled", id.getLocalName())); //$NON-NLS-1$
+ try {
+ cache.deleteCacheDirectory();
+ } catch (TeamException e) {
+ // Log the exception and continue
+ TeamPlugin.log(e);
}
- return cache.getFile(id.getQualifier());
}
/**
@@ -130,179 +112,49 @@ public class RemoteContentsCache {
}
/**
- * Return the <code>java.io.File</code> that contains the same contents as the remote resource
- * identified by the given unique id. It is up to the owner of the cache to use
- * an appropriate id that uniquely identified remote versions of a file.
- * @param id
- * @return
- */
- public synchronized File getFile(String id) {
- if (cacheFileNames == null) {
- // This probably means that the cache has been disposed
- throw new IllegalStateException(Policy.bind("RemoteContentsCache.cacheDisposed", name)); //$NON-NLS-1$
- }
- String physicalPath;
- if (cacheFileNames.containsKey(id)) {
- // cache hit
- physicalPath = (String)cacheFileNames.get(id);
- registerHit(id);
- } else {
- // cache miss
- physicalPath = String.valueOf(cacheDirSize++);
- cacheFileNames.put(id, physicalPath);
- registerHit(id);
- clearOldCacheEntries();
- }
- return getCacheFileForPhysicalPath(physicalPath);
- }
-
- /**
- * Return the InputStream that contains the cached contents for the given id. If an error occurs
- * reading the cached contents then the cache entry is automatically removed to allow the contents
- * to be refetched.
- *
- * @param id
- * @return an InputStream containing the cached contents or null if no contents are cached
- * @throws TeamException
- */
- public synchronized InputStream getContents(String id) throws TeamException {
- if (!cacheFileNames.containsKey(id)) {
- // The contents are not cached
- return null;
- }
- File ioFile = getFile(id);
- try {
- try {
- if (ioFile.exists()) {
- return new FileInputStream(ioFile);
- }
- } catch (IOException e) {
- // Try to purge the cache and continue
- purgeCacheFile(id);
- throw e;
- }
- } catch (IOException e) {
- // We will end up here if we couldn't read or delete the cache file
- throw new TeamException(Policy.bind("RemoteContentsCache.fileError", ioFile.getAbsolutePath()), e); //$NON-NLS-1$
- }
- // This can occur when there is no remote contents
- return new ByteArrayInputStream(new byte[0]);
- }
-
- /**
- * Set the contents for the cache entry at the given id to the contents provided in the given input stream. Upon
- * completion of this method the input stream will be closed even if an error occurred. If an error did occur, the
- * cache entry for the given id will be cleared.
- *
- * @param id
- * @param stream
- * @param monitor
- * @throws TeamException if an error occured opening or writing to the cache file
- */
- public synchronized void setContents(String id, InputStream stream, IProgressMonitor monitor) throws TeamException {
- File ioFile = getFile(id);
- try {
-
- // Open the cache file for writing
- OutputStream out;
- try {
- out = new BufferedOutputStream(new FileOutputStream(ioFile));
- } catch (FileNotFoundException e) {
- throw new TeamException(Policy.bind("RemoteContentsCache.fileError", ioFile.getAbsolutePath()), e); //$NON-NLS-1$
- }
-
- // Transfer the contents
- try {
- try {
- byte[] buffer = new byte[1024];
- int read;
- while ((read = stream.read(buffer)) >= 0) {
- Policy.checkCanceled(monitor);
- out.write(buffer, 0, read);
- }
- } finally {
- out.close();
- }
- } catch (IOException e) {
- // Make sure we don't leave the cache file around as it may not have the right contents
- purgeCacheFile(id);
- throw e;
- }
- } catch (IOException e) {
- throw new TeamException(Policy.bind("RemoteContentsCache.fileError", ioFile.getAbsolutePath()), e); //$NON-NLS-1$
- } finally {
- try {
- stream.close();
- } catch (IOException e1) {
- // Ignore close errors
- }
- }
- }
-
- /**
* Return whether the cache contains an entry for the given id. Register a hit if it does.
* @param id the id of the cache entry
* @return true if there are contents cached for the id
*/
- public boolean hasContents(String id) {
- boolean contains = cacheFileNames.containsKey(id);
- if (contains) {
- registerHit(id);
- }
- return contains;
- }
-
- /**
- * Purge the cache entry for the given id.
- * @param id
- */
- public synchronized void purge(String id) {
- purgeCacheFile(id);
- }
-
- private File getCacheFileForPhysicalPath(String physicalPath) {
- return new File(getCachePath().toFile(), physicalPath);
+ public boolean hasEntry(String id) {
+ return internalGetCacheEntry(id) != null;
}
-
- private IPath getCachePath() {
+
+ protected IPath getCachePath() {
return getStateLocation().append(CACHE_DIRECTORY).append(name);
}
private IPath getStateLocation() {
return TeamPlugin.getPlugin().getStateLocation();
}
-
- private void registerHit(String path) {
- cacheFileTimes.put(path, Long.toString(new Date().getTime()));
- }
private void clearOldCacheEntries() {
long current = new Date().getTime();
if ((lastCacheCleanup!=-1) && (current - lastCacheCleanup < CACHE_FILE_LIFESPAN)) return;
List stale = new ArrayList();
- for (Iterator iter = cacheFileTimes.keySet().iterator(); iter.hasNext();) {
- String f = (String) iter.next();
- long lastHit = Long.valueOf((String)cacheFileTimes.get(f)).longValue();
+ for (Iterator iter = cacheEntries.values().iterator(); iter.hasNext();) {
+ RemoteContentsCacheEntry entry = (RemoteContentsCacheEntry) iter.next();
+ long lastHit = entry.getLastAccessTimeStamp();
if ((current - lastHit) > CACHE_FILE_LIFESPAN){
- stale.add(f);
+ stale.add(entry);
}
}
for (Iterator iter = stale.iterator(); iter.hasNext();) {
- String f = (String) iter.next();
- purgeCacheFile(f);
+ RemoteContentsCacheEntry entry = (RemoteContentsCacheEntry) iter.next();
+ entry.dispose();
}
}
- private void purgeCacheFile(String path) {
- File f = getCacheFileForPhysicalPath((String)cacheFileNames.get(path));
+ private void purgeFromCache(String id) {
+ RemoteContentsCacheEntry entry = (RemoteContentsCacheEntry)cacheEntries.get(id);
+ File f = entry.getFile();
try {
deleteFile(f);
} catch (TeamException e) {
- // log the falied delete and continue
- TeamPlugin.log(e);
+ // Ignore the deletion failure.
+ // A failure only really matters when purging the directory on startup
}
- cacheFileTimes.remove(path);
- cacheFileNames.remove(path);
+ cacheEntries.remove(id);
}
private void createCacheDirectory() throws TeamException {
@@ -314,21 +166,25 @@ public class RemoteContentsCache {
if (! file.mkdirs()) {
throw new TeamException(Policy.bind("RemoteContentsCache.fileError", file.getAbsolutePath())); //$NON-NLS-1$
}
- cacheFileNames = new HashMap();
- cacheFileTimes = new HashMap();
+ cacheEntries = new HashMap();
lastCacheCleanup = -1;
cacheDirSize = 0;
}
private void deleteCacheDirectory() throws TeamException {
+ cacheEntries = null;
+ lastCacheCleanup = -1;
+ cacheDirSize = 0;
IPath cacheLocation = getCachePath();
File file = cacheLocation.toFile();
if (file.exists()) {
- deleteFile(file);
+ try {
+ deleteFile(file);
+ } catch (TeamException e) {
+ // Don't worry about problems deleting.
+ // The only case that matters is when the cache directory is created
+ }
}
- cacheFileNames = cacheFileTimes = null;
- lastCacheCleanup = -1;
- cacheDirSize = 0;
}
private void deleteFile(File file) throws TeamException {
@@ -342,4 +198,59 @@ public class RemoteContentsCache {
throw new TeamException(Policy.bind("RemoteContentsCache.fileError", file.getAbsolutePath())); //$NON-NLS-1$
}
}
+
+ /**
+ * Purge the given cache entry from the cache. This method should only be invoked from
+ * an instance of RemoteContentsCacheEntry after it has set it's state to DISPOSED.
+ * @param entry
+ */
+ protected void purgeFromCache(RemoteContentsCacheEntry entry) {
+ purgeFromCache(entry.getId());
+ }
+
+ private RemoteContentsCacheEntry internalGetCacheEntry(String id) {
+ RemoteContentsCacheEntry entry = (RemoteContentsCacheEntry)cacheEntries.get(id);
+ if (entry != null) {
+ entry.registerHit();
+ }
+ return entry;
+ }
+
+ /**
+ * @param id the id that uniquely identifes the remote resource that is cached.
+ * @return
+ */
+ public synchronized RemoteContentsCacheEntry getCacheEntry(String id) {
+ if (cacheEntries == null) {
+ // This probably means that the cache has been disposed
+ throw new IllegalStateException(Policy.bind("RemoteContentsCache.cacheDisposed", name)); //$NON-NLS-1$
+ }
+ RemoteContentsCacheEntry entry = internalGetCacheEntry(id);
+ if (entry == null) {
+ // cache miss
+ entry = createCacheEntry(id);
+ }
+ return entry;
+ }
+
+ private RemoteContentsCacheEntry createCacheEntry(String id) {
+ clearOldCacheEntries();
+ String filePath = String.valueOf(cacheDirSize++);
+ RemoteContentsCacheEntry entry = new RemoteContentsCacheEntry(this, id, filePath);
+ cacheEntries.put(id, entry);
+ return entry;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Provide access to the lock for the cache. This method should only be used by a cache entry.
+ * @return Returns the lock.
+ */
+ protected ILock getLock() {
+ return lock;
+ }
+
}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCacheEntry.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCacheEntry.java
new file mode 100644
index 000000000..c54cca9c7
--- /dev/null
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/sync/RemoteContentsCacheEntry.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.core.sync;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.core.Policy;
+
+/**
+ * This class provides the implementation for the ICacheEntry
+ */
+public class RemoteContentsCacheEntry {
+
+ public static final int UNINITIALIZED = 0;
+ public static final int READY = 1;
+ public static final int DISPOSED = 2;
+
+ private String id;
+ private String filePath;
+ private RemoteContentsCache cache;
+ private byte[] syncBytes;
+ private int state = UNINITIALIZED;
+ private long lastAccess;
+
+ public RemoteContentsCacheEntry(RemoteContentsCache cache, String id, String filePath) {
+ state = UNINITIALIZED;
+ this.cache = cache;
+ this.id = id;
+ this.filePath = filePath;
+ registerHit();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.sync.ICacheEntry#getContents()
+ */
+ public InputStream getContents() throws TeamException {
+ if (state != READY) return null;
+ registerHit();
+ File ioFile = getFile();
+ try {
+ try {
+ if (ioFile.exists()) {
+ return new FileInputStream(ioFile);
+ }
+ } catch (IOException e) {
+ // Try to purge the cache and continue
+ cache.purgeFromCache(this);
+ throw e;
+ }
+ } catch (IOException e) {
+ // We will end up here if we couldn't read or delete the cache file
+ throw new TeamException(Policy.bind("RemoteContentsCache.fileError", ioFile.getAbsolutePath()), e); //$NON-NLS-1$
+ }
+ // This can occur when there is no remote contents
+ return new ByteArrayInputStream(new byte[0]);
+ }
+
+ protected File getFile() {
+ return new File(cache.getCachePath().toFile(), filePath);
+ }
+
+ /**
+ * Set the contents of for this cache entry. This method supports concurrency by only allowing
+ * one cache entry to be written at a time. In the case of two concurrent writes to the same cache entry,
+ * the contents from the first write is used and the content from subsequent writes is ignored.
+ * @param stream an InputStream that provides the contents to be cached
+ * @param monitor a progress monitor
+ * @throws TeamException if the entry is DISPOSED or an I/O error occurres
+ */
+ public void setContents(InputStream stream, IProgressMonitor monitor) throws TeamException {
+ // Use a lock to only allow one write at a time
+ try {
+ beginOperation();
+ internalSetContents(stream, monitor);
+ } finally {
+ endOperation();
+ }
+ }
+
+ private synchronized void endOperation() {
+ cache.getLock().release();
+ }
+
+ private synchronized void beginOperation() {
+ cache.getLock().acquire();
+ }
+
+ private void internalSetContents(InputStream stream, IProgressMonitor monitor) throws TeamException {
+ // if the state is DISPOSED then there is a problem
+ if (state == DISPOSED) {
+ throw new TeamException("Cache entry in {0} for {1} has been disposed" + cache.getName() + id);
+ }
+ // Otherwise, the state is UNINITIALIZED or READY so we can proceed
+ registerHit();
+ File ioFile = getFile();
+ try {
+
+ // Open the cache file for writing
+ OutputStream out;
+ try {
+ if (state == UNINITIALIZED) {
+ out = new BufferedOutputStream(new FileOutputStream(ioFile));
+ } else {
+ // If the entry is READY, the contents must have been read in another thread.
+ // We still need to red the contents but they can be ignored since presumably they are the same
+ out = new ByteArrayOutputStream();
+ }
+ } catch (FileNotFoundException e) {
+ throw new TeamException(Policy.bind("RemoteContentsCache.fileError", ioFile.getAbsolutePath()), e); //$NON-NLS-1$
+ }
+
+ // Transfer the contents
+ try {
+ try {
+ byte[] buffer = new byte[1024];
+ int read;
+ while ((read = stream.read(buffer)) >= 0) {
+ Policy.checkCanceled(monitor);
+ out.write(buffer, 0, read);
+ }
+ } finally {
+ out.close();
+ }
+ } catch (IOException e) {
+ // Make sure we don't leave the cache file around as it may not have the right contents
+ cache.purgeFromCache(this);
+ throw e;
+ }
+
+ // Mark the cache entry as ready
+ state = READY;
+ } catch (IOException e) {
+ throw new TeamException(Policy.bind("RemoteContentsCache.fileError", ioFile.getAbsolutePath()), e); //$NON-NLS-1$
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e1) {
+ // Ignore close errors
+ }
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.sync.ICacheEntry#getSyncBytes(byte[])
+ */
+ public byte[] getSyncBytes() {
+ return syncBytes;
+ }
+
+ /**
+ * Set the sync bytes associated with the cached remote contents.
+ * This method is sychronized to ensure atomic setting of the bytes.
+ * @param bytes
+ */
+ public synchronized void setSyncBytes(byte[] bytes) {
+ syncBytes = bytes;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.sync.ICacheEntry#getState()
+ */
+ public int getState() {
+ return state;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.sync.ICacheEntry#getSize()
+ */
+ public long getSize() {
+ if (state != READY) return 0;
+ File ioFile = getFile();
+ if (ioFile.exists()) {
+ return ioFile.length();
+ }
+ return 0;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.sync.ICacheEntry#getLastAccessTimeStamp()
+ */
+ public long getLastAccessTimeStamp() {
+ return lastAccess;
+ }
+
+ /**
+ * Registers a hit on this cache entry. This updates the last access timestamp.
+ * Thsi method is intended to only be invokded from inside this class or the cahce itself.
+ * Other clients should not use it.
+ */
+ protected void registerHit() {
+ lastAccess = new Date().getTime();
+ }
+
+ public void dispose() {
+ // Use a lock to avoid changing state while another thread may be writting
+ try {
+ beginOperation();
+ state = DISPOSED;
+ cache.purgeFromCache(this);
+ } finally {
+ endOperation();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.sync.ICacheEntry#getId()
+ */
+ public String getId() {
+ return id;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java
index d907a414b..6a0ac543a 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSSyncInfo.java
@@ -185,7 +185,7 @@ public class CVSSyncInfo extends SyncInfo {
} else {
// We have conflictin additions.
// We need to fetch the contents of the remote to get all the relevant information (timestamp, permissions)
- // TODO: Do we really need to fetch the contents here?
+ // The most important thing we get is the keyword substitution mode which must be right to perform the commit
remote.getContents(Policy.monitorFor(monitor));
info = remote.getSyncInfo().cloneMutable();
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
index a7bff502e..9d661083f 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
@@ -11,7 +11,6 @@
package org.eclipse.team.internal.ccvs.core.resources;
import java.io.ByteArrayInputStream;
-import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
@@ -29,6 +28,7 @@ import org.eclipse.core.runtime.Path;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.sync.IRemoteResource;
import org.eclipse.team.core.sync.RemoteContentsCache;
+import org.eclipse.team.core.sync.RemoteContentsCacheEntry;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
@@ -67,6 +67,8 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
private byte[] syncBytes;
// cache the log entry for the remote file
private ILogEntry entry;
+ // state that indicates that the handle is actively fetching content
+ private boolean fetching = false;
/**
* Static method which creates a file as a single child of its parent.
@@ -179,6 +181,14 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
}
/* package*/ void fetchContents(IProgressMonitor monitor) throws CVSException {
+ try {
+ fetching = true;
+ internalFetchContents(monitor);
+ } finally {
+ fetching = false;
+ }
+ }
+ /* package*/ void internalFetchContents(IProgressMonitor monitor) throws CVSException {
monitor.beginTask(Policy.bind("RemoteFile.getContents"), 100);//$NON-NLS-1$
if (getRevision().equals(ResourceSyncInfo.ADDED_REVISION)) {
// The revision of the remote file is not known so we need to use the tag to get the status of the file
@@ -313,11 +323,12 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
* @see ICVSFile#getSize()
*/
public long getSize() {
- File ioFile = getRemoteContentsCache().getFile(getCacheRelativePath());
- if (ioFile.exists()) {
- return ioFile.length();
+ if (fetching) return 0;
+ RemoteContentsCacheEntry entry = getCacheEntry();
+ if (entry == null) {
+ return 0;
}
- return 0;
+ return entry.getSize();
}
/**
@@ -358,7 +369,7 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
* @see IManagedFile#setFileInfo(FileProperties)
*/
public void setSyncInfo(ResourceSyncInfo fileInfo, int modificationState) {
- syncBytes = fileInfo.getBytes();
+ setSyncBytes(fileInfo.getBytes(),modificationState);
}
/**
@@ -371,10 +382,14 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
}
public InputStream getContents() throws CVSException {
- // Return the cached contents
- InputStream cached = getCachedContents();
- if (cached != null) {
- return cached;
+ if (!fetching) {
+ // Return the cached contents
+ if (isContentsCached()) {
+ InputStream cached = getCachedContents();
+ if (cached != null) {
+ return cached;
+ }
+ }
}
// There was nothing cached so return an empty stream.
// This is done to allow the contents to be fetched
@@ -384,7 +399,13 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
private InputStream getCachedContents() throws CVSException {
try {
- return getRemoteContentsCache().getContents(getCacheRelativePath());
+ RemoteContentsCacheEntry entry = getCacheEntry();
+ byte[] newSyncBytes = entry.getSyncBytes();
+ if (newSyncBytes != null) {
+ // Make sure the sync bytes match the content that is being accessed
+ syncBytes = newSyncBytes;
+ }
+ return entry.getContents();
} catch (TeamException e) {
throw CVSException.wrapException(e);
}
@@ -392,7 +413,7 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
public void setContents(InputStream stream, int responseType, boolean keepLocalHistory, IProgressMonitor monitor) throws CVSException {
try {
- getRemoteContentsCache().setContents(getCacheRelativePath(), stream, monitor);
+ getCacheEntry().setContents(stream, monitor);
} catch (TeamException e) {
throw CVSException.wrapException(e);
}
@@ -420,7 +441,12 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
*/
public boolean isContentsCached() {
if (getRevision().equals(ResourceSyncInfo.ADDED_REVISION)) return false;
- return getRemoteContentsCache().hasContents(getCacheRelativePath());
+ String cacheRelativePath = getCacheRelativePath();
+ if (!getRemoteContentsCache().hasEntry(cacheRelativePath)) {
+ return false;
+ }
+ RemoteContentsCacheEntry entry = getRemoteContentsCache().getCacheEntry(cacheRelativePath);
+ return entry.getState() == RemoteContentsCacheEntry.READY;
}
/*
@@ -579,8 +605,16 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
/**
* @see org.eclipse.team.internal.ccvs.core.ICVSFile#setSyncBytes(byte[])
*/
- public void setSyncBytes(byte[] syncBytes, int modificationState) throws CVSException {
- setSyncInfo(new ResourceSyncInfo(syncBytes), ICVSFile.UNKNOWN);
+ public void setSyncBytes(byte[] syncBytes, int modificationState) {
+ if (fetching) {
+ RemoteContentsCacheEntry entry = getCacheEntry();
+ entry.setSyncBytes(syncBytes);
+ }
+ this.syncBytes = syncBytes;
+ }
+
+ private RemoteContentsCacheEntry getCacheEntry() {
+ return getRemoteContentsCache().getCacheEntry(getCacheRelativePath());
}
public String toString() {
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSWorkspaceSubscriberTest.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSWorkspaceSubscriberTest.java
index 72985d296..ced37be9a 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSWorkspaceSubscriberTest.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSWorkspaceSubscriberTest.java
@@ -1270,9 +1270,46 @@ public class CVSWorkspaceSubscriberTest extends CVSSyncSubscriberTest {
assertSyncEquals("testNestedMarkAsMerged sync check", project,
new String[] { "folder2/", "folder2/file.txt", "folder2/file2.txt"},
true, new int[] {
- SyncInfo.IN_SYNC,
- SyncInfo.OUTGOING | SyncInfo.CHANGE,
+ SyncInfo.IN_SYNC,
+ SyncInfo.OUTGOING | SyncInfo.CHANGE,
+ SyncInfo.CONFLICTING | SyncInfo.ADDITION
+ });
+ }
+
+ public void testMarkAsMergedOnBinaryFile() throws TeamException, CoreException, InvocationTargetException, InterruptedException {
+ // Create a project and checkout a copy
+ IProject project = createProject(new String[] { "file1.txt"});
+ IProject copy = checkoutCopy(project, "-copy");
+ // Add the same binary file to both projects to create a conflicting addition
+ buildResources(project, new String[] {"binary.gif"}, false);
+ addResources(copy, new String[] {"binary.gif"}, true);
+ assertIsBinary(copy.getFile("binary.gif"));
+ assertSyncEquals("testMarkAsMergedOnBinaryFile sync check", project,
+ new String[] {"binary.gif"},
+ true, new int[] {
+ SyncInfo.CONFLICTING | SyncInfo.ADDITION
+ });
+ markAsMerged(getSubscriber(), project, new String[] {"binary.gif"});
+ assertSyncEquals("testMarkAsMergedOnBinaryFile sync check", project,
+ new String[] {"binary.gif"},
+ true, new int[] {
+ SyncInfo.OUTGOING | SyncInfo.CHANGE
+ });
+ assertIsBinary(project.getFile("binary.gif"));
+ // Unmanage the file and do it again
+ // This tests the case were the contents are already cached locally
+ CVSWorkspaceRoot.getCVSFileFor(project.getFile("binary.gif")).unmanage(DEFAULT_MONITOR);
+ assertSyncEquals("testMarkAsMergedOnBinaryFile sync check", project,
+ new String[] {"binary.gif"},
+ true, new int[] {
SyncInfo.CONFLICTING | SyncInfo.ADDITION
});
+ markAsMerged(getSubscriber(), project, new String[] {"binary.gif"});
+ assertSyncEquals("testMarkAsMergedOnBinaryFile sync check", project,
+ new String[] {"binary.gif"},
+ true, new int[] {
+ SyncInfo.OUTGOING | SyncInfo.CHANGE
+ });
+ assertIsBinary(project.getFile("binary.gif"));
}
}

Back to the top