DEV Community

Committed Software
Committed Software

Posted on • Originally published at committed.software on

Vaadin Flow and Reactive Spring

philip swinburn 60220 unsplash

Vaadin 11 and Spring Boot 2

A couple of week back saw the release of Vaadin 11 which builds on the recent major changes to Vaadin 10. Vaadin allows Java developers to build web user interfaces in Java with Java components.

As we are heavy users of Java, and specifically Spring, Vaadin is an interesting technology for us. In this blog post, we will look at the use of some of the newer functionality of Spring Boot 2 with Vaadin.

This isn’t an introduction or tutorial for Vaadin, they already have some great resources and documentation for that. We’ll cover some tips (and gotchas) when using Vaadin and Spring.

Everything here works with Vaadin 10, which is the LTS version, but with Vaadin now following a 3-month release cycle the majority of developers will want to keep their apps up to date.

Spring and Vaadin

Vaadin 10+ plays well with Spring Boot 2 and has some specific annotations which can be used within components. This is covered in a whole chapter in the use Flow with Spring documentation.

We would highly recommend using Vaadin alongside Spring. If you are used to Vaadin, the Spring integration is very light but Spring will provide you with a considerable amount of additional engineering support:

  • Database access through with support for
  • Access to caching locally and externally
  • Authentication, including social sign on
  • Messaging support
  • External configuration mechanism
  • Executable JAR and service wrappers

In short, combining Spring and Vaadin you have a complete stack before you’ve written any code yourself.

If you are starting out with Vaadin they have a project starter for Spring Project Base with Spring.

If you have a paid Vaadin subscription, they also have a full stack Vaadin Spring starter too.

Using Reactive Spring

Spring Framework 5 and Spring Boot 2 offer the option to use the reactive types Flux and Mono.

Reactive types are a complex topic but consider them an improved, standardised and more flexible version of Java’s Stream (now reactive Flux) and Optional (now reactive Mono). Behind the scenes, they are designed for high throughput and non-blocking operations. Reactive types are an efficient and composable way to process data in applications.

Reactive types provide flexibility on which thread events are published, processed and consumed. Like many UI frameworks, Vaadin enforces strict control over which thread can update a UI component.

Here we have a counter of items, we can update by clicking the refresh button:

Text text = new Text("...");
Button button = new Button("Refresh", event -> {
  int count = 20;
  text.setValue(count + " items")
})

When the button is clicked, the event callback is run in a manner that allows UI updates and the text value updated.

So far, so un-reactive.

Suppose we have a Reactive Spring Data Repository:

// The repository
public interface PersonRepository extends ReactiveCrudRepository<Person, String> {

}

and use it in the following way

PersonRepository repo = ... // Typically injected by Spring

Text text = new Text("...");
Button button = new Button("Refresh", event -> {

  // Ask for the count
  Mono<Long> mono = repo.count();

  // WARNING: DOESN'T WORK!
  mono.subscribe(count -> text.setValue(count + " items"));
})

Here subscribe will call the lambda (count -> text.setValue) is called when then count operation is finished. This will happen asynchronously - the code above is basically just wiring an ‘on complete count’ callback. It will likely happen on the non-UI thread and hence without the proper UI locking that Vaadin requires. Clicking the button will cause an exception from Vaadin.

You could mono.block() in order to force waiting for the count, but that loses the benefits for reactive types. You don’t really want to force your UI to wait for long-running queries.

To run code on a UI thread you can use the UI.access() method. You can access the UI using the getUI() method on a Vaadin Component which returns an Optional. Thus an implementation of the above would be:

PersonRepository repo = ... // Auto wired / injected by Spring

Text text = new Text("...");
Button button = new Button("Refresh", event -> {
  Mono<Long> mono = repo.count();

  // In effect this says:
  // - when you get the count
  // - if you can get the UI
  // - schedule a ui update
  // - in that ui update, set the text value.
  mono.subscribe(count -> {
    getUI().ifPresent(ui -> {
      ui.access(() -> text.setValue(count + " items"));
    });
  }
})

That is quite a lot of code, and quite complex to read.

Taking a step back, really what we want is:

  • When the mono returns
  • Convert to it a set of commands (text.setValue)
  • Pass the commands to UI to execute

This can be done more neatly as:

repository.count()
    .map(count -> () -> text.setValue(count + "items"))
    .subscribe(doInUi(getUi()));

// where
public static Consumer<Command> doInUi(Optional<Ui> ui) {
  return command -> ui.ifPresent(u -> u.access(command));
}

You can make this nicer with Spring, by creating a UiSupport component which you can inject into your components:

@Component
@UIScope
public class UiSupport {

  private final UI ui;

  public UiSupport(@Inject UI ui) {
    this.ui = ui;
  }

  public Consumer<Command> doOnUi() {
    return command -> updateUi(command);
  }

  public void updateUi(Command command) {
    ui.access(command);
  }
}

Notice the component is scoped to the each individual Vaadin UI (@UiScope) because we will need one UiSupport per Vaadin UI (ie one for each user in each tab).

So our callback becomes:

@Autowired
    public MyComponent(UiSupport uiSupport) {

      Button button = new Button("Refresh", event -> {
          repository.count()
            .map(count -> () -> text.setValue(count + "items"))
            .subscribe(uiSupport.doOnUi());
      });
      //...
    }

Or perhaps more cleanly out of the lambdas:

@Autowired
    public MyComponent(UiSupport uiSupport) {
      this.uiSupport = uiSupport;

      Button button = new Button("Refresh", event -> refreshData());
      //...
    }

    private void refreshData() {
        repository.count()
          .map(this::setTextValue)
          .subscribe(uiSupport.doOnUi());
    }

    private Command setTextValue(long count) {
      return () -> text.setValue(count + "items")
    }

Reactive repositories as a Data Provider

Vaadin use DataProviders to provide the data items for tables, etc. If you are using Spring, you’ll often want this to be generated from a Spring Data Repository.

You can neatly wrap a reactive repository to a DataProvider using the fromCallbacks method:

public class ReactiveDataProvider {

  public static <T> DataProvider<T,?> fromReactiveRepository(ReactiveCrudRepository<T, ?> repository) {
    return DataProvider.fromCallbacks(
        query -> repository.findAll()
            // Basic sorting will work if you implement Comparable<T> on your data item (T)
            // but it's better to so this in the database using Sort
            // which is more performant and flexible
            // .sort(query.getInMemorySorting())
            .skip(query.getOffset())
            .take(query.getLimit())
            .toStream(),
        query -> repository.count().map(Math::toIntExact).block()
    );
  }
}

The above doesn’t use the DataProvider query, so it’s a bit more effort if you want to access a filtered result set. For a simple, but flexible, approach to filtering look at the QueryByExampleExecutor<T> which provides a neat way of passing a filter based on the data item class.

The ReactiveMongoRespository has query by example, so we can use:

public class ReactiveDataProvider {

  /// ...

  public static <T> DataProvider<T,Example<T>> fromReactiveMongoRepository(ReactiveMongoRepository<T, ?> repository) {
    return DataProvider.fromFilteringCallbacks(
        query -> 
            findByExample(repository, query.getFilter())
            .skip(query.getOffset())
            .take(query.getLimit())
            .toStream(),
        query -> countByExample(repository, query.getFilter())
            .map(Math::toIntExact).block()
    );
  }

  private static <T> Flux<T> findByExample(ReactiveMongoRepository<T,?> repository, Optional<Example<T>> filter) {
    if(filter.isPresent()) {
      return repository.findAll(filter.get());
    } else {
      return repository.findAll();
    }
  }

  private static <T> Mono<Long> countByExample(ReactiveMongoRepository<T,?> repository, Optional<Example<T>> filter) {
    if(filter.isPresent()) {
      return repository.count(filter.get());
    } else {
      return repository.count();
    }
  }
}

You can use this in your components with:

dataProvider = ReactiveDataProvider.fromReactiveMongoRepository(repository);
filteredDataProvider = dataProvider.withConfigurableFilter();

// You'd likely set this in response to user input but...
filteredDataProvider.setFilter(Example.of(new Person("hello")));

There’s a lot more you can do with Example using ExampleMatchers.

Broadcaster and Spring application

As Vaadin code exists on the server it has access to the full state of the application. For example, all the logged in users.

So functionality such as broadcast messaging or shared state is easily implemented in Vaadin. The Vaadin documentation uses a broadcast pattern to achieve this.

In Spring, we already have nice functionality for passing events around the application using @EventListener. However, it seems that you can not use this within Vaadin components, see Github issue 272.

In order to bridge Spring events to Vaadin we use the same broadcaster pattern - combining broadcast via the registered listeners and Spring components:

// This is a unified broadcaster outputting to Spring and (registered) Vaadin components
public abstract class AbstractBroadcaster<T> {
  private Executor executor = Executors.newSingleThreadExecutor();

  private LinkedList<Consumer<T>> listeners = new LinkedList<>();

  @Autowired
  private ApplicationEventPublisher publisher;

  public synchronized BroadcastRegistration register(Consumer<T> listener) {
    listeners.add(listener);

    return () -> {
      synchronized (Broadcaster.class) {
        listeners.remove(listener);
      }
    };
  }

  public synchronized void broadcast(T message) {
    // Send message to Spring components
    publisher.publishEvent(message);

    // Send message to our listeners in Vaadin
    for (Consumer<T> listener : listeners) {
      executor.execute(() -> listener.accept(message));
    }
  }
}

You can then create your own broadcaster for your specific event with:

@Service
public class PersonChangedBroadcaster extends AbstractBroadcaster<PersonsChangedEvent> {

}

You can now register a Vaadin route to respond to the event. Notice we register with the broadcaster onAttach and deregister onDetach.

@Route
@Push
public class MainView extends VerticalLayout {

  private final VaadinFlux vf;
  private final PersonRepository repository;
  private final PersonChangedBroadcaster notifier;
  private final DataProvider<Person,Example<Person>> dataProvider;
  private BroadcastRegistration notifierBroadcastRegistration;

  public MainView(VaadinFlux vf,
      @Autowired PersonRepository repository, PersonChangedBroadcaster notifier) {
    this.template = template;
    this.vf = vf;
    this.repository = repository;
    this.notifier = notifier;

    dataProvider = ReactiveDataProvider.fromReactiveMongoRepository(repository);

    // Create components here...
  }

  private void refreshData() {
    // When we are prompted refresh the data
    vf.updateUi(() -> dataProvider.refreshAll());
  }

  @Override
  protected void onAttach(AttachEvent attachEvent) {
    super.onAttach(attachEvent);

    // Register for events as soon as we attach
    this.notifierBroadcastRegistration = notifier.register(p -> this.refreshData());
  }

  @Override
  protected void onDetach(DetachEvent detachEvent) {
    super.onDetach(detachEvent);

    // When we 'detach' we no longer want events any more
    if (notifierBroadcastRegistration != null) {
      notifierBroadcastRegistration.deregister();
      notifierBroadcastRegistration = null;
    }
  }
}

Our MainView view will automatically update (thanks to @Push) whenever a PersonChangedEvent is sent.

A typical use case, inside Spring would be to automatically refresh all users’ UI when a data item is added or deleted (by any user). In Spring Mongo we can take advantage of the AbstractMongoEventListener to listen for save and delete functions. We can then push new events into our broadcaster:

@Service
public class PersonChangedListener extends AbstractMongoEventListener<Person> {

  private final PersonChangedBroadcaster notifier;

  public PersonChangedListener(ApplicationEventPublisher publisher,
      PersonChangedBroadcaster notifier) {
    this.notifier = notifier;
  }

  @Override
  public void onAfterSave(AfterSaveEvent<Person> event) {
    super.onAfterSave(event);
    notifyListeners(new PersonsChangedEvent(event.getSource()));
  }

  @Override
  public void onAfterDelete(AfterDeleteEvent<Person> event) {
    super.onAfterDelete(event);
    notifyListeners(new PersonsChangedEvent(event.getSource()));
  }

  private void notifyListeners(PersonsChangedEvent event) {
    notifier.broadcast(event);
  }
}

Using Vaadin

If, like us, you develop your Web UI with a framework such as React, the idea of moving back to Java might seem an odd step.

Where we feel Vaadin really shines is to support our backend services. These don’t have consumer user interfaces, but they do benefit from management UIs. An example might be data microservice, where we offer an API to programmatically create, list or delete data, but we’d also like to provide a management UI to allow that to be easily entered by a system manager. We might also expose other functionality via the Vaadin UI such a data export and batch import, which aren’t part of the API.

For these types of application, Vaadin allows us to quickly develop a UI which:

  • Has a simple unified build process for a service, since all code is compiled through Maven
  • Is deployed as part of the service itself, integrated into a single JAR
  • Takes advantage of the common Java code base (no duplication of code or opportunity for data transfer objects to diverge)

Outside that use case, Vaadin really shines where the server state is complex and needs to be shared. The above shows how in a few lines we can update all the users on important changes in state, triggered by the database. Thanks to Spring and Reactor we have non-blocking, performant code.

Photo by Philip Swinburn on Unsplash.

Top comments (0)