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