diff --git a/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java b/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java index 099f720baa..08dd3564aa 100644 --- a/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/contentdata/AbstractContentDataDAOImpl.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.domain.contentdata; import java.io.Serializable; @@ -197,8 +197,9 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO /** * Internally update a URL or create a new one if it does not exist */ - private void updateContentUrl(ContentUrlEntity contentUrl) + private boolean updateContentUrl(ContentUrlEntity contentUrl) { + int result = 0; if (contentUrl == null) { throw new IllegalArgumentException("Cannot look up ContentData by null ID."); @@ -206,13 +207,14 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO Pair pair = contentUrlCache.getByValue(contentUrl); if(pair != null) { - contentUrlCache.updateValue(pair.getFirst(), contentUrl); + result = contentUrlCache.updateValue(pair.getFirst(), contentUrl); } else { pair = contentUrlCache.getOrCreateByValue(contentUrl); - contentUrlCache.updateValue(pair.getFirst(), contentUrl); + result = contentUrlCache.updateValue(pair.getFirst(), contentUrl); } + return result == 1 ? true : false; } @Override @@ -558,24 +560,13 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO @Override public boolean updateContentUrlKey(String contentUrl, ContentUrlKeyEntity contentUrlKey) { - boolean success = true; - ContentUrlEntity existing = getContentUrl(contentUrl); - if(existing != null) + if (existing == null) { - ContentUrlEntity entity = ContentUrlEntity.setContentUrlKey(existing, contentUrlKey); - updateContentUrl(entity); + existing = getOrCreateContentUrl(contentUrl, contentUrlKey.getUnencryptedFileSize()); } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("No content url, not updating symmetric key"); - } - success = false; - } - - return success; + ContentUrlEntity entity = ContentUrlEntity.setContentUrlKey(existing, contentUrlKey); + return updateContentUrl(entity); } @Override @@ -613,6 +604,19 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO return contentUrlEntity; } + @Override + public ContentUrlEntity getOrCreateContentUrl(String contentUrl, long size) + { + ContentUrlEntity contentUrlEntity = new ContentUrlEntity(); + contentUrlEntity.setContentUrl(contentUrl); + contentUrlEntity.setSize(size); + Pair pair = contentUrlCache.getOrCreateByValue(contentUrlEntity); + Long newContentUrlId = pair.getFirst(); + contentUrlEntity.setId(newContentUrlId); + // Done + return contentUrlEntity; + } + /** * @param contentUrl the content URL to create or search for */ diff --git a/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java b/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java index 4380d75b73..4f24d61118 100644 --- a/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java +++ b/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAO.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.domain.contentdata; import java.util.Date; @@ -167,6 +167,13 @@ public interface ContentDataDAO */ ContentUrlEntity getOrCreateContentUrl(String contentUrl); + /** + * Get a content URL or create one if it does not exist + * + * @since 5.1 + */ + ContentUrlEntity getOrCreateContentUrl(String contentUrl, long size); + /** * Updates the content key for the given content url * diff --git a/source/test-java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java b/source/test-java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java index 0adba818b0..2aa5a8a482 100644 --- a/source/test-java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java +++ b/source/test-java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java @@ -35,7 +35,7 @@ import org.alfresco.repo.content.ContentContext; import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentStore; -import org.alfresco.repo.domain.contentdata.ContentDataDAO.ContentUrlHandler; +import org.alfresco.repo.domain.contentdata.ContentDataDAO.ContentUrlHandler; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; @@ -48,7 +48,7 @@ import org.alfresco.util.Pair; import org.alfresco.util.TempFileProvider; import org.junit.experimental.categories.Category; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DataIntegrityViolationException; /** * @see ContentDataDAO @@ -90,32 +90,32 @@ public class ContentDataDAOTest extends TestCase return txnHelper.doInTransaction(callback, false, false); } - private Pair update(final Long id, final ContentData contentData) - { - RetryingTransactionCallback> callback = new RetryingTransactionCallback>() - { - public Pair execute() throws Throwable - { - contentDataDAO.updateContentData(id, contentData); - return new Pair(id, contentData); - } - }; - return txnHelper.doInTransaction(callback, false, false); - } - - private void delete(final Long id) - { - RetryingTransactionCallback callback = new RetryingTransactionCallback() - { - public Void execute() throws Throwable - { - contentDataDAO.deleteContentData(id); - return null; - } - }; - txnHelper.doInTransaction(callback, false, false); - } - + private Pair update(final Long id, final ContentData contentData) + { + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() + { + public Pair execute() throws Throwable + { + contentDataDAO.updateContentData(id, contentData); + return new Pair(id, contentData); + } + }; + return txnHelper.doInTransaction(callback, false, false); + } + + private void delete(final Long id) + { + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + contentDataDAO.deleteContentData(id); + return null; + } + }; + txnHelper.doInTransaction(callback, false, false); + } + /** * Retrieves and checks the ContentData for equality */ @@ -149,15 +149,15 @@ public class ContentDataDAOTest extends TestCase public void testGetWithInvalidId() { - try - { - contentDataDAO.getContentData(-1L); - fail("Invalid ContentData IDs must generate DataIntegrityViolationException."); - } - catch (DataIntegrityViolationException e) - { - // Expected - } + try + { + contentDataDAO.getContentData(-1L); + fail("Invalid ContentData IDs must generate DataIntegrityViolationException."); + } + catch (DataIntegrityViolationException e) + { + // Expected + } } /** @@ -203,27 +203,27 @@ public class ContentDataDAOTest extends TestCase getAndCheck(resultPairLower.getFirst(), contentDataLower); } - public void testUpdate() throws Exception - { - ContentData contentData = getContentData(); - Pair resultPair = create(contentData); - Long id = resultPair.getFirst(); - // Update - contentData = ContentData.setMimetype(contentData, MimetypeMap.MIMETYPE_HTML); - contentData = ContentData.setEncoding(contentData, "UTF-16"); - // Don't update the content itself - update(id, contentData); - // Check - getAndCheck(id, contentData); - } - + public void testUpdate() throws Exception + { + ContentData contentData = getContentData(); + Pair resultPair = create(contentData); + Long id = resultPair.getFirst(); + // Update + contentData = ContentData.setMimetype(contentData, MimetypeMap.MIMETYPE_HTML); + contentData = ContentData.setEncoding(contentData, "UTF-16"); + // Don't update the content itself + update(id, contentData); + // Check + getAndCheck(id, contentData); + } + public void testDelete() throws Exception { ContentData contentData = getContentData(); Pair resultPair = create(contentData); getAndCheck(resultPair.getFirst(), contentData); - delete(resultPair.getFirst()); + delete(resultPair.getFirst()); try { getAndCheck(resultPair.getFirst(), contentData); @@ -245,81 +245,91 @@ public class ContentDataDAOTest extends TestCase contentUrlEntity = contentDataDAO.getContentUrl(contentUrlEntity.getContentUrl()); assertNotNull(contentUrlEntity); assertNotNull(contentDataDAO.getContentUrl(contentUrlEntity.getId())); + + // test with size + long size = 100l; + String url = "store://" + GUID.generate(); + contentUrlEntity = contentDataDAO.getOrCreateContentUrl(url, size); + contentUrlEntity = contentDataDAO.getContentUrl(contentUrlEntity.getContentUrl()); + assertNotNull(contentUrlEntity); + assertNotNull(contentDataDAO.getContentUrl(contentUrlEntity.getId())); + assertEquals("The size does not match.", size, contentUrlEntity.getSize()); + assertEquals("The content URL does not match.", url, contentUrlEntity.getContentUrl()); + } + + /** + * Check that orphaned content can be re-instated. + */ + public void testReinstate_ALF3867() + { + ContentData contentData = getContentData(); + Pair resultPair = create(contentData); + getAndCheck(resultPair.getFirst(), contentData); + delete(resultPair.getFirst()); + // Now create a ContentData with the same URL + create(contentData); + } + + public void testContentUrl_FetchingOrphansNoLimit() throws Exception + { + ContentData contentData = getContentData(); + Pair resultPair = create(contentData); + getAndCheck(resultPair.getFirst(), contentData); + delete(resultPair.getFirst()); + // The content URL is orphaned + final String contentUrlOrphaned = contentData.getContentUrl(); + final boolean[] found = new boolean[] {false}; + + // Iterate over all orphaned content URLs and ensure that we hit the one we just orphaned + ContentUrlHandler handler = new ContentUrlHandler() + { + public void handle(Long id, String contentUrl, Long orphanTime) + { + // Check + if (id == null || contentUrl == null || orphanTime == null) + { + fail("Invalid orphan data returned to handler: " + id + "-" + contentUrl + "-" + orphanTime); + } + // Did we get the one we wanted? + if (contentUrl.equals(contentUrlOrphaned)) + { + found[0] = true; + } + } + }; + contentDataDAO.getContentUrlsOrphaned(handler, Long.MAX_VALUE, Integer.MAX_VALUE); + assertTrue("Newly-orphaned content URL not found", found[0]); + } + + public void testContentUrl_FetchingOrphansWithLimit() throws Exception + { + // Orphan some content + for (int i = 0; i < 5; i++) + { + ContentData contentData = getContentData(); + Pair resultPair = create(contentData); + getAndCheck(resultPair.getFirst(), contentData); + delete(resultPair.getFirst()); + } + final int[] count = new int[] {0}; + + // Iterate over all orphaned content URLs and ensure that we hit the one we just orphaned + ContentUrlHandler handler = new ContentUrlHandler() + { + public void handle(Long id, String contentUrl, Long orphanTime) + { + // Check + if (id == null || contentUrl == null || orphanTime == null) + { + fail("Invalid orphan data returned to handler: " + id + "-" + contentUrl + "-" + orphanTime); + } + count[0]++; + } + }; + contentDataDAO.getContentUrlsOrphaned(handler, Long.MAX_VALUE, 5); + assertEquals("Expected exactly 5 results callbacks", 5, count[0]); } - /** - * Check that orphaned content can be re-instated. - */ - public void testReinstate_ALF3867() - { - ContentData contentData = getContentData(); - Pair resultPair = create(contentData); - getAndCheck(resultPair.getFirst(), contentData); - delete(resultPair.getFirst()); - // Now create a ContentData with the same URL - create(contentData); - } - - public void testContentUrl_FetchingOrphansNoLimit() throws Exception - { - ContentData contentData = getContentData(); - Pair resultPair = create(contentData); - getAndCheck(resultPair.getFirst(), contentData); - delete(resultPair.getFirst()); - // The content URL is orphaned - final String contentUrlOrphaned = contentData.getContentUrl(); - final boolean[] found = new boolean[] {false}; - - // Iterate over all orphaned content URLs and ensure that we hit the one we just orphaned - ContentUrlHandler handler = new ContentUrlHandler() - { - public void handle(Long id, String contentUrl, Long orphanTime) - { - // Check - if (id == null || contentUrl == null || orphanTime == null) - { - fail("Invalid orphan data returned to handler: " + id + "-" + contentUrl + "-" + orphanTime); - } - // Did we get the one we wanted? - if (contentUrl.equals(contentUrlOrphaned)) - { - found[0] = true; - } - } - }; - contentDataDAO.getContentUrlsOrphaned(handler, Long.MAX_VALUE, Integer.MAX_VALUE); - assertTrue("Newly-orphaned content URL not found", found[0]); - } - - public void testContentUrl_FetchingOrphansWithLimit() throws Exception - { - // Orphan some content - for (int i = 0; i < 5; i++) - { - ContentData contentData = getContentData(); - Pair resultPair = create(contentData); - getAndCheck(resultPair.getFirst(), contentData); - delete(resultPair.getFirst()); - } - final int[] count = new int[] {0}; - - // Iterate over all orphaned content URLs and ensure that we hit the one we just orphaned - ContentUrlHandler handler = new ContentUrlHandler() - { - public void handle(Long id, String contentUrl, Long orphanTime) - { - // Check - if (id == null || contentUrl == null || orphanTime == null) - { - fail("Invalid orphan data returned to handler: " + id + "-" + contentUrl + "-" + orphanTime); - } - count[0]++; - } - }; - contentDataDAO.getContentUrlsOrphaned(handler, Long.MAX_VALUE, 5); - assertEquals("Expected exactly 5 results callbacks", 5, count[0]); - } - private static final String[] MIMETYPES = new String[] { MimetypeMap.MIMETYPE_ACP,