mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Added the notion of a ContentContext to select the content store to write to.
Added AbstractRoutingContentStore as a start base implementation. Added warnings to drive config away from disused setTransactionService towards setRetryingTransactionHelper. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5734 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -55,7 +55,8 @@ import org.springframework.util.FileCopyUtils;
|
||||
* Implements all the convenience methods of the interface. The only methods
|
||||
* that need to be implemented, i.e. provide low-level content access are:
|
||||
* <ul>
|
||||
* <li>{@link #getDirectReadableChannel()} to read content from the repository</li>
|
||||
* <li>{@link #createReader()} to read content from the repository</li>
|
||||
* <li>{@link #getDirectReadableChannel()} to provide direct storage access</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Derek Hulley
|
||||
|
@@ -31,6 +31,7 @@ import java.util.Set;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.util.GUID;
|
||||
|
||||
/**
|
||||
@@ -45,16 +46,6 @@ import org.alfresco.util.GUID;
|
||||
*/
|
||||
public abstract class AbstractContentStore implements ContentStore
|
||||
{
|
||||
/**
|
||||
* Simple implementation that uses the
|
||||
* {@link ContentReader#exists() reader's exists} method as its implementation.
|
||||
*/
|
||||
public boolean exists(String contentUrl) throws ContentIOException
|
||||
{
|
||||
ContentReader reader = getReader(contentUrl);
|
||||
return reader.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new content URL. This must be supported by all
|
||||
* stores that are compatible with Alfresco.
|
||||
@@ -126,8 +117,33 @@ public abstract class AbstractContentStore implements ContentStore
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple implementation that uses the
|
||||
* {@link ContentReader#exists() reader's exists} method as its implementation.
|
||||
*/
|
||||
public boolean exists(String contentUrl) throws ContentIOException
|
||||
{
|
||||
ContentReader reader = getReader(contentUrl);
|
||||
return reader.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for URLs using null dates.
|
||||
*
|
||||
* @see ContentStore#getUrls(java.util.Date, java.util.Date)
|
||||
*/
|
||||
public final Set<String> getUrls() throws ContentIOException
|
||||
{
|
||||
return getUrls(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ContentContext
|
||||
* @see ContentStore#getWriter(ContentContext)
|
||||
*/
|
||||
public final ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl) throws ContentIOException
|
||||
{
|
||||
ContentContext ctx = new ContentContext(existingContentReader, newContentUrl);
|
||||
return getWriter(ctx);
|
||||
}
|
||||
}
|
||||
|
@@ -55,6 +55,7 @@ import org.springframework.util.FileCopyUtils;
|
||||
* Implements all the convenience methods of the interface. The only methods
|
||||
* that need to be implemented, i.e. provide low-level content access are:
|
||||
* <ul>
|
||||
* <li>{@link #getReader()} to create a reader to the underlying content</li>
|
||||
* <li>{@link #getDirectWritableChannel()} to write content to the repository</li>
|
||||
* </ul>
|
||||
*
|
||||
|
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.content;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* A store providing support for content store implementations that provide
|
||||
* routing of content read and write requests based on context.
|
||||
*
|
||||
* @see ContentContext
|
||||
*
|
||||
* @since 2.1
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public abstract class AbstractRoutingContentStore implements ContentStore
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(AbstractRoutingContentStore.class);
|
||||
|
||||
private SimpleCache<String, ContentStore> storesByContentUrl;
|
||||
private ReadLock storesCacheReadLock;
|
||||
private WriteLock storesCacheWriteLock;
|
||||
|
||||
protected AbstractRoutingContentStore()
|
||||
{
|
||||
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
storesCacheReadLock = lock.readLock();
|
||||
storesCacheWriteLock = lock.writeLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param storesCache cache of stores used to access URLs
|
||||
*/
|
||||
public void setStoresCache(SimpleCache<String, ContentStore> storesCache)
|
||||
{
|
||||
this.storesByContentUrl = storesCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns a list of all possible stores available for reading or writing
|
||||
*/
|
||||
protected abstract List<ContentStore> getAllStores();
|
||||
|
||||
/**
|
||||
* Get a content store based on the context provided. The applicability of the
|
||||
* context and even the types of context allowed are up to the implementation, but
|
||||
* normally there should be a fallback case for when the parameters are not adequate
|
||||
* to make a decision.
|
||||
*
|
||||
* @param ctx the context to use to make the choice
|
||||
* @return Returns the store most appropriate for the given context
|
||||
*/
|
||||
protected abstract ContentStore selectWriteStore(ContentContext ctx);
|
||||
|
||||
/**
|
||||
* Checks the cache for the store and ensures that the URL is in the store.
|
||||
*
|
||||
* @param contentUrl the content URL to search for
|
||||
* @return Returns the store matching the content URL
|
||||
*/
|
||||
private ContentStore selectReadStore(String contentUrl)
|
||||
{
|
||||
storesCacheReadLock.lock();
|
||||
try
|
||||
{
|
||||
// Check if the store is in the cache
|
||||
ContentStore store = storesByContentUrl.get(contentUrl);
|
||||
if (store != null && store.exists(contentUrl))
|
||||
{
|
||||
// We found a store and can use it
|
||||
return store;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
storesCacheReadLock.unlock();
|
||||
}
|
||||
// Get the write lock and double check
|
||||
storesCacheWriteLock.lock();
|
||||
try
|
||||
{
|
||||
// Double check
|
||||
ContentStore store = storesByContentUrl.get(contentUrl);
|
||||
if (store != null && store.exists(contentUrl))
|
||||
{
|
||||
// We found a store and can use it
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Found mapped store for content URL: \n" +
|
||||
" Content URL: " + contentUrl + "\n" +
|
||||
" Store: " + store);
|
||||
}
|
||||
return store;
|
||||
}
|
||||
// It isn't, so search all the stores
|
||||
List<ContentStore> stores = getAllStores();
|
||||
for (ContentStore storeInList : stores)
|
||||
{
|
||||
if (!store.exists(contentUrl))
|
||||
{
|
||||
// It is not in the store
|
||||
continue;
|
||||
}
|
||||
// We found one
|
||||
store = storeInList;
|
||||
// Put the value in the cache
|
||||
storesByContentUrl.put(contentUrl, store);
|
||||
break;
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Mapped content URL to store for reading: \n" +
|
||||
" Content URL: " + contentUrl + "\n" +
|
||||
" Store: " + store);
|
||||
}
|
||||
return store;
|
||||
}
|
||||
finally
|
||||
{
|
||||
storesCacheWriteLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This operation has to be performed on all the stores in order to maintain the
|
||||
* {@link ContentStore#exists(String)} contract.
|
||||
*/
|
||||
public boolean delete(String contentUrl) throws ContentIOException
|
||||
{
|
||||
boolean deleted = true;
|
||||
List<ContentStore> stores = getAllStores();
|
||||
for (ContentStore store : stores)
|
||||
{
|
||||
deleted &= store.delete(contentUrl);
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Deleted content URL from stores: \n" +
|
||||
" Stores: " + stores.size() + "\n" +
|
||||
" Deleted: " + deleted);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #selectReadStore(String)
|
||||
*/
|
||||
public boolean exists(String contentUrl) throws ContentIOException
|
||||
{
|
||||
ContentStore store = selectReadStore(contentUrl);
|
||||
return (store != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns a valid reader from one of the stores otherwise
|
||||
* a {@link EmptyContentReader} is returned.
|
||||
*/
|
||||
public ContentReader getReader(String contentUrl) throws ContentIOException
|
||||
{
|
||||
ContentStore store = selectReadStore(contentUrl);
|
||||
if (store != null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Getting reader from store: \n" +
|
||||
" Content URL: " + contentUrl + "\n" +
|
||||
" Store: " + store);
|
||||
}
|
||||
return store.getReader(contentUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Getting empty reader for content URL: " + contentUrl);
|
||||
}
|
||||
return new EmptyContentReader(contentUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a set of URLs from all stores.
|
||||
*/
|
||||
public Set<String> getUrls() throws ContentIOException
|
||||
{
|
||||
Set<String> urls = new HashSet<String>(1139);
|
||||
List<ContentStore> stores = getAllStores();
|
||||
for (ContentStore store : stores)
|
||||
{
|
||||
Set<String> storeUrls = store.getUrls();
|
||||
urls.addAll(storeUrls);
|
||||
}
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Found " + urls.size() + " URLs from " + stores.size() + " stores");
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a set of URLs from all stores given the date range.
|
||||
*/
|
||||
public Set<String> getUrls(Date createdAfter, Date createdBefore) throws ContentIOException
|
||||
{
|
||||
Set<String> urls = new HashSet<String>(1139);
|
||||
List<ContentStore> stores = getAllStores();
|
||||
for (ContentStore store : stores)
|
||||
{
|
||||
Set<String> storeUrls = store.getUrls(createdAfter, createdBefore);
|
||||
urls.addAll(storeUrls);
|
||||
}
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Found " + urls.size() + " URLs from " + stores.size() + " stores");
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a store for the given context and caches store that was used.
|
||||
*
|
||||
* @see #selectWriteStore(ContentContext)
|
||||
*/
|
||||
public ContentWriter getWriter(ContentContext context) throws ContentIOException
|
||||
{
|
||||
// Select the store for writing
|
||||
ContentStore store = selectWriteStore(context);
|
||||
ContentWriter writer = store.getWriter(context);
|
||||
// Cache the store against the URL
|
||||
storesCacheWriteLock.lock();
|
||||
try
|
||||
{
|
||||
String contentUrl = writer.getContentUrl();
|
||||
storesByContentUrl.put(contentUrl, store);
|
||||
}
|
||||
finally
|
||||
{
|
||||
storesCacheWriteLock.unlock();
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Got writer and cache URL from store: \n" +
|
||||
" Context: " + context + "\n" +
|
||||
" Writer: " + writer + "\n" +
|
||||
" Store: " + store);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see
|
||||
*/
|
||||
public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl) throws ContentIOException
|
||||
{
|
||||
return getWriter(new ContentContext(existingContentReader, newContentUrl));
|
||||
}
|
||||
}
|
91
source/java/org/alfresco/repo/content/ContentContext.java
Normal file
91
source/java/org/alfresco/repo/content/ContentContext.java
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.content;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
|
||||
/**
|
||||
* The location and lookup data for content. The very least data required to
|
||||
* find content or assign a content writer is the content URL and any previous
|
||||
* content that may have logically existed.
|
||||
* <p>
|
||||
* Although this class is doesn't enforce any conditions on the context,
|
||||
* derived instances may have relationships that need to be maintained between
|
||||
* various context values.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class ContentContext implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 6476617391229895125L;
|
||||
|
||||
/** An empty context. */
|
||||
public static final ContentContext NULL_CONTEXT = new ContentContext(null, null);
|
||||
|
||||
private ContentReader existingContentReader;
|
||||
private String contentUrl;
|
||||
|
||||
/**
|
||||
* Construct the instance with the content URL.
|
||||
*
|
||||
* @param existingContentReader content with which to seed the new writer - may be <tt>null</tt>
|
||||
* @param contentUrl the content URL - may be <tt>null</tt>
|
||||
*/
|
||||
public ContentContext(ContentReader existingContentReader, String contentUrl)
|
||||
{
|
||||
this.existingContentReader = existingContentReader;
|
||||
this.contentUrl = contentUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
sb.append("ContentContext")
|
||||
.append("[ contentUrl=").append(getContentUrl())
|
||||
.append(", existing=").append((getExistingContentReader() == null ? false : true))
|
||||
.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the content to seed the writer with - may be <tt>null</tt>
|
||||
*/
|
||||
public ContentReader getExistingContentReader()
|
||||
{
|
||||
return existingContentReader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the content URL for the content's context - may be <tt>null</tt>
|
||||
*/
|
||||
public String getContentUrl()
|
||||
{
|
||||
return contentUrl;
|
||||
}
|
||||
|
||||
}
|
@@ -27,6 +27,7 @@ package org.alfresco.repo.content;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ContentAccessor;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentStreamListener;
|
||||
@@ -48,6 +49,8 @@ import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
* <li> <b>year</b>: year </li>
|
||||
* <li> <b>month</b>: 1-based month of the year </li>
|
||||
* <li> <b>day</b>: 1-based day of the month </li>
|
||||
* <li> <b>hour</b>: 0-based hour of the day </li>
|
||||
* <li> <b>minute</b>: 0-based minute of the hour </li>
|
||||
* <li> <b>GUID</b>: A unique identifier </li>
|
||||
* </ul>
|
||||
* The old <b>file://</b> prefix must still be supported - and functionality
|
||||
@@ -77,14 +80,12 @@ public interface ContentStore
|
||||
public boolean exists(String contentUrl) throws ContentIOException;
|
||||
|
||||
/**
|
||||
* Get the accessor with which to read from the content
|
||||
* at the given URL. The reader is <b>stateful</b> and
|
||||
* can <b>only be used once</b>.
|
||||
* Get the accessor with which to read from the content at the given URL.
|
||||
* The reader is <b>stateful</b> and can <b>only be used once</b>.
|
||||
*
|
||||
* @param contentUrl the path to where the content is located
|
||||
* @return Returns a read-only content accessor for the given URL. There may
|
||||
* be no content at the given URL, but the reader must still be returned.
|
||||
* The reader may implement the {@link RandomAccessContent random access interface}.
|
||||
* @throws ContentIOException
|
||||
*
|
||||
* @see #exists(String)
|
||||
@@ -98,22 +99,31 @@ public interface ContentStore
|
||||
* <b>only be used once</b>. The location may be specified but must, in that case,
|
||||
* be a valid and unused URL.
|
||||
* <p>
|
||||
* The store will ensure that the {@link ContentAccessor#getContentUrl() new content URL} will
|
||||
* be valid for all subsequent read attempts.
|
||||
* <p>
|
||||
* By supplying a reader to existing content, the store implementation may
|
||||
* enable {@link RandomAccessContent random access}. The store implementation
|
||||
* can enable this by copying the existing content into the new location
|
||||
* before supplying a writer onto the new content.
|
||||
*
|
||||
* @param existingContentReader a reader onto any existing content for which
|
||||
* a writer is required - may be null
|
||||
* @param newContentUrl an unused, valid URL to use - may be null.
|
||||
* @return Returns a write-only content accessor, possibly implementing
|
||||
* the {@link RandomAccessContent random access interface}
|
||||
* @throws ContentIOException if completely new content storage could not be
|
||||
* created
|
||||
* @param context the context of content.
|
||||
* @return Returns a write-only content accessor
|
||||
* @throws ContentIOException if completely new content storage could not be created
|
||||
*
|
||||
* @see #getWriter(ContentReader, String)
|
||||
* @see ContentWriter#addListener(ContentStreamListener)
|
||||
* @see ContentWriter#getContentUrl()
|
||||
*/
|
||||
public ContentWriter getWriter(ContentContext context) throws ContentIOException;
|
||||
|
||||
/**
|
||||
* Shortcut method to {@link #getWriter(ContentContext)}.
|
||||
*
|
||||
* @see #getWriter(ContentContext)
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl) throws ContentIOException;
|
||||
|
||||
/**
|
||||
@@ -140,13 +150,11 @@ public interface ContentStore
|
||||
* <p>
|
||||
* A delete cannot be forced since it is much better to have the
|
||||
* file remain longer than desired rather than deleted prematurely.
|
||||
* The store implementation should safeguard files for certain
|
||||
* minimum period, in which case all files younger than a certain
|
||||
* age will not be deleted.
|
||||
*
|
||||
* @param contentUrl the URL of the content to delete
|
||||
* @return Return true if the content was deleted (either by this or
|
||||
* another operation), otherwise false
|
||||
* another operation), otherwise false. If the content no longer
|
||||
* exists, then <tt>true</tt> is returned.
|
||||
* @throws ContentIOException
|
||||
*/
|
||||
public boolean delete(String contentUrl) throws ContentIOException;
|
||||
|
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.content;
|
||||
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
|
||||
/**
|
||||
* A blank reader for which <code>exists()</code> always returns false.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class EmptyContentReader extends AbstractContentReader
|
||||
{
|
||||
/**
|
||||
* @param contentUrl the content URL
|
||||
*/
|
||||
public EmptyContentReader(String contentUrl)
|
||||
{
|
||||
super(contentUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns an instance of the this class
|
||||
*/
|
||||
@Override
|
||||
protected ContentReader createReader() throws ContentIOException
|
||||
{
|
||||
return new EmptyContentReader(this.getContentUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException
|
||||
{
|
||||
throw new UnsupportedOperationException("The content never exists");
|
||||
}
|
||||
|
||||
public boolean exists()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public long getLastModified()
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
|
||||
public long getSize()
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.content;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.ParameterCheck;
|
||||
|
||||
/**
|
||||
* Context information for node-related content.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class NodeContentContext extends ContentContext
|
||||
{
|
||||
private static final long serialVersionUID = -1836714367516857907L;
|
||||
|
||||
private NodeRef nodeRef;
|
||||
private QName propertyQName;
|
||||
|
||||
/**
|
||||
* Construct the instance with the content URL.
|
||||
*
|
||||
* @param existingContentReader content with which to seed the new writer - may be <tt>null</tt>
|
||||
* @param contentUrl the content URL - may be <tt>null</tt>
|
||||
* @param nodeRef the node holding the content metadata - may not be <tt>null</tt>
|
||||
* @param propertyQName the property holding the content metadata - may not be <tt>null</tt>
|
||||
*/
|
||||
public NodeContentContext(
|
||||
ContentReader existingContentReader,
|
||||
String contentUrl,
|
||||
NodeRef nodeRef,
|
||||
QName propertyQName)
|
||||
{
|
||||
super(existingContentReader, contentUrl);
|
||||
ParameterCheck.mandatory("nodeRef", nodeRef);
|
||||
ParameterCheck.mandatory("propertyQName", propertyQName);
|
||||
this.nodeRef = nodeRef;
|
||||
this.propertyQName = propertyQName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the node holding the content metadata
|
||||
*/
|
||||
public NodeRef getNodeRef()
|
||||
{
|
||||
return nodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Returns the property holding the content metadata
|
||||
*/
|
||||
public QName getPropertyQName()
|
||||
{
|
||||
return propertyQName;
|
||||
}
|
||||
}
|
@@ -77,7 +77,6 @@ public class RoutingContentService implements ContentService
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(RoutingContentService.class);
|
||||
|
||||
private TransactionService transactionService;
|
||||
private DictionaryService dictionaryService;
|
||||
private NodeService nodeService;
|
||||
private AVMService avmService;
|
||||
@@ -110,9 +109,12 @@ public class RoutingContentService implements ContentService
|
||||
this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Replaced by {@link #setRetryingTransactionHelper(RetryingTransactionHelper)}
|
||||
*/
|
||||
public void setTransactionService(TransactionService transactionService)
|
||||
{
|
||||
this.transactionService = transactionService;
|
||||
logger.warn("Property 'transactionService' has been replaced by 'retryingTransactionHelper'.");
|
||||
}
|
||||
|
||||
public void setRetryingTransactionHelper(RetryingTransactionHelper helper)
|
||||
@@ -313,7 +315,7 @@ public class RoutingContentService implements ContentService
|
||||
}
|
||||
String contentUrl = contentData.getContentUrl();
|
||||
|
||||
// TODO: Choose the store to read from at runtime
|
||||
// The context of the read is entirely described by the URL
|
||||
ContentReader reader = store.getReader(contentUrl);
|
||||
|
||||
// set extra data on the reader
|
||||
@@ -338,12 +340,11 @@ public class RoutingContentService implements ContentService
|
||||
|
||||
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
|
||||
{
|
||||
// TODO: Choose the store to write to at runtime
|
||||
|
||||
if (nodeRef == null)
|
||||
{
|
||||
ContentContext ctx = new ContentContext(null, null);
|
||||
// for this case, we just give back a valid URL into the content store
|
||||
ContentWriter writer = store.getWriter(null, null);
|
||||
ContentWriter writer = store.getWriter(ctx);
|
||||
// done
|
||||
return writer;
|
||||
}
|
||||
@@ -353,7 +354,8 @@ public class RoutingContentService implements ContentService
|
||||
|
||||
// get the content using the (potentially) existing content - the new content
|
||||
// can be wherever the store decides.
|
||||
ContentWriter writer = store.getWriter(existingContentReader, null);
|
||||
ContentContext ctx = new NodeContentContext(existingContentReader, null, nodeRef, propertyQName);
|
||||
ContentWriter writer = store.getWriter(ctx);
|
||||
|
||||
// Special case for AVM repository.
|
||||
Serializable contentValue = null;
|
||||
@@ -395,7 +397,7 @@ public class RoutingContentService implements ContentService
|
||||
public ContentWriter getTempWriter()
|
||||
{
|
||||
// there is no existing content and we don't specify the location of the new content
|
||||
return tempStore.getWriter(null, null);
|
||||
return tempStore.getWriter(ContentContext.NULL_CONTEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -32,6 +32,7 @@ import java.util.Set;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.content.AbstractContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
@@ -39,10 +40,11 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Provides a store of node content directly to the file system.
|
||||
* Provides a store of node content directly to the file system. The writers
|
||||
* are generated using information from the {@link ContentContext simple content context}.
|
||||
* <p>
|
||||
* The file names obey, as they must, the URL naming convention
|
||||
* as specified in the {@link org.alfresco.repo.content.ContentStore}.
|
||||
* as specified in the {@link org.alfresco.repo.content.ContentStore ContentStore interface}.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
@@ -241,8 +243,10 @@ public class FileContentStore extends AbstractContentStore
|
||||
/**
|
||||
* @return Returns a writer onto a location based on the date
|
||||
*/
|
||||
public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl)
|
||||
public ContentWriter getWriter(ContentContext ctx)
|
||||
{
|
||||
ContentReader existingContentReader = ctx.getExistingContentReader();
|
||||
String newContentUrl = ctx.getContentUrl();
|
||||
try
|
||||
{
|
||||
File file = null;
|
||||
|
@@ -35,6 +35,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.content.AbstractContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
@@ -106,7 +107,6 @@ public class ReplicatingContentStore extends AbstractContentStore
|
||||
|
||||
private static Log logger = LogFactory.getLog(ReplicatingContentStore.class);
|
||||
|
||||
private TransactionService transactionService;
|
||||
private RetryingTransactionHelper transactionHelper;
|
||||
private ContentStore primaryStore;
|
||||
private List<ContentStore> secondaryStores;
|
||||
@@ -131,17 +131,17 @@ public class ReplicatingContentStore extends AbstractContentStore
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to ensure that content listeners are executed in a transaction
|
||||
*
|
||||
* @param transactionService
|
||||
* @deprecated Replaced with {@link #setRetryingTransactionHelper(RetryingTransactionHelper)}
|
||||
*/
|
||||
public void setTransactionService(TransactionService transactionService)
|
||||
{
|
||||
this.transactionService = transactionService;
|
||||
logger.warn("Property 'transactionService' has been replaced with 'retryingTransactionHelper'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the retrying transaction helper.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public void setRetryingTransactionHelper(RetryingTransactionHelper helper)
|
||||
{
|
||||
@@ -273,7 +273,8 @@ public class ReplicatingContentStore extends AbstractContentStore
|
||||
return primaryContentReader;
|
||||
}
|
||||
// get a writer
|
||||
ContentWriter primaryContentWriter = primaryStore.getWriter(existingContentReader, contentUrl);
|
||||
ContentContext ctx = new ContentContext(existingContentReader, contentUrl);
|
||||
ContentWriter primaryContentWriter = primaryStore.getWriter(ctx);
|
||||
// copy it over
|
||||
primaryContentWriter.putContent(existingContentReader);
|
||||
// get a writer to the new content
|
||||
@@ -287,13 +288,10 @@ public class ReplicatingContentStore extends AbstractContentStore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl) throws ContentIOException
|
||||
public ContentWriter getWriter(ContentContext ctx)
|
||||
{
|
||||
// get the writer
|
||||
ContentWriter writer = primaryStore.getWriter(existingContentReader, newContentUrl);
|
||||
ContentWriter writer = primaryStore.getWriter(ctx);
|
||||
|
||||
// attach a replicating listener if outbound replication is on
|
||||
if (outbound)
|
||||
@@ -434,7 +432,8 @@ public class ReplicatingContentStore extends AbstractContentStore
|
||||
ContentReader reader = writer.getReader();
|
||||
String contentUrl = reader.getContentUrl();
|
||||
// in order to replicate, we have to specify the URL that we are going to write to
|
||||
ContentWriter replicatedWriter = store.getWriter(null, contentUrl);
|
||||
ContentContext ctx = new ContentContext(null, contentUrl);
|
||||
ContentWriter replicatedWriter = store.getWriter(ctx);
|
||||
// write it
|
||||
replicatedWriter.putContent(reader);
|
||||
|
||||
|
@@ -33,9 +33,9 @@ import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.alfresco.repo.content.AbstractContentReadWriteTest;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.filestore.FileContentStore;
|
||||
import org.alfresco.repo.transaction.DummyTransactionService;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.util.GUID;
|
||||
@@ -77,7 +77,6 @@ public class ReplicatingContentStoreTest extends AbstractContentReadWriteTest
|
||||
secondaryStores.add(store);
|
||||
}
|
||||
replicatingStore = new ReplicatingContentStore();
|
||||
replicatingStore.setTransactionService(new DummyTransactionService());
|
||||
replicatingStore.setPrimaryStore(primaryStore);
|
||||
replicatingStore.setSecondaryStores(secondaryStores);
|
||||
replicatingStore.setOutbound(false);
|
||||
@@ -184,7 +183,7 @@ public class ReplicatingContentStoreTest extends AbstractContentReadWriteTest
|
||||
|
||||
// pick a secondary store and write some content to it
|
||||
ContentStore secondaryStore = secondaryStores.get(2);
|
||||
ContentWriter writer = secondaryStore.getWriter(null, null);
|
||||
ContentWriter writer = secondaryStore.getWriter(ContentContext.NULL_CONTEXT);
|
||||
writer.putContent(SOME_CONTENT);
|
||||
String contentUrl = writer.getContentUrl();
|
||||
|
||||
|
@@ -28,6 +28,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.AbstractContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.node.db.NodeDaoService;
|
||||
import org.alfresco.repo.search.Indexer;
|
||||
@@ -148,7 +149,8 @@ public class MissingContentReindexComponentTest extends TestCase
|
||||
}
|
||||
|
||||
// now put some content in the store
|
||||
ContentWriter writer = contentStore.getWriter(null, contentUrl);
|
||||
ContentContext ctx = new ContentContext(null, contentUrl);
|
||||
ContentWriter writer = contentStore.getWriter(ctx);
|
||||
writer.setMimetype("text/plain");
|
||||
writer.setEncoding("UTF8");
|
||||
writer.putContent("123abc456def");
|
||||
|
Reference in New Issue
Block a user