wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

Deploying your frontend as webJar


In an API driven world back-end and front-end are clearly separated and might live on different servers alltogether. However for smaller applications serving static files happens from the same place as your backend lives

So many choices

The web server that proxies your application server could have a rule for static files, your firewall could so that, you use a static directory on your application server or pack, that's the story here, your front-end into a jar. I'm not discussing the merits of the different approaches here, that's a story for another time, but describe the workflow and tools for the JAR approach.

vert.x static routes

In Vertx a static route can be declared with a few lines of code:

Router router = Router.router(vertx);
router.route("/ui/*")
      .handler(StaticHandler.create("uitarget"));

Vertx will then look for the folder uitarget in its current working directory or on the classpath. So you will need to put your jar on the classpath

The swagger-ui example

There are lots of prepackaged UI jars available and easy to integrate into vert.x. For example the Swagger UI. Define a dependency in your pom.xml and a one liner to access the code:

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>swagger-ui</artifactId>
    <version>4.1.3</version>
</dependency>
Router router = Router.router(vertx);
router.route("/assets/lib/*").handler(StaticHandler.create("META-INF/resources/webjars"));

Packing your own front-end

Most modern build front-ends owe their executable form to an npm build command. If you are not sure check the documentation for React, Angular, Lightning, Vue, Ionic or whatever framework you fancy.

There are two plugins for maven that can process front-end work:

  • The Frontend Maven Plugin: Specialized module that handles download of NodeJS and running your NodeJS based build tools. Great when you don't have NodeJS installed anyway
  • The Exec Maven Plugin: Generic plugin to run stuff. Doesn't download NodeJS for you. More work to setup (that's what I picked)

The steps you will need to perform, actually not you, but your mvn package run:

  • run npm install
  • run npm build
  • move files into the target directory structure
  • build the Jar

All of this can be wrapped into your pom.xml. I usually add the front-end as a module to the whole project, so a build is always complete

Sample pom.xml

<build>
    <resources>
        <!-- Output of the NPM build -->
        <resource>
            <directory>build</directory>
            <filtering>false</filtering>
        </resource>
        <!-- Direct into the JAR -->
        <resource>
            <directory>jar</directory>
            <filtering>false</filtering>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <!-- Prepare the JAR -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>${maven.jar.plugin.version}</version>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>${maven.exec.plugin.version}</version>
            <executions>
                <!-- Optional: The following will output the npm configuration.
                                 efffect: CI logs will show the npm information -->
                <execution>
                    <id>npm config list (validate)</id>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <phase>validate</phase>
                    <configuration>
                        <executable>npm</executable>
                        <arguments>
                            <argument>config</argument>
                            <argument>list</argument>
                        </arguments>
                    </configuration>
                </execution>
                <!-- Required: The following will ensure `npm install` is called
                                 before anything else during the 'Default Lifecycle' -->
                <execution>
                    <id>npm install (compile)</id>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <phase>compile</phase>
                    <configuration>
                        <executable>npm</executable>
                        <arguments>
                            <argument>install</argument>
                        </arguments>
                    </configuration>
                </execution>
                <!-- Required: This following calls `npm run build`  -->
                <execution>
                    <id>npm run build (process-classes)</id>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <phase>process-classes</phase>
                    <configuration>
                        <executable>npm</executable>
                        <arguments>
                            <argument>run</argument>
                            <argument>build</argument>
                        </arguments>
                    </configuration>
                </execution>
                <!-- Required, if you have them.
                     The following will run unit tests in npm -->
                <execution>
                    <id>npm run test (integration-test)</id>
                    <goals>
                        <goal>exec</goal>
                    </goals>
                    <phase>integration-test</phase>
                    <configuration>
                        <executable>npm</executable>
                        <arguments>
                            <argument>run</argument>
                            <argument>test</argument>
                        </arguments>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

A closer look

The resources section makes sure, the npm output ends up inside the jar. Lacking this section the default resource path would be [project root]/src/main/resources. Note the two entries, one for the npm build output and one for any static file you want to add. While <filtering>false</filtering>is the default, adding it, makes it explicit.

You might consider using a third entry with <filtering>true</filtering>. Then each text file will be scanned and ${variableName} replaced with values. This comes in handy

The exec-maven-plugin can run any executable. In our case we need to run npm. Any execution needs several pieces of information:

  • id: the unique identifier for the execution, make one up
  • goal(s): In our case always exec
  • phase: The Maven Build Lifecyle is made up of phases, that follow each other: validate, compile, test, package, verify, install, deploy. You can't run, short of validate, a phase on its own. They follow each other. So when you type mvn package the phases running are: validate, compile, test, package. To confuse you a little more: there is more than one lifecyle: clean, default and site are build into maven. Make sure you learn more about the finer details of the options at hand. What's not described (at least I didn't find it): mvn site executes phases from the default lifecycle when it deems pieces missing or outdated
  • configuration: details of what shall run
  • executable: npm in our case. Luckily npm runs on all platforms
  • arguments: each parameter needs to be wrapped into argument tags

To make all of this work, we run npm install in the compile phase and npm run build in the process-classes phase than runs right after compile. This way the output is ready when the package phase kicks in and the maven-jar-plugin moves the output into the Jar.

There are more options you can experiment with, when the various npm command should run. I leave this to your experimentation.

As usual: YMMV


Posted by on 27 December 2021 | Comments (0) | categories: Java JavaScript vert.x

Comments

  1. No comments yet, be the first to comment