Modul 25 · Migration Deep Dive · Stufe 5 Enterprise Runtime

JNDI und Service-Auflösung

InitialContext und Service Locator wird nachvollziehbar in Constructor Injection und explizite Ports überführt. Die Akte zeigt nicht nur das Ziel, sondern die tatsächlichen Dateien, APIs, Dependencies, Codebelege, Tests, Risiken und Cutover-Schritte.

Service LocatorDependency Injection
5 → 3Produktionsdateien
80 → 32Java-Zeilen
2 → 1Testdateien
1 / 2Dependencies entfernt / neu
NIEDRIGRisiko · Score 1

Was sich konkret ändert

DimensionLegacyModernMigrationskonsequenz
ProgrammiermodellOverrideComponent, ServiceAnnotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen.
Abhängigkeiten1 direkte Dependencies2 direkte Dependencies2 neu, 1 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen.
Öffentliche API7 erkannte Methoden2 erkannte MethodenMethoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag.
Datenmodellclass GreetingService, class JndiServiceLocator, class LegacyGreetingUseCase, class MemoryContext, interface GreetingPortclass GreetingService, class GreetingUseCase, interface GreetingPortFeldnamen, 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 KonfigurationStartup, Lifecycle, Security Context und Remote-/Containerfehler separat testen.
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

  • Fachcode kennt keine Namensdienst-API.
  • Abhängigkeiten sind im Konstruktor vollständig sichtbar.
  • Fehlende Bindings werden beim Start statt im ersten Request erkannt.

Hauptrisiko und Testfokus

runtime lookup failure and hidden dependency

binding failure, constructor contract and context startup

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
JndiServiceLocator.java + MemoryContext.javaSpring Constructor InjectionNamensdienst/Locator entfällt aus dem Use Case.
GreetingPort.javaGreetingPort.javaFachlicher Port bleibt stabil.
GreetingService.javaGreetingService.javaImplementierung bleibt fachlich.
LegacyGreetingUseCase.javaGreetingUseCase.javaLookup wird Konstruktorabhängigkeit.

Legacy-Quellinventar

DateiTypAnnotationenöffentliche APIPatternZeilen
GreetingPort.javainterface GreetingPort5
GreetingService.javaclass GreetingServiceOverrideString greet(String name)8
JndiServiceLocator.javaclass JndiServiceLocatorT resolve(String name, Class<T> type)Service Locator20
LegacyGreetingUseCase.javaclass LegacyGreetingUseCaseString execute(String name)Service Locator15
MemoryContext.javaclass MemoryContextOverrideObject lookup(String name)
void bind(String name, Object obj)
void rebind(String name, Object obj)
void close()
32

Dependency- und Laufzeitmigration

StatusDependencyVersionScopePrüfung
ENTFERNTorg.junit.jupiter:junit-jupiterBOM/ParenttestLegacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt.
NEUorg.springframework.boot:spring-boot-starterBOM/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.
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
JndiServiceLocator.java + MemoryContext.java
Modern
Spring Constructor Injection

Migrationsbedeutung: Namensdienst/Locator entfällt aus dem Use Case.

Legacy-Code

projects/25-jndi-service-resolution/legacy/src/main/java/at/aydin/lab/jndi/legacy/JndiServiceLocator.java

package at.aydin.lab.jndi.legacy;

import javax.naming.*;

// Design Pattern: Service Locator
// Zweck: Versteckt JNDI-Lookups, behält aber die Laufzeitabhängigkeit von String-Namen.
public final class JndiServiceLocator {
    private final Context context;
    public JndiServiceLocator(Context context) {
        this.context = context;
    }

    public <T> T resolve(String name, Class<T> type) {
        try {
            return type.cast(context.lookup(name));
        } catch (NamingException ex) {
            throw new IllegalStateException(ex);
        }
    }
}

Legacy-Code

projects/25-jndi-service-resolution/legacy/src/main/java/at/aydin/lab/jndi/legacy/MemoryContext.java

package at.aydin.lab.jndi.legacy;

import javax.naming.*;
import java.util.*;

public final class MemoryContext extends InitialContext {
    private static final Map<String, Object> VALUES = new HashMap<>();
    public MemoryContext() throws NamingException {
        super(true);
    }

    @Override
    public Object lookup(String name) throws NamingException {
        Object value = VALUES.get(name);
        if (value == null) throw new NameNotFoundException(name);
        return value;
    }

    @Override
    public void bind(String name, Object obj) throws NamingException {
        if (VALUES.putIfAbsent(name, obj) != null) throw new NameAlreadyBoundException(name);
    }

    @Override
    public void rebind(String name, Object obj) {
        VALUES.put(name, obj);
    }

    @Override
    public void close() {
    }
}

Modern-Code

projects/25-jndi-service-resolution/modern/src/main/java/at/aydin/lab/jndi/modern/GreetingPort.java

package at.aydin.lab.jndi.modern;

// Design Pattern: Port
// Zweck: Fachcode kennt keinen JNDI-Namen und keine NamingException.
public interface GreetingPort {
    String greet(String name);
}
Legacy
GreetingPort.java
Modern
GreetingPort.java

Migrationsbedeutung: Fachlicher Port bleibt stabil.

Legacy-Code

projects/25-jndi-service-resolution/legacy/src/main/java/at/aydin/lab/jndi/legacy/GreetingPort.java

package at.aydin.lab.jndi.legacy;

public interface GreetingPort {
    String greet(String name);
}

Modern-Code

projects/25-jndi-service-resolution/modern/src/main/java/at/aydin/lab/jndi/modern/GreetingPort.java

package at.aydin.lab.jndi.modern;

// Design Pattern: Port
// Zweck: Fachcode kennt keinen JNDI-Namen und keine NamingException.
public interface GreetingPort {
    String greet(String name);
}
Technischen Unified Diff öffnen
--- GreetingPort.java
+++ GreetingPort.java
@@ -1,5 +1,7 @@
-package at.aydin.lab.jndi.legacy;
+package at.aydin.lab.jndi.modern;
 
+// Design Pattern: Port
+// Zweck: Fachcode kennt keinen JNDI-Namen und keine NamingException.
 public interface GreetingPort {
     String greet(String name);
 }
Legacy
GreetingService.java
Modern
GreetingService.java

Migrationsbedeutung: Implementierung bleibt fachlich.

Legacy-Code

projects/25-jndi-service-resolution/legacy/src/main/java/at/aydin/lab/jndi/legacy/GreetingService.java

package at.aydin.lab.jndi.legacy;

public final class GreetingService implements GreetingPort {
    @Override
    public String greet(String name) {
        return "Hallo " + name;
    }
}

Modern-Code

projects/25-jndi-service-resolution/modern/src/main/java/at/aydin/lab/jndi/modern/GreetingService.java

package at.aydin.lab.jndi.modern;

import org.springframework.stereotype.Service;

@Service
public class GreetingService implements GreetingPort {
    public String greet(String name) {
        return "Hallo " + name;
    }
}
Technischen Unified Diff öffnen
--- GreetingService.java
+++ GreetingService.java
@@ -1,7 +1,9 @@
-package at.aydin.lab.jndi.legacy;
+package at.aydin.lab.jndi.modern;
 
-public final class GreetingService implements GreetingPort {
-    @Override
+import org.springframework.stereotype.Service;
+
+@Service
+public class GreetingService implements GreetingPort {
     public String greet(String name) {
         return "Hallo " + name;
     }
Legacy
LegacyGreetingUseCase.java
Modern
GreetingUseCase.java

Migrationsbedeutung: Lookup wird Konstruktorabhängigkeit.

Legacy-Code

projects/25-jndi-service-resolution/legacy/src/main/java/at/aydin/lab/jndi/legacy/LegacyGreetingUseCase.java

package at.aydin.lab.jndi.legacy;

// Design Pattern: Service Locator
// Zweck: Zeigt die versteckte Laufzeitabhängigkeit auf JNDI-Name und Naming-Infrastruktur.
public final class LegacyGreetingUseCase {
    private final JndiServiceLocator locator;
    public LegacyGreetingUseCase(JndiServiceLocator locator) {
        this.locator = locator;
    }

    public String execute(String name) {
        GreetingPort port = locator.resolve("service/greeting", GreetingPort.class);
        return port.greet(name);
    }
}

Modern-Code

projects/25-jndi-service-resolution/modern/src/main/java/at/aydin/lab/jndi/modern/GreetingUseCase.java

package at.aydin.lab.jndi.modern;

import org.springframework.stereotype.Component;

@Component
public class GreetingUseCase {
    private final GreetingPort port;
    public GreetingUseCase(GreetingPort port) {
        this.port = port;
    }

    public String execute(String name) {
        return port.greet(name);
    }
}
Technischen Unified Diff öffnen
--- LegacyGreetingUseCase.java
+++ GreetingUseCase.java
@@ -1,15 +1,15 @@
-package at.aydin.lab.jndi.legacy;
+package at.aydin.lab.jndi.modern;
 
-// Design Pattern: Service Locator
-// Zweck: Zeigt die versteckte Laufzeitabhängigkeit auf JNDI-Name und Naming-Infrastruktur.
-public final class LegacyGreetingUseCase {
-    private final JndiServiceLocator locator;
-    public LegacyGreetingUseCase(JndiServiceLocator locator) {
-        this.locator = locator;
+import org.springframework.stereotype.Component;
+
+@Component
+public class GreetingUseCase {
+    private final GreetingPort port;
+    public GreetingUseCase(GreetingPort port) {
+        this.port = port;
     }
 
     public String execute(String name) {
-        GreetingPort port = locator.resolve("service/greeting", GreetingPort.class);
         return port.greet(name);
     }
 }

Umsetzungsplan mit Qualitäts-Gates

  1. Arbeitspaket 1
    Alle JNDI-Namen und Lookup-Stellen inventarisieren. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  2. Arbeitspaket 2
    GreetingPort als fachlichen Vertrag festlegen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  3. Arbeitspaket 3
    LegacyGreetingUseCase auf Konstruktorinjektion vorbereiten. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  4. Arbeitspaket 4
    GreetingUseCase und GreetingService als explizite Komponenten verdrahten. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  5. Arbeitspaket 5
    JNDI-Adapter nur an der Composition Root für Übergang behalten. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.
  6. Arbeitspaket 6
    MemoryContext/JndiServiceLocator nach Aufrufermigration entfernen. Nachweis: Commit/PR, automatisierter Test und aktualisierte Betriebsdokumentation.

Konkreter Test- und Abnahmekatalog

IDEbenePrüfungerforderlicher Nachweis
25-JNDI-SERVICE-RESOLUTION-A01Integration/ContractKein Use Case importiert javax.naming.Automatisierter Test und CI-Protokoll
25-JNDI-SERVICE-RESOLUTION-A02Integration/ContractUnit-Test erstellt Use Case mit Fake-Port.Automatisierter Test und CI-Protokoll
25-JNDI-SERVICE-RESOLUTION-A03Integration/ContractFehlende Implementierung stoppt Context-Start.Automatisierter Test und CI-Protokoll
25-JNDI-SERVICE-RESOLUTION-A04Integration/ContractJNDI-Name ist nicht mehr Teil des fachlichen Vertrags.Automatisierter Test und CI-Protokoll
25-JNDI-SERVICE-RESOLUTION-A05Integration/ContractParallelbetrieb liefert identische Ergebnisse.Automatisierter Test und CI-Protokoll
25-JNDI-SERVICE-RESOLUTION-T01UnitFachlogik ohne Container oder externen Dienst testen.Unit-Test
25-JNDI-SERVICE-RESOLUTION-T02RegressionLegacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen.Vergleichsreport
25-JNDI-SERVICE-RESOLUTION-T03NegativeFehlerhafte, leere und grenzwertige Eingaben prüfen.Negativtest
25-JNDI-SERVICE-RESOLUTION-T04OperationsStart, Health, Shutdown und Konfigurationsfehler prüfen.Deployment-/Startprotokoll
25-JNDI-SERVICE-RESOLUTION-T05LifecycleContainer-/Service-Lifecycle, Startup-Fehler und Recovery prüfen.Runtime-Test
25-JNDI-SERVICE-RESOLUTION-F01Fokusbinding failure, constructor contract and context startupModulspezifischer Testreport

Risikoregister des Moduls

RisikoAuswirkungGegenmaßnahmeGate
runtime lookup failure and hidden dependencyNIEDRIGbinding failure, constructor contract and context startupvor 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
Weniger Modern- als Legacy-TestdateienMITTELAbdeckungsmatrix auf fachliche Gleichwertigkeit prüfenvor Cutover

Konfiguration und Ressourcen

Keine zusätzlichen Ressourcen.

Rollback und Koexistenz

Ein Spring-Bean-Adapter kann den Port weiterhin aus JNDI beziehen, ohne JNDI zurück in den Use Case zu tragen.

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