JAKARTA EE FOR JUNIOR DEVELOPERS - Lesson 9 Defining Custom Qualifiers

 

 

INTRODUCTION

Using the @Default qualifier works only when there is only one implementation of our business logic interface. When there are more implementations then we need another mechanism to distinguish between mutliple beans of the same type. The @Qualifier annotation helps avoid ambiguity when the container needs to inject a dependency.

 WHY USE QUALIFIER

When there are multiple implementations of an interface or multiple beans of the same type, the container cannot decide which one to inject unless you provide additional information.

Let's reproduce this error by adding one more implementation to our project. If you remember from the previous lesson (https://groundofcode.blogspot.com/2025/02/JAKARTA-EE-FOR-JUNIOR-DEVELOPERS-Lesson-8-Introduction-to-Dependency-Injection.html) ,even if it was not necessary, we added the @Default built-in qualifier to the RandomEmployeeDepartment class just to emphasize the fact that the system is capable of automatically recognizing the one and only implementation of the DepartmentAssigment interface.

 We leave the previous code as it is (including the @Default annotation) and we just add one more implementation class with the name ItEmployeeDepartment without any annotations.

ItEmployeeDepartment.java

package com.mycompany.services;

public class ItEmployeeDepartment implements DepartmentAssignment {

    @Override
    public long generateDepartment() {
        return 2L;
    }
   
}

Even if the @Default qualifier is placed on the RandomEmployeeDepartment the application server can not decide clearly which on is the default implementation so it generates an error and stops the deployment of the application.

 

This issue can be easily solved if we create and assign our own custom qualifiers.

DEFINING NEW QUALIFIER TYPES

 According to the Jakarta EE CDI specification (https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#defining_qualifier_types) we must follow three easy steps to create and implement a custom qualifier.

  1. Define a Custom Qualifier: You create a custom annotation marked with @Qualifier.
  2. Annotate Beans: Use the custom qualifier on the beans you want to distinguish.
  3. Use the Qualifier in Injection Points: Use the same qualifier at the injection point to indicate which bean should be injected.

Let's follow these steps and fix the dependency error that we are currently facing.

First of all, we need to create two custom qualifier annotations - one with the name ITAssignment and another one with the name RandomAssignment.

Right click on the services package, and then select New and finally select Other. 

 

From the Categories, click and select Contexts and Dependency Injection (Jakarta EE) whereas from the File Types choose Qualifier Type. Click Next to continue to proceed to the next window.

 

Name the qualifier as ITAssignment and click Finish to complete the process. Follow the exact same steps to create the RandomAssignment qualifier as well.

 ITAssignment.java


package com.mycompany.services;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import jakarta.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface ITAssignment {
}

 

 RandomAssignment.java


package com.mycompany.services;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import jakarta.inject.Qualifier;

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface RandomAssignment {
}


This time let's pick IT department as the default department in case the user clicks on None on the department option. Add the @Default and the @ITAssignment qualifiers on the ITEmployeeDepartment implementation class.

 ITEmployeeDepartment

package com.mycompany.services;

import jakarta.enterprise.inject.Default;

@Default
@ITAssignment
public class ITEmployeeDepartment implements DepartmentAssignment {

    @Override
    public long generateDepartment() {
        return 2L;
    }
}

On the RandomEmployeeDepartment assign the @RandomAssignment qualifier.

 RandomEmployeeDepartment.java

package com.mycompany.services;

@RandomAssignment
public class RandomEmployeeDepartment implements DepartmentAssignment {

    @Override
    public long generateDepartment() {
        long randomNumber =  (long) (Math.random() * 4) + 1;
        return randomNumber;
    }
   
}


Finally, on the Controller class (Servlet) we add the @ITAssignment qualifier on the DepartmentAssignment reference.

 Controller.java

package com.mycompany.controllers;

import com.mycompany.jakartaee.entities.Department;
import com.mycompany.jakartaee.entities.Employee;
import com.mycompany.services.DepartmentAssignment;
import com.mycompany.services.ITAssignment;
import jakarta.annotation.Resource;
import jakarta.enterprise.inject.Default;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.transaction.HeuristicMixedException;
import jakarta.transaction.HeuristicRollbackException;
import jakarta.transaction.NotSupportedException;
import jakarta.transaction.RollbackException;
import jakarta.transaction.SystemException;
import jakarta.transaction.UserTransaction;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

@WebServlet(name = "Controller", urlPatterns = {"/Controller"})
public class Controller extends HttpServlet {

    @PersistenceContext
    private EntityManager em;

    @Resource
    private UserTransaction userTransaction;

    @Inject
    @Default
    @ITAssignment
    DepartmentAssignment departmentAssignment;

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response)
            throws ServletException, IOException {
        Long dept_id = null;

        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");
        String email = request.getParameter("email");
        String date = request.getParameter("hireDate");
        String phoneNumber = request.getParameter("phoneNumber");
        Double monthlySalary = Double.valueOf(request.getParameter("monthlySalary"));
        String dept = request.getParameter("department");

        Department department = new Department();
        Employee employee = new Employee();

        if ("None".equals(dept)) {
            department.setDept_id(departmentAssignment.generateDepartment());
        } else {
            switch (dept) {
                case "HR" ->
                    department.setDept_id(1L);
                case "IT" ->
                    department.setDept_id(2L);
                case "Finance" ->
                    department.setDept_id(3L);
                case "Marketing" ->
                    department.setDept_id(4L);
                default -> {
                }
            }
        }

        employee.setFirstName(firstName);
        employee.setLastName(lastName);
        employee.setMonthlySalary(monthlySalary);
        Date dt = null;
        try {
            dt = new SimpleDateFormat("yyyy-MM-dd").parse(date);
        } catch (ParseException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }
        employee.setHireDate(dt);
        employee.setEmail(email);
        employee.setPhoneNumber(phoneNumber);
        employee.setDepartment(department);

        try {
            userTransaction.begin();
            em.persist(employee);
            userTransaction.commit();
        } catch (NotSupportedException
                | SystemException | RollbackException
                | HeuristicMixedException | HeuristicRollbackException
                | SecurityException | IllegalStateException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }

        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Servlet NewServlet</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>The user " + employee.getFirstName() + " has been registered </h1>");
        out.println("</body>");
        out.println("</html>");

    }
}


After executing the application and choosing the None option under the Department field, we should be able to see that number 2 (IT) is the default value that is assigned to every new employee.

 



 

full-width



full-width

Post a Comment

0 Comments