#Enterprise-Java-Technologien verstehen

Dieses Kapitel bündelt die zentralen Legacy-Technologien: EJB, JTA, JMS, SOAP, JSP/Servlet, JPA/JDBC und Security.

#EJB-Lernpfad

#Was du verstehen musst

  • Stateless Session Beans
  • Stateful Session Beans
  • Singleton Beans
  • Message Driven Beans
  • Local vs Remote Interfaces
  • No-interface View
  • EJB Lifecycle
  • Pooling
  • Interceptors
  • Timer Services
  • Container Managed Transactions
  • Bean Managed Transactions
  • Security Context
  • JNDI Lookups
  • EJB Exceptions

#EJB-Analyse-Vorlage

markdown
# EJB Analyse: <Klassenname>

## Typ
- Stateless / Stateful / Singleton / MDB:

## Interfaces
- Local:
- Remote:
- No-interface:

## Transaktion
- Default:
- Annotationen:
- REQUIRES_NEW:
- NOT_SUPPORTED:
- setRollbackOnly:
- UserTransaction:

## Dependencies
- @EJB:
- @Resource:
- @PersistenceContext:
- SOAP:
- JMS:
- JNDI:
- Andere:

## Fachliche Verantwortung
- Use Cases:
- Tabellen:
- Events:
- externe Systeme:

## Risiken
- Transaktion:
- externe Calls:
- Side Effects:
- Tests fehlen:
- Migration schwierig wegen:

## Modernisierungsziel
- bleibt vorerst EJB:
- wird dünne Fassade:
- Fachlogik wandert nach:
- Adapter nötig für:

#EJB-Migrationsmuster

Alt:

java
@Stateless
public class OrderServiceBean {

    @PersistenceContext
    private EntityManager em;

    @EJB
    private AuditServiceBean auditService;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void createOrder(CreateOrderRequest request) {
        OrderEntity order = new OrderEntity();
        order.setCustomerId(request.getCustomerId());
        order.setStatus("NEW");

        em.persist(order);
        auditService.write("ORDER_CREATED");
    }
}

Zwischenziel:

java
public class CreateOrderUseCase {

    private final OrderRepository orderRepository;
    private final AuditPort auditPort;

    public CreateOrderUseCase(
            OrderRepository orderRepository,
            AuditPort auditPort
    ) {
        this.orderRepository = orderRepository;
        this.auditPort = auditPort;
    }

    public OrderId execute(CreateOrderCommand command) {
        Order order = Order.create(command.customerId());
        orderRepository.save(order);
        auditPort.orderCreated(order.id());
        return order.id();
    }
}

EJB als dünne Fassade:

java
@Stateless
public class OrderServiceBean {

    @PersistenceContext
    private EntityManager em;

    @EJB
    private AuditServiceBean auditServiceBean;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public OrderId createOrder(CreateOrderRequest request) {
        CreateOrderUseCase useCase = new CreateOrderUseCase(
                new JpaOrderRepository(em),
                new EjbAuditAdapter(auditServiceBean)
        );

        return useCase.execute(mapToCommand(request));
    }
}

#JTA- und Transaktions-Lernpfad

#Was du verstehen musst

  • REQUIRED
  • REQUIRES_NEW
  • MANDATORY
  • SUPPORTS
  • NOT_SUPPORTED
  • NEVER
  • Bean Managed Transactions
  • Container Managed Transactions
  • Rollback bei RuntimeException
  • Rollback bei checked Exceptions über @ApplicationException
  • setRollbackOnly
  • XA vs non-XA
  • Two Phase Commit
  • DB + JMS in einer Transaktion
  • Transaktionen über Remote Calls
  • Lazy Loading und EntityManager Lifecycle
  • Self-invocation und Proxy-Grenzen

#Transaction Map Vorlage

markdown
# Transaction Map: <Use Case>

## Main Transaction

Start:
- Klasse/Methode:
- TransactionAttribute:

Beteiligte Ressourcen:
- Datasource:
- Persistence Unit:
- JMS Queue/Topic:
- SOAP Calls:
- File/Mail:

DB Writes:
- Tabelle 1:
- Tabelle 2:

Nested Transactions:
- Klasse/Methode:
- Typ:
- Warum?

Rollback:
- RuntimeException:
- Checked Exception:
- @ApplicationException:
- setRollbackOnly:
- manuelles rollback:

Risiken:
- externer Call innerhalb TX:
- lange Locks:
- REQUIRES_NEW:
- XA:
- unklare Exception-Behandlung:

#Mapping EJB zu Spring

text
TransactionAttributeType.REQUIRED
    → @Transactional(propagation = Propagation.REQUIRED)

TransactionAttributeType.REQUIRES_NEW
    → @Transactional(propagation = Propagation.REQUIRES_NEW)

TransactionAttributeType.NOT_SUPPORTED
    → @Transactional(propagation = Propagation.NOT_SUPPORTED)

TransactionAttributeType.MANDATORY
    → @Transactional(propagation = Propagation.MANDATORY)

TransactionAttributeType.SUPPORTS
    → @Transactional(propagation = Propagation.SUPPORTS)

TransactionAttributeType.NEVER
    → @Transactional(propagation = Propagation.NEVER)

Achtung bei checked Exceptions:

java
@Transactional(rollbackFor = PaymentFailedException.class)
public void pay(...) {
    ...
}

#JMS-Lernpfad

#Was du verstehen musst

  • Queue vs Topic
  • Producer vs Consumer
  • MDB
  • Redelivery
  • Dead Letter Queue
  • Acknowledgement
  • transaktionale Message-Verarbeitung
  • Poison Messages
  • Message Selectors
  • Correlation ID
  • Message ID
  • Idempotenz
  • Retry-Strategien
  • XA mit DB + JMS

#Queue Map Vorlage

markdown
# Queue Map

| Queue/Topic | Producer | Consumer | Message Type | Retry | DLQ | Fachlicher Zweck |
|---|---|---|---|---|---|---|
| OrderCreatedQueue | OrderServiceBean | InvoiceMDB | XML | ja? | ja? | Rechnung erzeugen |

#MDB-Migrationsmuster

Alt:

java
@MessageDriven
public class InvoiceMDB implements MessageListener {

    @EJB
    private InvoiceServiceBean invoiceServiceBean;

    @Override
    public void onMessage(Message message) {
        invoiceServiceBean.createInvoice(message);
    }
}

Zwischenziel:

java
public class InvoiceMessageHandler {

    private final CreateInvoiceUseCase createInvoiceUseCase;

    public InvoiceMessageHandler(CreateInvoiceUseCase createInvoiceUseCase) {
        this.createInvoiceUseCase = createInvoiceUseCase;
    }

    public void handle(OrderCreatedMessage message) {
        createInvoiceUseCase.execute(
                new CreateInvoiceCommand(message.orderId())
        );
    }
}

MDB bleibt dünn:

java
@MessageDriven
public class InvoiceMDB implements MessageListener {

    private InvoiceMessageHandler handler;

    @Override
    public void onMessage(Message message) {
        OrderCreatedMessage parsed = OrderCreatedMessageMapper.fromJms(message);
        handler.handle(parsed);
    }
}

#Idempotenz-Tabelle

markdown
| Message | Idempotency Key | Prüfung | Aktion bei Duplikat |
|---|---|---|---|
| OrderCreated | orderId | Invoice exists? | ignorieren |
| PaymentCaptured | paymentId | Payment exists? | ignorieren |
| CustomerUpdated | customerId + version | newer version? | alte Version ignorieren |

#SOAP-Lernpfad

#Was du verstehen musst

  • WSDL lesen
  • XSD Typen
  • JAXB-Klassen
  • JAX-WS Client
  • JAX-WS Endpoint
  • SOAP Faults
  • WS-Security
  • MTOM
  • Timeout-Konfiguration
  • Contract-first vs Code-first
  • Versionierung von XML-Schemas
  • generierter Code vs manuell geänderter Code

#SOAP Map Vorlage

markdown
# SOAP Services

| Service | Richtung | Klasse | WSDL/XSD | Auth | Timeout | Risiko |
|---|---|---|---|---|---|---|
| CustomerService | Inbound | CustomerEndpoint | customer.wsdl | WS-Security | n/a | hoch |
| PaymentProvider | Outbound | PaymentSoapClient | payment.wsdl | Zertifikat | 30s? | hoch |

#SOAP Adapter Muster

Alt:

java
@WebServiceRef
private PaymentService paymentService;

public void pay(Order order) {
    paymentService.getPaymentPort().charge(...);
}

Besser:

java
public interface PaymentGateway {
    PaymentResult charge(PaymentCommand command);
}

SOAP Adapter:

java
public class SoapPaymentGateway implements PaymentGateway {

    private final PaymentPort port;

    public SoapPaymentGateway(PaymentPort port) {
        this.port = port;
    }

    @Override
    public PaymentResult charge(PaymentCommand command) {
        try {
            return map(port.charge(map(command)));
        } catch (SOAPFaultException ex) {
            throw new PaymentProviderException(ex);
        }
    }
}

#JSP/Servlet-Lernpfad

#Was du verstehen musst

  • JSP Scriptlets
  • JSTL
  • Custom Tags
  • Servlet Filter
  • Session State
  • Authentication Flow
  • CSRF
  • serverseitige Formulare
  • Businesslogik in JSP
  • direkte EJB/JNDI-Zugriffe aus JSP
  • Request/Session/Application Scope

#JSP Risikoanalyse

markdown
# JSP Risikoanalyse

| JSP | Zweck | Businesslogik? | Nutzt Session? | Ruft EJB/DAO? | Risiko |
|---|---|---|---|---|---|
| createOrder.jsp | Bestellung anlegen | ja | ja | ja | hoch |
| orderList.jsp | Liste anzeigen | nein | ja | nein | niedrig |

#JSP-Migrationsreihenfolge

text
JSP mit Scriptlets
    ↓
Servlet/Controller übernimmt Logik
    ↓
JSP wird reine View
    ↓
Application Service wird genutzt
    ↓
REST/API möglich
    ↓
moderne UI möglich

Alt:

jsp
<%
OrderService service = ...
List<Order> orders = service.findOrders();
%>

Besser:

java
@WebServlet("/orders")
public class OrderServlet extends HttpServlet {

    private OrderQueryService orderQueryService;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        req.setAttribute("orders", orderQueryService.findOrders());
        req.getRequestDispatcher("/WEB-INF/views/orders.jsp")
           .forward(req, resp);
    }
}

#JPA/JDBC/Datenbank-Lernpfad

#Was du verstehen musst

  • Persistence Units
  • EntityManager Lifecycle
  • Lazy Loading
  • Flush/Commit
  • JPQL
  • Criteria API
  • Native Queries
  • Stored Procedures
  • DB Trigger
  • Views
  • Shared Tables
  • Optimistic/Pessimistic Locking
  • Isolation Levels
  • Batch Updates
  • Connection Pools
  • XA Datasources

#Datenbank-Ownership-Tabelle

markdown
# Database Ownership

| Tabelle | Genutzt von | Schreibzugriffe | Kritisch? | Modernisierungsrisiko |
|---|---|---|---|---|
| ORDERS | OrderService, InvoiceMDB | viele | ja | hoch |
| AUDIT_LOG | AuditService | append-only | mittel | niedrig |
| CUSTOMER | CustomerService, CRM Sync | viele | ja | hoch |

#Entity vs Domain

JPA Entity:

java
@Entity
@Table(name = "ORDERS")
public class OrderEntity {

    @Id
    private String id;

    private String status;
    private String customerId;
}

Domain:

java
public class Order {

    private final OrderId id;
    private final CustomerId customerId;
    private OrderStatus status;

    public static Order create(CustomerId customerId) {
        return new Order(OrderId.newId(), customerId, OrderStatus.NEW);
    }

    public void cancel() {
        if (status == OrderStatus.SHIPPED) {
            throw new OrderCannotBeCancelledException(id);
        }
        this.status = OrderStatus.CANCELLED;
    }
}

#Security-Lernpfad

#Was du verstehen musst

  • Container Security
  • JAAS / Realm
  • web.xml Security Constraints
  • @RolesAllowed
  • @PermitAll
  • @DenyAll
  • SessionContext#getCallerPrincipal
  • isCallerInRole
  • Servlet Filters
  • Login Flow
  • Session Fixation
  • CSRF
  • Authorization in JSPs
  • Role Mapping im Application Server

#Security Map

markdown
# Security Map

| Funktion | Rolle | Prüfung wo? | Mechanismus | Risiko |
|---|---|---|---|---|
| Bestellung anzeigen | USER | web.xml | Container | mittel |
| Bestellung freigeben | MANAGER | EJB SessionContext | manuell | hoch |
| Admin-Seite | ADMIN | JSP/Filter | gemischt | hoch |

Modernisierungsrisiko:

text
Wenn EJB entfernt wird, gehen eventuell automatisch verloren:
- CallerPrincipal
- Rollenprüfung
- Security Context Propagation
- JAAS Realm Integration

⌂ Cockpit