Guava EventBus Examples

0

In this article, I will show you examples of publish-subscribe-style communication between components using Google Guava.

We will be using the EventBus class for subscribing and publishing events. We will start with a simple example and then build on it.

You may also want to read my other article on Guava ImmutableList Examples.

Dependency

Add guava artifactId, version 18.0 to your pom.xml.

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.javarticles.guava</groupId>
	<artifactId>guavaEventBusExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>18.0</version>
		</dependency>
	</dependencies>
</project>

Let’s start with our first example.

Simple event publish subscribe example

For a method to receive events, it should be annotated with @Subscribe and a listener class can have one or more of such methods. The argument of this method defines what type of event this class is listening for. In our example, method task is subscribed for events of string type. To register the listener, you need to call eventBus.register(listener)

SimpleListener:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public class SimpleListener {
    @Subscribe
    public void task(String s) {
        System.out.println("do task(" + s + ")");
    }
}

SimpleEventBusExample:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;

public class SimpleEventBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new SimpleListener());
        System.out.println("Post Simple EventBus Example");
        eventBus.post("Simple EventBus Example");
    }
}

Output:

Post Simple EventBus Example
do task(Simple EventBus Example)

Publish Multiple Event Types

A class can listen to more than one type of event. In the below listener, we have methods listening for String events and Integer event.

MultipleListeners:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public class MultipleListeners {
    @Subscribe
    public void task1(String s) {
        System.out.println("do task1(" + s +")");
    }
    
    @Subscribe
    public void task2(String s) {
        System.out.println("do task2(" + s +")");
    }
    
    @Subscribe
    public void intTask(Integer i) {
        System.out.println("do intTask(" + i +")");
    }
}

MultipleEventTypeBusExample:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;

public class MultipleEventTypeBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new MultipleListeners());
        System.out.println("Post 'Multiple Listeners Example'");
        eventBus.post("Multiple Listeners Example");      
        eventBus.post(1);
    }
}

Output:

Post 'Multiple Listeners Example'
do task2(Multiple Listeners Example)
do task1(Multiple Listeners Example)
do intTask(1)

Listener Hierarchy

In this example, the listener class extends BaseListener which in turn extends AbstractListener. Once an event is published, all the @Subscribe annotated methods in the listener’s hierarchy will be notified.
ListenerHierarchy:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public class ListenerHierarchy extends BaseListener {
    @Subscribe
    public void task(String s) {
        System.out.println("do task(" + s +")");
    }    
}

BaseListener:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public class BaseListener extends AbstractListener {
    @Subscribe
    public void baseTask(String s) {
        System.out.println("do baseTask(" + s + ")");
    }
}

AbstractListener:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public abstract class AbstractListener {
    @Subscribe
    public void commonTask(String s) {
        System.out.println("do commonTask(" + s + ")");
    }
}

ListenerHierarchyEventBusExample:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;

public class ListenerHierarchyEventBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new ListenerHierarchy());
        System.out.println("Post 'Listener Hierarchy Example'");
        eventBus.post("Listener Hierarchy Example");      
    }
}

Output:

Post 'Listener Hierarchy Example'
do commonTask(Listener Hierarchy Example)
do task(Listener Hierarchy Example)
do baseTask(Listener Hierarchy Example)

Events Hierarchy

If there are subscribers listening for concrete events and generic events, the concrete event listeners will get the notification first and then the generic ones.

In the below example, the FruitEaterListener contains a subscriber method called eat. It can eat generic fruit as well as specific fruit ‘Apple’. If an ‘Apple’ is published, first eat(Apple) gets notified and then eat(Fruit).

FruitEaterListener:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public class FruitEaterListener {
    @Subscribe
    public void eat(Fruit fruit) throws RawFruitException {
        System.out.println("eat(Fruit " + fruit +")");
    }  
    
    @Subscribe
    public void eat(Apple apple) {
        System.out.println("eat(" + apple +")");
    } 
}

Fruit:

package com.javarticles.guava;

public class Fruit {
    private String name;
    public Fruit(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }   
    public String toString() {
        return name;
    }
}

Apple:

package com.javarticles.guava;

public class Apple extends Fruit {

    public Apple() {
        super("Apple");
    }

}

EventHierarchyEventBusExample:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;

public class EventHierarchyEventBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        eventBus.register(new FruitEaterListener());
        System.out.println("Post 'Apple'");
        eventBus.post(new Apple());  
        System.out.println("Post 'Orange as Fruit'");
        eventBus.post(new Fruit("Orange"));  
    }
}

Output:

Post 'Apple'
eat(Apple)
eat(Fruit Apple)
Post 'Orange as Fruit'
eat(Fruit Orange)

Handling Subscriber thrown Exception

Let’s make some more changes to Fruit to make sure that the fruit is not raw. If it is raw, it won’t consume the fruit and RawFruitException will be thrown.
We will see how we can handle the exception.

FruitEaterListener:

package com.javarticles.guava;

import com.google.common.eventbus.Subscribe;

public class FruitEaterListener {
    @Subscribe
    public void eat(Fruit fruit) throws RawFruitException {
        if (!fruit.isRipe()) {
            System.out.println("Can't consume the mango, it is raw");
            throw new RawFruitException();
        }
        System.out.println("eat(Fruit " + fruit +")");
    }  
    
    @Subscribe
    public void eat(Apple apple) {
        System.out.println("eat(" + apple +")");
    } 
}

To handle the exception, we need to implement SubscriberExceptionHandler. The exception handler will check if the fruit is raw, if yes, it will ripe it and re-post it.

HandleNotAfruitException:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;

public class HandleNotAfruitException implements SubscriberExceptionHandler {

    public void handleException(Throwable exception,
            SubscriberExceptionContext context) {
        if (exception instanceof RawFruitException) {
            Fruit fruit = (Fruit) context.getEvent();
            fruit.ripe();
            EventBus eventBus = context.getEventBus();
            eventBus.post(fruit);
        }
    }
}

Output:

Post 'Raw Mango'
Can't consume the mango, it is raw
Mango is raw, let's ripe it
eat(Raw Mango)

Dead Event

If there is an event type for which there are no subscribers then we can register a DeadEvent listener which is fired when EventBus receives an event without any listener.
We can use DeadEvent for debug purposes to log warning messages.

DeadEventListener:

package com.javarticles.guava;

import com.google.common.eventbus.DeadEvent;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;

public class DeadEventListener {
    @Subscribe
    public void gotDeadEvent(DeadEvent deadEvent) {
        EventBus eventBus = (EventBus) deadEvent.getSource();
        System.out.println("Got dead event " + deadEvent.getEvent() + ", from " + eventBus);
    }
}

DeadEventBusExample:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;

public class DeadEventBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus(){
            public String toString() {
                return "DeadEventBus";
            }
        };
        eventBus.register(new DeadEventListener());
        System.out.println("Post 'Some Event'");
        eventBus.post("Some Event");
    }
}

Output:

Post 'Some Event'
Got dead event Some Event, from DeadEventBus

Unregister Event

You can unregister a listener and then it will stop receiving events.

UnregisterEventBusExample:

package com.javarticles.guava;

import com.google.common.eventbus.EventBus;

public class UnregisterEventBusExample {
    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        SimpleListener simpleListener = new SimpleListener();
        eventBus.register(simpleListener);
        System.out.println("Post Simple EventBus Example");
        eventBus.post("Simple EventBus Example");
        System.out.println("Unregister the subscriber");
        eventBus.unregister(simpleListener);
        System.out.println("Post Simple EventBus Example once again");
        eventBus.post("Simple EventBus Example");
    }
}

Output:

Post Simple EventBus Example
do task(Simple EventBus Example)
Unregister the subscriber
Post Simple EventBus Example once again

Download the source code

This was an example about Guava EventBus. You can download the source code here: guavaEventBusExample.zip

About Author

Ram's expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring. You can reach him at [email protected]

Comments are closed.