From 9d711213cce2dbc842bdd0982aea75cdda0c7fa6 Mon Sep 17 00:00:00 2001 From: Davide Date: Tue, 20 Apr 2021 16:32:41 +0200 Subject: [PATCH] SEARCH-2782 commit time as event time (#377) --- .../repo/domain/node/AbstractNodeDAOImpl.java | 12 +++ .../alfresco/repo/domain/node/NodeDAO.java | 25 ++++-- .../repo/domain/permissions/AclDAO.java | 57 +++++++------ .../repo/domain/permissions/AclDAOImpl.java | 59 +++++++------ .../alfresco/repo/event2/EventGenerator.java | 82 +++++++++++++------ .../resources/alfresco/events2-context.xml | 1 + .../repo/domain/node/NodeDAOTest.java | 26 ++++-- .../repo/event2/CreateRepoEventIT.java | 34 +++++++- .../permissions/impl/AclDaoComponentTest.java | 45 ++++++++++ 9 files changed, 246 insertions(+), 95 deletions(-) diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 06f724cb1d..b9fd46ea90 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -563,6 +563,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO Long txnId = txn.getId(); // Update it Long now = System.currentTimeMillis(); + txn.setCommitTimeMs(now); updateTransaction(txnId, now); } } @@ -604,6 +605,17 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO return txn; } + public Long getCurrentTransactionCommitTime() + { + Long commitTime = null; + TransactionEntity resource = AlfrescoTransactionSupport.getResource(KEY_TRANSACTION); + if(resource != null) + { + commitTime = resource.getCommitTimeMs(); + } + return commitTime; + } + public Long getCurrentTransactionId(boolean ensureNew) { TransactionEntity txn; diff --git a/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java b/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java index 7992fd4ff8..4b84067ea0 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/node/NodeDAO.java @@ -75,6 +75,13 @@ public interface NodeDAO extends NodeBulkLoader /* * Transaction */ + + /** + * @return the commit time of the current transaction entry or null if + * there have not been any modifications to nodes registered in the + * transaction. + */ + Long getCurrentTransactionCommitTime(); /** * @param ensureNew true to ensure that a new transaction entry is created @@ -927,14 +934,14 @@ public interface NodeDAO extends NodeBulkLoader * @param toNodeId Final node id * @return maximum commit time */ - public Long getMaxTxInNodeIdRange(Long fromNodeId, Long toNodeId); - - /** - * Gets the next commit time from [fromCommitTime] - * - * @param fromCommitTime Initial commit time - * @return next commit time - */ + public Long getMaxTxInNodeIdRange(Long fromNodeId, Long toNodeId); + + /** + * Gets the next commit time from [fromCommitTime] + * + * @param fromCommitTime Initial commit time + * @return next commit time + */ public Long getNextTxCommitTime(Long fromCommitTime); - + } diff --git a/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAO.java b/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAO.java index 1e74fc95ac..1b5eaa7410 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAO.java +++ b/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAO.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.permissions; import java.util.List; @@ -182,4 +182,11 @@ public interface AclDAO * @return Long */ public Long getMaxChangeSetIdByCommitTime(long maxCommitTime); + + /** + * @return the commit time of the current ACL change set entry or null if + * there have not been any modifications. + */ + public Long getCurrentChangeSetCommitTime(); + } diff --git a/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java b/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java index 5b6f24ac60..f5495b56e5 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAOImpl.java +++ b/repository/src/main/java/org/alfresco/repo/domain/permissions/AclDAOImpl.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.permissions; import java.io.Serializable; @@ -1637,6 +1637,7 @@ public class AclDAOImpl implements AclDAO } private static final String RESOURCE_KEY_ACL_CHANGE_SET_ID = "acl.change.set.id"; + private static final String RESOURCE_KEY_ACL_CHANGE_SET_COMMIT_TIME_MS = "acl.change.commit.set.time.ms"; private UpdateChangeSetListener updateChangeSetListener = new UpdateChangeSetListener(); /** @@ -1662,9 +1663,17 @@ public class AclDAOImpl implements AclDAO } // Update it long commitTimeMs = System.currentTimeMillis(); + AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_ACL_CHANGE_SET_COMMIT_TIME_MS, commitTimeMs); aclCrudDAO.updateAclChangeSet(changeSetId, commitTimeMs); } } + + @Override + public Long getCurrentChangeSetCommitTime() + { + return AlfrescoTransactionSupport.getResource(RESOURCE_KEY_ACL_CHANGE_SET_COMMIT_TIME_MS); + } + /** * Support to get the current ACL change set and bind this to the transaction. So we only make one new version of an * ACL per change set. If something is in the current change set we can update it. diff --git a/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java b/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java index a4c01021f1..1d1b7f78d3 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java +++ b/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java @@ -27,12 +27,16 @@ package org.alfresco.repo.event2; import java.io.Serializable; import java.net.URI; +import java.time.Instant; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Deque; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.node.TransactionEntity; import org.alfresco.repo.event.v1.model.EventType; import org.alfresco.repo.event.v1.model.RepoEvent; import org.alfresco.repo.event2.filter.ChildAssociationTypeFilter; @@ -92,6 +96,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin private TransactionService transactionService; private PersonService personService; protected NodeResourceHelper nodeResourceHelper; + protected NodeDAO nodeDAO; private EventGeneratorQueue eventGeneratorQueue; private NodeTypeFilter nodeTypeFilter; @@ -111,6 +116,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin PropertyCheck.mandatory(this, "transactionService", transactionService); PropertyCheck.mandatory(this, "personService", personService); PropertyCheck.mandatory(this, "nodeResourceHelper", nodeResourceHelper); + PropertyCheck.mandatory(this, "nodeDAO", nodeDAO); PropertyCheck.mandatory(this, "eventGeneratorQueue", eventGeneratorQueue); this.nodeTypeFilter = eventFilterRegistry.getNodeTypeFilter(); @@ -144,6 +150,11 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin new JavaBehaviour(this, "beforeDeleteAssociation")); } + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; @@ -366,15 +377,22 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin return (childAssociationTypeFilter.isExcluded(childAssocType) || (userFilter.isExcluded(user))); } - private EventInfo getEventInfo(String user) + protected EventInfo getEventInfo(String user) { - return new EventInfo().setTimestamp(ZonedDateTime.now()) + return new EventInfo().setTimestamp(getCurrentTransactionTimestamp()) .setId(UUID.randomUUID().toString()) .setTxnId(AlfrescoTransactionSupport.getTransactionId()) .setPrincipal(user) .setSource(URI.create("/" + descriptorService.getCurrentRepositoryDescriptor().getId())); } + private ZonedDateTime getCurrentTransactionTimestamp() + { + Long currentTransactionCommitTime = nodeDAO.getCurrentTransactionCommitTime(); + Instant commitTimeMs = Instant.ofEpochMilli(currentTransactionCommitTime); + return ZonedDateTime.ofInstant(commitTimeMs, ZoneOffset.UTC); + } + @Override protected void onBootstrap(ApplicationEvent applicationEvent) { @@ -392,35 +410,38 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin @Override public void afterCommit() { - try + if(isTransactionCommitted()) { - final Consolidators consolidators = getTxnConsolidators(this); - - // Node events - for (Map.Entry entry : consolidators.getNodes().entrySet()) + try { - EventConsolidator eventConsolidator = entry.getValue(); - sendEvent(entry.getKey(), eventConsolidator); - } + final Consolidators consolidators = getTxnConsolidators(this); - // Child assoc events - for (Map.Entry entry : consolidators.getChildAssocs().entrySet()) - { - ChildAssociationEventConsolidator eventConsolidator = entry.getValue(); - sendEvent(entry.getKey(), eventConsolidator); - } + // Node events + for (Map.Entry entry : consolidators.getNodes().entrySet()) + { + EventConsolidator eventConsolidator = entry.getValue(); + sendEvent(entry.getKey(), eventConsolidator); + } - // Peer assoc events - for (Map.Entry entry : consolidators.getPeerAssocs().entrySet()) - { - PeerAssociationEventConsolidator eventConsolidator = entry.getValue(); - sendEvent(entry.getKey(), eventConsolidator); + // Child assoc events + for (Map.Entry entry : consolidators.getChildAssocs().entrySet()) + { + ChildAssociationEventConsolidator eventConsolidator = entry.getValue(); + sendEvent(entry.getKey(), eventConsolidator); + } + + // Peer assoc events + for (Map.Entry entry : consolidators.getPeerAssocs().entrySet()) + { + PeerAssociationEventConsolidator eventConsolidator = entry.getValue(); + sendEvent(entry.getKey(), eventConsolidator); + } + } + catch (Exception e) + { + // Must consume the exception to protect other TransactionListeners + LOGGER.error("Unexpected error while sending repository events", e); } - } - catch (Exception e) - { - // Must consume the exception to protect other TransactionListeners - LOGGER.error("Unexpected error while sending repository events", e); } } @@ -430,6 +451,15 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin eventGeneratorQueue.accept(()-> createEvent(nodeRef, consolidator, eventInfo)); } + /** + * @return true if a node transaction is not only active, but also committed with modifications. + * This means that a {@link TransactionEntity} object was created. + */ + protected boolean isTransactionCommitted() + { + return nodeDAO.getCurrentTransactionCommitTime() != null; + } + private RepoEvent createEvent(NodeRef nodeRef, EventConsolidator consolidator, EventInfo eventInfo) { String user = eventInfo.getPrincipal(); diff --git a/repository/src/main/resources/alfresco/events2-context.xml b/repository/src/main/resources/alfresco/events2-context.xml index 028421f7ee..205c8ae7ea 100644 --- a/repository/src/main/resources/alfresco/events2-context.xml +++ b/repository/src/main/resources/alfresco/events2-context.xml @@ -42,6 +42,7 @@ + diff --git a/repository/src/test/java/org/alfresco/repo/domain/node/NodeDAOTest.java b/repository/src/test/java/org/alfresco/repo/domain/node/NodeDAOTest.java index 097de89665..6d67d45911 100644 --- a/repository/src/test/java/org/alfresco/repo/domain/node/NodeDAOTest.java +++ b/repository/src/test/java/org/alfresco/repo/domain/node/NodeDAOTest.java @@ -92,12 +92,10 @@ public class NodeDAOTest extends TestCase public void testTransaction() throws Throwable { final boolean[] newTxn = new boolean[] {false}; - RetryingTransactionCallback getTxnIdCallback = new RetryingTransactionCallback() - { - public Long execute() throws Throwable - { - return nodeDAO.getCurrentTransactionId(newTxn[0]); - } + RetryingTransactionCallback> getTxnIdCallback = () -> { + Long currentTransactionId = nodeDAO.getCurrentTransactionId(newTxn[0]); + Long currentTransactionCommitTime = nodeDAO.getCurrentTransactionCommitTime(); + return new Pair<>(currentTransactionId, currentTransactionCommitTime); }; // No txn try @@ -110,14 +108,24 @@ public class NodeDAOTest extends TestCase // Expected } // Read-only - assertNull("No Txn ID should be present in read-only txn", txnHelper.doInTransaction(getTxnIdCallback, true)); + Pair txn0 = txnHelper.doInTransaction(getTxnIdCallback); + Long txnId0 = txn0.getFirst(); + Long commitTime0 = txn0.getSecond(); + assertNull("No Txn ID should be present in read-only txn", txnId0); + assertNull("No Txn Commit time should be present in read-only txn", commitTime0); // First success - Long txnId1 = txnHelper.doInTransaction(getTxnIdCallback); + Pair txn1 = txnHelper.doInTransaction(getTxnIdCallback); + Long txnId1 = txn1.getFirst(); + Long commitTime1 = txn1.getSecond(); assertNull("No Txn ID should be present in untouched txn", txnId1); + assertNull("No Txn Commit time should be present in untouched txn", commitTime1); // Second success newTxn[0] = true; - Long txnId2 = txnHelper.doInTransaction(getTxnIdCallback); + Pair txn2 = txnHelper.doInTransaction(getTxnIdCallback); + Long txnId2 = txn2.getFirst(); + Long commitTime2 = txn2.getSecond(); assertNotNull("Txn ID should be present by forcing it", txnId2); + assertNotNull("Txn commit time should be present by forcing it", commitTime2); } public void testSelectNodePropertiesByTypes() throws Exception diff --git a/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java index 7496f7633a..db6b592c70 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java +++ b/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java @@ -26,9 +26,14 @@ package org.alfresco.repo.event2; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.List; import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.node.Transaction; import org.alfresco.repo.event.v1.model.EventData; import org.alfresco.repo.event.v1.model.EventType; import org.alfresco.repo.event.v1.model.NodeResource; @@ -38,6 +43,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.alfresco.util.PropertyMap; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; /** * @author Iulian Aftene @@ -45,6 +51,9 @@ import org.junit.Test; public class CreateRepoEventIT extends AbstractContextAwareRepoEvent { + @Autowired + private NodeDAO nodeDAO; + @Test public void testCreateEvent() { @@ -149,9 +158,32 @@ public class CreateRepoEventIT extends AbstractContextAwareRepoEvent assertTrue("isFile flag should be TRUE for nodeType=cm:content. ", resource.isFile()); assertFalse("isFolder flag should be FALSE for nodeType=cm:content. ", resource.isFolder()); } + + @Test + public void testEventTimestampEqualsToTransactionCommitTime() + { + String name = "TestFile-" + System.currentTimeMillis() + ".txt"; + PropertyMap propertyMap = new PropertyMap(); + propertyMap.put(ContentModel.PROP_NAME, name); + + //create a node and return the transaction id required later + Long transactionId = retryingTransactionHelper.doInTransaction(() -> { + nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, + QName.createQName(TEST_NAMESPACE, GUID.generate()), ContentModel.TYPE_CONTENT, propertyMap).getChildRef(); + return nodeDAO.getCurrentTransactionId(false); + }); + + RepoEvent> resultRepoEvent = getRepoEvent(1); + + Transaction transaction = nodeDAO.getTxnById(transactionId); + Instant commitTimeMs = Instant.ofEpochMilli(transaction.getCommitTimeMs()); + ZonedDateTime timestamp = ZonedDateTime.ofInstant(commitTimeMs, ZoneOffset.UTC); + + assertEquals(timestamp, resultRepoEvent.getTime()); + } @Test - public void testCteateMultipleNodesInTheSameTransaction() + public void testCreateMultipleNodesInTheSameTransaction() { retryingTransactionHelper.doInTransaction(() -> { for (int i = 0; i < 3; i++) diff --git a/repository/src/test/java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java b/repository/src/test/java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java index d461baa809..59ebfabe32 100644 --- a/repository/src/test/java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java +++ b/repository/src/test/java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java @@ -31,8 +31,16 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.transaction.HeuristicMixedException; +import javax.transaction.HeuristicRollbackException; +import javax.transaction.NotSupportedException; +import javax.transaction.RollbackException; import javax.transaction.Status; +import javax.transaction.SystemException; import javax.transaction.UserTransaction; import junit.framework.TestCase; @@ -54,6 +62,7 @@ import org.alfresco.repo.security.permissions.SimpleAccessControlEntry; import org.alfresco.repo.security.permissions.SimpleAccessControlListProperties; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeRef; @@ -71,9 +80,14 @@ import org.alfresco.test_category.OwnJVMTestsCategory; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.EqualsHelper; import org.alfresco.util.testing.category.DBTests; +import org.alfresco.util.transaction.TransactionListenerAdapter; +import org.awaitility.Awaitility; import org.junit.experimental.categories.Category; import org.springframework.context.ApplicationContext; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.equalTo; + @Category({OwnJVMTestsCategory.class, DBTests.class}) public class AclDaoComponentTest extends TestCase { @@ -244,6 +258,37 @@ public class AclDaoComponentTest extends TestCase assertEquals(aclProps.getInherits(), Boolean.TRUE); assertEquals(aclDaoComponent.getAccessControlListProperties(aclProps.getId()), aclProps); } + + public void testGetCurrentACLChangeSet() + throws HeuristicRollbackException, RollbackException, HeuristicMixedException, SystemException + { + + SimpleAccessControlListProperties properties = new SimpleAccessControlListProperties(); + properties.setAclType(ACLType.DEFINING); + properties.setVersioned(true); + Long id = aclDaoComponent.createAccessControlList(properties).getId(); + + AccessControlListProperties aclProps = aclDaoComponent.getAccessControlListProperties(id); + assertEquals(aclProps.getAclType(), ACLType.DEFINING); + assertEquals(aclProps.getAclVersion(), Long.valueOf(1l)); + assertEquals(aclProps.getInherits(), Boolean.TRUE); + + AtomicBoolean afterCommit = new AtomicBoolean(); + AlfrescoTransactionSupport.bindListener(new TransactionListenerAdapter() { + @Override + public void afterCommit() + { + //The commit time is available only after a transaction is committed + assertNotNull(aclDaoComponent.getCurrentChangeSetCommitTime()); + afterCommit.set(true); + } + }); + + testTX.commit(); + await("Commit time not null") + .atMost(3, TimeUnit.SECONDS) + .untilAtomic(afterCommit, equalTo(true)); + } public void testCreateShared() {