Stimulus with WebJars

Learn how to load Stimulus Javascript library with WebJars

Authors: Sergio del Amo

Micronaut Version: 4.6.3

1. Getting Started

In this guide, we will create a Micronaut application written in Java.

2. What you will need

To complete this guide, you will need the following:

3. Solution

We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.

4. Writing the Application

Create an application using the Micronaut Command Line Interface or with Micronaut Launch.

mn create-app example.micronaut.micronautguide \
    --features=views-thymeleaf \
    --build=maven \
    --lang=java \
    --test=junit
If you don’t specify the --build argument, Gradle with the Kotlin DSL is used as the build tool.
If you don’t specify the --lang argument, Java is used as the language.
If you don’t specify the --test argument, JUnit is used for Java and Kotlin, and Spock is used for Groovy.

The previous command creates a Micronaut application with the default package example.micronaut in a directory named micronautguide.

If you use Micronaut Launch, select Micronaut Application as application type and add views-thymeleaf features.

If you have an existing Micronaut application and want to add the functionality described here, you can view the dependency and configuration changes from the specified features, and apply those changes to your application.

5. Views

To use the Thymeleaf Java template engine to render views in a Micronaut application, add the following dependency on your classpath.

pom.xml
<dependency>
    <groupId>io.micronaut.views</groupId>
    <artifactId>micronaut-views-thymeleaf</artifactId>
    <scope>compile</scope>
</dependency>

6. Stimulus

This tutorial uses Stimulus in a Micronaut application.

Stimulus is a JavaScript framework with modest ambitions. It doesn’t seek to take over your entire front-end—in fact, it’s not concerned with rendering HTML at all. Instead, it’s designed to augment your HTML with just enough behavior to make it shine.

7. WebJars

We use WebJars to load Stimulus into our application.

WebJars are client-side web libraries (e.g. jQuery & Bootstrap) packaged into JAR (Java Archive) files.

  • Explicitly and easily manage the client-side dependencies in JVM-based web applications

  • Use JVM-based build tools (e.g. Maven, Gradle, sbt, …​) to download your client-side dependencies

  • Know which client-side dependencies you are using

  • Transitive dependencies are automatically resolved and optionally loaded via RequireJS

  • Deployed on Maven Central

8. WebJars via Static Resources

To use WebJars, we need to add a static resources entry in the application’s configuration.

src/main/resources/application.properties
micronaut.router.static-resources.webjars.paths=classpath:/META-INF/resources/webjars
micronaut.router.static-resources.webjars.mapping=/webjars/**

9. Stimulus via WebJars

You can obtain the Stimulus Maven coordinate in the WebJars website and add it to your application as you will any other Java library.

pom.xml
<dependency>
    <groupId>org.webjars.npm</groupId>
    <artifactId>hotwired__stimulus</artifactId>
    <version>3.2.1</version>
    <scope>runtime</scope>
</dependency>

10. WebJars Test

You can test the Javascript library is exposed:

src/test/java/example/micronaut/WebJarsStimulusTest.java
package example.micronaut;

import io.micronaut.http.client.BlockingHttpClient;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

@MicronautTest
class WebJarsStimulusTest {

    @Test
    void stimulusViaWebJarsAvailable(@Client("/") HttpClient httpClient) {
        BlockingHttpClient client = httpClient.toBlocking();
        assertDoesNotThrow(() -> client.exchange("/webjars/hotwired__stimulus/3.2.1/dist/stimulus.js"));
    }
}

11. Controller

Create a controller which renders a View.

src/main/java/example/micronaut/HomeController.java
package example.micronaut;

import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.views.View;

import java.util.Collections;
import java.util.Map;

@Controller (1)
class HomeController {

    @Get (2)
    @View("index.html") (3)
    Map<String, Object> index() {
        return Collections.emptyMap();
    }
}
1 The class is defined as a controller with the @Controller annotation mapped to the path /.
2 The @Get annotation maps the method to an HTTP GET request.
3 Use View annotation to specify which template to use to render the response.

12. View

The view builds a simple controller described in the Hello Stimulus tutorial on the Stimulus website.

src/main/resources/views/index.html
<!DOCTYPE html>
<html lang="en" th:fragment="layout(title, script, content)" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="module">
            import { Application, Controller } from "/webjars/hotwired__stimulus/3.2.1/dist/stimulus.js"
            window.Stimulus = Application.start()

            Stimulus.register("hello", class extends Controller {
                static targets = [ "name" ]

                greet() {
                    const element = this.nameTarget
                    const name = element.value
                    console.log(`Hello, ${name}!`)
                }
            })
        </script>

    </head>
    <body>
        <div data-controller="hello">
            <input data-hello-target="name" type="text">
            <button data-action="click->hello#greet">Greet</button>
        </div>
    </body>
</html>

13. Controller Test

The controller test verifies the application renders an HTML page.

src/test/java/example/micronaut/HomeControllerTest.java
package example.micronaut;

import io.micronaut.http.client.BlockingHttpClient;
import io.micronaut.http.client.HttpClient;
import io.micronaut.http.client.annotation.Client;
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;

@MicronautTest
class HomeControllerTest {

    @Test
    void rootPathServesHtml(@Client("/") HttpClient httpClient) {
        BlockingHttpClient client = httpClient.toBlocking();
        String html = assertDoesNotThrow(() -> client.retrieve("/"));
        assertTrue(html.contains("<!DOCTYPE html>"));
    }
}

14. Testing the Application

To run the tests:

./mvnw test

15. Running the Application

To run the application, use the ./mvnw mn:run command, which starts the application on port 8080.

Go to http://localhost:8080 enter your name and click the button. You will see in the console a message:

micronaut webjars stimulus

16. GraalVM Resource Configuration

We need the following configuration to access resources in Native Image

By default, the native-image builder will not integrate any of the resources that are on the classpath into the native executable.

Create a new file src/main/resources/META-INF/native-image/example.micronaut.micronautguide/resource-config.json:

src/main/resources/META-INF/native-image/example.micronaut.micronautguide/resource-config.json
{
  "resources": {
    "includes": [
      {"pattern": "META-INF/resources/webjars/hotwired__stimulus/3.2.1/dist/stimulus.js$"}
    ]
  }
}

17. Native Tests

This plugin supports running tests on the JUnit Platform as native images. This means that tests will be compiled and executed as native code.

First, add the following profile to pom.xml:

 <profile>
      <id>native</id>
      <build>
        <plugins>
          <plugin>
            <groupId>org.graalvm.buildtools</groupId>
            <artifactId>native-maven-plugin</artifactId>
            <extensions>true</extensions>
            <executions>
              <execution>
                <id>test-native</id>
                <goals>
                  <goal>test</goal>
                </goals>
                <phase>test</phase>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
    </profile>

Then, to execute the native tests, execute:

./mvnw -Pnative test

INFO: A test may be disabled within a GraalVM native image via the @DisabledInNativeImage annotation.

18. Generate a Micronaut Application Native Executable with GraalVM

We will use GraalVM, an advanced JDK with ahead-of-time Native Image compilation, to generate a native executable of this Micronaut application.

Compiling Micronaut applications ahead of time with GraalVM significantly improves startup time and reduces the memory footprint of JVM-based applications.

Only Java and Kotlin projects support using GraalVM’s native-image tool. Groovy relies heavily on reflection, which is only partially supported by GraalVM.

18.1. GraalVM Installation

The easiest way to install GraalVM on Linux or Mac is to use SDKMan.io.

Java 21
sdk install java 21.0.5-graal

For installation on Windows, or for a manual installation on Linux or Mac, see the GraalVM Getting Started documentation.

The previous command installs Oracle GraalVM, which is free to use in production and free to redistribute, at no cost, under the GraalVM Free Terms and Conditions.

Alternatively, you can use the GraalVM Community Edition:

Java 21
sdk install java 21.0.2-graalce

18.2. Native Executable Generation

To generate a native executable using Maven, run:

./mvnw package -Dpackaging=native-image

The native executable is created in the target directory and can be run with target/micronautguide.

It is possible to customize the name of the native executable or pass additional build arguments using the Maven plugin for GraalVM Native Image building. Declare the plugin as following:

pom.xml
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
    <version>0.10.3</version>
    <configuration>
        <!-- <1> -->
        <imageName>mn-graalvm-application</imageName> (1)
        <buildArgs>
              <!-- <2> -->
          <buildArg>-Ob</buildArg>
        </buildArgs>
    </configuration>
</plugin>
1 The native executable name will now be mn-graalvm-application.
2 It is possible to pass extra build arguments to native-image. For example, -Ob enables the quick build mode.

Go to http://localhost:8080 enter your name and click the button. You will see in the console a message.

19. Next Steps

Explore more features with Micronaut Guides.

Read more about:

20. Help with the Micronaut Framework

The Micronaut Foundation sponsored the creation of this Guide. A variety of consulting and support services are available.

21. License

All guides are released with an Apache license 2.0 license for the code and a Creative Commons Attribution 4.0 license for the writing and media (images…​).