mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-15 15:02:20 +00:00
CachingContentStore handles "spoof://" content URLs (ACE-4516:DataLoad file spoofing fails with S3 connector)
- Any store can now be used with the spoofed data load by wrapping it in the CachingContentStore (see S3 Connector) - Will work with S3 Connector against Alfresco 5.1 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@115382 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -27,6 +27,8 @@ import org.alfresco.repo.content.ContentContext;
|
|||||||
import org.alfresco.repo.content.ContentStore;
|
import org.alfresco.repo.content.ContentStore;
|
||||||
import org.alfresco.repo.content.caching.quota.QuotaManagerStrategy;
|
import org.alfresco.repo.content.caching.quota.QuotaManagerStrategy;
|
||||||
import org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategy;
|
import org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategy;
|
||||||
|
import org.alfresco.repo.content.filestore.FileContentStore;
|
||||||
|
import org.alfresco.repo.content.filestore.SpoofedTextContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
||||||
@@ -46,6 +48,9 @@ import org.springframework.context.ApplicationEventPublisherAware;
|
|||||||
* slower that FileContentStore - otherwise performance may actually degrade from its use.
|
* slower that FileContentStore - otherwise performance may actually degrade from its use.
|
||||||
* <p>
|
* <p>
|
||||||
* It is important that cacheOnInbound is set to true for exceptionally slow backing stores.
|
* It is important that cacheOnInbound is set to true for exceptionally slow backing stores.
|
||||||
|
* <p>
|
||||||
|
* This store handles the {@link FileContentStore#SPOOF_PROTOCOL} and can be used to wrap stores
|
||||||
|
* that do not handle the protocol out of the box e.g. the S3 connector's store.
|
||||||
*
|
*
|
||||||
* @author Matt Ward
|
* @author Matt Ward
|
||||||
*/
|
*/
|
||||||
@@ -121,15 +126,39 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
|
|||||||
return backingStore.getRootLocation();
|
return backingStore.getRootLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p>
|
||||||
|
* For {@link #SPOOF_PROTOCOL spoofed} URLs, the URL always exists.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean exists(String contentUrl)
|
public boolean exists(String contentUrl)
|
||||||
|
{
|
||||||
|
if (contentUrl.startsWith(FileContentStore.SPOOF_PROTOCOL))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return backingStore.exists(contentUrl);
|
return backingStore.exists(contentUrl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p>
|
||||||
|
* This store handles the {@link FileContentStore#SPOOF_PROTOCOL} so that underlying stores do not need
|
||||||
|
* to implement anything <a href="https://issues.alfresco.com/jira/browse/ACE-4516">related to spoofing</a>.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ContentReader getReader(String contentUrl)
|
public ContentReader getReader(String contentUrl)
|
||||||
{
|
{
|
||||||
|
// Handle the spoofed URL
|
||||||
|
if (contentUrl.startsWith(FileContentStore.SPOOF_PROTOCOL))
|
||||||
|
{
|
||||||
|
return new SpoofedTextContentReader(contentUrl);
|
||||||
|
}
|
||||||
|
|
||||||
// Use pool of locks - which one is determined by a hash of the URL.
|
// Use pool of locks - which one is determined by a hash of the URL.
|
||||||
// This will stop the content from being read/cached multiple times from the backing store
|
// This will stop the content from being read/cached multiple times from the backing store
|
||||||
// when it should only be read once - cached versions should be returned after that.
|
// when it should only be read once - cached versions should be returned after that.
|
||||||
@@ -317,6 +346,12 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
|
|||||||
@Override
|
@Override
|
||||||
public boolean delete(String contentUrl)
|
public boolean delete(String contentUrl)
|
||||||
{
|
{
|
||||||
|
if (contentUrl.startsWith(FileContentStore.SPOOF_PROTOCOL))
|
||||||
|
{
|
||||||
|
// This is not a failure but the content can never actually be deleted
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ReentrantReadWriteLock readWriteLock = readWriteLock(contentUrl);
|
ReentrantReadWriteLock readWriteLock = readWriteLock(contentUrl);
|
||||||
ReadLock readLock = readWriteLock.readLock();
|
ReadLock readLock = readWriteLock.readLock();
|
||||||
readLock.lock();
|
readLock.lock();
|
||||||
|
@@ -429,7 +429,7 @@ public class FileContentStore
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This implementation requires that the URL start with
|
* This implementation requires that the URL start with
|
||||||
* {@link FileContentStore#STORE_PROTOCOL }.
|
* {@link FileContentStore#STORE_PROTOCOL } or {@link FileContentStore#SPOOF_PROTOCOL }
|
||||||
*/
|
*/
|
||||||
public ContentReader getReader(String contentUrl)
|
public ContentReader getReader(String contentUrl)
|
||||||
{
|
{
|
||||||
|
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyLong;
|
import static org.mockito.Matchers.anyLong;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
@@ -40,6 +41,7 @@ import org.alfresco.repo.content.ContentContext;
|
|||||||
import org.alfresco.repo.content.ContentStore;
|
import org.alfresco.repo.content.ContentStore;
|
||||||
import org.alfresco.repo.content.caching.quota.QuotaManagerStrategy;
|
import org.alfresco.repo.content.caching.quota.QuotaManagerStrategy;
|
||||||
import org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategy;
|
import org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategy;
|
||||||
|
import org.alfresco.repo.content.filestore.SpoofedTextContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentReader;
|
||||||
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
||||||
@@ -371,6 +373,41 @@ public class CachingContentStoreTest
|
|||||||
verify(bsWriter).setMimetype("not/real/mimetype");
|
verify(bsWriter).setMimetype("not/real/mimetype");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Tests for spoofed content follow...
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spoofedGetReader()
|
||||||
|
{
|
||||||
|
cachingStore = new CachingContentStore(backingStore, cache, true);
|
||||||
|
String url = SpoofedTextContentReader.createContentUrl(Locale.ENGLISH, 0L, 1024L);
|
||||||
|
ContentReader reader = cachingStore.getReader(url);
|
||||||
|
assertTrue(reader.exists());
|
||||||
|
assertEquals(1024, reader.getSize());
|
||||||
|
verify(backingStore, never()).getReader(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spoofedDelete()
|
||||||
|
{
|
||||||
|
cachingStore = new CachingContentStore(backingStore, cache, true);
|
||||||
|
String url = SpoofedTextContentReader.createContentUrl(Locale.ENGLISH, 0L, 1024L);
|
||||||
|
boolean deleted = cachingStore.delete(url);
|
||||||
|
assertFalse(deleted);
|
||||||
|
verify(backingStore, never()).delete(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spoofedExists()
|
||||||
|
{
|
||||||
|
cachingStore = new CachingContentStore(backingStore, cache, true);
|
||||||
|
String url = SpoofedTextContentReader.createContentUrl(Locale.ENGLISH, 0L, 1024L);
|
||||||
|
boolean exists = cachingStore.exists(url);
|
||||||
|
assertTrue(exists);
|
||||||
|
verify(backingStore, never()).exists(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Tests for delegated methods follow...
|
// Tests for delegated methods follow...
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Reference in New Issue
Block a user