While I am still working with standard JEE architectures, I wanted to try out a Javascript frontend connecting to a Java backend in a self-contained application (SCA) for a long time. I am using Angular (at the moment the javascript UI framework with the biggest market share) and Vert.x for a showcase project. Vert.x sets the benchmarks for sheer speed and scalability and it offers a non-blocking programming model with a full-featured eco system.

The sources of this showcase project can be cloned our downloaded from this Repository:
https://bitbucket.org/mekasorocks/vertxangular

Some Design goals

  • resuse the domain model classes for backend (Java) and frontend (Angular)
  • clear separation between frontend and backend work (seperate projects for each task)
  • create a self contained application with Angular frontend and Java backend

The Maven project structure

  • parent project for this showcase (pom containing the 3 project modules)
    • data (the Java domain model classes aka pojos enhanced with a builder pattern)
    • web (the server side part of this application that uses Vert.x to create a web application server)
    • ui (the Angular part that will be used as a frontend for the application)

The Java part (server side)

Vert.x offers a great ecosystem for all kinds of business needs. I will use the vertx-web features in this project. Just one class is needed to create a full featured web server that will serve an Angular frontend project at the web root and a rest api that will serve the data for the frontend.

package de.mekaso.vertxangular.web;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.Json;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.StaticHandler;

public class Webserver extends AbstractVerticle {
  private static Logger LOGGER = LoggerFactory.getLogger(Webserver.class);
  private static final String CONTENT_TYPE = "application/json; charset=utf-8";
  private static final int DEFAULT_PORT_NO = 80;
  
  private HttpServer server;
  
  private PersonServiceImpl personService;

  /**
   * Just to start the webserver within an IDE.
   * @param args command line args
   */
  public static void main(String[] args) {
    Vertx vertx = Vertx.vertx();
    vertx.deployVerticle(Webserver.class.getName());
  }

  public void start(Future startFuture) throws Exception {
    this.server = this.vertx.createHttpServer();
    Router router = Router.router(vertx);
    // rest service methods
    router.get("/api/persons").handler(this::addRouteGetAll);
    // static web resources (UI files, html, js...)
    router.route().handler(StaticHandler.create().setCachingEnabled(false));
    router.route().handler(CorsHandler.create(".*"));
    int portNo = this.config().getInteger("server.port", DEFAULT_PORT_NO);
    server.requestHandler(router::accept).listen(portNo, asyncResult -> {
      if (asyncResult.succeeded()) {
        this.personService = new PersonServiceImpl();
        startFuture.complete();
      } else {
        startFuture.fail(asyncResult.cause());
      }
    });
  }
  
  private void addRouteGetAll(RoutingContext routingContext) {
    routingContext.response()
        .putHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE)
        .putHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*")
        .end(Json.encodePrettily(this.personService.getAll()));
  }

  public void stop(Future stopFuture) {
    stopFuture.complete();
  }
}

The rest endpoint /api/persons will fetch some Person objects that will be used by the front- and backend.

Transpiling the domain model Java classes to Typescript

The Java domain model classes have their own maven project and will be transpiled from Java to typescript with the Maven JSweet Plugin.

<plugin>
  <groupid>org.jsweet</groupid>
  <artifactid>jsweet-maven-plugin</artifactid>
  <configuration>
    <workingdir>target/.jsweet</workingdir>
    <module>commonjs</module>
    <sourcemap>false</sourcemap>
    <tsonly>true</tsonly>
    <outdir>target/js</outdir>
    <tsout>target/ts</tsout>
    <norootdirectories>true</norootdirectories>
    <targetversion>ES3</targetversion>
    <bundle>false</bundle>
    <verbose>true</verbose>
    <includes>
      <include>**/*.java>/include>
    </include></includes>
  </configuration>
  <executions>
    <execution>
      <id>transpile-domain-model</id>
      <phase>generate-sources</phase>
      <goals>
        <goal>jsweet</goal>
      </goals>
    </execution>
  </executions>
</plugin>

The generated typescript files will later be copied and used by the Angular javascript project.

<plugin>
  <groupid>org.apache.maven.plugins</groupid>
  <artifactid>maven-resources-plugin</artifactid>
  <executions>
    <execution>
      <id>import-domain-model</id>
      <phase>generate-resources</phase>
      <goals>
        <goal>copy-resources</goal>
      </goals>
      <configuration>
        <outputdirectory>${basedir}/src/main/angular/src/app/model</outputdirectory>
        <resources>
          <resource>
            <directory>../data/target/ts</directory>
            <filtering>true</filtering>
          </resource>
        </resources>
        <overwrite>true</overwrite>
      </configuration>
    </execution>
  </executions>
</plugin>  

The Angular part (client side javascript UI)

Now the Angular project can use our domain model classes and show the Person objects in the UI. The frontend-maven-plugin is used to download Node.js, npm (the Javascript infrastructure) and to build the UI project.

<plugin>
  <groupid>com.github.eirslett</groupid>
  <artifactid>frontend-maven-plugin</artifactid>
  <configuration>
    <nodeversion>${node.version}</nodeversion>
    <workingdirectory>src/main/angular/</workingdirectory>
    <nodedownloadroot>https://nodejs.org/dist/</nodedownloadroot>
    <outputdir>src/main/resources/webroot/</outputdir>
  </configuration>
  <executions>
    <execution>
      <id>install node and npm</id>
      <goals>
        <goal>install-node-and-npm</goal>
      </goals>
      <phase>generate-resources</phase>
    </execution>
    <execution>
      <id>npm install</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <phase>generate-resources</phase>
      <configuration>
        <arguments>install</arguments>
      </configuration>
    </execution>
    <execution>
      <id>npm run build</id>
      <goals>
        <goal>npm</goal>
      </goals>
      <phase>generate-resources</phase>
      <configuration>
        <arguments>run build</arguments>
      </configuration>
    </execution>
  </executions>
</plugin>  

The generated html and javascript files will be assembled in a jar file that will later be used for creating the self-contained application (SCA).

Creating the Self-contained application containing the Angular frontend

The maven-shade-plugin is used to create a self-contained application from our projects (web, ui and data).

<dependencies>
  <dependency>
    <groupid>io.vertx</groupid>
    <artifactid>vertx-web</artifactid>
  </dependency>
  ...
  <dependency>
    <groupid>de.mekaso.vertxangular</groupid>
    <artifactid>data</artifactid>
    <version>${project.version}</version>
  </dependency>
  <dependency>
    <groupid>de.mekaso.vertxangular</groupid>
    <artifactid>ui</artifactid>
    <version>${project.version}</version>
  </dependency>
</dependencies>
...
<plugin>
  <groupid>org.apache.maven.plugins</groupid>
  <artifactid>maven-shade-plugin</artifactid>
  <executions>
    <execution>
      <configuration>
        <transformers>
          <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <manifestentries>
              <main-class>io.vertx.core.Starter</main-class>
              <main-verticle>de.mekaso.vertxangular.web.Webserver</main-verticle>
            </manifestentries>
          </transformer>
        </transformers>
        <artifactset>
        <outputfile>${project.build.directory}/vertxangular-${project.version}.jar</outputfile>
      </artifactset></configuration>
    </execution>
  </executions>
</plugin>  

To create the application just execute a mvn clean install on the root project. As a result you will get a (about 7 MB) jar file in the web/target folder of the project. You can execute it by typing
java -jar vertxangular-0.0.1-SNAPSHOT.jar (or just double-click the file on Windows systems).
This will start the integrated Jetty server with our application on port 80. The output (http://localhost) will look like that:

 

The Angular UI gets data from a Vert.x backend
The Angular UI gets data from a Vert.x backend

I was surprised, that I needed so little code to create this application. The harder part was to create a maven multi-module structure that combines the Java backend part with the Javascript frontend UI project. After I found and configured the needed maven plugins, I was happy with the outcome and can recommend this structure for upcoming projects.

By Meik Kaufmann

I am a certified oracle enterprise architect and like to share my knowledge and experience that I gain in the projects I am working on.

2 thoughts on “Vert.x and Angular in a self-contained application”
  1. Hi, thanks for your sharing !
    wouldn’t be possible to get rid of jetty and just use a vertx http server to host the angular app?

    1. Sure you can use the http server to host a frontend (javascript) application, but you cannot use it to let a javascript frontend communicate to a java backend (that was the purpose of this post). The backend needs to be a servlet engine or a fully flexed JEE server.
      If you would like to use the vert.x http server for your frontend application and let the frontend talk to outside services, I can post an example for this on this blog (if you are interested, just write it in another comment).

Leave a Reply to Cristiano Cancel reply

Your email address will not be published. Required fields are marked *