Welcome To Fusebes - Dev & Programming Blog

How to use Log4j 2 with Spring Boot
03
Mar
2021

How to use Log4j 2 with Spring Boot

Apache Log4j 2 is a successor of Log4j 1.x (who would have guessed? :p).

Log4j 2 provides a significant improvement in performance compared to its predecessor. It contains asynchronous loggers and supports multiple APIs including SLF4J, commons logging, and java.util.loggging.

In this article, you’ll learn how to integrate and configure Log4j 2 in Spring Boot applications. You’ll configure different types of appenders including RollingFileAppender and SMTPAppender. You’ll also learn how to use Async logging capabilities provided by Log4j 2.

The idea is to build a simple Spring Boot application from scratch and demonstrate how to go about integrating and configuring Log4j 2 in the app.

So, Let’s get started!

Creating the Project

Let’s use Spring Boot CLI to generate the project. If you don’t have Spring Boot CLI installed, I highly encourage you to do so. Check out the Official Spring Boot documentation for any help with the installation.

Fire up your terminal and type the following command to generate the project –

spring init --name=log4j2-demo --dependencies=web log4j2-demo

Once the project is generated, import it into your favorite IDE. The project’s directory structure should look like this –

Spring Boot Log4j2 Example Directory Structure

Adding Log4j2

All the Spring Boot starters depend on spring-boot-starter-logging, which uses Logback by default. For using Log4j2, you need to exclude spring-boot-starter-logging and add spring-boot-starter-log4j2 dependency.

Open pom.xml file and add the following snippet to the <dependencies> section –

<!-- Exclude Spring Boot's Default Logging -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
	<exclusions>
		<exclusion>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-logging</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<!-- Add Log4j2 Dependency -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Configuring Log4j2

Spring Boot automatically configures Log4j if it finds a file named log4j2.xml or log4j2.json or log4j2.yaml in the classpath.

In this article, we’ll configure log4j 2 using XML. Create a new file log4j2.xml inside src/main/resources directory, and add the following configuration to it –

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
    <Properties>
        <Property name="LOG_PATTERN">
            %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex
        </Property>
    </Properties>
    <Appenders>
        <Console name="ConsoleAppender" target="SYSTEM_OUT" follow="true">
            <PatternLayout pattern="${LOG_PATTERN}"/>
        </Console>
    </Appenders>
    <Loggers>
        <Logger name="com.example.log4j2demo" level="debug" additivity="false">
            <AppenderRef ref="ConsoleAppender" />
        </Logger>

        <Root level="info">
            <AppenderRef ref="ConsoleAppender" />
        </Root>
    </Loggers>
</Configuration>

The above configuration defines a simple ConsoleAppender and declares two loggers – an application specific logger and the the root logger.

Using Log4j 2 in the app

Let’s add some logging code to check our logger configuration. Open Log4j2DemoApplication.java and replace it with the following code –

package com.example.log4j2demo;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Log4j2DemoApplication implements ApplicationRunner {
    private static final Logger logger = LogManager.getLogger(Log4j2DemoApplication.class);

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

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        logger.debug("Debugging log");
        logger.info("Info log");
        logger.warn("Hey, This is a warning!");
        logger.error("Oops! We have an Error. OK");
        logger.fatal("Damn! Fatal error. Please fix me.");
    }
}

We have added 5 simple logs of different log levels. When you run the app, it logs everything on the console.

Adding a Rolling File Appender

If you want your logs to be written to a file, then you can use a RollingFile appender. RollingFile appender creates a new file whenever the log file reaches a certain threshold specified by the triggering policy.

It stores the old log files with names matching the pattern specified by the filePattern parameter –

<!-- Rolling File Appender -->
<RollingFile name="FileAppender" fileName="logs/log4j2-demo.log" 
             filePattern="logs/log4j2-demo-%d{yyyy-MM-dd}-%i.log">
    <PatternLayout>
        <Pattern>${LOG_PATTERN}</Pattern>
    </PatternLayout>
    <Policies>
        <SizeBasedTriggeringPolicy size="10MB" />
    </Policies>
    <DefaultRolloverStrategy max="10"/>
</RollingFile>

In the above RollingFile configuration, I’ve specified a SizeBasedTriggeringPolicy which will roll files over when the size reaches 10MB. The <DefaultRollOverStrategy max="10" /> element specifies the maximum number of log files that will be kept.

There is another common triggering policy called TimeBasedTriggeringPolicy which is used commonly in Log4j2. You can use TimeBasedTriggeringPolicy to specify how often a rollover should occur based on the most specific time unit in the date/time pattern specified in the filePattern attribute.

Here is how you can use the TimeBasedTriggeringPolicy in the above example to roll files over every day –

<Policies>
    <TimeBasedTriggeringPolicy interval="1" />
</Policies>

The interval attribute specifies the frequency of rollover. So if you want to roll files over every week, you can specify interval="7".

In the above example, the date/time pattern of the file is {yyy-MM-dd}, where the most specific time unit is dd (date). Therefore the TimeBasedTriggeringPolicy roll files over based on date. If the date/time pattern was yyy-MM-dd-HH, the rollover would occur based on hour.

Finally, To use the RollingFile appender, you need to add the above RollingFile configuration to the <Appenders> section inside log4j2.xml, and configure one of your loggers to use it like so –

<Root level="info">
    <AppenderRef ref="ConsoleAppender"/>
    <AppenderRef ref="FileAppender"/>
</Root>

Adding an SMTP Appender

SMTP appender is very useful in production systems when you want to be notified about any errors in your application via email.

You can configure an SMTP appender to send ERROR emails to you using an SMTP server like so –

<!-- SMTP Appender -->
<SMTP name="MailAppender"
      subject="Log4j2 Demo [PROD]"
      to="youremail@example.com"
      from="log4j2-demo-alerts@example.com"
      smtpHost="yourSMTPHost"
      smtpPort="587"
      smtpUsername="yourSMTPUsername"
      smtpPassword="yourSMTPPassword"
      bufferSize="1">
    <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
    <PatternLayout>
        <Pattern>${LOG_PATTERN}</Pattern>
    </PatternLayout>
</SMTP>

Note that, for SMTP appender to work, you need to include spring-boot-starter-mail dependency to your pom.xml file –

<!-- Needed for SMTP appender -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

Asynchronous Logging

Log4j2 Supports Async loggers. These loggers provide a drastic improvement in performance compared to their synchronous counterparts.

Async Loggers internally use a library called Disruptor for asynchronous logging.

We need to include Disruptor dependency for using async loggers. Add the following to your pom.xml file –

<!-- Needed for Async Logging with Log4j 2 -->
<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>3.3.6</version>
</dependency>

Now you can either make all Loggers asynchronous, or create a mix of sync and async loggers.

1. Making all Loggers Asynchronous

Making all loggers asynchronous is very easy. You just need to set the SystemProperty Log4jContextSelector to org.apache.logging.log4j.core.async.AsyncLoggerContextSelector.

You can do that by adding the System Property at runtime like so –

mvn spring-boot:run -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

or, when using a packaged jar –

mvn clean package
java -jar target/log4j2-demo-0.0.1-SNAPSHOT.jar -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector

2. Using a mix of Sync and Async Loggers

You can also use a mix of sync and async loggers with Log4j 2 using the <AsyncLogger> configuration element.

In the following example, all the application specific logs will be asynchronous, and other root logs will be synchronous –

<Loggers>
    <AsyncLogger name="com.example.log4j2demo" level="debug" additivity="false">
        <AppenderRef ref="ConsoleAppender" />
        <AppenderRef ref="FileAppender" />
    </AsyncLogger>

    <Root level="info">
        <AppenderRef ref="ConsoleAppender" />
        <AppenderRef ref="FileAppender" />
    </Root>
</Loggers>

You don’t need to add any SystemProperty when you’re using <AsyncLogger> element. Just add the above loggers to your log4j2.xml file and you’re ready to go.

Conclusion

Congratulations folks! In this article, we learned how to integrate and configure Log4j 2 in Spring Boot apps. We also configured different types of appenders like ConsoleAppender, RollingFile Appender, and SMTP Appender. Finally, we learned how to use Async loggers provided by Log4j2.

How to delete a directory recursively with all its subdirectories and files in Java
03
Mar
2021

How to delete a directory recursively with all its subdirectories and files in Java

In this short article, you’ll learn how to delete a directory recursively along with all its subdirectories and files.

There are two examples that demonstrate how to achieve this task. The idea behind both of the examples is to traverse the file tree, and delete the files in any directory before deleting the directory itself.

Delete directory recursively – Java 8+

This example makes use of Files.walk(Path) method that returns a Stream<Path> populated with Path objects by walking the file-tree in depth-first order.

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

public class DeleteDirectoryRecursively {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("java");

        // Traverse the file tree in depth-first fashion and delete each file/directory.
        Files.walk(dir)
                .sorted(Comparator.reverseOrder())
                .forEach(path -> {
                    try {
                        System.out.println("Deleting: " + path);
                        Files.delete(path);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
    }
}

Delete directory recursively – Java 7

The following example uses Files.walkFileTree(Path, FileVisitor) method that traverses a file tree and invokes the supplied FileVisitor for each file.

We use a SimpleFileVisitor to perform the delete operation.

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class DeleteDirectoryRecursively1 {
    public static void main(String[] args) throws IOException {
        Path dir = Paths.get("java");

        // Traverse the file tree and delete each file/directory.
        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                System.out.println("Deleting file: " + file);
                Files.delete(file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                System.out.println("Deleting dir: " + dir);
                if (exc == null) {
                    Files.delete(dir);
                    return FileVisitResult.CONTINUE;
                } else {
                    throw exc;
                }
            }
        });
    }
}
Kotlin Inheritance, Method Overriding, and Property Overriding
07
Mar
2021

Kotlin Inheritance, Method Overriding, and Property Overriding

Inheritance is one of the key concepts of Object Oriented Programming (OOP). Inheritance enables re-usability. It allows a class to inherit features (properties and methods) from another class.

The class that inherits the features of another class is called the Child class or Derived class or Sub class, and the class whose features are inherited is called the Parent class or Base class or Super class.

All the classes in Kotlin have a common base class called Any. It corresponds to the Object class in Java. Every class that you create in Kotlin implicitly inherits from Any –

class Person // Implicitly inherits from the default Super class - Any

The Any class contains three methods namely equals()hashCode() and toString(). All the classes in Kotlin inherit these three methods from Any, and can override them to provide their own implementation.

Inheritance (Creating Base and Derived classes)

Here is how you declare a base class and a derived class in Kotlin –

// Base class (Super class)
open class Computer {
}

// Derived class (Sub class)
class Laptop: Computer() {
}

Notice the use of open keyword in the base class. By default, all the classes in Kotlin are final (non-inheritable).

To allow a class to be inherited by others, you must mark it with the open modifier.

Note that the child class has the responsibility to initialize the parent class. If the child class has a primary constructor, then it must initialize the parent class right in the class header with the parameters passed to its primary constructor –

// Parent class
open class Computer(val name: String,
                    val brand: String) {
}

// Child class (initializes the parent class)
class Laptop(name: String, 
             brand: String, 
             val batteryLife: Double) : Computer(name, brand) {
   
}

If the child class doesn’t have a primary constructor, then all of its secondary constructors have to initialize the parent class either by calling the super keyword directly or by delegating to another constructor that does that –

class Laptop : Computer {
    val batteryLife: Double

	// Calls super() to initialize the Parent class
    constructor(name: String, brand: String, batteryLife: Double): super(name, brand) {
        this.batteryLife = batteryLife
    }

	// Calls another constructor (which calls super())
    constructor(name: String, brand: String): this(name, brand, 0.0) {
        
    }
}

In the above examples, we initialized the parent class using its primary constructor. If the parent class contains one or more secondary constructors, then the child class can initialize the parent class using any of the primary constructor or secondary constructors.

Just keep in mind that the parent class needs to be initialized. It doesn’t matter which of its constructor is used to initialize it.

Inheritance Example with Properties and Member Functions

Let’s now see a complete example of Inheritance in Kotlin. Consider a banking application where people can have several types of Bank accounts like SavingsAccount, CurrentAccount etc.

In such cases, it makes sense to create a base class called BankAccount and let other classes like SavingsAccount and CurrentAccount inherit from the BankAccount class.

Following is a simple BankAccount class for our Banking application –

/**
 * BankAccount (Base Class)
 * @property accountNumber - Account Number (read-only)
 * @property accountName -  Account Name (read-only)
 * @property balance - Current Balance (Mutable)
 */

open class BankAccount(val accountNumber: String, val accountName: String) {
    var balance : Double = 0.0

    fun depositeMoney(amount: Double): Boolean {
        if(amount > 0) {
            balance += amount
            return true
        } else {
            return false
        }
    }

    fun withdrawMoney(amount: Double): Boolean {
        if(amount > balance) {
            return false
        } else {
            balance -= amount
            return true
        }
    }

}

A Savings account is a Bank account with some interest rate on the balance amount. We can model the SavingsAccount class in the following way –

/**
 * SavingsAccount (Derived Class)
 * @property interestRate - Interest Rate for SavingsAccount (read-only)
 * @constructor - Primary constructor for creating a Savings Account
 * @param accountNumber - Account Number (used to initialize BankAccount)
 * @param accountName - Account Name (used to initialize BankAccount)
 */

class SavingsAccount (accountNumber: String, accountName: String, val interestRate: Double) :
        BankAccount(accountNumber, accountName) {

    fun depositInterest() {
        val interest = balance * interestRate / 100
        this.depositeMoney(interest);
    }
}

The SavingsAccount class inherits the following features from the base class –

  • Properties – accountNumberaccountNamebalance
  • Methods – depositMoneywithdrawMoney

Let’s now write some code to test the above classes and methods –

fun main(args: Array<String>) {
    // Create a Savings Account with 6% interest rate
    val savingsAccount = SavingsAccount("64524627", "Rajeev Kumar Singh", 6.0)
    
    savingsAccount.depositeMoney(1000.0)
    
    savingsAccount.depositInterest()
    
    println("Current Balance = ${savingsAccount.balance}")
}

Overriding Member Functions

Just like Kotlin classes, members of a Kotlin class are also final by default. To allow a member function to be overridden, you need to mark it with the open modifier.

Moreover, The derived class that overrides a base class function must use the override modifier, otherwise, the compiler will generate an error –

open class Teacher {
    // Must use "open" modifier to allow child classes to override it
    open fun teach() {
        println("Teaching...")
    }
}

class MathsTeacher : Teacher() {
    // Must use "override" modifier to override a base class function
    override fun teach() {
        println("Teaching Maths...")
    }
}

Let’s test the above classes by defining the main method –

fun main(args: Array<String>) {
    val teacher = Teacher()
    val mathsTeacher = MathsTeacher()

    teacher.teach()  // Teaching...
    mathsTeacher.teach() // Teaching Maths..
}

Dynamic Polymorphism

Polymorphism is an important concept in Object Oriented Programming. There are two types of polymorphism –

  1. Static (compile-time) Polymorphism
  2. Dynamic (run-time) Polymorphism

Static polymorphism occurs when you define multiple overloaded functions with same name but different signatures. It is called compile-time polymorphism because the compiler can decide which function to call at compile itself.

Dynamic polymorphism occurs in case of function overriding. In this case, the function that is called is decided at run-time.

Here is an example –

fun main(args: Array<String>) {
    val teacher1: Teacher = Teacher()  // Teacher reference and object
    val teacher2: Teacher = MathsTeacher() // Teacher reference but MathsTeacher object

    teacher1.teach()  // Teaching...
    teacher2.teach() // Teaching Maths..
}

The line teacher2.teach() calls teach() function of MathsTeacher class even if teacher2 is of type Teacher. This is because teacher2 refers to a MathsTeacher object.

Overriding Properties

Just like functions, you can override the properties of a super class as well. To allow child classes to override a property of a parent class, you must annotate it with the open modifier.

Moreover, The child class must use override keyword for overriding a property of a parent class –

open class Employee {
    // Use "open" modifier to allow child classes to override this property
    open val baseSalary: Double = 30000.0
}

class Programmer : Employee() {
    // Use "override" modifier to override the property of base class
    override val baseSalary: Double = 50000.0
}

fun main(args: Array<String>) {
    val employee = Employee()
    println(employee.baseSalary) // 30000.0

    val programmer = Programmer()
    println(programmer.baseSalary) // 50000.0
}

Overriding Property’s Getter/Setter method

You can override a super class property either using an initializer or using a custom getter/setter.

In the example below, we’re overriding the age property by defining a custom setter method –

open class Person {
    open var age: Int = 1
}

class CheckedPerson: Person() {
    override var age: Int = 1
        set(value) {
            field = if(value > 0) value else throw IllegalArgumentException("Age can not be negative")
        }
}

fun main(args: Array<String>) {
    val person = Person()
    person.age = -5 // Works

    val checkedPerson = CheckedPerson()
    checkedPerson.age = -5  // Throws IllegalArgumentException : Age can not be negative
}

Calling properties and functions of Super class

When you override a property or a member function of a super class, the super class implementation is shadowed by the child class implementation.

You can access the properties and functions of the super class using super() keyword.

Here is an example –

open class Employee {
    open val baseSalary: Double = 10000.0

    open fun displayDetails() {
        println("I am an Employee")
    }
}

class Developer: Employee() {
    override var baseSalary: Double = super.baseSalary + 10000.0

    override fun displayDetails() {
        super.displayDetails()
        println("I am a Developer")
    }
}

Conclusion

That’s all in this article folks. I hope you understood how inheritance works in Kotlin

Scaffolding your Spring Boot Application
06
Feb
2021

Scaffolding your Spring Boot Application

Scaffolding, in modern web apps, has been made popular by tools like yeoman. These tools generate a standard prototype for your project with everything you need to get up and running, including the directory structure, the build system, the project dependencies etc.

Spring Boot has its own scaffolding tool. There are two ways to quickly bootstrap your Spring boot application –

  1. Using Spring Initializer web app hosted at http://start.spring.io
  2. Using Spring Boot CLI

Spring Initializer

Spring Initializer is a web tool which helps in quickly bootstrapping spring boot projects.

The tool is very simple to use. You just need to enter your project details and required dependencies. The tool will generate and download a zip file containing a standard spring boot project based on the details you have entered.

Spring Initializer Web Page

Follow these steps to generate a new project using Spring Initializer web app –

Step 1. Go to http://start.spring.io.

Step 2. Select Maven Project or Gradle Project in the Project section. Maven Project is the default.

Step 3. Select the language of your choice JavaKotlin, or Groovy. Java is the default

Step 4. Select the Spring Boot version. The current stable release is the default.

Step 5. Enter group and artifact details in Project metadata.

Step 5.1. Click Options to customize more specific details about your project like Name, Description, Package name, etc.

Step 6. Search for dependencies in the Dependencies section and press enter to add them to your project. You can also click on the menu icon besides the search icon to see the list of all the dependencies available and select the ones that you want to include in your project.

Step 7. Once you have selected all the dependencies and entered the project details, click Generate to generate your project.

The tool will generate and download a zip file containing your project’s prototype. The generated prototype will have all the project directories, the main application class and the pom.xml or build.gradle file with all the dependencies you had selected.

Just unzip the file and import it in an IDE of your choice and start working.

I created a maven project using Spring Initializer while writing this post and here is what I got –

Spring Boot Project Scaffolding Directory Structure

You see! I’ve got everything that is needed to get started. A standard spring boot project structure containing all the required directories along with a pom.xml file containing all the dependencies.

You can go to the project’s root directory and type the following command in your terminal to run the application.

$ mvn spring-boot:run

If you have generated a gradle project, you can run it using –

$ gradle bootRun

Spring Boot CLI

Spring Boot CLI is a command line tool that helps in quickly prototyping with Spring. You can also run Groovy scripts using this tool.

Checkout the Official Spring Boot documentation for instructions on how to install Spring Boot CLI for your operating system. Once installed, type the following command to see what you can do with Spring Boot CLI –

$ spring --help

usage: spring [--help] [--version]
       <command> [<args>]

Available commands are:

  run [options] <files> [--] [args]
    Run a spring groovy script

  grab
    Download a spring groovy scripts dependencies to ./repository

  jar [options] <jar-name> <files>
    Create a self-contained executable jar file from a Spring Groovy script

  war [options] <war-name> <files>
    Create a self-contained executable war file from a Spring Groovy script

  install [options] <coordinates>
    Install dependencies to the lib/ext directory

  uninstall [options] <coordinates>
    Uninstall dependencies from the lib/ext directory

  init [options] [location]
    Initialize a new project using Spring Initializr (start.spring.io)

  encodepassword [options] <password to encode>
    Encode a password for use with Spring Security

  shell
    Start a nested shell

Common options:

  --debug Verbose mode
    Print additional status information for the command you are running


See 'spring help <command>' for more information on a specific command.

In this post, we’ll look at spring init command to generate a new spring boot project. The spring init command internally uses Spring Initializr web app to generate and download the project.

Open your terminal and type –

$ spring help init

to get all the information about init command.

Following are few examples of how to use the command –

To list all the capabilities of the service:
    $ spring init --list

To creates a default project:
    $ spring init

To create a web my-app.zip:
    $ spring init -d=web my-app.zip

To create a web/data-jpa gradle project unpacked:
    $ spring init -d=web,jpa --build=gradle my-dir

Let’s generate a maven web application with JPA, Mysql and Thymleaf dependencies –

$ spring init -n=blog-sample -d=web,data-jpa,mysql,devtools,thymeleaf -g=com.test-a=blog-sample

Using service at https://start.spring.io
Content saved to 'blog-sample.zip'

This command will create a new spring boot project with all the specified settings.

Quick and Simple! isn’t it?

Conclusion

Congratulations folks! We explored two ways of quickly bootstrapping spring boot applications. I always use one of these two methods whenever I have to work with a new Spring Boot app. It saves a lot of time for me.

Spring Boot File Upload / Download Rest API Example
03
Mar
2021

Spring Boot File Upload / Download Rest API Example

Uploading and downloading files are very common tasks for which developers need to write code in their applications.

In this article, You’ll learn how to upload and download files in a RESTful spring boot web service.

We’ll first build the REST APIs for uploading and downloading files, and then test those APIs using Postman. We’ll also write front-end code in javascript to upload files.

Following is the final version of our application –

Spring Boot File Upload and Download AJAX Rest API Web Service

All right! Let’s get started.

Creating the Application

Let’s generate our application using Spring Boot CLI. Fire up your terminal, and type the following command to generate the app –

$ spring init --name=file-demo --dependencies=web file-demo
Using service at https://start.spring.io
Project extracted to '/Users/rajeevkumarsingh/spring-boot/file-demo'

You may also generate the application through Spring Initializr web tool by following the instructions below –

  1. Open http://start.spring.io
  2. Enter file-demo in the “Artifact” field.
  3. Add Web in the dependencies section.
  4. Click Generate to generate and download the project.

That’s it! You may now unzip the downloaded application archive and import it into your favorite IDE.

Configuring Server and File Storage Properties

First thing First! Let’s configure our Spring Boot application to enable Multipart file uploads, and define the maximum file size that can be uploaded. We’ll also configure the directory into which all the uploaded files will be stored.

Open src/main/resources/application.properties file, and add the following properties to it –

## MULTIPART (MultipartProperties)
# Enable multipart uploads
spring.servlet.multipart.enabled=true
# Threshold after which files are written to disk.
spring.servlet.multipart.file-size-threshold=2KB
# Max file size.
spring.servlet.multipart.max-file-size=200MB
# Max Request Size
spring.servlet.multipart.max-request-size=215MB

## File Storage Properties
# All files uploaded through the REST API will be stored in this directory
file.upload-dir=/Users/folder/uploads

Note: Please change the file.upload-dir property to the path where you want the uploaded files to be stored.

Automatically binding properties to a POJO class

Spring Boot has an awesome feature called @ConfigurationProperties using which you can automatically bind the properties defined in the application.properties file to a POJO class.

package com.example.filedemo.property;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
    private String uploadDir;

    public String getUploadDir() {
        return uploadDir;
    }

    public void setUploadDir(String uploadDir) {
        this.uploadDir = uploadDir;
    }
}

The @ConfigurationProperties(prefix = "file") annotation does its job on application startup and binds all the properties with prefix file to the corresponding fields of the POJO class.

If you define additional file properties in future, you may simply add a corresponding field in the above class, and spring boot will automatically bind the field with the property value.

Enable Configuration Properties

Now, To enable the ConfigurationProperties feature, you need to add @EnableConfigurationProperties annotation to any configuration class.

Open the main class src/main/java/com/example/filedemo/FileDemoApplication.java, and add the @EnableConfigurationProperties annotation to it like so –

package com.example.filedemo;

import com.example.filedemo.property.FileStorageProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties({
        FileStorageProperties.class
})
public class FileDemoApplication {

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

Writing APIs for File Upload and Download

Let’s now write the REST APIs for uploading and downloading files. Create a new controller class called FileController inside com.example.filedemo.controller package.

Here is the complete code for FileController –

package com.example.filedemo.controller;

import com.example.filedemo.payload.UploadFileResponse;
import com.example.filedemo.service.FileStorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@RestController
public class FileController {

    private static final Logger logger = LoggerFactory.getLogger(FileController.class);

    @Autowired
    private FileStorageService fileStorageService;
    
    @PostMapping("/uploadFile")
    public UploadFileResponse uploadFile(@RequestParam("file") MultipartFile file) {
        String fileName = fileStorageService.storeFile(file);

        String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                .path("/downloadFile/")
                .path(fileName)
                .toUriString();

        return new UploadFileResponse(fileName, fileDownloadUri,
                file.getContentType(), file.getSize());
    }

    @PostMapping("/uploadMultipleFiles")
    public List<UploadFileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
        return Arrays.asList(files)
                .stream()
                .map(file -> uploadFile(file))
                .collect(Collectors.toList());
    }

    @GetMapping("/downloadFile/{fileName:.+}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
        // Load file as Resource
        Resource resource = fileStorageService.loadFileAsResource(fileName);

        // Try to determine file's content type
        String contentType = null;
        try {
            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        } catch (IOException ex) {
            logger.info("Could not determine file type.");
        }

        // Fallback to the default content type if type could not be determined
        if(contentType == null) {
            contentType = "application/octet-stream";
        }

        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    }
}

The FileController class uses FileStorageService for storing files in the file system and retrieving them. It returns a payload of type UploadFileResponse after the upload is completed. Let’s define these classes one by one.

UploadFileResponse

As the name suggests, this class is used to return the response from the /uploadFile and /uploadMultipleFiles APIs.

Create UploadFileResponse class inside com.example.filedemo.payload package with the following contents –

package com.example.filedemo.payload;

public class UploadFileResponse {
    private String fileName;
    private String fileDownloadUri;
    private String fileType;
    private long size;

    public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
        this.fileName = fileName;
        this.fileDownloadUri = fileDownloadUri;
        this.fileType = fileType;
        this.size = size;
    }

	// Getters and Setters (Omitted for brevity)
}

Service for Storing Files in the FileSystem and retrieving them

Let’s now write the service for storing files in the file system and retrieving them. Create a new class called FileStorageService.java inside com.example.filedemo.service package with the following contents –

package com.example.filedemo.service;

import com.example.filedemo.exception.FileStorageException;
import com.example.filedemo.exception.MyFileNotFoundException;
import com.example.filedemo.property.FileStorageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

@Service
public class FileStorageService {

    private final Path fileStorageLocation;

    @Autowired
    public FileStorageService(FileStorageProperties fileStorageProperties) {
        this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
                .toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (Exception ex) {
            throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
        }
    }

    public String storeFile(MultipartFile file) {
        // Normalize file name
        String fileName = StringUtils.cleanPath(file.getOriginalFilename());

        try {
            // Check if the file's name contains invalid characters
            if(fileName.contains("..")) {
                throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
            }

            // Copy file to the target location (Replacing existing file with the same name)
            Path targetLocation = this.fileStorageLocation.resolve(fileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

            return fileName;
        } catch (IOException ex) {
            throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
        }
    }

    public Resource loadFileAsResource(String fileName) {
        try {
            Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if(resource.exists()) {
                return resource;
            } else {
                throw new MyFileNotFoundException("File not found " + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new MyFileNotFoundException("File not found " + fileName, ex);
        }
    }
}

Exception Classes

The FileStorageService class throws some exceptions in case of unexpected situations. Following are the definitions of those exception classes (All the exception classes go inside the package com.example.filedemo.exception).

1. FileStorageException

It’s thrown when an unexpected situation occurs while storing a file in the file system –

package com.example.filedemo.exception;

public class FileStorageException extends RuntimeException {
    public FileStorageException(String message) {
        super(message);
    }

    public FileStorageException(String message, Throwable cause) {
        super(message, cause);
    }
}

2. MyFileNotFoundException

It’s thrown when a file that the user is trying to download is not found.

package com.example.filedemo.exception;

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

@ResponseStatus(HttpStatus.NOT_FOUND)
public class MyFileNotFoundException extends RuntimeException {
    public MyFileNotFoundException(String message) {
        super(message);
    }

    public MyFileNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

Note that, we’ve annotated the above exception class with @ResponseStatus(HttpStatus.NOT_FOUND). This ensures that Spring boot responds with a 404 Not Found status when this exception is thrown.

Running the Application and Testing the APIs via Postman

We’re done developing our backend APIs. Let’s run the application and test the APIs via Postman. Type the following command from the root directory of the project to run the application –

mvn spring-boot:run

Once started, the application can be accessed at http://localhost:8080.

1. Upload File

Spring Boot File Upload Rest API Example

2. Upload Multiple Files

Spring Boot Upload Multiple Files Rest API Example

3. Download File

Spring Boot Download File Rest API Example

Developing the Front End

Our backend APIs are working fine. Let’s now write the front end code to let users upload and download files from our web app.

All the front end files will go inside src/main/resources/static folder. Following is the directory structure of our front-end code –

static
  └── css
       └── main.css
  └── js
       └── main.js
  └── index.html 

A bit of HTML

<!DOCTYPE html>
<html>
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
        <title>Spring Boot File Upload / Download Rest API Example</title>
        <link rel="stylesheet" href="/css/main.css" />
    </head>
    <body>
        <noscript>
            <h2>Sorry! Your browser doesn't support Javascript</h2>
        </noscript>
        <div class="upload-container">
            <div class="upload-header">
                <h2>Spring Boot File Upload / Download Rest API Example</h2>
            </div>
            <div class="upload-content">
                <div class="single-upload">
                    <h3>Upload Single File</h3>
                    <form id="singleUploadForm" name="singleUploadForm">
                        <input id="singleFileUploadInput" type="file" name="file" class="file-input" required />
                        <button type="submit" class="primary submit-btn">Submit</button>
                    </form>
                    <div class="upload-response">
                        <div id="singleFileUploadError"></div>
                        <div id="singleFileUploadSuccess"></div>
                    </div>
                </div>
                <div class="multiple-upload">
                    <h3>Upload Multiple Files</h3>
                    <form id="multipleUploadForm" name="multipleUploadForm">
                        <input id="multipleFileUploadInput" type="file" name="files" class="file-input" multiple required />
                        <button type="submit" class="primary submit-btn">Submit</button>
                    </form>
                    <div class="upload-response">
                        <div id="multipleFileUploadError"></div>
                        <div id="multipleFileUploadSuccess"></div>
                    </div>
                </div>
            </div>
        </div>
        <script src="/js/main.js" ></script>
    </body>
</html>

Some Javascript

'use strict';

var singleUploadForm = document.querySelector('#singleUploadForm');
var singleFileUploadInput = document.querySelector('#singleFileUploadInput');
var singleFileUploadError = document.querySelector('#singleFileUploadError');
var singleFileUploadSuccess = document.querySelector('#singleFileUploadSuccess');

var multipleUploadForm = document.querySelector('#multipleUploadForm');
var multipleFileUploadInput = document.querySelector('#multipleFileUploadInput');
var multipleFileUploadError = document.querySelector('#multipleFileUploadError');
var multipleFileUploadSuccess = document.querySelector('#multipleFileUploadSuccess');

function uploadSingleFile(file) {
    var formData = new FormData();
    formData.append("file", file);

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/uploadFile");

    xhr.onload = function() {
        console.log(xhr.responseText);
        var response = JSON.parse(xhr.responseText);
        if(xhr.status == 200) {
            singleFileUploadError.style.display = "none";
            singleFileUploadSuccess.innerHTML = "<p>File Uploaded Successfully.</p><p>DownloadUrl : <a href='" + response.fileDownloadUri + "' target='_blank'>" + response.fileDownloadUri + "</a></p>";
            singleFileUploadSuccess.style.display = "block";
        } else {
            singleFileUploadSuccess.style.display = "none";
            singleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
        }
    }

    xhr.send(formData);
}

function uploadMultipleFiles(files) {
    var formData = new FormData();
    for(var index = 0; index < files.length; index++) {
        formData.append("files", files[index]);
    }

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/uploadMultipleFiles");

    xhr.onload = function() {
        console.log(xhr.responseText);
        var response = JSON.parse(xhr.responseText);
        if(xhr.status == 200) {
            multipleFileUploadError.style.display = "none";
            var content = "<p>All Files Uploaded Successfully</p>";
            for(var i = 0; i < response.length; i++) {
                content += "<p>DownloadUrl : <a href='" + response[i].fileDownloadUri + "' target='_blank'>" + response[i].fileDownloadUri + "</a></p>";
            }
            multipleFileUploadSuccess.innerHTML = content;
            multipleFileUploadSuccess.style.display = "block";
        } else {
            multipleFileUploadSuccess.style.display = "none";
            multipleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
        }
    }

    xhr.send(formData);
}


singleUploadForm.addEventListener('submit', function(event){
    var files = singleFileUploadInput.files;
    if(files.length === 0) {
        singleFileUploadError.innerHTML = "Please select a file";
        singleFileUploadError.style.display = "block";
    }
    uploadSingleFile(files[0]);
    event.preventDefault();
}, true);


multipleUploadForm.addEventListener('submit', function(event){
    var files = multipleFileUploadInput.files;
    if(files.length === 0) {
        multipleFileUploadError.innerHTML = "Please select at least one file";
        multipleFileUploadError.style.display = "block";
    }
    uploadMultipleFiles(files);
    event.preventDefault();
}, true);

The above code is self explanatory. I’m using XMLHttpRequest along with FormData object to upload file(s) as multipart/form-data.

And some CSS

* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

body {
    margin: 0;
    padding: 0;
    font-weight: 400;
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    font-size: 1rem;
    line-height: 1.58;
    color: #333;
    background-color: #f4f4f4;
}

body:before {
    height: 50%;
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    background: #128ff2;
    content: "";
    z-index: 0;
}

.clearfix:after {
    display: block;
    content: "";
    clear: both;
}


h1, h2, h3, h4, h5, h6 {
    margin-top: 20px;
    margin-bottom: 20px;
}

h1 {
    font-size: 1.7em;
}

a {
    color: #128ff2;
}

button {
    box-shadow: none;
    border: 1px solid transparent;
    font-size: 14px;
    outline: none;
    line-height: 100%;
    white-space: nowrap;
    vertical-align: middle;
    padding: 0.6rem 1rem;
    border-radius: 2px;
    transition: all 0.2s ease-in-out;
    cursor: pointer;
    min-height: 38px;
}

button.primary {
    background-color: #128ff2;
    box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
    color: #fff;
}

input {
    font-size: 1rem;
}

input[type="file"] {
    border: 1px solid #128ff2;
    padding: 6px;
    max-width: 100%;
}

.file-input {
    width: 100%;
}

.submit-btn {
    display: block;
    margin-top: 15px;
    min-width: 100px;
}

@media screen and (min-width: 500px) {
    .file-input {
        width: calc(100% - 115px);
    }

    .submit-btn {
        display: inline-block;
        margin-top: 0;
        margin-left: 10px;
    }
}

.upload-container {
      max-width: 700px;
      margin-left: auto;
      margin-right: auto;
      background-color: #fff;
      box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
      margin-top: 60px;
      min-height: 400px;
      position: relative;
      padding: 20px;
}

.upload-header {
    border-bottom: 1px solid #ececec;
}

.upload-header h2 {
    font-weight: 500;
}

.single-upload {
    padding-bottom: 20px;
    margin-bottom: 20px;
    border-bottom: 1px solid #e8e8e8;
}

.upload-response {
    overflow-x: hidden;
    word-break: break-all;
}

If you use JQuery

If you prefer using jQuery instead of vanilla javascript, then you can upload files using jQuery.ajax() method like so –

$('#singleUploadForm').submit(function(event) {
    var formElement = this;
    // You can directly create form data from the form element
    // (Or you could get the files from input element and append them to FormData as we did in vanilla javascript)
    var formData = new FormData(formElement);

    $.ajax({
        type: "POST",
        enctype: 'multipart/form-data',
        url: "/uploadFile",
        data: formData,
        processData: false,
        contentType: false,
        success: function (response) {
            console.log(response);
            // process response
        },
        error: function (error) {
            console.log(error);
            // process error
        }
    });

    event.preventDefault();
});

Conclusion

All right folks! In this article, we learned how to upload single as well as multiple files via REST APIs written in Spring Boot. We also learned how to download files in Spring Boot. Finally, we wrote code to upload files by calling the APIs through javascript.

Thank you for reading! See you in the next post!

Spring Boot Database Migrations with Flyway
05
Mar
2021

Spring Boot Database Migrations with Flyway

Nothing is perfect and complete when it is built the first time. The database schema of your brand new application is no exception. It is bound to change over time when you try to fit new requirements or add new features.

Flyway is a tool that lets you version control incremental changes to your database so that you can migrate it to a new version easily and confidently.

In this article, you’ll learn how to use Flyway in Spring Boot applications to manage changes to your database.

We’ll build a simple Spring Boot application with MySQL Database & Spring Data JPA, and learn how to integrate Flyway in the app.

Let’s get started!

Creating the Application

Let’s use Spring Boot CLI to generate the application. Fire up your terminal and type the following command to generate the project.

spring init --name=flyway-demo --dependencies=web,mysql,data-jpa,flyway flyway-demo

Once the project is generated, import it into your favorite IDE. The directory structure of the application would look like this –

Spring Boot Flyway Database Migration Example Directory Structure

Configuring MySQL and Hibernate

First create a new MySQL database named flyway_demo.

Then Open src/main/application.properties file and add the following properties to it –

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://localhost:3306/flyway_demo?useSSL=false
spring.datasource.username = root
spring.datasource.password = root

## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

## This is important
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = validate

Please change spring.datasource.username and spring.datasource.password as per your MySQL installation.

The property spring.jpa.hibernate.ddl-auto is important. It tries to validate the database schema according to the entities that you have created in the application and throws an error if the schema doesn’t match the entity specifications.

Creating a Domain Entity

Let’s create a simple Entity in our application so that we can create and test flyway migration for this entity.

First, Create a new package called domain inside com.example.flywaydemo package. Then, create the following User.java file inside com.example.flywaydemo.domain package –

package com.example.flywaydemo.domain;

import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Column(unique = true)
    @Size(min = 1, max = 100)
    private String username;

    @NotBlank
    @Size(max = 50)
    private String firstName;

    @Size(max = 50)
    private String lastName;

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    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;
    }
}

Creating a Flyway Migration script

Flyway tries to read database migration scripts from classpath:db/migration folder by default.

All the migration scripts must follow a particular naming convention – V<VERSION_NUMBER>__<NAME>.sql. Checkout the Official Flyway documentation to learn more about naming convention.

Let’s create our very first database migration script. First, Create the db/migration folder inside src/main/resources directory –

mkdir -p src/main/resources/db/migration

Now, create a new file named V1__init.sql inside src/main/resources/db/migration directory and add the following sql scripts –

CREATE TABLE users (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  username varchar(100) NOT NULL,
  first_name varchar(50) NOT NULL,
  last_name varchar(50) DEFAULT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY UK_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Running the Application

When you run the application, flyway will automatically check the current database version and apply any pending migrations.

Run the app by typing the following command in terminal –

mvn spring-boot:run

When running the app for the first time, you’ll see the following logs pertaining to Flyway, which says that it has migrated the schema to version 1 – init.

Flyway Migration Logs

How does Flyway manage migrations?

Flyway creates a table called flyway_schema_history when it runs the migration for the first time and stores all the meta-data required for versioning the migrations in this table.

mysql> show tables;
+-----------------------+
| Tables_in_flyway_demo |
+-----------------------+
| flyway_schema_history |
| users                 |
+-----------------------+
2 rows in set (0.00 sec)

You can check the contents of flyway_schema_history table in your mysql database –

mysql> select * from flyway_schema_history;
+----------------+---------+-------------+------+--------------+------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description | type | script       | checksum   | installed_by | installed_on        | execution_time | success |
+----------------+---------+-------------+------+--------------+------------+--------------+---------------------+----------------+---------+
|              1 | 1       | init        | SQL  | V1__init.sql | 1952043475 | root         | 2018-03-06 11:25:58 |             16 |       1 |
+----------------+---------+-------------+------+--------------+------------+--------------+---------------------+----------------+---------+
1 row in set (0.00 sec)

It stores the current migration’s version, script file name, and checksum among other details in the table.

When you run the app, Flyway first validates the already applied migration scripts by calculating their checksum and matching it with the checksum stored in the meta-data table.

So, If you change V1__init.sql after the migration is applied, Flyway will throw an error saying that the checksum doesn’t match.

Therefore, for doing any changes to the schema, you need to create another migration script file with a new version V2__<NAME>.sql and let Flyway apply that when you run the app.

Adding multiple migrations

Let’s create another migration script and see how flyway migrates the database to the new version automatically.

Create a new script V2__testdata.sql inside src/main/resources/db/migration with the following contents –

INSERT INTO users(username, first_name, last_name) 
VALUES('fusebes', 'Yaniv', 'Levy');
INSERT INTO users(username, first_name, last_name) VALUES('flywaytest', 'Flyway', 'Test');

If you run the app now, flyway will detect the new migration script, and migrate the database to this version.

Open mysql and check the users table. You’ll see that the above two entries are automatically created in the users table –

mysql> select * from users;
+----+------------+-----------+------------+
| id | first_name | last_name | username   |
+----+------------+-----------+------------+
|  4 | Yaniv      | Levy      | fusebes    |
|  5 | Flyway     | Test      | flywaytest |
+----+------------+-----------+------------+
2 rows in set (0.01 sec)

Also, Flyway stores this new schema version in its meta-data table –

mysql> select * from flyway_schema_history;
+----------------+---------+-------------+------+------------------+-------------+--------------+---------------------+----------------+---------+
| installed_rank | version | description | type | script           | checksum    | installed_by | installed_on        | execution_time | success |
+----------------+---------+-------------+------+------------------+-------------+--------------+---------------------+----------------+---------+
|              1 | 1       | init        | SQL  | V1__init.sql     |  1952043475 | root         | 2018-03-06 11:25:58 |             16 |       1 |
|              2 | 2       | testdata    | SQL  | V2__testdata.sql | -1926058189 | root         | 2018-03-06 11:25:58 |              6 |       1 |
+----------------+---------+-------------+------+------------------+-------------+--------------+---------------------+----------------+---------+
2 rows in set (0.00 sec)

Conclusion

That’s all folks! In this article, You learned how to integrate Flyway in a Spring Boot application for versioning database changes.

Thank you for reading. See you in the next blog post.

Spring Boot2 @SpringBootApplication Auto Configuration
30
Mar
2021

Spring Boot 2 @SpringBootApplication Auto Configuration

Spring boot is very easy to use and it does a lot of things under the hood, you might not be aware of. In future, a good developer will be who will know exactly what is going on behind spring boot auto configuration, how to use it in your favor and how to disable certain sections which you do not want into your project.

To understand most basic things behind spring boot, we will create a minimum boot application with single dependency and single launch class file. We will then analyze the startup logs to get the insights.

Create Spring boot application with launch class

  1. Create a new maven project in eclipse with archetype “maven-archetype-quickstart“.
  2. Update pom.xml file with spring-boot-starter-web dependency and plugin information.<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.fusebes</groupId><artifactId>springbootdemo</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging> <name>springbootdemo</name><url>http://maven.apache.org</url> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent> <properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies> <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build> <repositories><repository><id>repository.spring.release</id><name>Spring GA Repository</name><url>http://repo.spring.io/release</url></repository></repositories></project>
  3. Create launch application.package com.fusebes.demo; import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ApplicationContext; @SpringBootApplicationpublic class App {public static void main(String[] args) {ApplicationContext ctx = SpringApplication.run(App.class, args);}}What this launch class does?Above class is called spring boot application launch class. It used to Bootstrap and launch a Spring application from a Java main() method. It typically does following things –
    • Create an instance of Spring’s ApplicationContext.
    • Enable the functionality to accept command-line arguments and expose them as Spring properties.
    • Load all the Spring beans as per the configuration. You can do other operations as well as per project need arises.

@SpringBootApplication Annotation

This annotation is a shortcut of applying 3 annotations in one statement –

  1. @SpringBootConfiguration@SpringBootConfiguration is new annotation in Spring boot 2. Previously, we have been using @Configuration annotation. You can use @Configuration in place of this. Both are same thing.It indicates that a class provides Spring Boot application @Configuration. It simply means that annotated class is a configuration class and shall be scanned for further configurations and bean definitions.
  2. @EnableAutoConfigurationThis annotation is used to enable auto-configuration of the Spring Application Context, attempting to guess and configure beans that you are likely to need. Auto-configuration classes are usually applied based on your classpath and what beans you have defined.Auto-configuration tries to be as intelligent as possible and will back-away as you define more of your own configuration. You can always manually exclude any configuration that you never want to apply using two methods –i) Use excludeName()
    ii) Using the spring.autoconfigure.exclude property in properties file. e.g.@EnableAutoConfiguration(excludeName = {"multipartResolver","mbeanServer"})Auto-configuration is always applied after user-defined beans have been registered.
  3. @ComponentScanThis annotation provides support parallel with Spring XML’s context:component-scan element.Either basePackageClasses() or basePackages() may be specified to define specific packages to scan. If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.

Run the launch application and check logs

Let’s start running it with the simplest option–running as a Java application. In your IDE, right-click on the application class and run it as Java Application. For getting insight of registered beans, I have added modified the launch application as below.

@SpringBootApplicationpublic class App {public static void main(String[] args) {ApplicationContext ctx = SpringApplication.run(App.class, args); String[] beanNames = ctx.getBeanDefinitionNames(); Arrays.sort(beanNames); for (String beanName : beanNames) {System.out.println(beanName);}}}

Now see the logs –

.   ____          _            __ _ _/\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/  ___)| |_)| | | | | || (_| |  ) ) ) )'  |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot ::        (v2.0.0.RELEASE) 2018-04-02 13:09:41.100  INFO 11452 --- [           main] com.fusebes.demo.App               : Starting App on FFC15B4E9C5AA with PID 11452 (C:\Users\zkpkhua\IDPPaymentTransfers_Integrated\springbootdemo\target\classes started by zkpkhua in C:\Users\zkpkhua\IDPPaymentTransfers_Integrated\springbootdemo)2018-04-02 13:09:41.108  INFO 11452 --- [           main] com.fusebes.demo.App               : No active profile set, falling back to default profiles: default2018-04-02 13:09:41.222  INFO 11452 --- [           main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@4450d156: startup date [Mon Apr 02 13:09:41 IST 2018]; root of context hierarchy2018-04-02 13:09:43.474  INFO 11452 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)2018-04-02 13:09:43.526  INFO 11452 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]2018-04-02 13:09:43.526  INFO 11452 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.282018-04-02 13:09:43.748  INFO 11452 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext2018-04-02 13:09:43.748  INFO 11452 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2531 ms2018-04-02 13:09:43.964  INFO 11452 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]2018-04-02 13:09:43.969  INFO 11452 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]2018-04-02 13:09:43.970  INFO 11452 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]2018-04-02 13:09:43.970  INFO 11452 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]2018-04-02 13:09:43.970  INFO 11452 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]2018-04-02 13:09:44.480  INFO 11452 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@4450d156: startup date [Mon Apr 02 13:09:41 IST 2018]; root of context hierarchy2018-04-02 13:09:44.627  INFO 11452 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)2018-04-02 13:09:44.630  INFO 11452 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)2018-04-02 13:09:44.681  INFO 11452 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2018-04-02 13:09:44.682  INFO 11452 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2018-04-02 13:09:44.747  INFO 11452 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2018-04-02 13:09:45.002  INFO 11452 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup2018-04-02 13:09:45.070  INFO 11452 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''2018-04-02 13:09:45.076  INFO 11452 --- [           main] com.fusebes.demo.App               : Started App in 4.609 seconds (JVM running for 5.263)appbasicErrorControllerbeanNameHandlerMappingbeanNameViewResolvercharacterEncodingFilterconventionErrorViewResolverdefaultServletHandlerMappingdefaultValidatordefaultViewResolverdispatcherServletdispatcherServletRegistrationerrorerrorAttributeserrorPageCustomizererrorPageRegistrarBeanPostProcessorfaviconHandlerMappingfaviconRequestHandlerhandlerExceptionResolverhiddenHttpMethodFilterhttpPutFormContentFilterhttpRequestHandlerAdapterjacksonCodecCustomizerjacksonObjectMapperjacksonObjectMapperBuilderjsonComponentModulelocaleCharsetMappingsCustomizermappingJackson2HttpMessageConvertermbeanExportermbeanServermessageConvertersmethodValidationPostProcessormultipartConfigElementmultipartResolvermvcContentNegotiationManagermvcConversionServicemvcHandlerMappingIntrospectormvcPathMatchermvcResourceUrlProvidermvcUriComponentsContributormvcUrlPathHelpermvcValidatormvcViewResolverobjectNamingStrategyorg.springframework.boot.autoconfigure.AutoConfigurationPackagesorg.springframework.boot.autoconfigure.condition.BeanTypeRegistryorg.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfigurationorg.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfigurationorg.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfigurationorg.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfigurationorg.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfigurationorg.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfigurationorg.springframework.boot.autoconfigure.http.codec.CodecsAutoConfigurationorg.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration$JacksonCodecConfigurationorg.springframework.boot.autoconfigure.info.ProjectInfoAutoConfigurationorg.springframework.boot.autoconfigure.internalCachingMetadataReaderFactoryorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfigurationorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfigurationorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfigurationorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfigurationorg.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfigurationorg.springframework.boot.autoconfigure.jmx.JmxAutoConfigurationorg.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfigurationorg.springframework.boot.autoconfigure.validation.ValidationAutoConfigurationorg.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfigurationorg.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfigurationorg.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfigurationorg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfigurationorg.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfigurationorg.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcatorg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfigurationorg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapterorg.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfigurationorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfigurationorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfigurationorg.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfigurationorg.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfigurationorg.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfigurationorg.springframework.boot.context.properties.ConfigurationBeanFactoryMetadataorg.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessororg.springframework.context.annotation.internalAutowiredAnnotationProcessororg.springframework.context.annotation.internalCommonAnnotationProcessororg.springframework.context.annotation.internalConfigurationAnnotationProcessororg.springframework.context.annotation.internalRequiredAnnotationProcessororg.springframework.context.event.internalEventListenerFactoryorg.springframework.context.event.internalEventListenerProcessorparameterNamesModulepreserveErrorControllerTargetClassPostProcessorpropertySourcesPlaceholderConfigurerrequestContextFilterrequestMappingHandlerAdapterrequestMappingHandlerMappingresourceHandlerMappingrestTemplateBuilderserver-org.springframework.boot.autoconfigure.web.ServerPropertiesservletWebServerFactoryCustomizersimpleControllerHandlerAdapterspring.http.encoding-org.springframework.boot.autoconfigure.http.HttpEncodingPropertiesspring.info-org.springframework.boot.autoconfigure.info.ProjectInfoPropertiesspring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonPropertiesspring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcPropertiesspring.resources-org.springframework.boot.autoconfigure.web.ResourcePropertiesspring.security-org.springframework.boot.autoconfigure.security.SecurityPropertiesspring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartPropertiesstandardJacksonObjectMapperBuilderCustomizerstringHttpMessageConvertertomcatServletWebServerFactorytomcatServletWebServerFactoryCustomizertomcatWebServerFactoryCustomizerviewControllerHandlerMappingviewResolverwebServerFactoryCustomizerBeanPostProcessorwebsocketContainerCustomizerwelcomePageHandlerMapping

You see how many beans got registered automatically. That’s beauty of spring boot. If you want to dig deeper into why any particular bean got registered? You can see that by putting a debug flag at application startup.

Simply pass -Ddebug=true as VM argument.

Now when you run the application, you will get lots of debug logs having similar information :

CodecsAutoConfiguration.JacksonCodecConfiguration matched:- @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition) CodecsAutoConfiguration.JacksonCodecConfiguration#jacksonCodecCustomizer matched:- @ConditionalOnBean (types: com.fasterxml.jackson.databind.ObjectMapper; SearchStrategy: all) found bean 'jacksonObjectMapper' (OnBeanCondition) DispatcherServletAutoConfiguration.DispatcherServletConfiguration matched:- @ConditionalOnClass found required class 'javax.servlet.ServletRegistration'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)- Default DispatcherServlet did not find dispatcher servlet beans (DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition) DispatcherServletAutoConfiguration.DispatcherServletRegistrationConfiguration matched:- @ConditionalOnClass found required class 'javax.servlet.ServletRegistration'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)- DispatcherServlet Registration did not find servlet registration bean (DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition) .........

Above logs tell why a particular bean was registered into spring context. This information is very useful when you debug the issues with auto configutation.

Similarily, everytime we add a new dependency to a Spring Boot project, Spring Boot auto-configuration automatically tries to configure the beans based on the dependency.

I hope that information discussed above will help you in future while debugging spring boot related issues.

Happy Learning !!

How to Change the Default Port in Spring Boot
26
Mar
2021

How to Change the Default Port in Spring Boot

Spring Boot provides sensible defaults for many configuration properties. But we sometimes need to customize these with our case-specific values.

And a common use case is changing the default port for the embedded server.

In this quick tutorial, we’ll cover several ways to achieve this.

2. Using Property Files

The fastest and easiest way to customize Spring Boot is by overriding the values of the default properties.

For the server port, the property we want to change is server.port.

By default, the embedded server starts on port 8080.

So, let’s see how to provide a different value in an application.properties file:

server.port=8081

Now the server will start on port 8081.

And we can do the same if we’re using an application.yml file:

server:
  port : 8081

Both files are loaded automatically by Spring Boot if placed in the src/main/resources directory of a Maven application.

2.1. Environment-Specific Ports

If we have an application deployed in different environments, we may want it to run on different ports on each system.

We can easily achieve this by combining the property files approach with Spring profiles. Specifically, we can create a property file for each environment.

For example, we’ll have an application-dev.properties file with this content:

server.port=8081

Then we’ll add another application-qa.properties file with a different port:

server.port=8082

Now, the property files configuration should be sufficient for most cases. However, there are other options for this goal, so let’s explore them as well.

3. Programmatic Configuration

We can configure the port programmatically either by setting the specific property when starting the application or by customizing the embedded server configuration.

First, let’s see how to set the property in the main @SpringBootApplication class:

@SpringBootApplication
public class CustomApplication {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(CustomApplication.class);
        app.setDefaultProperties(Collections
          .singletonMap("server.port", "8083"));
        app.run(args);
    }
}

Next, to customize the server configuration, we have to implement the WebServerFactoryCustomizer interface:

@Component
public class ServerPortCustomizer 
  implements WebServerFactoryCustomizer<ConfigurableWebServerFactory> {
 
    @Override
    public void customize(ConfigurableWebServerFactory factory) {
        factory.setPort(8086);
    }
}

Note that this applies to the Spring Boot 2.x version.

For Spring Boot 1.x, we can similarly implement the EmbeddedServletContainerCustomizer interface.

4. Using Command-Line Arguments

When packaging and running our application as a jar, we can set the server.port argument with the java command:

java -jar spring-5.jar --server.port=8083

or by using the equivalent syntax:

java -jar -Dserver.port=8083 spring-5.jar

5. Order of Evaluation

As a final note, let’s look at the order in which these approaches are evaluated by Spring Boot.

Basically, the configurations priority is

  • embedded server configuration
  • command-line arguments
  • property files
  • main @SpringBootApplication configuration

6. Conclusion

In this article, we saw how to configure the server port in a Spring Boot application.

A Guide on How to Write a Clean Code
18
Mar
2021

A Guide on How to Write a Clean Code

In this article, I’m going to talk about writing clean code in general and then end up with some examples. As an Android developer having a clean code has always been challenging for me which needs lots of effort and that’s just coding and coding.

Rules We Should Follow

  1. Ignore duplication (imply DRY principle-Don’t Repeat Yourself)
  2. Minimize entities, classes, and functions (avoid repetition)
  3. It should be readable and simple
  4. Be testable as it makes your code flexible and maintainable
  5. Follow SOLIDprinciples

S = Single-responsibility principle: A class should only have one purpose.

O = Open-closed principleA class should be open for extension, but closed for modification.

L = Liskov substitution principleAbstraction should be able to provide all needs of child class.

I = Interface segregation principleSmall interface is better than big one.

D = Dependency Inversion Principle: A class should depend on abstraction, not implementation.

6. Be careful with dependencies

As many as possible try to have one-directional dependency. When dependency goes in multiple directions, things get much more complicated which makes hard to update and change.

7. Don’t hardcode

Define constant or use variables instead of hardcoding the values which will not only help readability but also make it easy to change if it is being used at multiple places.

Now let’s start writing Clean Code 🚀 …

Valid Names

Naming is one of the hardest parts of programming and may take time to choose, but it is a strong way to convey your code’s intent to other developers who read them in the future which must show the purpose of that class, function, or variable and even tell what is going to do. Don’t append prefixes or type information. I always have a checklist in my mind as listed below which assures me that a name has been chosen well:

  • Are the variables named according to the convention (camelCase, PascalCase, etc)?.
  • Does the name have an appropriate length to ensure that no one will be confused by it?.
  • Are the name of variables clear about what they hold?.
  • Are the names meaningful, searchable, and easy to pronounce?.

Class names should not only be names (not verbs) but also should have the PascalCase convention. On the other hand, method names should be verbs or phrase verbs and follow the camelCase convention. The same rules apply to variable names.

Functions and Methods

Following S from SOLID principles, let functions and methods perform only one task which should be small. If the function arguments are too many, you should review your code and maybe pack them into an object or divide that task between some other functions. Prefer possible exceptions to return error codes and extract error handling try catch into their own function.

Comments

We should use comments only when it is necessary, not to explain bad code. Writing lengthy comments will not help us in changing our code into a clean one. If the code is bad, we should solve it by improving the code, not by adding instructions on how to use it, but it doesn’t mean that you shouldn’t use comments at all, sometimes it is important such as dealing with third party APIs where you need to explain some behavior.

Code Appearance

It might look unimportant at first glance, but it is of high importance to write your code with a well-organized format which makes it readable such as:

  • Do not write everything in a single line. Give proper whitespace, indentation, or line breaks in your code.

Indentation styles assist in identifying control flow and blocks of code. In some programming languages, indentation is used to delimit logical blocks of code; correct indentation in these cases is more than a matter of style. In other languages, indentation and white space do not affect function, although logical and consistent indentation makes code more readable. (Inspired by WIKIPEDIA)

  • Instance variables should be declared at the top of the class.
  • If functions are calling each other (dependent functions) they should be close while putting the caller at first.
  • It is often helpful to align similar elements vertically, to make typo-generated bugs more obvious.

Tests

The importance of Test code is equal to producing the code and of course, while writing tests you might find some bugs. However, it can sometimes be quite difficult to write a good test for a particular piece of code which is a result of a badly designed and untestable code. Testing is almost like coding for the second time so follow all the previous rules for writing a good test as well which should be as per below :

  • One assert and single concept per test
  • Easy to write and readable (simplicity)
  • Fast
  • Independent

To conclude, I hope this article could be helpful to write a better code although it is important to keep practicing and learning in this field 😃 …

Convert a String to Date (LocalDate, LocalDateTime, ZonedDateTime, LocalTime) in Java
03
Mar
2021

Convert a String to Date (LocalDate, LocalDateTime, ZonedDateTime, LocalTime) in Java

In this article, you’ll find several examples demonstrating how to convert a date represented as a String to a Date, LocalDate, LocalDateTime, ZonedDateTime, or LocalTime instance in Java.

How to convert a String to Date in Java using SimpleDateFormat

Dates are represented as Strings by using some patterns and symbols. Java’s SimpleDateFormat class uses the following patterns and symbols for parsing a String to Date.

SimpleDateFormat Date and Time Patterns

Let’s see an example:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatExample {
    public static void main(String[] args) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = "2020-01-31";

        try {
            // Parsing a String to Date
            Date date = dateFormatter.parse(dateStr);
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

Here is another example in which we parse a more complex date time representation:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatExample {
    public static void main(String[] args) {
        SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("E, MMM dd yyyy, hh:mm:ss a");
        String dateTimeStr = "Fri, Jan 31 2020, 10:30:45 PM";

        try {
            // Parse the String representation of date and time to Date
            Date date = dateTimeFormatter.parse(dateTimeStr);
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

Convert/Parse a String to LocalDate

We can also use the DateTime API introduced in Java 8 to convert a String to an instance of various DateTime classes like LocalDate, LocalTime, LocalDateTime, ZonedDateTime etc.

The DateTime API has a DateTimeFormatter class that can be used to define the date time format and parse the String according to the specified date time format. The DateTimeFormatter class uses the following patterns and symbols:

DateTimeFormatter Patterns and Symbols

Let’s see an example of parsing a String to LocalDate:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class LocalDateParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO Date format (yyyy-MM-dd) to LocalDate
        LocalDate date1 = LocalDate.parse("2020-02-28");
        System.out.println(date1);

        // Parse a String in a custom date format to LocalDate using DateTimeFormatter
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        LocalDate date2 = LocalDate.parse("28/02/2020", dateFormatter);
        System.out.println(date2);

        // Parse a String in a custom date-time format to LocalDate using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("E, MMM dd yyyy, hh:mm:ss a");
        LocalDate date3 = LocalDate.parse("Fri, Feb 14 2020, 10:20:50 PM", dateTimeFormatter);
        System.out.println(date3);

    }
}

Convert/Parse a String to LocalDateTime

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO DateTime format (yyyy-MM-ddTHH:mm:ss) to LocalDateTime
        LocalDateTime dateTime1 = LocalDateTime.parse("2020-01-31T10:15:30");
        System.out.println(dateTime1);

        // Parse a String in a custom date format to LocalDate using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy, HH:mm");
        LocalDateTime dateTime2 = LocalDateTime.parse("Jan 11 2020, 10:30", dateTimeFormatter);
        System.out.println(dateTime2);

        // Parse a String in a custom DateTime format to LocalDateTime using DateTimeFormatter
        DateTimeFormatter descriptiveDateTimeFormatter = DateTimeFormatter.ofPattern("E, MMM dd yyyy, hh:mm:ss a");
        LocalDateTime dateTime3 = LocalDateTime.parse("Fri, Feb 14 2020, 10:20:50 PM", descriptiveDateTimeFormatter);
        System.out.println(dateTime3);

    }
}

Convert/Parse a String to LocalTime

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class LocalTimeParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO Time format (HH:mm:ss) to LocalDate
        LocalTime time1 = LocalTime.parse("12:30:50");
        System.out.println(time1);

        // Parse a String in a custom date format to LocalTime using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss");
        LocalTime time2 = LocalTime.parse("28/02/2020 16:45:30", dateTimeFormatter);
        System.out.println(time2);

    }
}

Convert/Parse a String to ZonedDateTime

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class ZonedDateTimeParseExample {
    public static void main(String[] args) {
        // Parse a String in ISO DateTime format to ZonedDateTime
        ZonedDateTime dateTime1 = ZonedDateTime.parse("2020-01-31T10:15:30+01:00[Europe/Paris]");
        System.out.println(dateTime1);

        // Parse a String in a custom date time format to ZonedDateTime using DateTimeFormatter
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy, HH:mm (VV)");
        ZonedDateTime dateTime2 = ZonedDateTime.parse("Jan 11 2020, 10:30 (America/Los_Angeles)", dateTimeFormatter);
        System.out.println(dateTime2);
    }
}