diff --git a/pom.xml b/pom.xml
index 9a30ca8a5c..2b1feef50e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,7 +70,7 @@
2.11.0
2.10.2
1.2.5
- 0.0.2
+ 0.0.6
4.0.2
diff --git a/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java b/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java
new file mode 100644
index 0000000000..50d8601e00
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/event2/ChildAssociationEventConsolidator.java
@@ -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 .
+ * #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 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 getRepoEvent(EventInfo eventInfo)
+ {
+ EventType eventType = getDerivedEvent();
+
+ EventData.Builder eventDataBuilder = EventData.builder()
+ .setEventGroupId(eventInfo.getTxnId())
+ .setResource(resource);
+
+ EventData eventData = eventDataBuilder.build();
+ return RepoEvent.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 queue of event types
+ */
+ public Deque getEventTypes()
+ {
+ return eventTypes;
+ }
+}
diff --git a/src/main/java/org/alfresco/repo/event2/ChildAssociationEventSupportedPolicies.java b/src/main/java/org/alfresco/repo/event2/ChildAssociationEventSupportedPolicies.java
new file mode 100644
index 0000000000..c0db206715
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/event2/ChildAssociationEventSupportedPolicies.java
@@ -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 .
+ * #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
+{
+}
+
diff --git a/src/main/java/org/alfresco/repo/event2/EventConsolidator.java b/src/main/java/org/alfresco/repo/event2/EventConsolidator.java
index 4a53857814..528d7b4789 100644
--- a/src/main/java/org/alfresco/repo/event2/EventConsolidator.java
+++ b/src/main/java/org/alfresco/repo/event2/EventConsolidator.java
@@ -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 eventTypes;
- private final Set aspectsAdded;
- private final Set aspectsRemoved;
+ private final List aspectsAdded;
+ private final List aspectsRemoved;
private NodeResource.Builder resourceBuilder;
private Map propertiesBefore;
@@ -67,13 +68,14 @@ public class EventConsolidator implements EventSupportedPolicies
private QName nodeType;
private QName nodeTypeBefore;
private List 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 after)
{
propertiesAfter = after;
@@ -265,7 +291,7 @@ public class EventConsolidator implements EventSupportedPolicies
{
return null;
}
-
+
Builder builder = NodeResource.builder();
Map 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 getMappedAspectsBefore(Set currentAspects)
+
+ Set getMappedAspectsBefore(Set currentAspects)
{
if (currentAspects == null)
{
@@ -329,17 +364,21 @@ public class EventConsolidator implements EventSupportedPolicies
{
Set removed = helper.mapToNodeAspects(aspectsRemoved);
Set added = helper.mapToNodeAspects(aspectsAdded);
-
- Set before = new HashSet<>(currentAspects);
- if (!removed.isEmpty())
+
+ Set 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 Map getBeforeMapChanges(Map before, Map after)
@@ -413,4 +457,20 @@ public class EventConsolidator implements EventSupportedPolicies
{
return eventTypes;
}
-}
\ No newline at end of file
+
+
+ public List getAspectsAdded()
+ {
+ return aspectsAdded;
+ }
+
+ public List getAspectsRemoved()
+ {
+ return aspectsRemoved;
+ }
+
+ public boolean isResourceBeforeAllFieldsNull()
+ {
+ return resourceBeforeAllFieldsNull;
+ }
+}
diff --git a/src/main/java/org/alfresco/repo/event2/EventGenerator.java b/src/main/java/org/alfresco/repo/event2/EventGenerator.java
index 9fca423e28..6d9581ad35 100644
--- a/src/main/java/org/alfresco/repo/event2/EventGenerator.java
+++ b/src/main/java/org/alfresco/repo/event2/EventGenerator.java
@@ -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 nodeEvents = getTxnResourceMap(transactionListener);
+ Consolidators consolidators = getTxnConsolidators(transactionListener);
+ Map nodeEvents = consolidators.getNodes();
if (nodeEvents.isEmpty())
{
AlfrescoTransactionSupport.bindListener(transactionListener);
@@ -244,15 +291,60 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
return eventConsolidator;
}
- private Map getTxnResourceMap(Object resourceKey)
+
+ private Consolidators getTxnConsolidators(Object resourceKey)
{
- Map 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 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 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) () -> {
- 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 changedNodes = getTxnResourceMap(this);
- for (Map.Entry entry : changedNodes.entrySet())
+ final Consolidators consolidators = getTxnConsolidators(this);
+
+ // Node events
+ for (Map.Entry entry : consolidators.getNodes().entrySet())
{
EventConsolidator eventConsolidator = entry.getValue();
sendEvent(entry.getKey(), eventConsolidator);
}
- }
+
+ // Child assoc events
+ for (Map.Entry entry : consolidators.getChildAssocs().entrySet())
+ {
+ ChildAssociationEventConsolidator eventConsolidator = entry.getValue();
+ sendEvent(entry.getKey(), eventConsolidator);
+ }
+
+ // Peer assoc events
+ for (Map.Entry entry : consolidators.getPeerAssocs().entrySet())
+ {
+ PeerAssociationEventConsolidator eventConsolidator = entry.getValue();
+ sendEvent(entry.getKey(), eventConsolidator);
+ }
+ }
catch (Exception e)
{
// Must consume the exception to protect other TransactionListeners
LOGGER.error("Unexpected error while sending repository events", e);
}
}
+
+ 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 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) () -> {
+ event2MessageProducer.send(event);
+
+ return null;
+ }, true, false);
+ }
+ }
+
+
+ private static class Consolidators
+ {
+ private Map nodes;
+ private Map childAssocs;
+ private Map peerAssocs;
+
+ public Map getNodes()
+ {
+ if (nodes == null)
+ {
+ nodes = new LinkedHashMap<>(29);
+ }
+ return nodes;
+ }
+
+ public Map getChildAssocs()
+ {
+ if (childAssocs == null)
+ {
+ childAssocs = new LinkedHashMap<>(29);
+ }
+ return childAssocs;
+ }
+
+ public Map getPeerAssocs()
+ {
+ if (peerAssocs == null)
+ {
+ peerAssocs = new LinkedHashMap<>(29);
+ }
+ return peerAssocs;
+ }
}
}
diff --git a/src/main/java/org/alfresco/repo/event2/EventType.java b/src/main/java/org/alfresco/repo/event2/EventType.java
index 7ec89cb1c7..1220c9dbff 100644
--- a/src/main/java/org/alfresco/repo/event2/EventType.java
+++ b/src/main/java/org/alfresco/repo/event2/EventType.java
@@ -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";
+ }
}
diff --git a/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java b/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java
index 5dea5ba485..1af39e2d61 100644
--- a/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java
+++ b/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java
@@ -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 mapToNodeAspects(Set aspects)
+ public Set mapToNodeAspects(Collection aspects)
{
Set filteredAspects = new HashSet<>(aspects.size());
diff --git a/src/main/java/org/alfresco/repo/event2/PeerAssociationEventConsolidator.java b/src/main/java/org/alfresco/repo/event2/PeerAssociationEventConsolidator.java
new file mode 100644
index 0000000000..c708ee9a94
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/event2/PeerAssociationEventConsolidator.java
@@ -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 .
+ * #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 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 getRepoEvent(EventInfo eventInfo)
+ {
+ EventType eventType = getDerivedEvent();
+
+ EventData.Builder eventDataBuilder = EventData.builder()
+ .setEventGroupId(eventInfo.getTxnId())
+ .setResource(resource);
+
+ EventData eventData = eventDataBuilder.build();
+ return RepoEvent.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 queue of event types
+ */
+ public Deque getEventTypes()
+ {
+ return eventTypes;
+ }
+}
+
diff --git a/src/main/java/org/alfresco/repo/event2/PeerAssociationEventSupportedPolicies.java b/src/main/java/org/alfresco/repo/event2/PeerAssociationEventSupportedPolicies.java
new file mode 100644
index 0000000000..eb9171cee6
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/event2/PeerAssociationEventSupportedPolicies.java
@@ -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 .
+ * #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
+{
+}
diff --git a/src/main/java/org/alfresco/repo/event2/QNameHelper.java b/src/main/java/org/alfresco/repo/event2/QNameHelper.java
new file mode 100644
index 0000000000..3f463ff8f2
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/event2/QNameHelper.java
@@ -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 .
+ * #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;
+ }
+}
diff --git a/src/main/java/org/alfresco/repo/event2/filter/ChildAssociationTypeFilter.java b/src/main/java/org/alfresco/repo/event2/filter/ChildAssociationTypeFilter.java
new file mode 100644
index 0000000000..dc6466240f
--- /dev/null
+++ b/src/main/java/org/alfresco/repo/event2/filter/ChildAssociationTypeFilter.java
@@ -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 .
+ * #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 assocTypesBlackList;
+
+ public ChildAssociationTypeFilter(String filteredChildAssocTypes)
+ {
+ this.assocTypesBlackList = parseFilterList(filteredChildAssocTypes);
+ }
+
+ /**
+ *
+ * @see org.alfresco.repo.event2.filter.AbstractNodeEventFilter#getExcludedTypes()
+ */
+ @Override
+ public Set getExcludedTypes()
+ {
+ Set result = new HashSet<>();
+
+ // add child association types defined in repository.properties/alfresco-global.properties
+ assocTypesBlackList.forEach(childAssocType -> result.addAll(expandTypeDef(childAssocType)));
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/alfresco/repo/event2/filter/EventFilterRegistry.java b/src/main/java/org/alfresco/repo/event2/filter/EventFilterRegistry.java
index 371b6687c0..74c8cffb0a 100644
--- a/src/main/java/org/alfresco/repo/event2/filter/EventFilterRegistry.java
+++ b/src/main/java/org/alfresco/repo/event2/filter/EventFilterRegistry.java
@@ -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);
diff --git a/src/main/resources/alfresco/events2-context.xml b/src/main/resources/alfresco/events2-context.xml
index f170df4b5a..ecc12882f5 100644
--- a/src/main/resources/alfresco/events2-context.xml
+++ b/src/main/resources/alfresco/events2-context.xml
@@ -25,6 +25,10 @@
+
+
+
+
diff --git a/src/main/resources/alfresco/repository.properties b/src/main/resources/alfresco/repository.properties
index 8c06bf3947..cbaa1de777 100644
--- a/src/main/resources/alfresco/repository.properties
+++ b/src/main/resources/alfresco/repository.properties
@@ -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
diff --git a/src/test/java/org/alfresco/AllUnitTestsSuite.java b/src/test/java/org/alfresco/AllUnitTestsSuite.java
index 1f6c8e2cac..6b6a8911ce 100644
--- a/src/test/java/org/alfresco/AllUnitTestsSuite.java
+++ b/src/test/java/org/alfresco/AllUnitTestsSuite.java
@@ -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
{
diff --git a/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java b/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java
index 392a1a3355..cd3e57b7f8 100644
--- a/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java
+++ b/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java
@@ -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 eventData = repoEvent.getData();
+ assertNotNull(eventData);
+ ChildAssociationResource resource = eventData.getResource();
+ assertNotNull(resource);
+
+ return resource;
+ }
+
+ protected PeerAssociationResource getPeerAssocResource(RepoEvent repoEvent)
+ {
+ assertNotNull(repoEvent);
+ EventData eventData = repoEvent.getData();
+ assertNotNull(eventData);
+ PeerAssociationResource resource = eventData.getResource();
+ assertNotNull(resource);
+
+ return resource;
+ }
+
protected NodeResource getNodeResourceBefore(int eventSequenceNumber)
{
EventData eventData = getEventData(eventSequenceNumber);
@@ -339,6 +363,15 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
events.clear();
}
}
+
+ public List> getChildAssocEvents(RepoEventContainer repoEventContainer, EventType eventType)
+ {
+ List> 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;
+ }
}
-
-
diff --git a/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java b/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java
new file mode 100644
index 0000000000..4b2559df0c
--- /dev/null
+++ b/src/test/java/org/alfresco/repo/event2/ChildAssociationRepoEventIT.java
@@ -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 .
+ * #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 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 childAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
+ nodeService.getChildAssocs(parentNodeRef));
+
+ assertEquals(1, childAssociationRefs.size());
+ assertFalse(childAssociationRefs.get(0).isPrimary());
+
+ checkNumOfEvents(3);
+
+ final RepoEvent 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 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 parentRepoEvent = repoEventsContainer.getEvent(1);
+ assertEquals("Wrong repo event type.", EventType.NODE_CREATED.getType(), parentRepoEvent.getType());
+
+ RepoEvent 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 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 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 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 parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent1 = nodeService.getChildAssocs(
+ parent1NodeRef);
+ List childAssocParent2 = nodeService.getChildAssocs(
+ parent2NodeRef);
+ List 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> 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 parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent1 = nodeService.getChildAssocs(
+ parent1NodeRef);
+ List childAssocParent2 = nodeService.getChildAssocs(
+ parent2NodeRef);
+ List 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> 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 parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent1 = nodeService.getChildAssocs(
+ parent1NodeRef);
+ List childAssocParent2 = nodeService.getChildAssocs(
+ parent2NodeRef);
+ List 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> 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 children = Arrays.asList(child1NodeRef, child2NodeRef, child3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent = nodeService.getChildAssocs(parentNodeRef);
+
+ assertEquals(3, childAssocParent.size());
+ return null;
+ });
+
+ // 3 assoc.child.Created events should be created
+ List> 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 children = Arrays.asList(child1NodeRef, child2NodeRef, child3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent = nodeService.getChildAssocs(parentNodeRef);
+
+ assertEquals(3, childAssocParent.size());
+ return null;
+ });
+
+ // 3 assoc.child.Created events should be created
+ List> 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 parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 listChildAssociationRefs = retryingTransactionHelper.doInTransaction(() -> {
+ List childAssocParent1 = nodeService.getChildAssocs(
+ parent1NodeRef);
+ List childAssocParent2 = nodeService.getChildAssocs(
+ parent2NodeRef);
+ List 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> 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 parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 listChildAssociationRefs = retryingTransactionHelper.doInTransaction(() -> {
+ List childAssocParent1 = nodeService.getChildAssocs(
+ parent1NodeRef);
+ List childAssocParent2 = nodeService.getChildAssocs(
+ parent2NodeRef);
+ List 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> 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 children = Arrays.asList(child1NodeRef, child2NodeRef, child3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent = nodeService.getChildAssocs(parentNodeRef);
+
+ assertEquals(3, childAssocParent.size());
+ return null;
+ });
+
+ deleteNode(parentNodeRef);
+
+ checkNumOfEvents(11);
+
+ // 3 assoc.child.Deleted events should be created
+ List> 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 parents = Arrays.asList(parent1NodeRef, parent2NodeRef, parent3NodeRef);
+
+ checkNumOfEvents(4);
+
+ RepoEvent 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 childAssocParent1 = nodeService.getChildAssocs(
+ parent1NodeRef);
+ List childAssocParent2 = nodeService.getChildAssocs(
+ parent2NodeRef);
+ List 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> 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 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 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> 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());
+ }
+}
diff --git a/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java b/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java
new file mode 100644
index 0000000000..25fb458837
--- /dev/null
+++ b/src/test/java/org/alfresco/repo/event2/EventConsolidatorUnitTest.java
@@ -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 .
+ * #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 currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+
+ Set mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
+
+ assertEquals(0, mappedAspectsBefore.size());
+ }
+
+ @Test
+ public void testGetMappedAspectsBefore_AspectRemoved()
+ {
+ EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
+ eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
+
+ Set currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+
+ Set removed = new HashSet<>();
+ Set added = new HashSet<>();
+ removed.add("cm:contains");
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
+
+ assertEquals(3, mappedAspectsBefore.size());
+ }
+
+ @Test
+ public void testGetMappedAspectsBefore_AspectAdded()
+ {
+ EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
+ eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
+
+ Set currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+
+ Set removed = new HashSet<>();
+ Set added = new HashSet<>();
+ added.add("cm:auditable");
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
+
+ assertEquals(1, mappedAspectsBefore.size());
+ }
+
+
+ @Test
+ public void testGetMappedAspectsBefore_AspectAddedAndRemoved()
+ {
+ EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
+ eventConsolidator.addAspect(ContentModel.ASSOC_CONTAINS);
+
+ Set currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+
+ Set removed = new HashSet<>();
+ removed.add("cm:contains");
+ Set added = new HashSet<>();
+ added.add("cm:contains");
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set 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 currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+ currentAspects.add("cm:contains");
+
+ Set removed = new HashSet<>();
+ removed.add("cm:contains");
+ Set added = new HashSet<>();
+ added.add("cm:contains");
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set 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 currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+ currentAspects.add("cm:contains");
+
+ Set removed = new HashSet<>();
+ Set added = new HashSet<>();
+ added.add("cm:contains");
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set 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 currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+ currentAspects.add("cm:contains");
+
+ Set removed = new HashSet<>();
+ removed.add("cm:contains");
+ Set added = new HashSet<>();
+ added.add("cm:contains");
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set mappedAspectsBefore = eventConsolidator.getMappedAspectsBefore(currentAspects);
+
+ assertEquals(2, mappedAspectsBefore.size());
+ }
+
+ @Test
+ public void testGetMappedAspectsBefore_FilteredAspectAdded()
+ {
+ EventConsolidator eventConsolidator = new EventConsolidator(nodeResourceHelper);
+ eventConsolidator.addAspect(ContentModel.ASPECT_COPIEDFROM);
+
+ Set currentAspects = new HashSet<>();
+ currentAspects.add("cm:geographic");
+ currentAspects.add("cm:auditable");
+
+ Set removed = new HashSet<>();
+ Set added = new HashSet<>();
+
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsRemoved())).thenReturn(removed);
+ when(nodeResourceHelper.mapToNodeAspects(eventConsolidator.getAspectsAdded())).thenReturn(added);
+
+ Set 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));
+ }
+}
diff --git a/src/test/java/org/alfresco/repo/event2/EventFilterTest.java b/src/test/java/org/alfresco/repo/event2/EventFilterTest.java
index cf943b46f7..5110ec7472 100644
--- a/src/test/java/org/alfresco/repo/event2/EventFilterTest.java
+++ b/src/test/java/org/alfresco/repo/event2/EventFilterTest.java
@@ -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()
{
diff --git a/src/test/java/org/alfresco/repo/event2/PeerAssociationRepoEventIT.java b/src/test/java/org/alfresco/repo/event2/PeerAssociationRepoEventIT.java
new file mode 100644
index 0000000000..82b6785dfb
--- /dev/null
+++ b/src/test/java/org/alfresco/repo/event2/PeerAssociationRepoEventIT.java
@@ -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 .
+ * #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 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 peerAssociationRefs = retryingTransactionHelper.doInTransaction(
+ () ->
+ nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
+ assertEquals(1, peerAssociationRefs.size());
+
+ checkNumOfEvents(4);
+
+ final RepoEvent 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());
+ }
+
+ @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 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 peerAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
+ nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
+
+ assertEquals(2, peerAssociationRefs.size());
+
+ checkNumOfEvents(7);
+
+ List> 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 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 peerAssociationRefs = retryingTransactionHelper.doInTransaction(() ->
+ nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
+
+ assertEquals(2, peerAssociationRefs.size());
+
+ checkNumOfEvents(7);
+
+ List> 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 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 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 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 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 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 peerAssociationRefs = retryingTransactionHelper.doInTransaction(
+ () ->
+ nodeService.getSourceAssocs(content2NodeRef, ContentModel.ASSOC_ORIGINAL));
+ assertEquals(0, peerAssociationRefs.size());
+
+ checkNumOfEvents(2);
+ }
+}
diff --git a/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java b/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java
index ca328b56b1..6fdd4d8568 100644
--- a/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java
+++ b/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java
@@ -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
{
}
diff --git a/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java b/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java
index 25bcb70196..e3584259a3 100644
--- a/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java
+++ b/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java
@@ -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 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 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 aspectsAfter = resource.getAspectNames();
+ assertNotNull(aspectsAfter);
+ assertEquals(2, aspectsAfter.size());
+ assertTrue(aspectsAfter.contains("cm:auditable"));
+ assertTrue(aspectsAfter.contains("cm:auditable"));
+
+ NodeResource resourceBefore = getNodeResourceBefore(2);
+ Set aspectsBefore = resourceBefore.getAspectNames();
+ assertNotNull(aspectsBefore);
+ assertEquals(1, aspectsBefore.size());
+ assertTrue(aspectsBefore.contains("cm:auditable"));
+ }
}