mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
ALF-9613: caching content store
http://issues.alfresco.com/jira/browse/ALF-9613 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29662 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2011 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.content.caching;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.sf.ehcache.CacheManager;
|
||||
|
||||
import org.alfresco.repo.cache.EhCacheAdapter;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
import org.alfresco.repo.content.filestore.FileContentWriter;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.util.TempFileProvider;
|
||||
|
||||
/**
|
||||
* The one and only implementation of the ContentCache class.
|
||||
* <p>
|
||||
* Binary content data itself is stored on disk in temporary files managed
|
||||
* by Alfresco (see {@link org.alfresco.util.TempFileProvider}).
|
||||
* <p>
|
||||
* The in-memory lookup table is provided by Ehcache.
|
||||
*
|
||||
* @author Matt Ward
|
||||
*/
|
||||
public class ContentCacheImpl implements ContentCache
|
||||
{
|
||||
private static final String CACHE_DIR = "caching_cs";
|
||||
private static final String TMP_FILE_EXTENSION = ".tmp";
|
||||
private static final String EHCACHE_NAME = "contentStoreCache";
|
||||
private static final long T24_HOURS = 86400;
|
||||
private final File cacheRoot = TempFileProvider.getLongLifeTempDir(CACHE_DIR);
|
||||
private EhCacheAdapter<String, String> memoryStore;
|
||||
|
||||
public ContentCacheImpl()
|
||||
{
|
||||
// TODO: Configuration to be moved out into Spring
|
||||
memoryStore = new EhCacheAdapter<String, String>();
|
||||
configureMemoryStore();
|
||||
}
|
||||
|
||||
private void configureMemoryStore()
|
||||
{
|
||||
CacheManager manager = CacheManager.getInstance();
|
||||
|
||||
// Create the cache if it hasn't already been created.
|
||||
if (!manager.cacheExists(EHCACHE_NAME))
|
||||
{
|
||||
net.sf.ehcache.Cache memoryOnlyCache =
|
||||
new net.sf.ehcache.Cache(EHCACHE_NAME, 10000, false, false, T24_HOURS, T24_HOURS);
|
||||
|
||||
manager.addCache(memoryOnlyCache);
|
||||
}
|
||||
|
||||
memoryStore.setCache(manager.getCache(EHCACHE_NAME));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean contains(String contentUrl)
|
||||
{
|
||||
return memoryStore.contains(contentUrl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ContentReader getReader(String contentUrl)
|
||||
{
|
||||
if (memoryStore.contains(contentUrl))
|
||||
{
|
||||
String path = memoryStore.get(contentUrl);
|
||||
return new FileContentReader(new File(path), contentUrl);
|
||||
}
|
||||
|
||||
throw new CacheMissException(contentUrl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean put(String contentUrl, ContentReader source)
|
||||
{
|
||||
File cacheFile = createCacheFile(contentUrl);
|
||||
|
||||
// Copy the content from the source into a cache file
|
||||
if (source.getSize() > 0L)
|
||||
{
|
||||
source.getContent(cacheFile);
|
||||
// Add a record of the cached file to the in-memory cache.
|
||||
memoryStore.put(contentUrl, cacheFile.getAbsolutePath());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a File object and makes any intermediate directories in the path.
|
||||
*
|
||||
* @param contentUrl
|
||||
* @return File
|
||||
*/
|
||||
private File createCacheFile(String contentUrl)
|
||||
{
|
||||
File path = new File(cacheRoot, pathFromUrl(contentUrl));
|
||||
File parentDir = path.getParentFile();
|
||||
|
||||
parentDir.mkdirs();
|
||||
|
||||
File cacheFile = TempFileProvider.createTempFile(path.getName(), TMP_FILE_EXTENSION, parentDir);
|
||||
return cacheFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* @see org.alfresco.repo.content.caching.ContentCache#remove(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void remove(String contentUrl)
|
||||
{
|
||||
// Remove from the in-memory cache, but not from disk. Let the clean-up process do this asynchronously.
|
||||
memoryStore.remove(contentUrl);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @see org.alfresco.repo.content.caching.ContentCache#getWriter(org.alfresco.repo.content.ContentContext)
|
||||
*/
|
||||
@Override
|
||||
public ContentWriter getWriter(final String url)
|
||||
{
|
||||
// Get a writer to a cache file.
|
||||
final File cacheFile = createCacheFile(url);
|
||||
ContentWriter writer = new FileContentWriter(cacheFile);
|
||||
|
||||
// Attach a listener to populate the in-memory store when done writing.
|
||||
writer.addListener(new ContentStreamListener()
|
||||
{
|
||||
@Override
|
||||
public void contentStreamClosed() throws ContentIOException
|
||||
{
|
||||
memoryStore.put(url, cacheFile.getAbsolutePath());
|
||||
}
|
||||
});
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a content URL to a relative path name where the protocol will
|
||||
* be the name of a subdirectory. For example:
|
||||
* <p>
|
||||
* store://2011/8/5/15/4/386595e0-3b52-4d5c-a32d-df9d0b9fd56e.bin
|
||||
* <p>
|
||||
* will become:
|
||||
* <p>
|
||||
* store/2011/8/5/15/4/386595e0-3b52-4d5c-a32d-df9d0b9fd56e.bin
|
||||
*
|
||||
* @param contentUrl
|
||||
* @return String representation of relative path to file.
|
||||
*/
|
||||
private String pathFromUrl(String contentUrl)
|
||||
{
|
||||
return contentUrl.replaceFirst("://", "/");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user