# JNDI wirklich verstehen

Dieses Dokument ist bewusst eigenstaendig und langsamer als
`docs/10-ejb-konzepte/07-jndi-und-packaging.md` - dort wird JNDI vorausgesetzt und im
Zusammenspiel mit Deployment-Deskriptoren behandelt. Hier geht es nur um die eine Frage:
**Was ist JNDI eigentlich, und was passiert bei einem Lookup wirklich?**

## Die Analogie: ein Telefonbuch fuer Objekte

Stell dir vor, dein Code braucht eine Datenbankverbindung. Zwei Moeglichkeiten:

1. **Direkt:** Der Code enthaelt die JDBC-URL, Benutzername und Passwort fest im Quelltext
   oder in einer eigenen Konfigurationsdatei, die er selbst einliest.
2. **Ueber ein Telefonbuch:** Der Code fragt einen zentralen Dienst: "Gib mir das Ding, das
   unter dem Namen `jdbc/novaris/policyDS` eingetragen ist." Der Dienst gibt eine fertig
   konfigurierte, einsatzbereite Datenbankverbindung zurueck - der Code selbst weiss nicht
   und muss nicht wissen, welcher Server, welcher Port, welches Passwort dahintersteckt.

**JNDI (Java Naming and Directory Interface) ist genau dieses Telefonbuch** - ein
Java-Standard-API, um Objekte anhand eines Namens (statt einer direkten Referenz) zu finden.
Der Anwendungsserver (WebSphere, Liberty, ...) fuehrt dieses Telefonbuch und traegt beim
Start Eintraege ein: "der Name `jdbc/novaris/policyDS` zeigt auf diese konkrete,
konfigurierte `DataSource`."

## Warum das ueberhaupt lohnt

Der Witz an diesem Umweg: **derselbe Code laeuft unveraendert in Test- und
Produktivumgebung**, obwohl beide Umgebungen auf physisch unterschiedliche Datenbanken
zeigen. Nur der Telefonbucheintrag selbst (die Serverkonfiguration) unterscheidet sich -
nicht eine einzige Zeile Java-Code. Das ist derselbe Grundgedanke wie eine `.env`-Datei oder
Umgebungsvariablen in moderneren Systemen, nur als eingebauter Java-EE-Standardmechanismus,
schon lange bevor Container/Kubernetes/12-Factor-Apps diese Idee popularisiert haben.

## Was konkret passiert bei einem Lookup

```java
InitialContext context = new InitialContext();
Object result = context.lookup("jdbc/novaris/policyDS");
DataSource dataSource = (DataSource) result;
```

Schritt fuer Schritt:

1. `new InitialContext()` verbindet sich mit dem "Telefonbuch" des Anwendungsservers, in dem
   der aktuelle Code gerade laeuft (implizit - der Server stellt seine eigene JNDI-
   Implementierung automatisch bereit, kein zusaetzliches Setup im Code noetig).
2. `context.lookup("jdbc/novaris/policyDS")` fragt: "was ist unter diesem Namen
   eingetragen?"
3. Der Server schaut in seiner eigenen Konfiguration nach (bei Liberty: `server.xml`, siehe
   unten) und liefert das dort konfigurierte Objekt zurueck.
4. Das Ergebnis kommt als generisches `Object` zurueck (JNDI kennt den konkreten Typ nicht) -
   deshalb der Cast auf `DataSource`.

In diesem Projekt siehst du diesen Ablauf konkret in
`JndiLookupHelper.lookup(...)` (`novaris-common-legacy`):

```java
InitialContext context = new InitialContext();
Object bound = context.lookup(jndiName);
if (!expectedType.isInstance(bound)) { ... }
return (T) bound;
```

## Der bevorzugte Weg: Dependency Injection statt manuellem Lookup

Ein manueller Lookup wie oben ist in EJB-/Java-EE-Code die **Ausnahme**, nicht die Regel.
Der Normalfall ist Dependency Injection - der Container macht denselben Lookup fuer dich,
bevor deine Bean ueberhaupt benutzt wird:

```java
@Resource(lookup = "jdbc/novaris/policyDS")
private DataSource policyDataSource;
```

Das ist **exakt derselbe JNDI-Lookup**, nur macht ihn der Container automatisch beim
Erzeugen der Bean-Instanz, und er kann Fehler (Name existiert nicht) schon beim
Deployment melden, statt erst beim ersten tatsaechlichen Aufruf zur Laufzeit. Siehe z.B.
`AuditInterceptor`:

```java
@Resource(lookup = JndiNames.ORACLE_POLICY_DATASOURCE)
private DataSource policyDataSource;
```

**Merksatz:** `@Resource`/`@EJB`/`@PersistenceContext` sind nichts anderes als
"mach fuer mich einen JNDI-Lookup und reich mir das Ergebnis". Wenn du das einmal verinnerlicht
hast, ist Dependency Injection in Java EE keine Magie mehr, sondern nur eine Abkuerzung fuer
etwas, das du auch selbst schreiben koenntest (und in `JndiLookupHelper` teilweise auch tust).

## Wo der Name tatsaechlich "eingetragen" wird

Der JNDI-Name im Code (`jdbc/novaris/policyDS`) ist nur die eine Haelfte. Die andere Haelfte
- WAS unter diesem Namen tatsaechlich haengt - ist reine **Serverkonfiguration**, niemals
Java-Code. In diesem Projekt: `novaris-policy-core/src/main/liberty/config/server.xml`:

```xml
<dataSource jndiName="jdbc/novaris/policyDS" id="policyDS">
    <jdbcDriver libraryRef="oracleJdbcLib"/>
    <properties.oracle URL="${env.NOVARIS_ORACLE_JDBC_URL}" .../>
</dataSource>
```

Diese Zeile ist der eigentliche "Telefonbucheintrag" - sie sagt dem Server: "wenn jemand
nach `jdbc/novaris/policyDS` fragt, gib ihm eine `DataSource`, die mit diesen konkreten
Verbindungsdaten konfiguriert ist." Auf einer echten WebSphere-traditional-Installation
waere die aequivalente Stelle nicht eine Datei, sondern ein Eintrag in der
**Administrationskonsole** (oder ein `wsadmin`-Skript) - inhaltlich dieselbe Idee, andere
Werkzeuge.

**Das ist der Kern, den viele beim Erlernen von JNDI verpassen:** der JNDI-Name im
Java-Code und die Ressourcen-Definition auf dem Server sind zwei getrennte Dinge, die nur
durch den *gemeinsamen String* (`jdbc/novaris/policyDS`) miteinander verbunden sind. Es gibt
keine Typpruefung, keine Autovervollstaendigung, keine Compile-Zeit-Garantie zwischen den
beiden - ein Tippfehler auf einer der beiden Seiten fuehrt zu einem erst zur Laufzeit
sichtbaren Fehler. Genau deshalb existiert `JndiNames` als zentrale Konstanten-Klasse (siehe
deren Javadoc) - sie macht wenigstens die Java-Code-Seite tippfehlersicher.

## Die verschiedenen Namensraeume - eine Uebersicht

JNDI-Namen in Java EE fallen in mehrere Kategorien, die leicht durcheinandergehen:

| Namensform | Beispiel | Bedeutung |
|---|---|---|
| **Ressourcen-JNDI-Name** (Server-global) | `jdbc/novaris/policyDS` | Ein vom Server verwalteter, fest konfigurierter Name fuer eine technische Ressource (Datenquelle, JMS-Ziel). WebSphere-Konvention: `<typ>/<bereich>`. |
| **Portabler globaler EJB-Name** (EJB 3.1+) | `java:global/novaris-legacy/novaris-policy-core/PolicyManagementBean!...Local` | Standardisiertes Schema fuer Business-Interfaces von EJBs, serverunabhaengig. Siehe `JndiNames.POLICY_MANAGEMENT_LOCAL_EXAMPLE`. |
| **Komponentenbezogener Name** | `java:comp/env/jdbc/myDS` | Eine *lokale, komponenteneigene Umleitung* auf eine globale Ressource - siehe naechster Abschnitt. In diesem Projekt nicht direkt genutzt (wir binden meist direkt an globale Namen), aber in vielen aelteren Java-EE-Projekten der Normalfall. |

## Exkurs: Was ist `java:comp/env/...` und warum gibt es das?

Das ist der Teil, der Einsteiger am meisten verwirrt. `java:comp/env` ("Environment Naming
Context", kurz ENC) ist ein **zusaetzlicher Indirektionslayer**: statt im Code direkt den
globalen, serverweiten Ressourcennamen zu verwenden, definiert eine Komponente (ein Servlet,
eine EJB) einen *eigenen, lokalen* Namen in ihrem Deployment-Deskriptor
(`<resource-ref>`-Eintrag) und bindet diesen lokalen Namen erst dort an die tatsaechliche
globale Ressource.

**Warum der Umweg?** Portabilitaet zwischen mehreren Instanzen derselben Anwendung. Zwei
Beispiele:

- Zwei Kopien derselben Anwendung (Mandant A, Mandant B) sollen denselben Code, aber
  unterschiedliche Datenbanken verwenden - jede Kopie bindet ihren *eigenen*
  `java:comp/env/jdbc/policyDS` an eine andere globale Datenquelle, ohne dass der Code
  irgendetwas davon wissen muss.
- Eine wiederverwendbare Bibliotheks-Bean kann einen generischen, komponenteneigenen Namen
  verwenden, den jede Anwendung, die sie einbindet, individuell auf ihre eigene
  Ressource ummappen kann.

Dieses Projekt verzichtet bewusst auf diesen zusaetzlichen Indirektionslayer und bindet
direkt an globale Ressourcennamen (`@Resource(lookup = "jdbc/novaris/policyDS")` statt
`@Resource(name = "jdbc/policyDS")` mit separatem `<resource-ref>`-Mapping) - einfacher zu
verstehen fuer den EJB-Lernstoff dieses Projekts, auch wenn grosse, mandantenfaehige
Alt-Systeme haeufig die `java:comp/env`-Variante nutzen. `docs/10-ejb-konzepte/07-jndi-und-packaging.md`
geht auf das portable `java:global/...`-Schema als weitere Variante ein.

## Ein haeufiger Stolperstein: `NamingException` bei einem korrekt aussehenden Namen

Wenn ein JNDI-Lookup mit `NameNotFoundException` fehlschlaegt, obwohl der Name im Code
korrekt aussieht, liegt das fast immer an einem von drei Gruenden:

1. **Der Server hat die Ressource gar nicht gebunden** - der `server.xml`-Eintrag (oder das
   Admin-Console-Aequivalent) fehlt oder wurde nicht neu geladen.
2. **Falscher Namensraum-Praefix** - z.B. `java:comp/env/jdbc/policyDS` verwendet, obwohl
   die Ressource nur global unter `jdbc/policyDS` (ohne ENC-Indirektion) gebunden ist.
3. **Falscher Scope** - ein `java:module/...`-Name ist nur innerhalb desselben Moduls
   sichtbar, ein `java:app/...`-Name nur innerhalb derselben Anwendung; ein Zugriff aus
   einem anderen Modul/einer anderen Anwendung braucht zwingend den globalen
   `java:global/...`-Namen.

## Zusammenfassung in einem Satz

JNDI ist ein vom Anwendungsserver gefuehrtes Telefonbuch, das einen String-Namen im
Java-Code mit einem konkret konfigurierten Objekt auf dem Server verbindet - `@Resource`/
`@EJB`/`@PersistenceContext` sind nur bequeme, containergesteuerte Abkuerzungen fuer genau
diesen Lookup, den man (wie `JndiLookupHelper` zeigt) auch vollstaendig von Hand
nachbauen koennte.
