Java
Starte im Assistenten mit einer Java-Vorlage oder wähl Java-Anwendung für ein leeres Projekt oder
ein Repository, oder nimm Java im Bereich Laufzeit zu einem bestehenden Projekt dazu. Der Container
envoryx-<projekt>-java läuft mit ghcr.io/envoryx/envoryx-java:<version>. Das ist Eclipse Temurin
(<version>-jdk-noble) plus Maven, Gradle, git und socat. Er läuft als PUID:PGID, mit deinem Projekt
unter /var/www/html und einem Home-Verzeichnis unter /home/envoryx.
Das Repository von Maven liegt im Paket-Cache des Projekts; teilst du
den Cache, wird eine Abhängigkeit für alle Projekte nur einmal geladen. Gradle hält sein Home
(Abhängigkeiten, Wrapper-Distributionen) in ~/.gradle im Projekt-Home, auch bei geteiltem Paket-Cache:
Gradle reicht seine Cache-Sperren über eine Nachricht an localhost weiter, die keinen anderen Container
erreicht, und mit geteiltem Gradle-Home wartet Gradle in einem Projekt auf das eines anderen, bis es
aufgibt.
Versionen
Abschnitt betitelt „Versionen“Nur die LTS-Releases: Standard ist 25, außerdem gibt es 21 und 17.
Steht in pom.xml oder build.gradle(.kts) ein höheres Java-Level als die Version, mit der das
Projekt läuft, warnen Projektliste und Projektseite und nennen beide Versionen. Das
Spring-Boot-Template zielt zum Beispiel auf Java 25; stellst du so ein Projekt auf 21 um, bekommst
du diese Warnung statt später eines UnsupportedClassVersionError. Envoryx liest die Dateien, es
wertet sie nicht aus: Ein Level aus einer Variable oder einer Parent-POM ergibt keine Warnung.
Hast du das Level danach passend gesenkt, wartet noch eine Falle: Die Klassen in target/ oder
build/ hat das neuere JDK kompiliert, und weil sich die Quellen nicht geändert haben, kompilieren
weder Maven noch die Dev-Modi sie neu. Deshalb schaut Envoryx vor dem Server und vor einem Testlauf
auf die kompilierten Klassen, und sind sie für ein neueres Java als das im Container, löscht es
target/classes, target/test-classes oder build/classes, mit einer Zeile im Log. Der nächste
Build kompiliert dann alles für die richtige Version.
Maven oder Gradle
Abschnitt betitelt „Maven oder Gradle“Das entscheidest nicht du, sondern das Projekt. Eine pom.xml heißt Maven, sonst heißt eine
build.gradle oder build.gradle.kts Gradle. Bringt das Projekt seinen Wrapper mit (mvnw, gradlew),
gewinnt der Wrapper gegen Maven oder Gradle aus dem Image. Er läuft über sh, deshalb funktioniert
auch ein Wrapper, der in einem Zip oder einem Windows-Checkout sein Ausführungsrecht verloren hat.
Der Server
Abschnitt betitelt „Der Server“Schalte Java-Server ausführen ein und wähl eine Vorlage. Der Server wird zum Hauptprozess des Containers, startet automatisch neu und bekommt einen eigenen Host-Port. Bevor er startet, wartet er auf die Build-Datei. Schaltest du den Server aus und wieder ein, behält er diesen Port, ein Lesezeichen darauf bleibt also gültig.


| Vorlage | Entwicklung | Produktion | Port |
|---|---|---|---|
| Spring Boot | mvn spring-boot:run oder gradle bootRun |
einmal bauen, dann das Jar starten | 8080 |
| Quarkus | mvn quarkus:dev oder gradle quarkusDev |
einmal bauen, dann quarkus-run.jar |
8080 |
| Andere (bauen, dann java -jar) | - | einmal bauen, dann das Jar starten | 8080 |
In der Entwicklung kompiliert Quarkus bei der nächsten Anfrage nach einer Änderung neu. Die DevTools
von Spring Boot starten die Anwendung neu, wenn sich kompilierte Klassen ändern. Nach dem Bearbeiten
führst du also mvn compile im Java-Terminal aus oder lässt deine IDE bauen. Eine neue Abhängigkeit
ist etwas anderes: Die DevTools starten immer mit dem Klassenpfad neu, mit dem sie gestartet sind. Hast du
etwas in die pom.xml oder build.gradle aufgenommen, startest du das Projekt neu.
Der Produktionsmodus baut das Projekt einmal ohne die Tests (mvn package, gradle build) und startet
dann das Jar: bei Quarkus target/quarkus-app/quarkus-run.jar, sonst das neueste Jar in target/ oder
build/libs/, ohne die -plain-, -sources-, -javadoc- und -tests-Jars. Die dritte Vorlage für
Micronaut, Javalin, Helidon und Co. hat keinen Entwicklungsmodus. Sie baut und startet das Jar, das du
angibst (etwa build/libs/app.jar), oder das neueste.
Der Port steht in SERVER_PORT, QUARKUS_HTTP_PORT, MICRONAUT_SERVER_PORT und PORT, und die
passenden Host-Variablen stehen auf 0.0.0.0. Die gängigen Frameworks lauschen damit ohne weitere
Konfiguration dort, wo der Proxy sie erreicht.
Datenbank
Abschnitt betitelt „Datenbank“Für PostgreSQL, MySQL und MariaDB setzt Envoryx SPRING_DATASOURCE_URL, _USERNAME und _PASSWORD
für Spring Boot und QUARKUS_DATASOURCE_DB_KIND, _JDBC_URL, _USERNAME und _PASSWORD für Quarkus,
dazu eine einfache JDBC_URL für alles andere. Eine zusätzliche Datenbank namens analytics kommt als
ANALYTICS_JDBC_URL an. Auch MongoDB, Redis und Mailpit bekommen ihre Spring- und Quarkus-Variablen
(SPRING_DATA_MONGODB_URI, SPRING_DATA_REDIS_URL, QUARKUS_REDIS_HOSTS, SPRING_MAIL_HOST,
QUARKUS_MAILER_HOST und so weiter).
Die Dev Services von Quarkus sind abgeschaltet (QUARKUS_DEVSERVICES_ENABLED=false). Deine Anwendung
nutzt die Datenbank des Projekts und keinen Container, den Quarkus selbst starten würde.
Wo die Anwendung antwortet
Abschnitt betitelt „Wo die Anwendung antwortet“Ohne PHP und ohne Python-, Go- oder Ruby-Server ist der Java-Server die Anwendung:
https://<projekt>.<basis> und jede weitere Domain gehen direkt zu ihm, und Direktzugriff ist der
Java-Host-Port. Neben einem der anderen hat er nur seinen eigenen Host-Port.
Speicher
Abschnitt betitelt „Speicher“Eine JVM legt ihren Heap beim Start fest: Mit einem Speicherlimit
am Projekt ein Viertel davon, ohne Limit ein Viertel des Host-Speichers. Daraus folgen zwei Dinge. Gib
einem Java-Projekt Platz, etwa 2 GiB: In der Entwicklung laufen Build-Tool und Anwendung jeweils in
einer eigenen JVM, Gradle legt noch seinen Daemon dazu (einen zweiten, wenn du die Tests neben
bootRun laufen lässt), und mit 1 GiB reißt ein Testlauf den Dev-Server mit. Und änderst du das Limit
eines laufenden Projekts, setzt Envoryx es sofort um, die JVMs behalten aber den Heap, mit dem sie
gestartet sind. Ein niedrigeres Limit endet dann damit, dass der Kernel den Prozess beendet, statt mit
einem OutOfMemoryError von Java. Starte das Projekt nach einer Änderung am Limit also neu. Willst du
den Heap selbst festlegen, erreicht JAVA_TOOL_OPTIONS=-XX:MaxRAMPercentage=50 im Bereich Umgebung
jede JVM im Container.
Templates
Abschnitt betitelt „Templates“Spring Boot kommt von start.spring.io mit Spring Web, Actuator und DevTools, dazu JPA und der
Treiber, wenn das Projekt eine Datenbank hat. Quarkus REST kommt von code.quarkus.io mit REST
(Jackson) und SmallRye Health, dazu Hibernate ORM mit Panache und der Treiber, wenn es eine Datenbank
gibt. Beide tragen den Namen deines Projekts, laufen auf seiner Java-Version und werden einmal mit ihrem
Wrapper gebaut. Build-Tool im Assistenten wählt Maven (pom.xml, mvnw) oder Gradle (Kotlin-DSL,
build.gradle.kts, gradlew). Es steckt im zweiten Schritt unter Erweiterte Einstellungen, man
übersieht es also leicht; ohne Auswahl bekommst du Maven. Über die API ist das templateBuildTool mit
maven oder gradle. Der
erste Build dauert ein paar Minuten, während die Abhängigkeiten geladen werden.
Hat das Projekt eine Datenbank, lassen beide Vorlagen Hibernate die Tabellen anlegen, denn keins der
beiden Frameworks tut das gegen einen echten Datenbankserver von selbst: Die erste @Entity, die du
hinzufügst, würde sonst mit relation does not exist enden. Spring Boot bekommt
spring.jpa.hibernate.ddl-auto=update in application.properties. Sobald Flyway oder Liquibase das
Schema verwalten, stellst du das auf validate oder none. Quarkus bekommt
%dev.quarkus.hibernate-orm.schema-management.strategy=update, damit deine Daten das Live-Reload
überstehen, und %test.quarkus.hibernate-orm.schema-management.strategy=drop-and-create, weil die
Testdatenbank leer startet und ihre Tabellen bei jedem Lauf neu braucht. Beide Vorlagen bringen einen Pfad für den Health Check mit, den du gleich nutzen kannst: /actuator/health
bei Spring Boot, /q/health bei Quarkus.
Die Startseite von Quarkus leitet im Entwicklungsmodus auf die Dev UI weiter, die von sich aus nur
Anfragen für localhost beantwortet. Envoryx setzt QUARKUS_DEV_UI_HOSTS auf den Hostnamen des
Projekts, ein Muster für die übrigen Namen unter der Basis-Domain und den öffentlichen Host, damit
/q/dev-ui unter der Projekt-URL aufgeht. Das Muster braucht Quarkus 3.16 oder neuer; bei älteren
Versionen klappt nur der eigene Hostname des Projekts. Eine zusätzliche Domain außerhalb der
Basis-Domain trägst du im Bereich Umgebung in dieselbe Variable ein, durch Kommas getrennt. Der
Produktionsmodus hat ohnehin keine Dev UI.
Der Bereich Tests führt mvn test oder gradle test aus (filtern mit -Dtest oder --tests) und
zeigt das Ergebnis je Test aus den JUnit-Reports. Der Lauf richtet SPRING_DATASOURCE_URL,
QUARKUS_DATASOURCE_JDBC_URL, JDBC_URL und DATABASE_URL auf <datenbank>_test, die Envoryx vorher
anlegt. Bei einer URL mit Parametern wie ?useSSL=false kommt die Endung vor die Parameter, auch sie
landet also bei der Testdatenbank. Ein Test mit create-drop kann deine Entwicklungsdatenbank deshalb
nicht leeren.
Debuggen
Abschnitt betitelt „Debuggen“Mit JDWP debuggen startet die JVM des Servers mit einem JDWP-Agent, auf Port 5005, solange du nichts
anderes einstellst, und mit suspend=n, und veröffentlicht diesen Port auf einem Host-Port. In deinen
Build musst du dafür nichts einbauen: Bei spring-boot:run landet der Agent in der abgespaltenen JVM der
Anwendung, bei bootRun fügt ihn ein Init-Skript hinzu, und der Quarkus-Entwicklungsmodus bekommt
-Ddebug.
In IntelliJ IDEA nimmst du Run → Remote JVM Debug mit Host und Port aus dem Bereich IDE. In VS Code
verbindet sich das Extension Pack for Java mit einer launch.json wie dieser; der Bereich IDE zeigt sie
mit deinem Host und Port:
{ "type": "java", "name": "Attach to Envoryx", "request": "attach", "hostName": "<host>", "port": <port>}Ohne Server wird nur der Port veröffentlicht - für eine JVM, die du selbst im Java-Terminal mit
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 startest.
Willst du gleich ganz im Container arbeiten, öffnet IntelliJ IDEA das Projekt über JetBrains Gateway mit
JDK, Maven und Gradle aus dem Container, und VS Code macht dasselbe mit Remote-SSH. Der SSH-Benutzer ist
<projekt>.java, wenn das Projekt mehr als eine Runtime hat, siehe
IDE-Integration.
Aktionen und Worker
Abschnitt betitelt „Aktionen und Worker“Im Bereich Aktionen gibt es java -version und je nach Build-Tool package, clean,
dependency:tree und versions:display-dependency-updates für Maven oder build, clean,
dependencies und tasks für Gradle.
Für Arbeit im Hintergrund gibt es zwei Worker-Vorlagen: Jar-Datei startet java -jar <datei>, etwa
eine Spring-Boot-App mit Worker-Profil oder einen Queue-Consumer, und Build-Tool-Goal führt
mvn <goal> oder gradle <task> aus, zum Beispiel exec:java -Dexec.mainClass=com.example.Worker.
Cron-Jobs laufen im Java-Container wie in jedem anderen. Mehr dazu unter
Worker und Cron-Jobs.
Java später dazunehmen oder entfernen
Abschnitt betitelt „Java später dazunehmen oder entfernen“Die Java-Karte im Bereich Laufzeit hat einen Schalter Java aktivieren. Schaltest du ihn aus,
verschwinden der Java-Container und die Container der Java-Worker; deine Dateien und die
Worker-Definitionen bleiben. Auf der Kommandozeile kennt envoryx dafür --java <version>,
--java-server und --java-preset spring-boot|quarkus|jar.