This event is fired whenever a node, such as a folder or file, is updated or moved in the repository. The full name of this event is org.alfresco.event.node.Updated. The event is fired when the node’s name, type, properties, aspects, or content is updated.
Here is an example payload for this event type:
{ "specversion": "1.0", "type": "org.alfresco.event.node.Updated", "id": "ae5dac3c-25d0-438d-b148-2084d1ab05a6", "source": "/08d9b620-48de-4247-8f33-360988d3b19b", "time": "2021-01-26T10:29:42.99524Z", "dataschema": "https://api.alfresco.com/schema/event/repo/v1/nodeUpdated", "datacontenttype": "application/json", "data": { "eventGroupId": "b5b1ebfe-45fc-4f86-b71b-421996482881", "resource": { "@type": "NodeResource", "id": "d71dd823-82c7-477c-8490-04cb0e826e65", "primaryHierarchy": [ "5f355d16-f824-4173-bf4b-b1ec37ef5549", "93f7edf5-e4d8-4749-9b4c-e45097e2e19d", "c388532e-8da6-4d50-a6d2-4f3f3ac36ff7", "2fa2cde5-9d83-4460-a38c-cfe4ec9cca08" ], "name": "purchase-order-scan.pdf", "nodeType": "cm:content", "createdByUser": { "id": "admin", "displayName": "Administrator" }, "createdAt": "2021-01-21T11:14:15.695Z", "modifiedByUser": { "id": "admin", "displayName": "Administrator" }, "modifiedAt": "2021-01-26T10:29:42.529Z", "content": { "mimeType": "application/pdf", "sizeInBytes": 531152, "encoding": "UTF-8" }, "properties": { "cm:autoVersion": true, "cm:title": "Purchase Order", "cm:versionType": "MAJOR", "cm:versionLabel": "1.0", "cm:autoVersionOnUpdateProps": false, "cm:lastThumbnailModification": [ "doclib:1611227666770" ], "cm:description": "", "cm:taggable": null, "cm:initialVersion": true }, "aspectNames": [ "cm:versionable", "cm:author", "cm:thumbnailModification", "cm:titled", "rn:renditioned", "cm:auditable", "cm:taggable" ], "isFolder": false, "isFile": true }, "resourceBefore": { "@type": "NodeResource", "modifiedAt": "2021-01-21T11:14:25.223Z", "properties": { "cm:title": null, "cm:taggable": null, "cm:description": null }, "aspectNames": [ "cm:versionable", "cm:author", "cm:thumbnailModification", "cm:titled", "rn:renditioned", "cm:auditable" ] }, "resourceReaderAuthorities": [ "GROUP_EVERYONE" ], "resourceDeniedAuthorities": [] } }
The event data payload looks very similar to the data for a created node. There is just one extra object called resourceBefore that contains the property values before the update. In this case we can see that the cm:title property of the cm:titled aspect has been filled in (i.e. data.resource.properties.cm:title: "Purchase Order").
Using the Node Browser (see Using the Node Browser) the following NodeRefs were resolved as follows:
"id": "d71dd823-82c7-477c-8490-04cb0e826e65", /app:company_home/cm:Testing/cm:Inbound/cm:purchase-order-scan.pdf (cm:content) "primaryHierarchy": [ "5f355d16-f824-4173-bf4b-b1ec37ef5549", /app:company_home/cm:Testing/cm:Inbound (cm:folder) "93f7edf5-e4d8-4749-9b4c-e45097e2e19d", /app:company_home/cm:Testing (cm:folder) "c388532e-8da6-4d50-a6d2-4f3f3ac36ff7", /app:company_home (cm:folder) "2fa2cde5-9d83-4460-a38c-cfe4ec9cca08" Store root (sys:store_root)
The event payload is telling us that a file called purchase-order-scan.pdf (i.e. data.resource.name) of type cm:content (i.e. data.resource.nodeType) was updated by the user admin (i.e. data.resource.createdByUser.id) in the /Company Home/Testing/Inbound folder (i.e. data.resource.primaryHierarchy[0]). The updated node has a Node ID d71dd823-82c7-477c-8490-04cb0e826e65 (i.e. data.resource.id).
To find out the display name for a folder or file via its Node ID use the ReST API to get metadata (see Getting Folder/File Metadata). This call can also be used to get other properties for the created node as not all are returned in the event data (i.e. data.resource.properties).
When subscribing to the org.alfresco.event.node.Updated event it’s possible to filter out anything that is of no interest. So for example, if you are interested in files with content type cm:content updated in the folder called /Company Home/Testing/Inbound (e.g. Node ID 5f355d16-f824-4173-bf4b-b1ec37ef5549) it would be easy to configure this.
SDK5 - Plain Java
The following code shows how this can be done with SDK 5 and plain Java event handlers:
package org.alfresco.tutorial.events; import org.alfresco.event.sdk.handling.filter.EventFilter; import org.alfresco.event.sdk.handling.filter.IsFileFilter; import org.alfresco.event.sdk.handling.handler.OnNodeUpdatedEventHandler; import org.alfresco.event.sdk.model.v1.model.DataAttributes; import org.alfresco.event.sdk.model.v1.model.NodeResource; import org.alfresco.event.sdk.model.v1.model.RepoEvent; import org.alfresco.event.sdk.model.v1.model.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Sample event handler to demonstrate reacting to a document/file being updated. */ @Component public class ContentUpdatedEventHandler implements OnNodeUpdatedEventHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ContentUploadedEventHandler.class); public void handleEvent(final RepoEvent<DataAttributes<Resource>> repoEvent) { NodeResource beforeUpdateResource = (NodeResource) repoEvent.getData().getResourceBefore(); NodeResource afterUpdateResource = (NodeResource) repoEvent.getData().getResource(); LOGGER.info("A file was updated in the repository: {}, {}, {}", afterUpdateResource.getId(), afterUpdateResource.getNodeType(), afterUpdateResource.getName()); } public EventFilter getEventFilter() { return IsFileFilter.get() // Make sure it's a file .and(ParentFolderFilter.of("5f355d16-f824-4173-bf4b-b1ec37ef5549")); // Located in the /Company Home/Testing/Inbound folder } }
Note that you can get to the property values before the update via the repoEvent.getData().getResourceBefore() call. You can compare those to the values retreived via repoEvent.getData().getResource() and see what’s changed.
This code uses a custom ParentFolderFilter. For more information on a parent folder filter, see Software Development Kits (SDK).
For more information about how to extract all the properties from the message payload see the NodeResource object information available at Software Development Kits (SDK).
To create an SDK event handler project that uses Spring Integration follow the instructions on spring integration event handlers available at Software Development Kits (SDK).
SDK5 - Spring Integration
The following code shows how this can be done with SDK 5 and Spring Integration event handlers:
package org.alfresco.tutorial.events; import org.alfresco.event.sdk.handling.filter.EventTypeFilter; import org.alfresco.event.sdk.handling.filter.IsFileFilter; import org.alfresco.event.sdk.integration.EventChannels; import org.alfresco.event.sdk.integration.filter.IntegrationEventFilter; import org.alfresco.event.sdk.model.v1.model.DataAttributes; import org.alfresco.event.sdk.model.v1.model.NodeResource; import org.alfresco.event.sdk.model.v1.model.RepoEvent; import org.alfresco.event.sdk.model.v1.model.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.integration.dsl.IntegrationFlowAdapter; import org.springframework.integration.dsl.IntegrationFlowDefinition; import org.springframework.messaging.Message; import org.springframework.stereotype.Component; /** * Spring Integration based event handler that will execute code when a file is updated */ @Component public class UpdatedContentFlow extends IntegrationFlowAdapter { private static final Logger LOGGER = LoggerFactory.getLogger(UpdatedContentFlow.class); // Use builder to create an integration flow based on alfresco.events.main.channel event channel @Override protected IntegrationFlowDefinition<?> buildFlow() { return from(EventChannels.MAIN) // Listen to events coming from the Alfresco events channel .filter(IntegrationEventFilter.of(EventTypeFilter.NODE_UPDATED)) // Filter events and select only node updated events .filter(IntegrationEventFilter.of(IsFileFilter.get())) // Filter node and make sure it is a file node .filter(IntegrationEventFilter.of(ParentFolderFilter.of("5f355d16-f824-4173-bf4b-b1ec37ef5549"))) // Filter node and make sure we got correct parent folder ID (/Company Home/Testing/Inbound) .handle(t -> handleEvent(t)); // Handle event with a bit of logging } private void handleEvent(Message message) { RepoEvent<DataAttributes<Resource>> repoEvent = (RepoEvent<DataAttributes<Resource>>)message.getPayload(); NodeResource beforeUpdateResource = (NodeResource) repoEvent.getData().getResourceBefore(); NodeResource afterUpdateResource = (NodeResource) repoEvent.getData().getResource(); LOGGER.info("File updated: Before update {}, after update {}", beforeUpdateResource.toString(), afterUpdateResource.toString()); } }
Note that you can get to the property values before the update via the repoEvent.getData().getResourceBefore() call. You can compare those to the values retreived via repoEvent.getData().getResource() and see what’s changed.
This code uses a custom ParentFolderFilter. For more information on a parent folder filter, see Software Development Kits (SDK).
For more information about how to extract all the properties from the message payload see the NodeResource object information available at Software Development Kits (SDK).
To create an SDK event handler project that uses Spring Integration follow the instructions on spring integration event handlers available at Software Development Kits (SDK).
Apache Camel
The following code snippet shows how this could be done with an Apache Camel route configuration:
public class SimpleRoute extends RouteBuilder { @Override public void configure() { from("amqpConnection:topic:alfresco.repo.event2") .id("UpdatedFileRoute") .log("${body}") // Log all incoming events on this topic, even those that we are not interested in .choice() .when() // When the following is true: // The event type is node updated .jsonpath("$[?(@.type=='org.alfresco.event.node.Updated' && " + // and the node that was updated is a file "@.data.resource.nodeType=='cm:content' && " + // and the file is located in the /Company Home/Testing/Inbound folder "'5f355d16-f824-4173-bf4b-b1ec37ef5549' in @.data.resource.primaryHierarchy[:1])]") // Unpack the data into JSON format .unmarshal("publicDataFormat") // Call a Spring Bean with the event data .bean("updatedEventHandlerImpl", "onReceive(*, COPY)") .end(); } }
The jsonpath expression uses several of the event data properties to filter out exactly the events we are interested in.
In this case a Spring Bean with ID updatedEventHandlerImpl is called at the end of the route from where you could make the necessary ReST API calls.