Event driven programming using Spring Boot and Reactor
Join the DZone community and get the full member experience.
Join For FreeReactor, as the name suggests, is heavily influenced by the well-known Reactor design pattern. But it is also influenced by other event-driven design practices, as well as several awesome JVM-based solutions that have been developed over the years. Reactor's goal is to condense these ideas and patterns into a simple and reusable foundation for making event-driven programming much easier.
About this blogpost
This blogpost will try to teach you the basics of event driven programming using spring boot and reactor. It won't cover every aspect of reactor, nor will one be able to use it as complete reference. I will, however, try to give as much examples as possible in my accompanying code.
Accompanying code
This small tutorial is accompanied by a Github Repository. Not all content of the code at the repository will be discussed here, so don't forget to check it out later!
The code was compiled and tested using the JDK8, and will therefore require you to have Java 8 to test this application.
If you find anything in the repository that is unclear, or you something you'd like to see a seperate blogpost of, feel free to file it as an issue in the repository.
Running the example
Simply download the code - either using git or plain archive downloading. Make sure you have gradle installed.
gradle bootRun
The Code
In this section I'll go over the important components which wire up the example application. It's a full stack application, which means that it'll contain a model, repositories, services and controllers, as wel as a basic view, written in thymeleaf. The frontend won't be a subject of this article, however, feel free to check it out on Github!
Gradle Dependencies
All we need on top of our standard Spring Boot starter imports, is the following dependency:
1.
compile(
"org.projectreactor.spring:reactor-spring-context"
)
Some Jpa simple entity
01.
@Entity
02.
public
class
LogMessage {
03.
@Id
04.
@GeneratedValue
05.
private
Long id;
06.
private
String text;
07.
private
Date logDate;
08.
@Enumerated
(value = STRING)
09.
private
LogCategory category;
10.
}
11.
12.
public
enum
LogCategory {
13.
DEBUG, ERROR, INFO
14.
}
What is described here, is a stripped-down version of an entity called LogMessage. A LogMessage will just be an entry in our database containing a basic String and some metadata, such as a logDate and an enumerated category.
A Restful repository
1.
@RepositoryRestResource
(collectionResourceRel =
"logs"
, path =
"logs"
)
2.
public
interface
LogMessageRepository
extends
JpaRepository<LogMessage, Long> {
3.
}
With Spring Data JPA we can avoid all the boilerplate code which would normally fill our application. Just a simple interface is enough to expose the database in a modern fashion. We also added the @RepositoryRestResource annotation, which will later expose the entire repository as a REST-API. This is done by Spring Data Rest.
The Reactor AutoConfiguration
This configuration really speaks for itself. We don't need any special reactor implementation, and therefore, we can count on Spring Boot to provide us with an active Environment, as well as a ReactorAutoConfiguration. Simply Enable it using the @EnableReactor annotation.
1.
@Configuration
2.
@EnableReactor
3.
public
class
ReactorConfiguration {
4.
}
Wiring up our components - The receiving part
Event driven infrastructures always consist of at least the following two parts: A Sender and a Receiver that will somehow listen or register on a given endpoint. Let's start with some example code of the receiving part.
We'll start with registering on 2 events.
First of all, we'll register on an event that's triggered once channel log.(trace|debug) is being notified. As you'll see we use reactor.event.selector.Selectors.R, which is a selector we can use to match a certain regular expression.
The second selector we'll be using, will be a class-selector.
reactor.event.selector.Selectors.T will react on the notification of a class, in our case ReactorExampleException.
We could also be using reactor.event.selector.Selectors.$, which is just a simple String-based selector. The syntax highly resembles the JQuery Selectors Syntax
01.
@Component
02.
public
class
ReactorLoggingConfiguration {
03.
04.
@Autowired
05.
private
Reactor r;
06.
07.
@Autowired
08.
private
LoggingService loggingService;
09.
10.
@PostConstruct
11.
public
void
onStartUp() {
12.
r.on(R(
"log.(trace|debug)"
), logForDebug());
13.
r.on(T(ReactorExampleException.
class
), logForException());
14.
}
15.
16.
private
Consumer<Event<ReactorExampleException>> logForException() {
17.
return
logException -> loggingService.log(LogCategory.ERROR, logException.getData().getMessage());
18.
}
19.
private
Consumer<Event<String>> logForDebug() {
20.
return
logInfoEvent -> loggingService.log(LogCategory.INFO, logInfoEvent.getData());
21.
}
22.
}
Ahh, Java 8, isn't it a beauty? Because the callback for an event is wrapped in a Consumer, we can leverage the hassle of creating a Consumer implementation by writing a lambda expression.
Wiring up the components - The sending part
The sending part is fairly easy. All you have to do is used an Injected version of Reactor - called r here - and invoke the notify(key, value)
1.
@RequestMapping
(method = GET)
2.
public
String welcome() {
3.
r.notify(
"log.debug"
, Event.wrap(
"Wew, someone accessed our page!"
));
4.
return
"main"
;
5.
}
The result
If we first start up our application, we'll quickly notice that we started with an empty database. By default, Spring boot looks for a DataSource implementation on the classpath. We just added a h2-database, so everyone can test this application without any 3d party necessities, such as a mysql database for example.
Because we're using spring data rest on our LogMessageRepository, it is being fully exposed. Simply browse to the following url to consume the self-explanatory API.
01.
{
02.
"_links"
: {
03.
"self"
: {
04.
"href"
:
"http://localhost:8080/logs?sort=desc{&page,size}"
,
05.
"templated"
:
true
06.
}
07.
},
08.
"page"
: {
09.
"size"
:
20
,
10.
"totalElements"
:
0
,
11.
"totalPages"
:
0
,
12.
"number"
:
0
13.
}
14.
}
Of course, our database is still empty.
Because we added an event that's being triggered once a user visits our homepage, we can trigger it ourselves by visiting the following url.
Upon visiting the rest api again, we can now see a populated database.
01.
{
02.
"_embedded"
: {
03.
"logs"
: [ {
04.
"text"
:
"Wew, someone accessed our page!"
,
05.
"logDate"
:
"2014-09-08T09:44:56.024+0000"
,
06.
"category"
:
"INFO"
,
07.
"_links"
: {
08.
"self"
: {
09.
"href"
:
"http://localhost:8080/logs/1"
10.
}
11.
}
12.
} ]
13.
}
14.
}
What more can we find in the Github repo?
In the repository, I also added a thymeleaf template which connects through websockets to the server. Everytime someone accesses the application by visiting the homepage, a small fragment of the homepage would be updated using JavaScript.
This all is merely an example of how one would use Reactor in a project. If any bugs or questions arise, feel free to mark them as an issue in the repository.
Opinions expressed by DZone contributors are their own.
Comments