Tuesday, July 27, 2021

HashMap Internal Implementation in Java

HashMap internal implementation in Java or how does HashMap work internally in Java is a very important interview question. Some of the important points that you should know are-

  1. Where does HashMap store its elements internally?
  2. What is the term "bucket" in HashMap?
  3. What is hashing concept and how does it relate to HashMap?
  4. How does put() method work in HashMap?
  5. What happens if same hash is calculated for the keys or how are elements stored in case of hash collision?
  6. What happens if the null key is added.
  7. How does get() method work in HashMap?
  8. How does remove() method work in HashMap?

In this post we’ll go through the HashMap internal implementation in Java and try to explain the above mentioned points. Note that all the code snippet of the HashMap class provided in this post are from JDK 10.

Where does HashMap store its elements

Internally HashMap class in Java uses an Array (named table) of type Node to store its elements. Where Node<K, V> is an inner class with in HashMap class. Array is defined as follows in the HashMap class.

transient Node<K,V>[] table;
In a HashMap elements are stored as (key,value) pair and this (key,value) pair is represented by an interface Map.Entry. The Node class is an implementation of Map.Entry interface.

What is the term bucket in HashMap

When a (key, value) pair is added to a HashMap using that key a hash is calculated which gives the index in the array where that (key, value) pair will be added.

The term bucket used here is actually each index of the array. By default HashMap array is of length 16 so there are 16 buckets in a HashMap. Since the array is named table so table[0] is bucket0, table[1] is bucket1 and so on till bucket15.

When an element is added to the HashMap it is not added to that index in the array directly. Every bucket of the HashMap has an associated linked list and each array index holds reference to that linked list. Once the bucket to which the element has to be added is decided based on the calculated hash, a new node is created in the linked list which will have the (key, value) pair.

Following image shows how with in the internal implementation of HashMap, buckets and stored elements in the linked list, will look like.

HashMap internal implementation in Java

hashCode() and equals() method

For calculating hash, hashCode() method is called. equals() method is used to compare objects for equality.

Both of these methods are defined in the Object class in Java so available to all the classes, as Object class is super class for all the classes in Java. If you are using any custom object as key, ensure that these two methods hashCode() and equals() are implemented.

In HashMap hash is calculated using the key so it is very important that hashCode() is properly implemented for the fair distribution of the keys among all the buckets and there are less hash collisions. For example suppose you are using a custom object as key and the hashCode() implementation is not good. If you add 50 elements to the HashMap and same hash is calculated for 30 of them then the linked list associated with that bucket will have 30 elements where as other buckets will be relatively empty affecting the overall performance of HashMap.

equals() method implementation is used to verify if the inserted key is equal to any of the already inserted keys. Thus it is important to implement equals() method properly to ensure that an object is uniquely identified.

How does put() method work in HashMap

With all the groundwork done till now by going through buckets, hashing and hashCode() and equals() method it will be easy for you now to understand the internal implementation of HashMap in Java.

When you add a new (key,value) pair using put() method, first of all using key a hash will be calculated which will determine the bucket the (key, value) pair will go to.

If that bucket is empty a new linked list will be created, where first node of the linked list will be your (key, value) pair and the bucket (that array index) will hold the reference to that linked list.

If the bucket is not empty, that means linked list is already there. In that case equals() method is used to verify if such a key already exists in that bucket, if not found then a new node is created in the already existing linked list. In case equals() method returns true, that means key already exists in the bucket. In that case, the new value for the matching key will overwrite the old value.

In HashMap class implementation put() method is written as follows-

public V put(K key, V value) {
  return putVal(hash(key), key, value, false, true);
}

As you can see first thing it is doing is to calculate hash by passing key.

This explanation of put() method also covers the scenario when same hash is calculated for more than one key (Hash Collision scenario).

What happens when the null key is added

In HashMap adding one null key is permitted. When a (key, value) pair is added where key is null, hash calculation is not done and that (key, value) pair is always added to bucket 0.

You can see it from the internal implementation of the hash() method.

static final int hash(Object key) {
  int h;
  return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

How does get() method work in HashMap

In Java HashMap get() method is called with key as argument. Using that key, hash is calculated to determine the bucket where that element is stored. If the linked list associated with that bucket has more than one node then iteration of the linked list is done to match the stored keys with the passed key using equals method. When the matching key is found the associated value is returned.

In HashMap class implementation get() method is implemented as follows-

public V get(Object key) {
  Node<K,V> e;
  return (e = getNode(hash(key), key)) == null ? null : e.value;
}

How does remove() method work in HashMap

Implementation of remove() method is similar to get() method. Using the passed key, hash is calculated to determine the bucket where that element is stored. If the linked list associated with that bucket has more than one node then iteration of the linked list is done to match the stored keys with the passed key using equals method. When the matching key is found that node of the linked list is dereferenced.

HashMap changes in Java 8

HashMap implementation is designed to provide constant-time performance for the basic operations (get and put). But the performance of the HashMap may degrade if hashCode() is not properly implemented and there are lots of hash collisions.

As we have already seen in case of hash collisions one of the bucket will have more load and more (key, value) pairs will be added to the linked list associated with that bucket. For searching (get() method) in a linked list linear iteration of the linked list is done which will mean a worst case performance of O(n) if the searched key is the last node of the linked list.

To counter that problem of a particular linked list having more elements HashMap implementation is changed in Java 8. After a certain threshold is reached linked list is replaced by balanced tree to store elements. This change ensures performance of O(log(n)) in worst case scenarios rather than O(n) in the case of linked list.

That's all for the topic HashMap Internal Implementation in Java. If something is missing or you have something to share about the topic please write a comment.


You may also like

Sunday, July 18, 2021

Read Excel File in Java Using Apache POI

In this article we’ll see how to read Excel file in Java using Apache POI library. If you have to write to an Excel file in Java you can check this post- Write to Excel File in Java Using Apache POI

Apache POI

Apache POI is a Java API for Microsoft documents. Using Apache POI you can read and write MS Excel files using Java. In addition, you can read and write MS Word and MS PowerPoint files using Java.

Apache POI provides support for reading both OLE2 files and Office Open XML standards (OOXML) files.

  • OLE2 files include most Microsoft Office files such as XLS, DOC, and PPT as well as MFC serialization API based file formats.
  • Office OpenXML Format is the new standards based XML file format found in Microsoft Office 2007 and 2008. This includes XLSX, DOCX and PPTX.

Which means Apache POI supports reading excel files having .xls format as well as excel files with .xlsx format.

Maven dependencies for Apache POI

To read excel files using Apache POI you need to add the following dependency.

<dependency>
  <groupId>org.apache.poi</groupId>
  <artifactId>poi-ooxml</artifactId>
  <version>4.0.1</version>
</dependency>
This dependency adds the following jars-
poi-ooxml-4.0.1.jar
poi-4.0.1.jar
poi-ooxml-schemas-4.0.1.jar
curvesapi-1.05.jar
commons-compress-1.18.jar
commons-math3-3.6.1.jar
commons-collections4-4.2.jar
xmlbeans-3.0.2.jar

Here note that poi-4.0.1.jar has classes for working with old excel format (.xls) and poi-ooxml-4.0.1.jar has classes for working with newer excel format (.xlsx). Apache POI 4.0.1 requires Java 8 or newer.

Apache POI classes for reading excel files

Here is a primer about the classes that are used for reading excel file in Java using Apache POI.

Apache POI library provides two implementations for two excel spread sheet formats-

  • HSSF- It is the pure Java implementation for earlier excel format (.xls). Classes in this implementation will usually have HSSF prefix like HSSFWorkBook, HSSFSheet.
  • XSSF- It is the pure Java implementation for xslx file format (OOXML). Classes in this implementation will usually have XSSF prefix like XSSFWorkBook, XSSFSheet.

SS- It is a package built on top of HSSF and XSSF that provides common support for both formats with a common API. You should try to use classes from this package for better compatibility.

While working with excel file the usual progression is-

read excel file in Java

There are interfaces corresponding to this progression-

  • org.apache.poi.ss.usermodel.Workbook- High level representation of a Excel workbook. This is the first object most users will construct whether they are reading or writing a workbook. It is also the top level object for creating new sheets.
  • org.apache.poi.ss.usermodel.Sheet- High level representation of a Excel worksheet. Sheets are the central structures within a workbook.
  • org.apache.poi.ss.usermodel.Row- High level representation of a row of a spreadsheet.
  • org.apache.poi.ss.usermodel.Cell- High level representation of a cell in a row of a spreadsheet. Cells can be numeric, formula-based or string-based (text).

For creating a Workbook, WorkbookFactory class is used.

  • org.apache.poi.ss.usermodel.WorkbookFactory- Factory for creating the appropriate kind of Workbook (be it HSSFWorkbook or XSSFWorkbook), by auto-detecting from the supplied input.

Read excel file in Java using Apache POI example

In this Java example to read excel file we’ll read the following sheet (Employee.xslx) having employee information and create Employee objects using that data.

excel file POI
Employee class
public class Employee {
  private String firstName;
  private String lastName;
  private String department;
  private Date dob;
  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 getDepartment() {
    return department;
  }
  public void setDepartment(String department) {
    this.department = department;
  }
  public Date getDob() {
    return dob;
  }
  public void setDob(Date dob) {
    this.dob = dob;
  }	
}

Following class read data from the excel file and create objects.

import java.io.FileInputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

public class ReadExcel {
  private static final String EXCEL_FILE_PATH="F:\\knpcode\\Java\\Java Programs\\Java IO\\Employee.xlsx";
  public static void main(String[] args) {
    ReadExcel readExcel = new ReadExcel();
    readExcel.readExcelFile(EXCEL_FILE_PATH);
  }
  private void readExcelFile(String excelFilePath) {
    try {
      // get excel workbook
      Workbook workbook = WorkbookFactory.create(new FileInputStream(excelFilePath));
      // get excel sheet
      Sheet sheet = workbook.getSheetAt(0);
      //If you have more than one sheet in a workbook then you 
      // can iterate through sheets 
      /*Iterator<Sheet> sheetIterator = workbook.sheetIterator();
      while(sheetIterator.hasNext()) {
        Sheet sheet = sheetIterator.next();*/
        readSheet(sheet);             
      } catch (IOException | ParseException e) {
        e.printStackTrace();
      }
  }
    
  private void readSheet(Sheet sheet) throws ParseException{
    Iterator<Row> rowItr = sheet.iterator();
    List<Employee> empList = new ArrayList<>();
    // Iterate through rows
    while(rowItr.hasNext()) {
      Employee emp = new Employee();
      Row row = rowItr.next();
      // skip header (First row)
      if(row.getRowNum() == 0) {
          continue;
      }
      Iterator<Cell> cellItr = row.cellIterator();
      // Iterate each cell in a row
      while(cellItr.hasNext()) {                
        Cell cell = cellItr.next();
        int index = cell.getColumnIndex();
        switch(index) {
          case 0:
            emp.setFirstName((String)getValueFromCell(cell));
            break;
          case 1:
            emp.setLastName((String)getValueFromCell(cell));
              break;
          case 2:
            emp.setDepartment((String)getValueFromCell(cell));
              break;
          case 3:
            emp.setDob((Date)getValueFromCell(cell));
              break;
        }
      }
      empList.add(emp);
    }
    for(Employee emp : empList) {
      System.out.println("Employee information- " + emp.toString());
    }    
  }
	
  // Utility method to get cell value based on cell type
  private Object getValueFromCell(Cell cell) {
    switch(cell.getCellType()) {
      case STRING:
          return cell.getStringCellValue();
      case BOOLEAN:
          return cell.getBooleanCellValue();
      case NUMERIC:
        if(DateUtil.isCellDateFormatted(cell)) {
          return cell.getDateCellValue();
        }
        return cell.getNumericCellValue();
      case FORMULA:
        return cell.getCellFormula();
      case BLANK:
        return "";
      default:
        return "";                                
    }
  }
}
Output
Employee information- John Emerson Technology Mon Nov 12 00:00:00 IST 1990
Employee information- Shelly Mcarthy HR Sat May 07 00:00:00 IST 1960
Employee information- Tom Cevor Finance Mon Feb 03 00:00:00 IST 1992

Excel file used in the code has only a single sheet which can be retrieved using- Sheet sheet = workbook.getSheetAt(0);

If there are more than one sheet in the workbook then you can iterate through the sheets too-

Iterator<Sheet> sheetIterator = workbook.sheetIterator();
while(sheetIterator.hasNext()) {
  Sheet sheet = sheetIterator.next();
  // get data from sheet
}

That's all for the topic Read Excel File in Java Using Apache POI. If something is missing or you have something to share about the topic please write a comment.


You may also like

throw Vs throws in Java Exception Handling

In Java five keywords are used for exception handling namely – try, catch, finally, throw and throws. Out of these five, throw and throws keywords sound quite similar causing confusion thus the question what are the differences between throw and throws in Java exception handling.

Throw vs Throws in java

1- throws clause is used to declare an exception with in the method signature where as throw is used with in the code to actually throw an exception.

Exception declaration with in the method signature using throws
private void readFile() throws IOException {
  ....
  ....
}
Throwing exception using throw keyword
try{
  if(args.length != 2){
    throw new IllegalArgumentException("Two parameters should be passed");
  }
}

2- With throws clause multiple exceptions can be declared as a comma separated list. Where as throw can throw only a single instance of exception.

Declaring multiple exception with throws clause
private void readFile() throws FileNotFoundException, IOException, ArithmeticException {
  ....
  ....
}

3- With throws clause you can declare all the exceptions that may be thrown by your method code. You are not actually throwing an exception, just declaring them so the caller method can provide exception handling for the declared exceptions.

With throw you are actually throwing an exception that looks for the nearest catch block with the matching exception type.

4- With throws, while declaring exceptions you use the name of the exception classes itself.
With throw keyword you use an instance of the Throwable class or any of its sub-class, you don’t use the exception class itself.

With throws-
private void readFile() throws IOException
With throw
catch(IOException exp){   
 // throwing object of IOException class
 throw exp;
}
or you create an exception class object and throw it-
throw new IOException();
Class name (IOException) itself is given with the throws clause.

5- There is a restriction with throws clause that it has to be used with the method signature where as throw statement has to be part of the code where it may come anywhere inside the method, throw statement can be used with in a static block too.

Using throw with static block
static {
 try{
  ...
  ...
 }catch(Exception exp){
  System.out.println("Initialization error " + exp.getMessage());
  throw exp;
 }
}

That's all for the topic throw Vs throws in Java Exception Handling. If something is missing or you have something to share about the topic please write a comment.


You may also like

Saturday, July 17, 2021

Java throw Keyword With Examples

In the post try-catch block we have already seen some examples of exceptions but all those exceptions are thrown by Java run time. You may choose to throw an exception explicitly in your Java code, that can be done using throw statement in Java.

General form of Java throw statement

The throw statement requires a single argument; a throwable object.

throw throwableObj;

Here throwableObj must be an instance of Throwable class or any of its subclass.

How to get Throwable class instance

If you want to throw an exception explicitly you can get this Throwable class instance in two ways-

  1. You can create an instance using new operator. See example.
  2. You can use the exception parameter of the catch block and rethrow the exception. See example.

If there is a throw statement in a Java code then execution stops as soon as throw statement is encountered. Nearest catch block is checked for the matching exception type, if catch block has the matching exception type then that block handles the thrown exception. If matching exception type is not found then the next higher context is checked and so on.

Throw statement example with new exception instance

As stated above one of the way you can throw exception in your code is by creating an exception instance using new operator.

public class ThrowDemo {
  public static void main(String[] args) {
    try{
      if(args.length != 2){
        throw new IllegalArgumentException("Two parameters should be passed");
      }
    }catch(IllegalArgumentException exp){
      System.out.println("Exception in the code " + exp.getMessage());
      exp.printStackTrace();
    }
  }
}
Output
Exception in the code Two parameters should be passed
java.lang.IllegalArgumentException: Two parameters should be passed
at com.knpcode.ThrowDemo.main(ThrowDemo.java:8)

In the above code exception is thrown if 2 arguments are not passed. The constructor used while creating exception instance is the one that takes String argument. That way you can pass a clear reason why exception is thrown. There is a method getMessage() method in Exception classes that can be used to display that message.

Java throw statement example when same exception is rethrown

You can also rethrow the exception that is caught in a catch block. One of the common reason to do that is to see if there is any exception handler for that specific exception which is thrown. As example if you have a catch block with Exception as exception type it will catch most of the exceptions since Exception is higher up in the exception hierarchy . You can rethrow that exception to be caught by an exception handler with the specific exception type.

public class ExceptionDemo {
  public static void main(String[] args) {
    ExceptionDemo ed = new ExceptionDemo();
    try{
      ed.division(7, 0);
    }catch(ArithmeticException exp){
      System.out.println("Exception occurred while dividing" + exp.getMessage());
      exp.printStackTrace();
    }
  }
	
  private void division(int num1, int num2){
    double result;
    try{
      result = num1/num2;
      System.out.println("Result" + result);
    }catch(Exception exp){
      System.out.println("Exception occurred while dividing" + exp.getMessage());
      throw exp;
    }	
  }
}
Output
Exception occurred while dividing/ by zero
java.lang.ArithmeticException: / by zero
	at com.knpcode.ExceptionDemo.division(ExceptionDemo.java:18)
	at com.knpcode.ExceptionDemo.main(ExceptionDemo.java:8)
Exception occurred while dividing/ by zero
java.lang.ArithmeticException: / by zero
	at com.knpcode.ExceptionDemo.division(ExceptionDemo.java:18)
	at com.knpcode.ExceptionDemo.main(ExceptionDemo.java:8)

In the above code division method has a catch block with parameter of class Exception. From there you are rethrowing the caught exception. In the calling method it is caught again by the catch block which has parameter of type ArithmeticException.

Rethrowing a different exception

You can also catch one type of exception and rethrow exception of another type using Java throw keyword. Actually it is a best practice for exception handling to do that when you are propagating exception through separate layers. That helps in preserving loose coupling of your code.

For example, in your DB layer SQLException is caught there is no sense in letting the same exception propagate to your business layer. In this case best thing to do is catch the SQLException which is a checked exception in your DAO layer and rethrow another exception (unchecked) that should propagate to business layer. You should send the original exception instance as a parameter.

catch(SQLException ex){
  throw new RuntimeException("Error in DB layer", ex);
}

That's all for the topic Java throw Keyword With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

Thursday, July 15, 2021

Spring Boot Microservices Eureka + Ribbon

In the article Spring Boot Microservices example we saw an example of Spring Boot Microservice where we used Eureka for service registration and discovery. In this post we’ll extend that example further to see how to use both Eureka and Ribbon load balancer with Spring Boot to load the balance among micro services instances.

In the example there were two separate services User and Account which were registered with Eureka. From User service there was a call to Account service to fetch some data. In this post we’ll see how to configure more than one instances of Account service and use Ribbon load balancer to route the calls among those instances of Account service.

Load Balancing

In simple terms load balancing means distributing the load across several resources rather than putting all the load on a single resource. That helps in increasing throughput as there are more resources to share the load, increases reliability as there are more redundant resource to process the request even if any one resource goes down so there is no single point of failure.

Using Ribbon load balancing with Eureka

As we know Eureka is used for service registration and discovery where as Ribbon is a client side load balancer. Here I’ll try to explain how these two tools work together.

Eureka server maintains a service registry by registering each microservice with the Eureka server. When the inter-service communication happens, calling service interrogates the service registry using DiscoveryClient and gets in return all the instances of the called microservice. Now the question is, out of all the returned instances which one to call?

That’s where client load balancer like Ribbon comes in the picture. Client side load balancer uses an algorithm, like round robin (calling each instance in sequence) or using zone information (to locate a server in the same zone as the client), to get the instance of service that has to be called.

Spring Boot Microservice with loadbalancing

Spring Boot Micro Service with Ribbon example

In the Spring Boot Microservices example we have already seen how to configure Eureka server and how to register micro services User and Account with it by enabling discovery client (Using @EnableDiscoveryClient (or @EnableEurekaClient) annotation).

One problem was the URL used while calling Account service from User which hardcodes the host and port. That means every time same service will be called even if you create more instances of Account.

List<Account> accounts = new RestTemplate().exchange(
       "http://localhost:9000/accounts/{empId}", HttpMethod.GET, null, new
       ParameterizedTypeReference<List<Account>>(){}, id).getBody();

What we want is an abstraction so that the correct host and port is resolved at run time, that’s what we’ll try to configure using Netflix’s Ribbon load balancing service. To enable Ribbon you need to add the following dependency in pom.xml

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

Though this dependency is added automatically when you add dependency for eureka client. So, this dependency will suffice.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Creating another instance of Account Microservice

Now we want to scale up our service and need multiple copies of Account. We already created a Spring Boot project for the same with the following in application.properties.

eureka.client.service-url.default-zone=http://localhost:8761/eureka
server.port=9000
spring.application.name=account

After starting this instance you can change the port to 9001 and start the Account application again. That way you will have two instances one listening at port 9000 and another at port 9001.

You can also create a Separate Spring Boot project copy the files from Account and paste the following in application.properties

eureka.client.service-url.default-zone=http://localhost:8761/eureka
server.port=9001
spring.application.name=account

Either way you will have two instances running of the same service. You can verify it in Eureka Server by accessing URL - http://localhost:8761/

Eureka with ribbon

Creating LoadBalanced aware RestTemplate

Since we are using an instance of RestTemplate to make a call to another Service so we can make the RestTemplate bean load balanced aware. That can be done using @LoadBalanced annotation which instructs Netflix Ribbon to wrap this RestTemplate bean with load balancing advice.

Create a Config.java class in SpringBootUser project as given below.

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
 
@Configuration 
public class Config { 
  @Bean 
  @LoadBalanced
  RestTemplate restTemplate() { 
    return new RestTemplate(); 
  } 
}

Injecting the load balanced RestTemplate

Now you can inject this load balanced RestTemplate into the UserService. Then, in the URL, you can use the logical name of the service that was used to register it with Eureka. That’s how we are using this URL http://ACCOUNT/accounts/{empId} to access Account MicroService instance.

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;

@Service
public class UserService {
  @Autowired
  private RestTemplate restTemplate;
    public List<Account> showEmployees(@PathVariable("id") String id) {
    System.out.println(id);
    
    List<Account> accounts = restTemplate.exchange(
    "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new
    ParameterizedTypeReference<List<Account>>(){}, id).getBody();
    // Another call (to demo load balancing)
    accounts = restTemplate.exchange(
            "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new
            ParameterizedTypeReference<List<Account>>(){}, id).getBody();
    return accounts;          
  }
}

Testing the application

Once the changes are done start all the applications i.e. Eureka Server, User MicroService and two instances of Account MicroService.

In the console of SpringBootUser application you can see that the Load balancer has recognized the list of servers and one of them will be called to service the request.

2020-04-25 17:02:06.405  INFO 9908 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-04-25 17:02:06.573  INFO 9908 --- [nio-8080-exec-1] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-ACCOUNT
2020-04-25 17:02:06.575  INFO 9908 --- [nio-8080-exec-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: ACCOUNT instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-04-25 17:02:06.637  INFO 9908 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2020-04-25 17:02:06.905  INFO 9908 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-04-25 17:02:06.923  INFO 9908 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client ACCOUNT initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[user:9000, user:9001],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:2;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:user:9000;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 05:30:00 IST 1970;	First connection made: Thu Jan 01 05:30:00 IST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
, [Server:user:9001;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 05:30:00 IST 1970;	First connection made: Thu Jan 01 05:30:00 IST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@7ef76128

Accessing services

You can start the service by accessing URL- http://localhost:8080/user/1

Try to access the same URL in different browser tabs and you can see that requests are getting divided between two Account service instances.

Download source code- https://github.com/knpcode/SpringBoot-MicroService-Ribbon

That's all for the topic Spring Boot Microservices Eureka + Ribbon. If something is missing or you have something to share about the topic please write a comment.


You may also like

Wednesday, July 14, 2021

Spring Boot Microservices Example

In this article we’ll see a Spring Boot Microservices example with Eureka used for service registration and discovering the service. We’ll have two separate services User and Account developed as Microservices. With two microservices we'll also see how to call one microservice from another using RestTemplate.

When a large monolith application is split into two or more microservices those microservices may need to interact with each other. To do that these microservices need to be aware of each others existence and should be able to find each other. This process is known as service discovery. There is a tool called Eureka created by Netflix that can act as a discovery server, for that you need to register your microservices with the Eureka server.

So, in this Spring Boot Microservices example we are going to create 3 separate Spring Boot applications two for the functionality of User and Account and third one for Eureka Server.

Spring Boot application for Eureka Server

First we’ll create a Spring Boot project for configuring Eureka Server, this application acts as a service registry.

Starter you need to add for Eureka Server is spring-cloud-starter-netflix-eureka-server

Maven dependencies – pom.xml

pom.xml with the starter dependencies. Note that Spring Boot version used is 2.3.4.RELEASE and Spring Cloud version is Hoxton.SR8

<?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.4.RELEASE</version>
    <relativePath/>
  </parent>
  <groupId>com.knpcode</groupId>
  <artifactId>springeureka</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>EurekaServer</name>
  <description>Eureka Server project</description>

  <properties>
    <java.version>1.8</java.version>
    <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-server</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>

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

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

</project>

Application class

Application class with main method.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
  public static void main(String[] args) {
    SpringApplication.run(EurekaServerApplication.class, args);
  }
}

In the application class apart from @SpringBootApplication annotation another annotation @EnableEurekaServer is also added.

@EnableEurekaServer annotation indicates that we want to run a Eureka Server. By seeing a dependency on Spring Cloud Eureaka, Spring Boot automatically configures the application as a service registry.

Eureka Server Configuration

In the application.properties put the following.

server.port=8761
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

server.port configures the port Eureka Server runs on.

For a stand alone instance we don’t want Eureka Server to be a client too that is why these two entries-

eureka.client.register-with-eureka=false

eureka.client.fetch-registry=false

Running Eureka Server

Eureka server application is ready and you can run the EurekaServerApplication class to start the Eureka Server.

You should get the following messages if every thing runs fine-

2020-03-12 14:53:16.457  INFO 14400 --- [      Thread-10] e.s.EurekaServerInitializerConfiguration : Started Eureka Server
2020-03-12 14:53:16.503  INFO 14400 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8761 (http) with context path ''
2020-03-12 14:53:16.507  INFO 14400 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8761
2020-03-12 14:53:19.314  INFO 14400 --- [           main] o.n.s.EurekaServerApplication            : Started EurekaServerApplication in 30.203 seconds (JVM running for 33.929)

You can see the Eureka Server console by accessing URL- http://localhost:8761/

Spring boot eureka server

As you can see currently no instances are registered with Eureka. That’s what is the next task, to create Spring Boot microservices and register them with Eureka Server.

Spring Boot Account application

Create another Spring Boot project for Account Microservice, starter dependency for eureka client has to be added, in place of eureka-server, to register this Microservice as Eureka client. All the other dependencies remain same as used in Eureka Server application.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Rest Controller class

We’ll add a controller with the functionality to find all the accounts for the passed EmployeeId.

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AccountController {
  @Autowired
  private AccountService accountService;
  
  @GetMapping(value="/accounts/{empId}") 
  public List<Account>getAccountsByEmpId(@PathVariable String empId) { 
    System.out.println("EmpId------" + empId);
    List<Account> empAccountList = accountService.findAccountsByEmpId(empId);
    return empAccountList; 
  }
}

AccountService class

In the AccountService class you can see there is a dummy method to get the Accounts rather than accessing DB in order to keep the focus on interaction between microservices.

import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;

@Service
public class AccountService {
  public List<Account> findAccountsByEmpId(String empId){
    List<Account> accountList = getAccountList();
    List<Account> empAccountList = new ArrayList<>();
    for(Account account :  accountList) {
      if(account.getEmpId().equals(empId))
        empAccountList.add(account);
    }
    return empAccountList;
  }
    
  private List<Account> getAccountList(){
    List<Account> accountList = new ArrayList<>();
    accountList.add(new Account("1", "AC1", "MT"));
    accountList.add(new Account("1", "AC2", "IN"));
    accountList.add(new Account("2", "AC3", "IN"));
    return accountList;
  }
}
DTO Class

There is also an Account class that acts as a DTO or a model bean.

public class Account {
  private String empId;
  private String accountId;
  private String branch;
  Account(){
    
  }
  Account(String empId, String accountId, String branch){
    this.empId = empId;
    this.accountId = accountId;
    this.branch = branch;
  }
  public String getEmpId() {
    return empId;
  }
  public void setEmpId(String empId) {
    this.empId = empId;
  }
  public String getAccountId() {
    return accountId;
  }
  public void setAccountId(String accountId) {
    this.accountId = accountId;
  }
  public String getBranch() {
    return branch;
  }
  public void setBranch(String branch) {
    this.branch = branch;
  }
}

Application class

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootAccountApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootAccountApplication.class, args);
  }
}

Application class is annotated with the @EnableDiscoveryClient (so that this application can be discovered as Eureka client, you can also use @EnableEurekaClient annotation instead of @EnableDiscoveryClient) along with the @SpringBootApplication annotation.

Configuration for Eureka Client

Following properties are also to be added to the application.properties file to register Account Microservice as Eureka client.

eureka.client.service-url.default-zone=http://localhost:8761/eureka
server.port=9000
spring.application.name=account

eureka.client.service-url.default-zone property tells our microservice where to look for Eureka Server.

Using spring.application.name you give a logical name to your microservice.

Server port is configured as 9000 so this Account application runs on port 9000.

Registering Account Microservice as Eureka client

Run the SpringBootAccountApplication class to start this RESTful service. It will automatically be registered as Eureka client. You can verify that seeing the messages on the console.

2020-03-12 15:23:58.585  INFO 12416 --- [  restartedMain] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application ACCOUNT with eureka with status UP
2020-03-12 15:23:58.588  INFO 12416 --- [  restartedMain] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1584006838588, current=UP, previous=STARTING]
2020-03-12 15:23:58.597  INFO 12416 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_ACCOUNT/user:account:9000: registering service...
2020-03-12 15:23:58.940  INFO 12416 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9000 (http) with context path ''
2020-03-12 15:23:58.945  INFO 12416 --- [  restartedMain] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 9000
2020-03-12 15:23:59.194  INFO 12416 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_ACCOUNT/user:account:9000 - registration status: 204
2020-03-12 15:24:02.128  INFO 12416 --- [  restartedMain] o.n.a.SpringBootAccountApplication       : Started SpringBootAccountApplication in 31.85 seconds (JVM running for 35.175)

Verifying Eureka Server

If you refresh the URL for Eureka Server- http://localhost:8761/ now you should see an instance registered. Name of the instance is same as what was configured as a logical name using the following property.

spring.application.name=account

Spring Boot microservice example

Spring Boot User application

Another microservice we need to create is the User service so create another project. There again add the same starter dependency to register this Microservice as Eureka client.

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

Rest Controller class

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
  @Autowired
  private UserService userService;
  
  @GetMapping(value="/user/{id}")
  public List<Account> showEmployees(@PathVariable("id") String id) {     
    List<Account> accounts = userService.showEmployees(id);
    // displaying accounts
    for(Account acct : accounts) {
      System.out.println(acct.getEmpId());
      System.out.println(acct.getAccountId());
      System.out.println(acct.getBranch());
    }
    return accounts;          
  }
}
UserService Class

In method showEmployees there is a call to the Account microservice to get all the associated accounts for the passed employee ID.

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;

@Service
public class UserService {
  @Autowired
  private RestTemplate restTemplate;
  public List<Account> showEmployees(@PathVariable("id") String id) {
    System.out.println(id);
    //List<Account> accounts = new RestTemplate().exchange(
    //"http://localhost:9000/accounts/{empId}", HttpMethod.GET, null, new
    //ParameterizedTypeReference<List<Account>>(){}, id).getBody();
    
    List<Account> accounts = restTemplate.exchange(
    "http://ACCOUNT/accounts/{empId}", HttpMethod.GET, null, new
    ParameterizedTypeReference<List<Account>>(){}, id).getBody();
    return accounts;          
  }
}

restTemplate.exchange() is the method used for making remote call to another microservice.

  • First argument to restTemplate.exchange() is the URL to the Account microservice- "http://ACCOUNT/accounts/{empId}"
  • Second argument specifies that it is a HTTP Get command.
  • Third argument specifies the entity (headers and/or body) to write to the request. As we are not passing any request entity so it is null.
  • Fourth argument specifies the type of the response.
  • Fifth argument specifies the variables to expand in the template. We are passing id there which will replace {empId} in the URL.

Using Ribbon Load Balancer

In the above method you can see that the URL used for calling Microservice is http://ACCOUNT/accounts/{empId} though you can also use http://localhost:9000/accounts/{empId} but that hardcodes the location which is not good.

To avoid that hardcoding we are using Netflix's Ribbon service which can be integrated with Eureka. What we need to do is to mark a RestTemplate bean to be configured to use a LoadBalancerClient, to do that we can create RestTemplate bean as following.

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration 
public class Config { 
  @Bean 
  @LoadBalanced
  RestTemplate restTemplate() { 
    return new RestTemplate(); 
  } 
}

Once you have this Load balanced restTemplate instance then you can use the logical name of the service, in the URL, that was used to register it with Eureka. That’s how we are using this URL http://ACCOUNT/accounts/{empId} to access Account MicroService.

Application class

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootUserApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootUserApplication.class, args);
  }
}

Configuration for Eureka Client

Following properties are also to be added to the application.properties file to register User Microservice as Eureka client.

eureka.client.service-url.default-zone=http://localhost:8761/eureka
spring.application.name=user

Run the SpringBootUserApplication to start User MicroService. It will automatically be registered as Eureka client. You can verify that by seeing the messages on the console.

2020-03-12 16:24:00.228  INFO 9844 --- [  restartedMain] o.s.c.n.e.s.EurekaServiceRegistry        : Registering application USER with eureka with status UP
2020-03-12 16:24:00.231  INFO 9844 --- [  restartedMain] com.netflix.discovery.DiscoveryClient    : Saw local status change event StatusChangeEvent [timestamp=1584010440231, current=UP, previous=STARTING]
2020-03-12 16:24:00.240  INFO 9844 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_USER/user:user: registering service...
2020-03-12 16:24:00.402  INFO 9844 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient    : DiscoveryClient_USER/user:user - registration status: 204
2020-03-12 16:24:00.572  INFO 9844 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2020-03-12 16:24:00.577  INFO 9844 --- [  restartedMain] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8080
2020-03-12 16:24:03.278  INFO 9844 --- [  restartedMain] com.knpcode.user.SpringBootUserApplication   : Started SpringBootUserApplication in 28.889 seconds (JVM running for 33.647)

If you refresh the URL for Eureka Server- http://localhost:8761/ you should see both the MicroServices registered as Eureka clients.

Microservice with load balancing

Communication between MicroServices

Now we have two MicroSerivces created and running. Both of the MicroServices are registered with Eureka so these services can be discovered using Eureka.

Now when you access the URL http://localhost:8080/user/1 it will be serviced by showEmployees() method of the UserController in SpringBootUser application. From there using restTemplate.exchange() method it communicates with Account service.

Microservice with eureka client discovery

The URL (http://ACCOUNT/accounts/{empId}) passed in the exchange method triggers the whole process of load balancing and discovering the Eureka client as evident from the log messages.

2020-03-12 16:36:37.733  INFO 9844 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-03-12 16:36:37.915  INFO 9844 --- [nio-8080-exec-1] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-ACCOUNT
2020-03-12 16:36:37.916  INFO 9844 --- [nio-8080-exec-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: ACCOUNT instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2020-03-12 16:36:37.963  INFO 9844 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2020-03-12 16:36:38.090  INFO 9844 --- [nio-8080-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2020-03-12 16:36:38.098  INFO 9844 --- [nio-8080-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client ACCOUNT initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=ACCOUNT,current list of Servers=[user:9000],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:1;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:user:9000;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 05:30:00 IST 1970;	First connection made: Thu Jan 01 05:30:00 IST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@5820f552
1
AC1
MT
1
AC2
IN
2020-03-12 16:36:38.995  INFO 9844 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: ACCOUNT.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
favicon.ico
2020-03-12 16:38:59.147  INFO 9844 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration
2020-03-12 16:43:59.150  INFO 9844 --- [trap-executor-0] c.n.d.s.r.aws.ConfigClusterResolver      : Resolving eureka endpoints via configuration

Download sourcecode- SpringBoot-MicroService-Example

That's all for the topic Spring Boot Microservices Example. If something is missing or you have something to share about the topic please write a comment.


You may also like

Saturday, July 10, 2021

How to Copy a Directory in Java

This post shows how to copy a directory in Java where all the files and sub folders with in a directory are recursively copied to a new directory.

Options for copying a directory in Java

For copying the folder tree structure which includes the sub-directories and all the files you can use one of the following options in Java-

  • Using File.listFiles() method which returns an array of abstract pathnames denoting the files in the directory. Then you can iterate the array to list the files and copy them to the target directory, you will have to recursively call your method to list files with in the sub-directories. See example.
  • Java 7 onward you can use Files.walkFileTree method which walks a file tree rooted at a given starting file. See example.
  • Java 8 onward you can use Files.walk() method which returns the Path objects as stream by walking the file tree rooted at a given starting file. See example.

Directory structure used

Java programs shown here to copy a directory in Java use the following directory structure.

copy a folder in Java

With in the parent folder there is one sub folder Child with two files and one file is stored in the parent folder.

Copying directory in Java using Files.walk() method

Java 8 onward You can use Files.walk() method which returns the Path objects as stream. Each path in that Stream can be checked then to verify whether it's a directory or a file. If it is a file, it has to be copied to the target location, in case of directory you need to create that directory at the target location.

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class CopyDirectory {
  public static void main(String[] args) {
    final String SOURCE_DIR = "F:/knpcode/Parent";
    final String TARGET_DIR = "F:/knpcode/Parent_New";
    directoryCopy(SOURCE_DIR, TARGET_DIR);
  }

  private static void directoryCopy(String sourceDir, String targetDir){
    Path sourcePath = Paths.get(sourceDir);
    Path targetPath = Paths.get(targetDir);
    try(Stream<Path> filePaths = Files.walk(sourcePath)) {
      filePaths.forEach(filePath -> {
        try {
          if (Files.isRegularFile(filePath)) {
            Path newFile = targetPath.resolve(sourcePath.relativize(filePath));
            Files.copy(filePath, newFile);
            System.out.println("Copied file " + newFile);
          }else{
            Path newDir = targetPath.resolve(sourcePath.relativize(filePath));
            Files.createDirectory(newDir);
            System.out.println("Created Directory " + newDir);
          }
        }catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        } 
      });
    }catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
Created Directory F:\knpcode\Parent_New
Created Directory F:\knpcode\Parent_New\Child
Copied file F:\knpcode\Parent_New\Child\hello.txt
Copied file F:\knpcode\Parent_New\Child\Project.docx
Copied file F:\knpcode\Parent_New\Test.txt

Copying directory in Java using Files.walkFileTree() method

Java 7 onward You can use Files.walkFileTree() method using which you can walk the tree structure of the source directory and copy all files and sub-directories in the process.

One of the argument of this method is a FileVisitor interface. You do need to provide implementation of this interface as per your requirement.

FileVisitor interface has four methods, for listing files in a folder you do need to implement two of them; preVisitDirectory and visitFile.

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class CopyDirectory {
  public static void main(String[] args) {
    final String SOURCE_DIR = "F:/knpcode/Parent";
    final String TARGET_DIR = "G:/Parent_New";
    try {
      directoryCopy(SOURCE_DIR, TARGET_DIR);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  private static void directoryCopy(String sourceDir, String targetDir) throws IOException{
    Path sourcePath = Paths.get(sourceDir);
    Path targetPath = Paths.get(targetDir);
    // Walk the tree structure using WalkFileTree method       
    Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>(){
      @Override
      // Before visiting the directory, create directory 
      public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
        Path newDir = targetPath.resolve(sourcePath.relativize(dir));
        System.out.println("Path- " + newDir.toString());
        Files.createDirectory(newDir);
        return FileVisitResult.CONTINUE;
      }
      @Override
      // For each visited file copy it
      public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
        Path newFile = targetPath.resolve(sourcePath.relativize(file));
        System.out.println("Path- " + newFile.getFileName());
        Files.copy(file, newFile);                
        return FileVisitResult.CONTINUE;
      }
    });  
  }
}

Copying directory in Java using File.listFiles() method

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class CopyDirectory {
  public static void main(String[] args) {
    final String SOURCE_PATH = "F:/knpcode/Parent";
    final String TARGET_PATH = "F:/knpcode/Parent_New";
    File sourceDir = new File(SOURCE_PATH);
    File targetDir = new File(TARGET_PATH);
    try {
      directoryCopy(sourceDir, targetDir);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  private static void directoryCopy(File sourceDir, File targetDir) throws IOException{
    if(sourceDir.isDirectory()){
      // create target directory
      if(!targetDir.exists()){
        targetDir.mkdir();
        System.out.println("Created Directory " + targetDir);
      }
      File[] fileList = sourceDir.listFiles();
      for(File file : fileList){
        File sourceFile =  new File(sourceDir, file.getName());
        File targetFile = new File(targetDir, file.getName());
        // Recursive call in case of directory
        directoryCopy(sourceFile, targetFile);
      }
    }else{ // if it is a file
      Files.copy(sourceDir.toPath(), targetDir.toPath());
      System.out.println("Copied file " + targetDir);
    }
  }
}
Output
Created Directory F:\knpcode\Parent_New
Created Directory F:\knpcode\Parent_New\Child
Copied file F:\knpcode\Parent_New\Child\hello.txt
Copied file F:\knpcode\Parent_New\Child\Project.docx
Copied file F:\knpcode\Parent_New\Test.txt

That's all for the topic How to Copy a Directory in Java. If something is missing or you have something to share about the topic please write a comment.


You may also like

Friday, July 9, 2021

Delete a File or Directory Using a Java Program

This post shows how to delete a file or directory using a Java program and how to delete a directory recursively in Java. For deleting a file or directory, Java provides following options.

  • delete()- You can use the delete method of java.io.File class. This method deletes the file or directory denoted by this abstract pathname. If you are trying to delete a directory, then the directory must be empty in order to be deleted. Method returns true if the file or directory is successfully deleted otherwise returns false.
  • Files.delete(Path path)- Java 7 onward Files.delete() method can be used to delete a file or directory. For deleting a directory it must be ensured that the directory is empty. This method throws NoSuchFileException if the file does not exist and throws DirectoryNotEmptyException if the file is a directory and could not otherwise be deleted because the directory is not empty.
  • Files.deleteIfExists(Path path)- Another option in Files class to delete a file or directory is to use deleteIfExists() method. This method deletes a file or folder if it exists and returns true if the file was deleted by this method; false if the file could not be deleted because it did not exist. Same restriction for the directory applies that directory must be empty.

In the post we’ll see Java examples of deleting files and directories using the above mentioned methods. We’ll also see how to delete a non-empty directory by recursively deleting the files and sub-directories and ultimately deleting the parent directory.

Deleting file using java.io.File delete method

In the example code all the scenarios are covered-

  1. A file that exists at the given path is deleted.
  2. Trying to delete a file that doesn’t exist.
  3. Deleting an empty directory.
  4. Trying to delete non-empty directory.
public class DeleteFile {
  public static void main(String[] args) {
    File file = new File("F:\\knpcode\\Test\\postend.txt");
    fileDelete(file);

    // trying to delete file that doesn't exist
    file = new File("F:\\knpcode\\Test\\postend.txt");
    fileDelete(file);

    // Deleting empty directory
    file = new File("F:\\knpcode\\Test");
    fileDelete(file);

    // Deleting non-empty directory
    file = new File("F:\\knpcode\\Parent");
    fileDelete(file);
  }
	
  private static void fileDelete(File file){
    if(file.delete()){
      System.out.println("File " + file.getName() + " deleted successfully");
    }else{
      System.out.println("File " + file.getName() + " not deleted as it doesn't exist/non-empty directory");
    }
  }
}
Output
File postend.txt deleted successfully
File postend.txt not deleted as it doesn't exist/non-empty directory
File Test deleted successfully
File Parent not deleted as it doesn't exist/non-empty directory

Deleting file using Files delete and deleteIfExists method

Files.delete method to delete file

import java.io.IOException;
import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;

public class DeleteFile {
  public static void main(String[] args) {
    try {
      Files.delete(Paths.get("F:\\knpcode\\Test\\postend.txt"));
      // deleting same file again - file that doesn't exist scenario
      Files.delete(Paths.get("F:\\knpcode\\Test\\postend.txt"));
    } catch (NoSuchFileException e) {	
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (DirectoryNotEmptyException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
java.nio.file.NoSuchFileException: F:\knpcode\Test\postend.txt
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
	at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
	at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
	at java.nio.file.Files.delete(Files.java:1126)
	at com.knpcode.programs.DeleteFile.main(DeleteFile.java:16)

Files.deleteIfExists method to delete file

public class DeleteFile {

  public static void main(String[] args) {
    try {
      if(Files.deleteIfExists(Paths.get("F:\\knpcode\\Test\\postend.txt")))
        System.out.println("File deleted successfully");
      else
        System.out.println("File not deleted");
      // deleting same file again - file that doesn't exist scenario
      if(Files.deleteIfExists(Paths.get("F:\\knpcode\\Test\\postend.txt")))
        System.out.println("File deleted successfully");
      else
        System.out.println("File not deleted");
    }
    catch (DirectoryNotEmptyException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
File deleted successfully
File not deleted

Files.delete method to delete folder in Java

public class DeleteFile {
  public static void main(String[] args) {
    try {      
      // Deleting empty directory
      Files.delete(Paths.get("F:\\knpcode\\Test"));
      
      // Deleting non-empty directory
      Files.delete(Paths.get("F:\\knpcode\\Parent"));
      
    } catch (NoSuchFileException e) {	
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (DirectoryNotEmptyException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
Output
java.nio.file.DirectoryNotEmptyException: F:\knpcode\Parent
	at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:266)
	at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
	at java.nio.file.Files.delete(Files.java:1126)
	at com.knpcode.programs.DeleteFile.main(DeleteFile.java:22)

Deleting non-empty directory recursively in Java

As you can see from the above examples directory should be empty to be deleted, in case of non-empty directory it is not deleted. For deleting a non-empty directory you need to recursively walk through the folder structure and delete all the files and sub-directories before deleting the parent directory which is empty by then.

For recursively deleting a file in Java there are two options-

  1. Using File.listFiles() method which returns an array of abstract pathnames denoting the files in the directory. Then you can iterate the array to delete the files and you will have to recursively call your method to delete files with in the sub-directories.
  2. Java 7 onward you can use Files.walkFileTree() method which walks a file tree rooted at a given starting file.

Directory structure used

Java programs shown here to delete a non-empty directory in Java use the following directory structure.

delete directory using Java

With in the parent folder there are two sub-folders Child with two files and Empty with no file. One file is stored in the parent folder.

Deleting directory recursively using File.listFiles() method

public class DeleteDirectory {
  public static void main(String[] args) {
    // Source folder
    final String SOURCE_DIR = "F:/knpcode/Parent";
    File sourceDir = new File(SOURCE_DIR);
    directoryDeletion(sourceDir);
  }
	
  private static void directoryDeletion(File sourceDir){
    if(!sourceDir.isDirectory()){
      System.out.println("Not a directory.");
      return;
    }
    File[] fileList = sourceDir.listFiles();
    for(File file : fileList){
      // if directory call method recursively to 
      // list files with in sub-directories for deletion
      if(file.isDirectory()){
        System.out.println("Sub Directory- " + file.getName());
        directoryDeletion(file);
      }else{				 
        System.out.println("Deleting file- " + file.getName());
        // if it is a file then delete it
        file.delete();
      }
    }
    // For deleting sub-directories and parent directory
    System.out.println("Deleting Directory - " + sourceDir.getName());
    sourceDir.delete();
  }
}
Output
Sub Directory- Child
Deleting file- hello.txt
Deleting file- Project.docx
Deleting Directory - Child
Sub Directory- Empty
Deleting Directory - Empty
Deleting file- Test.txt
Deleting Directory – Parent

Deleting directory recursively using Java Files.walkFileTree method

Java 7 onward You can use Files.walkFileTree() method using which you can walk the tree structure of the source directory and delete all the files and sub-directories in the process. One of the argument of this method is a FileVisitor interface. You do need to provide implementation of this interface as per your requirement.

FileVisitor interface has four methods, for deleting directory recursively you do need to implement two of them; postVisitDirectory() (to delete directory after visiting all the files) and visitFile (to delete files).

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class DeleteDirectory {
  public static void main(String[] args) {
    // Source folder
    final String SOURCE_PATH = "F:/knpcode/Parent";
    try {
      directoryDeletion(SOURCE_PATH);
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
  }
	
  private static void directoryDeletion(String sourceDir) throws IOException {
    Path sourcePath = Paths.get(sourceDir);
    Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>(){
      @Override
      // Before visiting the directory, create directory 
      public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {		    		
        System.out.println("Deleting Directory- " + dir.toString());
        Files.delete(dir);
        return FileVisitResult.CONTINUE;
      }
      @Override
      // For each visited file delete it
      public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException{
        System.out.println("Deleting file- " + file.getFileName());
        Files.delete(file);                
        return FileVisitResult.CONTINUE;
      }
    });  
  }
}
Output
Deleting file- hello.txt
Deleting file- Project.docx
Deleting Directory- F:\knpcode\Parent\Child
Deleting Directory- F:\knpcode\Parent\Empty
Deleting file- Test.txt
Deleting Directory- F:\knpcode\Parent

That's all for the topic Delete a File or Directory Using a Java Program. If something is missing or you have something to share about the topic please write a comment.


You may also like