Wednesday 19 September 2018

Sequential V/S Parallel Stream


Sequential Stream and Parallel Stream in Java

We should remember to create parallel stream by calling parallelStream() method on the collection. If we are not calling parallelStream method then by default the sequential stream gets returned by stream() method.



Question: - When we can use Parallel Stream?

A parallel stream has a much higher overhead compared to a sequential one. I would like to use sequential streams by default and only consider parallel stream when :
  • If there is huge amount of items to process 
  • If I have a performance issue with the sequential stream.
  • They should be used when the output of the operation is not needed to be dependent on the order of elements present in source collection

Parallel Stream has equal performance impacts. Since each sub-stream is a single thread running and acting on the data, it has overhead compared to the sequential 
stream.

Customer.java

package com.gaurav.stream;
/**
 * @author Kumar Gaurav 
 * 
 */
public class Customer {
private int custId;
private double shoppingExpenditure;
public Customer(int custId, double shoppingExpenditure) {
super();
this.custId = custId;
this.shoppingExpenditure = shoppingExpenditure;
}
public int getCustId() {
return custId;
}
public void setCustId(int custId) {
this.custId = custId;
}
public double getShoppingExpenditure() {
return shoppingExpenditure;
}
public void setShoppingExpenditure(double shoppingExpenditure) {
this.shoppingExpenditure = shoppingExpenditure;
}
}

SequentialParallelStreamExample.java

package com.gaurav.stream;

/** In this example we have created a list of 500 employees out of which there are 400 customers whose shopping expenditure is more than 35000. 
 * We will see the time taken to process this filtered list by sequential and parallel stream.*/

import java.util.ArrayList;
import java.util.List;
/**
 * @author Kumar Gaurav 
 * 
 */
public class SequentialParallelStreamExample {

public static void main(String[] args) {

List<Customer> customerList = new ArrayList<Customer>();
for (int i = 0; i < 50; i++) {
customerList.add(new Customer(101, 345000));
customerList.add(new Customer(110, 90000));
customerList.add(new Customer(201, 35002));
customerList.add(new Customer(1011, 6987));
customerList.add(new Customer(301, 45001));
customerList.add(new Customer(1035, 32000));
customerList.add(new Customer(11010, 37000));
customerList.add(new Customer(1990, 85000));
customerList.add(new Customer(1999, 89235));
customerList.add(new Customer(11310, 49000));
}

/***** Creation of a Sequential Stream *****/
long startTime = System.currentTimeMillis();
System.out.println("Count of filtered List of Customer elements by Sequential Stream = "
+ customerList.stream().filter(e -> e.getShoppingExpenditure() > 35000).count());

long endTime = System.currentTimeMillis();
System.out.println("Time Taken by Sequential Stream = " + (endTime - startTime)+"ms" + "\n");

/***** Creation of a Parallel Stream *****/
startTime = System.currentTimeMillis();
System.out.println("Count of filtered List of Customer elements by Parallel Stream  = "
+ customerList.parallelStream().filter(e -> e.getShoppingExpenditure() > 35000).count());

endTime = System.currentTimeMillis();
System.out.println("Time Taken by Parallel Stream = " + (endTime - startTime)+"ms");
}
}

Output

Count of filtered List of Customer elements by Sequential Stream = 400
Time Taken by Sequential Stream = 38ms

Count of filtered List of Customer elements by Parallel Stream  = 400
Time Taken by Parallel Stream = 3ms

Java Stream API Example

Stream API examples

A stream represents a sequence of elements and supports different kind of operations to perform computations upon those elements. There are two types of stream operations:
  • Intermediate
  • Terminal
Intermediate operations return a stream so we can chain multiple intermediate operations without using semicolons. Terminal operations are either void or return a non-stream result.
We will see intermediate operations like filter, map, sorted where as forEach is a terminal operation.

List<String> list =
    Arrays.asList("GAURAV", "ADITYA", "RISHAB", "MIHIKA", "RAJEEV");
list
    .stream()
    .filter(s -> s.startsWith("G"))
    .map(String::toUpperCase)
    .sorted()
.forEach(System.out::println);

Such a chain of stream operations as seen in the above example is referred as operation pipeline. 

A function is non-interfering when it does not modify the underlying data source of the stream. 
We can see in the above example no lambda expression appears to modify list elements by adding or removing methods from the collection.

A function is stateless when the execution of the operation is deterministic, e.g. in the above example no lambda expression depends on any mutable variables which might change the execution state.

StreamAPIMethodUsesExample.java

package com.gaurav.stream;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamAPIMethodUsesExample {

public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100; i++) {
list.add(i);
}

/**
* Stream filter() example: We can use filter() method to check stream
* elements following a condition and generate a filtered list.
*
*/

Stream<Integer> sequentialStream = list.stream();
Stream<Integer> filteredNums = sequentialStream.filter(x -> x > 85);
System.out.println("Count of filtered Numbers greater then 85 is :");
filteredNums.forEach(x -> System.out.print(x + " "));

/**
* Stream sorted() example: We can use sorted() method to sort in
* natural as well as reverse order the stream elements by passing
* Comparator argument.
*/

Stream<String> namesStream1 = Stream.of("gaurav", "aditya", "rishab", "mihika", "987654");
List<String> reversedNames = namesStream1.sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println("\nStream sorting example in reverese order: " + reversedNames);

Stream<String> namesStream2 = Stream.of("GAURAV", "ADITYA", "RISHAB", "MIHIKA", "123456");
List<String> naturalSorting = namesStream2.sorted().collect(Collectors.toList());
System.out.println("Stream sorting example in natural order: " + naturalSorting);

/**
* Stream map() example: We can use map() to apply functions to an
* stream. I have used it to apply lower case function to a list of
* names.
*/
Stream<String> names = Stream.of("gauRAV", "Aditya", "riSHAB", "MIhika");
System.out.print("Map elements extracted in Lowercase by Stream is:" + names.map(s -> {
return s.toLowerCase();
}).collect(Collectors.toList()));

list = new ArrayList<Integer>();
for (int i = 100; i < 120; i++) {
list.add(i);
}
/**
* Stream count() example: We can use this to count
* the number of items available in the stream.
*/
Stream<Integer> numbersStream1 = list.stream();
System.out.println("\nTotal number of elements in the list stream is = " + numbersStream1.count());
/**
* Stream forEach() example: This can be used for iterating over the
* collections or stream. In place of iterator, We can use this. 
*/
Stream<Integer> numbersStream2 = list.stream();
System.out.println("\nNumbers available in the list stream are = ");
numbersStream2.forEach(x -> System.out.print(x+",")); 
/** Stream match() examples: We can use anyMatch(), allMatch() and noneMatch() methods of stream */
Stream<Integer> numbersStream3 = list.stream();
System.out.println("\nIs the given stream contains 119 ? "+numbersStream3.anyMatch(i -> i==119));
//Output:Is the given stream contains 119 ? true
Stream<Integer> numbersStream4 = list.stream();
System.out.println("Is the given stream contains all elements less than 120 ? "+numbersStream4.allMatch(i -> i<120));
//Output:Is the given stream contains all elements less than 120 ? true
Stream<Integer> numbersStream5 = Stream.of(1,2,3,4,5);
System.out.println("Is the given stream doesn't contain 120? "+numbersStream5.noneMatch(i -> i == 120));
//Output:Is the given stream doesn't contain 120? true
/**
* Stream reduce() method: We can use reduce() to perform a reduction
* on the elements of the stream and return an Optional.
*/
Stream<Integer> numbersStream6 = Stream.of(1,2,3,4,5,6);
Optional<Integer> intOptional = numbersStream6.reduce((i,j) -> {return i*j;});
if(intOptional.isPresent()){ 
System.out.println("\nMultiplication of the all the available numbers in given stream are = "+intOptional.get());
/**
* Stream findFirst() example: we can use it to find the first character from
* a stream starting with G.
*/
Stream<String> numbersStream7 = Stream.of("ADITYA", "RISHAB", "MIHIKA","GAURAV", "TIA");
Optional<String> nameStartWithG = numbersStream7.filter(i -> i.startsWith("G")).findFirst();
if(nameStartWithG.isPresent()){
System.out.println("Name which is starting with G = "+nameStartWithG.get()); 
}
}

}

Output:

Count of filtered Numbers greater then 85 is :
86 87 88 89 90 91 92 93 94 95 96 97 98 99 

Stream sorting example in reverese order: [rishab, mihika, gaurav, aditya, 987654]

Stream sorting example in natural order: [123456, ADITYA, GAURAV, MIHIKA, RISHAB]

Map elements extracted in Lowercase by Stream is:[gaurav, aditya, rishab, mihika]

Total number of elements in the list stream is = 20

Numbers available in the list stream are = 
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,

Is the given stream contains 119 ? true

Is the given stream contains all elements less than 120 ? true

Is the given stream doesn't contain 120? true

Multiplication of the all the available numbers in given stream are = 720

Name which is starting with G = GAURAV

Friday 7 September 2018

Global Exception Handling in Spring Boot

Spring Boot Exception Handling

Question: Why we should use Exception Handling?
Answer: 


  • We should present error or exception messages in user understandable manner, which will help to resolve these errors easily.
  • We should provide proper response status.
  • We should not present sensitive information in the response.

org.springframework.web.bind.annotation
Annotation Type ControllerAdvice

@Target(value=TYPE)
 @Retention(value=RUNTIME)
 @Documented
 @Component
public @interface ControllerAdvice

Specialization of @Component for classes that declare @ExceptionHandler, @InitBinder, or @ModelAttribute methods to be shared across multiple @Controller classes.

Classes with @ControllerAdvice can be declared explicitly as Spring beans or auto-detected via classpath scanning. All such beans are sorted via AnnotationAwareOrderComparator, i.e. based on @Order and Ordered, and applied in that order at runtime. 

For handling exceptions the first @ExceptionHandler to match the exception is used. For model attributes and InitBinder initialization, @ModelAttribute and @InitBinder methods will also follow @ControllerAdvice order.

By default the methods in an @ControllerAdvice apply globally to all Controllers. Use selectors annotations(), basePackageClasses(), and basePackages() (or its alias value()) to define a more narrow subset of targeted Controllers. 

If multiple selectors are declared, OR logic is applied, meaning selected Controllers should match 
at least one selector. Note that selector checks are performed at runtime and so adding many selectors may negatively impact performance and add complexity.

Since:
3.2



Spring introduces @ControllerAdvice annotation which allows you to write global exception handling code that can be applied to a all controllers or depending on controllers to a chosen package or even a specific annotation. 

We can handle global exception using @ControllerAdvice and @ExceptionHandler.
@ControllerAdvice annotation will be applied to all classes that use the @Controller annotation (which extends to classes using @RestController). 

We can use @ControllerAdvice in below formats:

  • @ControllerAdvice("com.gaurav.springboot")
  • @ControllerAdvice(value = "com.gaurav.springboot")
  • @ControllerAdvice(basePackages = "com.gaurav.springboot")

We have another way to define a specific package using basePackageClasses property, which will enable @ControllerAdvice to all controllers inside that package where that class resides.
@ControllerAdvice(basePackageClasses = Application.class)


To apply to specific classes use assignableTypes.
@ControllerAdvice(assignableTypes = UserController.class)


Spring Boot default Exception Handling


Suppose a resource is not available then the response thrown will be look alike below:
when we fire a request which does not exist like: http://localhost:8080/users/test
Then the response presented by Spring boot is like below

{
    "timestamp": "2018-09-07T05:54:32.828+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/users/test"
}

Demo example to configure Global Exception Handling in Spring Boot


1. pom.xml


<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.gaurav.springboot</groupId>
<artifactId>GlobalExceptionHandlingInSpringBoot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Global-Exception-Handling-In-SpringBoot</name>
<description>GlobalExceptionHandlingInSpringBoot - Sample Project</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

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

<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>


</project>

2. User.java


package com.gaurav.springboot.bean;

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

@Entity
@Table(name = "user")
public class User {

public User() {
}

public User(int id, String firstName, String lastName, String gender, int age) {
super();
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.gender = gender;
this.age = age;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

@Column(name = "first_name")
private String firstName;

@Column(name = "last_name")
private String lastName;

@Column(name = "gender")
private String gender;

@Column(name = "age")
private int age;

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
return true;
}

}

3. ErrorDetails.java

package com.gaurav.springboot.exception;
import java.util.Date;

import org.springframework.http.HttpStatus;

import com.fasterxml.jackson.annotation.JsonFormat;

public class ErrorDetails {

  @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd MMM yyyy hh:mm:ss aaa")
  private Date timestamp;
  private String message;
  private String description;

public Date getTimestamp() {
return timestamp;
}

public String getMessage() {
    return message;
  }

public String getDescription() {
return description;
}

public ErrorDetails(Date timestamp, String message, String description) {
    super();
    this.timestamp = timestamp;
    this.message = message;
    this.description = description;
  }

}

4. UserNotFoundException.java


package com.gaurav.springboot.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {

/**
*/
private static final long serialVersionUID = -8403805750611410023L;

public UserNotFoundException(String exception) {
super(exception);
}

}

5. CustomizedResponseEntityExceptionHandler.java

package com.gaurav.springboot.exception;
import java.util.Date;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
@RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

  @ExceptionHandler(Exception.class)
  public final ResponseEntity<ErrorDetails> handleExceptionsGlobally(Exception ex, WebRequest request) {
    ErrorDetails exceptionResponse = new ErrorDetails(new Date(), ex.getMessage(), 
        request.getDescription(false));
    return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
  }

  @ExceptionHandler(UserNotFoundException.class)
  public final ResponseEntity<ErrorDetails> handleUserNotFoundException(UserNotFoundException ex, WebRequest request) {
    ErrorDetails exceptionResponse = new ErrorDetails(new Date(), ex.getMessage(), 
        request.getDescription(false));
    return new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
  }

}

6. UserRepository.java

package com.gaurav.springboot.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.gaurav.springboot.bean.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long>{

}


7. UserController.java

package com.gaurav.springboot.controller;

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn;

import java.net.URI;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.gaurav.springboot.bean.User;
import com.gaurav.springboot.exception.UserNotFoundException;
import com.gaurav.springboot.repository.UserRepository;

@RestController
@RequestMapping(value = { "/users" })
public class UserController {

@Autowired
private UserRepository userRepository;

/**
* To create a user
* @param user
* @return
*/
@PostMapping("/new")
public ResponseEntity<Object> createUser(@RequestBody User user) {
User savedUser = userRepository.save(user);

URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedUser.getId()).toUri();

return ResponseEntity.created(location).build();

}
/**
* To retrieve all users
* @return
*/
@GetMapping("/get/all")
public List<User> getAllUsers() {
return userRepository.findAll();
}

/**
* To retrieve specific users
* @param id
* @return
*/
@GetMapping("/get/{id}")
public Resource<User> getUser(@PathVariable long id) {
Optional<User> user = userRepository.findById(id);

if (!user.isPresent())
throw new UserNotFoundException("id-" + id+" is not available");

Resource<User> resource = new Resource<User>(user.get());

ControllerLinkBuilder linkTo = linkTo(methodOn(this.getClass()).getAllUsers());

resource.add(linkTo.withRel("get-all-users"));

return resource;
}

/**
* To update specific user
* @param user
* @param id
* @return
*/
@PutMapping("/update/{id}")
public ResponseEntity<Object> updateUser(@RequestBody User user, @PathVariable long id) {

Optional<User> userDet = userRepository.findById(id);

if (!userDet.isPresent())
return ResponseEntity.notFound().build();

user.setId(id);
userRepository.save(user);

// return ResponseEntity.noContent().build();
return ResponseEntity.ok().build();
}
/**
* To Delete specific user
* @param id
*/
@DeleteMapping("/delete/{id}")
public void deleteUser(@PathVariable long id) {
userRepository.deleteById(id);
}
}

8. GlobalExceptionHandlingApplication.java


package com.gaurav.springboot;

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

@SpringBootApplication
public class GlobalExceptionHandlingApplication {

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

9. application.properties


logging.level.org.springframework.web=INFO
spring.datasource.driver: com.mysql.jdbc.Driver
spring.datasource.url: jdbc:mysql://localhost:3306/experimentdemo
spring.datasource.username:root
spring.datasource.password:root

spring.jpa.hibernate.dialect:org.hibernate.dialect.MySQL5Dialect
spring.jpa.show-sql:true
spring.jpa.hibernate.ddl-auto :update
spring.jpa.entitymanager.packagesToScan:com.gaurav.springboot

Get All Users: http://localhost:8080/users/get/all


Get Specific User: http://localhost:8080/users/get/1



POST(Create a new User):http://localhost:8080/users/new


Check the Created User in DB:



PUT(Updated the User): http://localhost:8080/users/update/11



Delete(Delete User): http://localhost:8080/users/delete/11