Leveraging Spring Container Events

Another thing you don’t have to invent yourself when building a Spring application is in-process eventing.  There is a lot of literature out there about the architectural advantages of eventing and messaging using a broker such as JMS or AMQP.  Spring provides lightweight templates for publishing and subscribing with these brokers, but that’s not what I am going to talk about.  The architectural principals that you benefit from with using a message broker are decoupling (you never talk directly to the listener of your message) and asynchronous processing (you don’t block waiting for a response).  Without a message broker, it’s still possible to benefit from these principals by simply plugging in to the Spring Container’s own publish and subscribe mechanism.

Old Mailbox wedged into a wall

Why would you want to do container messaging versus broker messaging?  First of all it’s never an either/or situation, you can leverage both, it’s simply a matter of what you can do locally versus distributed and it’s simple enough to mix and match.   Let’s take a common example use case for this: a user signs up for your web application.  This triggers all sorts of system activity, from creating an entry in a User table, to starting a trial or subscription, to sending a welcome email, or perhaps initiating an email confirmation process, or if you are “social” application pulling data from the connected social networks.  Imagine a UserController kicking this off by accepting a registration request.  A “coupled” approach would have this controller invoke each and every one of these processes in sequence, in a blocking or non-blocking fashion OR it could publish an event that a new user was created, and services dedicated to each function would pick up the message and act appropriately.  The difference between the two approaches is that the former is a God Object anti-pattern, and the latter is a nicely decoupled Pub/Sub pattern with a sprinkle of by Responsibility-driven design.  An added bonus is the ability to test all of these functions in isolation – the pub/sub mechanism simply binds them together at runtime.

Let’s look a bit more in detail about how this works.  First you need to decide what events you want to publish.  In the example above, it makes sense to have a UserCreationEvent with an attribute of the User, or the UniqueId of the user whatever you feel more comfortable with.  This object would extend Spring’s ApplicationEvent which itself has an event timestamp attribute.  So the class might look like this:

public class UserCreationEvent extends ApplicationEvent {
     private User user;

    public UserCreationEvent (Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

To publish this event, in our case from the UserController or better yet a UserService that takes care of creating the user in the system, we can just wire up the ApplicationEventPublisher:

@Autowired
 private ApplicationEventPublisher publisher;

Then when we’ve finished creating the user in the system, we can call

UserCreationEvent userCreationEvent = new UserCreationEvent (this, user);
publisher.publishEvent(userCreationEvent );

Once the event is fired, the Spring container will invoke a callback on all registered listeners of that type of event.  So let’s see how that part is set up.

A listener is simply a bean that implements ApplicationListener<YourEventType>, so in our case ApplicationListener<UserCreationEvent >.  Our listeners will all implement their own callback logic for the onApplicationEvent(UserCreationEvent  event) method.  If you are using Spring’s component scan capability this will be registered automatically.

@Component
public class UserWelcomeEmailListener implements ApplicationListener<UserCreationEvent > {

@Autowired
private EmailService emailService;

@Override
public void onApplicationEvent(UserActionEvent event) {
emailService.sendWelcomeEmail(event.getUser());
}
}

It’s important to note a few things about the default event multicasting mechanism:

  • It is all invoked on the same thread
  • One listener can terminate broadcasting by throwing an exception, and thus no guaranteed delivery contract

You can address these limitations by making sure all long-running operations are executed on another thread (using @Async for example) or plugging in your own (or supplied) implementation of the Multicaster by registering a bean that implements the interface with the name “applicationEventMulticaster”.  One easy way is to extend SimpleApplicationEventMulticaster and supply a threaded Executor.
To avoid one listener spoiling the party, you can either wrap all logic within each listener in a try/catch/finally block or in your custom Multicaster, wrap the calls to the handlers themselves in try/catch/finally blocks.

Another thing to be aware of as you think about this – if a failure in any of the processing that occurs after the event is published causes any inconsistency in the data or state of the User in our case, then you can’t do all this.  Each operation has to be able to deal with it’s own failures, and recovery process.  In other words, don’t do anything that needs to be part of the whole “create user” transaction into this type of pattern, in that case you don’t have a decoupled process so it’s better to not pretend you do.

As I mentioned before, there is no reason you can’t also leverage a distributed message broker in concert with Application events.  Simply have an application event listener publish the event to the broker (as sort of a relay).  In this way you get the benefit of both local and distributed messaging.  For example, imagine your billing service is another system that requires messages through RabbitMQ.  Create an ApplicationListener, and post the appropriate message.  You’ve achieved decoupling within and between applications, and leveraged two types of messaging technologies for great justice.

@Component
public class SubscriptionSignupListener implements ApplicationListener<UserCreationEvent > {

@Autowired
RabbitTemplate rabbitTemplate;

@Override
public void onApplicationEvent(UserActionEvent event) {
SubscriptionMessage newSubMsg = new SubscriptionMessage(new Date(), SubscriptionMessage.TRIAL_START, event.getUser());
rabbitTemplate.convertAndSend(MqConstants.NEW_SUB_KEY, newSubMsg);
}
}

So what about using something like Reactor instead?  Again, it’s not an either/or situation.  As Jon Brisbin notes in this article, Reactor is designed for “high throughput when performing reasonably small chunks of stateless, asynchronous processing” .  If your application or service has such processing, then by all means use that instead or in addition to ApplicationEvents.  Reactor in fact includes a few Excecutor implementations you can leverage so you can have your ApplicationEvent cake and eat it too!

Advertisements
Leveraging Spring Container Events