mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged BRANCHES/V3.2 to HEAD:
18363: WCM clustering - ETHREEOH-3962 (duplicate root node entry) 19091: Fix Part 1 ALF-726: v3.1.x Content Cleaner Job needs to be ported to v3.2 19159: Fixed ALF-726: Migrate pre-3.2 content URLs to new format and pick up tag existing orphaned content 19169: Fix fallout from 19159 for ALF-726: Migrate pre-3.2 content URLs to new format and pick up tag existing orphaned content 19262: ALF-726 Multithreading for content URL conversion git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19267 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* Copyright (C) 2005-2010 Alfresco Software Limited.
|
||||
*
|
||||
* This file is part of Alfresco
|
||||
@@ -14,60 +14,60 @@
|
||||
* 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.domain.contentdata;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||
import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner;
|
||||
import org.alfresco.repo.domain.LocaleDAO;
|
||||
import org.alfresco.repo.domain.encoding.EncodingDAO;
|
||||
import org.alfresco.repo.domain.mimetype.MimetypeDAO;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.extensions.surf.util.Pair;
|
||||
|
||||
/**
|
||||
* Abstract implementation for ContentData DAO.
|
||||
* <p>
|
||||
* This provides basic services such as caching, but defers to the underlying implementation
|
||||
* for CRUD operations.
|
||||
* <p>
|
||||
* The DAO deals in {@link ContentData} instances. The cache is primarily present to decode
|
||||
* IDs into <code>ContentData</code> instances.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
{
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.alfresco.repo.domain.contentdata;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||
import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner;
|
||||
import org.alfresco.repo.domain.LocaleDAO;
|
||||
import org.alfresco.repo.domain.encoding.EncodingDAO;
|
||||
import org.alfresco.repo.domain.mimetype.MimetypeDAO;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.extensions.surf.util.Pair;
|
||||
|
||||
/**
|
||||
* Abstract implementation for ContentData DAO.
|
||||
* <p>
|
||||
* This provides basic services such as caching, but defers to the underlying implementation
|
||||
* for CRUD operations.
|
||||
* <p>
|
||||
* The DAO deals in {@link ContentData} instances. The cache is primarily present to decode
|
||||
* IDs into <code>ContentData</code> instances.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
{
|
||||
private static final String CACHE_REGION_CONTENT_DATA = "ContentData";
|
||||
/**
|
||||
* Content URL IDs to delete before final commit.
|
||||
*/
|
||||
private static final String KEY_PRE_COMMIT_CONTENT_URL_DELETIONS = "AbstractContentDataDAOImpl.PreCommitContentUrlDeletions";
|
||||
|
||||
private static Log logger = LogFactory.getLog(AbstractContentDataDAOImpl.class);
|
||||
|
||||
/**
|
||||
* Content URL IDs to delete before final commit.
|
||||
*/
|
||||
private static final String KEY_PRE_COMMIT_CONTENT_URL_DELETIONS = "AbstractContentDataDAOImpl.PreCommitContentUrlDeletions";
|
||||
|
||||
private static Log logger = LogFactory.getLog(AbstractContentDataDAOImpl.class);
|
||||
|
||||
private final ContentDataCallbackDAO contentDataCallbackDAO;
|
||||
private MimetypeDAO mimetypeDAO;
|
||||
private EncodingDAO encodingDAO;
|
||||
private LocaleDAO localeDAO;
|
||||
private EagerContentStoreCleaner contentStoreCleaner;
|
||||
|
||||
private MimetypeDAO mimetypeDAO;
|
||||
private EncodingDAO encodingDAO;
|
||||
private LocaleDAO localeDAO;
|
||||
private EagerContentStoreCleaner contentStoreCleaner;
|
||||
|
||||
/**
|
||||
* Cache for the ContentData class:<br/>
|
||||
* KEY: ID<br/>
|
||||
@@ -85,98 +85,98 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
this.contentDataCache = new EntityLookupCache<Long, ContentData, Serializable>(contentDataCallbackDAO);
|
||||
}
|
||||
|
||||
public void setMimetypeDAO(MimetypeDAO mimetypeDAO)
|
||||
{
|
||||
this.mimetypeDAO = mimetypeDAO;
|
||||
}
|
||||
|
||||
public void setEncodingDAO(EncodingDAO encodingDAO)
|
||||
{
|
||||
this.encodingDAO = encodingDAO;
|
||||
}
|
||||
|
||||
public void setLocaleDAO(LocaleDAO localeDAO)
|
||||
{
|
||||
this.localeDAO = localeDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this property to enable eager cleanup of orphaned content.
|
||||
*
|
||||
* @param contentStoreCleaner an eager cleaner (may be <tt>null</tt>)
|
||||
*/
|
||||
public void setContentStoreCleaner(EagerContentStoreCleaner contentStoreCleaner)
|
||||
{
|
||||
this.contentStoreCleaner = contentStoreCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentDataCache the cache of IDs to ContentData and vice versa
|
||||
*/
|
||||
public void setMimetypeDAO(MimetypeDAO mimetypeDAO)
|
||||
{
|
||||
this.mimetypeDAO = mimetypeDAO;
|
||||
}
|
||||
|
||||
public void setEncodingDAO(EncodingDAO encodingDAO)
|
||||
{
|
||||
this.encodingDAO = encodingDAO;
|
||||
}
|
||||
|
||||
public void setLocaleDAO(LocaleDAO localeDAO)
|
||||
{
|
||||
this.localeDAO = localeDAO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this property to enable eager cleanup of orphaned content.
|
||||
*
|
||||
* @param contentStoreCleaner an eager cleaner (may be <tt>null</tt>)
|
||||
*/
|
||||
public void setContentStoreCleaner(EagerContentStoreCleaner contentStoreCleaner)
|
||||
{
|
||||
this.contentStoreCleaner = contentStoreCleaner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentDataCache the cache of IDs to ContentData and vice versa
|
||||
*/
|
||||
public void setContentDataCache(SimpleCache<Long, ContentData> contentDataCache)
|
||||
{
|
||||
{
|
||||
this.contentDataCache = new EntityLookupCache<Long, ContentData, Serializable>(
|
||||
contentDataCache,
|
||||
CACHE_REGION_CONTENT_DATA,
|
||||
contentDataCallbackDAO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new content for post-rollback handling
|
||||
*/
|
||||
protected void registerNewContentUrl(String contentUrl)
|
||||
{
|
||||
contentStoreCleaner.registerNewContentUrl(contentUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* A <b>content_url</b> entity was dereferenced. This makes no assumptions about the
|
||||
* current references - dereference deletion is handled in the commit phase.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new content for post-rollback handling
|
||||
*/
|
||||
protected void registerNewContentUrl(String contentUrl)
|
||||
{
|
||||
contentStoreCleaner.registerNewContentUrl(contentUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* A <b>content_url</b> entity was dereferenced. This makes no assumptions about the
|
||||
* current references - dereference deletion is handled in the commit phase.
|
||||
*/
|
||||
protected void registerDereferencedContentUrl(String contentUrl)
|
||||
{
|
||||
Set<String> contentUrls = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_CONTENT_URL_DELETIONS);
|
||||
if (contentUrls.size() == 0)
|
||||
{
|
||||
ContentUrlDeleteTransactionListener listener = new ContentUrlDeleteTransactionListener();
|
||||
AlfrescoTransactionSupport.bindListener(listener);
|
||||
}
|
||||
contentUrls.add(contentUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Pair<Long, ContentData> createContentData(ContentData contentData)
|
||||
{
|
||||
{
|
||||
Set<String> contentUrls = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_CONTENT_URL_DELETIONS);
|
||||
if (contentUrls.size() == 0)
|
||||
{
|
||||
ContentUrlDeleteTransactionListener listener = new ContentUrlDeleteTransactionListener();
|
||||
AlfrescoTransactionSupport.bindListener(listener);
|
||||
}
|
||||
contentUrls.add(contentUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Pair<Long, ContentData> createContentData(ContentData contentData)
|
||||
{
|
||||
if (contentData == null)
|
||||
{
|
||||
throw new IllegalArgumentException("ContentData values cannot be null");
|
||||
}
|
||||
Pair<Long, ContentData> entityPair = contentDataCache.getOrCreateByValue(contentData);
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Pair<Long, ContentData> getContentData(Long id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Pair<Long, ContentData> getContentData(Long id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot look up ContentData by null ID.");
|
||||
}
|
||||
}
|
||||
Pair<Long, ContentData> entityPair = contentDataCache.getByKey(id);
|
||||
if (entityPair == null)
|
||||
{
|
||||
throw new DataIntegrityViolationException("No ContentData value exists for ID " + id);
|
||||
}
|
||||
return entityPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void updateContentData(Long id, ContentData contentData)
|
||||
{
|
||||
if (id == null)
|
||||
@@ -197,21 +197,21 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void deleteContentData(Long id)
|
||||
{
|
||||
public void deleteContentData(Long id)
|
||||
{
|
||||
if (id == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot delete ContentData by null ID.");
|
||||
}
|
||||
int deleted = contentDataCache.deleteByKey(id);
|
||||
if (deleted < 1)
|
||||
{
|
||||
if (deleted < 1)
|
||||
{
|
||||
throw new ConcurrencyFailureException("ContentData with ID " + id + " no longer exists");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for <b>alf_content_data</b> DAO.
|
||||
*/
|
||||
private class ContentDataCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, ContentData, Serializable>
|
||||
@@ -254,83 +254,83 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates this instance into an externally-usable <code>ContentData</code> instance.
|
||||
*/
|
||||
private ContentData makeContentData(ContentDataEntity contentDataEntity)
|
||||
{
|
||||
// Decode content URL
|
||||
String contentUrl = contentDataEntity.getContentUrl();
|
||||
long size = contentDataEntity.getSize() == null ? 0L : contentDataEntity.getSize().longValue();
|
||||
// Decode mimetype
|
||||
Long mimetypeId = contentDataEntity.getMimetypeId();
|
||||
String mimetype = null;
|
||||
if (mimetypeId != null)
|
||||
{
|
||||
mimetype = mimetypeDAO.getMimetype(mimetypeId).getSecond();
|
||||
}
|
||||
// Decode encoding
|
||||
Long encodingId = contentDataEntity.getEncodingId();
|
||||
String encoding = null;
|
||||
if (encodingId != null)
|
||||
{
|
||||
encoding = encodingDAO.getEncoding(encodingId).getSecond();
|
||||
}
|
||||
// Decode locale
|
||||
Long localeId = contentDataEntity.getLocaleId();
|
||||
Locale locale = null;
|
||||
if (localeId != null)
|
||||
{
|
||||
locale = localeDAO.getLocalePair(localeId).getSecond();
|
||||
}
|
||||
// Build the ContentData
|
||||
ContentData contentData = new ContentData(contentUrl, mimetype, size, encoding, locale);
|
||||
// Done
|
||||
return contentData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the {@link ContentData} into persistable values using the helper DAOs
|
||||
*/
|
||||
private ContentDataEntity createContentDataEntity(ContentData contentData)
|
||||
{
|
||||
// Resolve the content URL
|
||||
Long contentUrlId = null;
|
||||
String contentUrl = contentData.getContentUrl();
|
||||
long size = contentData.getSize();
|
||||
if (contentUrl != null)
|
||||
{
|
||||
// We must find or create the ContentUrlEntity
|
||||
contentUrlId = getOrCreateContentUrlEntity(contentUrl, size).getId();
|
||||
}
|
||||
// Resolve the mimetype
|
||||
Long mimetypeId = null;
|
||||
String mimetype = contentData.getMimetype();
|
||||
if (mimetype != null)
|
||||
{
|
||||
mimetypeId = mimetypeDAO.getOrCreateMimetype(mimetype).getFirst();
|
||||
}
|
||||
// Resolve the encoding
|
||||
Long encodingId = null;
|
||||
String encoding = contentData.getEncoding();
|
||||
if (encoding != null)
|
||||
{
|
||||
encodingId = encodingDAO.getOrCreateEncoding(encoding).getFirst();
|
||||
}
|
||||
// Resolve the locale
|
||||
Long localeId = null;
|
||||
Locale locale = contentData.getLocale();
|
||||
if (locale != null)
|
||||
{
|
||||
localeId = localeDAO.getOrCreateLocalePair(locale).getFirst();
|
||||
}
|
||||
|
||||
// Create ContentDataEntity
|
||||
ContentDataEntity contentDataEntity = createContentDataEntity(contentUrlId, mimetypeId, encodingId, localeId);
|
||||
// Done
|
||||
return contentDataEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates this instance into an externally-usable <code>ContentData</code> instance.
|
||||
*/
|
||||
private ContentData makeContentData(ContentDataEntity contentDataEntity)
|
||||
{
|
||||
// Decode content URL
|
||||
String contentUrl = contentDataEntity.getContentUrl();
|
||||
long size = contentDataEntity.getSize() == null ? 0L : contentDataEntity.getSize().longValue();
|
||||
// Decode mimetype
|
||||
Long mimetypeId = contentDataEntity.getMimetypeId();
|
||||
String mimetype = null;
|
||||
if (mimetypeId != null)
|
||||
{
|
||||
mimetype = mimetypeDAO.getMimetype(mimetypeId).getSecond();
|
||||
}
|
||||
// Decode encoding
|
||||
Long encodingId = contentDataEntity.getEncodingId();
|
||||
String encoding = null;
|
||||
if (encodingId != null)
|
||||
{
|
||||
encoding = encodingDAO.getEncoding(encodingId).getSecond();
|
||||
}
|
||||
// Decode locale
|
||||
Long localeId = contentDataEntity.getLocaleId();
|
||||
Locale locale = null;
|
||||
if (localeId != null)
|
||||
{
|
||||
locale = localeDAO.getLocalePair(localeId).getSecond();
|
||||
}
|
||||
// Build the ContentData
|
||||
ContentData contentData = new ContentData(contentUrl, mimetype, size, encoding, locale);
|
||||
// Done
|
||||
return contentData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the {@link ContentData} into persistable values using the helper DAOs
|
||||
*/
|
||||
private ContentDataEntity createContentDataEntity(ContentData contentData)
|
||||
{
|
||||
// Resolve the content URL
|
||||
Long contentUrlId = null;
|
||||
String contentUrl = contentData.getContentUrl();
|
||||
long size = contentData.getSize();
|
||||
if (contentUrl != null)
|
||||
{
|
||||
// We must find or create the ContentUrlEntity
|
||||
contentUrlId = getOrCreateContentUrlEntity(contentUrl, size).getId();
|
||||
}
|
||||
// Resolve the mimetype
|
||||
Long mimetypeId = null;
|
||||
String mimetype = contentData.getMimetype();
|
||||
if (mimetype != null)
|
||||
{
|
||||
mimetypeId = mimetypeDAO.getOrCreateMimetype(mimetype).getFirst();
|
||||
}
|
||||
// Resolve the encoding
|
||||
Long encodingId = null;
|
||||
String encoding = contentData.getEncoding();
|
||||
if (encoding != null)
|
||||
{
|
||||
encodingId = encodingDAO.getOrCreateEncoding(encoding).getFirst();
|
||||
}
|
||||
// Resolve the locale
|
||||
Long localeId = null;
|
||||
Locale locale = contentData.getLocale();
|
||||
if (locale != null)
|
||||
{
|
||||
localeId = localeDAO.getOrCreateLocalePair(locale).getFirst();
|
||||
}
|
||||
|
||||
// Create ContentDataEntity
|
||||
ContentDataEntity contentDataEntity = createContentDataEntity(contentUrlId, mimetypeId, encodingId, localeId);
|
||||
// Done
|
||||
return contentDataEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the {@link ContentData} into persistable values using the helper DAOs
|
||||
*/
|
||||
private int updateContentDataEntity(ContentDataEntity contentDataEntity, ContentData contentData)
|
||||
@@ -387,83 +387,90 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
}
|
||||
|
||||
/**
|
||||
* Caching method that creates an entity for <b>content_url_entity</b>.
|
||||
*/
|
||||
private ContentUrlEntity getOrCreateContentUrlEntity(String contentUrl, long size)
|
||||
{
|
||||
// Create the content URL entity
|
||||
ContentUrlEntity contentUrlEntity = getContentUrlEntity(contentUrl);
|
||||
// If it exists, then we can just re-use it, but check that the size is consistent
|
||||
if (contentUrlEntity != null)
|
||||
{
|
||||
// Reuse it
|
||||
long existingSize = contentUrlEntity.getSize();
|
||||
if (size != existingSize)
|
||||
{
|
||||
logger.warn(
|
||||
"Re-using Content URL, but size is mismatched: \n" +
|
||||
" Inbound: " + contentUrl + "\n" +
|
||||
" Existing: " + contentUrlEntity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create it
|
||||
contentUrlEntity = createContentUrlEntity(contentUrl, size);
|
||||
}
|
||||
// Done
|
||||
return contentUrlEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentUrl the content URL to create or search for
|
||||
*/
|
||||
protected abstract ContentUrlEntity createContentUrlEntity(String contentUrl, long size);
|
||||
|
||||
/**
|
||||
* @param id the ID of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntity(Long id);
|
||||
|
||||
/**
|
||||
* @param contentUrl the URL of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
|
||||
|
||||
/**
|
||||
* @param contentUrl the URL of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist or is still
|
||||
* referenced by a <b>content_data</b> entity
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntityUnreferenced(String contentUrl);
|
||||
|
||||
/**
|
||||
* Method to create (or get an existing) content URL. The URL will be unorphaned
|
||||
* whether it has been created or is being re-used.
|
||||
*/
|
||||
private ContentUrlEntity getOrCreateContentUrlEntity(String contentUrl, long size)
|
||||
{
|
||||
// Create the content URL entity
|
||||
ContentUrlEntity contentUrlEntity = getContentUrlEntity(contentUrl);
|
||||
// If it exists, then we can just re-use it, but check that the size is consistent
|
||||
if (contentUrlEntity != null)
|
||||
{
|
||||
// Reuse it
|
||||
long existingSize = contentUrlEntity.getSize();
|
||||
if (size != existingSize)
|
||||
{
|
||||
logger.warn(
|
||||
"Re-using Content URL, but size is mismatched: \n" +
|
||||
" Inbound: " + contentUrl + "\n" +
|
||||
" Existing: " + contentUrlEntity);
|
||||
}
|
||||
// Check orphan state
|
||||
if (contentUrlEntity.getOrphanTime() != null)
|
||||
{
|
||||
Long id = contentUrlEntity.getId();
|
||||
updateContentUrlOrphanTime(id, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create it
|
||||
contentUrlEntity = createContentUrlEntity(contentUrl, size);
|
||||
}
|
||||
// Done
|
||||
return contentUrlEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentUrl the content URL to create or search for
|
||||
*/
|
||||
protected abstract ContentUrlEntity createContentUrlEntity(String contentUrl, long size);
|
||||
|
||||
/**
|
||||
* @param id the ID of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntity(Long id);
|
||||
|
||||
/**
|
||||
* @param contentUrl the URL of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
|
||||
|
||||
/**
|
||||
* @param contentUrl the URL of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist or is still
|
||||
* referenced by a <b>content_data</b> entity
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntityUnreferenced(String contentUrl);
|
||||
|
||||
/**
|
||||
* Update a content URL with the given orphan time
|
||||
*
|
||||
* @param id the unique ID of the entity
|
||||
* @param orphanTime the time (ms since epoch) that the entity was orphaned
|
||||
* @return Returns the number of rows updated
|
||||
*/
|
||||
protected abstract int updateContentUrlOrphanTime(Long id, long orphanTime);
|
||||
|
||||
/**
|
||||
* Create the row for the <b>alf_content_data<b>
|
||||
*/
|
||||
protected abstract ContentDataEntity createContentDataEntity(
|
||||
Long contentUrlId,
|
||||
Long mimetypeId,
|
||||
Long encodingId,
|
||||
Long localeId);
|
||||
|
||||
/**
|
||||
* @param id the entity ID
|
||||
* @return Returns the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentDataEntity getContentDataEntity(Long id);
|
||||
|
||||
/**
|
||||
*/
|
||||
protected abstract int updateContentUrlOrphanTime(Long id, Long orphanTime);
|
||||
|
||||
/**
|
||||
* Create the row for the <b>alf_content_data<b>
|
||||
*/
|
||||
protected abstract ContentDataEntity createContentDataEntity(
|
||||
Long contentUrlId,
|
||||
Long mimetypeId,
|
||||
Long encodingId,
|
||||
Long localeId);
|
||||
|
||||
/**
|
||||
* @param id the entity ID
|
||||
* @return Returns the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentDataEntity getContentDataEntity(Long id);
|
||||
|
||||
/**
|
||||
* Update an existing <b>alf_content_data</b> entity
|
||||
*
|
||||
* @param entity the existing entity that will be updated
|
||||
@@ -472,44 +479,44 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
protected abstract int updateContentDataEntity(ContentDataEntity entity);
|
||||
|
||||
/**
|
||||
* Delete the entity with the given ID
|
||||
*
|
||||
* @return Returns the number of rows deleted
|
||||
*/
|
||||
protected abstract int deleteContentDataEntity(Long id);
|
||||
|
||||
/**
|
||||
* Transactional listener that deletes unreferenced <b>content_url</b> entities.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class ContentUrlDeleteTransactionListener extends TransactionListenerAdapter
|
||||
{
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly)
|
||||
{
|
||||
// Ignore read-only
|
||||
if (readOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Set<String> contentUrls = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_CONTENT_URL_DELETIONS);
|
||||
* Delete the entity with the given ID
|
||||
*
|
||||
* @return Returns the number of rows deleted
|
||||
*/
|
||||
protected abstract int deleteContentDataEntity(Long id);
|
||||
|
||||
/**
|
||||
* Transactional listener that deletes unreferenced <b>content_url</b> entities.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class ContentUrlDeleteTransactionListener extends TransactionListenerAdapter
|
||||
{
|
||||
@Override
|
||||
public void beforeCommit(boolean readOnly)
|
||||
{
|
||||
// Ignore read-only
|
||||
if (readOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Set<String> contentUrls = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_CONTENT_URL_DELETIONS);
|
||||
long orphanTime = System.currentTimeMillis();
|
||||
for (String contentUrl : contentUrls)
|
||||
{
|
||||
ContentUrlEntity contentUrlEntity = getContentUrlEntityUnreferenced(contentUrl);
|
||||
if (contentUrlEntity == null)
|
||||
{
|
||||
// It is still referenced, so ignore it
|
||||
continue;
|
||||
}
|
||||
for (String contentUrl : contentUrls)
|
||||
{
|
||||
ContentUrlEntity contentUrlEntity = getContentUrlEntityUnreferenced(contentUrl);
|
||||
if (contentUrlEntity == null)
|
||||
{
|
||||
// It is still referenced, so ignore it
|
||||
continue;
|
||||
}
|
||||
// We mark the URL as orphaned.
|
||||
Long contentUrlId = contentUrlEntity.getId();
|
||||
Long contentUrlId = contentUrlEntity.getId();
|
||||
updateContentUrlOrphanTime(contentUrlId, orphanTime);
|
||||
// Pop this in the queue for deletion from the content store
|
||||
contentStoreCleaner.registerOrphanedContentUrl(contentUrl);
|
||||
}
|
||||
contentUrls.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pop this in the queue for deletion from the content store
|
||||
contentStoreCleaner.registerOrphanedContentUrl(contentUrl);
|
||||
}
|
||||
contentUrls.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user