Introduction to Lombok

Java is often criticized for being unnecessarily verbose when compared with other languages. Lombok provides a bunch of annotations that generate boilerplate code in the background, removing it from your classes, and, therefore, helping to keep your code clean. Less boilerplate means more concise code that’s easier to read and maintain. In this post, I’ll cover the Lombok features I use more regularly and show you how they can be used to produce cleaner, more concise code.

Local Variable Type Inference: val and var

Lots of languages infer the local variable type by looking at the expression on the right-hand side of the equals. Although this is now supported in Java 10+, it wasn’t previously possible without the help of Lombok. The snippet below shows how you have to explicitly specify the local type:

final Map<String, Integer> map = new HashMap<>();
map.put("Joe", 21);

In Lombok, we can shorten this by using val as follows:

val valMap = new HashMap<String, Integer>();
valMap.put("Sam", 30);

Note that under the covers, val creates a variable that is final and immutable. If you need a mutable local variable, you can use var instead.

@NotNull

It’s generally not a bad idea to null check method arguments, especially if the method forms an API being used by other devs. While these checks are straightforward, they can become verbose, especially when you have multiple arguments. As shown below, the added bloat doesn’t help readability and can become a distraction from the main purpose of the method.

public void notNullDemo(Employee employee, Account account){
    if(employee == null){
      throw new IllegalArgumentException("Employee is marked @NotNull but is null");
    }
    if(account == null){
      throw new IllegalArgumentException("Account is marked @NotNull but is null");
  }
    // do stuff
}

Ideally, you want the null check — without all the noise. That’s where the @NotNull comes into play. By marking your parameters with @NotNull,  Lombok generates a null check for that parameter on your behalf. Your method suddenly becomes much cleaner, but without losing those defensive null checks.

public void notNullDemo(@NotNull Employee employee, @NotNull Account account){
      // just do stuff
}

By default, Lombok will throw a NullPointerException, but if you want, you can configure Lombok to throw an IllegalArgumentException. I personally prefer the IllegalArgumentException as I think its a better fit if you go to the bother of checking the arguments.

Cleaner Data Classes

Data classes are an area where Lombok can really help reduce boilerplate code. Before we look at the options, let’s consider what kinds of boilerplate we typically have to deal with. A data class typically includes one or all of the following:

  • A constructor (without or with arguments)
  • Getter methods for private member variables
  • Setter methods for private nonfinal member variables
  • toStringmethod to help with logging
  • equals and hashCode (dealing with equality/collections)

The above can be generated by your IDE, so the issue isn’t with the time taken to write them. The problem is that a simple class with a handful of member variables can quickly become very verbose. Let’s see how Lombok can help to reduce clutter by helping with each of the above.

@Getter and @Setter

Consider the Car class below. When we generate getters and setters, we end up with nearly 50 lines of code to describe a class with 5 member variables.

public class Car {
  private String make;
  private String model;
  private String bodyType;
  private int yearOfManufacture;
  private int cubicCapacity;
  public String getMake() {
    return make;
  }
  public void setMake(String make) {
    this.make = make;
  }
  public String getModel() {
    return model;
  }
  public void setModel(String model) {
    this.model = model;
  }
  public String getBodyType() {
    return bodyType;
  }
  public void setBodyType(String bodyType) {
    this.bodyType = bodyType;
  }
  public int getYearOfManufacture() {
    return yearOfManufacture;
  }
  public void setYearOfManufacture(int yearOfManufacture) {
    this.yearOfManufacture = yearOfManufacture;
  }
  public int getCubicCapacity() {
    return cubicCapacity;
  }
  public void setCubicCapacity(int cubicCapacity) {
    this.cubicCapacity = cubicCapacity;
  }
}

Lombok can help by generating the getter and setter boilerplate on your behalf. By annotating each member variable with @Getter and @Setter, you end up with an equivalent class that looks like this:

public class Car {
  @Getter @Setter
  private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;
}

Note that you can only use @Setter on non-final member variables. Using it on final member variables will result in a compilation error.

@AllArgsConstructor

Data classes commonly include a constructor that takes a parameter for each member variable. An IDE generated constructor for the Car class is shown below:

public class Car {
  @Getter @Setter
  private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;
  public Car(String make, String model, String bodyType, int yearOfManufacture, int cubicCapacity) {
    super();
    this.make = make;
    this.model = model;
    this.bodyType = bodyType;
    this.yearOfManufacture = yearOfManufacture;
    this.cubicCapacity = cubicCapacity;
  }
}

We can achieve the same thing using the @AllArgsConstructor annotation. Like @Getter and  @Setter@AllArgsConstructor reduces boilerplate and keeps the class cleaner and more concise.

@AllArgsConstructor
public class Car {
  @Getter @Setter
  private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;
}

There are other options for generating constructors. @RequiredArgsConstructor will create a constructor with one argument per final member variable and @NoArgsConstructor will create a constructor with no arguments.

@ToString

It’s good practice to override the toString method on your data classes to help with logging. An IDE-generated toString method for the Car class looks like this:

@AllArgsConstructor
public class Car {
  @Getter @Setter
  private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;
  @Override
  public String toString() {
    return "Car [make=" + make + ", model=" + model + ", bodyType=" + bodyType + ", yearOfManufacture="
        + yearOfManufacture + ", cubicCapacity=" + cubicCapacity + "]";
  }
}

We can do away with this by using the @ToString annotation as follows:

@ToString
@AllArgsConstructor
public class Car {
  @Getter @Setter
  private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;

By default, Lombok generates a toString method that includes all member variables. This behavior can be overridden to exclude certain member variables the exclude attribute  @ToString(exclude={"someField"}, "someOtherField"}) .

@EqualsAndHashCode

If you’re doing any kind of object comparison with your data classes, you’ll need to override the equals and hashCode methods. Object equality is something you’ll define based on some business rules. For example, in my Car class, I might consider two objects equal if they have the same make, model, and body type. If I use the IDE to generate an equals method that checks the make, model, and body type, it will look something like this:

@Override
public boolean equals(Object obj) {
  if (this == obj)
    return true;
  if (obj == null)
    return false;
  if (getClass() != obj.getClass())
    return false;
  Car other = (Car) obj;
  if (bodyType == null) {
    if (other.bodyType != null)
      return false;
  } else if (!bodyType.equals(other.bodyType))
    return false;
  if (make == null) {
    if (other.make != null)
      return false;
  } else if (!make.equals(other.make))
    return false;
  if (model == null) {
    if (other.model != null)
      return false;
  } else if (!model.equals(other.model))
    return false;
  return true;
}

The equivalent hashCode implementation looks like this:

@Override
public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((bodyType == null) ? 0 : bodyType.hashCode());
  result = prime * result + ((make == null) ? 0 : make.hashCode());
  result = prime * result + ((model == null) ? 0 : model.hashCode());
  return result;
}

Although the IDE takes care of the heavy lifting, we still end up with considerable boilerplate code in the class. Lombok allows us to achieve the same thing using the @EqualsAndHashCode class annotation as shown below.

@ToString
@AllArgsConstructor
@EqualsAndHashCode(exclude = { "yearOfManufacture", "cubicCapacity" })
public class Car {
  @Getter @Setter
  private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;
}

By default, @EqualsAndHashCode  will create equals and hashCode methods that include all member variables. The exclude option can be used to tell Lombok to exclude certain member variables. In the code snippet above, I’ve excluded yearOfManufacture and cubicCapacity from the generated equals and hashCode methods.

@Data

If you want to keep your data classes as lean as possible, you can make use of the @Data  annotation. @Data is a shortcut for @Getter,  @Setter,  @ToString@EqualsAndHashCode, and  @RequiredArgsConstructor.

@ToString
@RequiredArgsConstructor
@EqualsAndHashCode(exclude = { "yearOfManufacture", "cubicCapacity" })
public class Car {
  @Getter @Setter  
private String make;
  @Getter @Setter
  private String model;
  @Getter @Setter
  private String bodyType;
  @Getter @Setter
  private int yearOfManufacture;
  @Getter @Setter
  private int cubicCapacity;  
}

By using @Data, we can reduce the class above to the following:

@Data
public class Car {
  private String make;
  private String model;
  private String bodyType;
  private int yearOfManufacture;
  private int cubicCapacity;  
}

Object Creation With @Builder

The builder design pattern describes a flexible approach to the creation of objects. Lombok helps you implement this pattern with minimal effort. Let’s look at an example using the simple Car class. Suppose we want to be able to create a variety of Car objects, but we want flexibility in terms of the attributes that we set at creation time.

@AllArgsConstructor
public class Car {
  private String make;
  private String model;
  private String bodyType;
  private int yearOfManufacture;
  private int cubicCapacity;  
  private List<LocalDate> serviceDate;
}

Let’s say we want to create a Car, but we only want to set the make and model. Using a standard all argument constructor on Car means that we’d supply only make and model and set the other arguments as null.

Car2 car = new Car2("Ford", "Mustang", null, null, null, null);

This works but it’s not ideal that we have to pass null for the arguments we’re not interested in. We could get around this by creating a constructor that takes only make and model. This is a reasonable solution but its not very flexible. What if we have lots of different permutations of fields that we might use to create a new Car? We’d end up with a bunch of different constructors representing all the possible ways we could instantiate a Car.

A clean, flexible way to solve this problem is with the builder pattern. Lombok helps you implement the builder pattern via the @Builder annotation. When you annotate the Car class with @Builder, Lombok does the following:

  • Adds a private constructor to Car
  • Creates a static CarBuilder class
  • Creates a setter style method on CarBuilder for each member variable in Car
  • Adds a build method on CarBuilder that creates a new instance of @Car.

Each setter style method on CarBuilder returns an instance of itself (CarBuilder). This allows you to chain method calls and provides you with a nice fluent API for object creation. Let’s see it in action.

Car muscleCar = Car.builder().make("Ford")
                             .model("mustang")
                             .bodyType("coupe")
                             .build();

Creating a Car with just make and model is now much cleaner than before. We simply call the generated builder method on Car to get an instance of CarBuilder, then call whatever setter style methods we’re interested in. Finally, we call a build to create a new instance of Car.

Another handy annotation worth mentioning is@Singular. By default, Lombok creates a standard setter style method for collections that takes a collection argument. In the example below, we create a new Car and set a list of service dates.

Car muscleCar = Car.builder().make("Ford")
                   .model("mustang")
                   .serviceDate(Arrays.asList(LocalDate.of(2016, 5, 4)))
                   .build();

Adding @Singularto collection member variables give you an extra method that allows you to add a single item to the collection.

@Builder
public class Car {
  private String make;
  private String model;
  private String bodyType;
  private int yearOfManufacture;
  private int cubicCapacity;  
  @Singular
  private List<LocalDate> serviceDate;
}

We can now add a single service date as follows:

Car muscleCar3 = Car.builder()
                    .make("Ford")
                    .model("mustang")
                    .serviceDate(LocalDate.of(2016, 5, 4))
                    .build();

This is a nice convenience method that helps keep our code clean when dealing with collections during object creation.

Logging

Another great Lombok feature is loggers. Without Lombok, to instantiate a standard SLF4J logger, you typically have something like this:

public class SomeService {
  private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
  public void doStuff(){
    log.debug("doing stuff....");
  }
}

These loggers are clunky and add unnecessary clutter to every class that requires logging. Thankfully, Lombok provides an annotation that creates the logger for you. All you have to do is add the annotation to the class and you’re good to go.

@Slf4j
public class SomeService {
  public void doStuff(){
    log.debug("doing stuff....");
  }
}

I’ve used the @SLF4J annotation here, but Lombok will generate loggers for most common Java logging frameworks. For more logger options, see the documentation.

Lombok Gives You Control

One of the things I really like about Lombok is that it’s unintrusive. If you decide that you want to provide your own method implementation when using the likes of @Getter,  @Setter, or  @ToString, your method will always take precedence over Lombok. This is nice because it allows you to use Lombok most of the time, but still take control when you need to.

Write Less, Do More

I’ve used Lombok on pretty much every project I’ve worked on for the past 4 or 5 years. I like it because it reduces clutter and you end up with cleaner, more concise code that’s easier to read. It won’t necessarily save you a lot of time, as most of the code it generates can be auto-generated by your IDE. With that said, I think the benefits of cleaner code more than justify adding it to your Java stack.

Further Reading

I’ve covered the Lombok features that I use regularly, but there are a bunch more that I haven’t touched on. If you like what you’ve seen so far and want to find out more, head on over and have a look at the Lombok docs.

Java 8 Functional Interfaces – When & How To Use Them?

Functional interfaces, lambda expressions and Stream API – these three features of Java 8 has turned Java programming into new style of programming called functional-style programming. Java is still an object-oriented programming language, but from Java 8, with the introduction of new features, most of the programming is done keeping functions in mind rather than objects. In this article, we will see Java 8 functional interfaces, @FunctionalInterface annotation, java.util.function package and how to use new Java 8 functional interfaces to compose lambda expressions with some simple examples.

Java 8 Functional Interfaces

1) Definition

Functional interfaces are the interfaces which has exactly one abstract method. They may have any number of default methods but must have only one abstract method. Functional interfaces provide only one functionality to implement.

There were functional interfaces exist before Java 8. It is not like that they are the whole new concept introduced only in Java 8. RunnableActionListenerCallable and Comaprator are some old functional interfaces which exist even before Java 8.

The new set of functional interfaces are introduced in Java 8 to make programmer’s job easy while writing lambda expressions. Your lambda expression must implement any one of these functional interfaces. These new functional interfaces are organised under java.util.function package.

2) @FunctionalInterface Annotation

@FunctionalInterface annotation is introduced in Java 8 to represent functional interfaces. Although, it is not compulsory to write functional interface using this annotation. But, if you are using @FunctionalInterface annotation then your interface should contain only one abstract method. If you try to write more than one abstract method, compiler will show the error.

3) java.util.function package

All Java 8 functional interfaces are organised in java.util.function package. Each functional interface in this package represents an operation that can be performed by the lambda expression.

Below table shows the list of all Java 8 functional interfaces along with their abstract method, which operation they represent and when to use them?

Java 8 functional interfaces

4) How To Use Java 8 Functional Interfaces In Real Time?

Let’s define Student class like below. We will be using this class in the subsequent examples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Student
{
    int id;
    
    String name;
    
    double percentage;
    
    String specialization;
    
    public Student(int id, String name, double percentage, String specialization)
    {
        this.id = id;
        
        this.name = name;
        
        this.percentage = percentage;
        
        this.specialization = specialization;
    }
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public double getPercentage() {
        return percentage;
    }
    public String getSpecialization() {
        return specialization;
    }
    @Override
    public String toString()
    {
        return id+"-"+name+"-"+percentage+"-"+specialization;
    }
}

Let listOfStudents be the list of 10 students.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
List<Student> listOfStudents = new ArrayList<Student>();
        
listOfStudents.add(new Student(111, "John", 81.0, "Mathematics"));
        
listOfStudents.add(new Student(222, "Harsha", 79.5, "History"));
        
listOfStudents.add(new Student(333, "Ruth", 87.2, "Computers"));
        
listOfStudents.add(new Student(444, "Aroma", 63.2, "Mathematics"));
        
listOfStudents.add(new Student(555, "Zade", 83.5, "Computers"));
        
listOfStudents.add(new Student(666, "Xing", 58.5, "Geography"));
        
listOfStudents.add(new Student(777, "Richards", 72.6, "Banking"));
        
listOfStudents.add(new Student(888, "Sunil", 86.7, "History"));
        
listOfStudents.add(new Student(999, "Jordan", 58.6, "Finance"));
        
listOfStudents.add(new Student(101010, "Chris", 89.8, "Computers"));

Let’s see how to use 4 important functional interfaces – PredicateConsumerFunction and Supplier using above listOfStudents.

a) Predicate – Tests an object

Predicate represents an operation which takes an argument T and returns a boolean. Use this functional interface, if you want to define a lambda expression which performs some test on an argument and returns true or false depending upon outcome of the test.

For example,

Imagine an operation where you want only a list of “Mathematics” students from the above listOfStudents. Let’s see how to do it using Predicate.

Lambda expression implementing Predicate : Checking specialization of a Student

1
2
3
4
5
6
7
8
9
10
11
Predicate<Student> mathematicsPredicate = (Student student) -> student.getSpecialization().equals("Mathematics");
        
List<Student> mathematicsStudents = new ArrayList<Student>();
        
for (Student student : listOfStudents)
{
    if (mathematicsPredicate.test(student))
    {
        mathematicsStudents.add(student);
    }
}

b) Consumer – Consumes an object

Consumer represents an operation which takes an argument and returns nothing. Use this functional interface If you want to compose a lambda expression which performs some operations on an object.

For example, displaying all students with their percentage.

Lambda expression implementing Consumer : Displaying all students with their percentage

1
2
3
4
5
6
7
8
Consumer<Student> percentageConsumer = (Student student) -> {
        System.out.println(student.getName()+" : "+student.getPercentage());
    };
        
for (Student student : listOfStudents)
{
    percentageConsumer.accept(student);
}

c) Function – Applies to an object

Function represents an operation which takes an argument of type T and returns a result of type R. Use this functional interface if you want to extract some data from an existing data.

For example, extracting only the names from listOfStudents.

Lambda expression implementing Function : Extracting only the names of all students

1
2
3
4
5
6
7
8
Function<Student, String> nameFunction = (Student Student) -> Student.getName();
        
List<String> studentNames = new ArrayList<String>();
        
for (Student student : listOfStudents)
{
    studentNames.add(nameFunction.apply(student));
}

d) Supplier – Supplies the objects

Supplier represents an operation which takes no argument and returns the results of type R. Use this functional interface when you want to create new objects.

Lambda expression implementing Supplier : Creating a new Student

1
2
3
Supplier<Student> studentSupplier = () -> new Student(111111, "New Student", 92.9, "Java 8");
        
listOfStudents.add(studentSupplier.get());

5) Functional Interfaces Supporting Primitive Type

Java 8 has also introduced functional interfaces which support primitive types. For example IntPredicateDoublePredicateLongConsumer etc… (See above table).

If an input or output is a primitive type then using these functional interfaces will enhance the performance of your code. For example, if input to a Predicate is primitive type intthen using intPredicate instead of Predicate will remove unnecessary boxing of input.

Thread Livelock

A livelock is a recursive situation where two or more threads would keep repeating a particular code logic. The intended logic is typically giving opportunity to the other threads to proceed in favor of ‘this’ thread.

A real-world example of livelock occurs when two people meet in a narrow corridor, and each tries to be polite by moving aside to let the other pass, but they end up swaying from side to side without making any progress because they both repeatedly move the same way at the same time.
From Oracle reference docs:

 A thread often acts in response to the action of another thread. If the other thread’s action is also a response to the action of another thread, then livelock may result. As with deadlock, livelocked threads are unable to make further progress. However, the threads are not blocked – they are simply too busy responding to each other to resume work.

For example consider a situation where two threads want to access a shared common resource via a Worker object but when they see that other Worker (invoked on another thread) is also ‘active’, they attempt to hand over the resource to other worker and wait for it to finish. If initially we make both workers active they will suffer from livelock.
main

The Common Resource Class

public class CommonResource {
    private Worker owner;

    public CommonResource (Worker d) {
        owner = d;
    }

    public Worker getOwner () {
        return owner;
    }

    public synchronized void setOwner (Worker d) {
        owner = d;
    }
}

The Worker Class

public class Worker {
    private String name;
    private boolean active;

    public Worker (String name, boolean active) {
        this.name = name;
        this.active = active;
    }

    public String getName () {
        return name;
    }

    public boolean isActive () {
        return active;
    }

    public synchronized void work (CommonResource commonResource, Worker otherWorker) {
        while (active) {
            // wait for the resource to become available.
            if (commonResource.getOwner() != this) {
                try {
                    wait(10);
                } catch (InterruptedException e) {
                   //ignore
                }
                continue;
            }

            // If other worker is also active let it do it's work first
            if (otherWorker.isActive()) {
                System.out.println(getName() +
                            " : handover the resource to the worker " +
                                                       otherWorker.getName());
                commonResource.setOwner(otherWorker);
                continue;
            }

            //now use the commonResource
            System.out.println(getName() + ": working on the common resource");
            active = false;
            commonResource.setOwner(otherWorker);
        }
    }
}

The main class

public class Livelock {

    public static void main (String[] args) {
        final Worker worker1 = new Worker("Worker 1 ", true);
        final Worker worker2 = new Worker("Worker 2", true);

        final CommonResource s = new CommonResource(worker1);

        new Thread(() -> {
            worker1.work(s, worker2);
        }).start();

        new Thread(() -> {
            worker2.work(s, worker1);
        }).start();
    }
}

Output:

There will be never ending recursion of the following output:

Worker 1  : handing over the resource to the worker: Worker 2
Worker 2 : handing over the resource to the worker: Worker 1
Worker 1  : handing over the resource to the worker: Worker 2
Worker 2 : handing over the resource to the worker: Worker 1
Worker 1  : handing over the resource to the worker: Worker 2
Worker 2 : handing over the resource to the worker: Worker 1
    ........

Avoiding Livelock

In above example we can fix the issue by processing the common resource sequentially rather than in different threads simultaneously.

Just like deadlock, there’s no general guideline to avoid livelock, but we have to be careful in scenarios where we change the state of common objects also being used by other threads, for example in above scenario. the Worker object.

Filter Vs Interceptor

Filter: 

filter dynamically intercepts requests and responses to transform or use the information contained in the requests or responses. Filters typically do not themselves create responses, but instead provide universal functions that can be “attached” to any type of servlet or JSP page.

Filters can perform many different types of functions. We’ll discuss examples of the italicized items in this paper:

  •   Authentication-Blocking requests based on user identity.
  •   Logging and auditing-Tracking users of a web application.
  •   Image conversion-Scaling maps, and so on.
  •   Data compression-Making downloads smaller.
  •   Localization-Targeting the request and response to a particular locale.

Request Filters can:

  •    perform security checks
  •   reformat request headers or bodies
  •   audit or log requests

Response Filters can:

  •   Compress the response stream
  •   append or alter the response stream
  •   create a different response altogether

Examples that have been identified for this design are

  •   Authentication Filters
  •   Logging and Auditing Filters
  •   Image conversion Filters
  •   Data compression Filters
  •   Encryption Filters
  •   Tokenizing Filters
  •   Filters that trigger resource access events
  •   XSL/T filters
  •   Mime-type chain Filter

Interceptors

Interceptors are used in conjunction with Java EE managed classes to allow developers to invoke interceptor methods in conjunction with method invocations or lifecycle events on an associated target class. Common uses of interceptors are logging, auditing, or profiling.

Interceptors can be defined within a target class as an interceptor method, or in an associated class called an interceptor class. Interceptor classes contain methods that are invoked in conjunction with the methods or lifecycle events of the target class.

1)  Cookie Interceptor

2)  Checkbox Interceptor

3)  FileUpload Interceptor

Difference:

A Servlet Filter is used in the web layer only, you can’t use it outside of a web context. Interceptors can be used anywhere. That’s the main difference.

for authentication of web pages you would use a servlet filter. For security stuff in your business layer or logging/bugtracing (a.k.a. independent of the web layer) you would use an Interceptor.

Apart from the fact that both Interceptors and filters are based on intercepting filter,there are few differences when it comes to Struts2.

Filters: (1)Based on Servlet Specification (2)Executes on the pattern matches on the request.(3) Not configurable method calls
Interceptors: (1)Based on Struts2. (2)Executes for all the request qualifies for a front controller( A Servlet filter ).And can be configured to execute additional interceptor for a particular action execution.(3)Methods in the Interceptors can be configured whether to execute or not by means of excludemethods or includeMethods

How JSON Web Token (JWT) Secures Your API

Image title

You’ve probably heard that JSON Web Token (JWT) is the current state-of-the-art technology for securing APIs.

Like most security topics, it’s important to understand how it works (at least, somewhat) if you’re planning to use it. The problem is that most explanations of JWT are technical and headache inducing.

Let’s see if I can explain how JWT can secure your API without crossing your eyes!

API Authentication

Certain API resources need restricted access. We don’t want one user to be able to change the password of another user, for example.

That’s why we protect certain resources make users supply their ID and password before allowing access— in other words, we authenticate them.

The difficulty in securing an HTTP API is that requests are stateless — the API has no way of knowing whether any two requests were from the same user or not.

So why don’t we require users to provide their ID and password on every call to the API? Only because that would be a terrible user experience.

JSON Web Token

What we need is a way to allow a user to supply their credentials just once, but then be identified in another way by the server in subsequent requests.

Several systems have been designed for doing this, and the current state-of-the-art standard is JSON Web Token.

There’s a great article on the topic, which makes a good analogy about how JSON web tokens work.

Instead of an API, imagine you’re checking into a hotel. The “token” is the plastic hotel security card that you get that allows you to access your room, and the hotel facilities, but not anyone else’s room.

When you check out of the hotel, you give the card back. This is analogous to logging out.

Structure of the Token

Normally, a JSON web token is sent via the header of HTTP requests. Here’s what one looks like:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

In fact, the token is the part after “Authorization: Bearer,” which is just the HTTP header info.

Before you conclude that it’s incomprehensible gibberish, there are a few things you can easily notice.

Firstly, the token consists of three different strings, separated by a period. These three string are base 64 encoded and correspond to the header, the payload, and the signature.

// Header
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
// Payload
eyJzdWIiOiIxMjM0NTY3ODkwIn0
// Signature
dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

Note: base 64 is a way of transforming strings to ensure they don’t get screwed up during transport across the web. It is not a kind of encryption and anyone can easily decode it to see the original data.

We can decode these strings to get a better understand of the structure of JWT.

The following is the decoded header from the token. The header is meta information about the token. It doesn’t tell us much to help build our basic understanding, so we won’t get into any detail about it.

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

The payload is of much more interest. The payload can include any data you like, but you might just include a user ID if the purpose of your token is API access authentication.

{
  "userId": "1234567890"
}

It’s important to note that the payload is not secure. Anyone can decode the token and see exactly what’s in the payload. For that reason, we usually include an ID rather than sensitive identifying information like the user’s email.

Even though this payload is all that’s needed to identify a user on an API, it doesn’t provide a means of authentication. Someone could easily find your user ID and forge a token if that’s all that was included.

So this brings us to the signature, which is the key piece for authenticating the token.

Hashing Algorithms

Before we explain how the signature works, we need to define what a hashing algorithm is.

To begin with, it’s a function for transforming a string into a new string called a hash. For example, say we wanted to hash the string “Hello, world.” Here’s the output we’d get using the SHA256 hashing algorithm:

4ae7c3b6ac0beff671efa8cf57386151c06e58ca53a78d83f36107316cec125f

The most important property of the hash is that you can’t use the hashing algorithm to identify the original string by looking at the hash.

There are many different types of hashing algorithms, but SHA256 is commonly used with JWT.

In other words, we can’t take the above hash and directly figure out that the original string was “Hello, world.” The hash is complicated enough that guessing the original string would be infeasible.

JWT Signature

So, coming back to the JWT structure, let’s now look at the third piece of the token, the signature. This actually needs to be calculated:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  "secret string"
);

Here’s an explanation of what’s going on here:

Firstly, HMACSHA256 is the name of a hashing function and takes two arguments: the string to hash, and the “secret” (defined below).

Secondly, the string we hash is the base 64 encoded header, plus the base 64 encoded payload.

Thirdly, the secret is an arbitrary piece of data that only the server knows.

Q. Why include the header and payload in the signature hash?

This ensures the signature is unique to this particular token.

Q. What’s the secret?

To answer this, let’s think about how you would forge a token.

We said before that you can’t determine a hash’s input from looking at the output. However, since we know that the signature includes the header and payload, as those are public information, if you know the hashing algorithm (hint: it’s usually specified in the header), you could generate the same hash.

But the secret, which only the server knows, is not public information. Including it in the hash prevents someone from generating their own hash to forge the token. And since the hash obscures the information used to create it, no one can figure out the secret from the hash, either.

The process of adding private data to a hash is called salting and makes cracking the token almost impossible.

Authentication Process

So now, you have a good idea of how a token is created. How do you use it to authenticate your API?

Login

A token is generated when a user logs in and is stored in the database with the user model.

loginController.js:

if (passwordCorrect) {
  user.token = generateToken(user.id);
  user.save();
}

The token then gets attached as the authorization header in the response to the login request.

loginController.js:

if (passwordCorrect) {
  user.token = generateToken(user.id);
  user.save();
  res.headers("authorization", `Bearer ${token}`).send();
}

Authenticating Requests

Now that the client has the token, they can attach it to any future requests to authentically identify the user.

When the server receives a request with an authorization token attached, the following happens:

  1. It decodes the token and extracts the ID from the payload.
  2. It looks up the user in the database with this ID.
  3. It compares the request token with the one that’s stored with the user’s model. If they match, the user is authenticated.

authMiddleware.js:

const token = req.header.token;
const payload = decodeToken(token);
const user = User.findById(payload.id);
if (user.token = token) {
  // Authorized
} else {
  // Unauthorized
}

Logging Out

If the user logs out, simply delete the token attached to the user model, and now the token will no longer work. A user will need to log in again to generate a new token.

logoutController.js:

user.token = null;
user.save();

Wrapping Up

So, that’s a very basic explanation of how you can secure an API using JSON Web Tokens. I hope your head doesn’t hurt too much.

There’s a lot more to this topic, though, so here’s some additional reading:

Kafka Technical Overview

Objective

In this article series, we will learn Kafka basics, Kafka delivery semantics, and configuration to achieve different semantics, Spark Kafka integration, and optimization. In Part 1 of this series we’ll look at Kafka basics.

Problem Statement

The following could be some of the problem statements:

  • Many sources and target systems to integrate. Generally, integration of many systems involves complexities like dealing with many protocols, messaging formats, etc.
  • Message systems handle high volume streams.

Integration of multiple source and target systems

Use Cases

Some of the use cases include:

  • Streaming processing
  • Tracking user activity, log aggregation, etc.
  • De-coupling systems

Integration of multiple source and target systems using Kafka

What Is Kafka?

Kafka is a horizontally scalable, fault tolerant, and fast messaging system. It’s a pub-sub model in which various producers and consumers can write and read. It decouples source and target systems. Some of the key features are:

  • Scale to 100s of nodes.
  • Can handle millions of messages per second.
  • Real-time processing (~10ms).

Kafka producer consumer integration

Key Terminologies

Topic, Partitions, and Offsets

A topic is a specific stream of data. It is very similar to a table in a NoSQL database. Like tables in a NoSQL database, the topic is split into partitions that enable topics to be distributed across various nodes. Like primary keys in tables, topics have offsets per partitions. You can uniquely identify a message using its topic, partition and offset.

DB Table and Kafka Topic analogy

Partitions

Partitions enable topics to be distributed across the cluster. Partitions are a unit of parallelism for horizontal scalability. One topic can have more than one partition scaling across nodes.

Kafka topic distribution across brokers

Messages are assigned to partitions based on partition keys; if there are no partition keys then the partition is randomly assigned. It’s important to use the correct key to avoid hotspots.

Kafka partitions & offsets in a topic

Each message in a partition is assigned an incremental id called an offset. Offsets are unique per partition and messages are ordered only within a partition. Messages written to partitions are immutable.

Kafka Architecture

The diagram below shows the architecture of Kafka.

Kafka Architecture

ZooKeeper

ZooKeeper is a centralized service for managing distributed systems. It offers hierarchical key-value store, configuration, synchronization, and name registry services to the distributed system it manages. ZooKeeper acts as ensemble layer (ties things together) and ensures high availability of the Kafka cluster. Kafka nodes are also called brokers. It’s important to understand that Kafka cannot work without ZooKeeper.

From the list of ZooKeeper nodes, one of the nodes is elected as a leader and the rest of the nodes follow the leader. In the case of a ZooKeeper node failure, one of the followers is elected as leader. More than 1 node is strongly recommended for high availability and more than 7 is not recommended.

ZooKeeper stores metadata and the current state of the Kafka cluster. For example details, like topic name, the number of partitions, replication, leader details of petitions, and consumer group details are stored in ZooKeeper. You can think of ZooKeeper like a project manager who manages resources in the project and remembers the state of the project.

Zookeeper leader and follower in a Kafka cluster

Key things to remember:

  • Manages list of brokers.
  • Elects broker leaders when a broker goes down.
  • Sends notifications on a new broker, new topic, deleted topic, lost brokers, etc.
  • From Kafka 0.10 on, consumer offsets are not stored in ZooKeeper, only the metadata of the cluster is stored in ZooKeepr.
  • The leader in ZooKeepr handles all writes and follower ZooKeepr handle only reads.

Broker

A broker is a single Kafka node that is managed by ZooKeeper. Set of brokers form a Kafka cluster. Topics that are created in Kaka are distributed across brokers based on the partition, replication, and other factors. When a broker node fails based on the state stored in zookeeper it automatically rebalances the cluster and also in case if a leader partition is lost then one of the follower petition is elected as the leader.

Broker and topic in a Kafka cluster

You can think of broker as a team leader who takes care of the assigned tasks, in case if a team lead isn’t available then the manager takes care of assigning tasks to other team members.

Replication

Partition replication in a Kafka cluster

A replication is making a copy of a partition available in another broker. Replication enables Kafka to be fault tolerant. When a partition of the topic is available in multiple brokers then one of the partitions in a broker is elected as leader and rest of the replication of partition are followers.

Partition replication by followers in a Kafka cluster

Replication enables Kafka to be fault tolerant even when a broker is down. For example, Topic B partition 0 is stored in both broker 0 and broker 1. Both producers and consumers are severed only by the leader. In case of a broker failure the partition from another broker is elected as a leader and it starts serving the producers and consumer groups. Replica partitions that are in sync with the leader are flagged as ISR (In Sync Replica).

Broker failure and partition leader election in a Kafka cluster

IT Team and Kafka Cluster Analogy

The diagram below depicts an analogy of an IT team and Kafka cluster.

IT Team and Kafka cluster analogy

Summary

Below is the summary of core components in Kafka.

Kafka component relationship

  • ZooKeeper manages Kafka brokers and their metadata.
  • Brokers are horizontally scalable Kafka nodes that contain topics and it’s replications.
  • Topics are message streams with one or more partitions.
  • Partitions contains messages with unique offsets per partition.
  • Replication enables Kafka to be fault tolerant using follower partitions.

Refer Kafka quickstart for Kafka setup.

Deep, Shallow and Lazy Copy with Java Examples

In object-oriented programming, object copying is creating a copy of an existing object, the resulting object is called an object copy or simply copy of the original object.There are several ways to copy an object, most commonly by a copy constructor or cloning.

We can define Cloning as “create a copy of object” Shallow, deep and lazy copy is related to cloning process
these are actually the ways for creating copy object.

Shallow Copy

  • Whenever we use default implementation of clone method we get shallow copy of object means it creates new instance and copies all the field of object to that new instance and returns it as object type, we need to explicitly cast it back to our original object. This is shallow copy of the object.
  • clone() method of the object class support shallow copy of the object. If the object contains primitive as well as nonprimitive or reference type variable in shallow copy, the cloned object also refers to the same object to which the original object refers as only the object references gets copied and not the referred objects themselves.
  • That’s why the name shallow copy or shallow cloning in Java. If only primitive type fields or Immutable objects are there then there is no difference between shallow and deep copy in Java.
//code illustrating shallow copy
public class Ex {
 
    private int[] data;
 
    // makes a shallow copy of values
    public Ex(int[] values) {
        data = values;
    }
 
    public void showData() {
        System.out.println( Arrays.toString(data) );
    }
}

The above code shows shallow copying. data simply refers to the same array as vals.

This can lead to unpleasant side effects if the elements of values are changed via some other reference.

public class UsesEx{
 
    public static void main(String[] args) {
        int[] vals = {3, 7, 9};
        Ex e = new Ex(vals);
        e.showData(); // prints out [3, 7, 9]
        vals[0] = 13;
        e.showData(); // prints out [13, 7, 9]
 
        // Very confusing, because we didn't
        // intentionally change anything about 
        // the object e refers to.
    }
}
Output 1 : [3, 7, 9]
Output 2 : [13, 7, 9]

Deep Copy

  • Whenever we need own copy not to use default implementation we call it as deep copy, whenever we need deep copy of the object we need to implement according to our need.
  • So for deep copy we need to ensure all the member class also implement the Cloneable interface and override the clone() method of the object class.

A deep copy means actually creating a new array and copying over the values.

// Code explaining deep copy
public class Ex {
     
    private int[] data;
 
    // altered to make a deep copy of values
    public Ex(int[] values) {
        data = new int[values.length];
        for (int i = 0; i < data.length; i++) {
            data[i] = values[i];
        }
    }
 
    public void showData() {
        System.out.println(Arrays.toString(data));
    }
}

public class UsesEx{
 
    public static void main(String[] args) {
        int[] vals = {3, 7, 9};
        Ex e = new Ex(vals);
        e.showData(); // prints out [3, 7, 9]
        vals[0] = 13;
        e.showData(); // prints out [3, 7, 9]
 
       // changes in array values will not be 
       // shown in data values. 
    }
}
Output 1 : [3, 7, 9]
Output 2 : [3, 7, 9]

Changes to the array vals will not result in changes to the array data.

when to use what
There is no hard and fast rule defined for selecting between shallow copy and deep copy but normally we should keep in mind that if an object has only primitive fields, then obviously we should go for shallow copy, but if the object has references to other objects, then based on the requirement, shallow copy or deep copy should be done. If the references are not updated then there is no point to initiate a deep copy.

Lazy Copy
A lazy copy can be defined as a combination of both shallow copy and deep copy. The mechanism follows a simple approach – at the initial state, shallow copy approach is used. A counter is also used to keep a track on how many objects share the data. When the program wants to modify the original object, it checks whether the object is shared or not. If the object is shared, then the deep copy mechanism is initiated.

Summary
In shallow copy, only fields of primitive data type are copied while the objects references are not copied. Deep copy involves the copy of primitive data type as well as objet references. There is no hard and fast rule as to when to do shallow copy and when to do a deep copy. Lazy copy is a combination of both of these approaches.

This article is contributed by Abhishek Gupta. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.