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:
Derek Hulley
2007-05-21 09:16:31 +00:00
parent 8ee3f781f4
commit 6f4ab835fe
14 changed files with 631 additions and 56 deletions

View File

@@ -74,9 +74,6 @@
<value>false</value> <value>false</value>
</property> </property>
<!-- this is required if outbound replication is active, otherwise not --> <!-- this is required if outbound replication is active, otherwise not -->
<property name="transactionService">
<ref bean="transactionComponent" />
</property>
<property name="retryingTransactionHelper"> <property name="retryingTransactionHelper">
<ref bean="retryingTransactionHelper"/> <ref bean="retryingTransactionHelper"/>
</property> </property>

View File

@@ -55,7 +55,8 @@ import org.springframework.util.FileCopyUtils;
* Implements all the convenience methods of the interface. The only methods * Implements all the convenience methods of the interface. The only methods
* that need to be implemented, i.e. provide low-level content access are: * that need to be implemented, i.e. provide low-level content access are:
* <ul> * <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> * </ul>
* *
* @author Derek Hulley * @author Derek Hulley

View File

@@ -31,6 +31,7 @@ import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
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.ContentWriter;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
/** /**
@@ -45,16 +46,6 @@ import org.alfresco.util.GUID;
*/ */
public abstract class AbstractContentStore implements ContentStore 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 * Creates a new content URL. This must be supported by all
* stores that are compatible with Alfresco. * stores that are compatible with Alfresco.
@@ -126,8 +117,33 @@ public abstract class AbstractContentStore implements ContentStore
return path; 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 public final Set<String> getUrls() throws ContentIOException
{ {
return getUrls(null, null); 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);
}
} }

View File

@@ -55,6 +55,7 @@ import org.springframework.util.FileCopyUtils;
* Implements all the convenience methods of the interface. The only methods * Implements all the convenience methods of the interface. The only methods
* that need to be implemented, i.e. provide low-level content access are: * that need to be implemented, i.e. provide low-level content access are:
* <ul> * <ul>
* <li>{@link #getReader()} to create a reader to the underlying content</li>
* <li>{@link #getDirectWritableChannel()} to write content to the repository</li> * <li>{@link #getDirectWritableChannel()} to write content to the repository</li>
* </ul> * </ul>
* *

View File

@@ -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));
}
}

View 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;
}
}

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.content;
import java.util.Date; import java.util.Date;
import java.util.Set; import java.util.Set;
import org.alfresco.service.cmr.repository.ContentAccessor;
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;
@@ -48,6 +49,8 @@ import org.alfresco.service.cmr.repository.ContentWriter;
* <li> <b>year</b>: year </li> * <li> <b>year</b>: year </li>
* <li> <b>month</b>: 1-based month of the 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>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> * <li> <b>GUID</b>: A unique identifier </li>
* </ul> * </ul>
* The old <b>file://</b> prefix must still be supported - and functionality * 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; public boolean exists(String contentUrl) throws ContentIOException;
/** /**
* Get the accessor with which to read from the content * Get the accessor with which to read from the content at the given URL.
* at the given URL. The reader is <b>stateful</b> and * The reader is <b>stateful</b> and can <b>only be used once</b>.
* can <b>only be used once</b>.
* *
* @param contentUrl the path to where the content is located * @param contentUrl the path to where the content is located
* @return Returns a read-only content accessor for the given URL. There may * @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. * 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 * @throws ContentIOException
* *
* @see #exists(String) * @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, * <b>only be used once</b>. The location may be specified but must, in that case,
* be a valid and unused URL. * be a valid and unused URL.
* <p> * <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 * By supplying a reader to existing content, the store implementation may
* enable {@link RandomAccessContent random access}. The store implementation * enable {@link RandomAccessContent random access}. The store implementation
* can enable this by copying the existing content into the new location * can enable this by copying the existing content into the new location
* before supplying a writer onto the new content. * before supplying a writer onto the new content.
* *
* @param existingContentReader a reader onto any existing content for which * @param context the context of content.
* a writer is required - may be null * @return Returns a write-only content accessor
* @param newContentUrl an unused, valid URL to use - may be null. * @throws ContentIOException if completely new content storage could not be created
* @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
* *
* @see #getWriter(ContentReader, String)
* @see ContentWriter#addListener(ContentStreamListener) * @see ContentWriter#addListener(ContentStreamListener)
* @see ContentWriter#getContentUrl() * @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; public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl) throws ContentIOException;
/** /**
@@ -140,13 +150,11 @@ public interface ContentStore
* <p> * <p>
* A delete cannot be forced since it is much better to have the * A delete cannot be forced since it is much better to have the
* file remain longer than desired rather than deleted prematurely. * 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 * @param contentUrl the URL of the content to delete
* @return Return true if the content was deleted (either by this or * @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 * @throws ContentIOException
*/ */
public boolean delete(String contentUrl) throws ContentIOException; public boolean delete(String contentUrl) throws ContentIOException;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -77,7 +77,6 @@ public class RoutingContentService implements ContentService
{ {
private static Log logger = LogFactory.getLog(RoutingContentService.class); private static Log logger = LogFactory.getLog(RoutingContentService.class);
private TransactionService transactionService;
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
private NodeService nodeService; private NodeService nodeService;
private AVMService avmService; private AVMService avmService;
@@ -110,9 +109,12 @@ public class RoutingContentService implements ContentService
this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath()); this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath());
} }
/**
* @deprecated Replaced by {@link #setRetryingTransactionHelper(RetryingTransactionHelper)}
*/
public void setTransactionService(TransactionService transactionService) public void setTransactionService(TransactionService transactionService)
{ {
this.transactionService = transactionService; logger.warn("Property 'transactionService' has been replaced by 'retryingTransactionHelper'.");
} }
public void setRetryingTransactionHelper(RetryingTransactionHelper helper) public void setRetryingTransactionHelper(RetryingTransactionHelper helper)
@@ -313,7 +315,7 @@ public class RoutingContentService implements ContentService
} }
String contentUrl = contentData.getContentUrl(); 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); ContentReader reader = store.getReader(contentUrl);
// set extra data on the reader // set extra data on the reader
@@ -338,12 +340,11 @@ public class RoutingContentService implements ContentService
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update) public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
{ {
// TODO: Choose the store to write to at runtime
if (nodeRef == null) if (nodeRef == null)
{ {
ContentContext ctx = new ContentContext(null, null);
// for this case, we just give back a valid URL into the content store // 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 // done
return writer; return writer;
} }
@@ -353,7 +354,8 @@ public class RoutingContentService implements ContentService
// get the content using the (potentially) existing content - the new content // get the content using the (potentially) existing content - the new content
// can be wherever the store decides. // 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. // Special case for AVM repository.
Serializable contentValue = null; Serializable contentValue = null;
@@ -395,7 +397,7 @@ public class RoutingContentService implements ContentService
public ContentWriter getTempWriter() public ContentWriter getTempWriter()
{ {
// there is no existing content and we don't specify the location of the new content // 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);
} }
/** /**

View File

@@ -32,6 +32,7 @@ import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.AbstractContentStore; 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.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
@@ -39,10 +40,11 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; 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> * <p>
* The file names obey, as they must, the URL naming convention * 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 * @author Derek Hulley
*/ */
@@ -241,8 +243,10 @@ public class FileContentStore extends AbstractContentStore
/** /**
* @return Returns a writer onto a location based on the date * @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 try
{ {
File file = null; File file = null;

View File

@@ -35,6 +35,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.AbstractContentStore; import org.alfresco.repo.content.AbstractContentStore;
import org.alfresco.repo.content.ContentContext;
import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.repository.ContentIOException; 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 static Log logger = LogFactory.getLog(ReplicatingContentStore.class);
private TransactionService transactionService;
private RetryingTransactionHelper transactionHelper; private RetryingTransactionHelper transactionHelper;
private ContentStore primaryStore; private ContentStore primaryStore;
private List<ContentStore> secondaryStores; private List<ContentStore> secondaryStores;
@@ -131,17 +131,17 @@ public class ReplicatingContentStore extends AbstractContentStore
} }
/** /**
* Required to ensure that content listeners are executed in a transaction * @deprecated Replaced with {@link #setRetryingTransactionHelper(RetryingTransactionHelper)}
*
* @param transactionService
*/ */
public void setTransactionService(TransactionService transactionService) public void setTransactionService(TransactionService transactionService)
{ {
this.transactionService = transactionService; logger.warn("Property 'transactionService' has been replaced with 'retryingTransactionHelper'.");
} }
/** /**
* Set the retrying transaction helper. * Set the retrying transaction helper.
*
* @since 2.0
*/ */
public void setRetryingTransactionHelper(RetryingTransactionHelper helper) public void setRetryingTransactionHelper(RetryingTransactionHelper helper)
{ {
@@ -273,7 +273,8 @@ public class ReplicatingContentStore extends AbstractContentStore
return primaryContentReader; return primaryContentReader;
} }
// get a writer // get a writer
ContentWriter primaryContentWriter = primaryStore.getWriter(existingContentReader, contentUrl); ContentContext ctx = new ContentContext(existingContentReader, contentUrl);
ContentWriter primaryContentWriter = primaryStore.getWriter(ctx);
// copy it over // copy it over
primaryContentWriter.putContent(existingContentReader); primaryContentWriter.putContent(existingContentReader);
// get a writer to the new content // get a writer to the new content
@@ -287,13 +288,10 @@ public class ReplicatingContentStore extends AbstractContentStore
} }
} }
/** public ContentWriter getWriter(ContentContext ctx)
*
*/
public ContentWriter getWriter(ContentReader existingContentReader, String newContentUrl) throws ContentIOException
{ {
// get the writer // get the writer
ContentWriter writer = primaryStore.getWriter(existingContentReader, newContentUrl); ContentWriter writer = primaryStore.getWriter(ctx);
// attach a replicating listener if outbound replication is on // attach a replicating listener if outbound replication is on
if (outbound) if (outbound)
@@ -434,7 +432,8 @@ public class ReplicatingContentStore extends AbstractContentStore
ContentReader reader = writer.getReader(); ContentReader reader = writer.getReader();
String contentUrl = reader.getContentUrl(); String contentUrl = reader.getContentUrl();
// in order to replicate, we have to specify the URL that we are going to write to // 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 // write it
replicatedWriter.putContent(reader); replicatedWriter.putContent(reader);

View File

@@ -33,9 +33,9 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.alfresco.repo.content.AbstractContentReadWriteTest; import org.alfresco.repo.content.AbstractContentReadWriteTest;
import org.alfresco.repo.content.ContentContext;
import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.content.filestore.FileContentStore; 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.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
@@ -77,7 +77,6 @@ public class ReplicatingContentStoreTest extends AbstractContentReadWriteTest
secondaryStores.add(store); secondaryStores.add(store);
} }
replicatingStore = new ReplicatingContentStore(); replicatingStore = new ReplicatingContentStore();
replicatingStore.setTransactionService(new DummyTransactionService());
replicatingStore.setPrimaryStore(primaryStore); replicatingStore.setPrimaryStore(primaryStore);
replicatingStore.setSecondaryStores(secondaryStores); replicatingStore.setSecondaryStores(secondaryStores);
replicatingStore.setOutbound(false); replicatingStore.setOutbound(false);
@@ -184,7 +183,7 @@ public class ReplicatingContentStoreTest extends AbstractContentReadWriteTest
// pick a secondary store and write some content to it // pick a secondary store and write some content to it
ContentStore secondaryStore = secondaryStores.get(2); ContentStore secondaryStore = secondaryStores.get(2);
ContentWriter writer = secondaryStore.getWriter(null, null); ContentWriter writer = secondaryStore.getWriter(ContentContext.NULL_CONTEXT);
writer.putContent(SOME_CONTENT); writer.putContent(SOME_CONTENT);
String contentUrl = writer.getContentUrl(); String contentUrl = writer.getContentUrl();

View File

@@ -28,6 +28,7 @@ import junit.framework.TestCase;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.AbstractContentStore; import org.alfresco.repo.content.AbstractContentStore;
import org.alfresco.repo.content.ContentContext;
import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.repo.search.Indexer; import org.alfresco.repo.search.Indexer;
@@ -148,7 +149,8 @@ public class MissingContentReindexComponentTest extends TestCase
} }
// now put some content in the store // 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.setMimetype("text/plain");
writer.setEncoding("UTF8"); writer.setEncoding("UTF8");
writer.putContent("123abc456def"); writer.putContent("123abc456def");