CodamAIDocs
Themafertig

Codegenerierung im Build

Der Build holt die Metadaten aus dem Hub, legt sie als YAML-Cache ab und erzeugt daraus 15 Klassen pro Modell. Hier steht der Ablauf und was wann entsteht.

Ausprägungen
online (Metadaten per REST)offline (CODEGEN_OFFLINE, vorhandener Cache)Abruf ganz überspringen (cdms.generator.fetch.skip)Export aus dem Hub als ZIPpro Modelleinmal pro Buildnur beim ersten Mal

Worum es geht

Du schreibst für ein Modell keine Entity, kein DTO, keinen Controller. Das erledigt der Codegenerator bei jedem Build. Er liest die Metadaten aus dem Hub und erzeugt daraus alle Klassen, die ein Modell braucht, von der REST-Schnittstelle bis zur Datenbank.

Der Generator ist ein Annotation-Processor. Das ist ein Programm, das der Java-Compiler während des Kompilierens aufruft und das selbst neue Quelldateien schreiben darf. Die neuen Dateien kompiliert der Compiler im selben Lauf gleich mit.

Der Ablauf

flowchart LR
    H[("Hub")]
    subgraph GS["Phase generate-sources"]
        F["Metadaten holen<br/>CdmsMetadataFetcher"]
        C[["YAML-Cache<br/>system, folders,<br/>models, enumerations"]]
        P["POM pflegen"]
    end
    subgraph CO["Phase compile"]
        G["Generator<br/>(Annotation-Processor)"]
        J["javac"]
        E["Entities anreichern<br/>(Hibernate)"]
    end
    subgraph PK["Phase package"]
        B["Spring-Boot-JAR<br/>oder Native Image"]
    end
    H -- "REST + Token" --> F
    F --> C
    F --> P
    C --> G
    G -- "target/generated-sources" --> J
    J --> E --> B

Den ganzen Ablauf erbst du vom Parent cdms-app-parent. In deiner eigenen pom.xml steht davon nichts. Das Profil wird aktiv, sobald es src/main/java gibt.

  1. 1
    Build→Keycloak
    holt ein Token (Client-Credentials) mit Client-ID und Secret aus der Konfiguration
  2. 2
    Build→Hub
    liest das System und fragt Ordner, Modelle, Felder, Hooks, Endpunkte und Enumerationen seitenweise ab
  3. 3
    Build
    setzt alles zusammen und schreibt den YAML-Cache: system.yaml, folders.yaml, models.yaml, enumerations.yaml
  4. 4
    Build
    pflegt die pom.xml nach den Einstellungen des Systems, siehe Projektrahmen
  5. 5
    Generator
    liest den Cache und erzeugt die Klassen aller Modelle nach target/generated-sources/annotations/
    Der Generator liest nur den Cache, keine Annotationen im Code. Das Java-Paket einer Klasse ist das Basispaket plus der Ordnerpfad aus dem Hub.
  6. 6
    Build
    kompiliert den generierten und den eigenen Code zusammen, reichert die Entities an
    Ergebnis: Fertige Klassen in target/classes, bereit zum Verpacken

Online, offline, überspringen

Woher der Generator seine Metadaten nimmt

Wann: der Normalfall

Der Build holt ein Token und liest die Metadaten frisch aus dem Hub. Danach steht der aktuelle Stand im Cache.

Ergebnis: Code entspricht dem Modell im Hub

Wann: CODEGEN_OFFLINE=true

Kein Token, kein Aufruf beim Hub. Der Build verlangt, dass alle vier Cache-Dateien da sind, und generiert aus ihnen. Fehlt eine, bricht er ab mit dem Hinweis, einmal mit CODEGEN_OFFLINE=false zu bauen. Die POM wird trotzdem gepflegt.

Ergebnis: Code entspricht dem Stand im Cache

Wann: -Dcdms.generator.fetch.skip=true

Der ganze Schritt „Metadaten holen“ entfällt, also auch die POM-Pflege. Der Generator braucht dann nur noch models.yaml im Cache. So baut ein frischer Projektrahmen beim ersten Mal, ohne Secret.

Ergebnis: Code entspricht dem Stand im Cache

Wann: du willst den Cache ohne Build erzeugen

GET /api/rest/cms/system/{id}/export liefert ein ZIP mit den vier YAML-Dateien. Entpackt ins Cache-Verzeichnis, baust du danach offline. Auch der Projektrahmen enthält diesen Stand schon unter .cache/.

Die Konfiguration

Der Build braucht ein paar Angaben. Er sucht sie in dieser Reihenfolge und nimmt den ersten Treffer:

Wo der Build nach einer Einstellung sucht
  1. Build
    Umgebungsvariable
    CODEGEN_CLIENT_SECRET
  2. Build
    abgeleitete Umgebungsvariable
    CDMS_GENERATOR_CLIENT_SECRET
  3. Build
    JVM-Property
    -Dcdms.generator.clientSecret=…
  4. Build
    Datei im Projekt
    clientSecret=… in cdms-generator.properties
  5. sonst der Standardwert
EinstellungUmgebungsvariableStandard
Issuer des Generator-Tokens, mit /realms/<realm>CODEGEN_KEYCLOAK_ISSUER– (Pflicht)
Client-ID / SecretCODEGEN_CLIENT_ID, CODEGEN_CLIENT_SECRET– (Pflicht)
Hub-AdresseCODEGEN_CDMS_URLim Projektrahmen gesetzt
System-IDCODEGEN_SYSTEM_ID– (Pflicht)
Cache-VerzeichnisCODEGEN_CACHE_DIRtarget/cache (im Projektrahmen .cache)
offlineCODEGEN_OFFLINEfalse
BasispaketCODEGEN_BASE_PACKAGEcom.codamai.cdms
POM pflegenCODEGEN_POM_GENERATEtrue

Die Pflichtangaben braucht nur der Online-Weg. Offline reichen Cache-Verzeichnis und Basispaket.

Was pro Modell entsteht

Für ein normales Modell entstehen 15 Klassen, für ein abstraktes Modell 10. Der Name ist immer der Modellname plus eine Endung. Für ein Modell Order:

Die 15 Klassen eines Modells nach Schicht
REST-Layer
Eingang
  • OrderApi – der REST-Controller
  • OrderCreatePayload
  • OrderUpdatePayload
  • OrderPayload2DtoMapper
System-Layer
Fachlogik
  • OrderSystem – Ablauf von Lesen und Schreiben
  • OrderDto
  • OrderMetaService – die Metadaten
  • OrderDto2EntityMapper
  • OrderMapperService
Autorisierung
Rechte
  • OrderAuthorizationLayer – prüft Rollen
  • Order{Feld}Filter – je Zugriffsfilter einer
Persistenz
Datenbank
  • OrderEntity
  • OrderDatabase
  • OrderTupleMapperService
  • OrderEntity2DtoMapper
  • OrderMap2EntityMapper
FallKlassen
normales Modell15, dazu eine je Zugriffsfilter
Singleton15. Api und System sind die Singleton-Varianten ohne id im Pfad
abstraktes Modell10. Es fehlen System, MapperService, AuthorizationLayer, Database, TupleMapperService, weil es keine eigenen Objekte hat. Seine Api sucht über alle Untertypen
Enumerationein Java-enum mit den Werten

Wie die Gestalten Payload, DTO, Entity und Meta zusammenspielen, zeigt Ein Modell, vier Gestalten.

Was einmal pro Build und einmal überhaupt entsteht

Wann entsteht welche Datei?
DateiOrtWann
Klassen je Modell und Enumerationtarget/generated-sources/bei jedem Build neu
RoleRegistryService, EntityRegistryService, JpaNativeHints, JacksonNativeHintstarget/generated-sources/einmal pro Build, über alle Modelle
Start.java, OpenApiConfiguration, NativeHintsConfigurationsrc/main/java/nur wenn die Datei fehlt, danach nie wieder
pom.xml, Bereich zwischen den cdms-managed-MarkernProjektwurzelbei jedem Online- oder Offline-Build nachgezogen

RoleRegistryService kennt alle Rollen und Attribute aller Modelle und stellt sie CIAS als Deklaration bereit, siehe Rollen deklarieren. EntityRegistryService kennt alle Entities. Die beiden …NativeHints sagen dem Native-Image-Compiler, welche Klassen er zur Laufzeit per Reflection braucht.

Fallen

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-parent – cdms-app-parent/pom.xml (Profil cdms-application: exec-maven-plugin, maven-compiler-plugin, hibernate-enhance, spring-boot, native)
  • CDMS/cdms-generator – remote/CdmsMetadataFetcher, KeycloakTokenClient, CdmsApiClient, CdmsCacheWriter, CdmsGeneratorConfig
  • CDMS/cdms-generator – generator/CdmsProcessor, loader/CdmsYamlLoader, AbstractProcessor, api/*, system/*, security/*, persistence/*, enumeration/EnumProcessor
  • CDMS/cdms-generator – CdmsPomProcessor, CdmsPomMerger
  • hub-backend – services/SystemExportApi, SystemExportService
  • documentation/10-cdms-grundlagen/03-generierung.md
Suchen