/* * Copyright (C) 2005-2009 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.contentdata; import java.util.ArrayList; import java.util.List; import java.util.Locale; import junit.framework.TestCase; 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.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.Pair; import org.alfresco.util.TempFileProvider; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ConfigurableApplicationContext; /** * @see ContentDataDAO * * @author Derek Hulley * @since 3.2 */ public class ContentDataDAOTest extends TestCase { private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext(); private TransactionService transactionService; private RetryingTransactionHelper txnHelper; private ContentDataDAO contentDataDAO; private ContentStore contentStore; @Override public void setUp() throws Exception { ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); transactionService = serviceRegistry.getTransactionService(); txnHelper = transactionService.getRetryingTransactionHelper(); ApplicationEventPublisher applicationEventPublisher = (ApplicationEventPublisher) ctx.getBean("applicationEventPublisher"); contentDataDAO = (ContentDataDAO) ctx.getBean("contentDataDAO"); contentStore = new FileContentStore(applicationEventPublisher, TempFileProvider.getTempDir()); } private Pair create(final ContentData contentData) { RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { public Pair execute() throws Throwable { Pair contentDataPair = contentDataDAO.createContentData(contentData); return contentDataPair; } }; return txnHelper.doInTransaction(callback, false, false); } /** * Retrieves and checks the ContentData for equality */ private void getAndCheck(final Long contentDataId, ContentData checkContentData) { RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { public Pair execute() throws Throwable { Pair contentDataPair = contentDataDAO.getContentData(contentDataId); return contentDataPair; } }; Pair resultPair = txnHelper.doInTransaction(callback, true, false); assertNotNull("Failed to find result for ID " + contentDataId, resultPair); assertEquals("ContentData retrieved not the same as persisted: ", checkContentData, resultPair.getSecond()); } private ContentData getContentData() { ContentContext contentCtx = new ContentContext(null, null); String contentUrl = contentStore.getWriter(contentCtx).getContentUrl(); ContentData contentData = new ContentData( contentUrl, MimetypeMap.MIMETYPE_TEXT_PLAIN, 12335L, "UTF-8", Locale.FRENCH); return contentData; } public void testGetWithInvalidId() { assertNull("Expected null for invalid ID", contentDataDAO.getContentData(-1L)); } /** * Check that the ContentData is decoded and persisted correctly. */ public void testCreateContentDataSimple() throws Exception { ContentData contentData = getContentData(); Pair resultPair = create(contentData); getAndCheck(resultPair.getFirst(), contentData); } /** * Check that the ContentData is decoded and persisted correctly. */ public void testCreateContentDataNulls() throws Exception { ContentData contentData = new ContentData(null, null, 0L, null, null); Pair resultPair = create(contentData); getAndCheck(resultPair.getFirst(), contentData); } /** * Ensure that upper and lowercase URLs don't clash * @throws Exception */ public void testEnsureCaseSensitiveStorage() throws Exception { ContentData contentData = getContentData(); String contentUrlUpper = contentData.getContentUrl().toUpperCase(); ContentData contentDataUpper = new ContentData( contentUrlUpper, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, "UTF-8", new Locale("FR")); String contentUrlLower = contentData.getContentUrl().toLowerCase(); ContentData contentDataLower = new ContentData( contentUrlLower, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, "utf-8", new Locale("fr")); Pair resultPairUpper = create(contentDataUpper); getAndCheck(resultPairUpper.getFirst(), contentDataUpper); Pair resultPairLower = create(contentDataLower); getAndCheck(resultPairLower.getFirst(), contentDataLower); } public void testDelete() throws Exception { ContentData contentData = getContentData(); Pair resultPair = create(contentData); getAndCheck(resultPair.getFirst(), contentData); contentDataDAO.deleteContentData(resultPair.getFirst()); try { getAndCheck(resultPair.getFirst(), contentData); fail("Entity still exists"); } catch (Throwable e) { // Expected } } private static final String[] MIMETYPES = new String[] { MimetypeMap.MIMETYPE_ACP, MimetypeMap.MIMETYPE_EXCEL, MimetypeMap.MIMETYPE_IMAGE_JPEG, MimetypeMap.MIMETYPE_JAVASCRIPT, MimetypeMap.MIMETYPE_RSS }; private static final String[] ENCODINGS = new String[] { "utf-8", "ascii", "latin1", "wibbles", "iso-whatever" }; private static final Locale[] LOCALES = new Locale[] { Locale.FRENCH, Locale.CHINESE, Locale.ITALIAN, Locale.JAPANESE, Locale.ENGLISH }; private List> speedTestWrite(String name, int total) { System.out.println("Starting write speed test: " + name); long start = System.nanoTime(); List> pairs = new ArrayList>(100000); // Loop and check for performance degradation for (int i = 0; i < (total / 200 / 5); i++) { for (int j = 0; j < 200; j++) { for (int k = 0; k < 5; k++) { ContentData contentData = getContentData(); String contentUrl = contentData.getContentUrl(); contentData = new ContentData( contentUrl, MIMETYPES[k], (long) j*k, ENCODINGS[k], LOCALES[k]); Pair pair = create(contentData); pairs.add(pair); } } // That's 1000 long now = System.nanoTime(); double diffMs = (double) (now - start) / 1E6; double aveMs = diffMs / (double) pairs.size(); String msg = String.format( " Wrote %7d rows; average is %5.2f ms per row or %5.2f rows per second", pairs.size(), aveMs, 1000.0 / aveMs); System.out.println(msg); } // Done return pairs; } private void speedTestRead(String name, List> pairs) { System.out.println("Starting read speed test: " + name); long start = System.nanoTime(); // Loop and check for performance degradation int num = 1; for (Pair pair : pairs) { Long id = pair.getFirst(); ContentData contentData = pair.getSecond(); // Retrieve it getAndCheck(id, contentData); // Report if (num % 1000 == 0) { long now = System.nanoTime(); double diffMs = (double) (now - start) / 1E6; double aveMs = diffMs / (double) num; String msg = String.format( " Read %7d rows; average is %5.2f ms per row or %5.2f rows per second", num, aveMs, 1000.0 / aveMs); System.out.println(msg); } num++; } // Done } public void testCreateSpeedIndividualTxns() { List> pairs = speedTestWrite(getName(), 2000); speedTestRead(getName(), pairs); } public void testCreateSpeedSingleTxn() { RetryingTransactionCallback>> writeCallback = new RetryingTransactionCallback>>() { public List> execute() throws Throwable { return speedTestWrite(getName(), 10000); } }; final List> pairs = txnHelper.doInTransaction(writeCallback, false, false); RetryingTransactionCallback readCallback = new RetryingTransactionCallback() { public Void execute() throws Throwable { speedTestRead(getName(), pairs); return null; } }; txnHelper.doInTransaction(readCallback, false, false); } }