Files
alfresco-community-repo/source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java
Will Abson 04cead57a6 Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (4.3/Cloud)
71594: Merged V4.2-BUG-FIX (4.2.3) to HEAD-BUG-FIX (4.3/Cloud)
      70332: Merged V4.1-BUG-FIX (4.1.9) to V4.2-BUG-FIX (4.2.3)
         70292: Merged DEV to V4.1-BUG-FIX (4.1.9)
            60077: Created branch for MNT-10067 work.
            60101: MNT-10067: first commit of a generic SQL script runner.
            Very rough and ready extraction of SchemaBootstrap SQL script execution functionality. Will serve as the basis for the implementation of MNT-10067.
            60147: MNT-10067: added tests for "dialect" placeholder resolution, including overriding of dialects.
            60180: MNT-10067: exception thrown when unable to find SQL script to execute
            60187: MNT-10067: renamed ScriptExecutor to ScriptExecutorImpl to make way for an interface definition.
            60188: MNT-10067: introduced a ScriptExecutor interface.
            60189: MNT-10067: renamed ScriptExecutorTest
            60190: MNT-10067: added ScriptExecutorImplIntegrationTest to repo test suite.
            60194: MNT-10067: a very simple initial implementation of a SQL script runner capable of running multiple scripts in a given
            directory.
            60195: MNT-10067: added integration test for ScriptBundleExecutorImpl.
            60196: MNT-10067: moved ScriptBundleExecutorImplTest to correct source tree.
            60197: MNT-10067: added ScriptBundleExecutorImplIntegrationTest to repo test suite.
            60263: MNT-10067: ScriptBundleExecutor(Impl) now stops executing the main batch of scripts upon failure and runs a post-script.
            60459: MNT-10067: minor change to test data to avoid implying that ScriptBundleExecutor.exec(String, String...) has an always-run
            final script.
            60482: MNT-10067: added integration test for ScriptBundleExecutor.execWithPostScript()
            60483: MNT-10067: committed missing files from r60482
            60488: MNT-10067: set appropriate log levels for log4j
            60620: MNT-10067: added alf_props_xxx clean-up script.
            60623: MNT-10067: minor tidy up of ScriptExecutorImpl (tidy imports, group fields at top of class)
            60625: MNT-10067: further tidy up ScriptExecutorImpl (removed redundant constants, made externally unused public constant private)
            60629: MNT-10067: fix tests broken by introduction of scriptExecutor bean in production code.
            60662: MNT-10067: added tests to check deletion of doubles, serializables and dates.
            61378: MNT-10067 : Cleanup alf_prop_XXX data
            Added MySQL, Oracle DB, MS SQL Server and IBM DB2 scripts.
            63371: MNT-10067: removed the vacuum and analyze statements from the postgresql script.
            63372: MNT-10067: replaced begin and commit statements (PostgreSQL script only) with --BEGIN TXN and --END TXN, handled by the
            script executor.
            63568: MNT-10067 : Cleanup alf_prop_XXX data
            Added start and end transaction marks to the scripts.
            64115: MNT-10067: added Quartz job that by default doesn't fire until 2099 and can be manually invoked over JMX.
            64223: MNT-10067: improved testing for AuditDAOTest and added PropertyValueDAOTest
            64685: MNT-10067: added AttributeServiceTest
            65796: MNT-10067 : Cleanup alf_prop_XXX data
            Implemented a performance test.
            65983: MNT-10067 : Cleanup alf_prop_XXX data
            Reworked the MySQL script.
            Added time measurements for entry creation.
            66116: MNT-10067 : Cleanup alf_prop_XXX data
            For MySQL:
            1) Renamed temp tables.
            2) Split the script into execution and cleanup of temp tables parts.
            67023: MNT-10067 : Cleanup alf_prop_XXX data
            Modified MySQL script to skip null values from alf_prop_unique_ctx.prop1_id.
            67199: MNT-10067 : Cleanup alf_prop_XXX data
            Implemented the latest changes of the script for other DB flavors.
            Removed the MS SQL Server transaction marks.
            67763: MNT-10067 : Cleanup alf_prop_XXX data
            Removed unnecessary temporary index dropping.
            Added additional cleanup before main script execution.
            68710: MNT-10067 : Cleanup alf_prop_XXX data
            Added batch logging.
            Moved clearCaches() statement in PropertyValueDAOImpl.cleanupUnusedValues() to finally block.
            68711: MNT-10067 : Cleanup alf_prop_XXX data
            Added batching for MySQL script.
            69602: MNT-10067 : Cleanup alf_prop_XXX data
            Updated scripts for all DB flavors with batching.
            69768: MNT-10067 : Cleanup alf_prop_XXX data
            Fixed failing ScriptBundleExecutorImplIntegrationTest and ScriptExecutorImplIntegrationTest.
            70058: Sync with latest changes in V4.1-BUG-FIX.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@74691 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2014-06-25 15:26:31 +00:00

703 lines
28 KiB
Java

/*
* Copyright (C) 2005-2014 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.domain.audit;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.domain.audit.AuditDAO.AuditApplicationInfo;
import org.alfresco.repo.domain.contentdata.ContentDataDAO;
import org.alfresco.repo.domain.propval.PropValGenerator;
import org.alfresco.repo.domain.propval.PropertyValueDAO;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.audit.AuditQueryParameters;
import org.alfresco.service.cmr.audit.AuditService.AuditQueryCallback;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.apache.commons.lang.mutable.MutableInt;
import org.junit.experimental.categories.Category;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @see ContentDataDAO
*
* @author Derek Hulley
* @since 3.2
*/
@Category(OwnJVMTestsCategory.class)
public class AuditDAOTest extends TestCase
{
private ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) ApplicationContextHelper.getApplicationContext();
private TransactionService transactionService;
private RetryingTransactionHelper txnHelper;
private AuditDAO auditDAO;
private PropertyValueDAO propertyValueDAO;
@Override
public void setUp() throws Exception
{
ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
transactionService = serviceRegistry.getTransactionService();
txnHelper = transactionService.getRetryingTransactionHelper();
auditDAO = (AuditDAO) ctx.getBean("auditDAO");
propertyValueDAO = ctx.getBean(PropertyValueDAO.class);
}
public void testAuditModel() throws Exception
{
final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf");
assertNotNull(file);
final URL url = new URL("file:" + file.getAbsolutePath());
RetryingTransactionCallback<Pair<Long, ContentData>> callback = new RetryingTransactionCallback<Pair<Long, ContentData>>()
{
public Pair<Long, ContentData> execute() throws Throwable
{
Pair<Long, ContentData> auditModelPair = auditDAO.getOrCreateAuditModel(url);
return auditModelPair;
}
};
Pair<Long, ContentData> configPair = txnHelper.doInTransaction(callback);
assertNotNull(configPair);
// Now repeat. The results should be exactly the same.
Pair<Long, ContentData> configPairCheck = txnHelper.doInTransaction(callback);
assertNotNull(configPairCheck);
assertEquals(configPair, configPairCheck);
}
public void testAuditApplication() throws Exception
{
final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf");
assertNotNull(file);
final URL url = new URL("file:" + file.getAbsolutePath());
RetryingTransactionCallback<Long> createModelCallback = new RetryingTransactionCallback<Long>()
{
public Long execute() throws Throwable
{
return auditDAO.getOrCreateAuditModel(url).getFirst();
}
};
final Long modelId = txnHelper.doInTransaction(createModelCallback);
final String appName = getName() + "." + System.currentTimeMillis();
final int count = 1000;
RetryingTransactionCallback<Void> createAppCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
for (int i = 0; i < count; i++)
{
AuditApplicationInfo appInfo = auditDAO.getAuditApplication(appName);
if (appInfo == null)
{
appInfo = auditDAO.createAuditApplication(appName, modelId);
}
}
return null;
}
};
long before = System.nanoTime();
txnHelper.doInTransaction(createAppCallback);
long after = System.nanoTime();
System.out.println(
"Time for " + count + " application creations was " +
((double)(after - before)/(10E6)) + "ms");
}
public void testAuditEntry() throws Exception
{
doAuditEntryImpl(1000);
}
/**
* @return Returns the name of the application
*/
private String doAuditEntryImpl(final int count) throws Exception
{
final File file = AbstractContentTransformerTest.loadQuickTestFile("pdf");
assertNotNull(file);
final URL url = new URL("file:" + file.getAbsolutePath());
final String appName = getName() + "." + System.currentTimeMillis();
RetryingTransactionCallback<Long> createAppCallback = new RetryingTransactionCallback<Long>()
{
public Long execute() throws Throwable
{
AuditApplicationInfo appInfo = auditDAO.getAuditApplication(appName);
if (appInfo == null)
{
Long modelId = auditDAO.getOrCreateAuditModel(url).getFirst();
appInfo = auditDAO.createAuditApplication(appName, modelId);
}
return appInfo.getId();
}
};
final Long sessionId = txnHelper.doInTransaction(createAppCallback);
final String username = "alexi";
RetryingTransactionCallback<Void> createEntryCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
for (int i = 0; i < count; i++)
{
Map<String, Serializable> values = Collections.singletonMap("/a/b/c", (Serializable) new Integer(i));
long now = System.currentTimeMillis();
auditDAO.createAuditEntry(sessionId, now, username, values);
}
return null;
}
};
long before = System.nanoTime();
txnHelper.doInTransaction(createEntryCallback);
long after = System.nanoTime();
System.out.println(
"Time for " + count + " entry creations was " +
((double)(after - before)/(10E6)) + "ms");
// Done
return appName;
}
public synchronized void testAuditQuery() throws Exception
{
// Some entries
doAuditEntryImpl(1);
final MutableInt count = new MutableInt(0);
final LinkedList<Long> timestamps = new LinkedList<Long>();
// Find everything, but look for a specific key
final AuditQueryCallback callback = new AuditQueryCallback()
{
public boolean valuesRequired()
{
return false;
}
public boolean handleAuditEntry(
Long entryId,
String applicationName,
String user,
long time,
Map<String, Serializable> values)
{
count.setValue(count.intValue() + 1);
timestamps.add(time);
return true;
}
public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error)
{
throw new AlfrescoRuntimeException(errorMsg, error);
}
};
final AuditQueryParameters params = new AuditQueryParameters();
params.addSearchKey("/a/b/c", null);
RetryingTransactionCallback<Void> findCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
auditDAO.findAuditEntries(callback, params, 2);
return null;
}
};
count.setValue(0);
timestamps.clear();
txnHelper.doInTransaction(findCallback);
assertTrue("Expected at least one result", count.intValue() > 0);
// // Make sure that the last two entries are in forward order (ascending time)
// Long lastTimestamp = timestamps.removeLast();
// Long secondLastTimeStamp = timestamps.removeLast();
// assertTrue("The timestamps should be in ascending order", lastTimestamp.compareTo(secondLastTimeStamp) > 0);
//
// Make sure that the last two entries differ in time
wait(1000L);
// Search in reverse order
doAuditEntryImpl(1);
RetryingTransactionCallback<Void> findReverseCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
params.setForward(false);
auditDAO.findAuditEntries(callback, params, 2);
params.setForward(true);
return null;
}
};
timestamps.clear();
txnHelper.doInTransaction(findReverseCallback);
//
// // Make sure that the last two entries are in reverse order (descending time)
// lastTimestamp = timestamps.removeLast();
// secondLastTimeStamp = timestamps.removeLast();
// assertTrue("The timestamps should be in descending order", lastTimestamp.compareTo(secondLastTimeStamp) < 0);
}
public void testAuditDeleteEntries() throws Exception
{
final AuditQueryCallback noResultsCallback = new AuditQueryCallback()
{
public boolean valuesRequired()
{
return false;
}
public boolean handleAuditEntry(
Long entryId,
String applicationName,
String user,
long time,
Map<String, Serializable> values)
{
fail("Expected no results. All entries should have been removed.");
return false;
}
public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error)
{
throw new AlfrescoRuntimeException(errorMsg, error);
}
};
// Some entries
final String appName = doAuditEntryImpl(1);
final AuditQueryParameters params = new AuditQueryParameters();
params.setApplicationName(appName);
// Delete the entries
RetryingTransactionCallback<Void> deletedCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Long appId = auditDAO.getAuditApplication(appName).getId();
auditDAO.deleteAuditEntries(appId, null, null);
// There should be no entries
auditDAO.findAuditEntries(noResultsCallback, params, -1);
return null;
}
};
txnHelper.doInTransaction(deletedCallback);
}
/**
* Ensure that only the correct application's audit entries are deleted.
* @throws Exception
*/
public void testAuditDeleteEntriesForApplication() throws Exception
{
final String app1 = doAuditEntryImpl(6);
final String app2 = doAuditEntryImpl(18);
final AuditQueryCallbackImpl resultsCallback = new AuditQueryCallbackImpl();
RetryingTransactionCallback<Void> deletedCallback = new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
Long app1Id = auditDAO.getAuditApplication(app1).getId();
auditDAO.deleteAuditEntries(app1Id, null, null);
// There should be no entries for app1
// but still entries for app2
auditDAO.findAuditEntries(resultsCallback, new AuditQueryParameters(), -1);
assertEquals("All entries should have been deleted from app1", 0, resultsCallback.numEntries(app1));
assertEquals("No entries should have been deleted from app2", 18, resultsCallback.numEntries(app2));
return null;
}
};
txnHelper.doInTransaction(deletedCallback);
}
/**
* Ensure that an application's audit entries can be deleted between 2 times.
* @throws Exception
*/
public void testAuditDeleteEntriesForApplicationBetweenTimes() throws Exception
{
RetryingTransactionCallback<Void> deletedCallback = new RetryingTransactionCallback<Void>()
{
AuditQueryCallbackImpl preDeleteCallback = new AuditQueryCallbackImpl();
AuditQueryCallbackImpl resultsCallback = new AuditQueryCallbackImpl();
public Void execute() throws Throwable
{
AuditApplicationInfo info1 = createAuditApp();
String app1 = info1.getName();
Long app1Id = info1.getId();
AuditApplicationInfo info2 = createAuditApp();
String app2 = info2.getName();
// Create items 10, 11, 12, 13, 14 for application 1
// Create items 21, 22 for application 2
createItem(info1, 10);
createItem(info1, 11);
Thread.sleep(10); // stop previous statements being executed during t1
Thread.sleep(10);
final long t1 = System.currentTimeMillis();
Thread.sleep(10);
Thread.sleep(10);
createItem(info2, 21);
createItem(info1, 12);
createItem(info1, 13);
Thread.sleep(10);
Thread.sleep(10);
final long t2 = System.currentTimeMillis();
Thread.sleep(10); // stop next statements being executed during t2
Thread.sleep(10);
createItem(info2, 22);
createItem(info1, 14);
auditDAO.findAuditEntries(preDeleteCallback, new AuditQueryParameters(), -1);
assertEquals(5, preDeleteCallback.numEntries(app1));
assertEquals(2, preDeleteCallback.numEntries(app2));
auditDAO.deleteAuditEntries(app1Id, t1, t2);
auditDAO.findAuditEntries(resultsCallback, new AuditQueryParameters(), -1);
assertEquals("Two entries should have been deleted from app1", 3, resultsCallback.numEntries(app1));
assertEquals("No entries should have been deleted from app2", 2, resultsCallback.numEntries(app2));
return null;
}
};
txnHelper.doInTransaction(deletedCallback);
}
/**
* Ensure audit entries can be deleted between two times - for all applications.
* @throws Exception
*/
public void testAuditDeleteEntriesBetweenTimes() throws Exception
{
RetryingTransactionCallback<Void> deletedCallback = new RetryingTransactionCallback<Void>()
{
AuditQueryCallbackImpl preDeleteCallback = new AuditQueryCallbackImpl();
AuditQueryCallbackImpl resultsCallback = new AuditQueryCallbackImpl();
public Void execute() throws Throwable
{
AuditApplicationInfo info1 = createAuditApp();
String app1 = info1.getName();
AuditApplicationInfo info2 = createAuditApp();
String app2 = info2.getName();
// Create items 10, 11, 12, 13, 14 for application 1
// Create items 21, 22 for application 2
createItem(info1, 10);
createItem(info1, 11);
Thread.sleep(10);
Thread.sleep(10); // stop previous statements being executed during t1
final long t1 = System.currentTimeMillis();
Thread.sleep(10);
Thread.sleep(10);
createItem(info2, 21);
createItem(info1, 12);
createItem(info1, 13);
Thread.sleep(10);
Thread.sleep(10);
final long t2 = System.currentTimeMillis();
Thread.sleep(10); // stop next statements being executed during t2
Thread.sleep(10);
createItem(info2, 22);
createItem(info1, 14);
auditDAO.findAuditEntries(preDeleteCallback, new AuditQueryParameters(), -1);
assertEquals(5, preDeleteCallback.numEntries(app1));
assertEquals(2, preDeleteCallback.numEntries(app2));
// Delete audit entries between times - for all applications.
auditDAO.deleteAuditEntries(null, t1, t2);
auditDAO.findAuditEntries(resultsCallback, new AuditQueryParameters(), -1);
assertEquals("Two entries should have been deleted from app1", 3, resultsCallback.numEntries(app1));
assertEquals("One entry should have been deleted from app2", 1, resultsCallback.numEntries(app2));
return null;
}
};
txnHelper.doInTransaction(deletedCallback);
}
/**
* Create an audit item
* @param appInfo The audit application to create the item for.
* @param value The value that will be stored against the path /a/b/c
*/
private void createItem(final AuditApplicationInfo appInfo, final int value)
{
String username = "alexi";
Map<String, Serializable> values = Collections.singletonMap("/a/b/c", (Serializable) value);
long now = System.currentTimeMillis();
auditDAO.createAuditEntry(appInfo.getId(), now, username, values);
}
/**
* Create an audit application.
* @return AuditApplicationInfo for the new application.
* @throws IOException
*/
private AuditApplicationInfo createAuditApp() throws IOException
{
String appName = getName() + "." + GUID.generate();
File file = AbstractContentTransformerTest.loadQuickTestFile("pdf");
assertNotNull(file);
URL url = new URL("file:" + file.getAbsolutePath());
AuditApplicationInfo appInfo = auditDAO.getAuditApplication(appName);
if (appInfo == null)
{
Long modelId = auditDAO.getOrCreateAuditModel(url).getFirst();
appInfo = auditDAO.createAuditApplication(appName, modelId);
}
return appInfo;
}
public class AuditQueryCallbackImpl implements AuditQueryCallback
{
private Map<String, Integer> countsByApp = new HashMap<String, Integer>();
public boolean valuesRequired()
{
return false;
}
public boolean handleAuditEntry(
Long entryId,
String applicationName,
String user,
long time,
Map<String, Serializable> values)
{
Integer count = countsByApp.get(applicationName);
if (count == null)
countsByApp.put(applicationName, 1);
else
countsByApp.put(applicationName, ++count);
return true;
}
public boolean handleAuditEntryError(Long entryId, String errorMsg, Throwable error)
{
throw new AlfrescoRuntimeException(errorMsg, error);
}
public int numEntries(String appName)
{
if (countsByApp.containsKey(appName))
return countsByApp.get(appName);
else
return 0;
}
}
/**
* MNT-10067: use a script to delete the orphaned audit data (property values).
*/
public void testScriptCanDeleteOrphanedProps() throws Exception
{
// single test
scriptCanDeleteOrphanedPropsWork(false);
}
private void scriptCanDeleteOrphanedPropsWork(final boolean performance) throws Exception
{
final int iterationStep, maxIterations;
if (performance)
{
iterationStep = 1000;
maxIterations = 1000;
}
else
{
iterationStep = 1;
maxIterations = 1;
}
UserTransaction txn;
for (int i = iterationStep; i <= maxIterations*iterationStep; i+=iterationStep)
{
List<String> stringValues = new LinkedList<String>();
List<Double> doubleValues = new LinkedList<Double>();
List<Date> dateValues = new LinkedList<Date>();
txn = transactionService.getUserTransaction();
long startCreate = System.currentTimeMillis();
txn.begin();
for (int j = 0; j < i; j++)
{
PropValGenerator valueGen = new PropValGenerator(propertyValueDAO);
String stringValue = valueGen.createUniqueString();
stringValues.add(stringValue);
Double doubleValue = valueGen.createUniqueDouble();
doubleValues.add(doubleValue);
Date dateValue = valueGen.createUniqueDate();
dateValues.add(dateValue);
AuditQueryCallbackImpl preDeleteCallback = new AuditQueryCallbackImpl();
AuditQueryCallbackImpl resultsCallback = new AuditQueryCallbackImpl();
AuditApplicationInfo info1 = createAuditApp();
String app1 = info1.getName();
String username = "alexi";
Map<String, Serializable> values = new HashMap<String, Serializable>();
values.put("/a/b/string-" + j, stringValue);
values.put("/a/b/double-" + j, doubleValue);
values.put("/a/b/date-" + j, dateValue);
// TODO: how to deal with Serializable values which cannot be retrieved later in test by value alone?
long now = System.currentTimeMillis();
auditDAO.createAuditEntry(info1.getId(), now, username, values);
auditDAO.findAuditEntries(preDeleteCallback, new AuditQueryParameters(), -1);
assertEquals(1, preDeleteCallback.numEntries(app1));
// Delete audit entries between times - for all applications.
auditDAO.deleteAuditEntries(info1.getId(), null, null);
if (!performance)
{
auditDAO.findAuditEntries(resultsCallback, new AuditQueryParameters(), -1);
assertEquals("All entries should have been deleted from app1", 0, resultsCallback.numEntries(app1));
}
}
txn.commit();
System.out.println("Created values for " + i + " entries in " + (System.currentTimeMillis() - startCreate) + " ms.");
txn = transactionService.getUserTransaction();
txn.begin();
if (!performance)
{
// Check there are some persisted values to delete.
// Unlike PropertyValueDAOTest we're using the getPropertyValue() method here,
// instead of the datatype-specific methods (e.g. getPropertyStringValue()).
// This is because AuditDAO persists an entire map of values resulting in different behaviour
// (i.e. dates are persisted as Serializable)
for (String stringValue : stringValues)
{
assertEquals(stringValue, propertyValueDAO.getPropertyValue(stringValue).getSecond());
}
for (Double doubleValue : doubleValues)
{
assertEquals(doubleValue, propertyValueDAO.getPropertyValue(doubleValue).getSecond());
}
for (Date dateValue : dateValues)
{
assertEquals(dateValue, propertyValueDAO.getPropertyValue(dateValue).getSecond());
}
}
long startDelete = System.currentTimeMillis();
propertyValueDAO.cleanupUnusedValues();
txn.commit();
System.out.println("Cleaned values for " + i + " entries in " + (System.currentTimeMillis() - startDelete) + " ms.");
if (!performance)
{
// Check all the properties have been deleted.
txn = transactionService.getUserTransaction();
txn.begin();
for (String stringValue : stringValues)
{
assertPropDeleted(propertyValueDAO.getPropertyValue(stringValue));
}
for (Double doubleValue : doubleValues)
{
assertPropDeleted(propertyValueDAO.getPropertyValue(doubleValue));
}
for (Date dateValue : dateValues)
{
assertPropDeleted(propertyValueDAO.getPropertyValue(dateValue));
}
txn.commit();
}
}
}
private void assertPropDeleted(Pair<Long, ?> value)
{
if (value != null)
{
String msg = String.format("Property value [%s=%s] should have been deleted by cleanup script.",
value.getSecond().getClass().getSimpleName(), value.getSecond());
fail(msg);
}
}
public void scriptCanDeleteOrphanedPropsPerformance() throws Exception
{
scriptCanDeleteOrphanedPropsWork(true);
}
public static void main(String[] args)
{
try
{
AuditDAOTest test = new AuditDAOTest();
test.setUp();
System.out.println("Press any key to run performance test.");
System.in.read();
test.scriptCanDeleteOrphanedPropsPerformance();
System.out.println("Press any key to shutdown.");
System.in.read();
test.tearDown();
}
catch (Throwable e)
{
e.printStackTrace();
}
finally
{
ApplicationContextHelper.closeApplicationContext();
}
}
}