INTRODUCTION
In this Jakarta EE for Junior developers lesson, we will convert our JSP and Servlet application to a Jakarta Faces application with the help of Named Beans.
NAMED BEANS
In Jakarta EE, Named Beans are managed beans that are explicitly given a name, allowing them to be referenced in the context of a Java EE application, especially in web and enterprise applications. Named beans are a part of the CDI (Contexts and Dependency Injection) specification, which provides a standard way of managing the lifecycle and dependencies of objects in a Java EE application. Named Beans are referring to the "C" (Contexts) part of the CDI. Please refer to the Jakarta EE CDI specification for more information (https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#implementation).
Managed beans are Java objects that are managed by the Jakarta EE container (such as a servlet container or application server). These beans can have various scopes (e.g., @RequestScoped
, @SessionScoped
, @ApplicationScoped
) to define their lifespan and visibility. In this lesson we will use the @RequestScoped so that our bean lives for the duration of a single HTTP request. A more detailed explanation and example will be given to the next lesson. CDI provides mechanisms to inject and manage the lifecycle of these beans.
Let's move the code from the Servlet controller to a EmployeeController managed bean. We will not need the Servlet anymore so you can delete it.
EmployeeController.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.context.RequestScoped;
import jakarta.enterprise.inject.Default;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
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;
@Named
@RequestScoped
public class EmployeeController {
private String firstName;
private String lastName;
private String email;
private String date;
private String phoneNumber;
private Double monthlySalary;
private String dept;
@PersistenceContext
private EntityManager em;
@Resource
private UserTransaction userTransaction;
private static final Logger logger = Logger.getLogger(
EmployeeController.class.getName());
@Inject
@Default
@ITAssignment
DepartmentAssignment departmentAssignment;
public String saveCustomer() {
Employee employee = new Employee();
Department department = new Department();
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(EmployeeController.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();
logger.log(Level.INFO, "Transaction has been completed with employee id: {0}",
employee.getEmployee_id());
} catch (NotSupportedException
| SystemException | RollbackException
| HeuristicMixedException | HeuristicRollbackException
| SecurityException | IllegalStateException ex) {
Logger.getLogger(EmployeeController.class.getName()).log(Level.SEVERE, null, ex);
}
return "confirmation";
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Double getMonthlySalary() {
return monthlySalary;
}
public void setMonthlySalary(Double monthlySalary) {
this.monthlySalary = monthlySalary;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
}
The @Named
annotation is used to assign a name to a bean. This name can then be used to reference the bean in a JavaServer Faces page, EL (Expression Language), or other parts of the application.
By default, the name of the bean is the lowercase version of its class name unless specified otherwise.
Named beans can be injected into other beans or components using CDI's @Inject
annotation. They can be injected either by their name or their type. Named beans are particularly useful in Jakarta Faces pages, where you may need to reference a bean by its name in the UI layer.
The @Named
annotation allows you to specify a different name for the bean than the default one, which can be useful for clarity, consistency, or avoiding naming conflicts. In our case, the bean can be called as employeeController
in our application.
So, we have created an EmployeeController
which is a named bean (employeeBean
), and it can be injected or referenced in JSF pages or other beans. It is request-scoped, meaning a new instance is created for each HTTP request.
This code defines an EmployeeController
class that manages the process of creating and saving an Employee
entity in a Jakarta EE environment. The controller handles form data, such as the employee's first name, last name, email, date of hire, phone number, monthly salary, and department, and saves this data to a database using Jakarta Persistence API.
The return statement redirects the application to a Jakarta Page with the name confirmation which we will create shortly.
JAKARTA FACES
JavaServer Faces (https://jakarta.ee/specifications/faces/4.0/jakarta-faces-4.0) is based on servlet technology. It is built on top of the Java Servlet API and relies on the request-response cycle of servlets for handling web requests. However, Jakarta Faces provides a higher-level abstraction for building user interfaces compared to raw servlets. While servlets are responsible for processing HTTP requests and generating HTTP responses, JSF adds a more structured, component-based approach for developing web applications. It provides a set of reusable UI components, a managed bean model, navigation, and event handling, simplifying the development of dynamic web pages.
So now we need to replace the Jakarta Pages with Jakarta Faces. We can delete the index.jsp page that we have created in previous lessons. Then, right click on Web Paged, choose New and then select JSP Page.
Name the page index and click Finish.
If you open the Web.xml file, you will notice that some extra information has been added automatically by the Apache Netbeans so you do have to insert it manually.
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="6.0" xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd">
<context-param>
<param-name>jakarta.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
The jakarta.faces.PROJECT_STAGE
parameter is specific to Jakarta Server Faces applications. It defines the environment stage of the application, such as Development
, Production
, or Production
. Setting it to Development
enables detailed error messages and other developer-friendly features.
The FacesServlet is responsible for handling Jakarta Faces requests.
<load-on-startup>
specifies when the servlet should be loaded. Setting this to 1
means that the servlet is loaded at startup (before any request is received), ensuring that Jakarta Faces is available when the application starts.
The <url-pattern>
defines the URL pattern that the servlet will handle. In this case, any URL starting with /faces/
will be handled by the FacesServlet
. For example, a URL like /faces/index.xhtml
will be processed by Jakarta Faces.
Let's see the complete code for the Jakarta Faces index.xhtml page and then we will explain it.
index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:f="jakarta.faces.core">
<h:head>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous"></link>
<title>Enter Employee Information</title>
</h:head>
<h:body class="container mt-5">
<h1 style="text-align: center">Employee Registration Form</h1>
<h:form class="card p-4 shadow-sm">
<div class="row">
<div class="col-md-6 mb-3">
<h:outputLabel for="firstName" value="First Name" class="col-md-6 mb-3"/>
<h:inputText id="firstName" value="#{employeeController.firstName}" class="form-control"/>
</div>
<div class="col-md-6 mb-3">
<h:outputLabel for="lastName" value="Last Name" class="col-md-6 mb-3"/>
<h:inputText id="lastName" value="#{employeeController.lastName}" class="form-control"/>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<h:outputLabel for="email" value="Email" class="col-md-6 mb-3"/>
<h:inputText id="email" value="#{employeeController.email}" class="form-control"/>
</div>
<div class="col-md-6 mb-3">
<h:outputLabel for="phone" value="Phone" class="col-md-6 mb-3"/>
<h:inputText id="phone" value="#{employeeController.phoneNumber}" class="form-control"/>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<h:outputLabel for="date" value="Hire Date" class="col-md-6 mb-3"/>
<h:inputText id="date" value="#{employeeController.date}" type="date" class="form-control"/>
</div>
<div class="col-md-6 mb-3">
<h:outputLabel for="salary" value="Monthly Salary" class="col-md-6 mb-3"/>
<h:inputText id="salary" value="#{employeeController.monthlySalary}" class="form-control"/>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<h:outputLabel for="department" value="Select Department:" class="col-md-6 mb-3"/>
<h:selectOneMenu id="department" value="#{employeeController.dept}">
<f:selectItem itemLabel="None" itemValue="None" />
<f:selectItem itemLabel="HR" itemValue="HR" />
<f:selectItem itemLabel="IT" itemValue="IT" />
<f:selectItem itemLabel="Finance" itemValue="Finance" />
<f:selectItem itemLabel="Marketing" itemValue="Marketing" />
</h:selectOneMenu>
</div>
</div>
<h:commandButton value="Submit" action="#{employeeController.saveCustomer()}"
class="btn btn-primary custom-btn-size w-50"/>
</h:form>
</h:body>
</html>
xmlns:h="jakarta.faces.html"
: Defines the namespace for JSF HTML components (<h:>
prefix).
xmlns:f="jakarta.faces.core"
: Defines the namespace for JSF core components (<f:>
prefix).
The form collects several pieces of information from the user (like the employee's first name, last name, email, phone number, hire date, salary, and department) using Jakarta Faces input components.
Let's further explore the first input that refers to firstname.
<h:outputLabel>
creates a label for the input field with the text "First Name". It is associated with the input field via the for="firstName"
attribute.
<h:inputText>
creates a text input field for the user to enter the first name. The value is bound to the firstName
property in the employeeController
managed bean, indicated by #{employeeController.firstName}
.
The class="form-control"
is a Bootstrap class that styles the input field.
The rest of the components operate in a similar way. Overall, this Jakarta Faces form is used to collect employee information. It contains input fields for first name, last name, email, phone number, hire date, monthly salary, and department, all linked to a managed bean (employeeController
). The data entered by the user is submitted via the h:commandButton
, which calls the saveCustomer()
method in the employeeController
to process the information. The page also uses Bootstrap for styling to make it look clean and responsive.
If everything goes smooth, and the data has been successfully created on the employee table, the application will redirect us to a new Jakarta Page as a confirmation.
Follow the same steps as before to create a second Jakarta Face page with the name confirmation.
confirmation.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html">
<h:head>
<title>Registration</title>
</h:head>
<h:body>
<p style="font-size: 20px">
The Employee #{employeeController.firstName} was registered successfully
</p>
</h:body>
</html>
Let's run our code and verify its functionality.
0 Comments
What do you think about Ground of Code?