Modul 09 · Migration Deep Dive · Stufe 3 Application Layer

Servlet und Web

javax.servlet wird nachvollziehbar in Spring MVC auf jakarta.servlet überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.

Front ControllerMVC Controller
2 → 2Produktionsdateien
42 → 26Java-Zeilen
1 → 1Testdateien
3 / 3Dependencies entfernt / neu
NIEDRIGRisiko · Score 0

Was sich konkret ändert

DimensionLegacyModernMigrationskonsequenz
ProgrammiermodellOverrideGetMapping, RestController, SpringBootApplicationAnnotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen.
Abhängigkeiten3 direkte Dependencies3 direkte Dependencies3 neu, 3 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen.
Öffentliche API2 erkannte Methoden2 erkannte MethodenMethoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag.
Datenmodellclass ServletLegacyApplication, class StatusServletclass StatusController, class WebModernApplicationFeldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet.
FehlerverhaltenLegacy-Exceptions und Rückgabewerteexplizitere Fach-/Framework-FehlerabbildungFehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden.
TestsBestands- und Golden-Master-TestsUnit-, Slice-, Contract- und IntegrationstestsDer Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt.
BetriebLegacy-Start/Lifecyclemodernes Packaging, Health und externe KonfigurationReverse Proxy, Context Path, Graceful Shutdown und Management-Exposition prüfen.
RollbackLegacy-Artefakt bleibt unverändertModern-Artefakt getrennt deploybarKein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein.

Fachlicher Vertrag: unverändert zu erhalten

  • Pfad, Statuscode, Content-Type und Response-Body bleiben vertragskonform.
  • Container-spezifische Startlogik wird vom Controller getrennt.
  • Statische Inhalte und API-Routen kollidieren nicht.

Hauptrisiko und Testfokus

container/API migration and endpoint compatibility

web slice, content type, health and malformed request tests

Die Modernisierung gilt erst als abgeschlossen, wenn der fachliche Vertrag automatisiert belegt ist.

Verbindliches Code- und Rollenmapping

Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.

Legacy-Rolle / DateiModern-Rolle / DateiBedeutung
StatusServlet.javaStatusController.javaServlet Adapter wird Spring MVC Controller.
ServletLegacyApplication.javaWebModernApplication.javaEmbedded Jetty Bootstrap wird Spring Boot.

Legacy-Quellinventar

DateiTypAnnotationenöffentliche APIPatternZeilen
ServletLegacyApplication.javaclass ServletLegacyApplicationvoid main(String[] args)21
StatusServlet.javaclass StatusServletOverridevoid doGet(HttpServletRequest request, HttpServletResponse response)Front Controller (kleine Demonstration)21

Modern-Quellinventar

DateiTypAnnotationenöffentliche APIPatternZeilen
StatusController.javaclass StatusControllerGetMapping, RestControllerMap<String, String> status()Front Controller + MVC Controller15
WebModernApplication.javaclass WebModernApplicationSpringBootApplicationvoid main(String[] args)11

Dependency- und Laufzeitmigration

StatusDependencyVersionScopePrüfung
ENTFERNTorg.eclipse.jetty:jetty-server${jetty10.version}compileLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
ENTFERNTorg.eclipse.jetty:jetty-servlet${jetty10.version}compileLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
ENTFERNTorg.junit.jupiter:junit-jupiterBOM/ParenttestLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
NEUorg.springframework.boot:spring-boot-starter-actuatorBOM/ParentcompileNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
NEUorg.springframework.boot:spring-boot-starter-testBOM/ParenttestNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
NEUorg.springframework.boot:spring-boot-starter-webBOM/ParentcompileNeue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen.
Direkte POM-Daten ersetzen keinen erfolgreichen Online-Build mit transitiver SBOM- und CVE-Prüfung.

Vorher-/Nachher-Codebelege

Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.

Legacy
StatusServlet.java
Modern
StatusController.java

Migrationsbedeutung: Servlet Adapter wird Spring MVC Controller.

Legacy-Code

projects/09-servlet-web/legacy/src/main/java/at/aydin/lab/web/legacy/StatusServlet.java

package at.aydin.lab.web.legacy;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class StatusServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setStatus(200);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(statusJson());
    }

    // Design Pattern: Front Controller (kleine Demonstration)
    // Zweck: Ein zentraler Servlet-Endpunkt verarbeitet den HTTP-Aufruf.
    static String statusJson() {
        return "{\"status\":\"UP\",\"stack\":\"javax.servlet\"}";
    }
}

Modern-Code

projects/09-servlet-web/modern/src/main/java/at/aydin/lab/web/modern/StatusController.java

package at.aydin.lab.web.modern;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
// Design Pattern: Front Controller + MVC Controller
// Zweck: Spring MVC übernimmt Routing, Konvertierung und HTTP-Infrastruktur.
public class StatusController {
    @GetMapping("/status")
    public Map<String, String> status() {
        return Map.of("status", "UP", "stack", "Spring MVC / jakarta.servlet");
    }
}
Technischen Unified Diff öffnen
--- StatusServlet.java
+++ StatusController.java
@@ -1,21 +1,15 @@
-package at.aydin.lab.web.legacy;
+package at.aydin.lab.web.modern;
 
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import java.util.Map;
 
-public class StatusServlet extends HttpServlet {
-    @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
-        response.setStatus(200);
-        response.setContentType("application/json;charset=UTF-8");
-        response.getWriter().write(statusJson());
-    }
-
-    // Design Pattern: Front Controller (kleine Demonstration)
-    // Zweck: Ein zentraler Servlet-Endpunkt verarbeitet den HTTP-Aufruf.
-    static String statusJson() {
-        return "{\"status\":\"UP\",\"stack\":\"javax.servlet\"}";
+@RestController
+// Design Pattern: Front Controller + MVC Controller
+// Zweck: Spring MVC übernimmt Routing, Konvertierung und HTTP-Infrastruktur.
+public class StatusController {
+    @GetMapping("/status")
+    public Map<String, String> status() {
+        return Map.of("status", "UP", "stack", "Spring MVC / jakarta.servlet");
     }
 }
Legacy
ServletLegacyApplication.java
Modern
WebModernApplication.java

Migrationsbedeutung: Embedded Jetty Bootstrap wird Spring Boot.

Legacy-Code

projects/09-servlet-web/legacy/src/main/java/at/aydin/lab/web/legacy/ServletLegacyApplication.java

package at.aydin.lab.web.legacy;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;

public final class ServletLegacyApplication {
    private ServletLegacyApplication() {
    }

    public static void main(String[] args) throws Exception {
        Server server = new Server(8083);
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        context.addServlet(new ServletHolder(new StatusServlet()), "/status");
        server.setHandler(context);
        server.start();
        System.out.println("Legacy Servlet: http://localhost:8083/status");
        server.join();
    }
}

Modern-Code

projects/09-servlet-web/modern/src/main/java/at/aydin/lab/web/modern/WebModernApplication.java

package at.aydin.lab.web.modern;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WebModernApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebModernApplication.class, args);
    }
}
Technischen Unified Diff öffnen
--- ServletLegacyApplication.java
+++ WebModernApplication.java
@@ -1,21 +1,11 @@
-package at.aydin.lab.web.legacy;
+package at.aydin.lab.web.modern;
 
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.servlet.ServletContextHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
 
-public final class ServletLegacyApplication {
-    private ServletLegacyApplication() {
-    }
-
-    public static void main(String[] args) throws Exception {
-        Server server = new Server(8083);
-        ServletContextHandler context = new ServletContextHandler();
-        context.setContextPath("/");
-        context.addServlet(new ServletHolder(new StatusServlet()), "/status");
-        server.setHandler(context);
-        server.start();
-        System.out.println("Legacy Servlet: http://localhost:8083/status");
-        server.join();
+@SpringBootApplication
+public class WebModernApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(WebModernApplication.class, args);
     }
 }

Umsetzungsplan mit Qualitäts-Gates

  1. Arbeitspaket 1
    Servlet-Mappings und Jetty-Startparameter dokumentieren. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  2. Arbeitspaket 2
    StatusServlet-Verhalten durch Mock-/HTTP-Contract-Tests sichern. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  3. Arbeitspaket 3
    StatusController mit identischem externem Vertrag implementieren. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  4. Arbeitspaket 4
    Fehlerbehandlung, CORS und Header zentral konfigurieren. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  5. Arbeitspaket 5
    Reverse-Proxy- und Context-Path-Verhalten testen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  6. Arbeitspaket 6
    Legacy-Container nach Umschaltung und Beobachtungsphase entfernen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.

Konkreter Test- und Abnahmekatalog

IDEbenePrüfungerforderlicher Nachweis
09-SERVLET-WEB-A01Integration/ContractHealth-/Status-Antwort ist semantisch identisch.Automatisierter Test und CI-Protokoll
09-SERVLET-WEB-A02Integration/ContractContent-Type und Encoding stimmen.Automatisierter Test und CI-Protokoll
09-SERVLET-WEB-A03Integration/ContractUnbekannte Pfade liefern definiertes 404-Verhalten.Automatisierter Test und CI-Protokoll
09-SERVLET-WEB-A04Integration/ContractKeine unbeabsichtigten Management-Endpunkte sind extern.Automatisierter Test und CI-Protokoll
09-SERVLET-WEB-A05Integration/ContractGraceful Shutdown funktioniert.Automatisierter Test und CI-Protokoll
09-SERVLET-WEB-T01UnitFachlogik ohne Container oder externen Dienst testen.Unit-Test
09-SERVLET-WEB-T02RegressionLegacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen.Vergleichsreport
09-SERVLET-WEB-T03NegativeFehlerhafte, leere und grenzwertige Eingaben prüfen.Negativtest
09-SERVLET-WEB-T04OperationsStart, Health, Shutdown und Konfigurationsfehler prüfen.Deployment-/Startprotokoll
09-SERVLET-WEB-F01Fokusweb slice, content type, health and malformed request testsModulspezifischer Testreport

Risikoregister des Moduls

RisikoAuswirkungGegenmaßnahmeGate
container/API migration and endpoint compatibilityNIEDRIGweb slice, content type, health and malformed request testsvor Cutover
Neue Framework-/Library-LaufzeitabhängigkeitenMITTELSBOM, Lizenz-, CVE- und transitive Dependency-Prüfung im Online-Buildvor Release
Legacy-Verhalten war implizit an entfernte Library gekoppeltMITTELContract-/Golden-Master-Tests und gezielte Fehlerpfadevor Abschaltung

Rollback und Koexistenz

Load Balancer oder Reverse Proxy kann wieder auf Jetty zeigen; der Response Contract bleibt dabei unverändert.

Abbruchkriterien

  • Fachlicher Golden-Master weicht ab.
  • Daten-, Nachrichten- oder API-Kompatibilität ist ungeklärt.
  • Fehlerquote, Latenz oder Ressourcenverbrauch überschreiten das vereinbarte Limit.
  • Monitoring oder Rückfallpfad ist nicht funktionsfähig.

Definition of Done

Direkte Arbeitslinks

⌂ Cockpit