Spring Integration event handlers - Spring Integration event handlers - Alfresco - Alfresco Events SDK for Out-of-Process Events - Alfresco/Alfresco-Events-SDK-for-Out-of-Process-Events/7.3/Alfresco-Events-SDK-for-Out-of-Process-Events/Install/Creating-event-handler-projects/Spring-Integration-event-handlers - 7.3 - 7.3

Alfresco Events SDK for Out-of-Process Events

Platform
Alfresco
Product
Alfresco Events SDK for Out-of-Process Events
Release
7.3
License
ft:lastPublication
2026-03-16T12:36:20.000000
ft:locale
en-US

Make sure you have completed prerequisites (see Prerequisites) and created a starter project (see Create a starting point Spring project).

To use Spring Integration based event handlers follow these steps:

Add the following dependency in the Maven project file (i.e. pom.xml):

<dependencies>
    <!-- Alfresco Java SDK 6 Spring Integration Event Handler API Spring Boot Starter -->
    <dependency>
        <groupId>org.alfresco</groupId>
        <artifactId>alfresco-java-event-api-spring-boot-starter</artifactId>
        <version>6.2.0</version>
    </dependency>
</dependencies>

Enable Spring Integration handlers in the src/main/resources/application.properties configuration file (by default pure Java event handlers is expected), add the following two extra properties:

# Where is Alfresco Active MQ JMS Broker running?
spring.activemq.brokerUrl=tcp://localhost:61616
# This property is required if you want Spring Boot to auto-define the ActiveMQConnectionFactory,
# otherwise you can define that bean in Spring config
spring.jms.cache.enabled=false
# Enable Spring Integration based event handlers
alfresco.events.enableSpringIntegration=true
# Turn off plain Java event handlers
alfresco.events.enableHandlers=false

Remove the default Spring Boot starter dependency (i.e. <artifactId>spring-boot-starter</artifactId>).

Test it:

$ mvn clean package -Dlicense.skip=true
[INFO] Scanning for projects...
...

$ java -jar target/events-0.0.1-SNAPSHOT.jar 
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.4.2)

2021-03-29 13:44:49.441  INFO 2599 --- [           main] o.a.tutorial.events.EventsApplication    : Starting EventsApplication v0.0.1-SNAPSHOT using Java 11.0.2 on MBP512-MBERGLJUNG-0917 with PID 2599 (/Users/mbergljung/IDEAProjects/docs-new/sdk5/sdk5-spring-integration-events-sample/target/events-0.0.1-SNAPSHOT.jar started by mbergljung in /Users/mbergljung/IDEAProjects/docs-new/sdk5/sdk5-spring-integration-events-sample)
2021-03-29 13:44:49.446  INFO 2599 --- [           main] o.a.tutorial.events.EventsApplication    : No active profile set, falling back to default profiles: default
2021-03-29 13:44:50.606  INFO 2599 --- [           main] faultConfiguringBeanFactoryPostProcessor : No bean named 'errorChannel' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created.
2021-03-29 13:44:50.613  INFO 2599 --- [           main] faultConfiguringBeanFactoryPostProcessor : No bean named 'taskScheduler' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created.
2021-03-29 13:44:50.621  INFO 2599 --- [           main] faultConfiguringBeanFactoryPostProcessor : No bean named 'integrationHeaderChannelRegistry' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created.
2021-03-29 13:44:50.775  INFO 2599 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.integration.config.IntegrationManagementConfiguration' of type [org.springframework.integration.config.IntegrationManagementConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-29 13:44:50.802  INFO 2599 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationChannelResolver' of type [org.springframework.integration.support.channel.BeanFactoryChannelResolver] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-29 13:44:50.803  INFO 2599 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'integrationDisposableAutoCreatedBeans' of type [org.springframework.integration.config.annotation.Disposables] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2021-03-29 13:44:51.924  INFO 2599 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
2021-03-29 13:44:52.014  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {logging-channel-adapter:_org.springframework.integration.errorLogger} as a subscriber to the 'errorChannel' channel
2021-03-29 13:44:52.015  INFO 2599 --- [           main] o.s.i.channel.PublishSubscribeChannel    : Channel 'application.errorChannel' has 1 subscriber(s).
2021-03-29 13:44:52.015  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean '_org.springframework.integration.errorLogger'
2021-03-29 13:44:52.015  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {transformer} as a subscriber to the 'acsEventsListeningFlow.channel#0' channel
2021-03-29 13:44:52.015  INFO 2599 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'application.acsEventsListeningFlow.channel#0' has 1 subscriber(s).
2021-03-29 13:44:52.015  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean 'acsEventsListeningFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsListeningFlow'
2021-03-29 13:44:52.015  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {recipient-list-router} as a subscriber to the 'acsEventsListeningFlow.channel#1' channel
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'application.acsEventsListeningFlow.channel#1' has 1 subscriber(s).
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean 'acsEventsListeningFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#1'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsListeningFlow'
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {bridge} as a subscriber to the 'alfresco.events.si.channel' channel
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'application.alfresco.events.si.channel' has 1 subscriber(s).
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean 'acsEventsSpringIntegrationFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsSpringIntegrationFlow'
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {bridge} as a subscriber to the 'acsEventsSpringIntegrationFlow.channel#1' channel
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'application.acsEventsSpringIntegrationFlow.channel#1' has 1 subscriber(s).
2021-03-29 13:44:52.016  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean 'acsEventsSpringIntegrationFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#1'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsSpringIntegrationFlow'
2021-03-29 13:44:52.017  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : Adding {bridge} as a subscriber to the 'alfresco.events.handlers.channel' channel
2021-03-29 13:44:52.017  INFO 2599 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'application.alfresco.events.handlers.channel' has 1 subscriber(s).
2021-03-29 13:44:52.017  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean 'acsEventsHandlersFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#0'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsHandlersFlow'
2021-03-29 13:44:52.017  INFO 2599 --- [           main] o.s.integration.channel.DirectChannel    : Channel 'application.acsEventsHandlersFlow.channel#1' has 1 subscriber(s).
2021-03-29 13:44:52.018  INFO 2599 --- [           main] o.s.i.endpoint.EventDrivenConsumer       : started bean 'acsEventsHandlersFlow.org.springframework.integration.config.ConsumerEndpointFactoryBean#1'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsHandlersFlow'
2021-03-29 13:44:52.018  INFO 2599 --- [           main] ishingJmsMessageListener$GatewayDelegate : started org.springframework.integration.jms.ChannelPublishingJmsMessageListener$GatewayDelegate@d8305c2
2021-03-29 13:44:52.539  INFO 2599 --- [           main] o.s.i.jms.JmsMessageDrivenEndpoint       : started bean 'acsEventsListeningFlow.jms:message-driven-channel-adapter#0'; defined in: 'class path resource [org/alfresco/event/sdk/autoconfigure/AlfrescoEventsAutoConfiguration.class]'; from source: 'bean method acsEventsListeningFlow'
2021-03-29 13:44:52.561  INFO 2599 --- [           main] o.a.tutorial.events.EventsApplication    : Started EventsApplication in 3.849 seconds (JVM running for 4.638)

Looks ready for some event handler code.

Now, start adding your event handler code, let’s add an event handler that will be triggered when a new document/file is uploaded. To do this we need to create a class that implements the org.springframework.integration.dsl.IntegrationFlow interface, we can use a helper adapter class (i.e. IntegrationFlowAdapter) for this:

package org.alfresco.tutorial.events;

import org.alfresco.event.sdk.handling.filter.EventTypeFilter;
import org.alfresco.event.sdk.integration.EventChannels;
import org.alfresco.event.sdk.integration.filter.IntegrationEventFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.dsl.IntegrationFlowAdapter;
import org.springframework.integration.dsl.IntegrationFlowDefinition;
import org.springframework.stereotype.Component;

/**
 * Spring Integration based event handler that will execute code when a file is uploaded
 */
@Component
public class NewContentFlow extends IntegrationFlowAdapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(NewContentFlow.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_CREATED)) // Filter events and select only node created events
                .handle(t -> LOGGER.info("File uploaded: {}", t.getPayload().toString())); // Handle event with a bit of logging
    }
}

Add the Spring Bean class into the same directory as the Spring Boot starter class. It doesn’t have to be added to this directory, but in this case we are just testing it, so no need to organize too much.

Now stop, build and start it up again:

$ ^C
...
$ mvn spring-boot:run -Dlicense.skip=true
...

Add a file via the Share user interface, you should see the following in the logs:

2021-03-30 10:09:22.738  INFO 9603 --- [erContainer#0-1] o.a.tutorial.events.NewContentFlow : File uploaded: RepoEvent [specversion=1.0, type=org.alfresco.event.node.Created, id=12100b22-8dae-4ebe-b114-ba9dc2f9755b, source=/3bc24dba-d1ae-4c04-af60-0294a4c68a7f, time=2021-03-30T09:09:22.711117Z, dataschema=https://api.alfresco.com/schema/event/repo/v1/nodeCreated, datacontenttype=application/json, data=EventData [eventGroupId=3196f0f6-b4aa-4834-9aa2-a58eaa8f121f, resource=NodeResource [id=4e1f0830-2452-4a4c-b20a-7146402ce665, name=somefile-again.txt, nodeType=cm:content, isFile=true, isFolder=false, createdByUser=UserInfo [id=admin, displayName=Administrator], createdAt=2021-03-30T09:09:22.324Z, modifiedByUser=UserInfo [id=admin, displayName=Administrator], modifiedAt=2021-03-30T09:09:22.324Z, content=ContentInfo [mimeType=text/plain, sizeInBytes=0, encoding=UTF-8], properties={cm:title=, app:editInline=true, cm:description=}, aspectNames=[app:inlineeditable, cm:titled, cm:auditable], primaryHierarchy=[19e067a9-5d2a-43ba-ac93-d273d938050c, 30afd06d-6ec7-4434-b1d6-1f7671b6b9a7, 7a82ddff-0869-430e-8cc8-623d97b98dc4]], resourceBefore=null]]

Now, this event handler will actually also be triggered when a folder is created. So how can we fix so the handler is only triggered when a file is created/uploaded? By adding a so called event filter to the class (see ):

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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.dsl.IntegrationFlowAdapter;
import org.springframework.integration.dsl.IntegrationFlowDefinition;
import org.springframework.stereotype.Component;

/**
 * Spring Integration based event handler that will execute code when a file is uploaded
 */
@Component
public class NewContentFlow extends IntegrationFlowAdapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(NewContentFlow.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_CREATED)) // Filter events and select only node created events
                .filter(IntegrationEventFilter.of(IsFileFilter.get())) // Filter node and make sure it is a file node
                .handle(t -> LOGGER.info("File uploaded: {}", t.getPayload().toString())); // Handle event with a bit of logging
    }
}

Here we are using the org.alfresco.event.sdk.handling.filter.IsFileFilter, which will make sure that the event handler is triggered only when the node type is cm:content or subtype thereof, which represents files. To use this filter with Spring Integration we use the IntegrationEventFilter wrapper.

If you are thinking, do I really need a whole class just to process an event? No you don’t, you can include a bean definition directly in the Spring Boot app class as follows:

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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;

@SpringBootApplication
public class EventsApplication {
    private static final Logger LOGGER = LoggerFactory.getLogger(EventsApplication.class);
    
    public static void main(String[] args) {
        SpringApplication.run(EventsApplication.class, args);
    }

    @Bean
    public IntegrationFlow logCreateFileNode() {
        return IntegrationFlows.from(EventChannels.MAIN) // Listen to events coming from the Alfresco events channel
                .filter(IntegrationEventFilter.of(EventTypeFilter.NODE_CREATED)) // Filter events and select only node created events
                .filter(IntegrationEventFilter.of(IsFileFilter.get())) // Filter node and make sure it is a file node
                .handle(t -> LOGGER.info("File uploaded: {}", t.getPayload().toString())) // Handle event with a bit of logging
                .get();
    }
}

It also makes sense to add error management code to the application class as follows:

@SpringBootApplication
public class EventsApplication {
    private static final Logger LOGGER = LoggerFactory.getLogger(EventsApplication.class);
    
    public static void main(String[] args) {
        SpringApplication.run(EventsApplication.class, args);
    }

    @Bean
    public IntegrationFlow logError() {
        return IntegrationFlows.from(EventChannels.ERROR).handle(t -> {
            LOGGER.info("Error: {}", t.getPayload().toString());
            MessageHandlingException exception = (MessageHandlingException) t.getPayload();
            exception.printStackTrace();
        }).get();
    }
...
}

For a complete list of events with sample code, see the Events Extension Points section in the Alfresco Content Services documentation.. For a complete list of Event Filters available in the SDK see Event handling library.

For information on how to implement a custom event filter see Implementing custom event filters.

For more information about how to extract all the properties from the message payload see The NodeResource object.