Explore Annotations in Java 8

Java 8

There are plenty of annotations in the sea!

Annotations were introduced back in Java SE 1.5, and the purpose of the Java annotation was to allow programmers to write metadata about their program. As per the Oracle Docs, the definition of an annotation is: “annotations, a form of metadata, provide data about a program that is not part of the program itself.” 

Annotations can be used anywhere in your code, i.e. within classes, methods, and variables. From Java 8, it can be used in type declaration as well.

You may also like:  Creating Annotations in Java

Annotated code does not have any direct relation to the program. It is only information to other programs or JVM who can use this information for their purpose.

Annotation Syntax

An annotation is declared using the character @ and the annotation name, i.e. @AnnotationName. When the compiler goes through this element, it understands that this is an annotation. For example:

@ExampleAnnotation
public class SampleClass {
}

The annotation above is called ExampleAnnotation and it is annotating the class SampleClass.

An annotation may have properties. These are given in key-value pairs when declaring the annotations. For example:

@ExampleAnnotation(name = ”first name”, age = 35)
public void simpleMethod() {
}

Note that here, the ExampleAnnotation is annotating a method. If an annotation has only one property, then the name of the property can be skipped when declaring the annotation. Below is an example:

@ExampleAnnotation(“I am the only property”)
public void simpleMethod() {
}

Multiple annotations are possible for an element. Check it out:

@Annotation1
@Annotation2(“Another Annotation”)
public class SimpleClass {
}

Form J2SE 8; the same annotations can be used multiple times to an element, for example:

@ExampleAnnotation(“Annotation used”)
@ExampleAnnotation(“Annotation repeated”)
public class SimpleClass {
}

This will be discussed in detail in the @Repeatable annotation section.

Predefined Annotations in Java

Java comes with a set of predefined annotations. The annotations available in Java Core are explained below:

@Retention: This annotation annotates other annotations and indicates the scope of the annotated annotations. Some possible values are:

  •  SOURCE — indicates that this annotation is available only in the source code and ignored by the Compiler and JVM, and hence not available in runtime.
  •  CLASS — indicates that this annotation is available to the Compiler but not JVM, and hence not available during runtime.
  •  RUNTIME — indicates that the annotation is available to JVM, and hence can be used in runtime.

@Target: This annotation indicates the target elements an annotation can be applied to:

  • ANNOTATION_TYPE — means that the annotation can be applied to other annotations.
  • CONSTRUCTOR — can be applied to a constructor.
  • FIELD — can be applied to a field or property.
  • LOCAL_VARIABLE — can be applied to a local variable.
  • METHOD— can be applied to a method.
  • PACKAGE— can be applied to a package declaration.
  • PARAMETER — can be applied to the parameters of a method.
  • TYPE — can be applied to ClassInterfaceAnnotation, or enum declaration.
  • PACKAGE— can be applied to package declaration.
  • TYPE_PARAMETER — can be applied to the type parameter declaration.
  • TYPE_USE — can be applied to any type

@Documented: This annotation can be applied to other annotations. It means that the annotated elements will be documented using the Javadoc tool.

@Inherited: By default, annotations are not inherited by subclasses. But if an annotation is marked as  @Inherited, that means when a class is annotated with that annotation, the annotation is also inherited by subclasses. This annotation is applicable only for class. Note that if an interface is annotated with that annotation, the annotation is not inherited by implementing classes.

@Deprecated: Indicates that the annotated element should not be used. This annotation gets the compiler to generate a warning message. It can be applied to methods, classes, and fields.

@SuppressWarnings: Indicates the compiler not to produce warnings for a specific reason or reasons.

@Override: This annotation informs the compiler that the element is overriding an element of the superclass. It is not mandatory to use when overriding elements, but it helps the compiler to generate errors when the overriding is not done correctly, for example, if the subclass method parameters are different than the superclass ones, or if the return type does not match.

@SafeVarargs: Asserts that the code of the method or constructor does not perform unsafe operations on its arguments.

@Repeatable Annotation

This annotation indicates that an annotation annotated with this one can be applied more than once to the same element.

This concept can be more clearly understood with the help of an example.

To use this annotation, first, we need to define an annotation, which can be used to annotate repeatedly to a class.

@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.TYPE_USE)
@Repeatable (RepeatableAnnotationContainer.class)
public @interface RepeatableAnnotation() {
    String values();
}

Here, RepeatableAnnotation is an annotation that can be used repeatedly to annotate an element.

Next, we need to define the RepeatableAnnotationContainer annotation type. This is basically a container of the annotation type, and it must have an array of the RepeatableAnnotation annotation type.

public @interface RepeatableAnnotationContainer {
    RepeatableAnnotation [] value();
}

Now, the Repeatable annotation can be used multiple times to annotate any element.

@RepeatableAnnotation (“I am annotating the class”)
@RepeatableAnnotation (“I am annotating the class again”)
@RepeatableAnnotation (“I am annotating the class for the third time”)
public class RepeatedAnnotationExample {
}

Next, to retrieve the value of the annotation in the program, the arrays of the container will be retrieved first. Each element of the array will contain one value. For example:

@RepeatableAnnotation (“I am annotating the class”)
@RepeatableAnnotation (“I am annotating the class again”)
@RepeatableAnnotation(“I am annotating the class for the third time”)
public class RepeatableAnnotationExample {
    public static void main(String [] args) {
        Class object = RepeatableAnnotationExample.class
        Annotation[] annotations = object.getAnnotations();
for (Annotation annotation : annotations) {
    RepeatableAnnotationContainer rac = (RepeatableAnnotationContainer) annotation;
    RepeatableAnnotation [] raArray = rac.value();
    for (RepeatableAnnotation ra : raArray) {
        System.out.println(ra.value);
    }
}
}

When the above code is executed, the output will be:

I am annotating the class
I am annotating the class again
I am annotating the class for the third time.

Type Annotations

After the release of Java 8, annotations can be applied to any type of use. This means that annotations can be used anywhere we use a type. As an example, when creating a class instance using a new operator, typecasting, when implementing an interface using an implements clause, throws a clause, etc., this form of annotation is called a type annotation.

The purpose of this type of annotation is to support improved analysis of Java programs and ensure stronger type checking. Up to the Java 8 release, Java contained no type-checking framework, but using the type annotation, a type checking framework can be written and used in java program.

As an example, suppose we want a particular variable to never be assigned null throughout our program. We can write a custom plugin NonNull to check this and annotate that particular variable with that custom annotation. The variable declaration should then be:

@NonNull String notNullString;

When the code is compiled, the compiler checks for potential problems and raised warnings when any such code is found where the variable may be assigned a null value.

Custom Annotations

Java allows programmers to define and implement custom annotations. The syntax to define custom annotations is:

public @interface CustomAnnotation { }

This creates a new annotation type called CustomAnnotation. The @interface keyword is used to define a custom annotation.

When defining custom annotations, two mandatory attributes must be defined for the annotation. Other attributes can be defined here, but these two are important and mandatory. These two attributes are the Retention Policy and Target.

These two attributes are declared in the form of annotations to that custom annotation. Also, properties to the annotations can be defined when defining the custom annotation. For example:

@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.ELEMENT)
public @interface CustomAnnotation {
    public String name() default “Mr Bean”;
    public String dateOfBirth();
}

In the above custom annotation, the Retention Policy is RUNTIME, that means it is available to the JVM at runtime and the Target is ELEMENT, which means it can be annotated to any element type.

Also, it has two properties: name with the default value Mr Bean and one dateOfBirth with no default value.

Note that the properties declared as Method don’t have any parameter and throws clause. Also, the return type is restricted to String, class, enums, and annotations and arrays of the mentioned return types.

Now, we can use our custom annotation in the following way:

@CustomAnnotation (dateOfBirth = “1980-06-25)
public class CustomAnnotatedClass {
}

Similarly, a custom annotation for methods can be created using the @Target ElementType.METHOD) annotation and can be used to annotate any method.

Retrieving Annotations and its Properties

The Java Reflection API contains several methods that can be used to retrieve in runtime annotations from classes, methods, and other elements.

The interface that contains all of these methods is the AnnotatedElement. The most important ones are:

  • getAnnotations(): Returns all annotations for the given element, also the ones that are not explicitly defined in the element definition.
  • isAnnotationPresent(annotation): Checks if the passed annotation is available or not in the current element.
  • getAnnotation(class): Retrieves a specific annotation passed as a parameter. Returns null if this annotation is not present for the given element.

This class is implemented by  java.lang.Class,  java.lang.reflect.Method, and java.lang.reflect.Field, among others, so it can be used basically with any kind of Java element.

The following program demonstrates how to get information about our defined custom annotation:

public static void main(String [] args) {
Class object = CustomAnnotatedClass.class; 
// Retrieve all annotations from the class 
Annotation[] annotations = object.getAnnotations(); 
for( Annotation annotation : annotations ) { 
System.out.println(annotation); 
} 
// Checks if an annotation is present 
if( object.isAnnotationPresent( CustomAnnotationClass.class ) ) { 
// Gets the desired annotation 
Annotation annotation = object.getAnnotation(CustomAnnotationClass.class) ; 
System.out.println(annotation); 
} 
// fetch the attributes of the annotation
for(Annotation annotation : annotations) { 
System.out.println(“name:  + annotation.name());
System.out.println(“Date of Birth: + annotation.dateOfBirth()); 
} 
// the same for all methods of the class 
for( Method method : object.getDeclaredMethods() ) { 
if( method.isAnnotationPresent( CustomAnnotationMethod.class ) ) { 
Annotation annotation = method.getAnnotation(CustomAnnotationMethod.class ); 
System.out.println( annotation ); 

} 
}
}

Conclusion

Annotations gradually became an essential part of developing any enterprise application using the J2EE stack. Nowadays, almost all popular libraries use annotations for different purposes, like code quality analysis, unit testing, XML parsing, dependency injection, and others. A few of the popular libraries that use annotations extensively are Junit, Hibernate, Spring MVC, Findbugs, JAXB, JUnit.

Further Reading

How Do Annotations Work in Java?

Creating Annotations in Java

A Guide to Spring Framework Annotations

14 Tips for Writing Spring MVC Controllers

Man writing a Spring MVC Controller

Here are the best tips and tricks for writing Spring MVC Controllers.

In this article, I’m going to share with you some of the fundamental techniques and best practices for writing a controller class with the Spring MVC framework. Typically, in Spring MVC, we write a controller class to handle requests coming from the client.

Then, the controller invokes a business class to process business-related tasks, and then redirects the client to a logical view name, which is resolved by Spring’s dispatcher servlet in order to render results or output. That completes a round trip of a typical request-response cycle.

You may also like: How Spring MVC Really Works

1. Using the @Controller Stereotype

This is the simplest way to create a controller class that can handle one or multiple requests. Just by annotating a class with the @Controller stereotype, for example:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
    @RequestMapping("/")
    public String visitHome() {
        // do something before returning view name
        return "home";
    }
}

As you can see, the visitHome() method handles requests coming to the application’s context path (/) by redirecting to the view named home.

NOTE: the @Controller stereotype can only be used when annotation-driven is enabled in Spring’s configuration file:

<annotation-driven />

When annotation-driven is enabled, the Spring container automatically scans for classes under the package specified in the following statement:

<context:component-scan base-package="net.codejava.spring" />

The classes annotated by the @Controller annotation are configured as controllers. This is most preferable because of its simplicity: There’s no need to declare beans for controllers in the configuration file.

NOTE: By using the @Controller annotation, you can have a multi-actions controller class that is able to serve multiple different requests. For example:

@Controller
public class MultiActionController {
    @RequestMapping("/listUsers")
    public ModelAndView listUsers() {

    }
    @RequestMapping("/saveUser")
    public ModelAndView saveUser(User user) {

    }
    @RequestMapping("/deleteUser")
    public ModelAndView deleteUser(User user) {

    }
}

As you can see in the above controller class, there are three handler methods that process three different requests  /listUsers/saveUser, and /deleteUser, respectively.

2. Implementing the Controller Interface

Another (and maybe classic) way of creating a controller in Spring MVC is having a class implement the Controller interface. For example:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

public class MainController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        System.out.println("Welcome main");
        return new ModelAndView("main");
    }
}

The implementing class must override the handleRequest() method, which will be invoked by the Spring dispatcher servlet when a matching request comes in. The request URL pattern handled by this controller is defined in the Spring’s context configuration file as follows:

<bean name="/main" class="net.codejava.spring.MainController" />

However, a drawback of this approach is that the controller class cannot handle multiple request URLs.

3. Extending the AbstractController Class

If you want to easily control the supported HTTP methods, session, and content caching. having your controller class extended the AbstractController class is ideal. Consider the following example:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
public class BigController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
        System.out.println("You're big!");
        return new ModelAndView("big");
    }
}

This creates a single-action controller with configurations regarding the supported methods, session, and caching, which can then be specified in the bean declaration of the controller. For example:

<bean name="/big" class="net.codejava.spring.BigController">
    <property name="supportedMethods" value="POST"/>
</bean>

This configuration indicates that only the POST method is supported by this controller’s hander method. For other configuration (session, caching), see AbstractController.

Spring MVC also offers several controller classes designed for specific purposes, including:

4. Specifying URL Mapping for the Handler Method

This is the mandatory task you must do when coding a controller class, which is designed for handling one or more specific requests. Spring MVC provides the @RequestMapping annotation, which is used for specifying URL mapping. For example:

@RequestMapping("/login")

That maps the URL pattern /login to be handled by the annotated method or class. When this annotation is used at the class level, the class becomes a single-action controller. For example:

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

@Controller
@RequestMapping("/hello")
public class SingleActionController {
    @RequestMapping(method = RequestMethod.GET)
    public String sayHello() {
        return "hello";
    }
}

When the @RequestMapping annotation is used at the method level, you can have a multi-action controller. For example:

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

@Controller
public class UserController {
    @RequestMapping("/listUsers")
    public String listUsers() {
        return "ListUsers";
    }
    @RequestMapping("/saveUser")
    public String saveUser() {
        return "EditUser";
    }
    @RequestMapping("/deleteUser")
    public String deleteUser() {
        return "DeleteUser";
    }
}

The @RequestMapping annotation can be also used for specifying multiple URL patterns to be handled by a single method. For example:

@RequestMapping({"/hello", "/hi", "/greetings"})

In addition, this annotation has other properties that may be useful in some cases, e.g. the method property which is covered in the next section.

5. Specifying HTTP Request Methods for the Handler Method

You can specify which HTTP method (GET, POST, PUT, …) is supported by a handler method using the method property of the  @RequestMapping annotation. Here’s an example:

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

@Controller
public class LoginController {
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String viewLogin() {
        return "LoginForm";
    }
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String doLogin() {
        return "Home";
    }
}

As you can see, this controller has two methods that handle the same URL pattern /login, but the former is for the GET method and the latter is for the POST method.

For more information about using the @RequestMapping annotation, see @RequestMapping annotation.

6. Mapping Request Parameters to Handler Method

One of the cool features of Spring MVC is that you can retrieve request parameters as regular parameters of the handler method by using the  @RequestParam annotation. This is a good way to decouple the controller from the  HttpServletRequest interface of the Servlet API.

Let’s look at various examples. Consider the following method:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public String doLogin(@RequestParam String username,
                      @RequestParam String password) {

}

Spring binds the method parameters username and password to the HTTP request parameters with the same names. That means you can invoke a URL as follows (if the request method is GET):

http://localhost:8080/spring/login?username=scott&password=tiger

Type conversion is also done automatically. For example, if you declare a parameter of type integer as follows:

@RequestParam int securityNumber

Then, Spring will automatically convert value of the request parameter (String) to the specified type (integer) in the handler method.

In case the parameter name is different than the variable name, you can specify the actual name of the parameter as follows:

@RequestParam("SSN") int securityNumber

The @RequestParam annotation also has two additional attributes, which might be useful in some cases. The required attribute specifies whether the parameter is mandatory or not. For example:

@RequestParam(required = false) String country

That means the parameter country is optional; hence, it can be missing from the request. In the above example, the variable country will be null if there is no such parameter present in the request.

Another attribute is defaultValue, which can be used as a fallback value when the request parameter is empty. For example:

@RequestParam(defaultValue = "18") int age

Spring also allows us to access all parameters as a Map object if the method parameter is of type  Map<String, String>. For example:

doLogin(@RequestParam Map<String, String> params)

Then the map params contains all request parameters in the form of key-value pairs. For more information about using the @RequestParam annotation, see @RequestParam annotation.

7. Returning Model and View

After business logic is processed, a handler method should return a view, which is then resolved by the Spring’s dispatcher servlet. Spring allows us to return either a String or a ModelAndView object from the handler method. In the following example, the handler method returns a String and represents a view named LoginForm:

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String viewLogin() {
    return "LoginForm";
}

That’s the simplest way of returning a view name. But if you want to send additional data to the view, you must return a ModelAndView object. Consider the following handler method:

@RequestMapping("/listUsers")
public ModelAndView listUsers() {
    List<User> listUser = new ArrayList<>();
    // get user list from DAO...
    ModelAndView modelView = new ModelAndView("UserList");
    modelView.addObject("listUser", listUser);
    return modelView;
}

As you can see, this handler method returns a ModelAndView object that holds the view name UserList and a collection of User objects, which can be used in the view.

Spring is also very flexible, as you can declare the ModelAndView object as a parameter of the handler method instead of creating a new one. Thus, the above example can be re-written as follows:

@RequestMapping("/listUsers")
public ModelAndView listUsers(ModelAndView modelView) {
    List<User> listUser = new ArrayList<>();
    // get user list from DAO...
    modelView.setViewName("UserList");
    modelView.addObject("listUser", listUser);
    return modelView;
}

You can learn more about the ModelAndView class by visiting: ModelAndView class.

8. Putting Objects Into the Model

In an application that follows the MVC architecture, the controller (C) should pass data into the model (M), which is then used in the view (V). As we see in the previous example, the addObject() method of the ModelAndView class is for putting an object to the model, in form of name-value pair:

modelView.addObject("listUser", listUser);
modelView.addObject("siteName", new String("CodeJava.net"));
modelView.addObject("users", 1200000);

Again, Spring is very flexible. You can declare a parameter of type Map in the handler method; Spring uses this map to store objects for the model. Let’s see another example:

@RequestMapping(method = RequestMethod.GET)
public String viewStats(Map<String, Object> model) {
    model.put("siteName", "CodeJava.net");
    model.put("pageviews", 320000);
    return "Stats";
}

This is even simpler than using the ModelAndView object. Depending on your taste, you can use either Map or ModelAndView object. Thanks for the flexibility of Spring.

9. Redirection in Handler Method

In case you want to redirect the user to another URL if a condition is met, just append  redirect:/ before the URL. The following code snippet gives an example:

// check login status....
if (!isLogin) {
    return new ModelAndView("redirect:/login");
}
// return a list of Users

In the above code, the user will be redirected to the  /login URL if it is not logged in.

10. Handling Form Submission and Form Validation

Spring makes it easy to handle form submission by providing the  @ModelAttribute annotation for binding form fields to a form backing object, and the BindingResult interface for validating form fields. The following code snippet shows a typical handler method that is responsible for handling and validating form data:

@Controller
public class RegistrationController {
    @RequestMapping(value = "/doRegister", method = RequestMethod.POST)
    public String doRegister(
        @ModelAttribute("userForm") User user, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            // form validation error
        } else {            
            // form input is OK
        }
        // process registration...
        return "Success";
    }
}

Learn more about the  @ModelAttribute annotation and the BindingResult interface from Spring’s official documentation:

11. Handling File Upload

Spring also makes it easy to handle file upload within a handler method, by automatically binding upload data to an array of CommonsMultipartFile objects. Spring uses Apache Commons FileUpload as the underlying multipart resolver.

The following code snippet shows how easy it is to get files uploaded from the client:

@RequestMapping(value = "/uploadFiles", method = RequestMethod.POST)
public String handleFileUpload(
        @RequestParam CommonsMultipartFile[] fileUpload) throws Exception {
    for (CommonsMultipartFile aFile : fileUpload){
        // stores the uploaded file
        aFile.transferTo(new File(aFile.getOriginalFilename()));
    }
    return "Success";
}

You can learn the complete solution for handling file upload with Spring MVC by following this Spring MVC File Upload Tutorial.

12. Autowiring Business Classes in the Controller

A controller should delegate the processing of business logic to relevant business classes. For this purpose, you can use the @Autowired annotation to let Spring automatically inject the actual implementation of a business class to the controller. Consider the following code snippet of a controller class:

@Controller
public class UserController {
    @Autowired
    private UserDAO userDAO;
    public String listUser() {
        // handler method to list all users
        userDAO.list();
    }
    public String saveUser(User user) {
        // handler method to save/update a user
        userDAO.save(user);
    }
    public String deleteUser(User user) {
        // handler method to delete a user
        userDAO.delete(user);
    }
    public String getUser(int userId) {
        // handler method to get a user
        userDAO.get(userId);
    }
}

Here, all business logic related to User management is provided by an implementation of the UserDAO interface. For example:

interface UserDAO {
    List<User> list();
    void save(User user);
    void checkLogin(User user);
}

By using the @Autowired annotation, the handler methods can delegate tasks to the business class, as we can see in the above example:

List<User> listUser = userDAO.list();

For more information about the  @Autowired annotation, see Annotation Type Autowired.

13. Accessing HttpServletRequest and HttpServletResponse

In some cases, you need to directly access the HttpServletRequest or HttpServletResponse objects within a handler method. By the flexibility of Spring, just add a relevant parameter to the handler method. For example:

@RequestMapping("/download")
public String doDownloadFile(
        HttpServletRequest request, HttpServletResponse response) {
    // access the request
    // access the response
    return "DownloadPage";
}

Spring detects and automatically injects the HttpServletRequest and HttpServletResponse objects into the method. Then, you can access the request and response such as getting InputStreamOutputStream, or returning a specific HTTP code.

14. Following the Single Responsibility Principle

Finally, there are two good practices you should follow when designing and coding controllers in Spring MVC:

  • A controller class should not execute business logic. Instead, it should delegate business processing to relevant business classes. This keeps the controller focusing on its designed responsibility is to control workflows of the application. For example:
@Controller
public class UserController {
    @Autowired
    private UserDAO userDAO;
    public String listUser() {
        // handler method to list all users
        userDAO.list();
    }
    public String saveUser(User user) {
        // handler method to save/update a user
        userDAO.save(user);
    }
    public String deleteUser(User user) {
        // handler method to delete a user
        userDAO.delete(user);
    }
    public String getUser(int userId) {
        // handler method to get a user
        userDAO.get(userId);
    }
}
  • Create each separate controller for each business domain. For example, UserController for controlling workflows of the user management, OrderController for controlling workflows of order processing, etc. For example:
@Controller
public class UserController {

}
@Controller
public class ProductController {

}
@Controller
public class OrderController {

}
@Controller
public class PaymentController {

}

There you have it! I have shared 14 tips that will help you write controller classes in Spring MVC properly and efficiently. However, that’s not the end. If you have other tips or suggestions, feel free to share your thoughts in the comments.

Further Reading

How Spring MVC Really Works

Spring MVC and Java-Based Configuration

Spring Framework: @RestController Vs. @Controller

Abstraction in Java

Abstraction in Java

If you have started to learn Java then I believe you must have somewhere come across term called object-oriented programming or OOPs concept. Now there are four pillars in Oops i.e., Abstraction, polymorphism, encapsulation and inheritance. In this article we will discuss about one of the four pillars of Oops i.e., Abstraction.

Abstraction basically is the art of hiding implementation details from user and provide the user what they want. Let’s try to understand with real world example. Most of us are quite fond of owning a car. When we go to place order for the car we are really not interested to understand very fine details of implementation of each and every component insider the car engine, Gear box etc., we leave those technical details and implementation for manufacturing engineers and mechanics to understand we are simply interested in the car so does the manufacturing company. They are interested to exactly provide us what we want and hide the fine implementation details from us. Likewise, there are tons of real-world examples where abstraction is in play whether smartphone you are using or smart television you are watching all have implemented abstraction in one way or the other.

Coming back to Java programming or any object-oriented programming to be more precise same principle follows code’s implementation details are hidden and only the necessary functionality is provided to the user. There are two ways to achieve abstraction in java: –

  1. By using interfaces
  2. By using abstract classes

Interfaces- Consider a television remote which only contains functionality to operate a television and it doesn’t serve any other purpose besides operating the television. You won’t be able to operate a refrigerator with a television remote. Here remote acts as an interface between you and the television. It contains all the necessary functionalities which you require while hiding the implementation details from you. In java Interfaces are similar to classes except they contain empty methods and can contain variables also. By empty methods it means that they don’t provide any implementation details and its for the classes or clients to provide the necessary implementation details for that method (or methods) when they implement the interface.

Syntax :-

public interface XYZ {

public void method ();

}

Example :

public interface TelevisionRemote {
public void turnOnTelevision();

public void turnOffTelevision();

}

A java class can just use the implements keyword to implement the interface and provide the implementation of the methods of the interface.

Example: –

public class Television implements TelevisionRemote{

   @Override

   public void turnOnTelevision(){

   //method implementation details

   }

   @Override

   public  void turnOffTelevision(){

   //method implementation details

   }

}

Interfaces provide contract specification or sets of rules for the classes implementing them. They set rules for classes and tell them what to do and not to do. In case the class does not provide implementation for all the methods of the interface then the class must be declared abstract. We will cover abstract classes later. They provide total abstraction which means that all the methods are empty and field variables are public static and final by default. Interfaces serve several features: –

1.They provide total abstraction.

2.They help to achieve what we call multiple inheritance as java doesn’t support multiple inheritance, but you can implement several interfaces in one class and thus it helps to achieve multiple inheritance.

3.They help to achieve loose coupling in design patterns implementation.

Abstract classes

Abstract classes are just like normal java class except they use keyword abstract in front of class declaration and method declaration.

Syntax: –

public abstract class XYZ {

public abstract methodName();

}

For example :

public abstract class Automobile {

   public abstract void engine();

   public void  gearBoxGearOne(){

     //method implementation

   }

}

Abstract classes are created using abstract keyword and they may have or may not have method implementation. If a method is declared abstract then its implementation has to be provided by the class extending the abstract class. We can have abstract class without abstract method as well as they can contain final methods also. A class that extends the abstract class is a child class for that abstract class and has to provide implementation for the abstract method declared in the abstract class.

Example :-

public class Car extends Automobile{

   @Override

   public void engine(){

     //Method implementation

   }

}

Now question must be arising why we have interfaces and abstract classes. There are few key differences worth noticing : –

1.Interfaces are implicitly abstract and cannot have implementations. Abstract classes can have method implementations.

2.Variables of interfaces are final by default. Abstract classes may or may not have final variable.

3.Interface methods are public whereas abstract classes can provide all types of access modifiers for its members i.e., public, protected, private.

4.Interface can extend interface only while classes can implement multiple interfaces and can extend one class only.

Thus, both abstract classes and interfaces are used to achieve abstraction and both have their own importance while designing a java solution but most preferable choice for most developers is to use interfaces as they provide complete abstraction. I hope this article helps to clear your doubts regarding abstraction.

Kafka Consumer Overview

This article is a continuation of Part 1 – Kafka Technical OverviewPart 2 – Kafka Producer Overview and Part 3 – Kafka Producer Delivery Semantics articles. Let’s look into Kafka consumer group, consumer, and protocol used in detail.

Consumer Role

Like a Kafka Producer that optimizes writes to Kafka, a Consumer is used for optimal consumption of Kafka data. The primary role of a Kafka consumer is to take Kafka connection and consumer properties to read records from the appropriate Kafka broker. Complexities of concurrent application consumption, offset management, delivery semantics, and a lot more are taken care of by Consumer APIs.

Properties

Some of the consumer properties in the bootstrap servers are: fetch.min.bytesmax.partition.fetch.bytesfetch.max.bytesenable.auto.commit, and many more. We will discuss some of these properties later in the next part of the article series.

Role of Kafka Consumers
Role of Kafka consumer

Multi-App Consumption

Multiple applications can consume records from the same Kafka topic, as shown in the diagram below. Each application that consumes data from Kafka gets it’s own copy and can read at its own speed. In other words, offsets consumed by one application could be different from another application. Kafka keeps tracks of the offsets consumed by each application in an internal__consumer_offset topic.

Kafka multi app consumption

Consumer Group and Consumer

Each application consuming data from Kafka is treated as a consumer group. For example, if two applications are consuming the same topic from Kafka, then, internally, Kafka creates two consumer groups. Each consumer group can have one or more consumers. If a topic has three partitions and an application consumes it, then a consumer group would be created and a consumer in the consumer group will consume all partitions of the topic. The diagram below depicts a consumer group with a single consumer.

Kafka multi partition single consumer

Kafka multi-partition single consumer

When an application wants to increase the speed of processing and process partitions in parallel then it can add more consumers to the consumer group. Kafka takes care of keeping track of offsets consumed per consumer in a consumer group, rebalancing consumers in the consumer group when a consumer is added or removed and lot more.

Kafka multi partition multi consumer

Kafka multi-partition multi-consumer

When there are multiple consumers in a consumer group, each consumer in the group is assigned one or more partitions. Each consumer in the group will process records in parallel from each leader partition of the brokers. A consumer can read from more than one partition.

Kafka consumer and multi partition consumption

It’s very important to understand that no single partition will be assigned to two consumers in the same consumer group; in other words, the same partition will not be processed by two consumers as shown in the diagram below.

Kafka same partition multiple consumer

Kafka same partition multiple-consumer

When consumers in a consumer group are more than partitions in a topic then over-allocated consumers in the consumer group will be unused.

Kafka unused consumer

Kafka unused consumer

When you have multiple topics and multiple applications consuming the data, consumer groups and consumers of Kafka will look similar to the diagram shown below.

Multiple application and multiple kafka topic

Multiple application and multiple Kafka topic

Coordinator and Leader Discovery

In order to manage the handshake between Kafka and the application that forms the consumer group and consumer, a coordinator on the Kafka side and a leader (one of the consumers in the consumer group) is elected. The first consumer that initiates the process is automatically elected as leader in the consumer group. As explained in the diagram below, for a consumer to join a consumer group, the following handshake processes take place:

  • Find coordinator
  • Join group
  • Sync group
  • Heartbeat
  • Leave group

Kafka consumer and coordinator protocol

Kafka consumer and coordinator protocol

Coordinator

In order to create or join a group, a consumer has to first find the coordinator on the Kafka side that manages the consumer group. The consumer makes a “find coordinator” request to one of the bootstrap servers. If a coordinator already doesn’t exist it’s identified based on a hashing formula and returned as a response to “find coordinator” request.

Join Group

Once the coordinator is identified, the consumer makes a “join group” request to the coordinator. The coordinator returns the consumer group leader and metadata details. If a leader already doesn’t exist then the first consumer of the group is elected as leader. Consuming application can also control the leader elected by the coordinator node.

Kafka consumer join group

Kafka consumer join group

Sync Group

After leader details are received for the join group request, the consumer makes a “Sync group” request to the coordinator. This request triggers the rebalancing process across consumers in the consumer group, as the partitions assigned to the consumers will change after the “sync group” request.

Kafka consumer sync group

Kafka consumer sync group

Rebalance

All consumers in the consumer group will receive updated partition assignments that they need to consume when a consumer is added/removed or “sync group” request is sent. Data consumption by all consumers in the consumer group will be halted until the rebalance process is complete.

Kafka consumer rebalance group

Kafka consumer rebalance group

Heartbeat

Each consumer in the consumer group periodically sends a heartbeat signal to its group coordinator. In the case of heartbeat timeout, the consumer is considered lost and rebalancing is initiated by the coordinator.

Kafka consumer heartbeat

Kafka consumer heartbeat

Leave Group

A consumer can choose to leave the group anytime by sending a “leave group” request. The coordinator will acknowledge the request and initiate a rebalance. In case the leader node leaves the group, a new leader is elected from the group and a rebalance is initiated.

Kafka consumer leave group

Kafka consumer leave group

Summary

As explained in Part 1of this series, “partitions” are units of parallelism. As consumers in a consumer group are limited by the partition in a topic, it’s very important to decide you partitions based on the SLA and scale your consumers accordingly. Consumer offsets are managed and stored by Kafka in an internal __consumer_offset topic. Each consumer in a consumer group follows the find coordinator, join group, sync group, heartbeat, and leave group protocols. In the next article in this series, we’ll look into Kafka consumer properties and delivery semantics.

 

Java Concurrency: Thread Confinement

Thread Confinement

Most concurrency problems occur only when we want to share a mutable variable, or mutable state, between threads. If a mutable state is shared between multiple threads, then all of them will be able to read and modify the value of the state, thus resulting in incorrect or unexpected behavior. One way to avoid this problem is to simply not share the data between the threads. This technique is known as thread confinement and is one of the simplest ways of achieving thread safety in our application.

The Java language, in itself, does not have any way of enforcing thread confinement. Thread confinement is achieved by designing your program in a way that does not allow your state to be used by multiple threads and is, thus, enforced by the implementation. There are a few types of thread confinement, as described below.

Ad-Hoc Thread Confinement

Ad-hoc thread confinement describes a way of thread confinement, where it is the total responsibility of the developer, or the group of developers working on that program, to ensure that the use of the object is restricted to a single thread. This approach is very very fragile and should be avoided in most cases.

One special case that comes under Ad-hoc thread confinement applies to volatile variables. It is safe to perform read-modify-write operations on the shared volatile variable as long as you ensure that the volatile variable is only written from a single thread. In this case, you are confining the modification to a single thread to prevent race conditions, and the visibility guarantees for volatile variables ensure that other threads see the most up to date value.

Stack Confinement

Stack confinement is confining a variable, or an object, to the stack of the thread. This is much stronger than Ad-hoc thread confinement, as it is limiting the scope of the object even more, by defining the state of the variable in the stack itself. For example, consider the following piece of code:

private long numberOfPeopleNamedJohn(List<Person> people) {
  List<Person> localPeople = new ArrayList<>();
  localPeople.addAll(people);
  return localPeople.stream().filter(person -> person.getFirstName().equals("John")).count();
}

In the above code, we pass on a list of person but do not directly use it. We, instead, create our own list, which is local to the currently executing thread, and add all the person in people to localPeople. Since we are defining our list in the  numberOfPeopleNamedJohn method only, this makes the variable  localPeople stack confined, as it exists on stack of one thread, and thus cannot be accessed by any other thread. This makes localPeople thread safe. The only thing we need to take care of here is that we should not allow localPeople to escape the scope of this method, to keep it stack confined. This should also be documented or commented when defining this variable, as generally, it’s only in the current developer’s mind to not let it escape, and in future, another developer may mess up.

ThreadLocal

ThreadLocalallows you to associate a per-thread value with a value-holding object. It allows you to store different objects for different threads and maintains which object corresponds to which thread. It has set and get accessor methods which maintain a separate copy of the value for each thread that uses it. The  get() method always returns the most updated value passed to  set() from the currently executing thread. Let’s look at an example:

public class ThreadConfinementUsingThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<String> stringHolder = new ThreadLocal<>();
        Runnable runnable1 = () -> {
            stringHolder.set("Thread in runnable1");
            try {
                Thread.sleep(5000);
                System.out.println(stringHolder.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable runnable2 = () -> {
            stringHolder.set("Thread in runnable2");
            try {
                Thread.sleep(2000);
                stringHolder.set("string in runnable2 changed");
                Thread.sleep(2000);
                System.out.println(stringHolder.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable runnable3 = () -> {
            stringHolder.set("Thread in runnable3");
            try {
                Thread.sleep(5000);
                System.out.println(stringHolder.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);
        Thread thread3 = new Thread(runnable3);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

In the above example, we have executed three threads, using the same  ThreadLocal object stringHolder. As you can see here, we have, first of all, set one string in every thread in the stringHolder object, making it contain three strings. Then, after some pause, we have changed the value from just the second thread. Below was the output of the program:

string in runnable2 changed
Thread in runnable1
Thread in runnable3

As you can see in the above output, the String for thread 2 changed, but the strings for thread 1 and thread 3 were unaffected. If we do not set any value before getting the value on a specific thread from ThreadLocal, then it returns null. After a thread is terminated, thread-specific objects in ThreadLocal become ready for garbage collection.