Engineering

Creating a modern Java application: Part 1

Author headshot
By Allan Almazan

Java has progressed much in the past few years. We take a look at a few new changes, how to get started developing with Java and getting up and running with a new web framework.

Back to all articles
Creating a modern Java application: Part 1

While it may not be the oldest programming language, Java is considered to be an "old" programming language. In 1995, the Java programming language first appeared alongside both JavaScript and PHP. All three languages have an immense history and have contributed much towards today’s computing world. However, a history that long does not come without a few bumps along the way.

Since its appearance, Java has built up a relatively negative reputation, especially as higher-level languages and frameworks started to take hold. Despite that, Java still ranks among the top 5 in IEEE Spectrum's default ranking in 2022 and among the top 3 in GitHub's top programming languages in 2022.

So that begs the question: "Is it still worth learning Java?" Which may also lead to another question for some: "I learned and used Java before, but left it because of [insert one or more reasons here]. Does it still matter?" In my opinion, the answer to both questions are: definitely yes.

In this post, we will be focusing on the latter of the questions above. We will take a quick look into Java's newer versions before diving into creating a new web application with one of the newer frameworks out there.

Java has matured even more

In my personal experience, I started with Java between versions 6 and 7 (does that age me?). Shortly after I was exposed to Android development and shifted to non-Java development. Since then, Java has advanced over 10 major versions and the next version is only two months away at the time of writing. Some of the more significant changes from versions 8 to 17 can be seen here.

Also at the time of writing, there are currently 3 LTS (long-term support) versions that exist simultaneously: versions 8, 11 and 17. The next LTS version, 21, is also slated to release in less than a year. In my opinion, version 8 is the, sort of, transitional version for those who have large code bases to upgrade. There were minor changes and upgrades, but the migration should be easily doable. Then came Java 11. You can see the drastic changes just by looking at Oracle's official migration documentation. Many modules and APIs were deprecated, updated and removed. Since we're starting from scratch and don't want to start with technical debt, we'll choose Java 17.

Choosing a version was easy, but installing can be difficult. Thankfully, we have a tool called SDKMAN! (the ! is part of the name and I'm not that excited about open source) which helps us install and manage our Java versions. Similar to pyenv, rbenv and nvm in the Python, Ruby and Node.js worlds, respectively, this helps download, install and manage different Java versions as well as install a number of a number of other Java libraries and SDKs.

A few new features of note

To demonstrate a few of Java’s latest features, we have a snippet below. All the features make using Java less verbose and maybe even more fun to use.

public class App {
    public static void main(String[] args) {
        // Java 16: record classes.
        // https://docs.oracle.com/en/java/javase/16/language/records.html
        // Both `Author` and `Book` are record classes.
        // They are created by a simple one-liner (at the minimum):
        // public record Author(String firstName, String lastName) {}
        // public record Book(String title, Author[] authors) {}

        // Java 10: implicit typing with the `var` keyword
        // https://docs.oracle.com/en/java/javase/17/language/local-variable-type-inference.html
        // No need to do something like:
        // Author john = new Author(...);
        var john = new Author("John", "Doe");
        var jane = new Author("Jane", "Doe");

        var book1 = new Book("My name is John Doe", new Author[]{john});
        var book2 = new Book("My name is Jane Doe", new Author[]{jane});
        var book3 = new Book("We are the Does", new Author[]{john, jane});

        // Java 8: Lambdas and the stream API
        // https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
        // Similar to JavaScript, Python and other higher-level languages, we can
        // chain together operations on groups of objects.
        var janeBooks = Stream.
                of(book1, book2, book3)
                // Java 11: local variable type inference
                // https://docs.oracle.com/en/java/javase/17/language/local-variable-type-inference.html
                // The variable `book` can be inferred as a `Book` type.
                .filter((var book) -> Arrays.asList(book.authors()).contains(jane))
                .toList();

        // Java 8: stream API with method reference
        // We are using `Book::title` to refer to the `Book.title()` method from the record class
        var bookTitles = janeBooks
                .stream()
                .map(Book::title)
                .toList();

        // Java 15: Text blocks
        // https://docs.oracle.com/en/java/javase/15/text-blocks/index.html
        // Python developers may be familiar with this syntax already and JavaScript close equivalent
        // is the template string backtick (`).
        System.out.printf("""
                Jane has authored the following books:

                %s
                """, String.join("\n", bookTitles));
    }
}

What we're going to build

For this post, and a subsequent post, we'll be focusing on creating an application with a framework as a base. We will then create a REST web application with CRUD (create, read, update, delete) functionality and database integration with Hibernate ORM. In the end, we will have a fully functional, database-backed application which can be deployed in a number of different ways thanks to the framework of choice, but we can discuss that in a later section.

Getting set up

First we'll need to install SDKMAN!. For this and the rest of the command line snippets, we'll be assuming you are on a Linux or MacOS terminal:

# Same instructions as on the SDKMAN install link above
$ curl -s "https://get.sdkman.io" | bash

$ sdk version

SDKMAN 5.16.0

For our Java version, we will be using OpenJDK from jdk.java.net. This distribution is the official reference implementation of Java since 7 and is GPLv2 licensed. Conversely, you could use the official Oracle distribution, but it is available under a non-open-source license. There are a number of other JDK distributions made for different situations, but we'll be using the official java.net version to keep it simple. You can see the other available versions here or enter sdk list java in your terminal.

$ sdk install java 17-open

[...]

Installing: java 17-open
Done installing!

Setting java 17-open as default.

Let's double-check which version we have:

$ java -version
openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)

Your development environment

If it's been a long time since you've developed in Java, you may have used the Eclipse IDE or NetBeans IDE. Feel free to use either of those, or any other IDE you're comfortable with. For this, and a future post, an IDE screenshots will be from JetBrains' IntelliJ IDEA.

The start of our application

For this example application, we will be using Quarkus. According to their about page:

Quarkus is a Kubernetes-native Java framework tailored for GraalVM and HotSpot, crafted from best-of-breed Java libraries and standards.

Does that mean anything for us right now? Not really. What do we need to know now? It runs fast and development is simpler with the tools Quarkus provides.

There are a few other Java frameworks that provide features similar to Quarkus, such as Micronaut and Spring Boot. If you are already familiar with the Spring ecosystem, Spring Boot can be a better choice for you. Micronaut is also aimed at "being small and running fast" like Quarkus, however, it is also built to be more familiar for Spring developers. One caveat: both Quarkus and Micronaut are only a few years old and may not be as mature in some areas, so it's best to check each framework to see which fits your project's needs.

For now, our needs are minimal and we'll move on with Quarkus. Let's install it:

# You can install via their install script
$ curl -Ls https://sh.jbang.dev | bash -s - trust add https://repo1.maven.org/maven2/io/quarkus/quarkus-cli/
$ curl -Ls https://sh.jbang.dev | bash -s - app install --fresh --force quarkus@quarkusio

# Or, since we have SDKMAN already installed, SDKMAN
$ sdk install quarkus

Double-checking our install:

$ quarkus

Quarkus CLI version 2.16.1.Final

Create Quarkus projects with Maven, Gradle, or JBang.
Manage extensions and source registries.

Create: quarkus create
Iterate: quarkus dev
Build and test: quarkus build

[...]

And now we use quarkus to help generate our starting point:

# We are specifying Java 17 and using Maven for our build process.
# `demo-todo` is our app name, which will also be the name of the directory. You can also use the format "com.example:app_name" if you also want to specify a group-id.
$ quarkus create app \
 --java=17 \
 --maven \
 demo-todo
-----------

applying codestarts...
📚  java
🔨  maven
📦  quarkus
📝  config-properties
🔧  dockerfiles
🔧  maven-wrapper
🚀  resteasy-reactive-codestart

-----------
[SUCCESS] ✅  quarkus project has been successfully generated in:
--> /java/quarkus/demo-todo
-----------
Navigate into this directory and get started: quarkus dev

Running your Quarkus app

At the start, your Quarkus app is completely working. We can see it in action by running the command at the end of our output above:

$ quarkus dev
# alternatively, you can use the Maven wrapper that's provided
# `./mvnw compile quarkus:dev`

$ quarkus dev
--2023-02-01 10:12:06--  https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
Resolving repo.maven.apache.org (repo.maven.apache.org)... 146.75.92.215
Connecting to repo.maven.apache.org (repo.maven.apache.org)|146.75.92.215|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 58727 (57K) [application/java-archive]
Saving to: ‘/java/quarkus/demo-todo/.mvn/wrapper/maven-wrapper.jar’

/java/quarkus/demo-todo/.mvn/wrapper 100%[====================================================================================================================================>]  57.35K  --.-KB/s    in 0.02s

2023-02-01 10:12:06 (2.43 MB/s) - ‘/java/quarkus/demo-todo/.mvn/wrapper/maven-wrapper.jar’ saved [58727/58727]

[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< com.example:demo-todo >------------------------
[INFO] Building demo-todo 0.0.1
[INFO] --------------------------------[ jar ]---------------------------------

[...]

Listening for transport dt_socket at address: 5005
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-02-01 10:35:41,634 INFO  [io.quarkus] (Quarkus Main Thread) demo-todo 0.0.1 on JVM (powered by Quarkus 2.16.1.Final) started in 0.892s. Listening on: http://localhost:8080

2023-02-01 10:35:41,642 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2023-02-01 10:35:41,642 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy-reactive, smallrye-context-propagation, vertx]

--
Tests paused
Press [r] to resume testing, [o] Toggle test output, [:] for the terminal, [h] for more options>

Initially, you'll see a lot of output as Maven downloads and builds dependencies that the app needs. After all of that's done, you can see near the end: Listening on: http://localhost:8080. Quarkus comes prepackaged with a built-in developer mode. Visiting that URL above on your favorite web browser and you'll get something similar to what we have below:

Quarkus start page

And visiting the dev UI will give you:

Quarkus detail page

There's a lot to take in here, so feel free to look around and see what the framework provides out of the box. Keep in mind that you customize your initial app (making it as minimal as possible or even as big as possible) by changing a few switches on the quarkus create app .

You can also see what's available to install as an extension with ./mvnw quarkus:list-extensions while in your project directory:

$ ./mvnw quarkus:list-extensions
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< com.example:demo-todo >------------------------
[INFO] Building demo-todo 0.0.1
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- quarkus-maven-plugin:2.16.1.Final:list-extensions (default-cli) @ demo-todo ---
[INFO] Current Quarkus extensions available:
[INFO]
[INFO] ✬ ArtifactId                                         Extension Name
[INFO] ✬ blaze-persistence-integration-quarkus              Blaze-Persistence
[INFO] ✬ camel-quarkus-activemq                             Camel ActiveMQ
[INFO] ✬ camel-quarkus-amqp                                 Camel AMQP
[INFO] ✬ camel-quarkus-arangodb                             Camel ArangoDb
[INFO] ✬ camel-quarkus-as2                                  Camel AS2

[...]

[INFO] ✬ quarkus-webjars-locator                            WebJar Locator
[INFO] ✬ quarkus-websockets                                 WebSockets
[INFO] ✬ quarkus-websockets-client                          WebSockets Client
[INFO]   quarkus-xmlsec                                     Santuario XML Security
[INFO]   quarkus-zeebe                                      Quarkus - Zeebe - Runtime
[INFO]   quarkus-zookeeper                                  Zookeeper
[INFO] ✬ runtime-tools-quarkus-extension                    Kogito Runtime Tools
[INFO]
[INFO] To get more information, append `-Dformat=full` to your command line.
[INFO]
[INFO] To list only extensions from specific category, append `-Dcategory="categoryId"` to your command line.
[INFO]
[INFO] Add an extension to your project by adding the dependency to your pom.xml or use `./mvnw quarkus:add-extension -Dextensions="artifactId"`

For the app itself, (make sure it's still running), we can see the list of endpoints from our dev UI (or visit: http://localhost:8080/q/dev/io.quarkus.quarkus-resteasy-reactive/endpoints). And below we can see it in action:

$ curl -i http://localhost:8080/hello

HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
content-length: 28

Hello from RESTEasy Reactive

Coming soon

In this post we've taken a quick look at Java's history and Java's present. We got our machine set up with SDKMAN! , Java 17 and Quarkus and finally got a Quarkus starter app running. In the next post we will take a closer look at Quarkus, its extensions and how they all tie together and work in concert. If you'd rather not wait, Quarkus's documentation and guides are also very helpful, but have a few areas (in my opinion) that are worth a second look -- and will be covered in the next post.

Here at Anvil engineering, we've been looking at the exciting possibilities and improvements provided by the next generation of Java versions and frameworks. We have examples available for using the Anvil API in Java for those looking to integrate PDF filling or e-signing capabilities into their own Java projects. We hope this post has helped you get started on your next Java/Quarkus application and hope to see you again on the next post. If you have any questions or comments, drop us a message at developers@useanvil.com.

Get a Document AI demo (from a real person)

Request a 30-minute demo and we'll be in touch soon. During the meeting our team will listen to your use case and suggest which Anvil products can help.
    Want to try Anvil first?
    Sign up for free or try it now with any document.
    Want to try Anvil first?
    Sign up for free or try it now with any document.