Dhruv garg
Posted on July 3, 2020
I have finally decided to post my first article and I am trying to learn quarkus. So here I am writing this article for our first supersonic Quarkus application. We will develop a RESTful application using JAX-RS for a To Do application.
In recent, I have heard many times that java is going to be obsolete and many reasons for this are valid. But, no more if java community can come up with something like quarkus then I am sure java is not going anywhere. It is really an awesome framework and hopefully you will also say that after reading this article.
What is Quarkus?
Quarkus is a Kubernetes Native Java framework tailored for GraalVM and HotSpot. The goal of Quarkus is to make Java a leading platform in Kubernetes and serverless environments while offering developers a unified reactive and imperative programming model to optimally address a wider range of distributed application architectures.
Why quarkus?
Whenever someone thinks about java development. They think that application will take huge memory and it will be hard to develop campared to it's counterparts. But quarkus, let's you develop high performance applications that take very low space and is easy to work with because of features like live reload. Also wide array of extensions and libraries provided by quarkus team and community makes building quarkus application much easier and fun (yeah java can be fun š ).
please go to this link for more information: https://quarkus.io/
summary:
- less memory usage
- fast startup time
- live reload out of the box
- uses standard java ee specifications. so if you are already familier with jakarta ee or even with spring you are good to go
- Quarkus does not use reflection API.
let's build a simple TO DO list endpoints
For setting up Development environment
you will need :
- JDK 8 or 11 with JAVA_HOME configured
- GraalVM (If you want to build native image)
- Apache Maven or Gradle
- IDE (I prefer intellij Idea)
Getting started
The easiest way is to go to https://code.quarkus.io/ and download zip file of initial application with default config and these libraries
- RestEasy implementation of JAX-RS
- RestEasy JsonB
It is your choice to use maven or gradle. I will be using gradle.
The project Structure will look like this:
quarkus-project/
āāā build.gradle
āāā gradle
ā āāā wrapper
ā āāā gradle-wrapper.jar
ā āāā gradle-wrapper.properties
āāā gradle.properties
āāā gradlew
āāā gradlew.bat
āāā README.md
āāā settings.gradle
āāā src
āāā main
ā āāā docker
ā ā āāā Dockerfile.jvm
ā ā āāā Dockerfile.native
ā āāā java
ā ā āāā org
ā ā āāā quarkus
ā ā āāā ExampleResource.java
ā āāā resources
ā āāā application.properties
ā āāā META-INF
ā āāā resources
ā āāā index.html
āāā native-test
ā āāā java
ā āāā org
ā āāā quarkus
ā āāā NativeToDoResourceIT.java
āāā test
āāā java
āāā org
āāā quarkus
āāā ToDoResourceTest.java
- The ExampleResource.java contains the REST Endpoint.
- The docker folder contains two files for building app as container. Dockerfile.jvm to run application on JVM and Dockerfile.native to build native app.
- The test and native-test for testing JVM and native application
- The static resources are put inside resources folder, like index.html.
- The application.properties is the yaml file used for configuration. e.g. Defining datasource and configuring database.
Building our ToDo API
For simplicity I will not be using database in this article and maybe show how to use it in next article.
You can try to run initial project using
./gradlew quarkusDev
./mvnw quarkus:dev
This will run app in dev mode which provides hot reload. so any change in code is instantly visible without restarting app. Go Ahead and try out this, by simply changing some text of return statement.
The initial project contain ExampleResource a simple hello end point that returns hello. Replace this with ToDoResource and add this code.
ToDoResource.java
This contains 6 EndPoints for basic CRUD operations:
- GET
/api/todo
returns all tasks - GET
/api/todo/{id}
returns task with given id - POST
/api/todo
with ToDo json object body to create new task - POST
/api/todo
with task String body to create new task - PATCH
/api/todo/status/{id}
to mark task completed or non-completed - DELETE
/api/todo/{id}
deletes task with given id
@Path Annotation specifies uri for endpoint. It is used to define base uri when declared at top of resource and specifies uri for specific API if specified over function. e.g. api/todo in this case is base uri and status/{id} is uri of changeStatus API.
@Produces Annotation tells which type of result is served by API. for e.g. JSON, plain text or HTML.
@Consumes Annotation tells which type of body is consumed by API. for e.g. JSON, plain text or HTML.
@Inject Annotation is used to inject Instanse of ToDoService using CDI. This enables us to just use the dependency/object we need without worrying about it's creation and lifecycle as it is managed by context and dependency injection system.
package org.quarkus;
import javax.inject.Inject;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/api/todo")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ToDoResource {
@Inject
ToDoService service;
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response newTask(@Valid ToDo task) {
try {
if (task == null) {
return Response.status(400).entity("Task not provided").build();
}
return Response.ok(service.addTask(task)).build();
}
catch (Exception e){
return Response.serverError().build();
}
}
@POST
@Consumes(MediaType.TEXT_PLAIN)
public Response newTaskString(@Valid String task) {
try {
if (task == null) {
return Response.status(400).entity("Task not provided").build();
}
return Response.ok(service.addTask(task)).build();
}
catch (Exception e){
return Response.serverError().build();
}
}
@GET
public Response allTasks(){
return Response.ok(service.allTasks()).build();
}
@GET
@Path("/{id}")
public Response task(@PathParam("id") String id){
return Response.ok(service.getTask(id)).build();
}
@PATCH
@Path("status/{id}")
public Response changeStatus(@PathParam("id") String id){
return Response.ok(service.changeStatus(id)).build();
}
@DELETE
@Path("/{id}")
public Response delete(@PathParam("id") String id){
return Response.ok(service.delete(id)).build();
}
}
ToDo.java
This is our POJO with constructors to initialize objects.
package org.quarkus;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Future;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.UUID;
public class ToDo {
@NotNull
public String id;
@NotNull
public String task;
@NotNull
public boolean isComplete;
@NotNull
public Date time;
public ToDo(String task, Date time) {
this.id = UUID.randomUUID().toString();
this.task = task;
this.isComplete = false;
this.time = time;
}
public ToDo(String task, boolean isComplete, Date time) {
this.id = UUID.randomUUID().toString();
this.task = task;
this.isComplete = isComplete;
this.time = time;
}
@Override
public String toString() {
return "ToDo{" +
"id='" + id + '\'' +
", task='" + task + '\'' +
", isComplete=" + isComplete +
", time=" + time +
'}';
}
}
TodoService.java
This class contains logic of application. It is ApplicationScoped which means only one instance of this class is created during lifetime of app and same instance is injected and reused wherever it is required by CDI system.
package org.quarkus;
import javax.enterprise.context.ApplicationScoped;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
@ApplicationScoped
public class ToDoService {
ArrayList<ToDo> list = new ArrayList<>();
public ToDoService(){
}
public boolean addTask(String task){
ToDo toDo = new ToDo(task, false, new Date());
return list.add(toDo);
}
public boolean addTask(ToDo task){
return list.add(task);
}
public List<ToDo> allTasks(){
return list;
}
public ToDo getTask(String id){
Optional<ToDo> item = list.stream().filter(toDo -> toDo.id.equals(id)).findFirst();
return item.orElse(null);
}
public ToDo changeStatus(String id){
ToDo todo = getTask(id);
todo.isComplete = !todo.isComplete;
return todo;
}
public boolean delete(String id){
return list.remove(getTask(id));
}
}
Now you can run the application using same command mentioned earlier. The output on terminal will look like this.
dhruv@dhruv-laptop:~/Programs/javaPrograms/to_do$ ./gradlew quarkusDev
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :quarkusDev
Listening for transport dt_socket at address: 5005
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2020-07-02 19:03:56,518 INFO [io.quarkus] (Quarkus Main Thread) to_do 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.5.2.Final) started in 6.566s. Listening on: http://0.0.0.0:8080
2020-07-02 19:03:56,578 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2020-07-02 19:03:56,579 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy, resteasy-jsonb]
<=========----> 75% EXECUTING [41s]
> :quarkusDev
If you look closely you will see that it is also telling all installed features like cdi, resteasy, resteasy-jsonb in 3rd line after quarkus banner.
Building the native executable
Use below command to generate a native executable
./mvnw package -Pnative
./gradlew build -Dquarkus.package.type=native
The build will produce target/{package_name}-1.0-SNAPSHOT-runner. You can run it using: ./target/{package_name}-1.0-SNAPSHOT-runner on linux.
For more details on proper setup follow this
Conclusion
In this article we have covered, how to create simple REST Api in Quarkus and why quarkus enables us developers to build better applications.
You can find source code here. star repo if you like it.
Hope you liked this article and it helped you to learn something new. This is my first time writing on quarkus and posting Article/Blog online. Feel free to let me know if anything is wrong in comments, also tell in comments if you liked it.
You can connect with me on twitter or linkedin
Thanks for reading this article. Best of luck for learning Quarkus.
Posted on July 3, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.