Uploaded by Ardi Ismail

96.Building+Microservices+in+Java-1629464982569

advertisement
The InfoQ eMag / Issue #96 / July 2021
Building
Microservices in Java
Spring Boot Tutorial:
Building Microservices
Deployed to Google
Cloud
Project Helidon Tutorial:
Building Microservices with
Oracle’s Lightweight Java
Framework
Getting Started
with Quarkus
FACILITATING THE SPREAD OF KNOWLEDGE AND INNOVATION IN PROFESSIONAL SOFTWARE DEVELOPMENT
InfoQ
@ InfoQ
InfoQ
InfoQ
Building
Microservices in Java
IN THIS ISSUE
Spring Boot Tutorial:
Building Microservices
Deployed to Google Cloud
Getting Started with
Quarkus
06
14
Project Helidon Tutorial:
Building Microservices with
Oracle’s Lightweight Java
Framework
Virtual Panel: the MicroProfile
Influence on Microservices
Frameworks
20
29
PRODUCTION EDITOR Ana Ciobotaru / COPY EDITORS Susan Conant DESIGN Dragos Balasoiu / Ana Ciobotaru
GENERAL FEEDBACK feedback@infoq.com / ADVERTISING sales@infoq.com / EDITORIAL editors@infoq.com
CONTRIBUTORS
Sergio Felix
Robert Cortez
Cesar Hernandez
is a Software Engineer in Google Cloud
where he works in Cloud Engineering
Productivity, an organization within Google
Cloud that focuses on making development
frictionless and improving Product & Eng
Excellence.
is a passionate Java Developer with more
than 10 years of experience. He is involved
in the Open Source Community to help
other individuals spread the knowledge
about Java technologies. He is a regular
speaker at conferences like JavaOne,
Devoxx, Devnexus, JFokus, and others. He
leads the Coimbra JUG and founded the
JNation Conference in Portugal. When he
is not working, he hangs out with friends,
plays computer games, and spends time
with family.
is a product manager and a former
architect atis a Senior Software Engineer at
Tomitribe with over 14 years of experience
in Enterprise Java Applications. He is a
Java Champion, Duke’s Choice Award
winner, Oracle Groundbreaker Ambassador,
Open Source advocate, Apache and Eclipse
Committer, teacher, and public speaker.
When Cesar is away from a computer,
he enjoys spending time with his family,
traveling, and playing music with the Java
Community Band, The Null Pointers. Follow
Cesar on Twitter.
Emily Jiang
Otavio Santana
Erin Schnabel
is a Java Champion. She is Liberty
Microservices Architect and Advocate,
Senior Technical Staff Member (STSM) in
IBM, based at Hursley Lab in the UK. Emily
is a MicroProfile guru and has been working
on MicroProfile since 2016 and leads the
specifications of MicroProfile Config, Fault
Tolerance and Service Mesh. She was a CDI
Expert Group member. She is passionate
about MicroProfile and Jakarta EE.
is a passionate software engineer focused
on Cloud and Java technology. He has
experience mainly in persistence polyglot
and high-performance applications in
finances, social media, and e-commerce.
Otavio is a member of both Expert Groups
and Expert Leader in several JSRs and
JCP executive committee. He is working
on several Apache and Eclipse Foundation
projects such as Apache Tamaya,
MicroProfile, Jakarta EE.
is a Senior Principal Software Engineer and
maker of things at Red Hat. She is a Java
Champion, with 20 years under her belt, as
a developer, technical leader, architect and
evangelist, and she strongly prefers being
up to her elbows in code.understanding of
how reactive operators work.
A LETTER FROM
THE EDITOR
Michael Redlich
is a Senior Research Technician at
ExxonMobil Research & Engineering
in Clinton, New Jersey (views are his
own) with experience in developing
custom scientific laboratory and
web applications for the past 30
years. He also has experience as a
Technical Support Engineer at AiLogix, Inc. (now AudioCodes) where
he provided technical support and
developed telephony applications for
customers. His technical expertise
includes object-oriented design and
analysis, relational database design
and development, computer security,
C/C++, Java, Python, and other
programming/scripting languages.
His latest passions include
MicroProfile, Jakarta EE, Helidon,
Micronaut and MongoDB.
Over the past few years, the Java
community has been offered a
wide variety of microservicesbased frameworks to build
enterprise, cloud-native and
serverless applications. Perhaps
you’ve been asking yourself
questions such as: What are
the benefits of building and
maintaining a microservicesbased application? Should
I migrate my existing
monolith-based application
to microservices? Is it worth
the effort to migrate? Which
microservices framework should
I commit to using? What are
MicroProfile and Jakarta EE?
What happened to Java EE? How
does Spring Boot fit into all of
this? What is GraalVM?
For those of us that may be old
enough to remember, the concept
of microservices emerged from
the service-oriented architecture
(SOA) that was introduced nearly
20 years ago. SOA applications
used technologies such as
the Web Services Description
Language (WSDL) and Simple
Object Access Protocol
(SOAP) to build enterprise
applications. Today, however, the
Representational State Transfer
(REST) protocol is the primary
method for microservices to
communicate with each other via
HTTP.
Since 2018, we’ve seen three
new open-source frameworks Micronaut, Helidon and Quarkus
- emerge to complement the
already existing Java middleware
open-source products such as
Open Liberty, WildFly, Payara and
Tomitribe. We have also seen the
emergence of GraalVM, a polyglot
virtual machine and platform
created by Oracle Labs that,
among other things, can convert
applications to native code.
In this eMag, you’ll be introduced
to some of these microservices
frameworks, MicroProfile, a set
of APIs that optimizes enterprise
Java for a microservices
architecture, and GraalVM. We’ve
The InfoQ eMag / Issue #87 / November 2020
hand-picked three full-length articles
and facilitated a virtual panel to explore
these frameworks.
In the first article, Sergio Felix, senior
software engineer at Google, provides
for you a step-by-step tutorial on how
to deploy applications to the Google
Cloud Platform. Starting with a basic
Spring Boot application, Sergio will then
containerize the application and deploy
it to Google Kubernetes Engine using
Skaffold and the Cloud Code IntelliJ
plugin.
In the second article, Roberto Cortez,
principal software engineer at Red Hat,
introduces you to Quarkus, explains
the motivation behind its creation
and demonstrates how it is different
from the other frameworks. Dubbed
“supersonic subatomic Java,” Quarkus
has received a significant amount of
attention within the Java community
since its initial release in 2019.
In the third article, I will introduce you
to Project Helidon, Oracle’s lightweight
Java microservices framework. I will
explain the differences between Helidon
SE and Helidon MP, explore the core
components of Helidon SE, show you
how to get started, and introduce
a movie application built on top of
Helidon MP. I also demonstrate how to
convert a Helidon application to native
code with GraalVM.
And finally, we present a virtual panel
featuring an all-star cast of Java
luminaries. Cesar Hernandez, senior
software engineer at Tomitribe,
Emily Jiang, Liberty microservice
architect and advocate at IBM, Otavio
Santana, staff software engineer at
xgeeks, and Erin Schnabel, senior
principal software engineer at Red Hat,
discuss the MicroProfile influence on
microservices frameworks. There was
also a discussion on how developers
and organizations are reverting
back to monolith-based application
development.
We hope you enjoy this edition of the
InfoQ eMag. Please share your feedback
via editors@infoq.com or on Twitter.
The InfoQ eMag / Issue #96 / July 2021
Spring Boot Tutorial: Building
Microservices Deployed to Google Cloud
by Sergio Felix, Software Engineer
With the increasing popularity of microservices in
the industry, there’s been a boom in technologies
and platforms from which to choose to build
applications. Sometimes it’s hard to pick
something to get started. In this article, I’ll show
you how to create a Spring Boot based application
that leverages some of the services offered by
Google Cloud. This is the approach we’ve been
using in our team at Google for quite some time. I
hope you find it useful.
The Basics
Let’s start by defining what we will build. We’ll
begin with a very basic Spring Boot-based
application written in Java. Spring is a mature
framework that allows us to quickly create very
powerful and feature-rich applications.
6
We’ll then make a few changes to containerize the
application using Jib (builds optimized Docker
and OCI images for your Java applications without
a Docker) and a distroless version of Java 11.
Jib works both with Maven and Gradle. We’ll use
Maven for this example.
Next, we will create a Google Cloud Platform (GCP)
project and use Spring Cloud GCP to leverage Cloud
Firestore. Spring Cloud GCP allows Spring-based
applications to easily consume Google services like
databases (Cloud Firestore, Cloud Spanner or even
Cloud SQL), Google Cloud Pub/Sub, Stackdriver for
logging and tracing, etc.
After that, we’ll make changes in our application
to deploy it to Google Kubernetes Engine (GKE).
GKE is a managed, production-ready environment
Finally, we will use Skaffold and Cloud Code to
make development easier. Skaffold handles the
workflow for building, pushing and deploying your
application. Cloud Code is a plugin for VS Code
and IntelliJ that works with Skaffold and your IDE
so that you can do things like deploy to GKE with a
click of a button. In this article, I’ll be using IntelliJ
with Cloud Code.
In addition, we’ll run some commands to make sure
that the application is running on your machine and
can communicate with the services running on your
project in Google Cloud. Let’s make sure we are
pointing to the correct project and authenticate you
using:
Setting up our Tools
Before we write any code, let’s make sure we have a
Google Cloud project and all the tools installed.
gcloud config set project <YOUR PROJECT
ID>
gcloud auth login
Creating a Google Cloud Project
Setting up a GCP instance is easy. You can
accomplish this by following these instructions.
This new project will allow us to deploy our
application to GKE, get access to a database
(Cloud Firestore) and will also allow us to have
a place where we can push our images when we
containerize the application.
Next, we’ll make sure your machine has application
credentials to run your application locally:
Install Cloud Code
Next, we’ll install Cloud Code. You can follow these
instructions on how to install Cloud Code to
IntelliJ. Cloud Code manages the installation of
Skaffold and Google SDK that we’ll use later in the
article. Cloud Code also allows us to inspect our
GKE deployments and services. Most importantly,
it also has a clever GKE development mode that
continuously listens to changes in your code
when it detects a change it builds the app, builds
the image, pushes the image to your registry,
deploys the application to your GKE cluster, starts
streaming logs and opens a localhost tunnel so you
can test your service locally. It›s like magic!
•
Google Container Registry API - This will allow
us to have a registry where we can privately
push our images.
•
Cloud Firestore in Datastore mode - This will
allow us to store entities in a NoSQL database.
Make sure to select Datastore mode so that we
can use Spring Cloud GCP’s support for it.
In order to use Cloud Code and proceed with our
application, let’s make sure that you log in using
the Cloud Code plugin by clicking on the icon that
should show up on the top right of your IntelliJ
window:
The InfoQ eMag / Issue #96 / July 2021
for deploying containerized Kubernetes-based
applications.
gcloud auth application-default login
Enabling the APIs
Now that we have everything set up we need to
enable the API’s we will be using in our application:
You can manage the APIs that are enabled in your
project by visiting your project’s API Dashboard.
Creating our Dog Service
First things first! We need to get started with a
simple application we can run locally. We’ll create
something important like a Dog microservice. Since
I’m using IntelliJ Ultimate I’ll go to `File -> New
-> Project…` and select «Spring Initializr». I’ll
select Maven, Jar, Java 11 and change the name to
something important like `dog` as shown below:
7
The InfoQ eMag / Issue #96 / July 2021
We’ll create a controller class for the Dog and the
REST endpoints:
@RestController
@Slf4j
public class DogController {
Click next and add: Lombok, Spring Web and GCP
Support:
@GetMapping(“/api/v1/dogs”)
public List<Dog> getAllDogs() {
log.debug(“->getAllDogs”);
return ImmutableList.of(new
Dog(“Fluffy”, 5),
new Dog(“Bob”, 6),
new Dog(“Cupcake”, 11));
}
@PostMapping(“/api/v1/dogs”)
public Dog saveDog(@RequestBody Dog
dog) {
log.debug(“->saveDog {}”, dog);
return dog;
}
}
The endpoints return a list of predefined dogs and
the saveDog endpoint doesn’t really do much, but
this is enough for us to get started.
If all went well, you should now have an application
that you can run. If you don’t want to use IntelliJ for
this, use the equivalent on your IDE or use Spring’s
Initilizr.
Next, we’ll add a POJO for our Dog service and a
couple of REST endpoints to test our application.
Our Dog object will have a name and an age and
we’ll use Lombok’s @Data annotation to save us
from writing setters, getters, etc. We’ll also use the
@AllArgsConstructor annotation to create a
constructor for us. We’ll use this later when we are
creating Dogs.
@Data
@AllArgsConstructor
public class Dog {
private String name;
private int age;
}
8
Using Cloud Firestore
Now that we have a skeleton app, let’s try to
use some of the services in GCP. Spring Cloud
GCP adds Spring Data support for Google Cloud
Firestore in Datastore mode. We’ll use this to store
our Dogs instead of using a simple list. Users will
now also be able to actually save a Dog in our
database.
To start we’ll add the Spring Cloud GCP Data
Datastore dependency to our POM:
<dependency>
<groupId>org.springframework.cloud</
groupId>
<artifactId>spring-cloud-gcp-datadatastore</artifactId>
</dependency>
Now, we can modify our Dog class so that it can be
stored. We’ll add an @Entity annotation and a @
Id annotation to a value of type Long to act as an
identifier for the entity:
Now we can create a regular Spring Repository
class as follows:
@Repository
public interface DogRepository extends
DatastoreRepository<Dog, Long> {}
As usual with Spring Repositories, there is no need
to write implementations for this interface since
we’ll be using very basic methods.
We can now modify the controller class. We’ll inject
the DogRepository into the DogController then
modify the class to use the repository as follows:
@RestController
@Slf4j
@RequiredArgsConstructor
public class DogController {
private final DogRepository
dogRepository;
@GetMapping(“/api/v1/dogs”)
public Iterable<Dog> getAllDogs() {
log.debug(“->getAllDogs”);
return dogRepository.findAll();
}
@PostMapping(“/api/v1/dogs”)
public Dog saveDog(@RequestBody Dog
dog) {
log.debug(“->saveDog {}”, dog);
return dogRepository.save(dog);
}
}
Note that we are using Lombok’s @
RequiredArgsConstructor to create a
constructor to inject our DogRepository. When
you run your application, the endpoints will call
your Dog service that will attempt to use Cloud
Firestore to retrieve or store the Dogs.
TIP: To quickly test this, you can create an HTTP
request in IntelliJ with the following:
POST http://localhost:8080/api/v1/dogs
Content-Type: application/json
{
}
“name”: “bob”,
“age”: 5
In only a few steps, we now have the application
up and running and consuming services from GCP.
Awesome! Now, let’s turn this into a container and
deploy it!
The InfoQ eMag / Issue #96 / July 2021
@Entity
@Data
@AllArgsConstructor
public class Dog {
@Id private Long id;
private String name;
private int age;
}
Containerizing the Dog Service
At this point, you could start writing a Dockerfile to
containerize the application we created above. Let’s
instead use Jib. One of the things I like about Jib
is that it separates your application into multiple
layers, splitting dependencies from classes. This
allows us to have faster builds so that you don’t
have to wait for Docker to rebuild your entire Java
application - just deploy the layers that changed. In
addition, Jib has a Maven plugin that makes it easy
to set up by just modifying the POM file in your
application.
To start using the plugin, we’ll need to modify our
POM file to add the following:
<plugin>
<groupId>com.google.cloud.
tools</groupId>
<artifactId>jib-maven-plugin</
artifactId>
<version>1.8.0</version>
<configuration>
<from>
<image>gcr.io/distroless/
java:11</image>
</from>
<to>
<image>gcr.io/<YOUR_GCP_
REGISTRY>/${project.artifactId}</image>
</to>
</configuration>
</plugin>
9
The InfoQ eMag / Issue #96 / July 2021
Notice we are using Google’s distroless image for
Java 11. “Distroless” images contain only your
application and its runtime dependencies. They do
not contain package managers, shells or any other
programs you would expect to find in a standard
Linux distribution.
Restricting what’s in your runtime container to
precisely what’s necessary for your app is a best
practice employed by Google and other tech giants
that have used containers in production for many
years. It improves the signal-to-noise of scanners
(e.g. CVE) and reduces the burden of establishing
provenance to just what you need.
Make sure to replace your GCP registry in the code
above to match the name of your project.
After doing this, you can attempt to build and push
the image of the app by running a command like:
$ ./mvnw install jib:build
This will build and test the application. Create the
image and then finally push the newly created
image to your registry.
NOTE: It’s usually a common good practice to use
a distro with a specific digest instead of using
“latest”. I’ll leave it up to the reader to decide what
base image and digest to use depending on the
version of Java you are using.
Deploying the Dog Service
At this point, we are almost ready to deploy our
application. In order to do this, let’s first create a
GKE cluster where we will deploy our application.
Creating a GKE Cluster
To create a GKE cluster, follow these instructions.
You’ll basically want to visit the GKE page, wait
for the API to get enabled and then click on the
button to create a cluster. You may use the default
settings, but just make sure that you click on the
“More options” button to enable full access to all
the Cloud APIs:
10
This allows your GKE nodes to have permissions to
access the rest of the Google Cloud services. After
a few moments the cluster will be created (please
check image on page 11).
Applications living inside of Kubernetes
Kubernetes likes to monitor your application to
ensure that it’s up and running. In the event of a
failure, Kubernetes knows that your application
is down and that it needs to spin up a new
instance. To do this, we need to make sure that our
application is able to respond when Kubernetes
pokes it. Let’s add an actuator and Spring Cloud
Kubernetes.
Add the following dependencies to your POM file:
<dependency>
<groupId>org.springframework.boot</
groupId>
<artifactId>spring-boot-starteractuator</artifactId>
</dependency>
If your application has an application.
properties file inside the src/main/
resources directory, remove it and create
an application.yaml file with the following
contents:
spring:
application:
name: dog-service
management:
endpoint:
health:
enabled: true
This adds a name to our application and exposes
the health endpoint mentioned above. To verify
that this is working, you may visit your application
The InfoQ eMag / Issue #96 / July 2021
at localhost:8080/actuator/health You
should see something like:
{
}
“status”: “UP”
Configuring to run in Kubernetes
In order for us to deploy our application to our
new GKE cluster, we need to write some additional
YAML. We need to create a deployment and a
service. Use the deployment that follows. Just
remember to replace the GCR name with the one
from your project:
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dog-service
spec:
selector:
matchLabels:
app: dog-service
replicas: 1
template:
metadata:
labels:
app: dog-service
spec:
containers:
- name: dog-service
image: gcr.io/<YOUR GCR
REGISTRY NAME>/dog
ports:
- containerPort: 8080
livenessProbe:
initialDelaySeconds: 20
httpGet:
port: 8080
path: /actuator/health
readinessProbe:
initialDelaySeconds: 30
httpGet:
port: 8080
path: /actuator/health
Add a service.yaml file with the following:
apiVersion: v1
kind: Service
metadata:
name: dog-service
spec:
type: NodePort
selector:
app: dog-service
ports:
- port: 8080
targetPort: 8080
The deployment contains a few changes to the
readiness and liveness probe. This is so that
Kubernetes uses these endpoints to poke the
app to see if it’s alive. The service exposes the
deployment so that other services can consume it.
After doing this, we can now start using the Cloud
Code plugin we installed at the beginning of this
article. From the Tools menu, select: Cloud Code
-> Kubernetes -> Add Kubernetes Support.
This will automatically add a Skaffold YAML to your
application and set up a few things for you so that
you can deploy to your cluster by clicking a button.
11
The InfoQ eMag / Issue #96 / July 2021
To confirm that all this worked you can inspect the
configuration from the Run/Debug Configurations
section in IntelliJ. If you click on the Develop on
Kubernetes run, it should have automatically picked
up your GKE cluster and Kubernetes configuration
files and should look something this:
Conclusions
Congratulations if you made it this far! The
application we built in this article showcases some
key technologies that most microservices-based
applications would use: a fast, fully managed,
serverless, cloud-native NoSQL document
Click OK and then click on the green “Play” button
at the top right:
database (Cloud Firestore), GKE a managed,
production-ready environment for deploying
Kubernetes-based containerized applications and
finally a simple cloud native microservice build with
Spring Boot.
After that, Cloud Code will build the app, create the
image, deploy the application to your GKE cluster
and stream the logs from Stackdriver into your local
machine. It will also open a tunnel so that you can
consume your service via localhost:8080.
You can also peak at the workloads page in the
Google Cloud Console. (Figure 8).
12
Along the way, we also learned how to use
a few tools like Cloud Code to streamline
your development workflow and Jib to build
containerized applications using common Java
patterns.
I hope you’ve found the article helpful and that
you give these technologies a try. If you found this
interesting, have a look at a bunch of codelabs that
Google offers where you can learn about Spring and
Google Cloud products.
Figure 8
•
Using Google
Kubernetes Engine
(GKE) along with Spring
Boot allows you to
quickly and easily set up
microservices.
•
Jib is a great way to
containerize your Java
application. It allows
you to create optimized
images without Docker
using Maven or Gradle.
•
Google’s Spring Cloud
GCP implementation
allows developers to
leverage Google Cloud
Platform (GCP) services
with little configuration
and using some of
Spring’s patterns.
•
Setting up Skaffold
with Cloud Code allows
developers to have
a nice development
cycle. This is especially
useful with starting to
prototype a new service.
The InfoQ eMag / Issue #96 / July 2021
TL;DR
13
The InfoQ eMag / Issue #96 / July 2021
Getting Started with Quarkus
by Roberto Cortez, Java Developer
Quarkus created quite a buzz in the enterprise Java
ecosystem in 2019. Like all other developers, I was
curious about this new technology and saw a lot
of potential in it. What exactly is Quarkus? How
is it different from other technologies established
in the market? How can Quarkus help me or my
organization? Let’s find out.
What is Quarkus?
The Quarkus project dubbed itself Supersonic
Subatomic Java. Is this actually real? What does
this mean? To better explain the motivation behind
the Quarkus project, we need to look into the
current state of software development.
From On-Premises to Cloud
The old way to deploy applications was to use
physical hardware. With the purchase of a
physical box, we paid upfront for the hardware
requirements. We had already made the investment,
so it wouldn’t matter if we used all the machine
14
resources or just a small amount. In most cases,
we wouldn’t care that much as long as we
could run the application. However, the Cloud is
now changing the way we develop and deploy
applications.
In the Cloud, we pay exactly for what we use. So
we have become pickier with our hardware usage. If
the application takes 10 seconds to start, we have
to pay for these 10 seconds even if the application
is not yet ready for others to consume.
Java and the Cloud
Do you remember when the first Java version was
released? Allow me to refresh your memory — it
was in 1996. There was no Cloud back then. In fact,
it only came into existence several years later. Java
was definitely not tailored for this new paradigm
and had to adjust. But how could we change a
paradigm after so many years tied to a physical box
It’s All About the Runtime
The way that many Java libraries and frameworks
evolved over the years was to perform a set
of enhancements during runtime. This was a
convenient way to add capabilities to your code in a
safe and declarative way. Do you need dependency
injection? Sure! Use annotations. Do you need
a transaction? Of course! Use an annotation. In
fact, you can code a lot of things by using these
annotations that the runtime will pick and handle
for you. But there is always a catch. The runtime
requires a scan of your classpath and classes
for metadata. This is an expensive operation that
consumes time and memory.
Quarkus Paradigm Shift
Quarkus addressed this challenge by moving
expensive operations like Bytecode Enhancement,
Dynamic ClassLoading, Proxying, and more to
compile time. The result is an environment that
consumes less memory, less CPU, and faster
startup. This is perfect for the use case of the
Cloud, but also useful for other use cases. Everyone
will benefit from less resources consumption
overall, no matter the environment.
Maybe Quarkus is Not So New
Have you heard of or used technologies such as
CDI, JAX-RS, or JPA? If so, the Quarkus stack is
composed of these technologies that have been
around for several years. If you know how to
develop these technologies, then you will know how
to develop a Quarkus application.
Do you recognize the following code?
@Path(“books”)
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
public class BookApi {
@Inject
BookRepository bookRepository;
@Path(“/{id}”)
Response get(@PathParam(“id”)Long id) {
return bookRepository.find(id)
.map(Response::ok)
.orElse(Response.status(NOT_FOUND))
.build();
}
}
Congratulations, you have your first Quarkus app!
The InfoQ eMag / Issue #96 / July 2021
where costs didn’t matter as much as they do in the
Cloud?
Best of Breed Frameworks and Standards
The Quarkus programming model is built on top
of proven standards, be it official standards or de
facto standards. Right now, Quarkus has first class
support for technologies like Hibernate, CDI,
Eclipse MicroProfile, Kafka, Camel, Vert.x, Spring,
Flyway, Kubernetes, Vault, just to name a few. When
you adopt Quarkus, you will be productive from
day one since you don’t really need to learn new
technologies. You just use what has been out there
for the past 10 years.
Are you looking to use a library that isn’t yet in the
Quarkus ecosystem? There is a good chance that
it will work out of the box without any additional
setup, unless you want to run it in GraalVM Native
mode. If you want to go one step further, you could
easily implement your own Quarkus extension to
provide support for a particular technology and
enrich the Quarkus ecosystem.
Quarkus Setup
So, you may be asking if there is something hiding
under the covers. In fact yes there is. You are
required to use a specific set of dependencies in
your project that are provided by Quarkus. Don’t
worry, Quarkus supports both Maven and Gradle.
For convenience, you can generate a skeleton
project in Quarkus starter page, and select which
technologies you would like to use. Just import it
in your favorite IDE and you are ready to go. Here
is a sample Maven project to use JAX-RS with
RESTEasy and JPA with Hibernate:
@GET
15
The InfoQ eMag / Issue #96 / July 2021
16
<?xml version=”1.0”?>
<project xsi:schemaLocation=”http://maven.
apache.org/POM/4.0.0 https://maven.apache.
org/xsd/maven-4.0.0.xsd” xmlns=”http://
maven.apache.org/POM/4.0.0”
xmlns:xsi=”http://www.w3.org/2001/
XMLSchema-instance”>
<modelVersion>4.0.0</modelVersion>
<groupId>org.acme</groupId>
<artifactId>code-with-quarkus</
artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.8.1</
compiler-plugin.version>
<maven.compiler.parameters>true</maven.
compiler.parameters>
<maven.compiler.source>1.8</maven.
compiler.source>
<maven.compiler.target>1.8</maven.
compiler.target>
<project.build.sourceEncoding>UTF-8</
project.build.sourceEncoding>
<project.reporting.
outputEncoding>UTF-8</project.reporting.
outputEncoding>
<quarkus-plugin.version>1.3.0.Final</
quarkus-plugin.version>
<quarkus.platform.artifact-id>quarkusuniverse-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</
quarkus.platform.group-id>
<quarkus.platform.version>1.3.0.Final</
quarkus.platform.version>
<surefire-plugin.version>2.22.1</
surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.groupid}</groupId>
<artifactId>${quarkus.platform.
artifact-id}</artifactId>
<version>${quarkus.platform.
version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</
artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</
artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</
artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</
artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</
artifactId>
<version>${quarkus-plugin.
version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</
artifactId>
<version>${compiler-plugin.
version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</
artifactId>
<version>${surefire-plugin.
version}</version>
<configuration>
<systemProperties>
<java.util.logging.manager>org.
jboss.logmanager.LogManager</java.util.
logging.manager>
</systemProperties>
You might have noticed that most of the
dependencies start with the groupId io.
quarkus and that they are not the usual
dependencies that you might find for Hibernate,
Resteasy, or Junit.
Quarkus Dependencies
Now, you may be wondering why Quarkus supplies
their own wrapper versions around these popular
libraries. The reason is to provide a bridge between
the library and Quarkus to resolve the runtime
dependencies at compile time. This is where
the magic of Quarkus happens and provides
projects with fast start times and smaller memory
footprints.
Does this mean that you are constrained to use
only Quarkus specific libraries? Absolutely not.
You can use any library you wish. You run Quarkus
applications on the JVM as usual, where you don’t
have limitations.
GraalVM and Native Images
Perhaps you already heard about this project
called GraalVM by Oracle Labs? In essence,
GraalVM is a Universal Virtual Machine to run
applications in multiple languages. One of the
most interesting features is the ability to build
your application in a Native Image and run it
even faster! In practice, this means that you just
have an executable to run with all the required
dependencies of your application resolved at
compile time.
This does not run on the JVM — it is a plain
executable binary file, but includes all necessary
components like memory management and
thread scheduling from a different virtual machine,
called Substrate VM to run your application.
For convenience, the sample Maven project already
has the required setup to build your project as
native. You do need to have GraalVM in your
system with the native-image tool installed.
Follow these instructions on how to do so. After
that, just build as any other Maven project but with
the native profile: mvn verify -Pnative. This will
generate a binary runner in the target folder, that
you can run as any other binary, with ./projectname-runner. The following is a sample output of
the runner in my box:
The InfoQ eMag / Issue #96 / July 2021
</configuration>
</plugin>
</plugins>
</build>
</project>
[io.quarkus] (main) code-with-quarkus
1.0.0-SNAPSHOT (powered by Quarkus
1.3.0.Final) started in 0.023s. Listening
on: http://0.0.0.0:8080
INFO [io.quarkus] (main) Profile prod
activated.
[io.quarkus] (main) Installed features:
[agroal, cdi, hibernate-orm, narayana-jta,
resteasy, resteasy-jsonb]
Did you notice the startup time? Only 0.023s. Yes,
our application doesn’t have much, but still pretty
impressive. Even for real applications, you will see
startup times in the order of milliseconds. You can
learn more about GraalVM on their website.
Developer Productivity
We have seen that Quarkus could help your
company become Cloud Native. Awesome. But
what about the developer? We all like new shiny
things, and we are also super lazy. What does
Quarkus do for the developer that cannot be done
with other technologies?
Well, how about hot reloading that actually works
without using external tools or complicated tricks?
Yes, it is true. After 25 years, since Java was born,
we now have a reliable way to change our code and
see those changes with a simple refresh. Again,
this is accomplished by the way Quarkus works
internally.
Everything is just code, so you don’t have to worry
about the things that made hot reloading difficult
anymore. It is a trivial operation.
17
The InfoQ eMag / Issue #96 / July 2021
To accomplish this, you have to run Quarkus in
Development Mode. Just run mvn quarkus:dev
and you are good to go. Quarkus will start up
and you are free to do the changes to your code
and immediately see them. For instance, you can
change your REST endpoint parameters, add new
methods, and change paths. Once you invoke them,
they will be updated reflecting your code changes.
How cool is that?
Is Quarkus Production Ready?
All of this seems to be too good to be true,
but is Quakus actually ready for production
environments? Yes it is.
A lot of companies are already adopting Quarkus as
their development/runtime environment. Quarkus
has a very fast release cadence (every few weeks),
and a strong Open Source community that helps
every developer in the Java community, whether
they are just getting started with Quarkus or are an
advanced user.
Check out this sample application that you can
download or clone. You can also read some of
the adoption stories in a few blog posts so you can
have a better idea of user experiences when using
Quarkus.
Conclusion
After a year of its official announcement, Quarkus
is already on version 1.3.1.Final. A lot of effort is
being put in the project to help companies and
developers to write applications that they can run
natively in the Cloud.
We don’t know how far Quarkus can go, but one
thing is for certain: Quarkus shook the entire Java
ecosystem in a space dominated by Spring. I
think the Java ecosystem can only win by having
multiple offerings that can push each other and
innovate to keep themselves competitives.
Resources
• Quarkus Website
18
•
Quarkus Start Page
•
Sample Github Repo
TL;DR
•
Quarkus is a new
technology aimed at
cloud development.
•
With Quarkus, you
can take advantage
of smaller runtimes
optimized for the cloud.
•
You don’t need to relearn
new APIs. Quarkus is
built on top of the bestof-breed technologies
from the last decade, like
Hibernate, RESTEasy,
Vert.x, and MicroProfile.
•
Quarkus is productive
from day one.
•
Quarkus is production
ready.
Feature-Driven Development: A
Brief Overview
Feature-driven development
(FDD) is a five-step Agile
framework that organizes
software development around
making progress on features in
one to two-week sprints. The five
steps are:
•
Develop an overall model
•
Build a features list
•
Plan by feature
•
Design by feature
•
Build by feature
A feature in FDD does not always
refer to a product feature; it could
refer to a small task or process a
client or user wishes to complete.
For example, “View all open
tasks”, “Pay on-line bill”, or “Chat
with my friends in the game.”
Features in FDD are similar to
user stories in Scrum.
As with other Agile software
development frameworks,
the goal of feature-driven
development is to iterate quickly
to satisfy the needs of the
customer. The five-step process
of FDD assigns roles and utilizes
a set of project management best
practices to ensure consistency,
making it easier for new team
members to onboard.
Roles
Before we can dive into the
methodology, it is important
to understand the six primary
roles involved in a featuredriven development team. Each
role serves a specific function
throughout the development
process, there may be more than
one person in a given role on a
team.
The Project Manager is the
leader of the whole project and
coordinates all the moving
parts, ensures deadlines get hit,
identifies gaps in the workflow,
and so on.
The Chief Architect creates the
blueprint for the overall system.
Part of their job is to educate the
people on the team about the
system›s design so each person
can effectively fit their individual
tasks within the context of the
whole project. The Chief Architect
approaches the project from a
holistic point of view.
The Development Manager
coordinates all the teams
ensuring they complete
their tasks on time providing
mentoring and leadership of
programming activities.
The Chief Programmer is an
experienced programmer that
leads a small development team,
helping with analysis and design
to keep the project moving in the
right direction.
The InfoQ eMag / Issue #96 / July 2021
SPONSORED ARTICLE
The Class Owners are individual
developers creating features
on smaller development teams.
Their responsibilities can include
designing, coding, testing, and
documenting the features or
classes.
The Domain Expert has
detailed knowledge of the user
requirements and understands
the problem customers want
solved.
In addition to the six primary
roles, there are eight supporting
roles that may be needed:
•
Release Manager
•
Language guru
•
Build engineer
•
Tool-smith
•
System administrator
•
Tester
•
Deployer
•
Technical writer
Please read the full-length version of this article
19
The InfoQ eMag / Issue #96 / July 2021
Project Helidon Tutorial: Building
Microservices with Oracle’s Lightweight
Java Framework
by Michael Redlich, Senior Research Technician at ExxonMobil Research & Engineering
Oracle introduced its new open-source
framework, Project Helidon, in September 2018.
Originally named J4C (Java for Cloud), Helidon
is a collection of Java libraries for creating
microservices-based applications. Within six
months of its introduction, Helidon 1.0 was
released in February 2019.
The current stable release is Helidon 1.4.4, but
Oracle is well on their way to releasing Helidon 2.0
planned for late Spring 2020.
20
This tutorial will introduce Helidon SE and Helidon
MP, explore the three core components of Helidon
SE, how to get started, and introduce a movie
application built on top of Helidon MP. There will
also be a discussion on GraalVM and what you can
expect with the upcoming release of Helidon 2.0.
Helidon Landscape
Helidon, designed to be simple and fast, is
unique because it ships with two programming
models: Helidon SE and Helidon MP. In the graph
below, you can see where Helidon SE and Helidon
Let’s start with the first version of
the startServer() method to start a Helidon web
server on a random available port:
private static void startServer() {
Routing routing = Routing.builder()
.any((request, response) ->
response.send(“Greetings from the web
server!” + “\n”))
.build();
Helidon SE
Helidon SE is a microframework that features three
core components required to create a microservice
-- a web server, configuration, and security -- for
building microservices-based applications. It is a
small, functional style API that is reactive, simple
and transparent in which an application server is
not required.
Let’s take a look at the functional style of Helidon
SE with this very simple example on starting the
Helidon web server using the WebServer interface:
WebServer.create(
Routing.builder()
.get(“/greet”, (req, res)
-> res.send(“Hello World!”))
.build())
.start();
Using this example as a starting
point, we will incrementally build a
formal startServer() method, part of a server
application for you to download, to explore the
three core Helidon SE components.
Web Server Component
Inspired by NodeJS and other Java frameworks,
Helidon’s web server component is an
asynchronous and reactive API that runs on top
of Netty. The WebServer interface provides basic
server lifecycle and monitoring enhanced by
configuration, routing, error handling, and building
metrics and health endpoints.
WebServer webServer = WebServer
.create(routing)
.start()
.toCompletableFuture()
.get(10, TimeUnit.SECONDS);
The InfoQ eMag / Issue #96 / July 2021
MP align with other popular microservices
frameworks.
System.out.println(“INFO: Server
started at: http://localhost:” + webServer.
port() + “\n”);
}
First, we need to build an instance of
the Routing interface that serves as an HTTP
request-response handler with routing rules. In
this example, we use the any() method to route the
request to the defined server response, “Greetings
from the web server!”, which will be displayed in the
browser or via the curl command.
In building the web server, we invoke the
overloaded create() method designed to accept
various server configurations. The simplest one, as
shown above, accepts the instance variable, routing,
that we just created to provide default server
configuration.
The Helidon web server was
designed to be reactive which means
the start() method returns and an instance of
the CompletionStage<WebServer> interface to
start the web server. This allows us to invoke
the toCompletableFuture() method. Since a specific
server port wasn’t defined, the server will find a
random available port upon startup.
Let’s build and run our server application with
Maven:
21
The InfoQ eMag / Issue #96 / July 2021
$ mvn clean package
$ java -jar target/helidon-server.jar
When the server starts, you should see the
following in your terminal window:
Apr 15, 2020 1:14:46 PM io.helidon.
webserver.NettyWebServer <init>
INFO: Version: 1.4.4
Apr 15, 2020 1:14:46 PM io.helidon.
webserver.NettyWebServer lambda$start$8
INFO: Channel ‘@default’ started: [id:
0xcba440a6, L:/0:0:0:0:0:0:0:0:52535]
INFO: Server started at: http://
localhost:52535
As shown on the last line, the Helidon web server
selected port 52535. While the server is running,
enter this URL in your browser or execute it with
following curl command in a separate terminal
window:
$ curl -X GET http://localhost:52535
You should see “Greetings from the web server!”
To shut down the web server, simply add this line of
code:
webServer.shutdown()
.thenRun(() -> System.out.
println(“INFO: Server is shutting down...
Good bye!”))
.toCompletableFuture();
Configuration Component
The configuration component loads and processes
configuration properties. Helidon’s Config interface
will read configuration properties from a defined
properties file, usually but not limited to,
in YAML format.
Let’s create an application.yaml file that will
provide configuration for the application, server,
and security.
app:
greeting: “Greetings from the web
server!”
22
server:
port: 8080
host: 0.0.0.0
security:
config:
require-encryption: false
providers:
- http-basic-auth:
realm: “helidon”
users:
- login: “ben”
password: “${CLEAR=password}”
roles: [“user”, “admin”]
- login: “mike”
password: “${CLEAR=password}”
roles: [“user”]
- http-digest-auth:
There are three main sections, or nodes,
within this application.yaml file
- app, server and security. The first two
nodes are straightforward. The greeting subnode
defines the server response that we hard-coded
in the previous example. The port subnode
defines port 8080 for the web server to use upon
startup. However, you should have noticed that
the security node is a bit more complex utilizing
YAML’s sequence of mappings to define multiple
entries. Separated by the ‘-’ character, two security
providers, http-basic-auth and http-digest-auth,
and two users, ben and mike, have been defined.
We will discuss this in more detail in the Security
Component section of this tutorial.
Also note that this configuration allows for
clear-text passwords as the config.requireencryption subsection is set to false. You would
obviously set this value to true in a production
environment so that any attempt to pass a cleartext password would throw an exception.
Now that we have a viable configuration file,
let’s update our startServer() method to take
advantage of the configuration we just defined.
Routing routing = Routing.builder()
.any((request, response) ->
response.send(config.get(“app.greeting”).
asString().get() + “\n”))
.build();
WebServer webServer = WebServer
.create(serverConfig, routing)
.start()
.toCompletableFuture()
.get(10, TimeUnit.SECONDS);
System.out.println(“INFO: Server
started at: http://localhost:” + webServer.
port() + “\n”);
}
First, we need to build an instance of
the Config interface by invoking its create()
method to read our configuration file.
The get(String key) method, provided
by Config, returns a node, or a specific subnode,
from the configuration file specified by key.
For example, config.get(“server”) will return
the content under the server node and config.
get(“app.greeting”) will return “Greetings from
the web server!”.
Next, we create an instance
of ServerConfiguration, providing immutable
web server information, by invoking
its create() method by passing in the
statement, config.get(“server”).
The instance variable, routing, is built like the
previous example except we eliminate hard-coding
the server response by calling config.get(“app.
greeting”).asString().get().
The web server is created like the previous
example except we use a different version of
the create() method that accepts the two instance
variables, serverConfig and routing.
We can now build and run this version of our web
server application using the same Maven and Java
commands. Executing the same curl command:
$ curl -X GET http://localhost:8080
You should see “Greetings from the web server!”
Security Component
Helidon’s security component provides
authentication, authorization, audit and outbound
security. There is support for a number of
implemented security providers for use in Helidon
applications:
•
HTTP Basic Authentication
•
HTTP Digest Authentication
•
HTTP Signatures
•
Attribute Based Access Control (ABAC)
Authorization
•
JWT Provider
•
Header Assertion
•
Google Login Authentication
•
OpenID Connect
•
IDCS Role Mapping
The InfoQ eMag / Issue #96 / July 2021
private static void startServer() {
Config config = Config.create();
ServerConfiguration serverConfig
= ServerConfiguration.create(config.
get(“server”));
You can use one of three approaches to implement
security in your Helidon application:
•
a builder pattern where you manually provide
configuration
•
a configuration pattern where you provide
configuration via a configuration file
•
a hybrid of the builder and configuration
patterns
We will be using the hybrid approach to implement
security in our application, but we need to do some
housekeeping first.
23
The InfoQ eMag / Issue #96 / July 2021
Let’s review how to reference the users defined
under the security node of our configuration file.
Consider the following string:
security.providers.0.http-basic-auth.
users.0.login
When the parser comes across a number in
the string, it indicates there are one or more
subnodes in the configuration file. In this example,
the 0 right after providers will direct the parser
to move into the first provider subnode, httpbasic-auth. The 0 right after users will direct
the parser to move into the first user subnode
containing login, password and roles. Therefore, the
above string will return the login, password and
role information for the user, ben, when passed
into the config.get() method. Similarly, the login,
password and role information for user, mike, would
be returned with this string:
security.providers.0.http-basic-auth.
users.1.login
Next, let’s create a new class to our web
server application, AppUser, that implements
the SecureUserStore.User interface:
public class AppUser implements
SecureUserStore.User {
private String login;
private char[] password;
private Collection<String> roles;
public AppUser(String login, char[]
password, Collection<String> roles) {
this.login = login;
this.password = password;
this.roles = roles;
}
@Override
public String login() {
return login;
}
@Override
public boolean isPasswordValid(char[]
chars) {
return false;
24
}
@Override
public Collection<String> roles() {
return roles;
}
@Override
public Optional<String>
digestHa1(String realm, HttpDigest.
Algorithm algorithm) {
return Optional.empty();
}
}
We will use this class to build a map of roles to
users, that is:
Map<String, AppUser> users = new
HashMap<>();
To accomplish this, we add a new
method, getUsers(), to our web server application
that populates the map using the configuration
from the http-basic-auth subsection of the
configuration file.
private static Map<String, AppUser>
getUsers(Config config) {
Map<String, AppUser> users = new
HashMap<>();
ConfigValue<String> ben = config.
get(“security.providers.0.http-basic-auth.
users.0.login”).asString();
ConfigValue<String> benPassword = config.
get(“security.providers.0.http-basic-auth.
users.0.password”).asString();
ConfigValue<List<Config>> benRoles =
config.get(“security.providers.0.http-basicauth.users.0.roles”).asNodeList();
ConfigValue<String> mike = config.
get(“security.providers.0.http-basic-auth.
users.1.login”).asString();
ConfigValue<String> mikePassword =
config.get(“security.providers.0.http-basicauth.users.1.password”).asString();
ConfigValue<List<Config>> mikeRoles =
config.get(“security.providers.0.http-basicauth.users.1.roles”).asNodeList();
return users;
}
Now that we have this new functionality built
into our web server application, let’s update
the startServer() method to add security
with Helidon’s implementation of HTTP Basic
Authentication:
private static void startServer() {
Config config = Config.create();
ServerConfiguration serverConfig
= ServerConfiguration.create(config.
get(“server”));
Map<String, AppUser> users =
getUsers(config);
displayAuthorizedUsers(users);
SecureUserStore store = user ->
Optional.ofNullable(users.get(user));
HttpBasicAuthProvider provider =
HttpBasicAuthProvider.builder()
.realm(config.get(“security.
providers.0.http-basic-auth.realm”).
asString().get())
.subjectType(SubjectType.USER)
.userStore(store)
.build();
Security security = Security.builder()
.config(config.get(“security”))
.addAuthenticationProvider(provider)
.build();
WebSecurity webSecurity = WebSecurity.
create(security)
.securityDefaults(WebSecurity.
authenticate());
Routing routing = Routing.builder()
.register(webSecurity)
.get(“/”, (request, response)
-> response.send(config.get(“app.greeting”).
asString().get() + “\n”))
.get(“/admin”, (request,
response) -> response.send(“Greetings from
the admin, “ + users.get(“admin”).login() +
“!\n”))
.get(“/user”, (request,
response) -> response.send(“Greetings from
the user, “ + users.get(“user”).login() +
“!\n”))
.build();
WebServer webServer = WebServer
.create(serverConfig, routing)
.start()
.toCompletableFuture()
.get(10, TimeUnit.SECONDS);
The InfoQ eMag / Issue #96 / July 2021
users.put(“admin”, new AppUser(ben.
get(), benPassword.get().toCharArray(),
Arrays.asList(“user”, “admin”)));
users.put(“user”, new AppUser(mike.
get(), mikePassword.get().toCharArray(),
Arrays.asList(“user”)));
System.out.println(“INFO: Server
started at: http://localhost:” + webServer.
port() + “\n”);
}
As we did in the previous example, we will build
the instance variables, config and serverConfig. We
then build our map of roles to users, users, with
the getUsers() method as shown above.
Using Optional for null type-safety,
the store instance variable is built from
the SecureUserStore interface as shown with the
lambda expression. A secure user store is used for
both HTTP Basic Authentication and HTTP Digest
Authentication. Please keep in mind that HTTP
Basic Authentication can be unsafe, even when
used with SSL, as passwords are not required.
We are now ready to build an instance
of HTTPBasicAuthProvider, one
of the implementing classes of
the SecurityProvider interface.
The realm() method defines the security realm
name that is sent to the browser (or any other
client) when unauthenticated. Since we have a
realm defined in our configuration file, it is passed
into the method.
The subjectType() method defines the principal
type a security provider would extract or propagate.
It accepts one of two SubjectType enumerations,
namely USER or SERVICE. The userStore() method
25
The InfoQ eMag / Issue #96 / July 2021
accepts the store instance variable we just built to
validate users in our application.
With our provider instance variable, we can now
build an instance of the Security class used to
bootstrap security and integrate it with other
frameworks. We use the config() and addAuthenticationProvider() methods to accomplish this.
Please note that more than one security
provider may be registered by chaining together
additional addAuthenticationProvider() methods.
For example, let’s assume we defined instance
variables, basicProvider and digestProvider, to
represent the HttpBasicAuthProvider and HttpDigestAuthProvider classes, respectively. Our security instance variable may be built as follows:
Security security = Security.builder()
.config(config.get(“security”))
.addAuthenticationProvider(basicProvider)
.addAuthenticationProvider(digestProvider)
.build();
The WebSecurity class implements
the Service interface which encapsulates
a set of routing rules and related logic.
The instance variable, webSecurity, is
built using the create() method by passing
in the security instance variable and
the WebSecurity.authentic() method, passed into
the securityDefaults() method, ensures the request
will go through the authentication process.
Our familiar instance variable, routing, that we’ve
built in the previous two examples looks much
different now. It registers the webSecurity instance
variable and defines the endpoints, ‘/’, ‘/admin’, and
‘/user’ by chaining together get() methods. Notice
that the /admin and /user endpoints are tied to
users, ben and mike, respectively.
Finally, our web server can be started! After all the
machinery we just implemented, building the web
server looks exactly like the previous example.
26
We can now build and run this version of
our web server application using the same
Maven and Java commands and execute the
following curl commands:
•
$ curl -X GET http://localhost:8080/ will
return “Greetings from the web server!”
•
$ curl -X GET http://localhost:8080/
admin will return “Greetings from the admin,
ben!”
•
$ curl -X GET http://localhost:8080/
user will return “Greetings from the user, mike!”
You can find a comprehensive server
application that demonstrates all three versions
of the startServer() method related to the three
core Helidon SE components we just explored.
You can also find more extensive Helidon security
examples that will show you how to implement
some of the other security providers.
Helidon MP
Built on top of Helidon SE, Helidon MP is a small,
declarative style API that is an implementation
of the MicroProfile specification, a platform that
optimizes enterprise Java for a microservices
architecture for building microservices-based
applications.
The MicroProfile initiative, formed in 2016 as
a collaboration of IBM, Red Hat, Payara and
Tomitribe, specified three original APIs - CDI
(JSR 365), JSON-P (JSR 374) and JAX-RS (JSR370) - considered the minimal amount of APIs for
creating a microservices application. Since then,
MicroProfile has grown to 12 core APIs along with
four standalone APIs to support reactive streams
and GraphQL. MicroProfile 3.3, released in February
2020, is the latest version.
Helidon MP currently supports MicroProfile 3.2.
For Java EE/Jakarta EE developers, Helidon MP is
an excellent choice due to its familiar declarative
approach with use of annotations. There is no
Let’s take a look at the declarative style of Helidon
MP with this very simple example on starting the
Helidon web server and how it compares to the
functional style with Helidon SE.
public class GreetService {
@GET
@Path(“/greet”)
public String getMsg() {
return “Hello World!”;
}
}
Notice the difference in this style compared to the
Helidon SE functional style.
Helidon Architecture
Now that you have been introduced to Helidon SE
and Helidon MP, let’s see how they fit together.
Helidon’s architecture can be described in the
diagram shown below. Helidon MP is built on top of
Helidon SE and the CDI extensions, explained in the
next section, extend the cloud-native capabilities of
Helidon MP.
CDI Extensions
Helidon ships with portable Context and
Dependency Injection (CDI) estensions that support
integration of various data sources, transactions
and clients to extend the cloud-native functionality
of Helidon MP applications. The following
extensions are provided:
•
HikariCP, a “zero-overhead” production ready
JDBC connection pool data source
•
Oracle Universal Connection Datapool (UCP)
data sources
•
Jedis, a small Redis Java client
•
Oracle Cloud Infrastructure (OCI) object storage
clients
•
Java Transactional API (JTA) transactions
Helidon Quick Start Guides
Helidon provides quick start guides for
both Helidon SE and Helidon MP. Simply visit these
pages and follow the instructions. For example,
you can quickly build a Helidon SE application by
simply executing the following Maven command in
your terminal window:
$ mvn archetype:generate
-DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.
archetypes \
-DarchetypeArtifactId=helidonquickstart-se \
-DarchetypeVersion=1.4.4 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.
quickstart.se
The InfoQ eMag / Issue #96 / July 2021
deployment model and no additional Java EE
packaging required.
This will generate a small, yet working application
in the folder, helidon-quickstart-se, that
includes a test and configuration files for the
application (application.yaml), logging
(logging.properties), building a native image
with GraalVM (native-image.properties),
containerizing the application with Docker
(Dockerfile and Dockerfile.native) and
orchestrating with Kubernetes (app.yaml).
Similarly, you can quickly build a Helidon MP
application:
$ mvn archetype:generate
-DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.
archetypes \
-DarchetypeArtifactId=helidonquickstart-mp \
-DarchetypeVersion=1.4.4 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-mp \
-Dpackage=io.helidon.examples.
quickstart.mp
This is a great starting point for building more
complex Helidon applications as we will discuss in
the next section.
27
The InfoQ eMag / Issue #96 / July 2021
Movie Application
Using a generated Helidon MP quickstart
application, additional classes - a POJO, a
resource, a repository, a custom exception, and
an implementation of ExceptionMapper - were
added to build a complete movie application that
maintains a list of Quentin Tarantino movies.
The HelidonApplication class, shown below,
registers the required classes.
@ApplicationScoped
@ApplicationPath(“/”)
public class HelidonApplication extends
Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new
HashSet<>();
set.add(MovieResource.class);
set.
add(MovieNotFoundExceptionMapper.class);
return Collections.
unmodifiableSet(set);
}
}
You can clone the GitHub repository to learn more
about the application.
GraalVM
Helidon supports GraalVM, a polyglot virtual
machine and platform, that converts applications to
native executable code. GraalVM, created by Oracle
Labs, is comprised of Graal, a just-in-time compiler
written in Java, SubstrateVM, a framework
that allows ahead-of-time compilation of Java
applications into executable images, and Truffle, an
open-source toolkit and API for building language
interpreters. The latest version is 20.1.0.
You can convert Helidon SE applications to
native executable code using GraalVM’s nativeimage utility that is a separate installation using
GraalVM’s gu utility:
$ gu install native-image
$ export
28
GRAALVM_HOME=/usr/local/bin/graalvm-cejava11-20.1.0/Contents/Home
Once installed, you can return to the helidonquickstart-se directory and execute the following
command:
$ mvn package -Pnative-image
This operation will take a few minutes, but once
complete, your application will be converted to
native code. The executable file will be found in
the /target directory.
The Road to Helidon 2.0
Helidon 2.0.0 is scheduled to be released in the
late Spring 2020 with Helidon 2.0.0.RC1 available
to developers at this time. Significant new
features include support for GraalVM on
Helidon MP applications, new Web Client
and DB Client components, a new CLI tool,
and implementations of the standalone
MicroProfile Reactive Messaging and Reactive
Streams Operators APIs.
Until recently, only Helidon SE applications were
able to take advantage of GraalVM due to the use of
reflection in CDI 2.0 (JSR 365), a core MicroProfile
API. However, due to customer demand, Helidon
2.0.0 will support Helidon MP applications to be
converted to a native image. Oracle has created
this demo application for the Java community to
preview this new feature.
To complement the original three core Helidon SE
APIs - Web Server, Configuration and Security - a
new Web Client API completes the set for Helidon
SE. Building an instance of the WebClient interface
allows you to process HTTP requests and
responses related to a specified endpoint. Just
like the Web Server API, Web Client may also be
configured via a configuration file.
You can learn more details on what developers can
expect in the upcoming GA release of Helidon 2.0.0.
The InfoQ eMag / Issue #96 / July 2021
Virtual Panel: the MicroProfile Influence
on Microservices Frameworks
by Michael Redlich, Senior Research Technician at ExxonMobil Research & Engineering
Since 2018, several new microservices frameworks
- including Micronaut, Helidon and Quarkus - have
been introduced to the Java community, and have
made an impact on microservices-based and
cloud-native applications development.
The MicroProfile community and specification was
created to enable the more effective delivery of
microservices by enterprise Java developers. This
effort has influenced how developers are currently
designing and building applications.
MicroProfile will continue to evolve with changes to
its current APIs and most likely the creation of new
APIs.
Developers should familiarize themselves with
Heroku’s “Twelve-Factor App,” a set of guiding
principles that can be applied with any language
or framework in order to create cloud-ready
applications.
When it comes to the decision to build an
application using either a microservices or
monolithic style, developers should analyze the
business requirements and technical context
before choosing the tools and architectures to use.
In mid-2016, two new initiatives, MicroProfile and
the Java EE Guardians (now the Jakarta EE
Ambassadors), had formed as a direct response
to Oracle having stagnated their efforts with the
release of Java EE 8.
The Java community felt that enterprise Java had
fallen behind with the emergence of web services
technologies for building microservices-based
applications.
29
The InfoQ eMag / Issue #96 / July 2021
Introduced at Red Hat’s DevNation conference on
June 27, 2016, the MicroProfile initiative was
created as a collaboration of vendors - IBM, Red
Hat, Tomitribe, Payara - to deliver microservices
for enterprise Java. The release of MicroProfile
1.0, announced at JavaOne 2016, consisted of
three JSR-based APIs considered minimal for
creating microservices: JSR-346 - Contexts and
Dependency Injection (CDI); JSR-353 - Java API for
JSON Processing (JSON-P); and JSR-339 - Java
API for RESTful Web Services (JAX-RS).
By the time MicroProfile 1.3 was released in
February 2018, eight community-based APIs,
complementing the original three JSR-based
APIs, were created for building more robust
microservices-based applications. A fourth
JSR-based API, JSR-367 - Java API for JSON
Binding (JSON-B), was added with the release of
MicroProfile 2.0.
Originally scheduled for a June 2020
release, MicroProfile 4.0 was delayed so
that the MicroProfile Working Group could
be established as mandated by the Eclipse
Foundation.
The working group defines the MicroProfile
Specification Process and a formal Steering
Committee composed of organizations and
Java User Groups (JUGs), namely Atlanta
JUG, IBM, Jelastic, Red Hat and Tomitribe. Other
organizations and JUGs are expected to join in
2021. The MicroProfile Working Group was able
to release MicroProfile 4.0 on December 23, 2020
featuring updates to all 12 core APIs and alignment
with Jakarta EE 8.
The founding vendors of MicroProfile offered their
own microservices frameworks, namely Open
Liberty (IBM), WildFly Swarm/Thorntail (Red
Hat), TomEE (Tomitribe) and Payara Micro (Payara),
that ultimately supported the MicroProfile initiative.
30
In mid-2018, Red Hat renamed WildFly Swarm,
an extension of Red Hat’s core application
server, WildFly, to Thorntail to provide their
microservices framework with its own identity.
However, less than a year later, Red Hat
released Quarkus, a “Kubernetes Native Java
stack tailored for OpenJDK HotSpot and GraalVM,
crafted from the best-of-breed Java libraries and
standards.”
Dubbed “Supersonic Subatomic Java,” Quarkus
quickly gained popularity in the Java community to
the point that Red Hat announced Thorntail’s endof-life in July 2020. Quarkus joined the relatively
new frameworks, Micronaut and Helidon, that were
introduced to the Java community less than a year
earlier. With the exception of Micronaut, all of these
microservices-based frameworks support the
MicroProfile initiative.
The core topics for this virtual panel are threefold:
first, to discuss how microservices frameworks
and building cloud-native applications have been
influenced by the MicroProfile initiative.
Second, to explore the approaches to developing
cloud-native applications with microservices and
monoliths, and also the recent trend in reverting
back to monolith-based application development.
And third, to debate several best practices for
building microservices-based and cloud-native
applications.
Panelists
• Cesar Hernandez, senior software engineer at
Tomitribe.
•
Emily Jiang, Liberty microservice architect and
advocate at IBM.
•
Otavio Santana, developer relations engineer at
Platform.sh.
•
Erin Schnabel, senior principal software
engineer at Red Hat.
Hernandez: MicroProfile has been allowing Java
developers to increase their productivity during
the creation of new distributed applications and,
at the same time, has allowed them to boost their
existing Jakarta EE (formerly known as Java EE)
architectures.
Jiang: Thanks to the APIs published by
MicroProfile, the cloud-native applications using
MicroProfile have become slim and portable. With
these standard APIs, cloud-native application
developers can focus on their business logics and
their productivities are significantly increased.
The cloud-native applications not only work on
the runtime that developed against originally, they
would also work on different runtimes that support
MicroProfile, such as Open Liberty, Quarkus,
Helidon, Payara, TomEE, etc. The developers
learned the APIs once and never need to worry
about re-learn a complete set of APIs in order to
achieve the same goal.
Santana: The term cloud-native is still a large gray
area and it’s concept is still under discussion. If
you, for example, read ten articles and books on the
subject, all these materials will describe a different
concept. However, what these concepts have in
common is the same objective - get the most out
of technologies within the cloud computing model.
MicroProfile popularized this discussion and
created a place for companies and communities
to bring successful and unsuccessful cases. In
addition, it promotes good practices with APIs,
such as MicroProfile Config and the third factor
of The Twelve-Factor App.
Schnabel: The MicroProfile initiative has done a
good job of providing Java developers, especially
those used to Java EE concepts, a path forward.
Specific specs, like Config, Rest Client, and Fault
Tolerance, define APIs that are essential for
microservices applications. That’s a good thing.
InfoQ: Since 2018, the Java community has been
introduced to Micronaut, Helidon and Quarkus. Do
you see a continuation of this trend? Will there be
more microservices frameworks introduced as we
move forward?
Hernandez: Yes, I think new frameworks and
platforms help in the synergy that produces
innovation within the ecosystem. Over time, these
innovations can lead to new standards.
The InfoQ eMag / Issue #96 / July 2021
InfoQ: How has the MicroProfile initiative, first
introduced in 2016, influenced the way developers
are building today’s microservices-based and
cloud-native applications?
Jiang: It is great to see different frameworks
introduced to aid Java developers to develop
microservices. It clearly means Java remains
attractive and still the top choice for developing
microservices. The other trend I saw is that the
newly emerging frameworks adopt MicroProfile
as the out-of-box solution for developing cloudnative microservices, as demonstrated by Quarkus,
Helidon, etc. Personally, I think there will be more
microservices framework introduced. I also predict
they might wisely adopt MicroProfile as their cloudnative solutions.
Santana: Yes, I strongly believe that there is a big
trend with this type of framework especially to
explore AOT compilation and the benefits from the
application’s cold start.
The use of reflection by the frameworks has its
trade-offs. For example, at the application start and
in-memory consumption, the framework usually
invokes the inner class ReflectionData within Class.
java. It is instantiated as type SoftReference, which
demands a certain time to leave the memory.
So, I feel that in the future, some frameworks
will generate metadata with reflection and other
frameworks will generate this type of information at
compile time like the Annotation Processing API or
similar. We can see this kind of evolution already
happening in CDI Lite, for example.
31
The InfoQ eMag / Issue #96 / July 2021
Another trend in this type of framework is to
support a native image with GraalVM. This
approach is very interesting when working with
serverless, after all, if you have code that will run
only once, code improvements like JIT and Garbage
Collector don’t make sense.
Schnabel: I don’t see the trend stopping. As
microservices become more specialized, there is a
lot more room to question what is being included
in the application. There will continue to be a
push to remove unnecessary dependencies and
reduce application size and memory footprint
for microservices, which will lead either to new
Java frameworks, or to more microservices in
other languages that aren’t carrying 20+ years of
conventions in their libraries.
InfoQ: With a well-rounded set of 12 core
MicroProfile APIs, do you see the need for
additional APIs (apart from the standalone
APIs) to further improve on building more
robust microservices-based and cloud-native
applications?
Hernandez: Eventually, we will need more than
the 12 core APIs. The ecosystem goes beyond
MicroProfile and Java; the tooling, infrastructure,
and other stakeholders greatly influence creating
new APIs.
Jiang: The MicroProfile community adapts itself
and stays agile. It transforms together with the
underlying framework or cloud infrastructure. For
example, due to the newly established CNCF project
OpenTelemetry (OpenTracing + OpenCensus),
MicroProfile will need to realign MicroProfile Open
Tracing with OpenTelemetry. Similarly, the previous
adopted technologies might not be mainstream any
more. MicroProfile will need to align with the new
trend. For example, with the widely adopted metrics
framework, Micrometer, which provides a simple
facade over the instrumentation clients for the
most popular monitoring systems, the MicroProfile
community is willing to work with Micrometer.The
32
other areas I think MicroProfile can improve is to
provide some guidance such as how to do logging,
require all MicroProfile supporters to support CORS,
etc. If any readers have any other suggestions,
please start up a conversation on the MicroProfile
Google Group. Personally, I would like to open
up the conversation in early 2021 on the new
initiatives and gaps MicroProfile should focus on.
Santana: Yes, and also improvements that need
and can be made on the existing ones. Many things
need to be worked together with the Jakarta EE
team. The first point would be to embrace The
Twelve-Factor App even more in the APIs. For
example, making it easier for those who need
credentials, such as username and password, in an
application through the MicroProfile Config API. I
could use JPA and the JMS as examples.
Another point would be how to think about the
integration of CDI events with Event-Driven Design
and increasingly explore the functional world within
databases and microservices.
Schnabel: There is still a lot of evolution to come in
the reactive programming space. We all understand
REST, but even that space is seeing some change
as pressure grows to streamline capabilities.
There is work to do to align with changing industry
practices around tracing and metrics, and I
think there will continue to be some changes as
the capabilities that used to be provided by an
application server move out into the infrastructure
be that raw Kubernetes, a serverless PaaS-esque
environment, or whatever comes next as we try to
make Kubernetes more friendly.
InfoQ: How would building cloud-native
applications be different if it weren’t for
microservices? For example, would it be more
difficult to build a monolith-based cloud-native
application?
Hernandez: When we remove microservices from
the cloud-native equation, I immediately think of
Jiang: I don’t see a huge difference between
building cloud-native applications vs.
microservices. Cloud-Native Application
includes modular monolith and microservices.
Microservices are normally small, while monolith
is large by tradition. MicroProfile APIs can be used
by either monolith or microservices. The important
bit is that cloud-native applications might contain
a few modules, which share the same release
cadence and are interconnected. Coud-native
applications should have their dedicated database.
Santana: So, as Java didn’t die, the monoliths won’t
die anytime soon either! Many of the best practices
we use today in the cloud-native environment
with microservices can be applied with monoliths.
It is worth remembering that The Twelve Factor
App, for example, is based on the book Patterns
of Enterprise Application Architecture, which is
from 2003 and the popularization of the cloud
environment occurred, mainly, in 2006 with Amazon
AWS.
In general, the best practices you need to build a
microservice architecture are also needed in the
monolith environment. For example, CI/CD, test
coverage, The Twelve Factor App, etc.
Since monoliths need fewer physical layers, usually
two or three as a database and application, they
tend to be easier to configure, monitor and deploy
in the cloud environment. The only caveat is that its
scalability in general is vertical, which often causes
the limit that your cloud provider can provide to be
exceeded.
Schnabel: In my opinion, the key characteristics for
cloud-native applications are deployment flexibility
and frequency: as long as your application does not
depend on the specifics of its environment (e.g., it
has credentials for backing services injected and
avoids special code paths), you’re in good shape.
If your monolith can be built, tested, and deployed
frequently and consistently (hello, automation!),
you’re ok.
InfoQ: Despite the success of microservices, recent
publications have been discussing the pitfalls of
microservices with recommendations to return
to monolith-based applications development.
What are your thoughts on this subject? Should
developers seriously consider a move back to
monolith-based applications development?
The InfoQ eMag / Issue #96 / July 2021
monolith uber-jars deployments. I feel that the
early adopters of container-based infrastructures
started taking advantage of cloud-native features
using SOAP architectures instead of JAX-RS based
microservices.
Hernandez: Developers should analyze the
business requirements and technical context
before choosing the tools and architectures to use.
As a developer, we get excited to test new
frameworks and tools. Still, we need to understand
and analyze the architecture for a particular
scenario. The challenge then turns into finding the
balance between the pros and cons of adopting or
moving back to existing architectures.
Jiang: Monolith is not evil. Sometimes modular
monolith performs as well as microservices.
Microservices are not the only choice for
cloud-native solutions. Choosing monolith or
microservices depends on the company team
structure and culture. If you have a small team
and all of the applications releasing at the same
cadence, monolith is the right choice. On the other
hand, if you have a distributed team and they
operate independently, microservices fit in well with
the team structure and culture.
If moving towards microservices causes the team
moving slower, moving back to monolith-based
application is wise.
In summary, there is no default answer. You have
to make your own choice based on your company
setting and culture. Choose whatever suits the
33
The InfoQ eMag / Issue #96 / July 2021
company best. You should not measure the
success based on whether you have microservices
or the number of the microservices.
Santana: As developers, we need to understand
that there are no silver bullets in any architectural
solution. All solutions have their respective tradeoffs. The problem was the herd effect, which led to
the community thinking that you, as a developer,
are wrong if you are not on microservices. In
technology, this was neither the first nor the
last buzzword that will flood the technological
community.
Overall, I see this with great eyes; I feel that the
community is mature enough to understand that
microservices cannot be applied to all possible
things, that is, common sense and pragmatism is
still the best tool for the developer/architect.
Schnabel: I agree with the sentiment: there is
no need to start with microservices, especially
if you’re starting something new. I’ve found that
new applications are pretty fluid in the beginning.
What you thought you were going to build isn’t
quite what you needed in the end. Using a
monolithic approach for that initial phase saves
a lot of effort: coordinating the development and
deployment of many services in the early days
of an application is more complicated, and there
isn’t a compelling reason to start there. Sound
software practices inside the monolith can set you
up for later refactoring into microservices once
your application starts to stabilize. I’ve found that
obvious candidates emerge pretty naturally.
InfoQ: What are some of your recommended best
practices for building microservices-based and
cloud-native applications?
Hernandez: Analyze first if the problem to solve
fits within the microservice approach and the
constraints or opportunities the particular
project has. All the benefits of the cloud-native
34
approach depend on how well you apply the base
architecture.
Jiang: The base practices for building
microservices-based and cloud-native applications
are to adopt The Twelve Factor App, which ensures
your microservices perform well in the cloud. Check
out my 12 Factor App talk at QCon London 2020 on
how to use MicroProfile and Kubernetes to develop
cloud-native applications. The golden tip is to use
an open and well-adopted standard for your cloudnative applications and then you are rest assured
they will work well in the cloud.
Santana: In addition to the practices, both
popularize such as DDD, The Twelve Factor App,
etc. I consider myself as having good sense
and being a pragmatic, professional, excellent
practitioner. You don’t need to be afraid to apply
something simple if it meets the requirement
and your business, so escape the herd effect
of technology. In the recent book I’m reading,
97 Things Every Cloud Engineer Should Know:
Collective Wisdom from the Experts, among several
tips I will quote two:
The first is no problem if you don’t use Kubernetes;
as I mentioned, don’t feel bad about not using a
technology when it’s not needed.
The second is the possibility of using services
that increase abstraction and decrease the risk
of implementing cloud-like PaaS, DBaaS. It is
important to understand that this type of service
brings the cloud-provider’s whole know-how
and makes operations such as backup/restore,
updating services such as databases something
much simpler. For example, I can mention
Platform.sh, which allows deploying cloud-native
applications straightforwardly in a GitOps centric
way.
If you’re going to use microservices, use
microservices. Understand that they are completely
decoupled, independent entities with their own
evolutionary lifecycle. APIs and contract testing
should be on your radar. Do your best to wrap your
head around the ramifications of microservices: if
you try to ensure that everything works together
in a staging environment and then deploy a set of
versioned services that work together, you’ve gone
back to a monolithic deployment model. If you have
to consistently release two or more services at the
same time because they depend on each other, you
are dealing with a monolith that has unnecessary
moving parts.
evolve with either new APIs or changes to existing
ones.
Our panelists also recommended that all
developers should also familiarize themselves with
Heroku’s “Twelve-Factor App,” a set of guiding
principles that can be applied with any language
or framework in order to create cloud-ready
applications.
The InfoQ eMag / Issue #96 / July 2021
Schnabel: Avoid special code paths. They are hard
to test and can be harder to debug. Make sure
environment-specific dependencies (hosts, ports,
credentials for backing services) are injected in a
consistent way regardless of the environment your
application runs in.
With the advent of microservices, monolithbased application development had seemingly
been characterized as “evil.” However, our
experts warned against subscribing to such
a characterization. Along with describing the
pros and cons, they explained situations in
which monolith-based application development
would be beneficial over microservices-based
application development. The challenging part
now is for you to take this wisdom and apply it
to your specific context and set of challenges.
Think about application security up front. Every
exposed API is a risk. Every container you build
will need to be maintained to mitigate discovered
vulnerabilities. Plan for that. When you’re moving to
a cloud-native environment, you should take it as
a given that “making an application work and then
forgetting about it and letting it run forever” is no
longer an option, and ensure you have the tools in
place for on-going maintenance.
Conclusions
In this virtual panel, we asked the expert
practitioners to discuss MicroProfile and
microservices frameworks. We even asked for their
thoughts on the recent trend to return to monolithbased application development.
Our experts agreed that MicroProfile has influenced
the way developers are building microservicesbased and cloud-native applications. The Java
community should expect new microservices
frameworks to emerge as MicroProfile itself will
35
Read recent issues
Kubernetes and Cloud
Architectures
Re-Examining Microservices
after the First Decade
Java Innovations That Are
on Their Way
Does it feel to you like the
modern application stack is
constantly shifting with new
technologies and practices
emerging at a blistering pace?
We’ve hand-picked a set of
articles that highlight where
we’re at today. With a focus
on cloud-native architectures
and Kubernetes, these
contributors paint a picture of
what’s here now, and what’s
on the horizon.
We have prepared this eMag
for you with content created
by professional software
developers who have been
working with microservices
for quite some time. If you
are considering migrating to
a microservices approach,
be ready to take some
notes about the lessons
learned, mistakes made, and
recommendations from those
experts.
In this eMag we want to talk
about “Innovations That Are
On Their Way”. This includes
massive, root-and-branch
changes such as Project
Valhalla as well as some
of the more incremental
deliveries coming from Project
Amber such as Records and
Sealed Types.
InfoQ
@InfoQ
InfoQ
InfoQ
Download