Super fast API development with Java using Quarkus

Recently I tried Quarkus for a fast development and was amazed by how fast this tool enable my project to lift off the ground. Here are the simple step to get started.

mvn io.quarkus:quarkus-maven-plugin:1.9.2.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=quarkus-bootstrap \
    -DclassName="org.acme.getting.started.GreetingResource" \
    -Dpath="/hello"
quarkus-bootstrap

Create an application service class

package org.acme.getting.started;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingService {
    public String greeting(String name) {
        return "Hello " + name;
    }
}

Inject to quarkus REST resource class

package org.acme.getting.started;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/hello")
public class GreetingResource {
    @Inject
    GreetingService greetingService;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String hello(@PathParam("name") String name) {
        return greetingService.greeting(name);
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }

}	

It’s even super fast for writing test, natually!

package org.acme.getting.started;

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import java.util.UUID;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
                .when().get("/hello")
                .then()
                .statusCode(200)
                .body(is("hello"));
    }

    @Test
    public void testGreetingEndpoint() {
        String uuid = UUID.randomUUID().toString();
        given()
                .pathParam("name", uuid)
                .when().get("/hello/greeting/{name}")
                .then()
                .statusCode(200)
                .body(is("Hello " + uuid));
    }

}

Now let’s run the test, and let’s test the endpoint as well

# Run tests
mvn test 

T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.acme.getting.started.GreetingResourceTest
2020-11-16 21:50:34,668 INFO  [io.quarkus] (main) Quarkus 1.9.2.Final on JVM started in 1.172s. Listening on: http://0.0.0.0:8081
2020-11-16 21:50:34,686 INFO  [io.quarkus] (main) Profile test activated. 
2020-11-16 21:50:34,686 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.342 s - in org.acme.getting.started.GreetingResourceTest
2020-11-16 21:50:35,678 INFO  [io.quarkus] (main) Quarkus stopped in 0.030s
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

# Run dev mode with hot deployment 
mvn quarkus:dev

❯ curl -w "\n" http://localhost:8080/hello
hello
❯ curl -w "\n" http://localhost:8080/hello/greeting/quarkus
Hello quarkus
❯ curl -w "\n" http://localhost:8080/hello/greeting/quarkus
Hello quarkus

# Let's pack a jar file and test as well
mvn clean package
java -jar target/getting-started-1.0-SNAPSHOT-runner.jar
curl -w "\n" http://localhost:8080/hello/greeting/quarkus
Hello quarkus
Add to my src(0)

No account yet? Register

Spring Boot fast api development

Developing a new secure RESTful API with Spring-boot is probably the easier, these are the simplest steps:

Go to https://start.spring.io/ and enter “Spring Web” in dependency, then download
You can also use the following pom in a maven project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>rest-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>rest-service</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

Data model class

package com.example.restservice;

public class Greeting {

	private final long id;
	private final String content;

	public Greeting(long id, String content) {
		this.id = id;
		this.content = content;
	}

	public long getId() {
		return id;
	}

	public String getContent() {
		return content;
	}
}

Spring boot REST controller

package com.example.restservice;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

	private static final String template = "Hello, %s!";
	private final AtomicLong counter = new AtomicLong();

	@GetMapping("/greeting")
	public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
		return new Greeting(counter.incrementAndGet(), String.format(template, name));
	}
}

Spring boot application class

package com.example.restservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RestServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(RestServiceApplication.class, args);
    }

}

Spring boot REST controller test class

package com.example.restservice;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
@AutoConfigureMockMvc
public class GreetingControllerTests {

	@Autowired
	private MockMvc mockMvc;

	@Test
	public void noParamGreetingShouldReturnDefaultMessage() throws Exception {

		this.mockMvc.perform(get("/greeting")).andDo(print()).andExpect(status().isOk())
				.andExpect(jsonPath("$.content").value("Hello, World!"));
	}

	@Test
	public void paramGreetingShouldReturnTailoredMessage() throws Exception {

		this.mockMvc.perform(get("/greeting").param("name", "Spring Community"))
				.andDo(print()).andExpect(status().isOk())
				.andExpect(jsonPath("$.content").value("Hello, Spring Community!"));
	}

}

Now run and test the application

mvn clean verify
mvn spring-boot:run

curl http://localhost:8080/greeting
{"id":1,"content":"Hello, World!"}

curl http://localhost:8080/greeting?name=Biden
{"id":2,"content":"Hello, Biden!"}
Add to my src(0)

No account yet? Register

the simplest way to call to REST api with Springboot

In this tutorial, we’ll demonstrate how easy it is to create a REST client with Spring boot to consume a REST api endpoint using declarative style

First, let’s create REST service using nodejs following the example here: https://leftsidemonitor.com/nodejs-express-create-project/

router.get('/api/users', function(req, res, next) {
  let users = [{
    'name': 'Hulk',
    'skill': 'Smash'
  }, {
    'name': 'Thor',
    'skill': 'thunder'
  }];

  res.end(JSON.stringify(users));
});

Let’s test this microservice:

curl http://localhost:3000/api/users
[
  {
    "name": "Hulk",
    "skill": "Smash"
  },
  {
    "name": "Thor",
    "skill": "thunder"
  }
]

Now in your Spring boot project, let’s define a model that is corresponded to the above data model

package com.leftsidemonitor.testhero.model;

import lombok.Data;

@Data
public class User {
    private String name;
    private String skill;
}

Before we go ahead and create a REST client, the following packages are needed, please update your pom.xml if you’re using maven

## Add to pom.xml
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-httpclient</artifactId>
  <version>11.0</version>
</dependency>
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-jackson</artifactId>
  <version>11.0</version>
</dependency>
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-slf4j</artifactId>
  <version>11.0</version>
</dependency>

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>testcontainers-bom</artifactId>
      <version>${testcontainers.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>Hoxton.SR6</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

Now is a simple REST client using Spring Cloud Feign library

package com.leftsidemonitor.testhero.client;

import com.leftsidemonitor.testhero.model.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

@FeignClient(name = "users", url = "${remote.api.user.client}")
public interface UserServiceClient {
    @GetMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
    List<User> getUsers();
}

Now let’s create a simple Service class that uses this REST client and also modify the receiving data:

package com.leftsidemonitor.testhero.service;

import com.leftsidemonitor.testhero.client.UserServiceClient;
import com.leftsidemonitor.testhero.model.User;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class UserService {
    private UserServiceClient userServiceClient;

    public List<User> getUsersFromRemoteAPI() {
        List<User> remoteUsers = userServiceClient.getUsers();
        return remoteUsers.stream()
                .peek(user -> user.setName("From a galaxy far away: " + user.getName()))
                .collect(Collectors.toList());
    }
}

And here is a controller class to bind all of these together

package com.leftsidemonitor.testhero.web;

import com.leftsidemonitor.testhero.model.User;
import com.leftsidemonitor.testhero.service.UserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/remote")
@Slf4j
@AllArgsConstructor
public class UserResource {
    private UserService userService;

    @GetMapping("/users")
    public List<User> getRemoteUsers() {
        return userService.getUsersFromRemoteAPI();
    }
}

Important: in order for FeignClient to work, there must be a simple configuration as following:

package com.leftsidemonitor.testhero.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Logger;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableFeignClients(basePackages = "com.leftsidemonitor.testhero.client")
public class FeignConfig {
    private static final ObjectMapper mapper = new ObjectMapper();

    private final ObjectFactory<HttpMessageConverters> messageConverters;

    public FeignConfig(ObjectFactory<HttpMessageConverters> messageConverters) {
        this.messageConverters = messageConverters;
    }

    @Bean
    public Decoder feignDecoder() {
        return new JacksonDecoder(mapper);
    }

    @Bean
    public Encoder feignEncoder() {
        return new JacksonEncoder(mapper);
    }

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.BASIC;
    }
}
remote.api.user.client=http://localhost:3000/api

Now let’s test:

curl http://localhost:8080/remote/users
[
  {
    "name": "From a galaxy far away: Hulk",
    "skill": "Smash"
  },
  {
    "name": "From a galaxy far away: Thor",
    "skill": "thunder"
  }
]
Add to my src(0)

No account yet? Register

Spring test private method and function argument

Suppose we have a service class that contains private method. How can we properly test a private method without changing it to public? The answer is to try to capture its input and output instead.

+ Service class with private method

package com.leftsidemonitor.testhero.service;

import com.leftsidemonitor.testhero.model.Customer;
import com.leftsidemonitor.testhero.repository.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class CustomerService {
    @Autowired
    private CustomerRepository customerRepository;

    public List<Customer> findByLastName(String lastName) {
        lastName = capitalize(lastName);

        return customerRepository.findByLastNameIgnoreCase(lastName);
    }

    private String capitalize(String str) {
        return str.toUpperCase();
    }
}

+ Test class with for private methods

package com.leftsidemonitor.testhero.service;

import com.leftsidemonitor.testhero.model.Customer;
import com.leftsidemonitor.testhero.repository.CustomerRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class CustomerServiceTest {
    @Mock
    private CustomerRepository customerRepository;
    private ArgumentCaptor<String> customerRepositoryArgumentCaptor = ArgumentCaptor.forClass(String.class);
    @InjectMocks
    private CustomerService customerService;

    @Test
    public void shouldCallToPrivateCapitalizeName() {
        // GIVEN
        var lastName = "Hood";

        // WHEN
        when(customerRepository.findByLastNameIgnoreCase(anyString())).thenReturn(List.of(new Customer()));
        var customers = customerService.findByLastName(lastName);

        // THEN
        verify(customerRepository, times(1)).findByLastNameIgnoreCase(customerRepositoryArgumentCaptor.capture());
        String modifiedLastName = customerRepositoryArgumentCaptor.getValue();
        assertThat(customers).hasSize(1);
        assertThat(modifiedLastName).isEqualTo("HOOD");
    }
}

The rest of other classes used in this example can be found here: https://leftsidemonitor.com/how-to-test-spring-jpa-repository/

Add to my src(0)

No account yet? Register

Java Simple Download URL into InputStream

Sometimes it’s very best to download a file from a URL into an InputStream and pass it to the next handle, here is an example

package com.leftsidemonitor.testhero.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

@Component
@Slf4j
public class JavaFileDownloader {

    public InputStream download(String publicFileUrl) throws Exception {
        log.debug("Downloading URL={}", publicFileUrl);
        try (InputStream input = new URL(publicFileUrl).openStream()) {
            return new ByteArrayInputStream(input.readAllBytes());
        } catch (IOException e) {
            log.error("Download error", e);
            throw new Exception("Download error", e);
        }
    }
}
Add to my src(0)

No account yet? Register

How to test Spring JPA Repository

Spring JPA is very convenient to use, and Spring provides a great tool for testing it too

+ Customer entity class

package com.leftsidemonitor.testhero.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Customer {

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private Long id;
	private String firstName;
	private String lastName;

	public Customer() {}

	public Customer(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	@Override
	public String toString() {
		return String.format(
				"Customer[id=%d, firstName='%s', lastName='%s']",
				id, firstName, lastName);
	}

	public Long getId() {
		return id;
	}

	public String getFirstName() {
		return firstName;
	}

	public String getLastName() {
		return lastName;
	}
}

+ CustomerRepository class

package com.leftsidemonitor.testhero.repository;

import com.leftsidemonitor.testhero.model.Customer;
import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface CustomerRepository extends CrudRepository<Customer, Long> {
    List<Customer> findByLastNameIgnoreCase(String lastName);
}

+ CustomerRepositoryTests class

package com.leftsidemonitor.testhero.repository;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import com.leftsidemonitor.testhero.model.Customer;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;

@DataJpaTest
public class CustomerRepositoryTests {
    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private CustomerRepository customerRepository;

    @Test
    public void testFindByLastName() {
        Customer customer = new Customer("first", "last");
        entityManager.persist(customer);

        List<Customer> findByLastName = customerRepository.findByLastNameIgnoreCase(customer.getLastName().toUpperCase());

        assertThat(findByLastName).extracting(Customer::getLastName).containsOnly(customer.getLastName());
    }
}
Add to my src(0)

No account yet? Register

Java 8 Optional Exception Handling

Java 8 Optional is a great tool to deal with null and exception handling for such null cases. The following example process for void or no return case and throw exception if the entity is not found.

Optional<User> optionalUser = userRepository.findByName(userName);

optionalUser.ifPresentOrElse(user -> doDelete(user), () -> {
    throw new UserNotFoundException(id, userId);
});

Instead if we want to do some action, transform and then return, following is the example:

Optional<User> optionalUser = userRepository.findByName(userName);

return optionalUser.map(user -> doUpdate(user))
  .orElseThrow(UserNotFoundException::new);
Add to my src(0)

No account yet? Register

JPA use two Entity classes for the same table using inheritance

Normally with JPA you’re creating one entity per one database table, what if you want to separate the entities using Java inheritance but using the same table? For example: Both StaffUser and NormalUser of a website are just User, they should be in the same table but for convenience on the data model and programming, how to separate them as illustrate below?

Luckily JPA already have @DiscriminatorColumn which can be used to differentiate between the derived entities, and both of these entities extends from the same abstractEntity which in tern mapped to one single table.

import lombok.Data;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;

@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@Data
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
public abstract class AbstractUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    private String firstName;
    private String lastName;
    private String age;
    private String email;
    private String password;
}

As can be seen, the above abstract class is mapped to one table users and have a discriminator column called “type”, this column determines the child entity as following:

import lombok.Data;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("NORMAL")
@Data
public class NormalUser extends AbstractUser {
    private String facebookId;
    private String topic;
}
import lombok.Data;

import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("STAFF")
@Data
public class StaffUser extends AbstractUser {
    private Long managerId;
    private String permissions;
}

Both NormallUser and StaffUser classes will now have all the properties declared in AbstractUser, both of them will be stored in the same table users but on the code module side, they are two different entities, forming from two different Java classes.

Add to my src(0)

No account yet? Register