Setup Monitoring System for your Spring Boot applications Using Spring Boot Admin

unsplash-logoZdeněk Macháček

In past few microservices articles, We discuss different Spring Cloud features.In microservices architecture we have lot of services doing small small tasks. Monitoring all these application becomes very critical and integral part of your technology stack.
Spring Boot Admin is Community project provides an admin interface for Spring Boot applications.
let check how we can setup and use Spring Boot Admin.

The code for this post is available for download here.

Setting Up Spring Boot Admin Server

To Add admin server in project we need to add below spring boot starter dependancy

spring boot admin starter server
1
2
3
4
  <dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

Add @EnableAdminServer on Main Application.

Admin Main Class
1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableAdminServer
public class SpringBootAdminServerApplication {

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

Now if we Start the application, the admin UI should be available

Register clients with admin Server

Each application that wants to register has to include the Spring Boot Admin Client dependancy.

Client
1
2
3
4
  <dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

And add Admin.client url in application.properties
spring.boot.admin.client.url=http://localhost:8080/
Spring Boot Admin will use spring.application.name property to display the name of application.

Our application is registered on Admin Consol, But apart for Status no other information is available

Magic of Spring Boot Actuator and Spring Boot Admin Server

Spring Boot Admin depends on Actuators endpoints to provide information. let’s enable Actuator endpoints.
put below in client application.
management.endpoints.web.exposure.include=*
Note, Expose only needed endpoints.

Tips and tricks

Display Build Info
To Display Build Info, Simply add build-info Goal in spring-boot-maven-plugin plugin

Build Info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Display Git Commit info
To know which commit id is deployed, We can simply use git-commit-id-plugin, git information will be available in info endpoint and will be displayed on admin ui.

Build Info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>

Logs
By default the log file is not accessible via actuator endpoints and therefore not visible in Spring Boot Admin. In order to enable the logfile actuator endpoint you need to configure Spring Boot to write a logfile, either by setting logging.path or logging.file.
Add below property in Client application.
logging.file=target/sample-boot-application.log
At run time we can also change the logs level, Which is useful in lower environments.
Multiple Instance
Admin Will Automatically Group applications based on application names and will show consolidated view.
Tags
Tags are a way to add visual markers per instance, they will appear in the application list as well as in the instance view.
spring.boot.admin.client.instance.metadata.tags.environment=QA

Notification

By default Spring Boot Admin will send registered application changes to DOWN or OFFLINE. We can do lot of customizations for Notifications.
Below is configurations for sending emails using google smpt server.
Use spring-boot-starter-mail dependancy in admin.

Email Config in Admin
1
2
3
4
5
6
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=XYZ@gmail.com
spring.mail.password=XYZ
spring.boot.admin.notify.mail.to=XYZ
spring.mail.properties.mail.smtp.starttls.enable=true

Discovery Clients

The Spring Boot Admin Server can use Spring Clouds DiscoveryClient to discover applications. The advantage is that the clients don’t have to include the spring-boot-admin-starter-client. You just have to add a DiscoveryClient implementation to your admin server - everything else is done by AutoConfiguration.

Security

As Admin Server is an spring boot application, Securing admin server is same as securing normal spring boot application.

Reference

Documentation
The code for this post is available for download here.

Share Comments

Service Discovery and Registration Using Spring Cloud Eureka

unsplash-logoFo Fa

In past few microservices articles, We discuss different Spring Cloud features, Like Config Server , OpenFeign and Ribbon. In this Post, Let’s talk about Spring Cloud Netflix – Eureka.Why we need it ? How to configure the Eureka server , How to register service in Registry Server?

In cloud based microservices environment, Servers come and go. Unlike the traditional architecture which work with servers with well known IP addresses and host names. In cloud platform, It is not possible to know the IP and Host names of services. Also the containers use dynamic IPs for autoscaling moreover Load balancing requires much more sophistication in registering and de-registering servers for balancing.
So we can not have tight coupling between the services based of IP or Host Names. Spring Cloud Netflix – Eureka helps to solve that problem.

The code for this post is available for download here.

In typical Eureka setup we have
Eureka Server: Which acts as service registry.
Eureka Client REST services which registers itself at the registry.

let’s Setup Eureka Server & Client for our demo application.

Demo Application: Our Simple PricingService calculates Price for the Product. Pricing Service gets some dynamic discount details from DiscountService.

Setting Up Eureka Server

To Add Eureka Server in project, we need to use the spring-cloud-starter-netflix-eureka-server artifact id. and use @EnableEurekaServer on main class.
Eureka server can also register it’s self as a Eureka client. For the demo we will disable that using registerWithEureka flag. Eureka Server can be configured to read configuration from Config server that we discuss Here. this property can be set using fetchRegistry flag.

Eureka Server
1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

Eureka Server Properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#Server Specifics
server:
port: 8761
spring:
application:
name: eureka-server
#Eureka Specifics
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

Start the EurekaServerApplication and you should be able to see Eureka dashboard at http://localhost:8761/

Setting Up Eureka Clients - Discount Service

let’s Setup Discount Service to register with Eureka Server. To include the Eureka Client in project we need to use spring-cloud-starter-netflix-eureka-client artifact ID.Spring Boot will identify the client jar in classpath and will try to register with Server. We can also use @EnableDiscoveryClient to do that. We need to provide information about the Eureka Server, This can be done using serviceUrl property.
Discount service has simple endpoint that returns the discount for product.

Discount Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@SpringBootApplication
@EnableDiscoveryClient
@Slf4j
public class DiscountServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DiscountServiceApplication.class, args);
}
}
@RestController
@RequestMapping("/discount/{product}")
@Slf4j
class DiscountController{
@GetMapping
public int getDiscountPercentage(@PathVariable("product") String product){
log.info("Getting Discount for Product {}",product);
return 50;
}
}

Discount Service properties
1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
application:
name: discount-service

server:
port: 8081

eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/

Start the DiscountServiceApplication and you should be able to see DiscountService registered on Eureka dashboard http://localhost:8761/
Note Application Name is very important as it is used for server for lookups

Magic of Registry: Calling Service using Registry & Feign

Now Let’s Call DiscountService to calculate price. Now as DiscountService is registered with Registry so we don’t need to know the host details.I have already discusses in details about Feign and how to use to call service in this Post. Feign works with eureka-client and we can call service using application.name. In below code snippet DiscountServiceClient is a FeignClient Which is calling service using registered application name.

Pricing Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class PricingServiceApplication {
public static void main(String[] args) {
SpringApplication.run(PricingServiceApplication.class, args);
}
}
@RestController
@Slf4j
class ServiceInstanceRestController {
@Autowired
private DiscountServiceClient discountServiceClient;
@GetMapping("/price/{product}")
public Int getPriceForProduct(@PathVariable("product") String product) {
int discount = discountServiceClient.getDiscountPercentage("Test");
int price = 100;
s = 100-discount
log.info("Discount is {}",discount);
return (s*price)/100;
}
}

@FeignClient("discount-service")
interface DiscountServiceClient {
@RequestMapping(method = RequestMethod.GET, value = "/discount/{product}")
int getDiscountPercentage(@PathVariable("product") String product);
}
Pricing Service properties
1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
application:
name: pricing-service

server:
port: 8080

eureka:
client:
healthcheck:
enabled: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/

What about Load Balancing ?

I have already discusses how to use and setup Ribbon for Client-side load balancing in this Post. Feign already uses Ribbon, so, if you use @FeignClient Ribbon will be used along with that. When Eureka is used in conjunction with Ribbon (that is, both are on the classpath), the ribbonServerList is overridden with an extension of DiscoveryEnabledNIWSServerList, which populates the list of servers from Eureka
if we start multiple instances of discount service then Ribbon will discount service in round robin algorithm.

Final Architecture of Application will be like this

The code for this post is available for download here.

Share Comments

Spring Cloud Netflix Ribbon

What is Ribbon?

Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients. Ribbon is aware of multiple instances of a service and chooses a particular instance of it. One advantage of this is client controls the load balancing algorithm.

Ribbon Works with Feign That we discussed in last post.

The Code for this post is available for download here.

Demo Application

Our Simple HelloService Returns String Hello Message From Server: {port_number}. We will launch multiple instance of HelloService. Our Ribbon Client will call HelloService in Round-Robin way.

HelloService
1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping("/helloworld")
public class HelloController {
@Autowired
private Environment environment;
@GetMapping
public String getMessage() {
String port = environment.getProperty("local.server.port");
return "Hello Message From Server " + port;
}
}

Getting Started With Spring Cloud Netflix Ribbon

To include Ribbon in project We need to use artifact id spring-cloud-starter-netflix-ribbon

netflix-ribbon Maven
1
2
3
4
     <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

Update FeignClient to use Ribbon Load Balancing

FeignClient With Ribbon
1
2
3
4
5
6
7
@FeignClient(name = "hello", configuration = HelloClientConfig.class)
@RibbonClient(name = "hello")
public interface HelloClient {

@RequestMapping(method = RequestMethod.GET, value = "/helloworld")
String getMessage();
}

A central concept in Ribbon is that of the named client. We Provide the application name in
application.properties.
application-name.ribbon.listOfServers Property is used to provide list of Servers.

application.properties
1
2
spring.application.name=hello
hello.ribbon.listOfServers: localhost:8080,localhost:8081

Test Application

  1. Start HelloService on port 8080 and 8081.
  2. Start Client Application on 8083.
    Then Go to http://localhost:8083/ribbon-test-client/
    first time it should return message as “Hello Message From Server 8080” and next time as “Hello Message From Server 8081”

The Code for this post is available for download here.

Share Comments

Simplifying Microservices Communication Using Spring Cloud OpenFeign

unsplash-logoPavan Trikutam

What is Feign?

Feign is a Java to HTTP client binder. Feign Simpliffyes the HTTP API Clients using declarative way.
Feign is a library for creating REST API clients in a declarative way. it makes writing web service clients easier. Developers can use declarative annotations to call rest servicese instead of writing repetitive boilerplate code.

Spring Cloud OpenFeign provides OpenFeign integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms

The code for this post is available for download here.

Demo Application
We Have Simple User Service that provides crud operations for Users. We will write client to call these rest endpoints.

Why Feign?

Without Feign, In Spring Boot Applications We will be using RestTemplate to call User service. We need to write code somewhat similar to below.

HTTP Client Using RestTemplate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@GetMapping
public List<User> getAllUsers() {
System.out.println("Calling User Service using Feign Client!!");
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<List<User>> response = restTemplate.exchange(
"http://localhost:8080/user/",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<User>>() {
});
List<User> users = response.getBody();
return users;
}
@GetMapping("{id}")
public User getUserById(@PathVariable("id") int id) {
Map<String, String> uriParams = new HashMap<String, String>();
uriParams.put("id", String.valueOf(id));
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:8080/user/{id}")
.buildAndExpand(uriParams)
.toUri();
System.out.println(uri);
RestTemplate restTemplate = new RestTemplate();
User forEntity = restTemplate.getForObject(uri, User.class);
return forEntity;
}
@PostMapping
public ResponseEntity addUser(@RequestBody User user) {
System.out.println("Add user");
System.out.println(user.toString());
RestTemplate restTemplate = new RestTemplate();
HttpEntity<User> request = new HttpEntity<>(user);
ResponseEntity exchange = restTemplate
.exchange("http://localhost:8080/user/", HttpMethod.POST, request, String.class);
return exchange;
}
@DeleteMapping("{id}")
public ResponseEntity deleteUser(@PathVariable int id) {
System.out.println("delete user");
Map<String, String> params = new HashMap<String, String>();
params.put("id", String.valueOf(id));
RestTemplate restTemplate = new RestTemplate();
restTemplate.delete("http://localhost:8080/user/{id}", params);
return new ResponseEntity("User Deleted successfully", HttpStatus.OK);
}

From above code we can easily figure out that most of the code is repetitive and has nothing to do the business logic. Let’s Simplify Above code using Spring Cloud OpenFeign.

Getting Started With Spring Cloud OpenFeign

To include Feign in project We need to use artifact id spring-cloud-starter-openfeign

openfeign Maven
1
2
3
4
   <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

EnableFeignClients

EnableFeignClients
1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableFeignClients
public class SpringCloudFeignClientDemoApplication {

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

Creating Feign Client With Sensible Defaults.

FeignClient
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

@FeignClient(name = "User", url = "http://localhost:8080")
public interface UserClient {

@RequestMapping(method = RequestMethod.GET, value = "/user")
List<User> getAllUsers();

@RequestMapping(method = RequestMethod.GET, value = "/user/{id}")
User getUser(@PathVariable("id") int id);

@RequestMapping(method = RequestMethod.DELETE, value = "/user/{id}")
ResponseEntity deleteUser(@PathVariable("id") int id);

@RequestMapping(method = RequestMethod.POST, value = "/user/")
ResponseEntity addUser(@RequestBody User user);

@RequestMapping(method = RequestMethod.PUT, value = "/user/")
ResponseEntity updateUser(User user);}

The above code is self explanatory. At minimal we just have to specify name = "User" is an arbitrary client name and url.
If we compare our UserClient with the code that we have written using RestTemplate, It’s visible that without writing any code specific to calling HTTP Service our UserClient supports all operations. Feign is doing magic under the hood.

Overriding Feign default properties

We Can provide custom configuration class to Overriding default properties.
e.g. let say we want to Log the headers, body, and metadata for both requests and responses.
lets Create UserClientConfig class to setup log level.

UserClientConfig
1
2
3
4
5
6
7
8
@Configuration
public class UserClientConfig {

@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.HEADERS;
}
}

Update UserClient To use this config class.

Updated FeignClient
1
2
3
4
5

@FeignClient(name = "User", url = "http://localhost:8080",configuration=UserClientConfig.class)
public interface UserClient {

}

Note : Feign gets integrated with Hystrix, Ribbon and Eureka quite easily. I will cover that in separate Post.

The code for this post is available for download here.

Share Comments

Update Config Dynamically Using Spring Cloud Bus and Spring Cloud Config

In The previous post Centralize Configurations Using Spring Cloud Config We Setup config Server and Centralized The Configuration properties in github.
In This post, We Will Check different options available to update Config properties in Client applications without restarting Them.
The code for this post is available for download here.

1 Restart Endpoint Of Spring Actuator Endpoint

The Simplest way to reload the application config without manually restarting it is Using Spring Boot Actuator Restart Endpoint.But this is not the best way to update the config.

2 Refresh Endpoint- @RefreshScope & @ConfigurationProperties

Spring allows beans to be refreshed dynamically at runtime using @RefreshScope. The Bean That are using @Value to read properties are need to be annotated with @RefreshScope. The Properties loaded by @ConfigurationProperties are automatically reloaded as @ConfigurationProperties are by default @RefreshScope
To Reload the bean annotated with @RefreshScope Run below post request.
$ curl localhost:8090/actuator/refresh -d {} -H "Content-Type: application/json"
Problem

But Again This method has Problem, In a real microservice environment, there will be a large number of independent application services. And It is not practical for the user to manually trigger the refresh event for all the related services whenever a property is changed.Spring Bus Provide Solution For this

3 Spring Cloud Bus

Spring Cloud Bus links nodes of a distributed system with a lightweight message broker. This can then be used to broadcast state changes, The configuration changes are publised as events to all connected nodes. For the POC We will be using AMQP broker as the transport.

Our targeted architecture will look like below

Lets update the System we build in The previous post Centralize Configurations Using Spring Cloud Config

Setup RabbitMQ with Docker

We will run RabbitMQ as a docker image. Install Docker Then Run below command to install rabbitmq
docker pull rabbitmq:3-management

To Run RabbitMQ use below command
docker run -d --hostname my-rabbit --name some-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management
Verify MQ is running. http://192.168.99.100:15672/#/ (Check What IP was generated for You). Default User name and password is guest/guest.

Add Cloud-bus to All Clients
1
2
3
4
      <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

Connect to MQ Bus

Add Properties related to MQ
1
2
3
4
spring.rabbitmq.host=192.168.99.100
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

Let’s Test Our Changes

  1. Start Config Server
  2. Start Multiple Instances of Customer Service.Start Customer Service on 8090,8091,8092
  3. Verify Config Properties http://localhost:8888/customer-service/dev
  4. Dev is configured as customer having gmail account as Premium Account.
  5. Check for http://localhost:8090/customer/niraj.sonawane@gmail.com We should get response as Premium account. Similar for 8091,8092
  6. Update Config properties in Github and push Changes
  7. Verify Updated Config Properties http://localhost:8888/customer-service/dev
  8. Now Check for http://localhost:8090/customer/niraj.sonawane@gmail.com We should get response as Premium account. Similar for 8091,8092. (As we have not yet refreshed anything)
  9. Send Bus Refresh request on 8090, http://localhost:8090/actuator/bus-refresh
  10. Now Check for http://localhost:8090/customer/niraj.sonawane@gmail.com We should get response as Free Account
  11. Also Changes will be reflected for other services,Now Check for http://localhost:8091/customer/niraj.sonawane@gmail.com & http://localhost:8092/customer/niraj.sonawane@gmail.com

  12. We have Send Bus refresh event only for 8090 But Still Changes are reflected on all nodes. Bus Will take responsibility of Sending refresh event

4 Github Webhook

Github Providers notification Event when changes are made in repository through a webhook. Github uses a POST to the webhook with a JSON body containing a list of commits and a header set to push.
Tu use Webhooks We Need to add spring-cloud-config-monitor dependency And Activate /monitor endpoint

To Add Webhooks Go to Your ConfigRepo->Settings-Webhooks. Note, You need Public Domain name for this to work.

The code for this post is available for download here.

Reference.

Share Comments

Centralize Configurations Using Spring Cloud Config

unsplash-logoAsh Edmonds

In This post we will Create Small POC for Spring Cloud Config and will use different Key Features of Spring Cloud Config.

The code for this post is available for download here. And Config files here

Problem
In a typical Microservices Architecture, We have Number of Small,Independent Services working together. Each service will have it’s own configurations in property files & we have multiple instances of each service. Now if we think about different environments like Development, Staging, and Prod etc It makes things more complicated.

Each time if we need to change the configuration of a microservice, we need go to the corresponding project, change its configuration and then restart the application for the change to take effect.
Managing These Configurations for multiple services across different environments becomes very critical
Spring Cloud Config provide Solutions to all these problems

What Is Spring Cloud Config ?

Spring Cloud Config provides server-side and client-side support for Externalized Configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments. The default implementation of the server storage backend uses git, so it easily supports labelled versions of configuration environments as well as being accessible to a wide range of tooling for managing the content. It is easy to add alternative implementations and plug them in with Spring configuration.

POC Application

Assume we have Small Application which includes two microservices Customer & Account. Customer service accepts email id of user and identifies if the user is Premium user or not. If the email id contains some keyword, then we will consider that customer as Premium Customer. At present we have defined the keyword in application.properties
e.g
For DEV - customer-service.emailtype=gmail.com
For QA - customer-service.emailtype=github.com
For DEV - customer-service.emailtype=microsoft.com

If I run http://localhost:8080/customer/niraj.sonawane@gmail.com in dev environment, I should get response as Premium Account.

Before Cloud Config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

// Simple RestController which check if email has keyword or not.
@RestController
public class CustomerProfileController {
@Autowired
private CustomerServiceConfigurations customerServiceConfigurations;

@GetMapping("/customer/{email}")
public String getCustomerDetails(@PathVariable String email) {
if (email.contains(customerServiceConfigurations.getEmailtype())) {
return "Premium Account";
}
return "Free Account";
}
}
//Configuration Class For loading configs from application.properties.
//All properties starting with customer-service will be loaded in this class.
@Component
@ConfigurationProperties("customer-service")
@Data
public class CustomerServiceConfigurations {

private String emailtype;

}

//application.properties has below properties.
customer-service.emailtype=gmail.com

Before Cloud Config

Our Task is to externalize the Configurations in Git repository using Spring Cloud

After Cloud Config

Infographic vector created by freepik - www.freepik.com

1 Spring Cloud Config Server

Config Server acts as a sort of intermediary between our Spring applications and a typically version-controlled repository of configuration files. All microservices access properties files using config server.
We need to add <artifactId>spring-cloud-config-server</artifactId> dependency. And need to use @EnableConfigServer annotation.

Config Server
1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableConfigServer
public class SpringCloudConfigServerApplication {

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

Connecting Config Server to version-controlled repository

Spring Cloud Config Server supports Git,SVN, JDBC (relational database) etc as a backend for configuration properties.The default implementation of EnvironmentRepository uses a Git backend. For this POC will use git.We can also use File System based backend for configuration properties using native profile.

Config Server application.properties
1
2
3
spring.application.name=spring-cloud-config-server
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/nirajsonawane/Spring-Cloud-Config-Server-Demo-Config-Repo.git

To Provide path of file based git system file://${user.home}/config-repo.

Run the config server and you should be able to access properties of customer-service at http://localhost:8888/customer-service/default
Similarly Account service properties will be available at http://localhost:8888/account-service/default

2 Config Client Configurations

To access Configurations through Config Server,Client applications needs to add
spring-cloud-starter-config dependency.
The properties to configure the Config Client must necessarily be read in before the rest of the application’s configuration is read from the Config Server, during the bootstrap phase.

Configure the Config server related properties in bootstrap.properties

bootstrap.properties
1
2
spring.application.name=customer-service
spring.cloud.config.uri=http://localhost:8888/

Now If we Start our Customer service, It will start accessing properties files from our config server. In logs we can verity this.
[ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888/
And If we run http://localhost:8080/customer/niraj.sonawane@gmail.com I should get response as Premium Account. (Will access Default Profile)

3 Managing Profiles, Environment Specific properties

To manage environment specific properties, i.e. profiles add profile specific properties in repository. For customer-service we will add below files in our git repository.

  • customer-service-dev.properties
  • customer-service-prod.properties
  • customer-service-qa.properties

To set any specific profile we can use spring.profiles.active parameter in bootstrap.properties or using command line argument -Dspring-boot.run.profiles

Precedence rules for profiles are also the same as in a regular Spring Boot application: Active profiles take precedence over defaults, and, if there are multiple profiles, the last one wins (similar to adding entries to a Map).

4 RefreshScope

Until Now, We have Solved the problem of Centralizing the properties and managing different profiles. Now We will focus on how to update properties at run time without application Restart.

By default, the configuration values are read on the client’s startup, and not again. We can force a bean to refresh its configuration - to pull updated values from the Config Server - by annotating the Classes that are refering with the Spring Cloud Config @RefreshScope and then by triggering a refresh event.
There are other options are also available, I will cover them in next Post.

5 Useful Properties and Other Key Features

  • if client cannot connect to the Config Serve then use spring.cloud.config.fail-fast=true for fail fast.
  • Config Client can Retry if it is not able to connect, use spring.cloud.config.retry.*
  • SSL certificate can be disabled by setting the git.skipSslValidation property to true (default is false).
  • Timeout Can be set on Config server to read properties from git, Use git.timeout property
  • Multiple Repositories can be used by using Pattern Matching
  • Config Server supports the Encryption and Decryption of properties using JCE
  • Config Server supports DiscoveryClient implementation such as Eureka Service Discovery Config Server register with the Discovery Service.

The code for this post is available for download here. And Config files here

Share Comments

Write Clean Asynchronous Code With CompletableFuture Java-8

unsplash-logorawpixel

Java 8 has introduced a lot of features. With Addition of CompletableFuture . Writing Clean & Readable Asynchronous code has become much more easier. CompletableFuture has more than 50 methods which makes it very useful.

The code for this post is available for download here.

CompletableFuture

CompletableFuture is an implementation of the Future & CompletionStage interface but with a lot of modern Features. It Supports lambdas and takes advantage of non-blocking methods via callbacks and promotes asynchronous reactive programming model. CompletableFuture allows us to write non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its Progress, Completion or Failure. CompletableFuture is inspired from ListenableFuture in Guava and Are similar to Promise in java scripts.

Why CompletableFuture instead Of Future?

Callable and Future were introduced in Java 5. Future is placeholders for a result that hasn’t happened yet.Future Can use a Runnable or Callable instance to complete the submitted task. There are two methods to get actual value from Future.
get() : When this method is called, thread will wait for result indefinitely.
get(long timeout, TimeUnit unit): When this method is called, thread will wait for result only for specified time.

There are multiple problems with Future
Blocking - The get method is blocking and need to wait until the computation is done. Future does not have any method that can notify on completion and does not have capability to attach a callback function.
Chaining & Composition - Many times we want to chain multiple future to complete long computation. You need to merger results and send results to another task. It’s Hard to implement such chaining with future.
Exception Handling - Future does not provide any construct for Exception Handling.
All these issues are addressed by CompletableFuture.
lets try different methods provided by CompletableFuture

Create Simple Completeable Future


The simplest way is to create CompleteableFuture is CompleteableFuture.completedFuture method which returns an a new, finished CompleteableFuture. Creating already Completed CompleteableFuture becomes very useful in many cases.
Create Completed CompleteableFuture
1
2
3
4
5
6
7
8
9
10
@Test
void simpleComletedCompletableFuture() {
CompletableFuture<String> completableFuture = CompletableFuture.completedFuture("Some Value");
assertTrue(completableFuture.isDone());
try {
assertEquals("Some Value", completableFuture.get());
} catch (ExecutionException | InterruptedException e) {
fail("No Exception expected");
}
}

Note : if we call get method on incomplete CompleteableFuture , the get call will block forever because the Future is never completed.We can use CompletableFuture.complete() method to manually complete a Future.

Simple Asynchronous computation using runAsync

If We want to run some task in background that does not returns any value, then we can use CompletableFuture.runAsync() it takes a Runnable and returns CompletableFuture<Void>

Simple Asynchronous computation using runAsync
1
2
3
4
5
6
7
8
9
10
11
   
public void process() {
System.out.println(Thread.currentThread() + " Process");
someStateVaribale.set(100);
}
@Test
void completableFutureRunAsync() {
CompletableFuture<Void> runAsync = CompletableFuture.runAsync(() -> process());
runAsync.join();
assertEquals(100, someStateVaribale.get());
}

Simple Asynchronous computation using SupplyAsync

If we want to run some task in background that Returns Some Value, then we can use CompletableFuture.supplyAsync() it takes a Supplier<T> and returns completableFuture<T>

Simple Asynchronous computation using supplyAsync
1
2
3
4
5
6
7
8
9
@Test
void completableFutureSupplyAsync() {
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(this::processSomeData);
try {
assertEquals("Some Value", supplyAsync.get()); //Blocking
} catch (ExecutionException | InterruptedException e) {
fail("No Exception expected");
}
}

CompletableFuture with Custom Executor

You might be wondering, Which Thread is executing the supplyAsync & runAsync task and Who is creating these Threads? Similar to parallel streams CompletableFuture executes these tasks in a thread obtained from the global ForkJoinPool.commonPool().
We Can always provide our custom Executor to CompletableFuture.All the methods in the CompletableFuture API has two variants, With or Without Executor.

CompletableFuture with Custom Executor
1
2
3
4
5
6
7
8
9
10
  @Test
void completableFutureSupplyAsyncWithExecuto() {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
CompletableFuture<String> supplyAsync = CompletableFuture.supplyAsync(this::processSomeData,newFixedThreadPool);
try {
assertEquals("Some Value", supplyAsync.get());
} catch (ExecutionException | InterruptedException e) {
fail("No Exception expected");
}
}

CompletableFuture Callbacks and Chaining

We know that CompletableFuture.get() is blocking and we want to avoid this. We should get some notification after Future completes.
CompletableFuture provides thenApply(), thenAccept() and thenRun() to attach callbacks

thenAccept()
If We want to run some code after receiving some value from Future then we can use thenAccept()
thenApply()
If We want to run some code after receiving value from Future and then want to return some value for this we can use thenAccept()
thenRun()
If We want to run some code after completion of the Future and dont want to return any value for this we can use thenRun()

CompletableFuture thenAccept thenApply thenRun
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  
@Test
public void completableFutureThenAccept() {
CompletableFuture.supplyAsync(this::process)
.thenAccept(this::notify) //Non Blocking,notify method will be called automatically after compilation or process method
.join();
assertEquals(100,someStateVaribale.get());
}

@Test
public void completableFutureThenApply() {
Integer notificationId = CompletableFuture.supplyAsync(this::process)
.thenApply(this::notify)//Non Blocking will return some value
.join();
assertEquals(new Integer(1),notificationId);
}

@Test
public void completableFutureThenApply() {
CompletableFuture.supplyAsync(this::process)
.thenRun(this::notifyMe)
.join();
assertEquals(100,someStateVaribale.get());
}

Chaining Callbacks
If We have large Asynchronous computation, Then we can continue passing values from one callback to another.

Chaining Callbacks
1
2
3
4
5
6
7
8
  
@Test
public void completableFutureThenApplyAccept() {
CompletableFuture.supplyAsync(this::findAccountNumber)
.thenApply(this::calculateBalance)
.thenApply(this::notifyBalance)
.thenAccept((i)->notifyByEmail()).join();
}

async variants of thenApply(),thenAccept() and thenRun()
Note In all the previus examples, All methods are executed on Same threads. But If we want them to be run on separate thread then we can use async variants of these methods.
Async Variants
1
2
3
4
5
6
7
8
9
10
11
12
13
  
@Test
public void completableFutureApplyAsync() {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);
ScheduledExecutorService newSingleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
CompletableFuture<Integer> completableFuture =
CompletableFuture
.supplyAsync(this::findAccountNumber,newFixedThreadPool)//will run on thread obtain from newFixedThreadPool
.thenApplyAsync(this::calculateBalance,newSingleThreadScheduledExecutor) //will run on thread obtain from newSingleThreadScheduledExecutor
.thenApplyAsync(this::notifyBalance);//will run on thread obtain from common pool
Integer balance = completableFuture.join();
assertEquals(Integer.valueOf(balance), Integer.valueOf(100));
}

CompletableFuture thenCompose and thenCombine

thenCompose
Let’s Say we want to first find Account Number and then calculate Balance for that account and after calculations we want to send notifications.
Now All these task are Dependent and methods are returning CompletableFuture , Then We need to use thenCompose Method.
This is similar to flatMap in case of Streams.

thenCompose
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
public CompletableFuture<Integer> findAccountNumber() {
sleep(1);
System.out.println(Thread.currentThread() + " findAccountNumber");
return CompletableFuture.completedFuture(10);
}
public CompletableFuture<Integer> calculateBalance(int accountNumber) {
System.out.println(Thread.currentThread() + " calculateBalance");
sleep(1);
return CompletableFuture.completedFuture(accountNumber * accountNumber);
}
public CompletableFuture<Integer> notifyBalance(Integer balance) {
System.out.println(Thread.currentThread() + "Sending Notification");
sleep(1);
return CompletableFuture.completedFuture(balance);

}
private void sleep(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void completableFutureThenCompose()
{
Integer join = findAccountNumber()
.thenComposeAsync(this::calculateBalance)
.thenCompose(this::notifyBalance)
.join();
assertEquals(new Integer(100), join);
}

thenCombine
As name suggest thenCombine is used to merge results of two independent CompletableFuture. Assume that for a person we get first name and last name by calling two different independent methods. To get the Full name we want ot merge results of both the methods then we will use thenCombine.

thenCombine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 
public CompletableFuture<String> findFirstName() {
return CompletableFuture.supplyAsync(() -> {
sleep(1);
return "Niraj";
});
}
public CompletableFuture<String> findLastName() {
return CompletableFuture.supplyAsync(() -> {
sleep(1);
return "Sonawane";
});
}
@Test
public void completableFutureThenCombine() {
CompletableFuture<String> thenCombine =
findFirstName().thenCombine(findLastName(), (firstName, lastname) -> {
return firstName + lastname;});
String fullName = thenCombine.join();
assertEquals("NirajSonawane", fullName);
}

CompletableFuture allOf

In Many scenario we want to run run multiple task in parallel and want to do some processing after all of them are complete.

Assume we want to find firstName of five different users and combine the results.The CompletableFuture.allOf static method allows to wait for completion of all of the Futures.
The allOf method has limitation that it does not return the combined results of all Futures. We you have to manually combine the results from Futures.

allOf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public CompletableFuture<String> findSomeValue() {
return CompletableFuture.supplyAsync(() -> {
sleep(1);
return "Niraj";
});
}
@Test
public void completableFutureAllof() {
List<CompletableFuture<String>> list = new ArrayList<>();
IntStream.range(0, 5).forEach(num -> {
list.add(findSomeValue());
});

CompletableFuture<Void> allfuture = CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()]));//Created All of object
CompletableFuture<List<String>> allFutureList = allfuture.thenApply(val -> {
return list.stream().map(f -> f.join()).collect(Collectors.toList());
});

CompletableFuture<String> futureHavingAllValues = allFutureList.thenApply(fn -> {
System.out.println("I am here");
return fn.stream().collect(Collectors.joining());});
String concatenateString = futureHavingAllValues.join();
assertEquals("NirajNirajNirajNirajNiraj", concatenateString);
}

CompletableFuture Exception Handling

Handing Exceptions in Multithreaded code in Java was always pain. Luckily CompletableFuture has a nice way of handling exceptions.

Exception Handling
1
2
3
4
CompletableFuture<Integer> thenApply = CompletableFuture
.supplyAsync(this::findAccountNumber)
.thenApply(this::calculateBalance)
.thenApply(this::notifyBalance)

In Above Code if findAccountNumber method throws the Exception then callback chain calculateBalance and notifyBalance will not be called. Future will be resolved with the exception occurred.Similarly if calculateBalance throws the Exception then after the callback chain will break.

Handel Exceptions using exceptionally

Exceptionally callback will be called if preceding methods fails with an exception. exceptionally Returns a new CompletableFuture that is completed when this CompletableFuture completes, with the result of the given function of the exception triggering this CompletableFuture’s completion when it completes exceptionally; otherwise, if this CompletableFuture completes normally, then the returned CompletableFuture also completes normally with the same value.

exceptionally
1
2
3
4
5
6
7
8
9
10
11
12
13
14
   @Test
public void completableFutureExceptionally()
{
CompletableFuture<Integer> thenApply = CompletableFuture.supplyAsync(this::findAccountNumber)
.thenApply(this::calculateBalance)
.thenApply(this::notifyBalance)
.exceptionally(ex -> {
System.out.println("Got Some Exception "+ex.getMessage());
System.out.println("Returning some default value");
return 0;
});
Integer join = thenApply.join();
assertEquals(new Integer(0), join);
}

Handel Exceptions using Handel Method

Handel method is more flexible than exceptionally method.As we get both exception as well as Result.

Handel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void completableFutureHandel()
{
CompletableFuture<Integer> thenApply = CompletableFuture.supplyAsync(this::findAccountNumber)
.thenApply(this::calculateBalance)
.thenApply(this::notifyBalance)
.handle((ok, ex) -> {
System.out.println("Code That we want to run in finally ");
if (ok != null) {
System.out.println("No Exception !!");
} else {
System.out.println("Got Exception " + ex.getMessage());
return -1;
}
return ok;
});
}

Handel Exceptions using WhenComplete Method

WhenComplete
1
2
3
4
5
6
7
8
@Test
public void completableFutureWhenComplete()
{
CompletableFuture.supplyAsync(this::findAccountNumber)
.thenApply(this::calculateBalance)
.thenApply(this::notifyBalance)
.whenComplete((i,t)->System.out.println("finally action"));
}

TimeOut java 9 Improvement

While Working on Asynchronous Code, We Need to handel timeouts. We Can not wait forever to finish the task. Unfortunately we do not have anything in java 8 for timeouts.
Java 9 has added orTimeout and completeOnTimeout methods to handel this.

If the task does not complete in given time, a TimeoutException will be thrown.

orTimeout
1
2
3
4
5
6
@Test
public void completableFutureWhenComplete()
{
CompletableFuture.supplyAsync(this::findAccountNumber)
.orTimeout(1, TimeUnit.MINUTES);
}

The code for this post is available for download here.

Share Comments

Document Spring Boot RESTful API With Swagger-2.0

unsplash-logoPuttipol Waipanya

Now Days Spring Boot is de facto standard for developing RESTful services.Spring Boot makes it very easy to build RESTful services.In SOAP based web services, you had a WSDL which works as documentation for your API. For Rest Services we do not have WSDL so documentation of API becomes more critical.

Swagger

Swagger 2 is an open source framework used to describe and document RESTful APIs.Swagger Can read your API’s structure and automatically build beautiful and interactive API documentation. Swagger makes documenting your RESTful services easy.
Check Docs for all Features.

The code for this post is available for download here.

Swagger + Spring Boot

Swagger Can easily integrate with Spring Boot. To Integrate Swagger we need to use Swagger UI & SpringFox.

Maven Dependency
1
2
3
4
5
6
7
8
9
10
11
   <dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

Demo Spring Boot RESTful Application

let’s Create documentation for our Demo Spring Boot RESTful Application. Demo Application expose User rest endpoint that allows to perform CURD operations for User.

User Endpoint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   @GetMapping
public List<User> getAllUsers() {
return userservice.getAllUsers();
}
@GetMapping("/{id}")
public User getUser(@PathVariable int id) {
return userservice.getUser(id);
}
@PostMapping
public ResponseEntity addUser(@RequestBody User user) {
userservice.addUser(user);
return new ResponseEntity("User Added successfully", HttpStatus.OK);
}
@PutMapping
public ResponseEntity updateUser(@RequestBody User user) {
userservice.updateUser(user);
return new ResponseEntity("User Updated successfully", HttpStatus.OK);
}
@DeleteMapping("/{id}")
public ResponseEntity deleteUser(@PathVariable int id) {
userservice.deleteUser(id);
return new ResponseEntity("User Deleted successfully", HttpStatus.OK);
}

Swagger Configuration

We need to create a Docket bean in a Spring Boot configuration to configure Swagger 2. A Springfox Docket instance provides the primary API configuration with sensible defaults and convenience methods for configuration.
@EnableSwagger2 Indicates that Swagger support should be enabled. This should be applied to a Spring java config and should have an accompanying @Configuration annotation. Loads all required beans defined in @SpringSwaggerConfig

Below is minimum configuration required for Swagger.

SwaggerConfig
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}

Now If we run the application and go to http://localhost:8080/swagger-ui.html documentation will be rendered by Swagger UI.

Update Swagger Configuration for Customization - ApInfo

ApiInfo
1
2
3
4
5
6
7
8
9
10
11
private ApiInfo apinfo() {
return new ApiInfoBuilder()
.title("User Management API")
.description("Rest API to Perfrom CURD ")
.termsOfServiceUrl("Some Terms of Services URL")
.version("1.0.0")
.license("Some License Info")
.licenseUrl("Some License URL")
.contact(new Contact("Niraj Sonawane", "https://nirajsonawane.github.io/","Niraj.Sonawane@gmail.com"))
.build();
}

Update Swagger Configuration for Customization - Selecting Specific Endpoints

When we verify the Generated documentation, We see that in Basic Error Controller along with our User Controller and Model.
We can Configure Swagger to generated documentation only for certain endpoints using RequestHandlerSelectors.

The Available options for Configurations are

  1. RequestHandlerSelectors.basePackage(basePackage)
  2. RequestHandlerSelectors.withClassAnnotation(annotation)
  3. RequestHandlerSelectors.withMethodAnnotation(annotation)

Swagger Annotations On Controller classes

Swagger provides annotations that can be applied on Controller classes to provide additional information,
We can annotate controllers and their methods and method parameters.

  • @Api describes the whole controller
  • @ApiOperation is used for description on a methods level
  • @ApiResponses is used for description of response codes on a methods level
  • @ApiParam is used for method parameters
Swagger Annotations On Controller classes
1
2
3
4
5
6
7
8
9
10
11
@GetMapping("/{id}")
@ApiOperation(consumes="application/json", produces="application/json",protocols="http", value = "getUser" )
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Successfully retrieved User"),
@ApiResponse(code = 401, message = "The request has not been applied because it lacks valid authentication credentials for the target resource"),
@ApiResponse(code = 403, message = "The server understood the request but refuses to authorize it"),
@ApiResponse(code = 404, message = "The resource not found")
})
public User getUser(@ApiParam("Id of user, Can not be null") @PathVariable int id) {
return userservice.getUser(id);
}

Swagger Annotations On Model classes

Swagger Core annotations, Can be used to specify additional information about your model Class.
e.g User Class annotated with these annotations can look something like this

Swagger Annotations On Model classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

@ApiModel(description = "Class representing User ")
public class User {

@ApiModelProperty(notes = "Unique ID for user", example = "1", required = true, position = 0)
private int id;

@ApiModelProperty(notes = "First Name of User", example = "Niraj", required = true, position = 1)
private String firstName;

@ApiModelProperty(notes = "Last Name of User", example = "Sonawane", required = true, position = 2)
private String lastName;

@ApiModelProperty(notes = "Middle Name of User", example = "Ashok", required = false, position = 3)
private String middleName;

@ApiModelProperty(notes = "Age of User", example = "32", required = true, position = 4)
private Integer age;
}

Swagger Integration with JSR-303 Annotations

JSR 303 Bean Validation is the specification of the Java API for JavaBean validation in Java EE and Java SE. This is very popular mechanism for validation and number of projects are using it.

JSR 303 Bean Validation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
public class User {
private int id;
@NotBlank
@Size(min = 2, max = 10)
private String firstName;

@NotBlank
private String lastName;

private String middleName;
@NotNull
@Min(2)
@Max(100)
private Integer age;
}

This is a common practice which is already widely used. Swagger can be easily configured to read these annotations and generate documentation based on such annotations.
This makes Swagger very useful as we can utilize what we already have in our project without writing all the constraints manually. Consumers also know what are restrictions on the values and what they should provide to API and what values to expect.

Add dependency in pom file
1
2
3
4
5
      <dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.9.2</version>
</dependency>
Import BeanValidatorPluginsConfiguration
1
2
3
4
5
 
@Configuration
@EnableSwagger2
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {

After Integration Model will look like below in generated documentation.

The code for this post is available for download here.

Share Comments

Junit 5 : Write Powerful Unit Test Cases Using Parameterized Tests

unsplash-logoWellington Rodrigues

JUnit 5

Unlike previous versions of JUnit. JUnit 5 is complete rewrite and has lot of interesting architecture changes. JUnit 5 is not Single project but compose from three sub-projects: Jupiter, Vintage, and Platform.

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

Check JUnit 5 User Guide For more details. You can find all code samples in on my GitHub Account

In this post We will discuss Parameterized Tests in JUnit 5.

Parameterized Tests

JUnit 5 Parameterized Test are very powerful. With the help of Parameterized Test We can remove the duplication in test cases.Parameterized test cases can also help us to cleanup the test code and remove the cluter.
As Name suggest Parameterized tests make it possible to run a test multiple times with different arguments. They are declared just like regular @Test methods but use the @ParameterizedTest annotation instead. In addition, you must declare at least one source that will provide the arguments for each invocation and then consume the arguments in the test method.

Simple ParameterizedTest

The following example demonstrates a parameterized test that uses the @ValueSource annotation to specify a String array as the source of arguments. Test Case will be called 2 times with parameter Hello and World.
Framework will be responsible for injecting parameter values

Simple ParameterizedTest
1
2
3
4
5
6
7
   @ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void withSomeValues(String word) {
System.out.println(word);
assertNotNull(word);

}

Display Names For ParameterizedTest

JUnit 5 has added new annotation @DisplayName, Which helps to provide more readable names to test classes and methods.These names will be displayed by test runners and test reporting.

Display Names
1
2
3
4
5
6
7
8
   
@DisplayName("String should not be null")
@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void withSomeValues(String word) {
System.out.println(word);
assertNotNull(word);
}

But in case of Parameterized Test,Sometimes we might need to name test cases based on arguments. JUnit 5 provides index and arguments variable for this

Display Names For ParameterizedTest
1
2
3
4
5
6
7
8
   
@ParameterizedTest(name = "Null Check Test #{index} with [{arguments}]")
@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void withSomeValues(String word) {
System.out.println(word);
assertNotNull(word);
}

Sources of Arguments

Out of the box, JUnit Jupiter provides number of Argument Source providers.

  1. @ValueSource
  2. @EnumSource
  3. @MethodSource
  4. @CsvSource
  5. @CsvFileSource
  6. @ArgumentsSource

Let’s try one by one

@EnumSource

@EnumSource provides a convenient way to use Enum constants. This annotation provides an optional names parameter that lets you specify which constants shall be used. If omitted, all constants will be used like in the following example.

EnumSource
1
2
3
4
5
6
7
    @ParameterizedTest(name = "withSomeName #{index} with Value [{arguments}]")
@EnumSource(MyTestEnum.class)
void withSomeEnum(MyTestEnum myEnum) {
System.out.println(myEnum);
assertNotNull(myEnum);
}

@MethodSource

@MethodSource allows you to refer one or more factory methods of the test class or external classes.

Method Source in Same class

  • Factory methods within the test class must be static.
  • Each factory method must generate a stream of arguments.
MethodSource in Same Class
1
2
3
4
5
6
7
8
9
10
11
   @ParameterizedTest
@MethodSource("createWordsWithLength")
void withMethodSource(String word, int length) {
System.out.println("withMethodSource");
assertNotNull(word);
}

private static Stream<Arguments> createWordsWithLength() {
return Stream.of(Arguments.of("Hello", 5), Arguments.of("JUnit 5", 7));
}

Method Source in Other class

  • Factory methods in external classes must be static.
  • Each factory method must generate a stream of arguments.
  • Factory methods must not accept any arguments.
    MethodSource in external classes
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       @ParameterizedTest
    @MethodSource("com.niraj.MethodSource#stringProvider")
    void withMethodSource(String word, int length) {
    System.out.println("withMethodSource");
    assertNotNull(word);
    }

    private static Stream<Arguments> createWordsWithLength() {
    return Stream.of(Arguments.of("Hello", 5), Arguments.of("JUnit 5", 7));
    }
    @CsvSource
    As name suggest @CsvSource allows you to express argument lists as comma-separated values (i.e., String literals).
    CsvSource
    1
    2
    3
    4
    5
       @ParameterizedTest
    @CsvSource({ "Hello, 5", "World, 5", "test,4" })
    void withCsvSource(String word, int length) {
    assertEquals(word.length(), length);
    }
    @CsvFileSource
    Similar to @CsvSource We can also provide csv values using file from classpath @CsvFileSource.
    CsvFileSource
    1
    2
    3
    4
    5
       @ParameterizedTest
    @CsvFileSource(resources = "/testdata.csv")
    void withCsvFileSource(String word, int length) {
    assertEquals(word.length(), length);
    }

ArgumentsSource

If any of the above Source provider does not meet your requirement, then you can use your custom Argument Source provider. You will need to implement ArgumentsProvider Interface.

ArgumentsSource
1
2
3
4
5
6
    @ParameterizedTest
@ArgumentsSource(StringArgumentsProvider.class)
void testWithArgumentsSource(String argument) {
assertNotNull(argument);
}
}
StringArgumentsProvider
1
2
3
4
5
6
public class StringArgumentsProvider implements ArgumentsProvider {

@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of("Hello", "world").map(Arguments::of);
}

Argument Conversion

In all above Test cases you might have observed that the arguments are getting converted to method parameter types. In all examples, arguments are getting converted to String.

Who is converting these arguments ?

What if we change it to int or any other types?

What happens if incase we want to use any User Defined object ?

Widening Conversion
JUnit Jupiter supports Widening Primitive Conversion for arguments supplied to a @ParameterizedTest. For example, a parameterized test annotated with @ValueSource(ints = { 1, 2, 3 }) can be declared to accept not only an argument of type int but also an argument of type long, float, or double.

Explicit Conversion
We Can Specify ArgumentConverter to convert to any user define object. In below example i wanted to convert String “niraj,sonawane” to Person object.

ArgumentConverter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    @ParameterizedTest
@ValueSource(strings ={"niraj,sonawane"})
void withCustomConverter(@ConvertWith(PersonConverter.class) Person person) {
assertEquals(Person.getFirstName(),"niraj");
assertEquals(Person.getLastName(),"sonawane");
}
//Convert class
public class PersonConverter implements ArgumentConverter {
@Override
public Object convert(Object source, ParameterContext context) throws ArgumentConversionException {
if (source instanceof String)
try {
String[] split = ((String) source).split(",");
return new Emp(split[0], split[1]);

} catch (NumberFormatException ex) {
String message = source + " is no correct string representation of a Emp.";
throw new ArgumentConversionException(message, ex);
}
throw new ArgumentConversionException(source + " is no valid point");
}
}
Share Comments

Applying Reactive Programing to Existing Batch Application- RXJava2

Today, I will share my experience of migrating our existing Spring batch application to reactive programing model using ReactiveX extensions.
The goal is to explain, What is ReactiveX-RXjava2 and demonstrating Working example.

What is ReactiveX And RxJava

ReactiveX Is An API for asynchronous programming with observable streams.ReactiveX is a combination of the best ideas from the Observer pattern, the Iterator pattern, and functional programming.

RxJava is the Java implementation of ReactiveX. RxJava provides Java API for asynchronous programming with observable streams.
ReactiveX

Why Reactive programming?

For Me Reactive Programming is a development model focus around Asynchronous Data Streams.
In Reactive programming, Data streams are the center part of your application. Mesages ,Events, Errors ,Event chains ,call etc are going to be conveyed As a data stream.
Reactive programming provides a simple way of asynchronous programming.

Migration Story

Read More

Share Comments