Feature/repo 5181 implement child assoc events (#1030)

Co-authored-by: Chris Shields <christopher.shields@alfresco.com>
Co-authored-by: Adina Ababei <adina.ababei@ness.com>
Co-authored-by: aftene <iulian.aftene@ness.com>
This commit is contained in:
Sara
2020-06-17 12:02:17 +01:00
committed by GitHub
parent bc7bdca3db
commit 1e2330386a
22 changed files with 2476 additions and 109 deletions

View File

@@ -70,7 +70,7 @@
<dependency.jackson.version>2.11.0</dependency.jackson.version>
<dependency.jackson-databind.version>2.10.2</dependency.jackson-databind.version>
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
<dependency.acs-event-model.version>0.0.2</dependency.acs-event-model.version>
<dependency.acs-event-model.version>0.0.6</dependency.acs-event-model.version>
<dependency.awaitility.version>4.0.2</dependency.awaitility.version>
</properties>

View File

@@ -0,0 +1,170 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import java.util.ArrayDeque;
import java.util.Deque;
import org.alfresco.repo.event.v1.model.ChildAssociationResource;
import org.alfresco.repo.event.v1.model.EventData;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.namespace.QName;
/**
* Encapsulates child association events that occurred in a single transaction.
*
* @author Chris Shields
* @author Sara Aspery
*/
public class ChildAssociationEventConsolidator implements ChildAssociationEventSupportedPolicies
{
private final Deque<EventType> eventTypes;
private final ChildAssociationRef childAssociationRef;
private ChildAssociationResource resource;
private final QNameHelper helper;
public ChildAssociationEventConsolidator(ChildAssociationRef childAssociationRef, QNameHelper helper)
{
this.eventTypes = new ArrayDeque<>();
this.childAssociationRef = childAssociationRef;
this.helper = helper;
this.resource = buildChildAssociationResource(this.childAssociationRef);
}
/**
* Builds and returns the {@link RepoEvent} instance.
*
* @param eventInfo the object holding the event information
* @return the {@link RepoEvent} instance
*/
public RepoEvent<ChildAssociationResource> getRepoEvent(EventInfo eventInfo)
{
EventType eventType = getDerivedEvent();
EventData.Builder<ChildAssociationResource> eventDataBuilder = EventData.<ChildAssociationResource>builder()
.setEventGroupId(eventInfo.getTxnId())
.setResource(resource);
EventData<ChildAssociationResource> eventData = eventDataBuilder.build();
return RepoEvent.<ChildAssociationResource>builder()
.setId(eventInfo.getId())
.setSource(eventInfo.getSource())
.setTime(eventInfo.getTimestamp())
.setType(eventType.getType())
.setData(eventData)
.build();
}
/**
* Add child association created event on create of a child association.
*
* @param childAssociationRef ChildAssociationRef
*/
@Override
public void onCreateChildAssociation(ChildAssociationRef childAssociationRef, boolean isNewNode)
{
eventTypes.add(EventType.CHILD_ASSOC_CREATED);
resource = buildChildAssociationResource(childAssociationRef);
}
/**
* Add child association deleted event on delete of a child association.
*
* @param childAssociationRef ChildAssociationRef
*/
@Override
public void beforeDeleteChildAssociation(ChildAssociationRef childAssociationRef)
{
eventTypes.add(EventType.CHILD_ASSOC_DELETED);
resource = buildChildAssociationResource(childAssociationRef);
}
private ChildAssociationResource buildChildAssociationResource(ChildAssociationRef childAssociationRef)
{
String parentId = childAssociationRef.getParentRef().getId();
String childId = childAssociationRef.getChildRef().getId();
String assocType = helper.getQNamePrefixString(childAssociationRef.getTypeQName());
return new ChildAssociationResource(parentId, childId, assocType);
}
/**
* @return a derived event for a transaction.
*/
private EventType getDerivedEvent()
{
if (isTemporaryChildAssociation())
{
// This event will be filtered out, but we set the correct
// event type anyway for debugging purposes
return EventType.CHILD_ASSOC_DELETED;
}
else if (eventTypes.contains(EventType.CHILD_ASSOC_CREATED))
{
return EventType.CHILD_ASSOC_CREATED;
}
else if (eventTypes.getLast() == EventType.CHILD_ASSOC_DELETED)
{
return EventType.CHILD_ASSOC_DELETED;
}
else
{
// Default to first event
return eventTypes.getFirst();
}
}
/**
* Whether or not the child association has been created and then deleted, i.e. a temporary child association.
*
* @return {@code true} if the child association has been created and then deleted, otherwise false
*/
public boolean isTemporaryChildAssociation()
{
return eventTypes.contains(EventType.CHILD_ASSOC_CREATED) && eventTypes.getLast() == EventType.CHILD_ASSOC_DELETED;
}
/**
* Get child association type.
*
* @return QName the child association type
*/
public QName getChildAssocType()
{
return childAssociationRef.getTypeQName();
}
/**
* Get event types.
*
* @return Deque<EventType> queue of event types
*/
public Deque<EventType> getEventTypes()
{
return eventTypes;
}
}

View File

@@ -0,0 +1,41 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import org.alfresco.repo.node.NodeServicePolicies;
/**
* Event generator supported policies.
*
* @author Chris Shields
* @author Sara Aspery
*/
public interface ChildAssociationEventSupportedPolicies extends NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.BeforeDeleteChildAssociationPolicy
{
}

View File

@@ -28,6 +28,7 @@ package org.alfresco.repo.event2;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
@@ -57,8 +58,8 @@ public class EventConsolidator implements EventSupportedPolicies
{
private final NodeResourceHelper helper;
private final Deque<EventType> eventTypes;
private final Set<QName> aspectsAdded;
private final Set<QName> aspectsRemoved;
private final List<QName> aspectsAdded;
private final List<QName> aspectsRemoved;
private NodeResource.Builder resourceBuilder;
private Map<QName, Serializable> propertiesBefore;
@@ -67,13 +68,14 @@ public class EventConsolidator implements EventSupportedPolicies
private QName nodeType;
private QName nodeTypeBefore;
private List<String> primaryHierarchyBefore;
private boolean resourceBeforeAllFieldsNull = true;
public EventConsolidator(NodeResourceHelper nodeResourceHelper)
{
this.helper = nodeResourceHelper;
this.eventTypes = new ArrayDeque<>();
this.aspectsAdded = new HashSet<>();
this.aspectsRemoved = new HashSet<>();
this.aspectsAdded = new ArrayList<>();
this.aspectsRemoved = new ArrayList<>();
}
/**
@@ -202,18 +204,42 @@ public class EventConsolidator implements EventSupportedPolicies
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
{
eventTypes.add(EventType.NODE_UPDATED);
aspectsAdded.add(aspectTypeQName);
addAspect(aspectTypeQName);
createBuilderIfAbsent(nodeRef);
}
void addAspect(QName aspectTypeQName)
{
if (aspectsRemoved.contains(aspectTypeQName))
{
aspectsRemoved.remove(aspectTypeQName);
}
else
{
aspectsAdded.add(aspectTypeQName);
}
}
@Override
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
{
eventTypes.add(EventType.NODE_UPDATED);
aspectsRemoved.add(aspectTypeQName);
removeAspect(aspectTypeQName);
createBuilderIfAbsent(nodeRef);
}
void removeAspect(QName aspectTypeQName)
{
if (aspectsAdded.contains(aspectTypeQName))
{
aspectsAdded.remove(aspectTypeQName);
}
else
{
aspectsRemoved.add(aspectTypeQName);
}
}
private void setAfterProperties(Map<QName, Serializable> after)
{
propertiesAfter = after;
@@ -265,7 +291,7 @@ public class EventConsolidator implements EventSupportedPolicies
{
return null;
}
Builder builder = NodeResource.builder();
Map<QName, Serializable> changedPropsBefore = getBeforeMapChanges(propertiesBefore, propertiesAfter);
@@ -276,27 +302,33 @@ public class EventConsolidator implements EventSupportedPolicies
if (!mappedProps.isEmpty())
{
builder.setProperties(mappedProps);
resourceBeforeAllFieldsNull = false;
}
String name = (String) changedPropsBefore.get(ContentModel.PROP_NAME);
if (name != null)
{
builder.setName(name);
resourceBeforeAllFieldsNull = false;
}
ContentInfo contentInfo = helper.getContentInfo(changedPropsBefore);
if (contentInfo != null)
{
builder.setContent(contentInfo);
resourceBeforeAllFieldsNull = false;
}
UserInfo modifier = helper.getUserInfo((String) changedPropsBefore.get(ContentModel.PROP_MODIFIER));
if (modifier != null)
{
builder.setModifiedByUser(modifier);
resourceBeforeAllFieldsNull = false;
}
ZonedDateTime modifiedAt =
helper.getZonedDateTime((Date) changedPropsBefore.get(ContentModel.PROP_MODIFIED));
if (modifiedAt != null)
{
builder.setModifiedAt(modifiedAt);
resourceBeforeAllFieldsNull = false;
}
}
@@ -304,22 +336,25 @@ public class EventConsolidator implements EventSupportedPolicies
if (!aspectsBefore.isEmpty())
{
builder.setAspectNames(aspectsBefore);
resourceBeforeAllFieldsNull = false;
}
if (primaryHierarchyBefore != null && !primaryHierarchyBefore.isEmpty())
{
builder.setPrimaryHierarchy(primaryHierarchyBefore);
resourceBeforeAllFieldsNull = false;
}
if (nodeTypeBefore != null)
{
builder.setNodeType(helper.getQNamePrefixString(nodeTypeBefore));
resourceBeforeAllFieldsNull = false;
}
return builder.build();
}
private Set<String> getMappedAspectsBefore(Set<String> currentAspects)
Set<String> getMappedAspectsBefore(Set<String> currentAspects)
{
if (currentAspects == null)
{
@@ -329,17 +364,21 @@ public class EventConsolidator implements EventSupportedPolicies
{
Set<String> removed = helper.mapToNodeAspects(aspectsRemoved);
Set<String> added = helper.mapToNodeAspects(aspectsAdded);
Set<String> before = new HashSet<>(currentAspects);
if (!removed.isEmpty())
Set<String> before = new HashSet<>();
if (!removed.isEmpty() || !added.isEmpty())
{
// Add all the removed aspects from the current list
before.addAll(removed);
}
if (!added.isEmpty())
{
// Remove all the added aspects from the current list
before.removeAll(added);
before = new HashSet<>(currentAspects);
if (!removed.isEmpty())
{
// Add all the removed aspects from the current list
before.addAll(removed);
}
if (!added.isEmpty())
{
// Remove all the added aspects from the current list
before.removeAll(added);
}
}
return before;
}
@@ -348,7 +387,12 @@ public class EventConsolidator implements EventSupportedPolicies
private boolean hasChangedAspect()
{
return !(aspectsRemoved.isEmpty() && aspectsAdded.isEmpty());
if ((aspectsRemoved.isEmpty() && aspectsAdded.isEmpty()) ||
org.apache.commons.collections.CollectionUtils.isEqualCollection(aspectsAdded, aspectsRemoved))
{
return false;
}
return true;
}
private <K, V> Map<K, V> getBeforeMapChanges(Map<K, V> before, Map<K, V> after)
@@ -413,4 +457,20 @@ public class EventConsolidator implements EventSupportedPolicies
{
return eventTypes;
}
}
public List<QName> getAspectsAdded()
{
return aspectsAdded;
}
public List<QName> getAspectsRemoved()
{
return aspectsRemoved;
}
public boolean isResourceBeforeAllFieldsNull()
{
return resourceBeforeAllFieldsNull;
}
}

View File

@@ -28,16 +28,22 @@ package org.alfresco.repo.event2;
import java.io.Serializable;
import java.net.URI;
import java.time.ZonedDateTime;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.repo.event2.filter.ChildAssociationTypeFilter;
import org.alfresco.repo.event2.filter.EventFilterRegistry;
import org.alfresco.repo.event2.filter.EventUserFilter;
import org.alfresco.repo.event2.filter.NodeTypeFilter;
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteAssociationPolicy;
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteChildAssociationPolicy;
import org.alfresco.repo.node.NodeServicePolicies.BeforeDeleteNodePolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnCreateAssociationPolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnCreateChildAssociationPolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnDownloadNodePolicy;
import org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy;
@@ -50,6 +56,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -71,7 +78,9 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean;
*
* @author Jamal Kaabi-Mofrad
*/
public class EventGenerator extends AbstractLifecycleBean implements InitializingBean, EventSupportedPolicies
public class EventGenerator extends AbstractLifecycleBean implements InitializingBean, EventSupportedPolicies,
ChildAssociationEventSupportedPolicies,
PeerAssociationEventSupportedPolicies
{
private static final Log LOGGER = LogFactory.getLog(EventGenerator.class);
@@ -86,8 +95,10 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
private PersonService personService;
private NodeTypeFilter nodeTypeFilter;
private ChildAssociationTypeFilter childAssociationTypeFilter;
private EventUserFilter userFilter;
private NodeResourceHelper nodeResourceHelper;
private QNameHelper qNameHelper;
private final EventTransactionListener transactionListener = new EventTransactionListener();
@Override
@@ -104,10 +115,13 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
PropertyCheck.mandatory(this, "personService", personService);
this.nodeTypeFilter = eventFilterRegistry.getNodeTypeFilter();
this.childAssociationTypeFilter = eventFilterRegistry.getChildAssociationTypeFilter();
this.userFilter = eventFilterRegistry.getEventUserFilter();
this.nodeResourceHelper = new NodeResourceHelper(nodeService, namespaceService, dictionaryService,
personService,
eventFilterRegistry);
this.qNameHelper = new QNameHelper(namespaceService);
this.nodeResourceHelper = new NodeResourceHelper(nodeService, dictionaryService,
personService,
eventFilterRegistry,
qNameHelper);
}
private void bindBehaviours()
@@ -128,6 +142,14 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
new JavaBehaviour(this, "onMoveNode"));
policyComponent.bindClassBehaviour(OnDownloadNodePolicy.QNAME, this,
new JavaBehaviour(this, "onDownloadNode"));
policyComponent.bindAssociationBehaviour(OnCreateChildAssociationPolicy.QNAME, this,
new JavaBehaviour(this, "onCreateChildAssociation"));
policyComponent.bindAssociationBehaviour(BeforeDeleteChildAssociationPolicy.QNAME, this,
new JavaBehaviour(this, "beforeDeleteChildAssociation"));
policyComponent.bindAssociationBehaviour(OnCreateAssociationPolicy.QNAME, this,
new JavaBehaviour(this, "onCreateAssociation"));
policyComponent.bindAssociationBehaviour(BeforeDeleteAssociationPolicy.QNAME, this,
new JavaBehaviour(this, "beforeDeleteAssociation"));
}
public void setPolicyComponent(PolicyComponent policyComponent)
@@ -204,7 +226,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
{
getEventConsolidator(nodeRef).onDownloadNode(nodeRef);
}
@Override
public void beforeDeleteNode(NodeRef nodeRef)
{
@@ -223,13 +245,38 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
getEventConsolidator(nodeRef).onRemoveAspect(nodeRef, aspectTypeQName);
}
@Override
public void onCreateChildAssociation(ChildAssociationRef childAssociationRef, boolean isNewNode)
{
getEventConsolidator(childAssociationRef).onCreateChildAssociation(childAssociationRef, isNewNode);
}
@Override
public void beforeDeleteChildAssociation(ChildAssociationRef childAssociationRef)
{
getEventConsolidator(childAssociationRef).beforeDeleteChildAssociation(childAssociationRef);
}
@Override
public void onCreateAssociation(AssociationRef associationRef)
{
getEventConsolidator(associationRef).onCreateAssociation(associationRef);
}
@Override
public void beforeDeleteAssociation(AssociationRef associationRef)
{
getEventConsolidator(associationRef).beforeDeleteAssociation(associationRef);
}
/**
* @return the {@link EventConsolidator} for the supplied {@code nodeRef} from
* the current transaction context.
*/
private EventConsolidator getEventConsolidator(NodeRef nodeRef)
{
Map<NodeRef, EventConsolidator> nodeEvents = getTxnResourceMap(transactionListener);
Consolidators consolidators = getTxnConsolidators(transactionListener);
Map<NodeRef, EventConsolidator> nodeEvents = consolidators.getNodes();
if (nodeEvents.isEmpty())
{
AlfrescoTransactionSupport.bindListener(transactionListener);
@@ -244,15 +291,60 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
return eventConsolidator;
}
private Map<NodeRef, EventConsolidator> getTxnResourceMap(Object resourceKey)
private Consolidators getTxnConsolidators(Object resourceKey)
{
Map<NodeRef, EventConsolidator> map = AlfrescoTransactionSupport.getResource(resourceKey);
if (map == null)
Consolidators consolidators = AlfrescoTransactionSupport.getResource(resourceKey);
if (consolidators == null)
{
map = new LinkedHashMap<>(29);
AlfrescoTransactionSupport.bindResource(resourceKey, map);
consolidators = new Consolidators();
AlfrescoTransactionSupport.bindResource(resourceKey, consolidators);
}
return map;
return consolidators;
}
/**
* @return the {@link EventConsolidator} for the supplied {@code childAssociationRef} from
* the current transaction context.
*/
private ChildAssociationEventConsolidator getEventConsolidator(ChildAssociationRef childAssociationRef)
{
Consolidators consolidators = getTxnConsolidators(transactionListener);
Map<ChildAssociationRef, ChildAssociationEventConsolidator> assocEvents = consolidators.getChildAssocs();
if (assocEvents.isEmpty())
{
AlfrescoTransactionSupport.bindListener(transactionListener);
}
ChildAssociationEventConsolidator eventConsolidator = assocEvents.get(childAssociationRef);
if (eventConsolidator == null)
{
eventConsolidator = new ChildAssociationEventConsolidator(childAssociationRef, qNameHelper);
assocEvents.put(childAssociationRef, eventConsolidator);
}
return eventConsolidator;
}
/**
* @return the {@link EventConsolidator} for the supplied {@code peerAssociationRef} from
* the current transaction context.
*/
private PeerAssociationEventConsolidator getEventConsolidator(AssociationRef peerAssociationRef)
{
Consolidators consolidators = getTxnConsolidators(transactionListener);
Map<AssociationRef, PeerAssociationEventConsolidator> assocEvents = consolidators.getPeerAssocs();
if (assocEvents.isEmpty())
{
AlfrescoTransactionSupport.bindListener(transactionListener);
}
PeerAssociationEventConsolidator eventConsolidator = assocEvents.get(peerAssociationRef);
if (eventConsolidator == null)
{
eventConsolidator = new PeerAssociationEventConsolidator(peerAssociationRef, qNameHelper);
assocEvents.put(peerAssociationRef, eventConsolidator);
}
return eventConsolidator;
}
private boolean isFiltered(QName nodeType, String user)
@@ -260,6 +352,11 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
return (nodeTypeFilter.isExcluded(nodeType) || (userFilter.isExcluded(user)));
}
private boolean isFilteredChildAssociation(QName childAssocType, String user)
{
return (childAssociationTypeFilter.isExcluded(childAssocType) || (userFilter.isExcluded(user)));
}
private EventInfo getEventInfo(String user)
{
return new EventInfo().setTimestamp(ZonedDateTime.now())
@@ -269,47 +366,6 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
.setSource(URI.create("/" + descriptorService.getCurrentRepositoryDescriptor().getId()));
}
private void sendEvent(NodeRef nodeRef, EventConsolidator consolidator)
{
if (consolidator.isTemporaryNode())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring temporary node: " + nodeRef);
}
return;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest node info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
final QName nodeType = consolidator.getNodeType();
if (isFiltered(nodeType, user))
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("EventFilter - Excluding node: '" + nodeRef + "' of type: '"
+ ((nodeType == null) ? "Unknown' " : nodeType.toPrefixString())
+ "' created by: " + user);
}
return;
}
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("List of Events:" + consolidator.getEventTypes());
LOGGER.trace("Sending event:" + event);
}
// Need to execute this in another read txn because Camel expects it
transactionService.getRetryingTransactionHelper().doInTransaction((RetryingTransactionCallback<Void>) () -> {
event2MessageProducer.send(event);
return null;
}, true, false);
}
@Override
protected void onBootstrap(ApplicationEvent applicationEvent)
{
@@ -329,18 +385,184 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
{
try
{
final Map<NodeRef, EventConsolidator> changedNodes = getTxnResourceMap(this);
for (Map.Entry<NodeRef, EventConsolidator> entry : changedNodes.entrySet())
final Consolidators consolidators = getTxnConsolidators(this);
// Node events
for (Map.Entry<NodeRef, EventConsolidator> entry : consolidators.getNodes().entrySet())
{
EventConsolidator eventConsolidator = entry.getValue();
sendEvent(entry.getKey(), eventConsolidator);
}
}
// Child assoc events
for (Map.Entry<ChildAssociationRef, ChildAssociationEventConsolidator> entry : consolidators.getChildAssocs().entrySet())
{
ChildAssociationEventConsolidator eventConsolidator = entry.getValue();
sendEvent(entry.getKey(), eventConsolidator);
}
// Peer assoc events
for (Map.Entry<AssociationRef, PeerAssociationEventConsolidator> 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);
}
}
private void sendEvent(NodeRef nodeRef, EventConsolidator consolidator)
{
if (consolidator.isTemporaryNode())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring temporary node: " + nodeRef);
}
return;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest node info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
final QName nodeType = consolidator.getNodeType();
if (isFiltered(nodeType, user))
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("EventFilter - Excluding node: '" + nodeRef + "' of type: '"
+ ((nodeType == null) ? "Unknown' " : nodeType.toPrefixString())
+ "' created by: " + user);
}
return;
}
if (event.getType().equals(EventType.NODE_UPDATED.getType()) && consolidator.isResourceBeforeAllFieldsNull())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring node updated event as no fields have been updated: " + nodeRef);
}
return;
}
logAndSendEvent(event, consolidator.getEventTypes());
}
private void sendEvent(ChildAssociationRef childAssociationRef, ChildAssociationEventConsolidator consolidator)
{
if (consolidator.isTemporaryChildAssociation())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring temporary child association: " + childAssociationRef);
}
return;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest association info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
final QName childAssocType = consolidator.getChildAssocType();
if (isFilteredChildAssociation(childAssocType, user))
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("EventFilter - Excluding child association: '" + childAssociationRef + "' of type: '"
+ ((childAssocType == null) ? "Unknown' " : childAssocType.toPrefixString())
+ "' created by: " + user);
}
return;
} else if (childAssociationRef.isPrimary())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("EventFilter - Excluding primary child association: '" + childAssociationRef + "' of type: '"
+ ((childAssocType == null) ? "Unknown' " : childAssocType.toPrefixString())
+ "' created by: " + user);
}
return;
}
logAndSendEvent(event, consolidator.getEventTypes());
}
private void sendEvent(AssociationRef peerAssociationRef, PeerAssociationEventConsolidator consolidator)
{
if (consolidator.isTemporaryPeerAssociation())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring temporary peer association: " + peerAssociationRef);
}
return;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest association info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
logAndSendEvent(event, consolidator.getEventTypes());
}
private void logAndSendEvent(RepoEvent<?> event, Deque<EventType> listOfEvents)
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("List of Events:" + listOfEvents);
LOGGER.trace("Sending event:" + event);
}
// Need to execute this in another read txn because Camel expects it
transactionService.getRetryingTransactionHelper().doInTransaction((RetryingTransactionCallback<Void>) () -> {
event2MessageProducer.send(event);
return null;
}, true, false);
}
}
private static class Consolidators
{
private Map<NodeRef, EventConsolidator> nodes;
private Map<ChildAssociationRef, ChildAssociationEventConsolidator> childAssocs;
private Map<AssociationRef, PeerAssociationEventConsolidator> peerAssocs;
public Map<NodeRef, EventConsolidator> getNodes()
{
if (nodes == null)
{
nodes = new LinkedHashMap<>(29);
}
return nodes;
}
public Map<ChildAssociationRef, ChildAssociationEventConsolidator> getChildAssocs()
{
if (childAssocs == null)
{
childAssocs = new LinkedHashMap<>(29);
}
return childAssocs;
}
public Map<AssociationRef, PeerAssociationEventConsolidator> getPeerAssocs()
{
if (peerAssocs == null)
{
peerAssocs = new LinkedHashMap<>(29);
}
return peerAssocs;
}
}
}

View File

@@ -32,21 +32,23 @@ package org.alfresco.repo.event2;
*/
public enum EventType
{
NODE_CREATED("Created"), NODE_UPDATED("Updated"), NODE_DELETED("Deleted"), NODE_DOWNLOADED("Downloaded");
NODE_CREATED(EventTypeConst.CREATED, ContextType.NODE), NODE_UPDATED(EventTypeConst.UPDATED, ContextType.NODE), NODE_DELETED(EventTypeConst.DELETED, ContextType.NODE), NODE_DOWNLOADED("Downloaded", ContextType.NODE),
CHILD_ASSOC_CREATED(EventTypeConst.CREATED, ContextType.CHILD_ASSOC), CHILD_ASSOC_DELETED(EventTypeConst.DELETED, ContextType.CHILD_ASSOC),
PEER_ASSOC_CREATED(EventTypeConst.CREATED, ContextType.PEER_ASSOC), PEER_ASSOC_DELETED(EventTypeConst.DELETED, ContextType.PEER_ASSOC);
private static final String PREFIX = "org.alfresco.event.";
private static final String CONTEXT = "node.";
private String type;
private ContextType contextType;
EventType(String type)
EventType(String type, ContextType contextType)
{
this.type = type;
this.contextType = contextType;
}
// Should be overridden if a type requires different context. E.g. auth
/* package*/ String getContext()
{
return CONTEXT;
return contextType.getContext();
}
@Override
@@ -64,5 +66,27 @@ public enum EventType
{
return toString();
}
private enum ContextType
{
NODE("node."), CHILD_ASSOC("assoc.child."), PEER_ASSOC("assoc.peer.");
private String context;
ContextType(String context)
{
this.context = context;
}
String getContext()
{
return context;
}
}
private static class EventTypeConst
{
private static final String CREATED = "Created";
private static final String UPDATED = "Updated";
private static final String DELETED = "Deleted";
}
}

View File

@@ -28,8 +28,7 @@ package org.alfresco.repo.event2;
import java.io.Serializable;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -54,8 +53,6 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.security.NoSuchPersonException;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PathUtil;
import org.apache.commons.logging.Log;
@@ -71,22 +68,23 @@ public class NodeResourceHelper
private static final Log LOGGER = LogFactory.getLog(NodeResourceHelper.class);
private final NodeService nodeService;
private final NamespaceService namespaceService;
private final DictionaryService dictionaryService;
private final PersonService personService;
private final NodeAspectFilter nodeAspectFilter;
private final NodePropertyFilter nodePropertyFilter;
private final QNameHelper qNameHelper;
public NodeResourceHelper(NodeService nodeService, NamespaceService namespaceService,
public NodeResourceHelper(NodeService nodeService,
DictionaryService dictionaryService, PersonService personService,
EventFilterRegistry eventFilterRegistry)
EventFilterRegistry eventFilterRegistry,
QNameHelper qNameHelper)
{
this.nodeService = nodeService;
this.namespaceService = namespaceService;
this.dictionaryService = dictionaryService;
this.personService = personService;
this.nodeAspectFilter = eventFilterRegistry.getNodeAspectFilter();
this.nodePropertyFilter = eventFilterRegistry.getNodePropertyFilter();
this.qNameHelper = qNameHelper;
}
public NodeResource.Builder createNodeResourceBuilder(NodeRef nodeRef)
@@ -227,19 +225,10 @@ public class NodeResourceHelper
// returns it in the form {uri}local.
public String getQNamePrefixString(QName k)
{
String key;
try
{
key = k.toPrefixString(namespaceService);
}
catch (NamespaceException e)
{
key = k.toString();
}
return key;
return qNameHelper.getQNamePrefixString(k);
}
public Set<String> mapToNodeAspects(Set<QName> aspects)
public Set<String> mapToNodeAspects(Collection<QName> aspects)
{
Set<String> filteredAspects = new HashSet<>(aspects.size());

View File

@@ -0,0 +1,170 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import java.util.ArrayDeque;
import java.util.Deque;
import org.alfresco.repo.event.v1.model.EventData;
import org.alfresco.repo.event.v1.model.PeerAssociationResource;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.namespace.QName;
/**
* Encapsulates peer association events occurred in a single transaction.
*
* @author Sara Aspery
*/
public class PeerAssociationEventConsolidator implements PeerAssociationEventSupportedPolicies
{
private final Deque<EventType> eventTypes;
private final AssociationRef associationRef;
private PeerAssociationResource resource;
private final QNameHelper helper;
public PeerAssociationEventConsolidator(AssociationRef associationRef, QNameHelper helper)
{
this.eventTypes = new ArrayDeque<>();
this.associationRef = associationRef;
this.helper = helper;
}
/**
* Builds and returns the {@link RepoEvent} instance.
*
* @param eventInfo the object holding the event information
* @return the {@link RepoEvent} instance
*/
public RepoEvent<PeerAssociationResource> getRepoEvent(EventInfo eventInfo)
{
EventType eventType = getDerivedEvent();
EventData.Builder<PeerAssociationResource> eventDataBuilder = EventData.<PeerAssociationResource>builder()
.setEventGroupId(eventInfo.getTxnId())
.setResource(resource);
EventData<PeerAssociationResource> eventData = eventDataBuilder.build();
return RepoEvent.<PeerAssociationResource>builder()
.setId(eventInfo.getId())
.setSource(eventInfo.getSource())
.setTime(eventInfo.getTimestamp())
.setType(eventType.getType())
.setData(eventData)
.build();
}
/**
* Add peer association created event on create of a peer association.
*
* @param associationRef AssociationRef
*/
@Override
public void onCreateAssociation(AssociationRef associationRef)
{
eventTypes.add(EventType.PEER_ASSOC_CREATED);
resource = buildPeerAssociationResource(associationRef);
}
/**
* Add peer association deleted event on delete of a peer association.
*
* @param associationRef AssociationRef
*/
@Override
public void beforeDeleteAssociation(AssociationRef associationRef)
{
eventTypes.add(EventType.PEER_ASSOC_DELETED);
resource = buildPeerAssociationResource(associationRef);
}
private PeerAssociationResource buildPeerAssociationResource(AssociationRef associationRef)
{
String sourceId = associationRef.getSourceRef().getId();
String targetId = associationRef.getTargetRef().getId();
String assocType = helper.getQNamePrefixString(associationRef.getTypeQName());
return new PeerAssociationResource(sourceId, targetId, assocType);
}
/**
* @return a derived event for a transaction.
*/
private EventType getDerivedEvent()
{
if (isTemporaryPeerAssociation())
{
// This event will be filtered out, but we set the correct
// event type anyway for debugging purposes
return EventType.PEER_ASSOC_DELETED;
}
else if (eventTypes.contains(EventType.PEER_ASSOC_CREATED))
{
return EventType.PEER_ASSOC_CREATED;
}
else if (eventTypes.getLast() == EventType.PEER_ASSOC_DELETED)
{
return EventType.PEER_ASSOC_DELETED;
}
else
{
// Default to first event
return eventTypes.getFirst();
}
}
/**
* Whether or not the association has been created and then deleted, i.e. a temporary association.
*
* @return {@code true} if the association has been created and then deleted, otherwise false
*/
public boolean isTemporaryPeerAssociation()
{
return eventTypes.contains(EventType.PEER_ASSOC_CREATED) && eventTypes.getLast() == EventType.PEER_ASSOC_DELETED;
}
/**
* Get peer association type.
*
* @return QName the peer association type
*/
public QName getAssocType()
{
return associationRef.getTypeQName();
}
/**
* Get event types.
*
* @return Deque<EventType> queue of event types
*/
public Deque<EventType> getEventTypes()
{
return eventTypes;
}
}

View File

@@ -0,0 +1,39 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import org.alfresco.repo.node.NodeServicePolicies;
/**
* Event generator supported policies for peer associations.
*
* @author Sara Aspery
*/
public interface PeerAssociationEventSupportedPolicies extends NodeServicePolicies.OnCreateAssociationPolicy,
NodeServicePolicies.BeforeDeleteAssociationPolicy
{
}

View File

@@ -0,0 +1,66 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
/**
* Helper for {@link QName} objects.
*
* @author Sara Aspery
*/
public class QNameHelper
{
private final NamespaceService namespaceService;
public QNameHelper(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* Returns the QName in the format prefix:local, but in the exceptional case where there is no registered prefix
* returns it in the form {uri}local.
*
* @param k QName
* @return a String representing the QName in the format prefix:local or {uri}local.
*/
public String getQNamePrefixString(QName k)
{
String key;
try
{
key = k.toPrefixString(namespaceService);
}
catch (NamespaceException e)
{
key = k.toString();
}
return key;
}
}

View File

@@ -0,0 +1,63 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2.filter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.alfresco.service.namespace.QName;
/**
* Implementation of the child association types filter.
*
* @author Sara Aspery
*/
public class ChildAssociationTypeFilter extends AbstractNodeEventFilter
{
private final List<String> assocTypesBlackList;
public ChildAssociationTypeFilter(String filteredChildAssocTypes)
{
this.assocTypesBlackList = parseFilterList(filteredChildAssocTypes);
}
/**
*
* @see org.alfresco.repo.event2.filter.AbstractNodeEventFilter#getExcludedTypes()
*/
@Override
public Set<QName> getExcludedTypes()
{
Set<QName> result = new HashSet<>();
// add child association types defined in repository.properties/alfresco-global.properties
assocTypesBlackList.forEach(childAssocType -> result.addAll(expandTypeDef(childAssocType)));
return result;
}
}

View File

@@ -85,6 +85,11 @@ public class EventFilterRegistry implements BeanFactoryAware
return getFilter("event2NodePropertyFilter", NodePropertyFilter.class);
}
public ChildAssociationTypeFilter getChildAssociationTypeFilter()
{
return getFilter("event2ChildAssociationTypeFilter", ChildAssociationTypeFilter.class);
}
public EventUserFilter getEventUserFilter()
{
return getFilter("event2UserFilter", EventUserFilter.class);

View File

@@ -25,6 +25,10 @@
<constructor-arg value="${repo.event2.filter.users}"/>
<constructor-arg type="boolean" value="${user.name.caseSensitive}" />
</bean>
<bean id="event2ChildAssociationTypeFilter" class="org.alfresco.repo.event2.filter.ChildAssociationTypeFilter" parent="abstractNodeEventFilter">
<constructor-arg value="${repo.event2.filter.childAssocTypes}"/>
</bean>
<!-- End of Event2 Filters -->
<bean id="eventGeneratorV2" class="org.alfresco.repo.event2.EventGenerator">

View File

@@ -1314,7 +1314,8 @@ contentPropertyRestrictions.whitelist=
# Type and aspect filters which should be excluded
# Note: System folders node types are added by default
repo.event2.filter.nodeTypes=sys:*, fm:*, cm:thumbnail, cm:failedThumbnail, cm:rating, rma:rmsite include_subtypes
repo.event2.filter.nodeAspects=
repo.event2.filter.nodeAspects=sys:*
repo.event2.filter.childAssocTypes=rn:rendition
# Comma separated list of users which should be excluded
# Note: username's case-sensitivity depends on the {user.name.caseSensitive} setting
repo.event2.filter.users=System, null

View File

@@ -205,7 +205,8 @@ import org.junit.runners.Suite;
org.alfresco.transform.client.registry.TransformServiceRegistryConfigTest.class,
org.alfresco.repo.event2.EventFilterTest.class,
org.alfresco.repo.node.DownloadNotifierServiceImplUnitTest.class
org.alfresco.repo.node.DownloadNotifierServiceImplUnitTest.class,
org.alfresco.repo.event2.EventConsolidatorUnitTest.class
})
public class AllUnitTestsSuite
{

View File

@@ -37,8 +37,10 @@ import javax.jms.ConnectionFactory;
import org.alfresco.model.ContentModel;
import org.alfresco.opencmis.CMISConnector;
import org.alfresco.repo.event.databind.ObjectMapperFactory;
import org.alfresco.repo.event.v1.model.ChildAssociationResource;
import org.alfresco.repo.event.v1.model.EventData;
import org.alfresco.repo.event.v1.model.NodeResource;
import org.alfresco.repo.event.v1.model.PeerAssociationResource;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
@@ -237,6 +239,28 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
return resource;
}
protected ChildAssociationResource getChildAssocResource(RepoEvent repoEvent)
{
assertNotNull(repoEvent);
EventData<ChildAssociationResource> eventData = repoEvent.getData();
assertNotNull(eventData);
ChildAssociationResource resource = eventData.getResource();
assertNotNull(resource);
return resource;
}
protected PeerAssociationResource getPeerAssocResource(RepoEvent repoEvent)
{
assertNotNull(repoEvent);
EventData<PeerAssociationResource> eventData = repoEvent.getData();
assertNotNull(eventData);
PeerAssociationResource resource = eventData.getResource();
assertNotNull(resource);
return resource;
}
protected NodeResource getNodeResourceBefore(int eventSequenceNumber)
{
EventData<NodeResource> eventData = getEventData(eventSequenceNumber);
@@ -339,6 +363,15 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
events.clear();
}
}
public List<RepoEvent<NodeResource>> getChildAssocEvents(RepoEventContainer repoEventContainer, EventType eventType)
{
List<RepoEvent<NodeResource>> assocChildCreatedEvents = new ArrayList<>();
for (int i = 1; i <= repoEventContainer.getEvents().size(); i++)
{
if (repoEventContainer.getEvent(i).getType().equals(eventType.getType()))
assocChildCreatedEvents.add(repoEventContainer.getEvent(i));
}
return assocChildCreatedEvents;
}
}

View File

@@ -0,0 +1,723 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import java.util.Arrays;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.event.v1.model.ChildAssociationResource;
import org.alfresco.repo.event.v1.model.EventData;
import org.alfresco.repo.event.v1.model.NodeResource;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.junit.Before;
import org.junit.Test;
/**
* @author Adina Ababei
* @author Iulian Aftene
*/
public class ChildAssociationRepoEventIT extends AbstractContextAwareRepoEvent
{
private RepoEventContainer repoEventsContainer;
@Before
public void initContainer()
{
repoEventsContainer = getRepoEventsContainer();
repoEventsContainer.reset();
}
@Test
public void testAddChildAssociation()
{
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(2);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(),
resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(),
resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(
parentNodeRef,
childNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
List<ChildAssociationRef> childAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getChildAssocs(parentNodeRef));
assertEquals(1, childAssociationRefs.size());
assertFalse(childAssociationRefs.get(0).isPrimary());
checkNumOfEvents(3);
final RepoEvent<NodeResource> childAssocRepoEvent = getChildAssocEvents(repoEventsContainer,
EventType.CHILD_ASSOC_CREATED).get(0);
assertEquals("Wrong repo event type.", EventType.CHILD_ASSOC_CREATED.getType(), childAssocRepoEvent.getType());
assertNotNull("Repo event ID is not available.", childAssocRepoEvent.getId());
assertNotNull("Source is not available", childAssocRepoEvent.getSource());
assertEquals("Repo event source is not available.",
"/" + descriptorService.getCurrentRepositoryDescriptor().getId(),
childAssocRepoEvent.getSource().toString());
assertNotNull("Repo event creation time is not available.", childAssocRepoEvent.getTime());
assertEquals("Invalid repo event datacontenttype", "application/json",
childAssocRepoEvent.getDatacontenttype());
assertEquals(EventData.JSON_SCHEMA, childAssocRepoEvent.getDataschema());
final EventData<NodeResource> nodeResourceEventData = getEventData(childAssocRepoEvent);
// EventData attributes
assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId());
assertNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore());
final ChildAssociationResource childAssociationResource = getChildAssocResource(childAssocRepoEvent);
assertEquals("Wrong parent", parentNodeRef.getId(), childAssociationResource.getParent().getId());
assertEquals("Wrong child", childNodeRef.getId(), childAssociationResource.getChild().getId());
assertEquals("Wrong assoc type", "cm:contains", childAssociationResource.getAssocType());
}
@Test
public void testRemoveChildAssociation()
{
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(2);
RepoEvent<NodeResource> parentRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), parentRepoEvent.getType());
RepoEvent<NodeResource> childRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), childRepoEvent.getType());
ChildAssociationRef childAssociationRef = retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(
parentNodeRef,
childNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
List<ChildAssociationRef> childAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getChildAssocs(parentNodeRef));
assertEquals(1, childAssociationRefs.size());
assertFalse(childAssociationRefs.get(0).isPrimary());
checkNumOfEvents(3);
retryingTransactionHelper.doInTransaction(() ->
nodeService.removeChildAssociation(childAssociationRef));
childAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getChildAssocs(parentNodeRef));
assertEquals(0, childAssociationRefs.size());
checkNumOfEvents(4);
final RepoEvent<NodeResource> childAssocRepoEvent = getChildAssocEvents(repoEventsContainer,
EventType.CHILD_ASSOC_DELETED).get(0);
assertEquals("Wrong repo event type.", EventType.CHILD_ASSOC_DELETED.getType(), childAssocRepoEvent.getType());
assertNotNull("Repo event ID is not available. ", childAssocRepoEvent.getId());
assertNotNull("Source is not available", childAssocRepoEvent.getSource());
assertEquals("Repo event source is not available. ",
"/" + descriptorService.getCurrentRepositoryDescriptor().getId(),
childAssocRepoEvent.getSource().toString());
assertNotNull("Repo event creation time is not available. ", childAssocRepoEvent.getTime());
assertEquals("Repo event datacontenttype", "application/json", childAssocRepoEvent.getDatacontenttype());
assertEquals(EventData.JSON_SCHEMA, childAssocRepoEvent.getDataschema());
final EventData<NodeResource> nodeResourceEventData = getEventData(childAssocRepoEvent);
// EventData attributes
assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId());
assertNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore());
final ChildAssociationResource childAssociationResource = getChildAssocResource(childAssocRepoEvent);
assertEquals("Wrong parent", parentNodeRef.getId(), childAssociationResource.getParent().getId());
assertEquals("Wrong child", childNodeRef.getId(), childAssociationResource.getChild().getId());
assertEquals("Wrong assoc type", "cm:contains", childAssociationResource.getAssocType());
}
@Test
public void testOneChildListOfParentsAssociations()
{
final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent3NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(
parents,
childNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent1 = nodeService.getChildAssocs(
parent1NodeRef);
List<ChildAssociationRef> childAssocParent2 = nodeService.getChildAssocs(
parent2NodeRef);
List<ChildAssociationRef> childAssocParent3 = nodeService.getChildAssocs(
parent3NodeRef);
assertEquals(1, childAssocParent1.size());
assertEquals(1, childAssocParent2.size());
assertEquals(1, childAssocParent3.size());
return null;
});
checkNumOfEvents(7);
// 3 assoc.child.Created events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_CREATED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
}
@Test
public void testOneChildMultipleParentsSameTransaction()
{
final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent3NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() -> {
for (NodeRef parent : parents)
{
nodeService.addChild(parent,
childNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate()));
}
return null;
});
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent1 = nodeService.getChildAssocs(
parent1NodeRef);
List<ChildAssociationRef> childAssocParent2 = nodeService.getChildAssocs(
parent2NodeRef);
List<ChildAssociationRef> childAssocParent3 = nodeService.getChildAssocs(
parent3NodeRef);
assertEquals(1, childAssocParent1.size());
assertEquals(1, childAssocParent2.size());
assertEquals(1, childAssocParent3.size());
return null;
});
// 3 assoc.child.Created events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_CREATED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
// All events in the transaction should have the same eventGroupId
String assocEventGroupID1 = getEventData(childAssocEvents.get(0)).getEventGroupId();
String assocEventGroupID2 = getEventData(childAssocEvents.get(1)).getEventGroupId();
String assocEventGroupID3 = getEventData(childAssocEvents.get(2)).getEventGroupId();
assertEquals(assocEventGroupID1, assocEventGroupID2);
assertEquals(assocEventGroupID2, assocEventGroupID3);
}
@Test
public void testOneChildMultipleParentsDifferentTransaction()
{
final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent3NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
for (NodeRef parent : parents)
{
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(
parent,
childNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
}
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent1 = nodeService.getChildAssocs(
parent1NodeRef);
List<ChildAssociationRef> childAssocParent2 = nodeService.getChildAssocs(
parent2NodeRef);
List<ChildAssociationRef> childAssocParent3 = nodeService.getChildAssocs(
parent3NodeRef);
assertEquals(1, childAssocParent1.size());
assertEquals(1, childAssocParent2.size());
assertEquals(1, childAssocParent3.size());
return null;
});
// 3 assoc.child.Created events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_CREATED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
assertEquals(parent1NodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getParent().getId());
assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(0)).getAssocType());
assertEquals(parent2NodeRef.getId(), getChildAssocResource(childAssocEvents.get(1)).getParent().getId());
assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(1)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(1)).getAssocType());
assertEquals(parent3NodeRef.getId(), getChildAssocResource(childAssocEvents.get(2)).getParent().getId());
assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(2)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(2)).getAssocType());
}
@Test
public void testOneParentMultipleChildrenSameTransaction()
{
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef child1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef child2NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef child3NodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> children = Arrays.asList(child1NodeRef, child2NodeRef, child3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() -> {
for (NodeRef child : children)
{
nodeService.addChild(parentNodeRef, child, ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate()));
}
return null;
});
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent = nodeService.getChildAssocs(parentNodeRef);
assertEquals(3, childAssocParent.size());
return null;
});
// 3 assoc.child.Created events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_CREATED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
}
@Test
public void testOneParentMultipleChildrenDifferentTransaction()
{
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef child1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef child2NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef child3NodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> children = Arrays.asList(child1NodeRef, child2NodeRef, child3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
for (NodeRef child : children)
{
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(parentNodeRef, child, ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
}
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent = nodeService.getChildAssocs(parentNodeRef);
assertEquals(3, childAssocParent.size());
return null;
});
// 3 assoc.child.Created events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_CREATED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
assertEquals(parentNodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getParent().getId());
assertEquals(child1NodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(0)).getAssocType());
assertEquals(parentNodeRef.getId(), getChildAssocResource(childAssocEvents.get(1)).getParent().getId());
assertEquals(child2NodeRef.getId(), getChildAssocResource(childAssocEvents.get(1)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(1)).getAssocType());
assertEquals(parentNodeRef.getId(), getChildAssocResource(childAssocEvents.get(2)).getParent().getId());
assertEquals(child3NodeRef.getId(), getChildAssocResource(childAssocEvents.get(2)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(2)).getAssocType());
}
@Test
public void testDeleteAssociationsOneChildMultipleParentsSameTransaction()
{
final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent3NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(parents, childNodeRef, ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
List<ChildAssociationRef> listChildAssociationRefs = retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent1 = nodeService.getChildAssocs(
parent1NodeRef);
List<ChildAssociationRef> childAssocParent2 = nodeService.getChildAssocs(
parent2NodeRef);
List<ChildAssociationRef> childAssocParent3 = nodeService.getChildAssocs(
parent3NodeRef);
assertEquals(1, childAssocParent1.size());
assertEquals(1, childAssocParent2.size());
assertEquals(1, childAssocParent3.size());
return Arrays.asList(childAssocParent1.get(0), childAssocParent2.get(0), childAssocParent3.get(0));
});
retryingTransactionHelper.doInTransaction(() -> {
for (ChildAssociationRef childAssociationRef : listChildAssociationRefs)
{
nodeService.removeChildAssociation(childAssociationRef);
}
return null;
});
checkNumOfEvents(10);
// 3 assoc.child.Deleted events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_DELETED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
}
@Test
public void testDeleteAssociationOneParentMultipleChildrenDifferentTransactions()
{
final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent3NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(parents, childNodeRef, ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
List<ChildAssociationRef> listChildAssociationRefs = retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent1 = nodeService.getChildAssocs(
parent1NodeRef);
List<ChildAssociationRef> childAssocParent2 = nodeService.getChildAssocs(
parent2NodeRef);
List<ChildAssociationRef> childAssocParent3 = nodeService.getChildAssocs(
parent3NodeRef);
assertEquals(1, childAssocParent1.size());
assertEquals(1, childAssocParent2.size());
assertEquals(1, childAssocParent3.size());
return Arrays.asList(childAssocParent1.get(0), childAssocParent2.get(0), childAssocParent3.get(0));
});
for (ChildAssociationRef childAssociationRef : listChildAssociationRefs)
{
retryingTransactionHelper.doInTransaction(() ->
nodeService.removeChildAssociation(childAssociationRef));
}
checkNumOfEvents(10);
// 3 assoc.child.Deleted events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_DELETED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
assertEquals(parent1NodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getParent().getId());
assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(0)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(0)).getAssocType());
assertEquals(parent2NodeRef.getId(), getChildAssocResource(childAssocEvents.get(1)).getParent().getId());
assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(1)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(1)).getAssocType());
assertEquals(parent3NodeRef.getId(), getChildAssocResource(childAssocEvents.get(2)).getParent().getId());
assertEquals(childNodeRef.getId(), getChildAssocResource(childAssocEvents.get(2)).getChild().getId());
assertEquals("cm:contains", getChildAssocResource(childAssocEvents.get(2)).getAssocType());
}
@Test
public void testDeleteParentWithMultipleChildAssociations()
{
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef child1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef child2NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef child3NodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> children = Arrays.asList(child1NodeRef, child2NodeRef, child3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() -> {
for (NodeRef child : children)
{
nodeService.addChild(parentNodeRef, child, ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate()));
}
return null;
});
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent = nodeService.getChildAssocs(parentNodeRef);
assertEquals(3, childAssocParent.size());
return null;
});
deleteNode(parentNodeRef);
checkNumOfEvents(11);
// 3 assoc.child.Deleted events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_DELETED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
}
@Test
public void testDeleteChildWithMultipleParentAssociations()
{
final NodeRef parent1NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent2NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef parent3NodeRef = createNode(ContentModel.TYPE_FOLDER);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
List<NodeRef> parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
checkNumOfEvents(4);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(4);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
nodeService.addChild(parents, childNodeRef, ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate())));
retryingTransactionHelper.doInTransaction(() -> {
List<ChildAssociationRef> childAssocParent1 = nodeService.getChildAssocs(
parent1NodeRef);
List<ChildAssociationRef> childAssocParent2 = nodeService.getChildAssocs(
parent2NodeRef);
List<ChildAssociationRef> childAssocParent3 = nodeService.getChildAssocs(
parent3NodeRef);
assertEquals(1, childAssocParent1.size());
assertEquals(1, childAssocParent2.size());
assertEquals(1, childAssocParent3.size());
return null;
});
deleteNode(childNodeRef);
checkNumOfEvents(11);
// 3 assoc.child.Deleted events should be created
List<RepoEvent<NodeResource>> childAssocEvents = getChildAssocEvents(repoEventsContainer, EventType.CHILD_ASSOC_DELETED);
assertEquals("Wrong association events number",3, childAssocEvents.size());
}
@Test
public void testUpdateNodeAddChildAssociationNodeEventsFirst()
{
final NodeRef parentNodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef childNodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(2);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
{
nodeService.setType(parentNodeRef, ContentModel.TYPE_FOLDER);
return nodeService.addChild(
parentNodeRef,
childNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(TEST_NAMESPACE, GUID.generate()));
});
List<ChildAssociationRef> childAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getChildAssocs(parentNodeRef));
assertEquals(1, childAssociationRefs.size());
assertFalse(childAssociationRefs.get(0).isPrimary());
checkNumOfEvents(4);
// Check the node events occur before the child association event
List<RepoEvent<NodeResource>> repoEvents = getRepoEventsContainer().getEvents();
assertEquals("org.alfresco.event.node.Created", repoEvents.get(0).getType());
assertEquals("org.alfresco.event.node.Created", repoEvents.get(1).getType());
assertEquals("org.alfresco.event.node.Updated", repoEvents.get(2).getType());
assertEquals("org.alfresco.event.assoc.child.Created", repoEvents.get(3).getType());
}
}

View File

@@ -0,0 +1,296 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.junit.Test;
public class EventConsolidatorUnitTest
{
private NodeResourceHelper nodeResourceHelper = mock(NodeResourceHelper.class);
@Test
public void testGetMappedAspectsBeforeRemovedAndAddedEmpty()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(0, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_AspectRemoved()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
Set<String> removed = new HashSet<>();
Set<String> added = new HashSet<>();
removed.add("cm:contains");
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(3, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_AspectAdded()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
Set<String> removed = new HashSet<>();
Set<String> added = new HashSet<>();
added.add("cm:auditable");
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(1, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_AspectAddedAndRemoved()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
Set<String> removed = new HashSet<>();
removed.add("cm:contains");
Set<String> added = new HashSet<>();
added.add("cm:contains");
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(2, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_AspectRemovedAndAdded()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
currentAspects.add("cm:contains");
Set<String> removed = new HashSet<>();
removed.add("cm:contains");
Set<String> added = new HashSet<>();
added.add("cm:contains");
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(0, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_AspectAddedTwiceRemovedOnce()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
currentAspects.add("cm:contains");
Set<String> removed = new HashSet<>();
Set<String> added = new HashSet<>();
added.add("cm:contains");
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(2, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_AspectRemovedTwiceAddedOnce()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
currentAspects.add("cm:contains");
Set<String> removed = new HashSet<>();
removed.add("cm:contains");
Set<String> added = new HashSet<>();
added.add("cm:contains");
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(2, mappedAspectsBefore.size());
}
@Test
public void testGetMappedAspectsBefore_FilteredAspectAdded()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASPECT_COPIEDFROM);
Set<String> currentAspects = new HashSet<>();
currentAspects.add("cm:geographic");
currentAspects.add("cm:auditable");
Set<String> removed = new HashSet<>();
Set<String> added = new HashSet<>();
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
Set<String> mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
assertEquals(0, mappedAspectsBefore.size());
}
@Test
public void testAddAspect()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
assertEquals(1, eventConsolidator.getAspectsAdded().size());
assertEquals(0, eventConsolidator.getAspectsRemoved().size());
assertTrue(eventConsolidator.getAspectsAdded().contains(ContentModel.ASSOC_CONTAINS));
}
@Test
public void testRemoveAspect()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
assertEquals(0, eventConsolidator.getAspectsAdded().size());
assertEquals(1, eventConsolidator.getAspectsRemoved().size());
assertTrue(eventConsolidator.getAspectsRemoved().contains(ContentModel.ASSOC_CONTAINS));
}
@Test
public void testAddAspectRemoveAspect()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
assertEquals(0, eventConsolidator.getAspectsAdded().size());
assertEquals(0, eventConsolidator.getAspectsRemoved().size());
}
@Test
public void testRemoveAspectAddAspect()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
assertEquals(0, eventConsolidator.getAspectsAdded().size());
assertEquals(0, eventConsolidator.getAspectsRemoved().size());
}
@Test
public void testAddAspectTwiceRemoveAspectOnce()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
assertEquals(1, eventConsolidator.getAspectsAdded().size());
assertEquals(0, eventConsolidator.getAspectsRemoved().size());
assertTrue(eventConsolidator.getAspectsAdded().contains(ContentModel.ASSOC_CONTAINS));
}
@Test
public void testAddAspectOnceRemoveAspectTwice()
{
EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
eventConsolidator.removeAspect(ContentModel.ASSOC_CONTAINS);
assertEquals(0, eventConsolidator.getAspectsAdded().size());
assertEquals(1, eventConsolidator.getAspectsRemoved().size());
assertTrue(eventConsolidator.getAspectsRemoved().contains(ContentModel.ASSOC_CONTAINS));
}
}

View File

@@ -37,6 +37,8 @@ import java.util.Collections;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.event2.filter.ChildAssociationTypeFilter;
import org.alfresco.repo.event2.filter.EventUserFilter;
import org.alfresco.repo.event2.filter.NodeAspectFilter;
import org.alfresco.repo.event2.filter.NodePropertyFilter;
@@ -61,6 +63,7 @@ public class EventFilterTest
private static NodePropertyFilter propertyFilter;
private static NodeTypeFilter typeFilter;
private static NodeAspectFilter aspectFilter;
private static ChildAssociationTypeFilter childAssociationTypeFilter;
private static EventUserFilter caseInsensitive_userFilter;
private static EventUserFilter caseSensitive_userFilter;
@@ -80,6 +83,8 @@ public class EventFilterTest
NamespaceService.CONTENT_MODEL_1_0_URI);
namespaceService.registerNamespace(NamespaceService.FORUMS_MODEL_PREFIX,
NamespaceService.FORUMS_MODEL_1_0_URI);
namespaceService.registerNamespace(NamespaceService.RENDITION_MODEL_PREFIX,
NamespaceService.RENDITION_MODEL_1_0_URI);
propertyFilter = new NodePropertyFilter();
propertyFilter.setNamespaceService(namespaceService);
@@ -96,6 +101,11 @@ public class EventFilterTest
aspectFilter.setDictionaryService(dictionaryService);
aspectFilter.init();
childAssociationTypeFilter = new ChildAssociationTypeFilter("rn:rendition");
childAssociationTypeFilter.setNamespaceService(namespaceService);
childAssociationTypeFilter.setDictionaryService(dictionaryService);
childAssociationTypeFilter.init();
caseInsensitive_userFilter = new EventUserFilter("System, john.doe, null", false);
caseSensitive_userFilter = new EventUserFilter("System, john.doe, null", true);
}
@@ -139,6 +149,15 @@ public class EventFilterTest
assertFalse(aspectFilter.isExcluded(ContentModel.ASPECT_TITLED));
}
@Test
public void childAssociationTypeFilter()
{
assertTrue("Rendition child association type should have been filtered.",
childAssociationTypeFilter.isExcluded(RenditionModel.ASSOC_RENDITION));
assertFalse(childAssociationTypeFilter.isExcluded(ContentModel.ASSOC_CONTAINS));
}
@Test
public void userFilter_case_insensitive()
{

View File

@@ -0,0 +1,374 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.event2;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.event.v1.model.EventData;
import org.alfresco.repo.event.v1.model.NodeResource;
import org.alfresco.repo.event.v1.model.PeerAssociationResource;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.junit.Before;
import org.junit.Test;
/**
* @author Chris Shields
*/
public class PeerAssociationRepoEventIT extends AbstractContextAwareRepoEvent
{
private RepoEventContainer repoEventsContainer;
@Before
public void initContainer()
{
repoEventsContainer = getRepoEventsContainer();
repoEventsContainer.reset();
}
@Test
public void testAddPeerAssociation()
{
final NodeRef content1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content2NodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(2);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() ->
nodeService.createAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL));
List<AssociationRef> peerAssociationRefs = retryingTransactionHelper.doInTransaction(
() ->
nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
assertEquals(1, peerAssociationRefs.size());
checkNumOfEvents(4);
final RepoEvent<NodeResource> peerAssocRepoEvent = getChildAssocEvents(repoEventsContainer,
EventType.PEER_ASSOC_CREATED).get(0);
assertEquals("Wrong repo event type.",
EventType.PEER_ASSOC_CREATED.getType(),
peerAssocRepoEvent.getType());
assertNotNull("Repo event ID is not available. ", peerAssocRepoEvent.getId());
assertNotNull("Source is not available", peerAssocRepoEvent.getSource());
assertEquals("Repo event source is not available. ",
"/" + descriptorService.getCurrentRepositoryDescriptor().getId(),
peerAssocRepoEvent.getSource().toString());
assertNotNull("Repo event creation time is not available. ", peerAssocRepoEvent.getTime());
assertEquals("Repo event datacontenttype", "application/json",
peerAssocRepoEvent.getDatacontenttype());
assertEquals(EventData.JSON_SCHEMA, peerAssocRepoEvent.getDataschema());
final EventData<NodeResource> nodeResourceEventData = getEventData(peerAssocRepoEvent);
// EventData attributes
assertNotNull("Event data group ID is not available. ", nodeResourceEventData.getEventGroupId());
assertNull("resourceBefore property is not available", nodeResourceEventData.getResourceBefore());
final PeerAssociationResource peerAssociationResource = getPeerAssocResource(peerAssocRepoEvent);
assertEquals("Wrong source", content1NodeRef.getId(), peerAssociationResource.getSource().getId());
assertEquals("Wrong target", content2NodeRef.getId(), peerAssociationResource.getTarget().getId());
assertEquals("Wrong assoc type", "cm:original", peerAssociationResource.getAssocType());
}
@Test
public void testAddMultiplePeerAssociationSameTransaction()
{
final NodeRef content1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content2NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content3NodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(3);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() -> {
nodeService.createAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
nodeService.createAssociation(
content3NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
return null;
});
List<AssociationRef> peerAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
assertEquals(2, peerAssociationRefs.size());
checkNumOfEvents(7);
List<RepoEvent<NodeResource>> peerAssocRepoEvent = getChildAssocEvents(repoEventsContainer, EventType.PEER_ASSOC_CREATED);
// we should have 2 assoc.peer.Created events
assertEquals("Wrong association events number",2, peerAssocRepoEvent.size());
}
@Test
public void testAddMultiplePeerAssociationDifferentTransaction()
{
final NodeRef content1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content2NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content3NodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(3);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(3);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
retryingTransactionHelper.doInTransaction(() -> {
nodeService.createAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
return null;
});
retryingTransactionHelper.doInTransaction(() -> {
nodeService.createAssociation(
content3NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
return null;
});
List<AssociationRef> peerAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
assertEquals(2, peerAssociationRefs.size());
checkNumOfEvents(7);
List<RepoEvent<NodeResource>> peerAssocRepoEvent = getChildAssocEvents(repoEventsContainer, EventType.PEER_ASSOC_CREATED);
// we should have 2 assoc.peer.Created events
assertEquals("Wrong association events number",2, peerAssocRepoEvent.size());
assertEquals("Wrong source",
content1NodeRef.getId(),
getPeerAssocResource(peerAssocRepoEvent.get(0)).getSource().getId());
assertEquals("Wrong target",
content2NodeRef.getId(),
getPeerAssocResource(peerAssocRepoEvent.get(0)).getTarget().getId());
assertEquals("Wrong assoc type",
"cm:original",
getPeerAssocResource(peerAssocRepoEvent.get(0)).getAssocType());
assertEquals("Wrong source",
content3NodeRef.getId(),
getPeerAssocResource(peerAssocRepoEvent.get(1)).getSource().getId());
assertEquals("Wrong target",
content2NodeRef.getId(),
getPeerAssocResource(peerAssocRepoEvent.get(1)).getTarget().getId());
assertEquals("Wrong assoc type",
"cm:original",
getPeerAssocResource(peerAssocRepoEvent.get(1)).getAssocType());
}
@Test
public void testRemovePeerAssociation()
{
final NodeRef content1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content2NodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(2);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), resultRepoEvent.getType());
// Create peer association
retryingTransactionHelper.doInTransaction(() ->
nodeService.createAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL));
List<AssociationRef> peerAssociationRefs = retryingTransactionHelper.doInTransaction(
() ->
nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
assertEquals(1, peerAssociationRefs.size());
checkNumOfEvents(4);
// Remove peer association
retryingTransactionHelper.doInTransaction(() ->
{
nodeService.removeAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
return null;
});
peerAssociationRefs = retryingTransactionHelper.doInTransaction(
() ->
nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
assertEquals(0, peerAssociationRefs.size());
checkNumOfEvents(6);
// Check the peer assoc created event
final RepoEvent<NodeResource> peerAssocRepoEvent = getChildAssocEvents(repoEventsContainer,
EventType.PEER_ASSOC_CREATED).get(0);
assertEquals("Wrong repo event type.",
EventType.PEER_ASSOC_CREATED.getType(),
peerAssocRepoEvent.getType());
assertNotNull("Repo event ID is not available. ", peerAssocRepoEvent.getId());
assertNotNull("Source is not available", peerAssocRepoEvent.getSource());
assertEquals("Repo event source is not available. ",
"/" + descriptorService.getCurrentRepositoryDescriptor().getId(),
peerAssocRepoEvent.getSource().toString());
assertNotNull("Repo event creation time is not available. ", peerAssocRepoEvent.getTime());
assertEquals("Repo event datacontenttype", "application/json",
peerAssocRepoEvent.getDatacontenttype());
assertEquals(EventData.JSON_SCHEMA, peerAssocRepoEvent.getDataschema());
final EventData nodeResourceEventData = getEventData(peerAssocRepoEvent);
// EventData attributes
assertNotNull("Event data group ID is not available. ",
nodeResourceEventData.getEventGroupId());
assertNull("resourceBefore property is not available",
nodeResourceEventData.getResourceBefore());
final PeerAssociationResource peerAssociationResource = getPeerAssocResource(
peerAssocRepoEvent);
assertEquals("Wrong source", content1NodeRef.getId(),
peerAssociationResource.getSource().getId());
assertEquals("Wrong target", content2NodeRef.getId(),
peerAssociationResource.getTarget().getId());
assertEquals("Wrong assoc type", "cm:original",
peerAssociationResource.getAssocType());
// Check the peer assoc deleted event
final RepoEvent<NodeResource> peerAssocRepoEvent2 = getChildAssocEvents(repoEventsContainer,
EventType.PEER_ASSOC_DELETED).get(0);
assertEquals("Wrong repo event type.",
EventType.PEER_ASSOC_DELETED.getType(),
peerAssocRepoEvent2.getType());
assertNotNull("Repo event ID is not available. ", peerAssocRepoEvent2.getId());
assertNotNull("Source is not available", peerAssocRepoEvent2.getSource());
assertEquals("Repo event source is not available. ",
"/" + descriptorService.getCurrentRepositoryDescriptor().getId(),
peerAssocRepoEvent2.getSource().toString());
assertNotNull("Repo event creation time is not available. ", peerAssocRepoEvent2.getTime());
assertEquals("Repo event datacontenttype", "application/json",
peerAssocRepoEvent2.getDatacontenttype());
assertEquals(EventData.JSON_SCHEMA, peerAssocRepoEvent2.getDataschema());
final EventData nodeResourceEventData2 = getEventData(peerAssocRepoEvent2);
// EventData attributes
assertNotNull("Event data group ID is not available. ",
nodeResourceEventData2.getEventGroupId());
assertNull("resourceBefore property is not available",
nodeResourceEventData2.getResourceBefore());
final PeerAssociationResource peerAssociationResource2 = getPeerAssocResource(
peerAssocRepoEvent2);
assertEquals("Wrong source", content1NodeRef.getId(),
peerAssociationResource2.getSource().getId());
assertEquals("Wrong target", content2NodeRef.getId(),
peerAssociationResource2.getTarget().getId());
assertEquals("Wrong assoc type", "cm:original",
peerAssociationResource2.getAssocType());
}
@Test
public void testAddAndRemovePeerAssociationSameTransaction()
{
final NodeRef content1NodeRef = createNode(ContentModel.TYPE_CONTENT);
final NodeRef content2NodeRef = createNode(ContentModel.TYPE_CONTENT);
checkNumOfEvents(2);
RepoEvent<NodeResource> resultRepoEvent = repoEventsContainer.getEvent(1);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(),
resultRepoEvent.getType());
resultRepoEvent = repoEventsContainer.getEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(),
resultRepoEvent.getType());
// Create peer association
retryingTransactionHelper.doInTransaction(() ->
{
nodeService.createAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
nodeService.removeAssociation(
content1NodeRef,
content2NodeRef,
ContentModel.ASSOC_ORIGINAL);
return null;
});
List<AssociationRef> peerAssociationRefs = retryingTransactionHelper.doInTransaction(
() ->
nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
assertEquals(0, peerAssociationRefs.size());
checkNumOfEvents(2);
}
}

View File

@@ -33,7 +33,9 @@ import org.junit.runners.Suite.SuiteClasses;
@SuiteClasses({ org.alfresco.repo.event2.CreateRepoEventIT.class,
org.alfresco.repo.event2.UpdateRepoEventIT.class,
org.alfresco.repo.event2.DeleteRepoEventIT.class,
org.alfresco.repo.event2.DownloadRepoEventIT.class })
org.alfresco.repo.event2.DownloadRepoEventIT.class,
org.alfresco.repo.event2.ChildAssociationRepoEventIT.class,
org.alfresco.repo.event2.PeerAssociationRepoEventIT.class })
public class RepoEvent2ITSuite
{
}

View File

@@ -25,6 +25,7 @@
*/
package org.alfresco.repo.event2;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -76,6 +77,8 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
return null;
});
checkNumOfEvents(2);
resultRepoEvent = getRepoEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(),
resultRepoEvent.getType());
@@ -152,6 +155,8 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
return null;
});
checkNumOfEvents(2);
resultRepoEvent = getRepoEvent(2);
assertEquals("Wrong repo event type.", EventType.NODE_UPDATED.getType(), resultRepoEvent.getType());
@@ -608,6 +613,8 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
return null;
});
checkNumOfEvents(4);
NodeResource resourceBefore = getNodeResourceBefore(4);
NodeResource resource = getNodeResource(4);
@@ -628,7 +635,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertNull(resourceBefore.getCreatedAt());
assertNull(resourceBefore.getCreatedByUser());
assertNull(resourceBefore.getProperties());
assertNotNull(resourceBefore.getAspectNames());
assertNull(resourceBefore.getAspectNames());
assertNotNull(resourceBefore.getPrimaryHierarchy());
assertNull("Content should have been null.", resource.getContent());
assertNull("Content should have been null.", resourceBefore.getContent());
@@ -820,4 +827,62 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent
assertEquals("Wrong node parent.", folder2ID, moveFileParentAfterMove);
}
@Test
public void testAddAspectRemoveAspectFromContentSameTransactionTest()
{
final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT);
NodeResource resource = getNodeResource(1);
final Set<String> originalAspects = resource.getAspectNames();
assertNotNull(originalAspects);
retryingTransactionHelper.doInTransaction(() -> {
// Add cm:geographic aspect with default value
nodeService.addAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC, null);
// Remove cm:geographic aspect
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC);
return null;
});
checkNumOfEvents(1);
}
@Test
public void testAddAspectRemoveAspectAddAspectFromContentSameTransactionTest()
{
final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT);
NodeResource resource = getNodeResource(1);
final Set<String> originalAspects = resource.getAspectNames();
assertNotNull(originalAspects);
retryingTransactionHelper.doInTransaction(() -> {
// Add cm:geographic aspect with default value
nodeService.addAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC, null);
// Remove cm:geographic aspect
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC);
// Add cm:geographic aspect with default value
nodeService.addAspect(nodeRef, ContentModel.ASPECT_GEOGRAPHIC, null);
return null;
});
checkNumOfEvents(2);
resource = getNodeResource(2);
Set<String> aspectsAfter = resource.getAspectNames();
assertNotNull(aspectsAfter);
assertEquals(2, aspectsAfter.size());
assertTrue(aspectsAfter.contains("cm:auditable"));
assertTrue(aspectsAfter.contains("cm:auditable"));
NodeResource resourceBefore = getNodeResourceBefore(2);
Set<String> aspectsBefore = resourceBefore.getAspectNames();
assertNotNull(aspectsBefore);
assertEquals(1, aspectsBefore.size());
assertTrue(aspectsBefore.contains("cm:auditable"));
}
}