();
- editions.add(serverDescriptor.getEdition());
- this.nodeService.setProperty(currentDescriptorNodeRef, ContentModel.PROP_SYS_VERSION_EDITION,
- (Serializable) editions);
+ this.nodeService.setProperty(
+ currentDescriptorNodeRef,
+ ContentModel.PROP_SYS_VERSION_EDITION,
+ new ContentData(null, null, 0L, null));
}
// done
diff --git a/source/java/org/alfresco/repo/domain/ContentDataId.java b/source/java/org/alfresco/repo/domain/ContentDataId.java
new file mode 100644
index 0000000000..98373ef78d
--- /dev/null
+++ b/source/java/org/alfresco/repo/domain/ContentDataId.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005-2008 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.domain;
+
+import java.io.Serializable;
+
+/**
+ * Data type carrying the ID of a ContentData
reference.
+ *
+ * @author Derek Hulley
+ * @since 3.2.1
+ */
+public class ContentDataId implements Serializable
+{
+ private static final long serialVersionUID = -4980820192507809266L;
+
+ private final Long id;
+
+ public ContentDataId(Long id)
+ {
+ super();
+ this.id = id;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "ContentDataId [id=" + id + "]";
+ }
+
+ public Long getId()
+ {
+ return id;
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/NodePropertyValue.java b/source/java/org/alfresco/repo/domain/NodePropertyValue.java
index 4515c16b66..bd8be643f3 100644
--- a/source/java/org/alfresco/repo/domain/NodePropertyValue.java
+++ b/source/java/org/alfresco/repo/domain/NodePropertyValue.java
@@ -127,7 +127,18 @@ public class NodePropertyValue implements Cloneable, Serializable
@Override
Serializable convert(Serializable value)
{
- return DefaultTypeConverter.INSTANCE.convert(Long.class, value);
+ if (value == null)
+ {
+ return null;
+ }
+ else if (value instanceof ContentDataId)
+ {
+ return ((ContentDataId)value).getId();
+ }
+ else
+ {
+ return DefaultTypeConverter.INSTANCE.convert(Long.class, value);
+ }
}
},
FLOAT
@@ -467,6 +478,41 @@ public class NodePropertyValue implements Cloneable, Serializable
{
return DefaultTypeConverter.INSTANCE.convert(Period.class, value);
}
+ },
+ CONTENT_DATA_ID
+ {
+ @Override
+ public Integer getOrdinalNumber()
+ {
+ return Integer.valueOf(21);
+ }
+
+ @Override
+ protected ValueType getPersistedType(Serializable value)
+ {
+ return ValueType.LONG;
+ }
+
+ @Override
+ Serializable convert(Serializable value)
+ {
+ if (value == null)
+ {
+ return null;
+ }
+ else if (value instanceof Long)
+ {
+ return value;
+ }
+ else if (value instanceof ContentDataId)
+ {
+ return ((ContentDataId)value).getId();
+ }
+ else
+ {
+ return DefaultTypeConverter.INSTANCE.convert(ContentData.class, value);
+ }
+ }
}
;
@@ -566,6 +612,10 @@ public class NodePropertyValue implements Cloneable, Serializable
{
return ValueType.PERIOD;
}
+ else if (value instanceof ContentDataId)
+ {
+ return ValueType.CONTENT_DATA_ID;
+ }
else
{
// type is not recognised as belonging to any particular slot
@@ -592,7 +642,7 @@ public class NodePropertyValue implements Cloneable, Serializable
valueTypesByPropertyType.put(DataTypeDefinition.DATE, ValueType.DATE);
valueTypesByPropertyType.put(DataTypeDefinition.DATETIME, ValueType.DATE);
valueTypesByPropertyType.put(DataTypeDefinition.CATEGORY, ValueType.NODEREF);
- valueTypesByPropertyType.put(DataTypeDefinition.CONTENT, ValueType.CONTENT);
+ valueTypesByPropertyType.put(DataTypeDefinition.CONTENT, ValueType.CONTENT_DATA_ID);
valueTypesByPropertyType.put(DataTypeDefinition.TEXT, ValueType.STRING);
valueTypesByPropertyType.put(DataTypeDefinition.MLTEXT, ValueType.MLTEXT);
valueTypesByPropertyType.put(DataTypeDefinition.NODE_REF, ValueType.NODEREF);
diff --git a/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java b/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java
index a6fe734b85..afb20c5f96 100644
--- a/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java
@@ -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 .
- */
-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.
- *
- * This provides basic services such as caching, but defers to the underlying implementation
- * for CRUD operations.
- *
- * The DAO deals in {@link ContentData} instances. The cache is primarily present to decode
- * IDs into ContentData
instances.
- *
- * @author Derek Hulley
- * @since 3.2
- */
-public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
-{
+ * along with Alfresco. If not, see .
+ */
+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.
+ *
+ * This provides basic services such as caching, but defers to the underlying implementation
+ * for CRUD operations.
+ *
+ * The DAO deals in {@link ContentData} instances. The cache is primarily present to decode
+ * IDs into ContentData
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:
* KEY: ID
@@ -85,98 +85,98 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
this.contentDataCache = new EntityLookupCache(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 null)
- */
- 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 null)
+ */
+ public void setContentStoreCleaner(EagerContentStoreCleaner contentStoreCleaner)
+ {
+ this.contentStoreCleaner = contentStoreCleaner;
+ }
+
+ /**
+ * @param contentDataCache the cache of IDs to ContentData and vice versa
+ */
public void setContentDataCache(SimpleCache contentDataCache)
- {
+ {
this.contentDataCache = new EntityLookupCache(
contentDataCache,
CACHE_REGION_CONTENT_DATA,
contentDataCallbackDAO);
- }
-
- /**
- * Register new content for post-rollback handling
- */
- protected void registerNewContentUrl(String contentUrl)
- {
- contentStoreCleaner.registerNewContentUrl(contentUrl);
- }
-
- /**
- * A content_url 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 content_url 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 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 createContentData(ContentData contentData)
- {
+ {
+ Set 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 createContentData(ContentData contentData)
+ {
if (contentData == null)
{
throw new IllegalArgumentException("ContentData values cannot be null");
}
Pair entityPair = contentDataCache.getOrCreateByValue(contentData);
return entityPair;
- }
-
- /**
- * {@inheritDoc}
- */
- public Pair getContentData(Long id)
- {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Pair getContentData(Long id)
+ {
if (id == null)
- {
+ {
throw new IllegalArgumentException("Cannot look up ContentData by null ID.");
- }
+ }
Pair 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 alf_content_data DAO.
*/
private class ContentDataCallbackDAO extends EntityLookupCallbackDAOAdaptor
@@ -254,83 +254,83 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
}
/**
- * Translates this instance into an externally-usable ContentData
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 ContentData
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 content_url_entity.
- */
- 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 content url entity
- * @return Return the entity or null if it doesn't exist
- */
- protected abstract ContentUrlEntity getContentUrlEntity(Long id);
-
- /**
- * @param contentUrl the URL of the content url entity
- * @return Return the entity or null if it doesn't exist
- */
- protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
-
- /**
- * @param contentUrl the URL of the content url entity
- * @return Return the entity or null if it doesn't exist or is still
- * referenced by a content_data 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 content url entity
+ * @return Return the entity or null if it doesn't exist
+ */
+ protected abstract ContentUrlEntity getContentUrlEntity(Long id);
+
+ /**
+ * @param contentUrl the URL of the content url entity
+ * @return Return the entity or null if it doesn't exist
+ */
+ protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
+
+ /**
+ * @param contentUrl the URL of the content url entity
+ * @return Return the entity or null if it doesn't exist or is still
+ * referenced by a content_data 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 alf_content_data
- */
- protected abstract ContentDataEntity createContentDataEntity(
- Long contentUrlId,
- Long mimetypeId,
- Long encodingId,
- Long localeId);
-
- /**
- * @param id the entity ID
- * @return Returns the entity or null if it doesn't exist
- */
- protected abstract ContentDataEntity getContentDataEntity(Long id);
-
- /**
+ */
+ protected abstract int updateContentUrlOrphanTime(Long id, Long orphanTime);
+
+ /**
+ * Create the row for the alf_content_data
+ */
+ protected abstract ContentDataEntity createContentDataEntity(
+ Long contentUrlId,
+ Long mimetypeId,
+ Long encodingId,
+ Long localeId);
+
+ /**
+ * @param id the entity ID
+ * @return Returns the entity or null if it doesn't exist
+ */
+ protected abstract ContentDataEntity getContentDataEntity(Long id);
+
+ /**
* Update an existing alf_content_data 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 content_url entities.
- *
- * @author Derek Hulley
- */
- public class ContentUrlDeleteTransactionListener extends TransactionListenerAdapter
- {
- @Override
- public void beforeCommit(boolean readOnly)
- {
- // Ignore read-only
- if (readOnly)
- {
- return;
- }
- Set 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 content_url entities.
+ *
+ * @author Derek Hulley
+ */
+ public class ContentUrlDeleteTransactionListener extends TransactionListenerAdapter
+ {
+ @Override
+ public void beforeCommit(boolean readOnly)
+ {
+ // Ignore read-only
+ if (readOnly)
+ {
+ return;
+ }
+ Set 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();
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java b/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java
index fb900fed22..4d553b1df6 100644
--- a/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java
+++ b/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
@@ -14,33 +14,34 @@
* 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 .
- */
-package org.alfresco.repo.domain.contentdata;
-
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.domain.contentdata;
+
import java.util.List;
-import java.util.Set;
-
-import org.alfresco.error.AlfrescoRuntimeException;
-import org.alfresco.service.cmr.repository.ContentData;
-import org.springframework.extensions.surf.util.Pair;
-import org.springframework.dao.ConcurrencyFailureException;
-
-/**
- * DAO services for alf_content_data table
- *
- * @author Derek Hulley
- * @since 3.2
- */
-public interface ContentDataDAO
-{
- /**
- * Create a new ContentData instance.
- *
- * @param contentData the ContentData details
- * @return the ContentData pair (id, ContentData) (never null)
- */
- Pair createContentData(ContentData contentData);
+import java.util.Set;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.springframework.extensions.surf.util.Pair;
+import org.springframework.dao.ConcurrencyFailureException;
+import org.springframework.dao.DataIntegrityViolationException;
+
+/**
+ * DAO services for alf_content_data table
+ *
+ * @author Derek Hulley
+ * @since 3.2
+ */
+public interface ContentDataDAO
+{
+ /**
+ * Create a new ContentData instance.
+ *
+ * @param contentData the ContentData details
+ * @return the ContentData pair (id, ContentData) (never null)
+ */
+ Pair createContentData(ContentData contentData);
/**
* Update a content data instance
@@ -49,46 +50,55 @@ public interface ContentDataDAO
* @param contentData the new data
*/
void updateContentData(Long id, ContentData contentData);
-
- /**
- * @param id the unique ID of the entity
- * @return the ContentData pair (id, ContentData) or null if it doesn't exist
- * @throws AlfrescoRuntimeException if the ID provided is invalid
- */
- Pair getContentData(Long id);
-
- /**
- * Delete an instance of content.
- * @param id the unique ID of the entity
- * @throws ConcurrencyFailureException if the ID does not exist
- */
- void deleteContentData(Long id);
-
- /**
- * Deletes all alf_content_data rows that are referenced by the given node
- *
- * @param nodeId the node ID
- * @param qnameIds the content properties to target
- */
- void deleteContentDataForNode(Long nodeId, Set qnameIds);
-
- /**
- * Interface for callbacks during content URL enumeration
- *
- * @author Derek Hulley
- * @since 3.2
- */
- public static interface ContentUrlHandler
- {
+
+ /**
+ * Creates an immediately-orphaned content URL, if possible
+ *
+ * @param contentUrl the URL to create if it doesn't exist
+ * @return Returns the ID-URL pair
+ * @throws DataIntegrityViolationException if the URL already exists
+ */
+ Pair createContentUrlOrphaned(String contentUrl);
+
+ /**
+ * @param id the unique ID of the entity
+ * @return the ContentData pair (id, ContentData) or null if it doesn't exist
+ * @throws AlfrescoRuntimeException if the ID provided is invalid
+ */
+ Pair getContentData(Long id);
+
+ /**
+ * Delete an instance of content.
+ * @param id the unique ID of the entity
+ * @throws ConcurrencyFailureException if the ID does not exist
+ */
+ void deleteContentData(Long id);
+
+ /**
+ * Deletes all alf_content_data rows that are referenced by the given node
+ *
+ * @param nodeId the node ID
+ * @param qnameIds the content properties to target
+ */
+ void deleteContentDataForNode(Long nodeId, Set qnameIds);
+
+ /**
+ * Interface for callbacks during content URL enumeration
+ *
+ * @author Derek Hulley
+ * @since 3.2
+ */
+ public static interface ContentUrlHandler
+ {
void handle(Long id, String contentUrl, Long orphanTime);
- }
-
- /**
+ }
+
+ /**
* Enumerate all available content URLs that were orphaned on or before the given time
- *
+ *
* @param contentUrlHandler the callback object to process the rows
* @param maxOrphanTime the maximum orphan time
- */
+ */
void getContentUrlsOrphaned(ContentUrlHandler contentUrlHandler, long maxOrphanTime);
/**
@@ -104,4 +114,4 @@ public interface ContentDataDAO
* Delete a batch of content URL entities.
*/
int deleteContentUrls(List ids);
-}
+}
diff --git a/source/java/org/alfresco/repo/domain/contentdata/ibatis/ContentDataDAOImpl.java b/source/java/org/alfresco/repo/domain/contentdata/ibatis/ContentDataDAOImpl.java
index 54d1d636e7..123760c9c2 100644
--- a/source/java/org/alfresco/repo/domain/contentdata/ibatis/ContentDataDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/contentdata/ibatis/ContentDataDAOImpl.java
@@ -18,16 +18,21 @@
*/
package org.alfresco.repo.domain.contentdata.ibatis;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.ibatis.IdsEntity;
import org.alfresco.repo.domain.contentdata.AbstractContentDataDAOImpl;
import org.alfresco.repo.domain.contentdata.ContentDataEntity;
import org.alfresco.repo.domain.contentdata.ContentUrlEntity;
import org.alfresco.service.cmr.repository.ContentData;
+import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.extensions.surf.util.Pair;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import com.ibatis.sqlmap.client.event.RowHandler;
@@ -60,6 +65,17 @@ public class ContentDataDAOImpl extends AbstractContentDataDAOImpl
this.template = sqlMapClientTemplate;
}
+ public Pair createContentUrlOrphaned(String contentUrl)
+ {
+ ContentUrlEntity contentUrlEntity = new ContentUrlEntity();
+ contentUrlEntity.setContentUrl(contentUrl);
+ contentUrlEntity.setSize(0L);
+ contentUrlEntity.setOrphanTime(System.currentTimeMillis());
+ Long id = (Long) template.insert(INSERT_CONTENT_URL, contentUrlEntity);
+ // Done
+ return new Pair(id, contentUrl);
+ }
+
@Override
protected ContentUrlEntity createContentUrlEntity(String contentUrl, long size)
{
@@ -135,7 +151,7 @@ public class ContentDataDAOImpl extends AbstractContentDataDAOImpl
}
}
- public int updateContentUrlOrphanTime(Long id, long orphanTime)
+ public int updateContentUrlOrphanTime(Long id, Long orphanTime)
{
ContentUrlEntity contentUrlEntity = new ContentUrlEntity();
contentUrlEntity.setId(id);
@@ -178,7 +194,14 @@ public class ContentDataDAOImpl extends AbstractContentDataDAOImpl
contentDataEntity.setMimetypeId(mimetypeId);
contentDataEntity.setEncodingId(encodingId);
contentDataEntity.setLocaleId(localeId);
- template.insert(INSERT_CONTENT_DATA, contentDataEntity);
+ try
+ {
+ template.insert(INSERT_CONTENT_DATA, contentDataEntity);
+ }
+ catch (Throwable e)
+ {
+ throw new AlfrescoRuntimeException("Failed to insert ContentData: " + contentDataEntity, e);
+ }
// Done
return contentDataEntity;
}
@@ -226,23 +249,30 @@ public class ContentDataDAOImpl extends AbstractContentDataDAOImpl
public void deleteContentDataForNode(Long nodeId, Set qnameIds)
{
- /*
- * TODO: use IN clause in parameters
- */
- for (Long qnameId : qnameIds)
+ if (qnameIds.size() == 0)
{
- // Get the ContentData that matches (may be multiple due to collection properties)
- Map params = new HashMap(11);
- params.put("nodeId", nodeId);
- params.put("qnameId", qnameId);
- @SuppressWarnings("unchecked")
- List ids = (List) template.queryForList(SELECT_CONTENT_DATA_BY_NODE_AND_QNAME, params);
- // Delete each one
- for (Long id : ids)
+ // There will be no results
+ return;
+ }
+ IdsEntity idsEntity = new IdsEntity();
+ idsEntity.setIdOne(nodeId);
+ idsEntity.setIds(new ArrayList(qnameIds));
+ @SuppressWarnings("unchecked")
+ List ids = (List) template.queryForList(SELECT_CONTENT_DATA_BY_NODE_AND_QNAME, idsEntity);
+ // Delete each one
+ for (Long id : ids)
+ {
+ try
{
// Delete the ContentData entity
deleteContentData(id);
}
+ catch (ConcurrencyFailureException e)
+ {
+ // The DB may return results even though the row has just been
+ // deleted. Since we are deleting the row, it doesn't matter
+ // if it is deleted here or not.
+ }
}
}
}
diff --git a/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java b/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java
index 8c9343066f..1c6c0d89de 100644
--- a/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/patch/AbstractPatchDAOImpl.java
@@ -19,8 +19,12 @@
package org.alfresco.repo.domain.patch;
import java.util.List;
+import java.util.Map;
+import org.alfresco.ibatis.BatchingDAO;
import org.alfresco.repo.domain.avm.AVMNodeEntity;
+import org.alfresco.repo.domain.contentdata.ContentDataDAO;
+import org.alfresco.service.cmr.repository.ContentData;
/**
@@ -31,8 +35,25 @@ import org.alfresco.repo.domain.avm.AVMNodeEntity;
* @author janv
* @since 3.2
*/
-public abstract class AbstractPatchDAOImpl implements PatchDAO
+public abstract class AbstractPatchDAOImpl implements PatchDAO, BatchingDAO
{
+ private ContentDataDAO contentDataDAO;
+
+ protected AbstractPatchDAOImpl()
+ {
+ }
+
+ /**
+ * Set the DAO that supplies {@link ContentData} IDs
+ */
+ public void setContentDataDAO(ContentDataDAO contentDataDAO)
+ {
+ this.contentDataDAO = contentDataDAO;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public Long getAVMNodesCountWhereNewInStore()
{
return getAVMNodeEntitiesCountWhereNewInStore();
@@ -63,4 +84,91 @@ public abstract class AbstractPatchDAOImpl implements PatchDAO
protected abstract List getNullVersionLayeredDirectoryNodeEntities();
protected abstract List getNullVersionLayeredFileNodeEntities();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see #getAdmOldContentProperties(Long, Long)
+ */
+ public void updateAdmV31ContentProperties(Long minNodeId, Long maxNodeId)
+ {
+ List