Bug 303833 - [sfs] inconsistent last modified handling
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CacheService.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CacheService.java index dc74b3d..63ff364 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CacheService.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CacheService.java
@@ -14,9 +14,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -39,7 +36,6 @@ private final Lock readLock = this.rwl.readLock(); private final Lock writeLock = this.rwl.writeLock(); private IContentHandleFactory handleFactory; - private static final DateFormat DFFORTRACE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss::SSS"); //$NON-NLS-1$ /** * @param handleFactory @@ -82,43 +78,11 @@ tempHandle.setContents(input, monitor); - // os = tempHandle.openOutputStream(append, monitor); } finally { unlockForWrite(); } - // Util.transferStreams(input, os, monitor); - - this.addFromTempHandle(tempHandle, System.currentTimeMillis()); - } - - public void addContentWithTimestamp(IPath path, InputStream input, long timestamp, int options, IProgressMonitor monitor) - throws CoreException { - - boolean append = (options & ISemanticFileSystem.CONTENT_APPEND) > 0; - - if (SfsSpiTraceLocation.CACHESERVICE.isActive()) { - SfsSpiTraceLocation.getTrace().traceEntry(SfsSpiTraceLocation.CACHESERVICE.getLocation(), - new Object[] {path.toString(), DFFORTRACE.format(new Date(timestamp)), new Boolean(append)}); - } - - ITemporaryContentHandle tempHandle; - - try { - lockForWrite(); - - tempHandle = this.handleFactory.createTemporaryHandle(this, path, append); - - tempHandle.setContents(input, monitor); - - // os = tempHandle.openOutputStream(append, monitor); - } finally { - unlockForWrite(); - } - - // Util.transferStreams(input, os, monitor); - - this.addFromTempHandle(tempHandle, timestamp); + this.addFromTempHandle(tempHandle); } public InputStream getContent(IPath path) throws CoreException { @@ -181,60 +145,6 @@ } } - public long getContentTimestamp(IPath path) throws CoreException { - - if (SfsSpiTraceLocation.CACHESERVICE.isActive()) { - SfsSpiTraceLocation.getTrace().traceEntry(SfsSpiTraceLocation.CACHESERVICE.getLocation(), path.toString()); - } - - try { - lockForRead(); - - ICachedContentHandle cacheFile = createCacheContentHandle(path); - - long result; - if (cacheFile.exists()) { - result = cacheFile.lastModified(); - } else { - result = -1; - } - - if (SfsSpiTraceLocation.CACHESERVICE.isActive()) { - if (result >= 0) { - SfsSpiTraceLocation.getTrace().traceExit(SfsSpiTraceLocation.CACHESERVICE.getLocation(), - DFFORTRACE.format(new Date(result))); - } else { - SfsSpiTraceLocation.getTrace().traceExit(SfsSpiTraceLocation.CACHESERVICE.getLocation(), new Long(result)); - } - } - - return result; - } finally { - unlockForRead(); - } - } - - public void setContentTimestamp(IPath path, long timestamp) throws CoreException { - - if (SfsSpiTraceLocation.CACHESERVICE.isActive()) { - SfsSpiTraceLocation.getTrace().traceEntry(SfsSpiTraceLocation.CACHESERVICE.getLocation(), - new Object[] {path.toString(), DFFORTRACE.format(new Date(timestamp))}); - } - - try { - lockForWrite(); - - ICachedContentHandle cacheFile = createCacheContentHandle(path); - - if (cacheFile.exists()) { - cacheFile.setLastModified(timestamp); - } - // TODO 0.1: if the entry doesn't exist - } finally { - unlockForWrite(); - } - } - public void removeContent(IPath path, IProgressMonitor monitor) throws CoreException { if (SfsSpiTraceLocation.CACHESERVICE.isActive()) { @@ -276,15 +186,14 @@ * used from {@link CachingOutputStream#close()} * * @param tempHandle - * @param timestamp * @throws CoreException */ - void addFromTempHandle(ITemporaryContentHandle tempHandle, long timestamp) throws CoreException { + void addFromTempHandle(ITemporaryContentHandle tempHandle) throws CoreException { try { lockForWrite(); - tempHandle.commit(timestamp); + tempHandle.commit(); } finally { unlockForWrite();
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedFileHandle.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedFileHandle.java index 8eb31aa..1851bf9 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedFileHandle.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedFileHandle.java
@@ -45,14 +45,6 @@ return this.factory.checkFileExists(this.cacheFile); } - public long lastModified() { - return this.factory.getLastModified(this.cacheFile); - } - - public void setLastModified(long timestamp) throws CoreException { - this.factory.setLastModified(this.cacheFile, timestamp); - } - public void delete() { this.factory.tryDelete(this.cacheFile); }
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedMemoryHandle.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedMemoryHandle.java index e8a936d..90e0f00 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedMemoryHandle.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachedMemoryHandle.java
@@ -28,7 +28,6 @@ private byte[] myContent = new byte[0]; boolean exists = false; - long time = 0l; /** * @param path @@ -45,14 +44,6 @@ return this.exists; } - public long lastModified() { - return this.time; - } - - public void setLastModified(long timestamp) { - this.time = timestamp; - } - public void delete() { this.exists = false; this.myCache.removeStore(this.myPath);
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachingOutputStream.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachingOutputStream.java index 7492faf..80e0039 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachingOutputStream.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/CachingOutputStream.java
@@ -42,13 +42,13 @@ public void close() throws IOException { InputStream stream = null; try { + long timestamp = System.currentTimeMillis(); - this.cacheService.addFromTempHandle(this.fileHandle, System.currentTimeMillis()); + this.cacheService.addFromTempHandle(this.fileHandle); IPath path = this.fileHandle.getKey(); stream = this.cacheService.getContent(path); - long cacheTimestamp = this.cacheService.getContentTimestamp(path); long appendPosition = this.fileHandle.getAppendPosition(); long skipped = 0l; @@ -60,7 +60,7 @@ throw new IOException(MessageFormat.format(Messages.CachingOutputStream_CouldNotSkip_XMSG, appendPosition, skipped)); } - this.callback.cacheUpdated(stream, cacheTimestamp, this.appendMode); + this.callback.cacheUpdated(stream, timestamp, this.appendMode); } catch (CoreException e) { // $JL-EXC$ ignore
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/FileHandleFactory.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/FileHandleFactory.java index 4c499f0..7f29fc3 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/FileHandleFactory.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/FileHandleFactory.java
@@ -260,39 +260,6 @@ /** * * @param cacheFile - * @return last modified - */ - public long getLastModified(File cacheFile) { - try { - lockForWrite(); - return getActiveFileHandle(cacheFile).lastModified(); - } finally { - unlockForWrite(); - } - } - - /** - * - * @param cacheFile - * @param timestamp - * @throws CoreException - */ - public void setLastModified(File cacheFile, long timestamp) throws CoreException { - try { - lockForWrite(); - if (!getActiveFileHandle(cacheFile).setLastModified(timestamp)) { - throw new SemanticResourceException(SemanticResourceStatusCode.FILECACHE_ERROR_SETTING_TIMESTAMP, new Path(cacheFile - .getAbsolutePath()), MessageFormat.format(Messages.TemporaryFileHandle_TimstampSetOnCommit_XMSG, cacheFile - .getAbsolutePath())); - } - } finally { - unlockForWrite(); - } - } - - /** - * - * @param cacheFile * @return input stream * @throws FileNotFoundException */
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ICachedContentHandle.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ICachedContentHandle.java index e8eddb4..ba6f4db 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ICachedContentHandle.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ICachedContentHandle.java
@@ -29,21 +29,6 @@ public boolean exists(); /** - * - * @return the content timestamp - */ - public long lastModified(); - - /** - * - * @param timestamp - * the timestamp - * @throws CoreException - * upon failure - */ - public void setLastModified(long timestamp) throws CoreException; - - /** */ public void delete();
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ITemporaryContentHandle.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ITemporaryContentHandle.java index d19dc84..7731fa9 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ITemporaryContentHandle.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/ITemporaryContentHandle.java
@@ -24,8 +24,8 @@ * <p> * This is instantiated when content is to be written to the cache. The * {@link OutputStream}-like write methods below are used to store the content - * temporarily. Eventually, the cache service will call the - * {@link #commit(long)} method which must (logically) copy the content into a + * temporarily. Eventually, the cache service will call the {@link #commit()} + * method which must (logically) copy the content into a * {@link ICachedContentHandle}. * */ @@ -38,14 +38,12 @@ public IPath getKey(); /** - * Commits temporary content into cache and sets the timestamp + * Commits temporary content into cache * - * @param timestamp - * the timestamp to set * @throws CoreException * */ - public void commit(long timestamp) throws CoreException; + public void commit() throws CoreException; /** *
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryFileHandle.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryFileHandle.java index 6f10d7d..5f020f1 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryFileHandle.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryFileHandle.java
@@ -76,7 +76,7 @@ return this.file; } - public void commit(long timestamp) throws CoreException { + public void commit() throws CoreException { try { this.outputStream.flush(); @@ -99,8 +99,6 @@ if (!this.appendMode) { this.factory.doRename(this.getFile(), this.cacheFile); } - - this.factory.setLastModified(cacheFile, timestamp); } public IPath getKey() {
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryMemoryHandle.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryMemoryHandle.java index 6279cca..072e061 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryMemoryHandle.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/internal/resources/semantic/cacheservice/TemporaryMemoryHandle.java
@@ -35,15 +35,13 @@ /** * @throws CoreException */ - public void commit(long timestamp) throws CoreException { + public void commit() throws CoreException { CachedMemoryHandle handle = MemoryCache.getInstance().getOrCreateMemoryStore(this.path.toString()); byte[] content; Util.safeClose(this.bos); content = this.bos.toByteArray(); handle.setContents(content, this.appendMode); - - handle.setLastModified(timestamp); } public IPath getKey() {
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/CachingContentProvider.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/CachingContentProvider.java index 463e9ac..26ec78a 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/CachingContentProvider.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/CachingContentProvider.java
@@ -30,8 +30,6 @@ import org.eclipse.core.runtime.QualifiedName; import org.eclipse.osgi.util.NLS; - - /** * This will delegate calls to openInputStream and openOutputStream to local * copies of the semantic files. @@ -61,7 +59,7 @@ * */ public abstract class CachingContentProvider extends ContentProvider { - + private static final QualifiedName RESOURCE_TIMESTAMP = new QualifiedName(SemanticResourcesPlugin.PLUGIN_ID, "ResourceTimestamp"); //$NON-NLS-1$ // TODO 0.1: add convenience/helper methods to cleanup cache along with // file/folder removal @@ -151,7 +149,9 @@ try { is = openInputStreamInternal(childStore, monitor, setter); - cacheService.addContentWithTimestamp(path, is, setter.getTimestamp(), EFS.NONE, monitor); + cacheService.addContent(path, is, EFS.NONE, monitor); + + setResourceTimestamp(childStore, setter.getTimestamp(), monitor); } finally { Util.safeClose(is); } @@ -169,9 +169,10 @@ ICacheUpdateCallback callback = new ICacheUpdateCallback() { - public void cacheUpdated(InputStream newContent, long cacheTimestamp, boolean append) { - onCacheUpdate(childStore, newContent, cacheTimestamp, append, monitor); + public void cacheUpdated(InputStream newContent, long timestamp, boolean append) throws CoreException { + setResourceTimestamp(childStore, timestamp, monitor); + onCacheUpdate(childStore, newContent, timestamp, append, monitor); } }; return cacheService.wrapOutputStream(path, appendMode, callback, monitor); @@ -343,8 +344,8 @@ * To be implemented by concrete subclasses. * <p> * This method is called both from - * {@link #openInputStream(ISemanticFileStore, IProgressMonitor)} and from - * the default implementation of + * {@link #openInputStream(ISemanticFileStore, IProgressMonitor)} and + * (indirectly) from the default implementation of * {@link #getResourceTimestamp(ISemanticFileStore, IProgressMonitor)}. This * method allows retrieval of content and content timestamp with one * roundtrip (e.g. via plain HTTP GET). But this approach has a drawback @@ -352,14 +353,9 @@ * update of the resource tree. * <p> * Important: The timestamp that is obtained via {@code timeStampSetter} - * parameter is only used to set timestamp on cache service entry. + * parameter is then passed to * {@link #setResourceTimestamp(ISemanticFileStore, long, IProgressMonitor)} - * method is not called by - * {@link #openInputStream(ISemanticFileStore, IProgressMonitor)}. This is - * done because the standard implementation of - * {@link #getResourceTimestamp(ISemanticFileStore, IProgressMonitor)} uses - * cache service to consistently store both content and timestamps in one - * place. + * method. * <p> * Content providers that are able to retrieve the resource timestamp * independently from content, should override the methods @@ -367,9 +363,7 @@ * {@link #setResourceTimestamp(ISemanticFileStore, long, IProgressMonitor)} * and * {@link #onCacheUpdate(ISemanticFileStore, InputStream, long, boolean, IProgressMonitor)} - * and provide an own timestamp handling that is independent from the cache - * service and allows lazy content retrieval. In this case, the value - * returned via timeStampSetter parameter is irrelevant and may be 0. + * and provide an own timestamp handling that allows lazy content retrieval. * <p> * When implementing own timestamp handling, it is important to take into * account that the method @@ -415,8 +409,8 @@ * the semantic file store * @param newContent * the new cache content - * @param cacheTimestamp - * the timestamp as read from the cache + * @param timestamp + * the timestamp of the change * @param append * <code>true</code> to indicate that the cache was updated in * append mode; in this case, only the appended data will be @@ -424,30 +418,25 @@ * @param monitor * may be null */ - public void onCacheUpdate(ISemanticFileStore semanticFileStore, InputStream newContent, long cacheTimestamp, boolean append, + public void onCacheUpdate(ISemanticFileStore semanticFileStore, InputStream newContent, long timestamp, boolean append, IProgressMonitor monitor) { // by default, we do nothing } public long getResourceTimestamp(ISemanticFileStore semanticFileStore, IProgressMonitor monitor) throws CoreException { - + + if (!semanticFileStore.isExists()) { + return EFS.NONE; + } String stampString = semanticFileStore.getPersistentProperty(RESOURCE_TIMESTAMP); - if (stampString != null){ + if (stampString != null) { return Long.parseLong(stampString); } - // the cache service can also give some information, but depending on the - // underlying file system, precision may be seconds, not milliseconds - ICacheService cacheService = this.getCacheService(); - IPath path = semanticFileStore.getPath(); - long timestamp = cacheService.getContentTimestamp(path); - if (timestamp >= 0) { - return timestamp; - } + // no property set, fill cache that will retrieve the content that will + // set the timestamp - // timestamp == -1 means that the content is not in the cache, so we try - // to get it MultiStatus stat = new MultiStatus(SemanticResourcesPlugin.PLUGIN_ID, IStatus.OK, NLS.bind( Messages.CachingContentProvider_FillCache_XGRP, semanticFileStore.getPath().toString()), null); @@ -458,9 +447,10 @@ throw new CoreException(stat); } - timestamp = cacheService.getContentTimestamp(path); - if (timestamp >= 0) { - return timestamp; + // try to read the property again + stampString = semanticFileStore.getPersistentProperty(RESOURCE_TIMESTAMP); + if (stampString != null) { + return Long.parseLong(stampString); } throw new SemanticResourceException(SemanticResourceStatusCode.CACHED_CONTENT_NOT_FOUND, semanticFileStore.getPath(), @@ -468,24 +458,9 @@ } public void setResourceTimestamp(ISemanticFileStore semanticFileStore, long timestamp, IProgressMonitor monitor) throws CoreException { - - semanticFileStore.setPersistentProperty(RESOURCE_TIMESTAMP, Long.toString(timestamp)); - - // we also update the cache information, but this is additional information only - ICacheService cacheService = this.getCacheService(); - IPath path = semanticFileStore.getPath(); - if (cacheService.hasContent(path)) { - cacheService.setContentTimestamp(path, timestamp); - } - // TODO we probably don't want to get a cache update, do we? -// else { -// // ugly as it is, this will set the timestamp on the cache file -// // properly -// InputStream is = openInputStream(semanticFileStore, monitor); -// Util.safeClose(is); -// cacheService.setContentTimestamp(path, timestamp); -// } + semanticFileStore.setPersistentProperty(RESOURCE_TIMESTAMP, Long.toString(timestamp)); + } }
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheService.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheService.java index d5c4fce..45ad905 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheService.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheService.java
@@ -15,7 +15,6 @@ import java.io.OutputStream; import org.eclipse.core.filesystem.EFS; -import org.eclipse.core.resources.semantic.ISemanticFileSystem; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; @@ -48,25 +47,6 @@ public void addContent(IPath path, InputStream input, int options, IProgressMonitor monitor) throws CoreException; /** - * Adds the given content to the cache with timestamp - * - * @param path - * the path - * @param input - * the content - * @param timestamp - * the timestamp - * @param options - * only {@link ISemanticFileSystem#CONTENT_APPEND} is supported - * @param monitor - * may be null - * @throws CoreException - * upon failure - */ - public void addContentWithTimestamp(IPath path, InputStream input, long timestamp, int options, IProgressMonitor monitor) - throws CoreException; - - /** * @param path * the path * @return the content the content @@ -89,27 +69,6 @@ * * @param path * the path - * @return timestamp or -1 if content doesn't exist - * @throws CoreException - * upon failure - */ - public long getContentTimestamp(IPath path) throws CoreException; - - /** - * - * @param path - * the path - * @param timestamp - * the timestamp - * @throws CoreException - * upon failure - */ - public void setContentTimestamp(IPath path, long timestamp) throws CoreException; - - /** - * - * @param path - * the path * @param monitor * may be null * @throws CoreException
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheUpdateCallback.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheUpdateCallback.java index 0e08543..bb4061d 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheUpdateCallback.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ICacheUpdateCallback.java
@@ -13,6 +13,8 @@ import java.io.InputStream; +import org.eclipse.core.runtime.CoreException; + /** * Called after successful cache updates * @@ -28,6 +30,7 @@ * @param append * if <code>true</code>, the content only represents the appended * portion of the cache + * @throws CoreException */ - public void cacheUpdated(InputStream newContent, long cacheTimestamp, boolean append); + public void cacheUpdated(InputStream newContent, long cacheTimestamp, boolean append) throws CoreException; }
diff --git a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ISemanticContentProvider.java b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ISemanticContentProvider.java index ed16ab9..924c2ec 100644 --- a/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ISemanticContentProvider.java +++ b/bundles/org.eclipse.core.resources.semantic/src/org/eclipse/core/resources/semantic/spi/ISemanticContentProvider.java
@@ -16,6 +16,7 @@ import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.semantic.ISemanticFileSystem; +import org.eclipse.core.resources.semantic.ISemanticResource; import org.eclipse.core.resources.semantic.SyncDirection; import org.eclipse.core.resources.team.FileModificationValidator; import org.eclipse.core.runtime.CoreException; @@ -55,8 +56,7 @@ * <p> * If some data/state must be preserved between Eclipse restarts, * {@link ISemanticFileStore#setPersistentProperty(org.eclipse.core.runtime.QualifiedName, String)} - * should be used. - * </li> + * should be used.</li> * </ul> * The Semantic File System will initialize content providers via extension * point <code>org.eclipse.core.resources.semantic.spi.contentProvider</code> @@ -427,6 +427,7 @@ /** * This may be called by * {@link IFileStore#putInfo(org.eclipse.core.filesystem.IFileInfo, int, IProgressMonitor)} + * or other methods. * <p> * * @param semanticFileStore
diff --git a/examples/org.eclipse.core.resources.semantic.examples/src/org/eclipse/core/resources/semantic/examples/providers/RemoteStoreContentProvider.java b/examples/org.eclipse.core.resources.semantic.examples/src/org/eclipse/core/resources/semantic/examples/providers/RemoteStoreContentProvider.java index 6d67c03..6e05a18 100644 --- a/examples/org.eclipse.core.resources.semantic.examples/src/org/eclipse/core/resources/semantic/examples/providers/RemoteStoreContentProvider.java +++ b/examples/org.eclipse.core.resources.semantic.examples/src/org/eclipse/core/resources/semantic/examples/providers/RemoteStoreContentProvider.java
@@ -294,13 +294,13 @@ long localTime = -1l; ICacheService srv = getCacheService(); if (srv.hasContent(semanticFileStore.getPath())) { - localTime = srv.getContentTimestamp(semanticFileStore.getPath()); + localTime = getResourceTimestamp(semanticFileStore, monitor); } if (direction == SyncDirection.OUTGOING || localTime > remoteTime) { // outgoing OutputStream os = file.getOutputStream(false); Util.transferStreams(srv.getContent(semanticFileStore.getPath()), os, monitor); - file.setTimestamp(srv.getContentTimestamp(semanticFileStore.getPath())); + file.setTimestamp(localTime); store.serialize(monitor); } else if (direction == SyncDirection.INCOMING || remoteTime > localTime) { // incoming @@ -417,8 +417,9 @@ RemoteFile file = ((RemoteFile) item); - getCacheService().addContentWithTimestamp(semanticFileStore.getPath(), new ByteArrayInputStream(file.getContent()), - file.getTimestamp(), ISemanticFileSystem.NONE, monitor); + getCacheService().addContent(semanticFileStore.getPath(), new ByteArrayInputStream(file.getContent()), ISemanticFileSystem.NONE, + monitor); + setResourceTimestamp(semanticFileStore, file.getTimestamp(), monitor); setReadOnly(semanticFileStore, true, monitor); }
diff --git a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestsCachingProvider.java b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestsCachingProvider.java index 14c0cd1..1d12c53 100644 --- a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestsCachingProvider.java +++ b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/TestsCachingProvider.java
@@ -28,6 +28,7 @@ import org.eclipse.core.resources.semantic.ISemanticFile; import org.eclipse.core.resources.semantic.ISemanticFileSystem; import org.eclipse.core.resources.semantic.ISemanticFolder; +import org.eclipse.core.resources.semantic.SyncDirection; import org.eclipse.core.resources.semantic.spi.Util; import org.eclipse.core.resources.semantic.test.provider.CachingTestContentProvider; import org.eclipse.core.resources.semantic.test.provider.CachingTestContentProviderBase; @@ -301,4 +302,119 @@ Assert.assertEquals("File existence", false, file.exists()); } + + /** + * TODO move this to the base class to test all content provider after + * refactoring of tests wrt obtaining the remote file + * + * @throws Exception + */ + @Test + public void testParallelReadAndWrite() throws Exception { + + final IFolder root = this.testProject.getFolder("root"); + final IFolder parent = root.getFolder("Folder1"); + + final IFile file = parent.getFile("File1"); + Assert.assertFalse("File should not exist", file.exists()); + + IWorkspaceRunnable runnable = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) throws CoreException { + ISemanticFolder sfr = (ISemanticFolder) parent.getAdapter(ISemanticFolder.class); + sfr.addFile("File1", ISemanticFileSystem.NONE, monitor); + try { + Util.transferStreams(new ByteArrayInputStream("New Remote".getBytes("UTF-8")), file1.getOutputStream(false), monitor); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + }; + + ResourcesPlugin.getWorkspace().run(runnable, new NullProgressMonitor()); + + runnable = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) throws CoreException { + InputStream is = null; + try { + is = file.getContents(); + assertContentsEqual(file, "Hello"); + + ISemanticFile sFile = (ISemanticFile) file.getAdapter(ISemanticFile.class); + sFile.synchronizeContentWithRemote(SyncDirection.INCOMING, ISemanticFileSystem.NONE, monitor); + // we are still holding the file input stream, so we are + // seeing the out dated content + assertContentsEqual(file, "New Remote"); + // drop the input stream + Util.safeClose(is); + + // TODO now we need a refresh! + file.refreshLocal(IResource.DEPTH_INFINITE, monitor); + + assertContentsEqual(file, "New Remote"); + + } finally { + Util.safeClose(is); + } + + } + }; + + ResourcesPlugin.getWorkspace().run(runnable, new NullProgressMonitor()); + + } + + /** + * + * TODO move this to the base class to test all content provider after + * refactoring of tests wrt obtaining the remote file + * + * @throws Exception + */ + @Test + public void testAppendContentsToOpenFile() throws Exception { + + final IFolder root = this.testProject.getFolder("root"); + final IFolder parent = root.getFolder("Folder1"); + + final IFile file = parent.getFile("File1"); + + final ISemanticFile sfile = (ISemanticFile) file.getAdapter(ISemanticFile.class); + + IWorkspaceRunnable runnable = new IWorkspaceRunnable() { + + public void run(IProgressMonitor monitor) throws CoreException { + ISemanticFolder folder = (ISemanticFolder) file.getParent().getAdapter(ISemanticFolder.class); + folder.addFile(file.getName(), ISemanticFileSystem.NONE, monitor); + assertContentsEqual(file, "Hello"); + sfile.validateEdit(null); + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // $JL-EXC$ + } + file.appendContents(new ByteArrayInputStream(" appended".getBytes()), EFS.NONE, monitor); + // TODO does not work with plain provider; fix after integration + // of other tests + assertContentsEqual(file, "Hello appended"); + InputStream is = null; + try { + is = file.getContents(); + assertContentsEqual(file, "Hello appended"); + file.appendContents(new ByteArrayInputStream(" again".getBytes()), EFS.NONE, monitor); + assertContentsEqual(file, "Hello appended again"); + + // TODO this should also work with an opened file + sfile.synchronizeContentWithRemote(SyncDirection.OUTGOING, ISemanticFileSystem.NONE, monitor); + Assert.assertEquals("Wrong remote content", new String(file1.getContent()), "Hello appended again"); + } finally { + Util.safeClose(is); + } + } + }; + + ResourcesPlugin.getWorkspace().run(runnable, new NullProgressMonitor()); + + } }
diff --git a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/provider/CachingTestContentProviderBase.java b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/provider/CachingTestContentProviderBase.java index 2066dff..a811377 100644 --- a/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/provider/CachingTestContentProviderBase.java +++ b/tests/org.eclipse.core.resources.semantic.test/src/org/eclipse/core/resources/semantic/test/provider/CachingTestContentProviderBase.java
@@ -54,7 +54,7 @@ * */ public abstract class CachingTestContentProviderBase extends CachingContentProvider implements ISemanticContentProviderRemote, -ISemanticContentProviderLocking { + ISemanticContentProviderLocking { /** * If set to any value, this indicates that write-through should be * performed @@ -90,20 +90,20 @@ } public void addResource(ISemanticFileStore parentStore, String name, ResourceType resourceType, IProgressMonitor monitor) - throws CoreException { + throws CoreException { switch (resourceType) { - case FILE_TYPE: - this.addFileFromRemote(parentStore, name, monitor); - break; - case FOLDER_TYPE: - this.addFolderFromRemote(parentStore, name, monitor); - break; - case UNKNOWN_TYPE: - addResourceFromRemote(parentStore, name, monitor); - break; - default: - throw new CoreException(new Status(IStatus.ERROR, SemanticResourcesPlugin.PLUGIN_ID, "Can not create resource of type " - + resourceType.name())); + case FILE_TYPE : + this.addFileFromRemote(parentStore, name, monitor); + break; + case FOLDER_TYPE : + this.addFolderFromRemote(parentStore, name, monitor); + break; + case UNKNOWN_TYPE : + addResourceFromRemote(parentStore, name, monitor); + break; + default : + throw new CoreException(new Status(IStatus.ERROR, SemanticResourcesPlugin.PLUGIN_ID, "Can not create resource of type " + + resourceType.name())); } } @@ -115,7 +115,7 @@ RemoteItem item = ((RemoteFolder) parentItem).getChild(name); if (item == null) { throw new SemanticResourceException(SemanticResourceStatusCode.REMOTE_RESOURCE_NOT_FOUND, childStore.getPath(), - "No such resource"); + "No such resource"); } if (item.getType() != RemoteItem.Type.FILE) { throw new SemanticResourceException(SemanticResourceStatusCode.RESOURCE_WITH_OTHER_TYPE_EXISTS, item.getPath(), ""); @@ -133,7 +133,7 @@ RemoteItem item = ((RemoteFolder) parentItem).getChild(name); if (item == null) { throw new SemanticResourceException(SemanticResourceStatusCode.REMOTE_RESOURCE_NOT_FOUND, childStore.getPath(), - "No such resource"); + "No such resource"); } if (item.getType() != RemoteItem.Type.FOLDER) { throw new SemanticResourceException(SemanticResourceStatusCode.RESOURCE_WITH_OTHER_TYPE_EXISTS, item.getPath(), ""); @@ -152,7 +152,7 @@ RemoteItem item = ((RemoteFolder) parentItem).getChild(name); if (item == null) { throw new SemanticResourceException(SemanticResourceStatusCode.REMOTE_RESOURCE_NOT_FOUND, childStore.getPath(), - "No such resource"); + "No such resource"); } if (item.getType() == Type.FILE) { addFileFromRemote(childStore, name, monitor); @@ -165,7 +165,7 @@ } public void createFileRemotely(ISemanticFileStore childStore, String name, InputStream source, Object context, IProgressMonitor monitor) - throws CoreException { + throws CoreException { byte[] buffer; if (source != null) { @@ -196,7 +196,7 @@ } public void createResourceRemotely(ISemanticFileStore childStore, String name, Object context, IProgressMonitor monitor) - throws CoreException { + throws CoreException { createFileRemotely(childStore, name, null, context, monitor); @@ -229,17 +229,15 @@ } public ISemanticSpiResourceInfo fetchResourceInfo(ISemanticFileStore semanticFileStore, int options, IProgressMonitor monitor) - throws CoreException { - return new SemanticSpiResourceInfo(options, semanticFileStore - .getSessionProperty(LOCKHANDLE) != null, true, semanticFileStore + throws CoreException { + return new SemanticSpiResourceInfo(options, semanticFileStore.getSessionProperty(LOCKHANDLE) != null, true, semanticFileStore .getPersistentProperty(READONLY) != null, - getStore().getItemByPath(semanticFileStore.getPath().removeFirstSegments(2)) != null, - null, null); + getStore().getItemByPath(semanticFileStore.getPath().removeFirstSegments(2)) != null, null, null); } @Override - public InputStream openInputStreamInternal(ISemanticFileStore store, IProgressMonitor monitor, - ICacheTimestampSetter timeStampSetter) throws CoreException { + public InputStream openInputStreamInternal(ISemanticFileStore store, IProgressMonitor monitor, ICacheTimestampSetter timeStampSetter) + throws CoreException { RemoteItem item = getStore().getItemByPath(store.getPath().removeFirstSegments(2)); if (item.getType() != Type.FILE) { throw new SemanticResourceException(SemanticResourceStatusCode.RESOURCE_WITH_OTHER_TYPE_EXISTS, item.getPath(), ""); @@ -281,12 +279,11 @@ } } - public void synchronizeContentWithRemote(ISemanticFileStore store, SyncDirection direction, IProgressMonitor monitor, - MultiStatus status) { + public void synchronizeContentWithRemote(ISemanticFileStore store, SyncDirection direction, IProgressMonitor monitor, MultiStatus status) { RemoteItem item = getStore().getItemByPath(store.getPath().removeFirstSegments(2)); if (item.getType() != Type.FILE) { status.add(new SemanticResourceException(SemanticResourceStatusCode.RESOURCE_WITH_OTHER_TYPE_EXISTS, item.getPath(), "") - .getStatus()); + .getStatus()); return; } try { @@ -305,7 +302,7 @@ ICacheService srv = getCacheService(); IPath storePath = store.getPath(); - long localStamp = srv.getContentTimestamp(storePath); + long localStamp = getResourceTimestamp(store, monitor); long remoteStamp = file.getTimestamp(); // we need more elaborate checks here (read-only...) @@ -331,9 +328,9 @@ if (syncIn) { - srv.addContentWithTimestamp(storePath, new ByteArrayInputStream(file.getContent()), file.getTimestamp(), - ISemanticFileSystem.NONE, monitor); + srv.addContent(storePath, new ByteArrayInputStream(file.getContent()), ISemanticFileSystem.NONE, monitor); + setResourceTimestamp(store, file.getTimestamp(), monitor); } } catch (CoreException ce) { status.add(ce.getStatus());
diff --git a/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/TestsRestContentProvider.java b/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/TestsRestContentProvider.java index 576fdc4..a192475 100644 --- a/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/TestsRestContentProvider.java +++ b/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/TestsRestContentProvider.java
@@ -295,7 +295,7 @@ try { // make sure we get another file timestamp - Thread.sleep(2000); + Thread.sleep(100); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); @@ -324,7 +324,7 @@ try { // make sure we get another file timestamp - Thread.sleep(2000); + Thread.sleep(100); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace();
diff --git a/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/provider/RestTestContentProvider.java b/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/provider/RestTestContentProvider.java index 7e2c309..88e935c 100644 --- a/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/provider/RestTestContentProvider.java +++ b/tests/org.eclipse.ui.resources.semantic.examples.test/src/org/eclipse/core/resources/semantic/test/provider/RestTestContentProvider.java
@@ -48,29 +48,23 @@ public class RestTestContentProvider extends SampleRESTReadonlyContentProvider { @Override - public ISemanticSpiResourceInfo fetchResourceInfo( - ISemanticFileStore semanticFileStore, int options, - IProgressMonitor monitor) throws CoreException { + public ISemanticSpiResourceInfo fetchResourceInfo(ISemanticFileStore semanticFileStore, int options, IProgressMonitor monitor) + throws CoreException { String uriString; - if (SemanticSpiResourceInfo.isOptionRequested( - ISemanticFileSystem.RESOURCE_INFO_URI_STRING, options)) { + if (SemanticSpiResourceInfo.isOptionRequested(ISemanticFileSystem.RESOURCE_INFO_URI_STRING, options)) { uriString = this.getURIStringInternal(semanticFileStore); } else { uriString = null; } boolean existsRemotely = false; - if (SemanticSpiResourceInfo.isOptionRequested( - ISemanticFileSystem.RESOURCE_INFO_EXISTS_REMOTELY, options)) { + if (SemanticSpiResourceInfo.isOptionRequested(ISemanticFileSystem.RESOURCE_INFO_EXISTS_REMOTELY, options)) { String remoteURI = getURIString(semanticFileStore); if (remoteURI == null) { - throw new SemanticResourceException( - SemanticResourceStatusCode.REMOTE_URI_NOT_FOUND, - semanticFileStore.getPath(), NLS.bind( - "Remote URI is not set for file {0}", - semanticFileStore.getPath().toString())); + throw new SemanticResourceException(SemanticResourceStatusCode.REMOTE_URI_NOT_FOUND, semanticFileStore.getPath(), NLS.bind( + "Remote URI is not set for file {0}", semanticFileStore.getPath().toString())); } try { @@ -84,18 +78,15 @@ } boolean isReadOnly = isReadOnlyInternal(semanticFileStore); - return new SemanticSpiResourceInfo(options, false, false, isReadOnly, - existsRemotely, uriString, this - .getContentTypeInternal(semanticFileStore)); + return new SemanticSpiResourceInfo(options, false, false, isReadOnly, existsRemotely, uriString, this + .getContentTypeInternal(semanticFileStore)); } @Override - public void revertChanges(ISemanticFileStore semanticFileStore, - IProgressMonitor monitor) throws CoreException { + public void revertChanges(ISemanticFileStore semanticFileStore, IProgressMonitor monitor) throws CoreException { - MultiStatus status = new MultiStatus(TestPlugin.PLUGIN_ID, IStatus.OK, - NLS.bind("Revert Change Result for {0}", semanticFileStore - .getPath().toString()), null); + MultiStatus status = new MultiStatus(TestPlugin.PLUGIN_ID, IStatus.OK, NLS.bind("Revert Change Result for {0}", semanticFileStore + .getPath().toString()), null); dropCache(semanticFileStore, monitor, new IDropCacheVisitor() { public boolean shouldDrop(ISemanticFileStore store) { @@ -108,19 +99,19 @@ throw new CoreException(status); } + // this can only happen on files, no recursion + this.setReadOnly(semanticFileStore, true, monitor); + fillCache(semanticFileStore, monitor, status); if (!status.isOK()) { throw new CoreException(status); } - // this can only happen on files, no recursion - this.setReadOnly(semanticFileStore, true, monitor); } @Override - public void synchronizeContentWithRemote( - ISemanticFileStore semanticFileStore, SyncDirection direction, - IProgressMonitor monitor, MultiStatus status) { + public void synchronizeContentWithRemote(ISemanticFileStore semanticFileStore, SyncDirection direction, IProgressMonitor monitor, + MultiStatus status) { if (semanticFileStore.getType() == ISemanticFileStore.FILE) { if (semanticFileStore.isLocalOnly()) { @@ -132,9 +123,7 @@ uri = new URI(getURIString(semanticFileStore)); } catch (URISyntaxException e) { // $JL-EXC$ ignore - status.add(new Status(IStatus.ERROR, - SemanticResourcesPluginExamples.PLUGIN_ID, e - .getMessage())); + status.add(new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage())); return; } catch (CoreException e) { status.add(e.getStatus()); @@ -147,25 +136,19 @@ long remoteTimestamp; try { - remoteTimestamp = uri.toURL().openConnection() - .getLastModified(); + remoteTimestamp = uri.toURL().openConnection().getLastModified(); } catch (MalformedURLException e) { // $JL-EXC$ ignore - status.add(new Status(IStatus.ERROR, - SemanticResourcesPluginExamples.PLUGIN_ID, e - .getMessage())); + status.add(new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage())); return; } catch (IOException e) { // $JL-EXC$ ignore - status.add(new Status(IStatus.ERROR, - SemanticResourcesPluginExamples.PLUGIN_ID, e - .getMessage())); + status.add(new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage())); return; } long localTimestamp; try { - localTimestamp = getCacheService().getContentTimestamp( - semanticFileStore.getPath()); + localTimestamp = getResourceTimestamp(semanticFileStore, monitor); } catch (CoreException e) { status.add(e.getStatus()); return; @@ -175,25 +158,19 @@ } if (direction == SyncDirection.INCOMING || syncIn) { - this.dropCache(semanticFileStore, monitor, - this.deleteAllVisitor, status); + this.dropCache(semanticFileStore, monitor, this.deleteAllVisitor, status); this.fillCache(semanticFileStore, monitor, status); } if (direction == SyncDirection.OUTGOING || syncOut) { try { File file = new File(uri); - Util.transferStreams(getCacheService().getContent( - semanticFileStore.getPath()), new FileOutputStream( - file), monitor); - file.setLastModified(getCacheService().getContentTimestamp( - semanticFileStore.getPath())); + Util.transferStreams(getCacheService().getContent(semanticFileStore.getPath()), new FileOutputStream(file), monitor); + file.setLastModified(getResourceTimestamp(semanticFileStore, monitor)); } catch (CoreException e) { status.add(e.getStatus()); } catch (IOException e) { // $JL-EXC$ ignore - status.add(new Status(IStatus.ERROR, - SemanticResourcesPluginExamples.PLUGIN_ID, e - .getMessage())); + status.add(new Status(IStatus.ERROR, SemanticResourcesPluginExamples.PLUGIN_ID, e.getMessage())); return; } } @@ -208,8 +185,7 @@ } for (IFileStore store : childStores) { if (store instanceof ISemanticFileStore) { - synchronizeContentWithRemote((ISemanticFileStore) store, - direction, monitor, status); + synchronizeContentWithRemote((ISemanticFileStore) store, direction, monitor, status); } } } @@ -220,20 +196,17 @@ // @Override - public void addResource(ISemanticFileStore childStore, String name, - ResourceType resourceType, IProgressMonitor monitor) + public void addResource(ISemanticFileStore childStore, String name, ResourceType resourceType, IProgressMonitor monitor) throws CoreException { switch (resourceType) { - case FOLDER_TYPE: - // create internal data - childStore.addChildFolder(name); + case FOLDER_TYPE : + // create internal data + childStore.addChildFolder(name); - break; + break; - default: - throw new SemanticResourceException( - SemanticResourceStatusCode.METHOD_NOT_SUPPORTED, childStore - .getPath(), "Not supported"); + default : + throw new SemanticResourceException(SemanticResourceStatusCode.METHOD_NOT_SUPPORTED, childStore.getPath(), "Not supported"); } } @@ -246,14 +219,12 @@ return e.getStatus(); } } - return new Status(IStatus.OK, - SemanticResourcesPluginExamples.PLUGIN_ID, null); + return new Status(IStatus.OK, SemanticResourcesPluginExamples.PLUGIN_ID, null); } @Override public IStatus validateSave(ISemanticFileStore semanticFileStore) { - return validateEdit(new ISemanticFileStore[] { semanticFileStore }, - null); + return validateEdit(new ISemanticFileStore[] {semanticFileStore}, null); } }