Quarkus 4

Download as pdf or txt
Download as pdf or txt
You are on page 1of 10

Chapter 2 – Getting Started with Quarkus

@Validated

@NotNull
private String message;

@NotNull

private String name;

@Valid

public String getMessage() { return this.message; }


public void setMessage(String message) { this.message = message; }

public String getName() { return this.name; }


public void setName(String name) { this.name = name; }

@NotNull
@Positive
private Integer prizeAmount;

@NotEmpty
private List<String> recipients;
public Integer getPrizeAmount() { return this.prizeAmount; }
public void setPrizeAmount(Integer prizeAmount) { this.prizeAmount =
prizeAmount; }
public List<String> getRecipients() { return this.recipients; }
public void setRecipients(List<String> recipients) { this.recipients =
recipients; }
}
}

Listing 2.5 shows that required fields must be annotated. Additionally, Spring’s
@Validated annotation must be placed on the class to enable any validation, and
the @Valid annotation must exist on any nested class attributes. Additional bean vali-
dation annotations can be used for more advanced validation, such as the @Positive
annotation placed on the prizeAmount nested class.

Quarkus
There are a few differences in Quarkus. First, Quarkus uses interfaces rather than

approach when it comes to required versus optional properties. By default, all prop-

an Optional
the @Valid or @Validated annotations to perform validation. Quarkus validates a
-
dation extension is present.

Quarkus for Spring Developers | 31


Chapter 2 – Getting Started with Quarkus

Quarkus by Listing 2.6.

@WithName(“message”) Note: For consistency with


String getMessage(); the Spring Boot example
in Listing 2.5, the Quarkus
example in Listing 2.6 uses
@WithDefault(“!”) method names derived from

The @WithName annotations


@WithName(“name”) are unnecessary if the
Optional<String> getName(); method names are the same
as the property name. For
@WithName(“content”) example, if getMessage()
was renamed to message(),
the preceding @WithName
annotation could be removed.
@Positive
@WithName(“prizeAmount”)
Integer getPrizeAmount();

@WithName(“recipients”)
List<String> getRecipients();
}
}

The previous section showed how to inject values into properties during configura-
tion. In practice, it is often necessary to inject different sets of configuration values
at different times. For example, the configurations for testing are often different
from the configurations for running the application in production. Both Quarkus
and Spring support this capability by the concept of a Profile. However, one ma-
jor difference is that Quarkus unifies all configurations for all profiles into a single
configuration file.

Description
dev Activated when running in development mode (i.e., mvn quarkus:dev).
test Activated when running tests (i.e., mvn verify).
prod

Quarkus for Spring Developers | 32


Chapter 2 – Getting Started with Quarkus

Mechanism Example Similar Spring action


./mvnw package
system property (declared within
application.properties) or

QUARKUS_PROFILE export QUARKUS_PROFILE= export


environment variable

If both the system property and the environment variable are set, the system property

.env

_{PROFILE-NAME}_KEY=value

.env

_DEV_GREETING_NAME=Quarkus for Spring Developer

Example
Return to the src/main/resources/application.properties
present is the one added previously. The value of the greeting.name property will be
Quarkus (properties)

%dev.greeting.name=Quarkus for Spring Developer (dev)


%prod.greeting.name=Quarkus for Spring Developer (prod)
%test.greeting.name=RESTEasy

Your application.properties

greeting.name=Quarkus (properties)
%dev.greeting.name=Quarkus for Spring Developer (dev)
%prod.greeting.name=Quarkus for Spring Developer (prod)
%test.greeting.name=RESTEasy

Return to the browser window and refresh the page. You should now see the text Hello
Quarkus for Spring Developer (dev). Return to the terminal where the ./mvnw
quarkus:dev command is running and terminate dev mode, typically using
keystrokes.

Now run the application outside of dev mode to activate the prod

$ ./mvnw clean package

Quarkus for Spring Developers | 33


Chapter 2 – Getting Started with Quarkus

quarkus-run.jar will be created inside the target/


quarkus-app directory. Now run the application by running the following command:

$ java -jar target/quarkus-app/quarkus-run.jar

When the application starts up, it should print out . Also, no-
tice the startup time. In most cases, the startup time will be less than one second. This

applications to start up without the delays found in many Java applications.

Return to the browser window and refresh the page. You should now see the text Hello
Quarkus for Spring Developer (prod). Return to the terminal and terminate the
running application in the same way as before.

Dependency Injection

needs to function correctly. Dependency injection was one of the key components of
Spring Framework. It became so popular that Java and the Java EE platform followed
what Spring was doing. Today, Contexts and Dependency Injection (CDI) for Java 2.0
(JSR-365) [2.23] is the base for ArC [2.24], the dependency injection framework used
by Quarkus.

One of the main differences between dependency injection in Quarkus and Spring
is that Quarkus performs as much of the injection as possible at build time, whereas
Spring performs injection at runtime. Quarkus’s build-time augmentation leads to much
better startup performance because ArC just needs to load all the metadata that was
already computed at build time.

attributes. Additionally, in both Spring and Quarkus, the default process of matching
a bean to an injection point is done via type. This pattern is synonymous with Spring’s
“injection by type.” In Quarkus, exactly one bean must be assignable to an injection
point; otherwise, the build fails. In Spring, if there were multiple beans of the same type,
the application would build but fail to start.

whether the application is correct as part of their local development or CI/CD pipelines.
There is no need to wait until the application is deployed into an environment and start-

developer productivity and lower project costs.

Scopes
Quarkus and Spring also support the notion of bean scopes. Table 2.8 describes the
scopes available for a Quarkus application, along with the Spring equivalents. Quarkus
extensions can provide other custom scopes. For example, the quarkus-narayana-jta
extension provides @javax.transaction.TransactionScoped.

Quarkus for Spring Developers | 34


Chapter 2 – Getting Started with Quarkus

Table 2.8: Available Quarkus bean scopes.

Quarkus scope Spring equivalent Quarkus description


annotation
@ApplicationScoped @Scope(“singleton”) A single bean instance is used for the
application and shared among all injection
points. The instance is created lazily once a
method is invoked on its proxy.
@Singleton @Scope(“singleton”) Just like @ApplicationScoped except that
no proxy is used. The instance is created when
an injection point resolves to an injected @
Singleton bean.
@RequestScoped @Scope(“request”) The bean instance is associated with the
current request, usually an HTTP request.
@Dependent @Scope(“prototype”) A pseudo-scope. The instances are not shared,
and every injection point spawns a new instance
of the dependent bean. The lifecycle of a
dependent bean is bound to the bean injecting
it. The bean will be created and destroyed along
with the bean injecting it.
@SessionScoped @Scope(“session”) This scope is backed by a javax.servlet.
http.HttpSession object. This annotation
is available only if the quarkus-undertow
extension is used.

@ApplicationScoped or @Singleton?
Quarkus provides two different scopes representing Spring’s singleton scope:
@ApplicationScoped and @Singleton. Which one to use depends on what you are
trying to accomplish.

An @Singleton bean has no proxy. Proxies should be familiar to Spring users because
they are the backbone behind Spring’s dependency injection mechanism. When the
Spring runtime injects a bean into another bean, the injection is usually a proxy to the
actual bean. It’s a placeholder where Spring can wrap the bean with some alternate
implementation or delay the bean’s instantiation.

Because an @Singleton bean lacks a proxy, the bean is created eagerly when an instance
@Singleton bean
can not be mocked. By contrast, an instance of an @ApplicationScoped bean is created
lazily once an instance is acted upon @ApplicationScoped

can with an @Singleton bean.

As a general rule of thumb, the recommendation is to use @ApplicationScoped by


default unless there is a compelling reason to use @Singleton. @ApplicationScoped

Example
Let’s take the example being used so far in this chapter and change it to use de-
pendency injection. All the logic for computing a greeting message resides in the
GreetingResource class. Follow these steps to create a new class containing the
required functionality and refactor GreetingResource to inject a new bean.

1. Return to the terminal and execute ./mvnw quarkus:dev there.


2. In the src/main/java/org/acme directory, create a new Java class with the
GreetingService.java.

Quarkus for Spring Developers | 35


Chapter 2 – Getting Started with Quarkus

3. Add the @ApplicationScoped annotation to the class.


4. Inject the greeting attribute into the GreetingService class exactly as in the
GreetingResource class, by adding a attribute and a constructor
injecting the .
5. Inside GreetingService, create a getGreeting method:
public String getGreeting() {
return “Hello “ + this.greeting;
}

6. Open the GreetingResource class.


7. Remove the greeting attribute from the class.
8. Add a new attribute to the class:
9. Change the constructor within the class to instead inject a GreetingService argument:
public GreetingResource(GreetingService greetingService) {
this.greetingService = greetingService;
}

10. Change the return of the hello() method to be:


return this.greetingService.getGreeting();

Listings 2.8 and 2.9 show the completed classes.

Listing 2.8: Completed GreetingService.java.


@ApplicationScoped
public class GreetingService {

greeting) {
this.greeting = greeting;
}

public String getGreeting() {


return “Hello “ + this.greeting;
}
}

Listing 2.9: Completed GreetingResource.java.


@Path(“/hello-resteasy”)
public class GreetingResource {

public GreetingResource(GreetingService greetingService) {


this.greetingService = greetingService;
}

@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return this.greetingService.getGreeting();
}
}

Quarkus for Spring Developers | 36


Chapter 2 – Getting Started with Quarkus

Return to your browser and refresh the page. You should see the text Hello Quarkus
for Spring Developer (dev). Return to the terminal where the ./mvnw quarkus:dev
command is running and terminate Dev Mode.

The GreetingResource class uses constructor injection to inject the GreetingService.

-
ples in this book promote constructor injection. Listing 2.10 shows what the Greeting
Resource
Quarkus advises against
using private

@Path(“/hello-resteasy”)
public class GreetingResource {
@Inject up build and application
GreetingService greetingService; startup times and reduces
an application’s memory
@GET footprint.
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return this.greetingService.getGreeting();
}
}

Lifecycle Callbacks
Both Quarkus and Spring support creating lifecycle callbacks on beans, supporting the
and @PreDestroy annotations found in the javax.annotation
package. Methods annotated with are invoked before the bean
instance is placed into service. It is safe to perform bean initialization within the method.
Methods annotated with @PreDestroy are invoked before the destruction of the bean
instance. It is safe to perform cleanup tasks within the method.

Native Image
Quarkus’s design accounted for native compilation from the onset. A Quarkus native
executable starts much faster and uses far less memory than a traditional JVM. Sim-
ilar native image capabilities in Spring are still considered experimental or beta as of
this writing.

To build a native image and run native image tests, you need to install either a distribu-
tion of GraalVM or a working container runtime. Additionally, Maven or Gradle has some Note: Maven and Gradle
scaffolding to facilitate the building of the native image and launching it for tests. scaffolding isn’t discussed in
this book. However, it is doc-
The following example assumes that GraalVM CE or Mandrel has been downloaded, umented extensively in the
and either the GRAALVM_HOME environment variable has been set to its location. Once Quarkus guide for building
all the requirements are met, a native image can be built using a simple command on a native executable [2.25],
the terminal: along with how to use a
container runtime environ-
$ ./mvnw package -Pnative ment instead of installing a
GraalVM distribution locally.
Native compilation can take several minutes to complete. Once complete, the native The code.quarkus.io gen-
executable chapter-2-simple-project-1.0.0-SNAPSHOT-runner can be found in erator provides everything
the project’s target directory. On the terminal, execute the following: needed for Maven or Gradle
to function properly.
$ ./target/chapter-2-simple-project-1.0.0-SNAPSHOT-runner

Quarkus for Spring Developers | 37


Chapter 2 – Getting Started with Quarkus

The command will produce the following output:

__ ____ __ _____ ___ __ ____ ______


--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
INFO [io.quarkus] (main) chapter-2-simple-project 1.0.0-SNAPSHOT native (powered
by Quarkus 2.xx.x.Final) started in 0.015s. Listening on: http://0.0.0.0:8080

INFO [io.quarkus] (main) Installed features: [cdi, resteasy,


smallrye-context-propagation]

From this output, notice the application started in just 15 milliseconds. Return to
your browser and refresh the page. You should see the text Hello Quarkus for
Spring Developer (prod).

Resident Set Size


Resident set size (RSS) [2.26] is the amount of memory occupied by a process. Calcu-
lating the RSS of a process is dependent on the operating system used. For macOS and
Linux-based systems, the commands also vary by shell. In the bash shell on macOS or
Red Hat Enterprise Linux, running the following command:

$ ps -o pid,rss,command -p $(pgrep -f chapter-2-simple-project-1.0.0-SNAPSHOT-runner)

results in the following output:

13949 18604 ./target/chapter-2-simple-project-1.0.0-SNAPSHOT-runner

The 18604 value in the RSS column is the RSS size, in kilobytes, of the running process. Note: The startup time and
Dividing that number by 1024 gives an RSS size of just over 18 megabytes for the run- RSS output was gathered
ning application’s memory footprint. from a terminal running on
macOS. Your startup time
Return to the terminal where the ./target/chapter-2-simple-project-1.0.0- and RSS output may vary.
SNAPSHOT-runner command is running and terminate it the same way as before.

Testing
Testing is essential in any application to ascertain that the required functionality is
implemented correctly. Both Quarkus and Spring include testing frameworks based on
JUnit 5 [2.27] and Mockito [2.28].

Continuous Testing
As mentioned in Chapter 1, Quarkus enables test-driven development by under-
standing which tests are affected by classes and methods within the application. As
changes are made to the application source code, Quarkus can automatically rerun
affected tests in the background, giving developers instant feedback about the code
they are writing.

To enable continuous testing, return to the terminal and execute ./mvnw quarkus:dev.
Once the Quarkus banner appears, notice in the terminal the following text at the very
bottom: Tests paused, press [r] to resume, also shown in Figure 2.6.

Quarkus for Spring Developers | 38


Chapter 2 – Getting Started with Quarkus

Figure 2.6: Continuous testing output.

Press r in the terminal to enable continuous testing. The console should show output
similar to:

--
All 1 tests are passing (0 skipped), 1 tests were run in 4552ms.

Unit Testing
Unit testing aims to isolate a single class so methods on the class can be tested. The
test retains complete control over any external dependencies the class under test may
have. One way to facilitate control over dependencies is to use constructor injection
when creating classes, as seen in the previous examples. Bootstrapping the entire
application isn’t necessary in most cases when using this pattern with either Spring or
Quarkus. Simply constructing a class and passing mocks or other required classes via
the constructor and then asserting results is good enough.

Let’s see this in action by adding a test class for the GreetingService class created
earlier. Create a new Java class called GreetingServiceTest inside the src/test/
java/org/acme folder. Inside that class, add a single test method and save the changes:

@Test
public void getGreetingOk() {
Assertions.assertEquals(“Hello Quarkus”,
new GreetingService(“Quarkus”).getGreeting());
}

Listing 2.11 shows the entire GreetingServiceTest class.

Listing 2.11: GreetingServiceTest.java.


public class GreetingServiceTest {
@Test
public void getGreetingOk() {
Assertions.assertEquals(“Hello Quarkus”, new GreetingSer-
vice(“Quarkus”).getGreeting());
}
}

Return to the terminal and notice that Quarkus automatically ran the test successfully.
Also notice that only one test was run: the test that was just created. The existing test in
the GreetingResourceTest.java class was not rerun.

--
All 2 tests are passing (0 skipped), 1 tests were run in 53ms.

There are times when you need the underlying framework to provide capabilities
for you to write tests. One such example is when exposing RESTful endpoints. In
that case, a test would issue an HTTP request with specific parameters, such as a
request path, headers, and request parameters, and then perform assertions on
some returned JSON structure. This kind of testing can’t be done by instantiating a
class and calling methods directly on it, either in Quarkus or in Spring.

Quarkus for Spring Developers | 39


Chapter 2 – Getting Started with Quarkus

When the chapter-2-simple-project project was generated earlier, a test class


named GreetingResourceTest inside the src/test/java/org/acme folder was
generated along with the project. The specifics of the REST-assured frame-
work [2.29] used in this example will be discussed in detail in Chapter 3. However,
the testHelloEndpoint method asserts that, when issuing an HTTP GET to
the /hello-resteasy endpoint, the result is the value Hello RESTEasy. Listing 2.12
shows the test in its current form.

Listing 2.12: GreetingResourceTest.java.


@QuarkusTest
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get(“/hello-resteasy”)
.then()

.body(is(“Hello RESTEasy”));
}
}

Another thing to notice is the @QuarkusTest annotation at the top of the class. This
annotation is similar to the @SpringBootTest annotation on tests in a Spring Boot
application. The main difference in Quarkus is that the application is started only once
and reused for all tests annotated with @QuarkusTest. In contrast, in Spring Boot,
the application is started before each test class annotated with @SpringBootTest
and then shut down after all tests in the class are executed. Subsequently, a Spring
application restarts when the next test class annotated with @SpringBootTest is
executed.

Additionally, classes annotated with @QuarkusTest can inject other beans, as we’ll
see in the next section. Quarkus ensures that the state of the beans in the running
application matches tests or test class’s requirements without having to restart the
application.

Mocking
Earlier the GreetingResource class was refactored to inject a GreetingService, so
the GreetingResourceTest class test should be refactored as well. The Greetin-
gResource class needs to be tested in isolation, so it would be better for the test to
mock the GreetingService class.

The quarkus-junit5-mockito library is required to accomplish this. The quarkus-


junit5-mockito library contains many utilities, classes, and annotations that make
Mockito easier to use within Quarkus. Many of these are similar to the spring-boot-test
library provided by Spring Boot.

Open the project’s pom.xml <dependencies> section. Towards the


end of the the section, add the following:

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>

Quarkus for Spring Developers | 40

You might also like