Jakarta EE

Jakarta EE

Jakarta EE: La Piattaforma Enterprise Java per il Cloud Native

Jakarta Enterprise Edition (EE) rappresenta l’evoluzione open source di Java EE, progettata specificamente per supportare lo sviluppo di applicazioni enterprise moderne e cloud native. In questo articolo esamineremo in dettaglio la suddetta piattaforma le sue caratteristiche principali, l’ecosistema tecnologico e forniremo una guida pratica per sviluppatori e ingegneri informatici.

Storia e Origini di Jakarta EE

Jakarta EE nasce dalla transizione di Java Enterprise Edition sotto la gestione dell’Eclipse Foundation nel 2017. Questa transizione è avvenuta quando Oracle ha deciso di trasferire le tecnologie Java EE alla Eclipse Foundation per accelerare l’innovazione e lo sviluppo della piattaforma enterprise Java.

Il nome “Jakarta” è un riferimento alla capitale dell’Indonesia e rappresenta simbolicamente un nuovo inizio per la piattaforma enterprise Java. La Eclipse Foundation, insieme a un consorzio di leader dell’industria tecnologica tra cui Fujitsu, IBM, Oracle, Red Hat, e Tomitribe, coordina lo sviluppo e l’evoluzione di Jakarta Enterprise Edition.

L’evoluzione storica della piattaforma può essere tracciata attraverso le seguenti fasi:

  • J2EE (Java 2 Enterprise Edition): Le prime versioni della piattaforma enterprise
  • Java EE: L’evoluzione moderna sotto Oracle
  • Jakarta EE: L’era open source sotto Eclipse Foundation

Caratteristiche Principali e Finalità Jakarta EE

Jakarta Enterprise Edition è progettata per supportare lo sviluppo di applicazioni enterprise scalabili, sicure e affidabili. Le caratteristiche principali includono:

Architettura Modulare

La piattaforma è strutturata in specifiche modulari che possono essere implementate singolarmente o in combinazione, offrendo flessibilità nella costruzione di applicazioni enterprise di diverse dimensioni e complessità.

Cloud Native Support

Jakarta Enterprise Edition è specificamente ottimizzata per ambienti cloud, supportando pattern architetturali moderni come microservizi, containerizzazione e deployment distribuiti.

Backward Compatibility

Mantiene la compatibilità con le applicazioni Java Enterprise Edition esistenti, proteggendo gli investimenti tecnologici e facilitando la migrazione graduale.

Standard Aperti

Basata su specifiche aperte e standard dell’industria, garantisce interoperabilità e prevenzione del vendor lock-in.

I Profili di Jakarta EE

Jakarta Enterprise Edition è organizzata in tre profili principali, ciascuno progettato per specifici casi d’uso e requisiti architetturali:

Jakarta EE Core Profile

Il Core Profile, introdotto con Jakarta EE 10, definisce un sottoinsieme minimal delle specifiche Jakarta Enterprise Edition specificamente progettato per runtime più piccoli. Questo profilo è ottimizzato per:

  • Microservizi: Applicazioni di dimensioni ridotte con footprint limitato
  • Ahead-of-Time (AOT) Compilation: Supporto per la compilazione nativa con strumenti come GraalVM
  • Cloud Native Applications: Applicazioni containerizzate con tempi di startup rapidi

Il Core Profile include le seguenti specifiche essenziali:

  • Jakarta CDI (Contexts and Dependency Injection)
  • Jakarta RESTful Web Services
  • Jakarta JSON Processing e JSON Binding
  • Jakarta Annotations

Jakarta EE Web Profile

Il Web Profile rappresenta un sottoinsieme completo della piattaforma Jakarta EE, specificatamente progettato per lo sviluppo di applicazioni web moderne. Include tutte le tecnologie del Core Profile più:

  • Jakarta Servlet
  • Jakarta Server Pages (JSP)
  • Jakarta Server Faces (JSF)
  • Jakarta Bean Validation
  • Jakarta Persistence (JPA)
  • Jakarta Transactions
  • Jakarta Security

Questo profilo è ideale per applicazioni web tradizionali che richiedono un set completo di funzionalità enterprise senza la complessità della piattaforma completa.

Jakarta EE Platform (Full Profile)

Il Full Platform include tutte le specifiche Jakarta Enterprise Edition, fornendo il set completo di servizi enterprise. Oltre alle tecnologie dei profili precedenti, include:

  • Jakarta Enterprise Beans (EJB)
  • Jakarta Message Service (JMS)
  • Jakarta Mail
  • Jakarta Batch
  • Jakarta Concurrency
  • Jakarta Connector Architecture

Linguaggi e Competenze Richieste

Linguaggio Principale: Java

Jakarta EE è primariamente sviluppata in Java, richiedendo una solida conoscenza del linguaggio e del suo ecosistema. Le versioni recenti di Jakarta EE supportano:

  • Jakarta EE 10: Richiede Java 11 come versione minima
  • Jakarta EE 11: Supporta Java 17 come versione di riferimento

Competenze Tecniche Fondamentali

Per sviluppare efficacemente con Jakarta Enterprise Edition, sono necessarie le seguenti competenze:

Architetture Enterprise:

  • Pattern architetturali enterprise (MVC, Dependency Injection, Aspect-Oriented Programming)
  • Principi di design SOLID
  • Architetture a microservizi e SOA

Tecnologie Web:

  • Protocolli HTTP/HTTPS
  • Architetture RESTful
  • Formati di dati JSON e XML
  • Tecnologie front-end per l’integrazione

Gestione Dati:

  • Database relazionali e SQL
  • Object-Relational Mapping (ORM)
  • Transazioni ACID
  • Ottimizzazione delle query

Sicurezza:

  • Autenticazione e autorizzazione
  • Crittografia e certificati digitali
  • Secure coding practices
  • OAuth 2.0 e OpenID Connect

Iniziare un Progetto Jakarta EE

Setup dell’Ambiente di Sviluppo

Per iniziare un progetto Jakarta Enterprise Edition, è necessario configurare un ambiente di sviluppo appropriato:

IDE Consigliato: IntelliJ IDEA Ultimate o Eclipse IDE for Enterprise Java and Web Developers sono le scelte più popolari per lo sviluppo Jakarta Enterprise Edition, offrendo supporto completo per le specifiche e strumenti di debugging avanzati.

Build Tool: Maven è il build tool de facto per progetti Jakarta Enterprise Edition, sebbene Gradle sia una valida alternativa.

Application Server: GlassFish è l’implementation server di riferimento per Jakarta EE, ma sono disponibili alternative come WildFly, Open Liberty, e Payara.

Struttura di Progetto Base Jakarta EE

Un progetto Jakarta Enterprise Edition tipico utilizza la seguente struttura Maven:

my-jakarta-project/
├── pom.xml
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/
│   │   │       ├── config/
│   │   │       ├── model/
│   │   │       ├── service/
│   │   │       └── rest/
│   │   ├── resources/
│   │   │   └── META-INF/
│   │   │       └── beans.xml
│   │   └── webapp/
│   │       └── WEB-INF/
│   │           └── web.xml
│   └── test/
│       └── java/
└── target/

Configurazione Maven

Il file pom.xml per un progetto Jakarta EE dovrebbe includere le seguenti dipendenze base:

<?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>jakarta-ee-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jakarta.ee.version>10.0.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>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Sviluppo di RESTful Web Services Jakarta EE

Jakarta Enterprise Edition fornisce Jakarta RESTful Web Services (JAX-RS) per lo sviluppo di servizi web RESTful. Ecco un esempio completo di implementazione:

Configurazione dell’Applicazione REST

package com.example.config;

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/api")
public class RestApplication extends Application {
    // La classe è vuota, la configurazione è gestita tramite annotazioni
}

Modello di Dati

package com.example.model;

import jakarta.json.bind.annotation.JsonbProperty;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Email;

public class User {
    
    @JsonbProperty("id")
    private Long id;
    
    @NotBlank(message = "Il nome è obbligatorio")
    @JsonbProperty("name")
    private String name;
    
    @Email(message = "Email non valida")
    @JsonbProperty("email")
    private String email;
    
    // Costruttori
    public User() {}
    
    public User(Long id, String name, String email) {
        this.id = id;
        this.name = name;
        this.email = email;
    }
    
    // Getter e Setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

Service Layer con CDI

package com.example.service;

import com.example.model.User;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

@ApplicationScoped
public class UserService {
    
    private final Map<Long, User> users = new ConcurrentHashMap<>();
    private final AtomicLong idCounter = new AtomicLong(1);
    
    public UserService() {
        // Dati di esempio per il testing
        createUser(new User(null, "Mario Rossi", "mario.rossi@example.com"));
        createUser(new User(null, "Luigi Verdi", "luigi.verdi@example.com"));
    }
    
    public List<User> getAllUsers() {
        return new ArrayList<>(users.values());
    }
    
    public Optional<User> getUserById(Long id) {
        return Optional.ofNullable(users.get(id));
    }
    
    public User createUser(User user) {
        Long id = idCounter.getAndIncrement();
        user.setId(id);
        users.put(id, user);
        return user;
    }
    
    public Optional<User> updateUser(Long id, User updatedUser) {
        if (users.containsKey(id)) {
            updatedUser.setId(id);
            users.put(id, updatedUser);
            return Optional.of(updatedUser);
        }
        return Optional.empty();
    }
    
    public boolean deleteUser(Long id) {
        return users.remove(id) != null;
    }
}

REST Controller

package com.example.rest;

import com.example.model.User;
import com.example.service.UserService;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;

@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
    
    @Inject
    private UserService userService;
    
    @GET
    public Response getAllUsers() {
        List<User> users = userService.getAllUsers();
        return Response.ok(users).build();
    }
    
    @GET
    @Path("/{id}")
    public Response getUserById(@PathParam("id") Long id) {
        return userService.getUserById(id)
                .map(user -> Response.ok(user).build())
                .orElse(Response.status(Response.Status.NOT_FOUND).build());
    }
    
    @POST
    public Response createUser(@Valid User user) {
        User createdUser = userService.createUser(user);
        return Response.status(Response.Status.CREATED)
                .entity(createdUser)
                .build();
    }
    
    @PUT
    @Path("/{id}")
    public Response updateUser(@PathParam("id") Long id, @Valid User user) {
        return userService.updateUser(id, user)
                .map(updatedUser -> Response.ok(updatedUser).build())
                .orElse(Response.status(Response.Status.NOT_FOUND).build());
    }
    
    @DELETE
    @Path("/{id}")
    public Response deleteUser(@PathParam("id") Long id) {
        boolean deleted = userService.deleteUser(id);
        return deleted ? 
                Response.noContent().build() : 
                Response.status(Response.Status.NOT_FOUND).build();
    }
}

Sicurezza nei Servizi Web RESTful

Jakarta Enterprise Edition fornisce Jakarta Security per implementare meccanismi di sicurezza robusti. Ecco come implementare l’autenticazione e l’autorizzazione:

Configurazione di Sicurezza Base

package com.example.config;

import jakarta.annotation.security.DeclareRoles;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import jakarta.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import jakarta.security.enterprise.AuthenticationStatus;
import jakarta.security.enterprise.credential.UsernamePasswordCredential;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@ApplicationScoped
@DeclareRoles({"USER", "ADMIN"})
public class CustomAuthenticationMechanism implements HttpAuthenticationMechanism {
    
    @Override
    public AuthenticationStatus validateRequest(
            HttpServletRequest request, 
            HttpServletResponse response, 
            HttpMessageContext httpMessageContext) {
        
        String authHeader = request.getHeader("Authorization");
        
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            
            // Validazione del token (implementazione semplificata)
            if (isValidToken(token)) {
                String username = extractUsernameFromToken(token);
                String role = extractRoleFromToken(token);
                
                return httpMessageContext.notifyContainerAboutLogin(
                    username, 
                    Set.of(role)
                );
            }
        }
        
        return httpMessageContext.responseUnauthorized();
    }
    
    private boolean isValidToken(String token) {
        // Implementazione della validazione del token
        return "valid-token-123".equals(token);
    }
    
    private String extractUsernameFromToken(String token) {
        // Estrazione dell'username dal token
        return "user@example.com";
    }
    
    private String extractRoleFromToken(String token) {
        // Estrazione del ruolo dal token
        return "USER";
    }
}

REST Controller con Sicurezza

package com.example.rest;

import com.example.model.User;
import com.example.service.UserService;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.security.enterprise.SecurityContext;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.util.List;

@Path("/secure/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class SecureUserResource {
    
    @Inject
    private UserService userService;
    
    @Context
    private SecurityContext securityContext;
    
    @GET
    @RolesAllowed({"USER", "ADMIN"})
    public Response getAllUsers() {
        List<User> users = userService.getAllUsers();
        return Response.ok(users).build();
    }
    
    @POST
    @RolesAllowed("ADMIN")
    public Response createUser(User user) {
        User createdUser = userService.createUser(user);
        return Response.status(Response.Status.CREATED)
                .entity(createdUser)
                .build();
    }
    
    @DELETE
    @Path("/{id}")
    @RolesAllowed("ADMIN")
    public Response deleteUser(@PathParam("id") Long id) {
        boolean deleted = userService.deleteUser(id);
        return deleted ? 
                Response.noContent().build() : 
                Response.status(Response.Status.NOT_FOUND).build();
    }
    
    @GET
    @Path("/profile")
    @RolesAllowed({"USER", "ADMIN"})
    public Response getCurrentUserProfile() {
        String currentUser = securityContext.getCallerPrincipal().getName();
        
        // Logica per recuperare il profilo dell'utente corrente
        return Response.ok()
                .entity(Map.of("user", currentUser, "authenticated", true))
                .build();
    }
}

Gestione Exception Personalizzata

package com.example.exception;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import java.util.HashMap;
import java.util.Map;

@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
    
    @Override
    public Response toResponse(ConstraintViolationException exception) {
        Map<String, String> errors = new HashMap<>();
        
        for (ConstraintViolation<?> violation : exception.getConstraintViolations()) {
            String propertyPath = violation.getPropertyPath().toString();
            String message = violation.getMessage();
            errors.put(propertyPath, message);
        }
        
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("status", "error");
        errorResponse.put("message", "Errori di validazione");
        errorResponse.put("errors", errors);
        
        return Response.status(Response.Status.BAD_REQUEST)
                .entity(errorResponse)
                .build();
    }
}

Ecosystem e Strumenti Jakarta EE

L’ecosistema Jakarta include numerosi strumenti e framework che facilitano lo sviluppo:

Application Servers

  • GlassFish: Server di riferimento ufficiale
  • WildFly: Server application enterprise di Red Hat
  • Open Liberty: Runtime leggero di IBM
  • Payara: Fork commerciale di GlassFish

Testing Framework

  • Arquillian: Framework per testing di integrazione
  • TestContainers: Per testing con container Docker
  • JUnit 5: Framework di testing standard per Java

Monitoring e Observability

  • MicroProfile Metrics: Metriche standardizzate
  • MicroProfile Health: Health checks per applicazioni
  • MicroProfile OpenTracing: Distributed tracing

Conclusioni Jakarta EE

L’ecosistema Jakarta rappresenta l’evoluzione naturale delle tecnologie enterprise Java, offrendo una piattaforma robusta, modulare e cloud-ready per lo sviluppo di applicazioni enterprise moderne. La sua architettura a profili permette di scegliere il livello di complessità appropriato per ogni progetto, dal Core Profile per microservizi al Full Platform per applicazioni enterprise complete.

L’adozione di Jakarta Enterprise Edition garantisce:

  • Standardizzazione: Basata su specifiche aperte e standard dell’industria
  • Portabilità: Applicazioni che funzionano su diversi application server
  • Scalabilità: Architettura progettata per carichi enterprise
  • Sicurezza: Meccanismi di sicurezza integrati e robusti
  • Futuro: Roadmap di sviluppo guidata dalla community

Per sviluppatori e architetti software, Jakarta Enterprise Edition offre un percorso chiaro per modernizzare applicazioni esistenti e sviluppare nuove soluzioni enterprise che sfruttano i benefici del cloud computing mantenendo la solidità e l’affidabilità che caratterizzano la piattaforma Java enterprise.

(fonte) (fonte) (fonte)

Innovaformazione, scuola informatica specialistica segue costantemente il mercato IT ed accompagna le aziende nella formazione continua dei team di sviluppatori e DevOps. Trovate l’elenco corsi nell’offerta formativa QUI.

Per altri articoli tecnici consigliamo di navigare sul nostro blog QUI.

Per info e preventivi per le aziende: info@innovaformazione.net – tel. 3471012275 (Dario Carrassi)

Ti potrebbe interessare

Articoli correlati