4

How to run Vaadin in Jakarta EE Containers

 9 months ago
source link: https://www.mastertheboss.com/web/vaadin/how-to-run-vaadin-in-jakarta-ee-containers/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

In this tutorial we will learn how to create a Vaadin Flow project that we can run on any Jakarta EE Container such as WildFly or TomEE. We will start from a starter template and we will adapt it to create a sample Customer Form that binds its fields into the Database.

Setting up the Vaadin Flow Jakarta EE project

Firstly, if you are new to Vaadin Flow, we recommend heading first to this introduction article: Vaadin Tutorial: Building Modern Web Applications with Ease

In order to bootstrap a Vaadin Flow project which contains the right dependencies for a Jakarta EE container (WildFly), you can surf to the project initializer at: https://start.vaadin.com/app

vaadin wildfly jakarta ee tutorial

Expand the options by clicking on Edit and choose as Architecture “Jakarta EE“. Then, download the project.

A quick look at the project

pom.xml

pom.xml reveals that, besides the Jakarta EE 10 dependencies and the general

vaadin

vaadin dependency, our project contains the

vaadin-cdi

vaadin-cdi dependency. This is essential to bridge the world of Vaadin Flow with Jakarta EE that is based on CDI:

<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>10.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<!-- Replace artifactId with vaadin-core to use only free components -->
<artifactId>vaadin</artifactId>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-cdi</artifactId>
</dependency>
<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-web-api</artifactId>
    <version>10.0.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.vaadin</groupId>
    <!-- Replace artifactId with vaadin-core to use only free components -->
    <artifactId>vaadin</artifactId>
</dependency>
<dependency>
    <groupId>com.vaadin</groupId>
    <artifactId>vaadin-cdi</artifactId>
</dependency>

Besides, the project also includes WildFly Maven plugin, therefore will deploy our application to WildFly in a snap.

Coding our Vaadin Flow Route

The built-in project already contains a couple of example Classes:

├── main
│ ├── java
│ │ └── org
│ │ └── vaadin
│ │ └── example
│ │ ├── AppShell.java
│ │ ├── GreetService.java
│ │ └── MainView.java
src
├── main
│   ├── java
│   │   └── org
│   │       └── vaadin
│   │           └── example
│   │               ├── AppShell.java
│   │               ├── GreetService.java
│   │               └── MainView.java
  • The AppShell Class is essential for defining and configuring the Progressive Web App aspects of a Vaadin application. It sets up PWA-related metadata and defines the theme to be used for styling UI components across the application.
  • The
    MainView
    MainView Class defines a Vaadin Flow Route with a minimal set of Components in it, to print an Hello Message.

In order to customize our project, we will customize the

MainView

MainView Class. The new implementation will contain a Form with some fields that will be submitted to the back-end Service:

@Route("")
@CdiComponent
public class MainView extends VerticalLayout {
@Inject
private CustomerService service;
private Binder<Customer> binder = new Binder<>(Customer.class);
@PostConstruct
public void init() {
TextField firstName = new TextField("First Name");
TextField lastName = new TextField("Last Name");
TextField email = new TextField("Email");
ComboBox<String> countryComboBox = new ComboBox<>("Country");
countryComboBox.setItems("USA", "Canada"); // Add your country options here
binder.bind(firstName, Customer::getFirstName, Customer::setFirstName);
binder.bind(lastName, Customer::getLastName, Customer::setLastName);
binder.bind(email, Customer::getEmail, Customer::setEmail);
binder.bind(countryComboBox, Customer::getCountry, Customer::setCountry);
Button registerButton = new Button("Register", event -> {
Customer customer = new Customer();
binder.writeBeanIfValid(customer);
service.register(customer);
// Perform registration logic here
String message = "Registration successful for: " +
firstName.getValue() + " " +
lastName.getValue() + ", Email: " +
email.getValue();
Notification.show(message);
// Clear all fields after registration is completed
binder.removeBean(); // Remove current bean from the binder
binder.setBean(new Customer()); // Set a new instance of Customer to the binder
registerButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
registerButton.addClickShortcut(Key.ENTER);
addClassName("centered-content");
add(firstName, lastName, email, countryComboBox, registerButton);
@Route("")
@CdiComponent
public class MainView extends VerticalLayout {

    @Inject
    private CustomerService service;
    private Binder<Customer> binder = new Binder<>(Customer.class);

    @PostConstruct
    public void init() {

        TextField firstName = new TextField("First Name");
        TextField lastName = new TextField("Last Name");
        TextField email = new TextField("Email");
        ComboBox<String> countryComboBox = new ComboBox<>("Country");
        countryComboBox.setItems("USA", "Canada"); // Add your country options here

        binder.bind(firstName, Customer::getFirstName, Customer::setFirstName);
        binder.bind(lastName, Customer::getLastName, Customer::setLastName);
        binder.bind(email, Customer::getEmail, Customer::setEmail);
        binder.bind(countryComboBox, Customer::getCountry, Customer::setCountry);

        Button registerButton = new Button("Register", event -> {

            Customer customer = new Customer();
            binder.writeBeanIfValid(customer);
            service.register(customer);

            // Perform registration logic here
            String message = "Registration successful for: " +
                    firstName.getValue() + " " +
                    lastName.getValue() + ", Email: " +
                    email.getValue();
            Notification.show(message);
            // Clear all fields after registration is completed
            binder.removeBean(); // Remove current bean from the binder
            binder.setBean(new Customer()); // Set a new instance of Customer to the binder
        });


        registerButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        registerButton.addClickShortcut(Key.ENTER);

        addClassName("centered-content");
        add(firstName, lastName, email, countryComboBox, registerButton);
    }

}

The most interesting addition is the use of the Binder Class. The Binder class in Vaadin is a crucial component used for binding data between UI components and Java objects. It plays a pivotal role in simplifying the process of synchronizing and validating data entered in UI components (like text fields, checkboxes, etc.) with corresponding data objects in Java.

In our example, by executing

binder.writeBeanIfValid(customer),

binder.writeBeanIfValid(customer), we copy the Form Components in a new Customer Object.

vaadin flow jakarta ee tutorial

Here is the Customer definition:

@Entity
public class Customer {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String email;
// Getters / setters
@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    // Getters / setters
}

Finally, once that we have all Customer properties in place, we can call the

service.register(customer)

service.register(customer) that will persist the Customer in the Database:

@ApplicationScoped
public class CustomerService {
@PersistenceContext
EntityManager em;
@Transactional
public void register(Customer customer) {
em.persist(customer);
@ApplicationScoped
public class CustomerService {
    @PersistenceContext
    EntityManager em;

    @Transactional
    public void register(Customer customer) {
        em.persist(customer);
    }
}

Running our Flow

Since the WildFly plugin is available, we can start WildFly and deploy the application as follows:

mvn install wildfly:deploy
mvn install wildfly:deploy

The application will register on the root Web Context. Therefore we can access it at: http://localhost:8080:

Screenshot-from-2023-12-16-19-51-25.png

You can experiment, by filling up the Form and clicking on the Register button. Verify that the operation completes successfully. For the sake of brevity, we didn’t discuss here the persistence.xml configuration of our application which uses just the H2 database. You can check the full source code at the end of this article.

Fixing the Error NoClassDefFoundError: com/sun/nio/file/ExtendedWatchEventModifier

By deploying the Vaadin starter on WildFly 30, I have hit the following error:

19:54:47,193 ERROR [com.vaadin.base.devserver.FileWatcher] (ForkJoinPool.commonPool-worker-18) Error starting file watcher: java.lang.NoClassDefFoundError: com/sun/nio/file/ExtendedWatchEventModifier
19:54:47,193 ERROR [com.vaadin.base.devserver.FileWatcher] (ForkJoinPool.commonPool-worker-18) Error starting file watcher: java.lang.NoClassDefFoundError: com/sun/nio/file/ExtendedWatchEventModifier

As discussed in this issue, this is related to a defect in the File Watcher when you are deploying a multi module project on WildFly. Until the issue is fixed, the workaround is to is to add com.sun.nio.file as a dependency.

Therefore, add the following configuration to your

maven.war.plugin

maven.war.plugin:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Dependencies>jdk.unsupported</Dependencies>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
            <archive>
                <manifestEntries>
                    <Dependencies>jdk.unsupported</Dependencies>
                </manifestEntries>
            </archive>
        </configuration>
</plugin>

Conclusion

This article was a complete walkthrough the set up and creation of a complete Vaadin Flow project. We have covered the key aspects related to the creation of a Form with some components and how to bind them so that we can use as DTO for a Service Class. We also have discussed how to fix an issue related to the deployment on WildFly of the Vaadin Flow Jakarta EE project.

Source code: https://github.com/fmarchioni/mastertheboss/tree/master/web/vaadin/jakartaee


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK