Jakarta EE Logo

Jakarta EE explained clearly - build modern enterprise applications with these APIs: Part 2

In the second part of our series on Jakarta EE libraries, we continue our overview and look at another selection of key technologies. We will highlight the features and applications of these libraries and show how they contribute to the development of modern enterprise applications.

In the second part of our series on Jakarta EE libraries, we continue our overview and look at another selection of key technologies. You'll learn when to use which API - and how they help you develop robust and modern Java applications.

Decoupled business logic through Jakarta Enterprise Beans (EJB)

With Jakarta EJB, you can develop distributed, transaction-safe and secure components. The best-known variants are session beans (stateless/stateful) and message-driven beans - ideal for your business logic or event processing.

<dependency>
  <groupId>jakarta.ejb</groupId>
  <artifactId>jakarta.ejb-api</artifactId>
</dependency>
import jakarta.ejb.Stateless;
import jakarta.ejb.TransactionAttribute;
import jakarta.ejb.TransactionAttributeType;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

@Stateless
public class ProductService {

    @PersistenceContext
    private EntityManager entityManager;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Product createProduct(String name, Double price) {
        Product product = new Product();
        product.setName(name);
        product.setPrice(price);

        entityManager.persist(product);
        return product;
    }
}

Integrating dynamic content - with Jakarta Expression Language (EL)

Jakarta EL is the bridge between backend logic and UI: Expressions such as ${user.name} you can access Java beans directly in JSF or JSP - ideal for dynamic forms and web content.

<dependency>
  <groupId>jakarta.el</groupId>
  <artifactId>jakarta.el-api</artifactId>
</dependency>
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Named;

@Named
@RequestScoped
public class UserBean {

    private User user;

    public String getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public String getFirstName() {
        return user.firstName();
    }

    public void getLastName() {
        return user.lastName();
    }
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
    <title>User Information</title>
</h:head>
<h:body>
    <h:form>
        <h:outputLabel value="First Name:" for="firstName" />
        <h:inputText id="firstName" value="#{userBean.firstName}" />

        <h:outputLabel value="Last Name:" for="lastName" />
        <h:inputText id="lastName" value="#{userBean.lastName}" />
    </h:form>
</h:body>
</html>

Cross-sectional functions as if from a construction kit - with interceptors

With Jakarta Interceptors, you can separate logging, auditing or security from the core of your business logic, for example. About @AroundInvoke you access method calls - centrally and reusable.

<dependency>
  <groupId>jakarta.interceptor</groupId>
  <artifactId>jakarta.interceptor-api</artifactId>
</dependency>
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.Interceptor;
import jakarta.interceptor.InvocationContext;

@Logging
@Interceptor
public class LoggingInterceptor {

    @AroundInvoke
    public Object logMethodEntry(InvocationContext context) throws Exception {
        String className = context.getTarget().getClass().getSimpleName();
        String methodName = context.getMethod().getName();
        System.out.println("Entering method: " + className + "." + methodName);

        try {
            return context.proceed();
        } finally {
            System.out.println("Exiting method: " + className + "." + methodName);
        }
    }
}

Easily translate objects into JSON - with Jakarta JSON Binding

With Jakarta JSON Binding you can convert POJOs bidirectionally into JSON and back - with type checking, configuration and automatic serialization.

<dependency>
  <groupId>jakarta.json.bind</groupId>
  <artifactId>jakarta.json.bind-api</artifactId>
</dependency>
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.json.bind.JsonbConfig;

public class JsonbExample {
    public static void main(String[] args) {
        JsonbConfig config = new JsonbConfig().withFormatting(true);
        Jsonb jsonb = JsonbBuilder.create(config);

        // Java-Objekt zu JSON
        Person person = new Person("John", "Doe", 30);
        String json = jsonb.toJson(person);
        System.out.println("Serialized JSON: " + json);

        // JSON zu Java-Objekt
        String jsonInput = "{\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"age\":25}";
        Person deserializedPerson = jsonb.fromJson(jsonInput, Person.class);
        System.out.println("Deserialized: " + deserializedPerson.getFirstName() + " " + deserializedPerson.getLastName());
    }
}

Flexible JSON handling - with Jakarta JSON Processing

With Jakarta JSON Processing (JSON-P) you can build or analyze JSON structures object-based - without any additional library: ideal for REST endpoints, configurations or logs.

<dependency>
  <groupId>jakarta.json</groupId>
  <artifactId>jakarta.json-api</artifactId>
</dependency>
import jakarta.json.Json;
import jakarta.json.JsonObject;

public class JsonpCreateExample {
    public static void main(String[] args) {
        JsonObject personJson = Json.createObjectBuilder()
            .add("firstName", "John")
            .add("lastName", "Doe")
            .add("age", 30)
            .add("email", "john.doe@example.com")
            .build();

        System.out.println("Created JSON: " + personJson);
    }
}
import jakarta.json.*;
import java.io.StringReader;

public class JsonpParseExample {
    public static void main(String[] args) {
        String jsonInput = "{\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"age\":25,\"email\":\"jane.doe@example.com\"}";

        try (JsonReader jsonReader = Json.createReader(new StringReader(jsonInput))) {
            JsonObject jsonObject = jsonReader.readObject();

            String firstName = jsonObject.getString("firstName");
            int age = jsonObject.getInt("age");

            System.out.println("Parsed: " + firstName + ", age " + age);
        }
    }
}

Sending e-mails made easy - with Jakarta Mail

With Jakarta Mail, you can easily send system-side emails from your application - e.g. for order confirmations, system notifications or password resets.

<dependency>
  <groupId>com.sun.mail</groupId>
  <artifactId>jakarta.mail</artifactId>
</dependency>
import jakarta.mail.*;
import jakarta.mail.internet.*;
import java.util.Properties;

public class MailExample {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("mail.smtp.host", "smtp.example.com");
        props.put("mail.smtp.port", "587");

        Session session = Session.getInstance(props);

        try {
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress("team@example.com"));
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("user@example.com"));
            message.setSubject("Deine erste Jakarta-Mail");
            message.setText("Diese E-Mail wurde per Jakarta Mail API verschickt.");

            Transport.send(message);
            System.out.println("E-Mail erfolgreich versendet.");
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}

Lightweight components with Jakarta Managed Beans

Simple administration, lifecycle and dependency injection: Managed beans are ideal if you need small, configurable components with little overhead.

<dependency>
  <groupId>jakarta.annotation</groupId>
  <artifactId>jakarta.annotation-api</artifactId>
</dependency>
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named;
import java.io.InputStream;
import java.util.Properties;

@Named
@ApplicationScoped
public class ConfigBean {

    private Properties configProperties;

    @PostConstruct
    public void init() {
        configProperties = new Properties();
        try (InputStream input = getClass().getClassLoader().getResourceAsStream("config.properties")) {
            configProperties.load(input);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public String getConfigProperty(String key) {
        return configProperties.getProperty(key);
    }
}

Monitoring with JMX - by Jakarta Management

Do you want to record metrics or monitor components? With Jakarta Management, you can use Java Management Extensions (JMX) to monitor your application at runtime.

<dependency>
  <groupId>jakarta.management</groupId>
  <artifactId>jakarta.management-api</artifactId>
</dependency>
public interface MemoryMonitorMBean {
    long getUsedMemory();
    long getMaxMemory();
}

public class MemoryMonitor implements MemoryMonitorMBean {
    public long getUsedMemory() {
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    }
    public long getMaxMemory() {
        return Runtime.getRuntime().maxMemory();
    }
}
import javax.management.*;
import java.lang.management.ManagementFactory;

public class MemoryMonitorExample {
    public static void main(String[] args) throws Exception {
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
        ObjectName objectName = new ObjectName("com.example:type=MemoryMonitor");
        MemoryMonitor monitor = new MemoryMonitor();
        mBeanServer.registerMBean(monitor, objectName);
    }
}

Asynchronous communication - with Jakarta Messaging (JMS)

With Jakarta Messaging you integrate message-based communication - whether in the point-to-point or publish/subscribe model.

<dependency>
  <groupId>jakarta.jms</groupId>
  <artifactId>jakarta.jms-api</artifactId>
</dependency>
import jakarta.jms.*;
import javax.naming.InitialContext;

public class SimpleJMSConsumer {

    public static void main(String[] args) {
        try {
            InitialContext ctx = new InitialContext();
            ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup("jms/ConnectionFactory");
            Queue queue = (Queue) ctx.lookup("jms/MyQueue");

            try (JMSContext jmsContext = connectionFactory.createContext()) {
                JMSConsumer consumer = jmsContext.createConsumer(queue);
                String message = consumer.receiveBody(String.class);
                System.out.println("Nachricht empfangen: " + message);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Structuring web applications - with Jakarta MVC

Jakarta MVC is the front-end counterpart to REST: You get a model-view-controller structure including JAX-RS integration for clean HTML views.

<dependency>
  <groupId>jakarta.mvc</groupId>
  <artifactId>jakarta.mvc-api</artifactId>
</dependency>
import jakarta.mvc.Controller;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;

@Path("greeting")
@Controller
public class GreetingController {

    @GET
    @Produces("text/html")
    public String sayHello() {
        return "greeting.jsp";
    }
}

Connecting objects and databases - with Jakarta Persistence (JPA)

With Jakarta JPA you map objects to relational tables - including unit-of-work and transaction management.

<dependency>
  <groupId>jakarta.persistence</groupId>
  <artifactId>jakarta.persistence-api</artifactId>
</dependency>
import jakarta.persistence.*;

@Entity
public class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    public Person() {}
    public Person(String name) {
        this.name = name;
    }

    // Getter / Setter
}
import jakarta.persistence.*;

public class JpaExample {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-pu");
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();
        em.persist(new Person("Alice"));
        em.getTransaction().commit();

        Person found = em.find(Person.class, 1L);
        System.out.println("Gefundene Person: " + found.getName());

        em.close();
        emf.close();
    }
}

REST APIs made easy - with Jakarta RESTful Web Services (JAX-RS)

REST in enterprise quality: With JAX-RS you develop modern HTTP endpoints - with JSON/XML, content negotiation and integration into other APIs.

<dependency>
  <groupId>jakarta.ws.rs</groupId>
  <artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("hello")
public class HelloWorldService {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String sayHello() {
        return "Hello, JAX-RS!";
    }
}

Conclusion: More Jakarta EE? Of course.

This part also shows: Jakarta EE is modular, standardized and practical. You only integrate what you need - without reinventing the wheel.

To be continued ...
In the third and final part, we take another look at other APIs - including Jakarta WebSocket, Security, Server Faces (JSF) and more.

Further links

Jakarta EE is a project of the Eclipse Foundation. The logo is a registered trademark.

Marius Dienel

About ME

Marius Dienel is an IT specialist in the field of application development and has been working at doubleSlash since 2019. As a software developer, he has expertise in Java EE, Spring and OSGi, among other things. He also deals with DevOps-related topics for the Business Filemanager product.

All contributions from Marius Dienel

Learn more

Further information on our website and in our newsletter

Arrow up