Search This Blog

Friday 10 August 2012

Publishing Events asynchronously

In the previous post we saw how Spring's ApplicationContext provided for event publishing and listening. The default listener that Spring implements is synchronous. The publish method will wait until the methods of all the listeners have completed successfully. This kind of blocking behaviour may not be suitable for all scenarios.
Hence Spring provides us with an alternative strategy:
  1. Use an implementation of the ApplicationEventMulticaster that supports asynchronous publishing.
  2. Create a bean of the same with id "applicationEventMulticaster"
  3. At start-up, if the ApplicationContext detects this bean, then it uses it for its Event publishing model.
Consider the xml configuration file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="customEventListener" class="com.listener.CustomEventSleepListener" />

    <bean id="applicationEventMulticaster"
        class="org.springframework.context.event.SimpleApplicationEventMulticaster">
        <property name="taskExecutor" >
            <bean class="org.springframework.core.task.SimpleAsyncTaskExecutor">
                </bean>
        </property>
    </bean>

</beans>
  1. The ApplicationContext inherits from the AbstractApplicationContext. It includes the below method that sets up the applicationEventMulticaster
    /**
     * Initialize the ApplicationEventMulticaster.
     * Uses SimpleApplicationEventMulticaster if none defined in the context.
     * @see org.springframework.context.event.SimpleApplicationEventMulticaster
     */
    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
            this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
            if (logger.isDebugEnabled()) {
            logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
    }
    
  2. If no ApplicationEventMulticaster is specified Spring creates a SimpleApplicationEventMulticaster. The default behaviour of the class is to publish events synchronously. This is how it worked in our previous post.
  3. However if we specify our own ApplicationEventMulticaster bean with id "applicationEventMulticaster", the ApplicationContext uses our instance. This is what we have done above.
  4. In case of SimpleApplicationEventMulticaster, if we specify our own Executor, then we can make it work in an asynchronous manner. In this case I have specified SimpleAsyncTaskExecutor as the executor to be used. This is a simple thread based executor that provides us with the needed asynchronous behaviour.
The start up logs indicate the same:
203  [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanF
actory  - Creating shared instance of singleton bean 'applicationEventMulticaster'
234  [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanF
actory  - Creating instance of bean 'org.springframework.core.task.SimpleAsyncTa
skExecutor#f99ff5'
...
250  [main] DEBUG org.springframework.beans.CachedIntrospectionResults  - Found 
bean property 'taskExecutor' of type [java.util.concurrent.Executor]
250  [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanF
actory  - Finished creating instance of bean 'applicationEventMulticaster'
To test the asynchronous behavior I created a listener that takes it own sweet time to complete.
public class CustomEventSleepListener implements
        ApplicationListener<CustomMsgEvent> {

    @Override
    public void onApplicationEvent(CustomMsgEvent applicationEvent) {
        System.out.println("AllApplicationEventListener " + " received applicationEvent - start");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }//sleep for 3 seconds
        System.out.println("AllApplicationEventListener " + " received applicationEvent - done");
    }

}
As can be seen, the thread actually sleeps for three seconds. Consider the test code:
public class TestAsync {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-async-publish.xml");
        System.out.println("Application context has been started - publishing message");
        CustomMsgEvent customMsgEvent = new CustomMsgEvent(applicationContext, "Test message");
        applicationContext.publishEvent(customMsgEvent); //-should not block here
        System.out.println("The event was published successfully");
    }
}
The output is as below:
Application context has been started - publishing message
Created a Custom event
The event was published successfully
AllApplicationEventListener  received applicationEvent - start
AllApplicationEventListener  received applicationEvent - done
As can be seen the publish method finished immediately without waiting for the listeners to complete execution.

12 comments:

  1. You should have a look at Spring Integration which provide better event publishing and listening.

    ReplyDelete
    Replies
    1. Thanks Nicolas, I'll give that a look. However if ApplicationContext's mechanism satisfies requirements then Spring Integration would probably be an over kill.

      Delete
  2. Can we define the behaviour? sometimes we want to have it synchronously... Thank you for your post.

    ReplyDelete
  3. Hi,
    The ApplicationContext by default supports synchronous event publication. You can directly use that - http://learningviacode.blogspot.in/2012/07/spring-and-events-and-listeners.html

    ReplyDelete
    Replies
    1. Thank you for your quick response Mr. Varghese. What I would mean was that I would have both (synchronous and asynchronous events) in the same project. I have 3 events: 2 need to be synchronous and 1 asynchronous. Any recommendation? Thank you for your time.

      Delete
    2. Hi Fran,
      Your requirements seem to lean more towards synchronous flow then asynchronous. But I guess that could change in the future.
      A simple solution would to be use the synchronous mode of publishing events. Where asynchronous response is needed, execute the code of the listeners on a separate thread.
      As far as I am aware making the same applicationContext delegate in both synchronous and asynchronous manner is not supported.
      Cheers

      Delete
  4. This is so interesting blog. You are best listing knowledge provide at this site. I am very excited read this nice article. You can visit my website.
    Property Development Events

    ReplyDelete
  5. Spring supports either sync or async event publishing in an ApplicationContext. Is there any way to make it support both sync and async event emitting in single context?

    ReplyDelete
    Replies
    1. As far as I am aware no :(. You can see how the application context is wired to use a single Event Publisher. What you can do is override that class.....

      Delete
    2. I think the best way to achieve both synchronous and asynchronous support would be to first chose for asynchronous and replace the default SimpleAsyncTaskExecutor by one that would dispatch task execution to threads that you could control by using a latch. In that case you would either wait for the latch to finish for synchronous behaviour and simply not wait for asynchronous behaviour.
      One other way would be to implement your own ApplicationEventMulticaster which would dispatch synchronous and asynchronous events handling to some specific handler (one would handle the message in the same thread, the other would create a new non-blocking thread)

      Delete
    3. Here is a post that shows how to do just that:
      http://www.keyup.eu/en/blog/101-synchronous-and-asynchronous-spring-events-in-one-application

      Delete
  6. This comment has been removed by a blog administrator.

    ReplyDelete