diff --git a/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java b/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java index 9ad0a3cb60..f313c25dd8 100644 --- a/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java +++ b/repository/src/main/java/org/alfresco/repo/domain/permissions/FixedAclUpdater.java @@ -169,6 +169,12 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli listeners.add(listener); } + /** Unregister a {@link FixedAclUpdaterListener} to be notified when a node is updated by an instance of this class. */ + public static void unregisterListener(FixedAclUpdaterListener listener) + { + listeners.remove(listener); + } + public void init() { onInheritPermissionsDisabledDelegate = policyComponent diff --git a/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java b/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java index 1d1b7f78d3..0981104a14 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java +++ b/repository/src/main/java/org/alfresco/repo/event2/EventGenerator.java @@ -31,8 +31,10 @@ import java.time.Instant; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Deque; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.UUID; import org.alfresco.repo.domain.node.NodeDAO; @@ -54,8 +56,11 @@ import org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy; import org.alfresco.repo.node.NodeServicePolicies.OnSetNodeTypePolicy; import org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy; +import org.alfresco.repo.policy.Behaviour; +import org.alfresco.repo.policy.BehaviourDefinition; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.policy.ServiceBehaviourBinding; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -103,10 +108,27 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin private ChildAssociationTypeFilter childAssociationTypeFilter; private EventUserFilter userFilter; protected final EventTransactionListener transactionListener = new EventTransactionListener(); + protected boolean enabled; + private Set behaviours; + + public void setEnabled(boolean enabled) + { + this.enabled = enabled; + } + + public boolean isEnabled() + { + return enabled; + } @Override public void afterPropertiesSet() { + if (!isEnabled()) + { + return; + } + PropertyCheck.mandatory(this, "policyComponent", policyComponent); PropertyCheck.mandatory(this, "nodeService", nodeService); PropertyCheck.mandatory(this, "namespaceService", namespaceService); @@ -126,28 +148,56 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin private void bindBehaviours() { - policyComponent.bindClassBehaviour(OnCreateNodePolicy.QNAME, this, - new JavaBehaviour(this, "onCreateNode")); - policyComponent.bindClassBehaviour(BeforeDeleteNodePolicy.QNAME, this, - new JavaBehaviour(this, "beforeDeleteNode")); - policyComponent.bindClassBehaviour(OnUpdatePropertiesPolicy.QNAME, this, - new JavaBehaviour(this, "onUpdateProperties")); - policyComponent.bindClassBehaviour(OnSetNodeTypePolicy.QNAME, this, - new JavaBehaviour(this, "onSetNodeType")); - policyComponent.bindClassBehaviour(OnAddAspectPolicy.QNAME, this, - new JavaBehaviour(this, "onAddAspect")); - policyComponent.bindClassBehaviour(OnRemoveAspectPolicy.QNAME, this, - new JavaBehaviour(this, "onRemoveAspect")); - policyComponent.bindClassBehaviour(OnMoveNodePolicy.QNAME, this, - new JavaBehaviour(this, "onMoveNode")); - 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")); + setClassBehaviour(OnCreateNodePolicy.QNAME, "onCreateNode"); + setClassBehaviour(BeforeDeleteNodePolicy.QNAME, "beforeDeleteNode"); + setClassBehaviour(OnUpdatePropertiesPolicy.QNAME, "onUpdateProperties"); + setClassBehaviour(OnSetNodeTypePolicy.QNAME, "onSetNodeType"); + setClassBehaviour(OnAddAspectPolicy.QNAME, "onAddAspect"); + setClassBehaviour(OnRemoveAspectPolicy.QNAME, "onRemoveAspect"); + setClassBehaviour(OnMoveNodePolicy.QNAME, "onMoveNode"); + setAssociationBehaviour(OnCreateChildAssociationPolicy.QNAME, "onCreateChildAssociation"); + setAssociationBehaviour(BeforeDeleteChildAssociationPolicy.QNAME, "beforeDeleteChildAssociation"); + setAssociationBehaviour(OnCreateAssociationPolicy.QNAME, "onCreateAssociation"); + setAssociationBehaviour(BeforeDeleteAssociationPolicy.QNAME, "beforeDeleteAssociation"); + } + + /** + * Disable Events2 generated events + */ + public void disable() + { + if (!isEnabled()) + { + return; + } + setEnabled(false); + disableBehaviours(); + } + + /** + * Enable Events2 generated events + */ + public void enable() + { + if (isEnabled()) + { + return; + } + + setEnabled(true); + + if (behaviours == null) + { + behaviours = new HashSet(); + afterPropertiesSet(); + bindBehaviours(); + + } + else + { + enableBehaviours(); + } + } public void setNodeDAO(NodeDAO nodeDAO) @@ -289,6 +339,62 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin return new PeerAssociationEventConsolidator(peerAssociationRef, nodeResourceHelper); } + private void setClassBehaviour(QName policyQName, String method) + { + Behaviour behaviour = bindClassBehaviour(policyQName, method); + behaviours.add(behaviour); + } + + private void setAssociationBehaviour(QName policyQName, String method) + { + Behaviour behaviour = bindAssociationBehaviour(policyQName, method); + behaviours.add(behaviour); + } + + protected Behaviour bindClassBehaviour(QName policyQName, String method) + { + BehaviourDefinition behaviourDef = policyComponent.bindClassBehaviour(policyQName, this, + new JavaBehaviour(this, method)); + return behaviourDef.getBehaviour(); + } + + protected Behaviour bindAssociationBehaviour(QName policyQName, String method) + { + BehaviourDefinition behaviourDef = policyComponent.bindAssociationBehaviour(policyQName, this, + new JavaBehaviour(this, method)); + return behaviourDef.getBehaviour(); + } + + private void disableBehaviours() + { + disableBehaviours(behaviours); + } + + protected void disableBehaviours(Set bindedBehaviours) + { + if (bindedBehaviours != null) + { + bindedBehaviours.forEach(behaviour -> { + behaviour.disable(); + }); + } + } + + private void enableBehaviours() + { + enableBehaviours(behaviours); + } + + protected void enableBehaviours(Set bindedBehaviours) + { + if (bindedBehaviours != null) + { + bindedBehaviours.forEach(behaviour -> { + behaviour.enable(); + }); + } + } + /** * @return the {@link EventConsolidator} for the supplied {@code nodeRef} from * the current transaction context. @@ -396,6 +502,11 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin @Override protected void onBootstrap(ApplicationEvent applicationEvent) { + if (!isEnabled()) + { + return; + } + behaviours = new HashSet(); bindBehaviours(); } diff --git a/repository/src/main/resources/alfresco/events2-context.xml b/repository/src/main/resources/alfresco/events2-context.xml index 205c8ae7ea..9589b01d75 100644 --- a/repository/src/main/resources/alfresco/events2-context.xml +++ b/repository/src/main/resources/alfresco/events2-context.xml @@ -43,6 +43,9 @@ + + ${repo.event2.enabled} + diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties index de3085bc12..09abc8e190 100644 --- a/repository/src/main/resources/alfresco/repository.properties +++ b/repository/src/main/resources/alfresco/repository.properties @@ -1219,6 +1219,7 @@ contentPropertyRestrictions.enabled=true contentPropertyRestrictions.whitelist= # Repo events2 +repo.event2.enabled=true # 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 diff --git a/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorDisabledTest.java b/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorDisabledTest.java new file mode 100644 index 0000000000..14a5f886e4 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/event2/EventGeneratorDisabledTest.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.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Session; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.event.v1.model.RepoEvent; +import org.apache.activemq.ActiveMQConnection; +import org.apache.activemq.ActiveMQConnectionFactory; +import org.apache.activemq.command.ActiveMQTextMessage; +import org.awaitility.Awaitility; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class EventGeneratorDisabledTest extends AbstractContextAwareRepoEvent +{ + private static final String EVENT2_TOPIC_NAME = "alfresco.repo.event2"; + private static final String BROKER_URL = "tcp://localhost:61616"; + + @Autowired @Qualifier("event2ObjectMapper") + private ObjectMapper objectMapper; + + @Autowired + protected ObjectMapper event2ObjectMapper; + + //private EventGenerator eventGenerator; + + private ActiveMQConnection connection; + protected List> receivedEvents; + + + @Before + public void setup() throws Exception + { + ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL); + connection = (ActiveMQConnection) connectionFactory.createConnection(); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Destination destination = session.createTopic(EVENT2_TOPIC_NAME); + MessageConsumer consumer = session.createConsumer(destination); + + receivedEvents = Collections.synchronizedList(new LinkedList<>()); + consumer.setMessageListener(new MessageListener() + { + @Override + public void onMessage(Message message) + { + String text = getText(message); + RepoEvent event = toRepoEvent(text); + receivedEvents.add(event); + } + + private RepoEvent toRepoEvent(String json) + { + try + { + return objectMapper.readValue(json, RepoEvent.class); + } catch (Exception e) + { + e.printStackTrace(); + return null; + } + } + }); + } + + @After + public void shutdownTopicListener() throws Exception + { + connection.close(); + connection = null; + } + + @Test + public void shouldNotReceiveEvent2EventsOnNodeCreation() throws Exception + { + if (eventGenerator.isEnabled()) + { + eventGenerator.disable(); + } + + createNode(ContentModel.TYPE_CONTENT); + + Awaitility.await().pollDelay(6, TimeUnit.SECONDS).until(() -> receivedEvents.size() == 0); + + assertTrue(EVENT_CONTAINER.getEvents().size() == 0); + assertTrue(receivedEvents.size() == 0); + + eventGenerator.enable(); + + } + + @Test + public void shouldReceiveEvent2EventsOnNodeCreation() throws Exception + { + if (!eventGenerator.isEnabled()) + { + eventGenerator.enable(); + } + + createNode(ContentModel.TYPE_CONTENT); + + Awaitility.await().atMost(6, TimeUnit.SECONDS).until(() -> receivedEvents.size() == 1); + + assertTrue(EVENT_CONTAINER.getEvents().size() == 1); + assertTrue(receivedEvents.size() == 1); + + RepoEvent sent = getRepoEvent(1); + RepoEvent received = receivedEvents.get(0); + assertEventsEquals("Events are different!", sent, received); + } + + private void assertEventsEquals(String message, RepoEvent expected, RepoEvent current) + { + assertEquals(message, expected, current); + } + + private static String getText(Message message) + { + try + { + ActiveMQTextMessage am = (ActiveMQTextMessage) message; + return am.getText(); + } catch (JMSException e) + { + return null; + } + } + +} diff --git a/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java b/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java index 6937822f87..82b0716c03 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java +++ b/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2ITSuite.java @@ -35,7 +35,8 @@ import org.junit.runners.Suite.SuiteClasses; DeleteRepoEventIT.class, ChildAssociationRepoEventIT.class, PeerAssociationRepoEventIT.class, - EventGeneratorTest.class + EventGeneratorTest.class, + EventGeneratorDisabledTest.class }) public class RepoEvent2ITSuite {