Skip to content

soasada/undertow-vuejs

Repository files navigation

Web application demo with Undertow + Vue.js

Inspired in https://github.com/jonashackt/spring-boot-vuejs

Sample project that shows how to build single-page applications with Undertow and Vue.js. The project is divided in two modules: backend and frontend.

Backend

In the backend module we have the following dependencies:

  • WEB SERVER: Undertow. (via popok)
  • RDBMS: MySQL (provided in a docker image).
  • DB CONNECTION POOL: HikariCP.
  • IMMUTABLE MODELS: Lombok.
  • JSON: Jackson. (via popok)
  • HTTP AUTHORIZATION: JWT.
  • LOGGING: Log4j2.
  • TESTING: JUnit 5.

Spring JDBC (via popok) provides the functionality to use aliases in the SQL queries. One of the solutions is to put prefixes to the column names of the database tables. More info: https://stackoverflow.com/questions/15184709/cachedrowsetimpl-getstring-based-on-column-label-throws-invalid-column-name

Guava is optional, is part of a proof of concept with RequestHandler.java and ResponseHandler.java. This is a work in progress approach.

1. Web server

The web server is configured via app.properties.

The router of the server is an Undertow HttpHandler you can see it in Router.java. All of the HTTP API handlers are wrapped with BlockingHandler in Handlers.java because all of them do blocking operations and we execute all these operations in the pool of workers of undertow. One of the reasons of this is the blocking nature of the JDBC.

Static files are served from the public folder inside resources folder. The frontend project compiles all the static files and the maven-resources-plugin moves the files from /frontend/target/dist to /resources/public in the backend module. This happens in the backend module compilation.

2. Database

The database is configured via .properties files (thanks to HikariCP that supports them) via db_prod_pool.properties for production configuration and db_test_pool.properties for testing. The class responsible of load this config files is: HikariConnectionPool.java.

In order to access to the database, I use raw JDBC with a minimal abstraction to build queries (Query.java) and execute them (Database.java). To map a table record to an object I made a mapper interface (JdbcMapper.java) to do it manually (the mapping). One of the problems that I have encountered is the aliases problem: If you want to return the ResultSet to other layers of your application, you have to store it in some cache and return to the layers. This is because if you close the ResultSet you lose the connection and the ResultSet would be empty. To avoid this situation you can use the CachedRowSet class, and then you can close the ResultSet and the connection returns to the pool, with the connection closed (returned to the pool) and the record in memory you can map it to an object.

Frontend

The frontend module is pretty simple is a back-office application to manage the houses and the furniture of it's users.

Features

  • HTTPs/HTTP2.
  • Redirection from HTTP to HTTPs.
  • Gzip compression for static files.
  • Authorization via JWT.
  • Proxy all frontend requests to our backend.
  • Single artifact deploy.
  • Minimal JDBC abstraction.
  • Migrations.
  • Database testing with docker.
  • Immutable JSON messages.
  • Application configuration.
  • OpenJDK 12 support.
  • Easy backend and frontend testing.
  • Streams data from the backend to the frontend via SSE with Reactor and vanilla undertow.

Try it!

Demo here, remember to accept risks (self-signed cert), for login use:
admin
admin

Steps to deploy

  1. Download JDK13 from: https://jdk.java.net/13/ and put in your machine at:

/opt/prod_jdk

Maven will use this folder in order to compile the project, this allow us to update easily the JDK version. If you want to have the java as a command without typing /opt/prod_jdk/bin/java you can do this:

sudo ln -s /opt/prod_jdk/bin/java /usr/local/bin/java

or (ubuntu based) add this line at the end of your ~/.bashrc:

export PATH=/opt/apache-maven-3.6.2/bin:$PATH (for maven)
export JAVA_HOME=/opt/prod_jdk
export PATH=$PATH:$JAVA_HOME/bin

  1. Compile Vue.js project and generate static files.

mvn -U clean install -pl :frontend

  1. Remove previous static files.

rm -rf backend/src/main/resources/public

  1. Test migrations (require a running docker).

mvn -U clean -pl :backend -Dflyway.configFiles=src/main/resources/database/test_migrations.properties flyway:migrate

  1. Compile backend, move static files to public/ and generate jar file (deployable artifact).

export APP_ENV=test && mvn -U clean compile test package -pl :backend

  1. Run application (change APP_ENV with prod or test).

export APP_ENV=test && java -Xms64M -Xmx512M -Dlogfilename=web -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -jar backend/target/backend-0.0.1.jar

NOTE: There is a script to deploy locally (local_deploy.sh), use it for development only!.

Manifesto

You don't need a backend framework or an ORM to be able to make web applications, why not plain java?. "Don't reinvent the wheel" they say, and you pick a framework, now you have a bicycle with: a washing machine, an embedded microwave and a neon light beacon... Do you really need all of this features?

Wait a moment... why are you saying this about frameworks?

The problem isn't frameworks, it's us (developers). Try to learn the basics first, later learn a framework. Learn things like OOP, FP, HTTP, client-server model, plain SQL, VMs... and then, when you know these things, when you are tired of doing these things without no help, when you are tired of doing repetitive things only then, take a framework.

Many of the problems that you are going to have with a framework, is due to the lack of knowledge of the framework itself or the basics. Soon you will have a good amount of spaghetti code unmaintainable, with some modules of your project that fits the "framework boundaries" and others that use hacks to fit these boundaries with your knowledge of the language, resulting in performance issues, cryptic errors, updating problems, development delays (slow cycles) and more...