Jakarta EE platform reference covering specifications, APIs, reference
Scanned 9/4/2026
Install to Claude Code
npx -y skills add paulpas/agent-skill-router --skill jakarta-ee --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Jakarta Ee?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/paulpas-jakarta-ee)More formats (shields.io, HTML) on the badges page.
---
name: jakarta-ee
description: Jakarta EE platform reference covering specifications, APIs, reference
implementations, build configuration, and architecture patterns for enterprise Java
development.
license: MIT
compatibility: opencode
metadata:
version: "1.0.0"
domain: cncf
triggers: jakarta ee, jakarta-ee, enterprise java, servlet, jax-rs, cdi, jsf, ejb javanamespace migration
archetypes:
- educational
- strategic
anti_triggers:
- brainstorming
- vague ideation
- non-containerized architecture
response_profile:
verbosity: medium
directive_strength: low
abstraction_level: strategic
role: reference
scope: infrastructure
output-format: manifests
content-types:
- guidance
- examples
- do-dont
- config
related-skills: microprofile, jakarta-migration
---
# Jakarta EE Platform
Provides architecture guidance, specification references, and implementation patterns for building enterprise Java applications on the Jakarta EE platform. Acts as a reference for selecting containers, configuring build systems, wiring CDI beans, JAX-RS resources, JPA entities, and JSF pages into production-grade deployments.
## TL;DR Checklist
- [ ] Choose the right container (WildFly for full EE, Payara for GlassFish compatibility, OpenLiberty for modular cloud-native)
- [ ] Use `jakarta.platform:jakarta.jakartaee-api` dependency — never `javax.*` coordinates
- [ ] Configure CDI producers and beans.xml before wiring cross-cutting concerns
- [ ] Set JTA transaction boundaries at the service/manager layer, not on individual DAO methods
- [ ] Include a complete WAR structure with web.xml namespace declarations matching jakarta.* schema
---
## Purpose and Use Cases
Jakarta EE is the open-source evolution of Java EE (formerly J2EE), governed by the Eclipse Foundation. It provides standardized APIs for enterprise application development: servlets, REST services, dependency injection, persistence, messaging, security, and more.
**Use Jakarta EE when:**
- Building large-scale enterprise applications requiring certified platform compatibility
- Migrating from legacy Java EE / GlassFish deployments
- Needing transaction management, connection pooling, and JMS out of the box
- Targeting multi-vendor interoperability (any Jakarta EE 10/11 server runs the same WAR)
- Building services that integrate with MicroProfile for cloud-native capabilities
**Consider Spring Boot instead when:**
- The team has deep Spring ecosystem expertise and no legacy Java EE investments
- You need rapid prototyping with embedded servers and auto-configuration
- The deployment model is exclusively Kubernetes with containerized microservices
- You require non-standard or experimental features before they reach Jakarta EE spec status
**Consider plain Java SE when:**
- The application has no enterprise concerns (no transactions, persistence, or web layer)
- Building CLI tools, background daemons, or simple HTTP servers using frameworks like Spark or javalin
- The team prefers minimal dependencies and explicit configuration over platform conveniences
---
## When to Use
Use this skill when:
- Architecting a new enterprise Java application on a Jakarta EE 10 or 11 server
- Migrating a legacy Java EE 8 (GlassFish, JBoss EAP 7.x) application to Jakarta EE 9+
- Selecting the right container runtime (WildFly full platform vs. OpenLiberty modular vs. Payara)
- Configuring CDI beans, JAX-RS resources, and JPA persistence layers with proper transaction boundaries
- Deploying a WAR-based application to Kubernetes using containerized Jakarta EE servers
- Integrating MicroProfile specifications (health, metrics, config) on top of the Jakarta EE platform
- Setting up database connection pooling and JTA distributed transactions in a multi-datasource environment
---
## When NOT to Use
Avoid this skill for:
- Starting a greenfield microservice project — Spring Boot or Quarkus may be more productive choices
- Building simple HTTP services without enterprise concerns — consider lightweight frameworks like Spark Java, Javalin, or Helidon SE
- Containerizing Node.js, Python, or Go applications — Jakarta EE is exclusively for the Java ecosystem
---
## Core Workflow
1. **Select Container Runtime** — Choose the appropriate Jakarta EE server based on requirements:
- Full platform compliance (EJB, JMS, Batch): WildFly or Payara Server
- Cloud-native with modular features and fast startup: OpenLiberty
- Small footprint with existing Tomcat infrastructure: TomEE
- Native GraalVM compilation: Quarkus (Jakarta EE compatibility mode)
**Checkpoint:** Verify the chosen server supports at least Jakarta EE 10 Web Profile for the target deployment environment.
2. **Configure Build Dependencies** — Declare `jakarta.platform:jakarta.jakartaee-api` with `<scope>provided</scope>` in pom.xml. For fine-grained control, declare individual jakarta.* API dependencies instead of the umbrella artifact. Never add implementation JARs as compile-time dependencies.
**Checkpoint:** Run `mvn dependency:tree` to confirm no javax.* artifacts appear in the transitive dependency graph.
3. **Wire CDI Components** — Define beans with appropriate scopes (`@RequestScoped` for web-layer, `@ApplicationScoped` for stateless services). Configure `beans.xml` with `bean-discovery-mode="annotated"` or `"all"`. Add producer methods for cross-cutting concerns (datasources, interceptors).
**Checkpoint:** Ensure every injected dependency has exactly one qualifying bean or is wrapped in Optional.
4. **Configure Persistence Layer** — Define `persistence.xml` with JTA-managed entity manager factory. Annotate service methods with `@TransactionAttribute` (REQUIRED for writes, SUPPORTS for reads). Never annotate repository/DAO methods with transaction attributes — they inherit from the caller.
**Checkpoint:** Verify all entity classes have `@Entity`, `@Table`, and a default no-arg constructor required by JPA providers.
5. **Deploy as Packaged WAR** — Package the application using Maven Assembly or Shade plugin for uber-jars, or use container-native deployment (copy .war to config/apps/ for OpenLiberty). For Kubernetes, create a ConfigMap with the artifact and mount it into the container.
**Checkpoint:** Test deployment on a local Jakarta EE reference server before pushing to CI/CD pipeline artifacts.
---
## Architecture Design Patterns
### Jakarta EE Component Model
Jakarta EE applications follow a layered architecture where the container provides lifecycle management for each component type:
```
┌─────────────────────────────────────────────────────────┐
│ Client Layer │
│ (Browser, Mobile, REST Consumer) │
└───────────────────────┬─────────────────────────────────┘
│ HTTP / JMS
┌───────────────────────▼─────────────────────────────────┐
│ Web Tier │
│ ┌────────────┐ ┌──────────┐ ┌─────────────────────┐ │
│ │ JSF │ │ JAX-RS │ │ Servlet Filter Chain│ │
│ │ (Faces) │ │ Resources│ │ Security / Logging │ │
│ └─────┬──────┘ └────┬─────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ ┌─────▼──────────────▼────────────────────▼──────────┐ │
│ │ CDI Context & DI Container │ │
│ │ (Wires beans, manages scopes: @RequestScoped, │ │
│ │ @SessionScoped, @ApplicationScoped, @Conversation)│ │
│ └─────────────────────┬──────────────────────────────┘ │
└────────────────────────┼─────────────────────────────────┘
│ JPA / JDBC / JMS
┌────────────────────────▼─────────────────────────────────┐
│ Persistence Tier │
│ ┌────────────┐ ┌──────────┐ ┌─────────────────────┐ │
│ │ JPA │ │ JTA │ │ Connection Pool │ │
│ │ (Hibernate)│ │ Tx Mgr │ │ (HikariCP/Tomcat) │ │
│ └─────┬──────┘ └──────────┘ └─────────────────────┘ │
│ │ │
│ ┌─────▼──────────────────────────────────────────────┐ │
│ │ Database (JDBC) │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
Background Tier: Message-Driven Beans (JMS consumers)
Scheduled Tasks @Schedule / Concurrency Utils
```
### WAR Structure
A Jakarta EE web application packaged as a WAR follows this standard layout:
```
myapp.war
├── META-INF/
│ ├── MANIFEST.MF
│ └── context.xml # Container-specific configuration
├── WEB-INF/
│ ├── web.xml # Deployment descriptor (optional in EE 10+)
│ ├── beans.xml # CDI configuration
│ ├── persistence.xml # JPA entity manager factory config
│ ├── faces-config.xml # JSF navigation config (if used)
│ └── classes/
│ └── com/example/
│ ├── resource/
│ │ └── MyResource.class # JAX-RS
│ ├── service/
│ │ ├── UserService.class # CDI @ApplicationScoped
│ │ └── UserDTO.class
│ ├── repository/
│ │ └── UserRepository.class # JPA DAO
│ ├── entity/
│ │ └── UserEntity.class # @Entity
│ └── interceptor/
│ └── LoggingInterceptor.class
├── resources/
│ └── application.properties
└── index.html # Static web content
```
### Key Build Configuration
The build system declares the Jakarta EE API dependency and compiles against it. The actual implementation is provided by the runtime container at deployment time:
```xml
<!-- pom.xml — Jakarta EE 10 Web Profile -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>10.0.0</version>
<scope>provided</scope>
</dependency>
<!-- Specific API dependencies for precise control -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>4.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
```
### Component Interaction Flow
**REST API request lifecycle:**
1. HTTP request enters the container (WildFly/Payara/OpenLiberty)
2. CDI creates or retrieves a request-scoped proxy for the JAX-RS resource class
3. The JAX-RS runtime dispatches to the method annotated with `@GET`, `@POST`, etc.
4. CDI dependency injection populates `@Inject` fields (services, repositories)
5. The service layer invokes JPA repository methods
6. JTA coordinates the transaction — begins before first DB call, commits after response
7. Response serializes to JSON/XML via Jakarta JSON-P or JSON-B
---
## Key Specifications Table
| Specification | Spec Number | Maven Group ID | Brief Description |
|--------------|-------------|----------------|-------------------|
| **Servlet** | JSR 369 / 6.0 | `jakarta.servlet` | HTTP request/response processing, filter chains, session management |
| **JAX-RS** | JSR 388 / 3.1 | `jakarta.ws.rs` | RESTful web services with annotations (`@Path`, `@GET`, `@POST`) |
| **CDI** | JSR 365 / 4.0 | `jakarta.enterprise` | Dependency injection, contextual lifecycle, events, interceptors, alternatives |
| **JPA** | JSR 338 / 3.1 | `jakarta.persistence` | Object-relational mapping, entity management, JPQL queries |
| **JSF** | JSR 372 / 4.0 | `jakarta.faces` | Component-based web UI framework with server-side state management |
| **EJB** | JSR 383 / 4.0 | `jakarta.ejb` | Enterprise JavaBeans — session beans, message-driven beans, timers |
| **Bean Validation** | JSR 380 / 3.0 | `jakarta.validation` | Declarative constraint validation (`@NotNull`, `@Size`, custom validators) |
| **JSON-P** | JSR 374 / 3.1 | `jakarta.json` | Streaming (JsonReader/Writer) and object model (JsonObject) JSON processing |
| **JSON-B** | JSR 367 / 2.0 | `jakarta.json.bind` | Java-to-JSON binding with annotations (`@JsonbProperty`, type adapters) |
| **JMS** | JSR 238 / 3.1 | `jakarta.jms` | Messaging API — queues, topics, producers, consumers, message listeners |
| **JTA** | JSR 907 / 2.1 | `jakarta.transaction` | Distributed transaction management, `@Transactional`, UserTransaction |
| **WebSocket** | JSR 368 / 3.1 | `jakarta.websocket` | Full-duplex bidirectional communication over a single TCP connection |
| **Interceptors** | JSR 318 / 3.0 | `jakarta.interceptor` | AOP-style cross-cutting concerns — logging, security, transactions |
| **Security** | JSR 375 / 3.1 | `jakarta.security.enterprise` | Programmatic and declarative authentication, identity stores |
| **Concurrency Utils** | JSR 236 / 2.0 | `jakarta.concurrent` | Managed thread pools, async execution, managed executors |
| **Mail** | JSR 337 / 2.1 | `jakarta.mail` | Email sending/receiving API (Eclipse Mail) |
---
## Reference Implementations
| Server | Description | Best Used For | Notable Features |
|--------|-------------|---------------|------------------|
| **WildFly** (JBoss) | Red Hat's reference implementation of Jakarta EE Full Platform | Large enterprises, microservices with full EE spec support | WildFly Swarm/JAR overlay for uber-jar distribution, Kubernetes operators, extensive management CLI |
| **Payara Server** | GlassFish-based server with commercial support options | Migration from Oracle/Sun GlassFish, high-availability clusters | Auto-deployment of MicroProfile, built-in clustering (Hazelcast), Payara Micro for lightweight deployment |
| **OpenLiberty** (IBM) | Modular, cloud-native Jakarta EE server | Containerized deployments, fast startup in Kubernetes | Feature-based activation (only load what you need), rapid restart, OpenTelemetry built-in, Liberty Profile |
| **TomEE** | Apache Tomcat + EJB/CDI/JAX-RS addons | Lightweight deployments, teams already using Tomcat | Smallest footprint of full EE servers, pluggable profiles (Plus, PlusJSR356, JavaEE), Maven plugin for assembly |
| **Quarkus** | Cloud-native Java framework with Jakarta EE compatibility | Native compilation with GraalVM, Kubernetes-first architecture | Super-fast startup (< 1s), dev mode with hot reload, extension-based architecture, not a full container but supports most specs |
---
## Integration Approaches
### MicroProfile Integration
MicroProfile sits on top of Jakarta EE as a cloud-native overlay. It adds specifications focused on microservice operational concerns:
```xml
<!-- Enable MicroProfile features in pom.xml -->
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>6.1</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
```
On WildFly, install the MicroProfile feature: `feature-manager add microprofile-6.1`
On OpenLiberty, add features to `server.xml`: `<feature>microprofile-6.1</feature>`
### Database Connectivity
JPA with JTA provides transaction-scoped persistence contexts:
```java
@Stateless
public class UserService {
@PersistenceContext(unitName = "default")
private EntityManager entityManager;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public UserDTO createUser(UserCreateRequest request) {
// Transaction begins before this line, commits after return
UserEntity entity = new UserEntity();
entity.setUsername(request.username());
entity.setEmail(request.email());
entityManager.persist(entity);
// Convert to DTO for layer boundary separation
return mapToDto(entity);
}
}
```
### JMS Messaging
Jakarta EE containers provide managed connection factories and resource adapters:
```java
@Stateless
public class OrderMessageProcessor implements MessageListener {
@Resource(lookup = "java:comp/env/jms/OrderConnectionFactory")
private ConnectionFactory connectionFactory;
@PersistenceContext(unitName = "orderDatabase")
private EntityManager entityManager;
@Override
public void onMessage(Message message) {
try {
if (message instanceof TextMessage textMsg) {
Order order = parseOrder(textMsg.getText());
processOrder(order);
// JTA transaction commits with the EJB method completion
}
} catch (JMSException e) {
LOGGER.error("Failed to process JMS message", e);
throw new RuntimeException("Message processing failed", e);
}
}
private void processOrder(Order order) {
OrderEntity entity = new OrderEntity();
entity.setOrderId(order.id());
entity.setStatus("PROCESSING");
entityManager.persist(entity);
}
}
```
### Kubernetes Deployment Manifest
Complete deployment for a Jakarta EE application on OpenLiberty:
```yaml
# kubernetes/deployment.yaml — Jakarta EE app on OpenLiberty
apiVersion: apps/v1
kind: Deployment
metadata:
name: enterprise-app
labels:
app: enterprise-app
version: v1.0.0
spec:
replicas: 3
selector:
matchLabels:
app: enterprise-app
template:
metadata:
labels:
app: enterprise-app
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9080"
spec:
containers:
- name: enterprise-app
image: openliberty/wlp-javaee10:24.0.0.12-full-jre
ports:
- containerPort: 9080
name: http
- containerPort: 9443
name: https
- containerPort: 9090
name: mgmt
volumeMounts:
- name: app-deployments
mountPath: /config/configDropins/defaults/autoscale.xml
subPath: autoscale.xml
- name: myapp
mountPath: /config/apps/myapp.war
subPath: myapp.war
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
httpGet:
path: /liveness
port: 9080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /readiness
port: 9080
initialDelaySeconds: 20
periodSeconds: 5
volumes:
- name: myapp
configMap:
name: app-artifacts
- name: app-deployments
configMap:
name: openliberty-config
---
# Service exposing the application
apiVersion: v1
kind: Service
metadata:
name: enterprise-app-service
spec:
selector:
app: enterprise-app
ports:
- port: 80
targetPort: 9080
name: http
type: ClusterIP
```
---
## Complete Working Example
### JAX-RS Resource with CDI, JPA, and Bean Validation
```java
package com.example.resource;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;
import java.util.stream.Collectors;
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
@Inject
private UserService userService;
@GET
public Response getAllUsers() {
List<UserDTO> users = userService.findAll();
return Response.ok(users).build();
}
@GET
@Path("{id}")
public Response getUserById(@PathParam("id") Long id) {
UserDTO user = userService.findById(id);
if (user == null) {
return Response.status(Response.Status.NOT_FOUND)
.entity(new ErrorResponse("User not found: " + id)).build();
}
return Response.ok(user).build();
}
@POST
@Transactional
public Response createUser(@Valid UserCreateRequest request) {
// Bean Validation intercepts here — @Valid triggers constraint checks
// on the request body before this method body executes
UserDTO created = userService.create(request);
return Response.status(Response.Status.CREATED)
.location(java.net.URI.create("/users/" + created.id()))
.entity(created).build();
}
@PUT
@Path("{id}")
@Transactional
public Response updateUser(@PathParam("id") Long id, @Valid UserUpdateRequest request) {
UserDTO updated = userService.update(id, request);
if (updated == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.ok(updated).build();
}
@DELETE
@Path("{id}")
@Transactional
public Response deleteUser(@PathParam("id") Long id) {
boolean deleted = userService.delete(id);
if (!deleted) {
return Response.status(Response.Status.NOT_FOUND).build();
}
return Response.noContent().build();
}
// Validation request DTO — Bean Validation annotations are enforced by @Valid
public record UserCreateRequest(
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
String username,
@NotBlank(message = "Email is required")
@Email(message = "Must be a valid email address")
String email,
@NotBlank(message = "Password is required")
@Size(min = 8, message = "Password must be at least 8 characters")
String password
) {}
public record ErrorResponse(String message) {}
}
```
### CDI Service Layer with JPA Persistence
```java
package com.example.service;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import jakarta.transaction.Transactional;
@ApplicationScoped
@Transactional
public class UserService {
@Inject
private EntityManager entityManager;
public UserDTO findById(Long id) {
var entity = entityManager.find(UserEntity.class, id);
return entity != null ? toDto(entity) : null;
}
public java.util.List<UserDTO> findAll() {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<UserEntity> query = cb.createQuery(UserEntity.class);
Root<UserEntity> root = query.from(UserEntity.class);
query.select(root).orderBy(cb.asc(root.get("username")));
TypedQuery<UserEntity> typedQuery = entityManager.createQuery(query);
return typedQuery.getResultStream().map(this::toDto)
.collect(java.util.stream.Collectors.toList());
}
public UserDTO create(UserResource.UserCreateRequest request) {
// Check uniqueness before persisting
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
Root<UserEntity> root = countQuery.from(UserEntity.class);
countQuery.select(cb.count(root))
.where(cb.equal(root.get("username"), request.username()));
Long existingCount = entityManager.createQuery(countQuery).getSingleResult();
if (existingCount > 0) {
throw new IllegalStateException("Username already exists: " + request.username());
}
UserEntity entity = new UserEntity();
entity.setUsername(request.username());
entity.setEmail(request.email());
entity.setPasswordHash(hashPassword(request.password()));
entityManager.persist(entity);
entityManager.flush(); // Ensure ID is assigned
return toDto(entity);
}
public boolean delete(Long id) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaDelete<UserEntity> delete = cb.createCriteriaDelete(UserEntity.class);
Root<UserEntity> root = delete.from(UserEntity.class);
delete.where(cb.equal(root.get("id"), id));
int affected = entityManager.createQuery(delete).executeUpdate();
return affected > 0;
}
private String hashPassword(String rawPassword) {
// In production, use BCrypt or Argon2 — shown here for example completeness
return java.security.MessageDigest.getInstance("SHA-256")
.digest(rawPassword.getBytes(java.nio.charset.StandardCharsets.UTF_8))
.map(b -> String.format("%02x", b))
.reduce("", String::concat);
}
private UserDTO toDto(UserEntity entity) {
return new UserDTO(
entity.getId(),
entity.getUsername(),
entity.getEmail()
// Password hash is never exposed in the DTO
);
}
public record UserDTO(Long id, String username, String email) {}
}
```
### JPA Entity with Bean Validation
```java
package com.example.entity;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
@Entity
@Table(name = "app_users",
uniqueConstraints = @UniqueConstraint(columnNames = "username"))
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Username is required")
@Size(min = 3, max = 50)
@Column(nullable = false, unique = true, length = 50)
private String username;
@Email
@NotBlank(message = "Email is required")
@Column(nullable = false, length = 120)
private String email;
@NotBlank
@Size(min = 8)
@Column(name = "password_hash", nullable = false)
private String passwordHash;
@Column(name = "created_at")
private LocalDateTime createdAt = LocalDateTime.now();
// Default constructor required by JPA
protected UserEntity() {}
public UserEntity(String username, String email, String passwordHash) {
this.username = username;
this.email = email;
this.passwordHash = passwordHash;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPasswordHash() { return passwordHash; }
public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }
public LocalDateTime getCreatedAt() { return createdAt; }
}
```
---
## Common Pitfalls
| Pitfall | Cause | Prevention |
|---------|-------|------------|
| **javax vs jakarta namespace confusion** | Mixing old `javax.servlet` imports with new server or vice versa | Use `jakarta.*` exclusively; verify all transitive dependencies declare Jakarta namespaces |
| **CDI proxy limitations** | `@RequestScoped` proxies cannot be used in static contexts or when serializing | Never call `.getClass()` on a CDI bean expecting the target type — use interfaces and inject at runtime |
| **Transaction boundary misconfiguration** | Placing `@Transactional` on repository methods instead of service layer causes nested transaction overhead | Annotate service/manager methods only; repositories remain non-transactional and rely on the caller's context |
| **JSF state management bloat** | Server-side view state stored in HTTP session grows large for pages with many components | Use client-side state saving (`<param><name>javax.faces.STATE_SAVING_METHOD</name><value>client</value></param>`) or switch to RESTeasy + JSON for APIs |
| **Missing beans.xml** | CDI scanning fails if `beans.xml` is absent in a module that requires explicit bean discovery | Include an empty `<beans/>` descriptor in `WEB-INF/beans.xml` when using `bean-discovery-mode="all"` |
| **Memory pressure on large WARs** | Heavy JSF pages with many components, unlimited session scopes holding entities | Profile heap usage with Eclipse MAT; implement lazy loading (`FetchType.LAZY`) for all entity associations |
---
## Constraints
### MUST DO
- Use `jakarta.*` namespace exclusively — never import `javax.*` packages in new code
- Set JTA transaction boundaries at the service layer, not on individual DAO or repository methods
- Include an explicit `beans.xml` with `bean-discovery-mode="annotated"` or `"all"` for CDI modules
- Configure connection pooling via the container (not application-level pools like HikariCP directly)
- Use Jakarta Bean Validation annotations (`@NotNull`, `@Size`) on DTOs passed to JAX-RS resources
- Separate entity models from DTOs at service boundaries — never expose JPA entities to the REST layer
### MUST NOT DO
- Mix `javax.*` and `jakarta.*` dependencies in the same application — this causes class loader conflicts
- Use `@Stateless` or `@Singleton` EJB annotations for everything — prefer CDI `@ApplicationScoped` for stateless services
- Store large objects (blobs, lists of entities) in HTTP sessions without size limits
- Annotate entity fields with Bean Validation instead of using XML mapping or separate constraint groups
- Deploy applications as exploded WARs in production — use packaged `.war` archives for performance and security
---
## Output Template
When configuring or reviewing a Jakarta EE application, produce:
1. **Container Selection** — Recommended server with rationale (full platform vs. web profile, cloud-native requirements)
2. **Build Configuration** — Complete `pom.xml` dependency declarations with scope annotations
3. **Component Architecture** — WAR structure diagram listing all layers and their dependencies
4. **CDI Wiring Plan** — Bean scopes, producer methods, and interceptors configured for cross-cutting concerns
5. **Transaction Design** — Service method transaction attributes and boundary decisions
---
## Related Skills
| Skill | Purpose |
|-------|---------|
| `microprofile` | Cloud-native overlay specifications (config, health, metrics) that run on top of Jakarta EE |
| `jakarta-migration` | Step-by-step process for migrating Java EE 8 (`javax.*`) applications to Jakarta EE 9+ |
---
## Live References
> Authoritative documentation links for the Jakarta EE platform.
- [Jakarta EE Official Site](https://jakarta.ee/)
- [Jakarta EE Specification Repository (GitHub)](https://github.com/eclipse-jakartaee)
- [WildFly Documentation](https://docs.wildfly.org/)
- [Payara Server Documentation](https://docs.payara.fish/)
- [OpenLiberty Documentation](https://openliberty.io/docs/)
- [TomEE Documentation](https://tomee.apache.org/documentation.html)
- [Jakarta EE API Javadoc](https://javadoc.io/doc/jakarta.platform/jakarta.jakartaee-api/latest/)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!