Recently I started working with reactive streams and event based systems using projectreactor.io. I thought it would be nice to use an event based approach to communicate between UI and server, so I dug around a bit and found vaadin-spring and vaadin-push.
So here is my buzzword-clock, a single file application that shows the current time using as many fancy technologies as possible:
- Spring Boot with web-starter
- Spring @Scheduled for the ticking clock
- vaadin spring for the UI
- vaadin push with atmosphere for updating the web-UI from the server
- Reactor for the (async) EventBus.
For a full pom.xml, check out the repo.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.jangalinski.playground.clock; | |
import com.vaadin.annotations.Push; | |
import com.vaadin.server.VaadinRequest; | |
import com.vaadin.spring.annotation.SpringUI; | |
import com.vaadin.ui.Label; | |
import com.vaadin.ui.UI; | |
import com.vaadin.ui.VerticalLayout; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import org.springframework.scheduling.annotation.EnableScheduling; | |
import org.springframework.scheduling.annotation.Scheduled; | |
import org.springframework.stereotype.Component; | |
import reactor.Environment; | |
import reactor.bus.Event; | |
import reactor.bus.EventBus; | |
import reactor.fn.Consumer; | |
import java.time.LocalTime; | |
import java.time.format.DateTimeFormatter; | |
import static reactor.bus.selector.Selectors.$; | |
@SpringBootApplication | |
public class BuzzwordClockApplication { | |
public static String TOPIC = "clock"; | |
public static void main(String... args) { | |
SpringApplication.run(BuzzwordClockApplication.class, args); | |
} | |
@Configuration | |
public static class ReactorConfiguration { | |
static { | |
Environment.initializeIfEmpty().assignErrorJournal(); | |
} | |
@Bean | |
public EventBus eventBus() { | |
return EventBus.create(Environment.get()); | |
} | |
} | |
@Component | |
@EnableScheduling | |
public static class Clock { | |
@Autowired | |
private EventBus eventBus; | |
@Scheduled(fixedDelay = 1000L) | |
public void everySecond() { | |
eventBus.notify(TOPIC, Event.wrap(LocalTime.now())); | |
} | |
} | |
@SpringUI | |
@Push | |
public static class ApplicationUI extends UI implements Consumer<Event<LocalTime>> { | |
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); | |
@Autowired | |
private EventBus eventBus; | |
private final Label labelTime = new Label(LocalTime.now().format(dateTimeFormatter)); | |
@Override | |
protected void init(VaadinRequest vaadinRequest) { | |
labelTime.setStyleName("h1"); | |
final VerticalLayout layout = new VerticalLayout(); | |
layout.setMargin(true); | |
layout.setSpacing(true); | |
layout.addComponent(labelTime); | |
setContent(layout); | |
eventBus.on($(TOPIC), this); | |
} | |
@Override | |
public void accept(reactor.bus.Event<LocalTime> event) { | |
access(() -> labelTime.setValue(event.getData().format(dateTimeFormatter))); | |
} | |
} | |
} |
Main Building blocks
- It is a spring-boot application, starting an embedded tomcat servlet container.
- It defines a Vaadin UI spring bean that is discovered and bound to “/”
- The “clock” bean is scheduled, its method is triggered every second.
- Using the reactor framework, it defines an Environment and an EventBus bean that loosly couples the server and the UI.
- Every second an event is fired (notify) containing the current local time as payload
- The UI consumes the localtime event and updates the label with the formatted time
Thanks to
For the push part, I leveraged the Using Server-Push in a Vaadin app example by P.J.Meisch.
Open points
Seams like I didn’t get the @EnableReactor annotation … I had to configure the reactor environment in a static block.