Java Enterprise Edition (Jakarta EE)
Introduction to Jakarta EE
Jakarta EE (formerly Java EE) is a set of specifications extending Java SE with enterprise features such as distributed computing and web services.
Key Features:
- Distributed computing
- Web services
- Component model
- Security infrastructure
- Transaction management
- Database connectivity
Project Setup
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jee-application</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<jakarta.ee.version>9.1.0</jakarta.ee.version>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>${jakarta.ee.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Architecture
Multi-tier Architecture
Tiers:
- Client Tier (Web browsers, applications)
- Web Tier (Servlets, JSP, JSF)
- Business Tier (EJB, CDI)
- Data Tier (JPA, JDBC)
Application Server Structure
src/
├── main/
│ ├── java/
│ │ └── com/example/
│ │ ├── entity/
│ │ ├── ejb/
│ │ ├── web/
│ │ └── util/
│ ├── resources/
│ │ └── META-INF/
│ │ ├── persistence.xml
│ │ └── beans.xml
│ └── webapp/
│ ├── WEB-INF/
│ │ ├── web.xml
│ │ └── faces-config.xml
│ └── resources/
└── test/
└── java/
Core Components
Enterprise JavaBeans (EJB)
@Stateless
public class OrderService {
@PersistenceContext
private EntityManager em;
@Inject
private PaymentService paymentService;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Order createOrder(OrderDTO orderDTO) {
Order order = new Order();
order.setItems(orderDTO.getItems());
order.setTotal(calculateTotal(orderDTO.getItems()));
em.persist(order);
paymentService.processPayment(order);
return order;
}
private BigDecimal calculateTotal(List items) {
return items.stream()
.map(item -> item.getPrice()
.multiply(new BigDecimal(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
Context and Dependency Injection (CDI)
@ApplicationScoped
public class UserService {
@Inject
private UserRepository userRepository;
@Inject
private Event userCreatedEvent;
public User createUser(UserDTO userDTO) {
User user = new User();
user.setUsername(userDTO.getUsername());
user.setEmail(userDTO.getEmail());
user = userRepository.save(user);
userCreatedEvent.fire(new UserCreatedEvent(user));
return user;
}
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface Secure {}
Persistence
JPA Configuration
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="3.0"
xmlns="https://jakarta.ee/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<persistence-unit name="primaryPU" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<properties>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</persistence-unit>
</persistence>
Entity and Repository
@Entity
@Table(name = "users")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String username;
@Email
private String email;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List orders = new ArrayList<>();
// Getters and setters
}
@Stateless
public class UserRepository {
@PersistenceContext
private EntityManager em;
public User findById(Long id) {
return em.find(User.class, id);
}
public List findAll() {
return em.createQuery("SELECT u FROM User u", User.class)
.getResultList();
}
public User save(User user) {
if (user.getId() == null) {
em.persist(user);
return user;
} else {
return em.merge(user);
}
}
public void delete(User user) {
em.remove(em.contains(user) ? user : em.merge(user));
}
}
Web Components
Servlet
@WebServlet("/api/users/*")
public class UserServlet extends HttpServlet {
@Inject
private UserService userService;
@Inject
private ObjectMapper objectMapper;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String pathInfo = request.getPathInfo();
if (pathInfo == null || pathInfo.equals("/")) {
List users = userService.findAll();
sendJsonResponse(response, users);
} else {
Long userId = Long.parseLong(pathInfo.substring(1));
User user = userService.findById(userId);
if (user != null) {
sendJsonResponse(response, user);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
}
private void sendJsonResponse(HttpServletResponse response,
Object data)
throws IOException {
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
objectMapper.writeValue(response.getWriter(), data);
}
}
JSF Page
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>User Management</title>
</h:head>
<h:body>
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="Username:" for="username"/>
<h:inputText id="username"
value="#{userBean.user.username}"
required="true"/>
<h:outputLabel value="Email:" for="email"/>
<h:inputText id="email"
value="#{userBean.user.email}"
required="true"/>
<h:commandButton value="Save"
action="#{userBean.save}"/>
</h:panelGrid>
<h:dataTable value="#{userBean.users}" var="user">
<h:column>
<f:facet name="header">Username</f:facet>
#{user.username}
</h:column>
<h:column>
<f:facet name="header">Email</f:facet>
#{user.email}
</h:column>
<h:column>
<h:commandButton value="Delete"
action="#{userBean.delete(user)}"/>
</h:column>
</h:dataTable>
</h:form>
</h:body>
</html>
Security
Authentication and Authorization
@DatabaseIdentityStoreDefinition(
dataSourceLookup = "java:jboss/datasources/ExampleDS",
callerQuery = "SELECT password FROM users WHERE username = ?",
groupsQuery = "SELECT role FROM user_roles WHERE username = ?"
)
@FormAuthenticationMechanismDefinition(
loginToContinue = @LoginToContinue(
loginPage = "/login.xhtml",
errorPage = "/login-error.xhtml"
)
)
@ApplicationScoped
public class SecurityConfig {
}
@WebServlet("/secured/*")
@ServletSecurity(
@HttpConstraint(
rolesAllowed = "ADMIN",
transportGuarantee = TransportGuarantee.CONFIDENTIAL
)
)
public class SecuredServlet extends HttpServlet {
@Inject
private SecurityContext securityContext;
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String username = securityContext.getCallerPrincipal()
.getName();
response.getWriter()
.write("Welcome " + username);
}
}
Security Best Practices:
- Use HTTPS for all communications
- Implement proper authentication
- Use role-based access control
- Secure sensitive data
- Implement input validation
- Use secure session management
- Enable security headers
- Regular security updates