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.
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.
| Dimension | Legacy | Modern | Migrationskonsequenz |
|---|---|---|---|
| Programmiermodell | Override | Component, Service | Annotationen und Containerfunktionen werden nur dort eingesetzt, wo sie eine konkrete technische Verantwortung übernehmen. |
| Abhängigkeiten | 1 direkte Dependencies | 2 direkte Dependencies | 2 neu, 1 entfernt; Versionen und transitive Auswirkungen im erfolgreichen Online-Build prüfen. |
| Öffentliche API | 7 erkannte Methoden | 2 erkannte Methoden | Methoden werden nach fachlicher Rolle gemappt; reine Bootstrap- und Framework-Methoden sind kein fachlicher Vertrag. |
| Datenmodell | class GreetingService, class JndiServiceLocator, class LegacyGreetingUseCase, class MemoryContext, interface GreetingPort | class GreetingService, class GreetingUseCase, interface GreetingPort | Feldnamen, IDs, Null-Semantik, Gleichheit und Serialisierungsform werden separat regressionstestet. |
| Fehlerverhalten | Legacy-Exceptions und Rückgabewerte | explizitere Fach-/Framework-Fehlerabbildung | Fehler dürfen nicht nur technisch übersetzt werden; Status, Ursache, Retrybarkeit und Client-Vertrag müssen erhalten oder versioniert werden. |
| Tests | Bestands- und Golden-Master-Tests | Unit-, Slice-, Contract- und Integrationstests | Der Modern-Pfad wird zuerst gegen denselben fachlichen Vektor geprüft und danach um neue technische Risiken ergänzt. |
| Betrieb | Legacy-Start/Lifecycle | modernes Packaging, Health und externe Konfiguration | Startup, Lifecycle, Security Context und Remote-/Containerfehler separat testen. |
| Rollback | Legacy-Artefakt bleibt unverändert | Modern-Artefakt getrennt deploybar | Kein Rollback über Datenverlust: Schema, Nachrichten und verschlüsselte Daten müssen rückwärtslesbar oder durch Dual-Read abgesichert sein. |
runtime lookup failure and hidden dependency
binding failure, constructor contract and context startup
Die Zuordnung ist semantisch: Eine Legacy-Klasse kann in mehrere moderne Rollen zerlegt werden.
| Legacy-Rolle / Datei | Modern-Rolle / Datei | Bedeutung |
|---|---|---|
| JndiServiceLocator.java + MemoryContext.java | Spring Constructor Injection | Namensdienst/Locator entfällt aus dem Use Case. |
| GreetingPort.java | GreetingPort.java | Fachlicher Port bleibt stabil. |
| GreetingService.java | GreetingService.java | Implementierung bleibt fachlich. |
| LegacyGreetingUseCase.java | GreetingUseCase.java | Lookup wird Konstruktorabhängigkeit. |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| GreetingPort.java | interface GreetingPort | — | — | — | 5 |
| GreetingService.java | class GreetingService | Override | String greet(String name) | — | 8 |
| JndiServiceLocator.java | class JndiServiceLocator | — | T resolve(String name, Class<T> type) | Service Locator | 20 |
| LegacyGreetingUseCase.java | class LegacyGreetingUseCase | — | String execute(String name) | Service Locator | 15 |
| MemoryContext.java | class MemoryContext | Override | Object lookup(String name) void bind(String name, Object obj) void rebind(String name, Object obj) void close() | — | 32 |
| Datei | Typ | Annotationen | öffentliche API | Pattern | Zeilen |
|---|---|---|---|---|---|
| GreetingPort.java | interface GreetingPort | — | — | Port | 7 |
| GreetingService.java | class GreetingService | Service | String greet(String name) | — | 10 |
| GreetingUseCase.java | class GreetingUseCase | Component | String execute(String name) | — | 15 |
| Status | Dependency | Version | Scope | Prüfung |
|---|---|---|---|---|
| ENTFERNT | org.junit.jupiter:junit-jupiter | BOM/Parent | test | Legacy-Laufzeit entfällt oder wird durch Plattform/BOM ersetzt. |
| NEU | org.springframework.boot:spring-boot-starter | BOM/Parent | compile | Neue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen. |
| NEU | org.springframework.boot:spring-boot-starter-test | BOM/Parent | test | Neue Laufzeit-/Testabhängigkeit; transitiv, lizenz- und security-seitig prüfen. |
Die folgenden Ausschnitte stammen direkt aus den enthaltenen Projekten. Dadurch ist sichtbar, welche Verantwortung tatsächlich verschoben wurde.
Migrationsbedeutung: Namensdienst/Locator entfällt aus dem Use Case.
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);
}
}
}
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() {
}
}
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);
}
Migrationsbedeutung: Fachlicher Port bleibt stabil.
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);
}
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);
}
--- 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);
}
Migrationsbedeutung: Implementierung bleibt fachlich.
package at.aydin.lab.jndi.legacy;
public final class GreetingService implements GreetingPort {
@Override
public String greet(String name) {
return "Hallo " + name;
}
}
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;
}
}
--- 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;
}
Migrationsbedeutung: Lookup wird Konstruktorabhängigkeit.
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);
}
}
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);
}
}
--- 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);
}
}
| ID | Ebene | Prüfung | erforderlicher Nachweis |
|---|---|---|---|
| 25-JNDI-SERVICE-RESOLUTION-A01 | Integration/Contract | Kein Use Case importiert javax.naming. | Automatisierter Test und CI-Protokoll |
| 25-JNDI-SERVICE-RESOLUTION-A02 | Integration/Contract | Unit-Test erstellt Use Case mit Fake-Port. | Automatisierter Test und CI-Protokoll |
| 25-JNDI-SERVICE-RESOLUTION-A03 | Integration/Contract | Fehlende Implementierung stoppt Context-Start. | Automatisierter Test und CI-Protokoll |
| 25-JNDI-SERVICE-RESOLUTION-A04 | Integration/Contract | JNDI-Name ist nicht mehr Teil des fachlichen Vertrags. | Automatisierter Test und CI-Protokoll |
| 25-JNDI-SERVICE-RESOLUTION-A05 | Integration/Contract | Parallelbetrieb liefert identische Ergebnisse. | Automatisierter Test und CI-Protokoll |
| 25-JNDI-SERVICE-RESOLUTION-T01 | Unit | Fachlogik ohne Container oder externen Dienst testen. | Unit-Test |
| 25-JNDI-SERVICE-RESOLUTION-T02 | Regression | Legacy- und Modern-Ergebnis für denselben Golden-Master-Vektor vergleichen. | Vergleichsreport |
| 25-JNDI-SERVICE-RESOLUTION-T03 | Negative | Fehlerhafte, leere und grenzwertige Eingaben prüfen. | Negativtest |
| 25-JNDI-SERVICE-RESOLUTION-T04 | Operations | Start, Health, Shutdown und Konfigurationsfehler prüfen. | Deployment-/Startprotokoll |
| 25-JNDI-SERVICE-RESOLUTION-T05 | Lifecycle | Container-/Service-Lifecycle, Startup-Fehler und Recovery prüfen. | Runtime-Test |
| 25-JNDI-SERVICE-RESOLUTION-F01 | Fokus | binding failure, constructor contract and context startup | Modulspezifischer Testreport |
| Risiko | Auswirkung | Gegenmaßnahme | Gate |
|---|---|---|---|
| runtime lookup failure and hidden dependency | NIEDRIG | binding failure, constructor contract and context startup | vor Cutover |
| Neue Framework-/Library-Laufzeitabhängigkeiten | MITTEL | SBOM, Lizenz-, CVE- und transitive Dependency-Prüfung im Online-Build | vor Release |
| Legacy-Verhalten war implizit an entfernte Library gekoppelt | MITTEL | Contract-/Golden-Master-Tests und gezielte Fehlerpfade | vor Abschaltung |
| Weniger Modern- als Legacy-Testdateien | MITTEL | Abdeckungsmatrix auf fachliche Gleichwertigkeit prüfen | vor Cutover |
Keine zusätzlichen Ressourcen.
Ein Spring-Bean-Adapter kann den Port weiterhin aus JNDI beziehen, ohne JNDI zurück in den Use Case zu tragen.