Creating a Web Application with Spring Boot

Creating a Web Application with Spring Boot

In this post, we will explore details of Creating a Web Application with Spring Boot. We will explore how Spring Boot can help you to accelerate your application development.We will be building a simple web application with Spring Boot and add some useful services to it.

 

1. Introduction

One of the main challenges to starting up a new project is an initial setup for the project. We need to take a call about the different directory structure and also need to make sure we are following all the industry standards.For creating a web application with Spring Boot, we need following tools

  • Our preferred IDE (I will be using IntelliJ) 
  • Maven
  • JDK 1.8+

 

 

2. Creating Project Structure

There are multiple ways to use Spring Boot Initializr to generate project structure for you.

  1. Using  Spring Initializr Web Interface.
  2. Use Spring Boot CLI tool.
  3. Use your IDE

For the simplicity of this post, we are using Spring Initializer web interface to generate project Structure.

Spring Initializr Web interface your browser and you will be presented with a wizard to start your configurations.

Spring Initializr

You are required to fill some information in the web interface to start with

 

  1. What kind of project you want to generate (Maven or Gradle)
  2. What is your preferred language (Apart from Java you will get an option for Kotlin and Groovy)?
  3. Spring Boot Version
  4. Standard project group and artifact details.
  5. Dependencies.

Dependencies is an interesting feature in the web interface, based on your selected Dependencies, web interface will automatically add Spring Boot Starter dependencies in the generated pom.xml file.In case you want a more control on the generated project structure or not sure what all dependencies you want to add to your project, click on the “Switch to the full version”. 

We will be using The Web and Thymeleaf (For UI) for this post.

 

3. Project Structure

Spring Boot does not require any specific code layout or structure.We can always follow some of the best practices suggested by Spring Boot team, however, final structure will be driven by your project requirement.

 

Here is the layout of our sample application

Project Structure

 

 

4. Pom.xml

Let’s start looking into pom.xml file to understand Spring Boot configurations in more detail. I will be covering only Spring Boot related changes in pom.xml. Here is the pom.xml file from our sample project.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.javadevjournal</groupId>
   <artifactId>javadevspringboot</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>war</packaging>

   <name>javadevspringboot</name>
   <description>Java Dev Journal project for Spring Boot</description>

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

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

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-tomcat</artifactId>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

</project>

One of the main features of Spring Boot is the “Starters”, they are an easy way to add required dependencies (jars) in our class path. When using Spring Boot, we don’t have to add jar/dependencies in our class path (In case a starter is not available, you might have to add these dependencies in the pom.xml or can create your own custom starter). We just need to add correct “Starters” in our pom.xml file and Spring Boot will make sure to add those dependencies automatically.

 

5. Main Application

Here is our main Spring Boot application class, this is a Spring Configuration class. The annotation @SpringBootApplication enables the Spring Context and all the startup magic of Spring Boot.

 

@SpringBootApplication
public class WebApplication extends WebMvcConfigurerAdapter {

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

 

5. 1 @SpringBootApplication Annotation

@SpringBootApplication annotation. @SpringBootApplication is equivalent to using @Configuration, @EnableAutoConfiguration and @ComponentScan with their default values.If you are starting your project, it’s recommended to use annotation.

 

Using @SpringBootApplication in your main class is equivalent to following 3 annotations

 

  • @Configuration as a source of bean definitions
  • @EnableAutoConfiguration It gives Spring Boot an idea as to how you want to configure your application.
  • @ComponentScan to automatically pick up all Spring components, including @Configuration classes

 

5. 2 Main Method

Another interesting feature of our main class is the main method. This is a standard method that will follow standard Java workflow. Our main class will pass on control to Spring Boot SpringApplication class.

SpringApplication Class run method will be used to BootStrap an application.

 

6. Welcome Controller

Last part of our setup, we will create a welcome controller which will be responsible to handles GET requests for /greeting by returning the name of a View, in this case, “welcome”. A View is responsible for rendering the HTML content.

 

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;


@Controller
public class WelcomeController {

    @RequestMapping("/welcome")
    public String welcome() {

        return "welcome";
    }
}

This is a very simple controller but has covered a lot of points in our setup.

  • @Controller annotation indicates that an annotated class is a "Controller" (e.g. a web controller).
  • @RequestMapping annotation ensures that HTTP requests to /welcome are mapped to the welcome() method.
  • We have not specified any method to the @RequestMapping annotation as default maps all HTTP operations by default.
  • As we are using Thymeleaf for view technology and returning “welcome” from welcome() method, Thymeleaf parses the template welcome.html and produce the output.

 

6. 1 UI Template 

Here is our simple Thymeleaf HTML template.

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>

</head>
<body>
Hello and Welcome to our Web Application
</body>
</html>

While using Thymeleaf as our template engine, Spring Boot will look for resources by surrounding the view name with a prefix and suffix (externalized to spring.thymeleaf.prefix and spring.thymeleaf.suffix, defaults ‘classpath:/templates/’ and ‘.html’ respectively).

 

 

7. Run Application

We are done with our simple web application, it’s time to run our application. Although it is possible to package this service as a traditional WAR file for deployment to an external application server, the simpler approach demonstrated is to create a standalone application. To run our application from IDE, We need to run our web application as a standalone java application.

  • With Maven, we can run the application using mvn spring-boot:run command.
  • we can build the JAR file with mvn clean package command and run jar by using java -jar target/demo-app-0.1.0.jar.

Now the site is up and running, visit,http://localhost:8080/welcome and if everything is in place, you should have following output on your web browser.

"Hello and Welcome to our Web Application"

 

8. Summary

In this post, we learned Creating a Web Application with Spring Boot. Spring Boot comes with many builds in feature to create and run web application more quickly and with minimal efforts.

Spring MVC Custom Validator

Spring MVC Custom Validator

In this post, we will learn about using Spring MVC Custom Validator. We will explore as to how to create and use your own custom validator.

Introduction

Spring MVC provides first-hand support for JSR-303 Bean Validation API. We can use this API to validate our input data. Bean Validation API ship with a number of different validators which can be used with the use of simple annotation, however in case we need to validate data based on custom business rules, we have the option to create our own custom validator and hook it with Spring MVC validation framework.

We will be exploring Spring MVC Custom Validator and see how to hook it in our application.

 

1. Maven Setup

In order to create and use the custom validator, we need to validation API and JSR-303 compliance validation API in our class path, we will be using Hibernate validator in our example. To start we need to add following entry in our pom.xml file

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>

We will be using Spring Boot for this post, so we will be using Spring Boot starters for add validation API in our class path. To use Hibernate validation API in your Spring Boot application, you need to add the following starter in your pom.xml file


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
As we know, you do not need to specify the version when using Spring Boot as it will be handled automatically by Spring Boot.

2. Custom Class Level Validation

To understand it, let’s take a simple example where we want to create a custom validator to validate two fields in our input form to make sure they are equal. (let’s assume we want to confirm customer email address)

2.1 Annotation

We will start by creating a new @interface to define our new custom constraint.

@Constraint(validatedBy = FieldMatchValidator.class)
@Documented
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface FieldMatch {
    String message() default "Fields are not matching";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * @return The first field
     */
    String first();

    /**
     * @return The second field
     */
    String second();

    /**
     * Defines several <code>@FieldMatch</code> annotations on the same element
     *
     * @see FieldMatch
     */
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        FieldMatch[] value();
    }
}

With @Constraint annotation, we are specifying which class will actually be performing validation, in our case FieldMatchValidator class will be doing the actual validation, message() will define the message which will be shown to the customer when validation fails, this is the default message and it can be changed through configuration (we will see that shortly).

 

2.2 Validation Class

Let’s have a look at the actual validation class which will be responsible for performing our custom validation.

public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object> {


    private String firstFieldName;
    private String secondFieldName;

    public void initialize(final FieldMatch constraintAnnotation) {
        this.firstFieldName = constraintAnnotation.first();
        this.secondFieldName = constraintAnnotation.second();
    }

    public boolean isValid(final Object value, final ConstraintValidatorContext context) {
        try {
            final Object firstObj = PropertyUtils.getProperty(value, this.firstFieldName);
            final Object secondObj = PropertyUtils.getProperty(value, this.secondFieldName);
            return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
        } catch (final Exception ex) {
            //LOG.info("Error while getting values from object", ex);
            return false;
        }

    }
}

Our Validation class implements ConstraintValidator interface and should implement isValid method, all our validation logic will go inside this method.In our custom validation, we are fetching values from the both fields and matching them to return a boolean value.

 

3. Validation in Action

It’s time to see our custom validation in action, to demonstrate it, we will be using a simple Spring Boot based web application where we will be validating 2 fields and will throw an error in case both fields do not match with each other.

3.1 Sample Controller
@Controller
public class CustomValidatorController {

    @RequestMapping("/custom-validator")
    public String hello(Person person) {
        return "customValidator";
    }

    @PostMapping("/custom-validator")
    public String demoValidation(@Valid Person person, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {

            return "customValidator";
        }

        return "redirect:/results";
    }

    @RequestMapping("/success")
    public String welcome() {
        return "welcome";
    }
}
3.2 Person Object
@FieldMatch(first = "email", second = "confirmEmail", message = "Email does not match")
public class Person {

    @NotNull
    private String name;
    private String email;
    private String confirmEmail;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getConfirmEmail() {
        return confirmEmail;
    }

    public void setConfirmEmail(String confirmEmail) {
        this.confirmEmail = confirmEmail;
    }
}

As you can see, we have added @FieldMatch annotation on the top of our Person class which will compare email and confirmEmail fields and in case they don’t match, it will throw a validation error.

3.3 View
<html>
<body>
<form action="#" th:action="@{/custom-validator}" th:object="${person}" method="post">
    <table>
        <p th:if="${#fields.hasErrors('global')}" th:errors="*{global}">
            Incorrect date
        </p>
        <tr>
            <td>Name:</td>
            <td><input type="text" th:field="*{name}" /></td>
            <td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
        </tr>
        <tr>
            <td>Email:</td>
            <td><input type="text" th:field="*{email}" /></td>
            <td th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</td>
        </tr>
        <tr>
            <td>Confirm Email:</td>
            <td><input type="text" th:field="*{confirmEmail}" /></td>
            <td th:if="${#fields.hasErrors('confirmEmail')}" th:errors="*{confirmEmail}">Email Error</td>
        </tr>
        <tr>
            <td><button type="submit">Submit</button></td>
        </tr>
    </table>
</form>
</body>
</html>

If we run our application and add non-matching email id’s, we will get “Email does not match” error on the front end.

Spring MVC Custom Validator

We can also define custom validation on the field level. We need to use the same approach to define field level annotation.

4. Custom Field Level Validation

We can also define custom validation on the field level. We need to use the similar approach to define field level annotation.

4.1 Annotation
@Constraint(validatedBy = CountryMatchValidator.class)
@Documented
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RUNTIME)
public @interface CountryMatch {
    String message() default "Invalid Country";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    /**
     * Defines several <code>@FieldMatch</code> annotations on the same element
     *
     * @see FieldMatch
     */
    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RUNTIME)
    @Documented
    @interface List {
        FieldMatch[] value();
    }
}

4.2 Validation Class
public class CountryMatchValidator implements ConstraintValidator<CountryMatch, String> {


    /**
     * Initializes the validator in preparation for
     * {@link #isValid(Object, ConstraintValidatorContext)} calls.
     * The constraint annotation for a given constraint declaration
     * is passed.
     * <p/>
     * This method is guaranteed to be called before any use of this instance for
     * validation.
     *
     * @param constraintAnnotation annotation instance for a given constraint declaration
     */
    @Override
    public void initialize(CountryMatch constraintAnnotation) {

    }

    /**
     * Implements the validation logic.
     * The state of {@code value} must not be altered.
     * <p/>
     * This method can be accessed concurrently, thread-safety must be ensured
     * by the implementation.
     *
     * @param country  country
     * @param context context in which the constraint is evaluated
     * @return {@code false} if {@code value} does not pass the constraint
     */
    @Override
    public boolean isValid(String country, ConstraintValidatorContext context) {
        try {
            return country == null && country.equals("US");
        } catch (final Exception ex) {
            //LOG.info("Error while getting values from object", ex);
            return false;
        }
    }
}
4.3 Person Object
@FieldMatch(first = "email", second = "confirmEmail", message = "Email does not match")
public class Person {

    @NotNull
    private String name;
    private String email;
    private String confirmEmail;

    @CountryMatch(message = "Country should be US")
    private String country;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getConfirmEmail() {
        return confirmEmail;
    }

    public void setConfirmEmail(String confirmEmail) {
        this.confirmEmail = confirmEmail;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }
}

In order to initiate Spring MVC JSR validation, you need to annotate your model with @Valid annotation in your controller

5. Summary

In this post, we learn about using Spring MVC Custom Validator. We covered how to create and use class and field level validation.

 

All the code of this article is available Over on Github. This is a Maven-based project.

Split a String in Java

Split a String in Java

In this small post, we will explore how to split a string in Java.

 

Introduction

Splitting a String in Java is a very common operation, we will discuss different method available in Java to split a String.

 

1. String#split() Method

Java String class provide a convenient and easy split() method to split String a String.The split method comes in 2 flavours and takes a regular expression as an input. Split method is powerful and mostly sufficient for most use cases.

public void split(){
    String input = "This-is-a-sample-input";
    String[] parts= input.split("-");
    for(int i =0; i< parts.length;i++){
        System.out.println(parts[i]);
    }
}

If you want to limit the number of resulting parts, you can pass the second argument (limit) to the split method.The limit parameter controls the number of times the pattern is applied and therefore affects the length of the resulting array. Here is an another variation of the above example

public void splitWithLimit(){
    String input = "This-is-a-sample-input";
    String[] parts= input.split("-", 2);
    for(int i =0; i< parts.length;i++){
        System.out.println(parts[i]);
    }
}

If you run above example, output will be

This
is-a-sample-input

Please note that split() method takes a regular expression as an input and we need to escape special character if needed (Please check different special character available in regular expression ). We can use \ to escape special characters or Pattern.quote() for it.

 

2. Split String Using Java 8

With Java 8, we can use Stream and Collector to split String. Here is a simple code to split a string in Java 8

public void splitStringJava8(){
    String input = "This-is-a-sample-input";
    List<String> parts = Pattern.compile("-")
                         .splitAsStream(input)
                         .collect(Collectors.toList());
    parts.forEach(s-> System.out.println(s));
}

 

3. Split String Apache Utils

Apache StringUtils provide null safe methods to split a string, here is a sample code for the same

public void apacheStringUtils(){
    String input = "This-is-a-sample-input";
    String[] parts =StringUtils.split(input ,"-");
    for(int i =0; i< parts.length;i++){
        System.out.println(parts[i]);
    }
}

In case you are not passing any delimiter to above method, whitespace will be taken as a default delimiter by it.

 

Conclusion

In this post, we saw different methods to split a string in Java including methods available in Apache’s StringUtils class. I believe that for most of the case split() method provided by the String class will serve our purpose.

 

All the code of this article is available Over on Github. This is a Maven-based project.

Iterate through a Map in Java

How to iterate through a Map in Java

In this articles, we will explore the different ways to Iterate through a Map in Java. We will start with a simple use case and will explore how we can do this using Java 8 Stream API.

 

Introduction

Iterating through Java Map is very common tasks and we face this tasks daily basis. Java Map API provides keySet(), valueSet() or entrySet() option to fetch content/data from the Map.

The Map.entrySet method returns a collection-view of the map, whose elements are of this class. The only way to obtain a reference to a map entry is from the iterator of this collection view. These Map.Entry objects are valid only for the duration of the iteration.

Entry.getKey() returns the key corresponding to this entry. Let’s explore different options to Iterate through a Map in Java.

 

Example

To start working on the different options, let take a simple Map contains few values. Here is code for the simple Map.

Map<String, String> sampleMap= new HashMap<String,String>();

sampleMap.put("one", "one");
sampleMap.put("two", "two");
sampleMap.put("three", "three");
sampleMap.put("four", "four");
sampleMap.put("five", "five");
sampleMap.put("six", "six");

1.  EntrySet with for Loop 

public void iterateForEach(Map<String, String> sampleMap){
    for(Map.Entry<String,String> mapValue : sampleMap.entrySet()){
        System.out.println(mapValue.getKey()+" :" + mapValue.getValue());
    }
}

In above code, we have we converted Map to entry set and then we iterated through the entries using for each loop.

2.  EntrySet with for Loop 

If you don’t want to use For Each loop, we can convert entrySet() to the iterator and use an iterator to loop through the map content.

public void iterator(Map<String, String> map){
    Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
    while(iterator.hasNext()){
        Map.Entry<String, String> entry = iterator.next();
        System.out.println(entry.getKey()+" :" + entry.getValue());
    }
}

I will not prefer this approach and will go with for each loop.

3.  For Each with Java 8 Lambdas

The introduction of Lambdas in Java 8 provides a more concise and easy to read solution.

public void lambdas(Map<String, String> map){
    map.forEach((key,value) ->System.out.println(key + ":"+ value));
}

One of the interesting thing with above approach is that we don’t need to convert the map to entry set for iteration.

4.  Java 8 Stream API

Stream was introduced in Java 8 as one of the main features, we can use Stream API to iterate through the map.

public void stream(Map<String, String> map){
    map.entrySet().stream().forEach(entry-> System.out.println(entry.getKey() +": "+entry.getValue()));
}

I will not use this since we are converting Map to entry set and iterating through the element which is more or less equivalent to for each loop.

If you want you can use Java 8 Stream API parallel method but I will not cover that in this post.

5.  Performance

For small map size (<=100), Java 8 based for each loop have the best performance. In the case of Map size is greater than 10000 elements, simple for each loop seems to be faster than other approaches.Please execute your own benchmarking before taking a call for your implementation.

6.  Conclusion

In this short post, we covered some of the methods to Iterate through a Map in Java along with few options provided by Java 8. 

All the code of this article is available Over on Github. This is a Maven-based project.

FailureAnalyzer in Spring Boot

FailureAnalyzer in Spring Boot

In this post, we will be exploring  FailureAnalyzer in Spring Boot. We will also learn to Create a Custom FailureAnalyzer in Spring Boot.

 

Introduction

Most of the time when we encounter an exception at the server startup time, we need to read it very closely to understand what went wrong before we try to fix it.

Through FailureAnalyzer, Spring Boot provides a great way to intercept exception at the startup and will turn them in more human readable format (We don’t have to scroll through the entire stack trace). Spring Boot comes with a number of  FailureAnalyzer starting from application context related exceptions, JSR-303 validations and more.

Here is an example where port 8080 was already in use and when we tried to run our Spring Boot application on port 8080, PortInUseFailureAnalyzer intercepted this exception and provided a more readable and user-friendly error message.

***************************
APPLICATION FAILED TO START
***************************
Description:
Embedded servlet container failed to start. Port 8080 was already in use.
Action:
Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Here is a list of the few FailureAnalyzer provided by Spring Boot

  1. PortInUseFailureAnalyzer
  2. NoUniqueBeanDefinitionFailureAnalyzer
  3. BeanCurrentlyInCreationFailureAnalyzer

You can find a complete list of FailureAnalyzer by checking org.springframework.boot.diagnostics package under Spring Boot. 

Spring Boot provides an easy way to create our own custom FailureAnalyzer.

 

1. Creating Custom FailureAnalyzer

To create our own custom FailureAnalyzer, we can use AbstractFailureAnalyzer as our convenient extension point.AbstractFailureAnalyzer will check if a specified exception is present and will allow our custom analyzer to handle it.

Let’s create a custom FailureAnalyzer in Spring Boot for the following use case

  1. We will try to inject a different bean for a given dependencies.
  2. When we will try to inject it, Spring will throw BeanNotOfRequiredTypeException since we are trying to inject a different bean.

Here is our sample FailureAnalyzer code

public class CustomFailureAnalyzer extends AbstractFailureAnalyzer<BeanNotOfRequiredTypeException> {

    /**
     * Returns an analysis of the given {@code failure}, or {@code null} if no analysis
     * was possible.
     *
     * @param rootFailure the root failure passed to the analyzer
     * @param cause       the actual found cause
     * @return the analysis or {@code null}
     */
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, BeanNotOfRequiredTypeException cause) {
        String message ="####################### This is a custom fail Message ################ %n"+
                getDescription(cause);
        return new FailureAnalysis(message , (String)null, cause);
    }

    private String getDescription(BeanNotOfRequiredTypeException ex) {

        StringWriter description = new StringWriter();
        PrintWriter printer = new PrintWriter(description);
        printer.printf(
                "The bean %s could not be injected"
                        + "due to %s",
                ex.getBeanName(), ex.getRequiredType().getName());
        return description.toString();
    }
}

The main point is the analyze() method which will get triggered and Spring Boot will pass on a Throwable object along with the case (Exception which was thrown by Spring for our case).  We can get detail information from the cause instance to print a user-friendly message.

 

2. Registering Custom FailureAnalyzer

We need a special way to register our custom FailureAnalyzer with Spring Boot so as Spring Boot should be able to call our custom FailureAnalyzer in case exception is thrown by the system. We need to register it using spring.factories property file in META-INF folder.

In case META-INF or spring.factories properties file is not present, we need to create it manually. To register custom FailureAnalyzer, add following entry in spring.factories

org.springframework.boot.diagnostics.FailureAnalyzer=\
  com.umeshawasthi.failureanalyzer.CustomFailureAnalyzer

if you have multipleFailureAnalyzer You can register all of those in as a comma separated entries

org.springframework.boot.diagnostics.FailureAnalyzer=\
com.umeshawasthi.failureanalyzer.CustomFailureAnalyzer,\ com.umeshawasthi.failureanalyzer.CustomFailureAnalyzer1

At this point, we are all set with our CustomFailureAnalyzer and ready to test it.

 

3. FailureAnalyzer in Action

I have created 2 classes in my demo project with the following signature

public class AdminDAO {

    public void helloAdmin(){
        System.out.println("Hello Admin");
    }
}
@Repository("adminDAO")
public class AdminDAOImpl {

    public void setupAdmin(){
        //default implimentation
    }
}

I have created another Controller where I am trying to inject “adminDAO” bean to AdminDAO.

@RestController
public class HelloWorldController {
   @Resource(name = "adminDAO")
   private AdminDAO adminDAO;
   //some methods
}

If I run my Spring Boot Application, Spring will try to inject adminDao of type AdminDAOImpl in AdminDAO, since these are not compatible, Spring will throw BeanNotOfRequiredTypeException. In the current use case, Spring Boot will check to determine a valid FailureAnalyzer is registered and will pass on information to registered FailureAnalyzer.

In our case, we have registered CustomFailureAnalyzer to handle such case, Spring Boot will pass on this information to our custom FailureAnalyzer to produce a more user-friendly message.

Here is the output when we will run our application
***************************
APPLICATION FAILED TO START
***************************
Description:
################# This is a custom fail Message ################ %nThe bean adminDAO could not be injecteddue to com.umeshawasthi.service.AdminDAO

Summary

In this article, we explored an interesting feature provided by Spring Boot. We saw how Spring Boot FailureAnalyzer work and how to create our own custom FailureAnalyzer.

All the code of this article is available Over on Github. This is a Maven-based project.

Java NIO2 File Attributes

Introduction to Java NIO2 File Attributes API

In this post, we will cover Java NIo2 File Attributes. File attributes are also known as metadata. Metadata can be used to find out information about the file systems like if it’s a regular file or a directory, who is the owner of the file or directory.

I have already covered Java NIO2Java NIO Path and Java NIO Selector as part of the new NIO features introduced in Java 7. 

 

Introduction

File metadata can be used to get information about the file ranging from the size of the file to the access permissions. These File Attributes can be used to check critical information before moving ahead with real file-based operations. 

I will be covering different metadata attributes in this post.

 

1.   Basic File Attributes

To get information about all the basic attributes of the file, we can use Files.readAttribute() method. This method will return BasicFileAttributes which can be used to access various basic file attributes.This approach is more efficient as we are not required to make a separate call to the underlying file system to read individual attributes.

We will get BasicFileAttributes for a given path and then check what all information we can get from this

Path path = Paths.get("/Users/umesh/personal/tutorials/source");
BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes basicFileAttributes = fileAttributeView.readAttributes();

We first retrieved BasicFileAttributeView and retrieved.BasicFileAttributes from it.We went this route since BasicFileAttributeView contains set of file attributes consist of mandatory and optional file attributes as defined by the BasicFileAttributes interface.

Once we have BasicFileAttribute, we can get all basic file attribute information from it.

1.1   Check Creation Time

To find creation time of the file, use creationTime() method

basicFileAttributes.creationTime()

 

1.2  Last Access Time

basicFileAttributes.lastAccessTime()

 

1.3  Check for Directory

We can also check if a given path is a file or directory 

basicFileAttributes.isDirectory()

 

1.4  Basic Attribute Example

Here is complete example demonstrating various methods available to get basic file information.

Path path = Paths.get("/Users/umesh/personal/tutorials/source");
BasicFileAttributeView fileAttributeView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
BasicFileAttributes basicFileAttributes = fileAttributeView.readAttributes();

System.out.println("creationTime: " + basicFileAttributes.creationTime());
System.out.println("lastAccessTime: " + basicFileAttributes.lastAccessTime());
System.out.println("lastModifiedTime: " + basicFileAttributes.lastModifiedTime());

System.out.println("isDirectory: " + basicFileAttributes.isDirectory());
System.out.println("isOther: " + basicFileAttributes.isOther());
System.out.println("isRegularFile: " + basicFileAttributes.isRegularFile());
System.out.println("isSymbolicLink: " + basicFileAttributes.isSymbolicLink());
System.out.println("size: " + basicFileAttributes.size());

All method related to getting time returns FileTime object which is different than simple TimeStamp 

2.  File Owner Information

In order to get high-level file ownership information, We can use FileOwnerAttributeView provided by Java NIO2 API.

FileOwnerAttributeView fileOwner=Files.getFileAttributeView(path, FileOwnerAttributeView.class);
System.out.println("File Owner Name " +fileOwner.getOwner());

You can get information about the POSIX or DOS file attributes by passing DosFileAttributes.class or PosixFileAttributes.class to Files.getFileAttributeView() method.

 

3. File Store Attributes

Java NIO2 API provides a convenient FileStore class to get information about the file. We can use this class to get information about the underlying file system like how much space is available, how much used etc. 

FileStore store = Files.getFileStore(path);

long total = store.getTotalSpace() / 1024;
long used = (store.getTotalSpace() - store.getUnallocatedSpace()) /1024;
long available = store.getUsableSpace() /1024;

Summary

In this post, we covered basics as how to get information about the basic file attributes using Java 7 NIO2 File Attributes.

 

All the code of this article is available Over on Github. This is a Maven-based project.

Java Base64 Encoding and Decoding

Java Base64 Encoding and Decoding

In this post, we will explore various options of Java Base64 Encoding and Decoding. We will be covering mainly new Base64 API introduced in Java 8.

 

Introduction

Base64 encoding was missing from standard JDK before Java 8. Java 8 introduced a simple yet powerful API under java.util package or specifically a utility class java.util.Base64. In this post, we will be covering Java Base64 encoding and decoding.

The Base64 class consists of static factory methods for obtaining Base64 based encoder and decoder. We can obtain following 3 types of Base64 encoder/ decoder

  1. Basic
  2. URL and FileName
  3. MIME 

 

1.  Java 8 Basic Encoder

Java 8 Basic Base64 encoder is a simple encoder.It will simply encode without adding any line feed/line separator characters. 

void basicEncoder(final String input) {
    String simpleBase64 = Base64.getEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
    System.out.println(simpleBase64);
}

The output of the encoder will be mapped to characters in the Base64 Alphabet: A-Za-z0-9+/ and it will reject data that is outside this base64 alphabet.

 

1.1  Java 8 Basic Decoder

Decoding encoded String back to original form is quite simple with new API, all we have to do it to get encode and pass decoded data to it.

void basicDecoder(final String encodedData) {
    byte[] decodeData = Base64.getDecoder().decode(encodedData);
    System.out.println(new String(decodeData, StandardCharsets.UTF_8));
}

We are no longer required to use those external dependencies (e.g. Sun classes etc) to do encoding and decoding. 

2.  URL Encoding

URL encoding works very similarly to Basic encoding and it uses “URL and Filename safe Base64 Alphabet”. 

String input= "search?_base64";
void urlEncoding(final String input) {
    String urlEncode= Base64.getUrlEncoder().encodeToString(input.getBytes(StandardCharsets.UTF_8));
    System.out.println("Encoding using URL Encoder "+urlEncode);
}

2.1  URL Decoder

URL decoder work in a similar way as encoder work, we need to get UrlDecoder() and pass the encoded data to it.

void urlDecoding(final String encodedData) {
    byte[] decodeData = Base64.getUrlDecoder().decode(encodedData);
    System.out.println(new String(decodeData, StandardCharsets.UTF_8));
}

3.  MIME Encoding

MIME encoding will use Base64 encoding using Basic Alphabet and encoded output will be converted into MIME format (no more than 76 characters each and uses a carriage return ‘\r’ followed immediately by a linefeed ‘\n’ as the line separator).

private static void mimeEncoder(){
    StringBuilder stringBuffer = new StringBuilder();
    for (int t = 0; t < 10; ++t) {
        stringBuffer.append(UUID.randomUUID().toString());
    }

    String mimeEncoding = Base64.getMimeEncoder().
                          encodeToString(stringBuffer.toString().getBytes(StandardCharsets.UTF_8));

    System.out.println("MIME Encoding is " +mimeEncoding);
}

Output

MIME Encoding is:: ZjY5NzBkYTctMDFmNy00YmY3LTk4YjAtMmYxZGUzYzNhM2QwZTNmM2I4M2EtODg0Yy00ZjlkLTlm
YTgtMjIzZWY1ZTMzNGQ2ODVlZDA0ZDQtMjJjMy00NzUxLWEwYTYtYzM2ZTNhOTFjNzk0MmEwY2Iz
ZDYtYmRmNC00MmUzLTllMzQtZjIzMDRkMmMxMzgyMmIxNTExZjEtZDdkYS00ZjIwLTlhMzUtYjYy
ZTFiNTc3ZmQ1NzM3NDViM2ItNTRkMC00MTM4LTgxMjMtNmQ4ZDgyMzRlYTI4YTA1NjFhMGEtNWY3
NC00NDdmLTlhNWItOWIxNTgxOWY4NzJlMTRhZThmN2MtNjdiOS00ZDcwLWEwZmYtOGEyNjM3MDBm
NzNlMTRiNzdiY2YtYWI2ZS00MmNmLWI5NjAtMjFmZThmZjZmMmJjYWU0MmQ0MmUtZDE1ZC00NTdk
LThmMzUtNTUyMTlhMzRiMmYx

2.1  MIME Decoder

byte[] decodeData = Base64.getMimeDecoder().decode(encodedData);
System.out.println(new String(decodeData, StandardCharsets.UTF_8));

 

4.  Conclusion

In this post, we explored  Java Base64 Encoding and Decoding API introduced in Java 8. This new API is simple and powerful and at the same time will be available within the JDK without the need for external dependencies (commons-codec or sun.misc.BASE64Decoder).

All the code of this article is available Over on Github. This is a Maven-based project.

References

Base64

StackWalker API In Java 9

Introduction to StackWalker API In Java 9

In this post, we will be covering  StackWalker API in Java 9Stack Walking API is yet another features being introduced in Java 9

 

Introduction

To put Stack Walk API  in Java 9 in simple words, It provides capabilities to walk through the stack in Java. StackWalker provides a snapshot of the current thread stack trace along with some methods to access it.

Before Java 9, There was no standard / efficient way to access selected frames of the stack and get information about the Class instance of each frame. We have the following options available to get information from the Stack Trace

  1. getStackTrace() method from java.lang.Throwable Class which returns an array of StackTraceElement.

So if you want to get information about the stack trace before Java 9, a simpler code will look similar to 

StackTraceElement[] stackTrace = new Throwable().getStackTrace();

Above method require JVM to capture the snapshot of the entire stack and return this information back to the calling method / API. This is not a very efficient solution due to following use cases.

  1. Capturing the entire snapshot of the stack is not an efficient memory operation and will have an impact.
  2.  You might only need some frame from the stack but still, you will be provided with the entire snapshot.
  3. Taking into account the second point, We have to process some frames which might not be useful for us.
  4. One of the most important points is that as part of StackTraceElement objects, you will get information about the class name, method name but will never get hold of the actual Class instance.
  5. Some of the VM implementation may strip some stack information in favor of performance, so you might not get full stack information even when calling Thread.getStackTrace() method.

If you want you can use java.lang.SecurityManager.getClassContext() method to get access to the Class instance but I will not be covering that part in this post.

 

1. Why Use StackWalker

One of natural question which comes to our mind is why do we need  StackWalker API in Java 9? Here are some of the benefits which you will be getting from this new API.

  1. It will provide you a way to filter/skip classes which give us the flexibility to process our interest specific classes.
  2. StackWalker API provides a way to load only certain frames (e.g. load only 10 frames) which will be helping us to handle performance issue.
  3. We can directly get instance of the declaring class without using java.lang.SecurityManager.getClassContext() method.
  4. It will provide you a way to understanding application behavior more easily.

 

2. StackWalker Basic

Let’s cover some of the basic of the StackWalker API along with details as to how we can use it in future.StackWalker class is easy to use and provide some convenient method to work on the stack.

 

2.1 Obtaining a StackWalker

StackWalker API provides a static getInstance method which can be used to obtained StackWalker instance.

StackWalker stackWalker = StackWalker.getInstance();

There are other variants of the getInstance() method which can be used based on the individual requirements.

Let’s start building our example to demonstrate various features of the StackWalker API in Java 9.

 

3  StackWalker Demo

Here is our sample class with some method creating method chain.

package com.umeshawasthi.java9.example.stackwalker;

public class StackWalkerExample {


    public static void stackWalkerMethod1() {
        stackWalkerMethod1();
    }

    public static void stackWalkerMethod2() {
        stackWalkerMethod2();
    }

    public static void stackWalkerMethod3() {
        stackWalkerMethod3();
    }

    public static void stackWalkerMethod4() {
        mainWalkerMethod();
    }

    public static void printCompleteStackTrace() {

    }

}

3.1  Get All Stack Information

In order to get the complete stack, we will get an instance of the StackWalker and will use walk method to opens a sequential stream.

public static void printCompleteStackTrace() {

    List<StackWalker.StackFrame> stack = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
            .walk((s) -> s.collect(Collectors.toList()));

     stack.forEach(System.out::println);
}

In above method we get an instance of the StackWalker and use the walk method opens a sequential stream of StackFrames for the current thread. 

Here is the output of the above method.

java/com.umeshawasthi.java9.example.stackwalker.StackWalkerExample.printCompleteStackTrace(StackWalkerExample.java:39)
java/com.umeshawasthi.java9.example.stackwalker.StackWalkerExample.stackWalkerMethod3(StackWalkerExample.java:28)
java/com.umeshawasthi.java9.example.stackwalker.StackWalkerExample.stackWalkerMethod2(StackWalkerExample.java:23)
java/com.umeshawasthi.java9.example.stackwalker.StackWalkerExample.stackWalkerMethod1(StackWalkerExample.java:18)
java/com.umeshawasthi.java9.example.stackwalker.StackWalkerExample.main(StackWalkerExample.java:13)

 

3.2  Filter StackFrames for Certain Classes

Let’s say we are only interested in a stack from a specific class, to achieve this we do not have to go through each and every frame. StackWalker API provides a convenient option to filter out results which are of our interest.

public void filterStackFrame() {
    List<StackWalker.StackFrame> filterFrames = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
            .walk((s) -> s.filter(f -> f.getClassName().contains(StackWalkerDemo3.class.getName()))
                    .collect(Collectors.toList()));

    filterFrames.forEach(System.out::println);
}

As we saw in above example, We are only interested in the StackFrame from the StackWalkerDemo3 class, so we passed it as a filter and StackWalker API will filter out it for us. Below is the output of the above code

java/com.umeshawasthi.java9.example.stackwalker.StackWalkerFilterExample$StackWalkerDemo3.stackWalkerMethod3(StackWalkerFilterExample.java:35)

3.3  Shows all Reflection and Hidden Frames

StackWalker API provides a way to show all reflection and hidden Frames which are hidden by default. StackWalker configured with this SHOW_REFLECT_FRAMES option will show all reflection frames, a similar way we can use SHOW_HIDDEN_FRAMES  to show all hidden frames.

 

public static void printCompleteStackTrace() {

    List<StackWalker.StackFrame> stack = StackWalker.getInstance(StackWalker.Option.SHOW_REFLECT_FRAMES)
            .walk((s) -> s.collect(Collectors.toList()));

     stack.forEach(System.out::println);
}

3.4  Limit Number of StackFrames

Let’s say we just want the top 3 stack frames from the current thread, we can use limit option for achieving this. 

List<StackWalker.StackFrame> stack = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
        .walk((s) ->s.limit(3).collect(Collectors.toList()));

 

3.5  Get Calling Class Instance

We discussed in the starting of this post that StackWalker API provides a way to get hold of the calling class Instance which was not possible before Java 9 (We do have some indirect way). To get hold of the calling class instance you need RETAIN_CLASS_REFERENCE while getting StackWalker instance.

RETAIN_CLASS_REFERENCE will retain an instance of all classes walked by StackWalker, we can use getCallerClass and getDeclaringClass method to get hold of the class instance.

 

public static void getCallerClass() {

    Class calledClass = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
            .getCallerClass();

    System.out.println("Caller Class is " +calledClass.getCanonicalName());
}

Output of above program is

Caller Class is com.umeshawasthi.java9.example.stackwalker.StackWalkerGetClass

 

4  Summary

In this post, we explore StackWalker API being introduced in Java 9. We checked how we can get stack based on our requirement with an option to filter or limit results along with the ability to display hidden frames. We explored possibility to get hold of the calling Class instance using Java 9 StackWalker API

If you are interested in learning other features being introduced in Java 9, Please read following posts on Java 9

Java 9 REPL

Collection Factory Methods in Java 9

All the code of this article is available Over on Github. This is a Maven-based project.

References

JEP 259

StackWalker

@ConfigurationProperties in Spring Boot

Introduction to @ConfigurationProperties in Spring Boot

In this article, we will be covering @ConfigurationProperties in Spring Boot. Spring Boot provides a very clean way to load properties for a given application.It provides an easy and manageable way to externalized configurations along with the ability to bind and validate these configurations.

 

1. Introduction

Consider following entries in a property file (say custom.properties)

user.firstName = Umesh
user.lastName = Awasthi
user.greeting = Hello Umesh
user.blogName = umeshawasthi.com

If I have to use these property files in the Spring application (without Spring Boot), I will be using it in following way

public class SimpleSpringPropertyTest {
    @Value("${user.firstName}") private String firstName;
    @Value("${user.lastName}") private String lastName;
 } 

@Value("${proprties}") annotation is quite handy and easy to use, but it will really be a very tedious process if we have a number of properties. Spring Boot has introduced a new approach to handling these properties in a more clean way with an option to validate these configuration value.

 

2. Setup

We do not need any special setup to enable @ConfigurationProprties feature in Spring Boot, We need to define spring-boot-starter-parent as our parent in our project’s pom.xml

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

Above entry in pom.xml will ensure that all required dependencies are in your class path.

 

3. Introduction to @ConfigurationProprties

In order to understand this feature, we can take an example of a custom property file which contains configuration properties for Database, Email Server and something else, on a high level that property might look like


#Database Configuration

db.driver =org.hsqldb.jdbcDriver
db.username	=test
db.password	=test
db.tablePrefix =_prefix

#SMTP Configuration

mail.from [email protected]	 
mail.host [email protected]
mail.port =25
mail.security.userName 	=test
mail.security.password 	=test

#Server Configurations

server.tomcat.httpPort =80
server.tomcat.sslPort =443
server.tomcat.ajpPort =444
server.tomcat.jmxPort =445

#Global Properties
username=umesh
welcomeMessage = Welcome Umesh!!!

3.1 Binding Properties

We will start by creating a separate POJO class to store and handle our application specific configuration properties by annotating it with @ConfigurationProperties 

@Configuration
@ConfigurationProperties
public class ApplicationConfigurationProp {

}

@Configuration annotation will allow Spring to detect and register this Bean which means we can inject this configuration bean in our application. Above code will work fine if we want to access only global properties (i.e. username and welcomeMessage).

@Configuration annotation is most suitable when we want to access hierarchical properties and we want to access/filter properties based on the prefix. Let’s say we want to bind all the properties starting with prefix “mail” to our  ApplicationConfigurationProp  Class, we can use prefix property on the @ConfigurationProperties annotation.

 

@Configuration
@ConfigurationProperties(prefix = "mail")
public class ApplicationConfigurationProp {


    private String from;
    private String host;
    private int port;

    //getter and setter

    public static class Security{
        private String userName;
        private String password;

        //getter and setter
    }

}

Once we run above application, all properties defined in the property files with prefix “mail” will automatically be bind / assigned to this object.

 

3.2 Binding Custom Properties File

While working on the above example we assume that all these properties are defined in the Spring Boot’s application.properties file, let’s say we want to define these properties in our custom property file (custom.properties) and not in the application.properties file. We can use @PropertySource annotation to define custom property file.

@Configuration
@PropertySource("classpath:custom.properties")
@ConfigurationProperties(prefix = "mail")
public class ApplicationConfigurationProp {
}

 

3.3 Relaxed binding

One of the interesting features of the Spring Boot property binding is “relaxed binding rules”. Under relaxed binding, Spring Boot doesn’t need to be an exact match between the properties.

For a given property db.username, all of the following variation are valid in Spring Boot property binding

Property NameDescription
db.userName 
db.user-name Dashed notation will work for username
db.user_name underscore notation
db.USER_NAMEupper case format

 

4. Property Validation

We can use JSR-303 Validation API to validate property defined using@ConfigurationProperties. In order to use bean validation with Spring Boot, we need to add JSR-303 compliant validation API in our project. For this post, I will be using Hibernate Validator by adding it in our pom.xml file

<dependency>
    <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>5.4.1.Final</version>
   <relativePath/>
</dependency>

We need to add standard @Validated annotation in order for bean validation to validate given bean. To understand how Bean Validation will work with @ConfigurationProperties let’s take an example where we want to ensure that userName should be validated against following rules

  1. Minimum length of username should be 5
  2. User Name length can not be more than 10.

In order to achieve this, we will be adding JSR-303 javax.validation constraint annotations directly on your configuration class.

@Configuration
@PropertySource("classpath:custom.properties")
@ConfigurationProperties(prefix = "mail")
@Validated
public class ApplicationConfigurationProp {

    @Valid
    private Security security= new Security();

    public static class Security{

        @Length(max = 10, min = 5)
        private String userName;

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }
    }

}

We have following property file defined in our custom.properties file 

mail.security.userName  =test

If we run our application, Spring Boot will try to bind userName property in our configuration class and it will throw error as validation will fail for the given property


***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target com.umeshawasthi.confi[email protected]69637b10 failed:

    Property: mail.security.userName
    Value: test
    Reason: length must be between 5 and 10


Action:

Update your application's configuration

I have used .properties file in this post, Please note that @ConfigurationProperties supports both .properties and .yml file

5. Complex or Nested Properties

I have taken a simple example (with nested class) to demonstrate how Spring Boot can map these properties in our configuration class, We can use similar techniques to bind even complex hierarchy using Spring Boot’s @ConfigurationProperties

app.servers[0]=dev.test.com
app.servers[1]=foo.test.com

To bind above properties using Spring Boot’s @ConfigurationProperties, We only need to define properties in the target bean either as a java.util.List, or Set. Converting these properties in to List or Map will be handled by Spring DataBinder. (You can even register your custom Data Binder to map custom properties).

@ConfigurationProperties(prefix="app")
public class AppConfig {

    private List servers = new ArrayList();

    public List getServers() {
        return this.servers;
    }
}

Summary

In this post, we explored @ConfigurationProperties in Spring Boot. We explored how this is being used by Spring Boot to bind properties in our object. We checked how to specify customer property path for binding properties and way to validate injected properties using JSR-303 Bean Validation API. We touched briefly relax binding feature. 

Spring Boot Provides a very clean and flexible approach to bind property files in configuration object.

 

Complete Example

All the code of this article is available Over on Github. This is a Maven-based project.

Java NIO2 WatchService

Java NIO2 – Watching a directory for changes

Java NIO2 WatchService was introduced in Java 7 as part of the NIO2. WacthService provides the ability for monitoring a file or directory for change.

In this articles, we will explore this exciting yet less known feature introduced under Java 7 NIO.  

 

1. Introduction 

WatchService can also be called as notification API, It allows the user to register directory(s) with the service with the ability for us to define a type of event we are interested (like file creation, deletion etc. ), it will notify user when it will detect change in the directory(s).

e.g. Let’s say you want to detect any if a new file(s) are being created in a given directory, you can use WacthService to register this directory and tell service to notify you if a new file is being created.

Most of the modern IDE provide such feature which enables them to detect any file change happening in the workbench (Have you ever seen a popup indicating file has been changed, please update ). If we want to implement a similar feature, we can create a polling API but the solution might not be perfect.

Java 7 NIO2 WacthService is a scalable solution to achieve above objectives.

 

2. WatchService Overview

The first step is to create a WatchService instance using FileSystems class

WatchService watchService = FileSystems.getDefault().newWatchService();

Register all directories with WatchService, which we are planning to monitor, during this registration we also need to specify what kind of event  we are interested in

Path directory = Paths.get("/Users/umesh/personal/tutorials/source");
WatchKey key = directory.register(watchService,
        ENTRY_CREATE,
        ENTRY_DELETE,
        ENTRY_MODIFY);

Pay close attention to following 2 points

  1. Path’s register method took WatchService as the first method
  2. The second argument to the method is of type StandardWatchEventKinds, which indicates what kind of event we are interested.

We can only register Watchable interface with WacthService and since Path class implements it, so this directory/path got registered with WacthService.

 

3.  StandardWatchEventKinds

While registering with WacthService, we need to specify what event(s) we are interested in. You can pass any or all of the following value during registration.

 

Event NameDescription
ENTRY_CREATETriggered when a new entry is created in the watched directory (file or directory creation).
ENTRY_DELETETriggered when an entry is deleted/ moved in watched directory.
ENTRY_MODIFYTriggered when an entry is modified in the watched directory.
OVERFLOWIndicates that events might have been lost or discarded. You do not have to register for the OVERFLOW event to receive it.

You won’t be able to register individual files in WacthService. Service will throw NotDirectoryException in case you will register files with WacthService.

 

3.  WatchKey

When we register with WacthService, it returned WacthKey as a token, WacthKey contains certain state.

  1. When we first register with WatchService, WacthKey will be in ready state.
  2. In the case of an event, the key is signaled and queued so that it can be retrieved.
  3. WatchKey will remain in the same state until we call reset method.

 

4.  Processing Event

WatchService do not have any callback feature, it provided number of different ways to poll for getting this information

WatchKey pollEvent= watchService.poll();

poll() method will return queued key if available or will return null if unavailable.

WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);

Above will return key immediately if available, in case it is not available, API will wait till the time specified in “timeout” parameter.

If we want to wait till the event occurred, We can use wait() method of the API. This method will return key immediately (if available) else it will wait for the event

WatchKey wait = watchService.take();

For WacthService to work correctly, we need to put the event back in the ready state once it has been processed. Call reset method on the WacthKey to reset it

key.reset();

In order to process rest of events, we need to fetch List of WatchEvent from the pollEvent() method. Here is a small example indicating how to handle and process these events in a real life use case.

WatchKey wait;
while ((wait = watchService.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
        if (event.kind() == ENTRY_CREATE) {
            //handle create
        }
        if (event.kind() == ENTRY_DELETE) {
            //handle delete
        }
    }
}

5.  Complete Example

package com.umeshawasthi.tutorials.corejava.io.nio2;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

import static java.nio.file.StandardWatchEventKinds.*;

/**
 * Created by umesh on 6/15/17.
 */
public class NIOWatchService {

    public static void main(String[] args) throws IOException, InterruptedException {

        WatchService watchService = FileSystems.getDefault().newWatchService();

        Path directory = Paths.get("/Users/umesh/personal/tutorials/source");
        directory.register(watchService,
                ENTRY_CREATE,
                ENTRY_DELETE,
                ENTRY_MODIFY);

        WatchKey key;
        while ((key = watchService.take()) != null) {
            for (WatchEvent<?> event : key.pollEvents()) {
                if (event.kind() == ENTRY_CREATE) {
                    //handle create
                }
                if (event.kind() == ENTRY_DELETE) {
                    //handle delete
                }

                System.out.println(event.kind()+ " Event Happened on "+event.context());
            }

            key.reset();
        }
    }
}

When we run this program and done some changes in the source directory, we saw following output

ENTRY_MODIFY Event Happened on index2.html
ENTRY_MODIFY Event Happened on .DS_Store
ENTRY_CREATE Event Happened on untitled folder
ENTRY_MODIFY Event Happened on .DS_Store
ENTRY_CREATE Event Happened on WatchService
ENTRY_DELETE Event Happened on untitled folder

 

6.  When to Use WatchService

Watch Service API provides really some interesting features and  can be used in following places easily

  1. Processing files (e.g. Processing product price file), If price file is dropped in the directory, it will notify custom program to process it.
  2. An application server that watches a directory, waiting for some file to redeploy.

 

In this article, we explore one of the interesting feature introduced under Java 7 NIO Package. We explored various features of WatchService API and discussed how to use the different poll method to get information about the event. We also created a complete example to demonstrate how all these pieces work together.

All the code of this article is available Over on Github. This is a Maven-based project.

 

References

WatchService