Spring Tools 4 Update

Am 22.01.2020 wurde das neue Update der beliebten Spring Tools 4 Suite veröffentlicht. Im allgemeinen sind Verbesserungen und Fehlerbeseitigung in Eclipse vorhanden. Ausnahme bildet der Hover Mechanismus, der nun auch Connection Failures anzeigen kann.

Spring Boot

  • Live hover können nun Connection Failures anzeigen

Eclipse

  • Completions werden nun asynchron durchgeführt
  • Early access builds für Eclipse 4.15 verfügbar
  • Bei der Autovervollständigung für Parameter in @Value Annotationen wurde immer ein zusätzliches NewLine eingefügt
  • Das Spring Boot Dashboard konnte Projekte die ein Leerzeichen enthalten nicht starten
  • Eine NPE in PropertiesJavaDefinitionHandler.adjustedHighlightRangeForKey wurde gefixt
  • Neueste m2e Snapshot um Probleme mit JUnit 5 Tests zu beseitigen

Drone

Drone im Zusammenspiel mit Gitea kann als CI/CD Pipeline eingesetzt werden. Dank zahlreicher Plugins, lässt sich so auch ein Continuous Delivery Pipeline für das Deployment aufbauen. Hier in diesem Beispiel soll es aber vielmehr um die Installation und Konfiguration gehen. Es wird ein Java Programm, das mit Gradle als Buildtool übersetzt wird, einen Status in Gitea als sogenanntes Badge anzeigen. Dazu wird nach jedem Commit ein Docker Image als Grundlage verwendet, um das Programm mit Gradle zu bauen und testen.

Vorbereitung

Zunächst habe ich einen DNS Eintrag drone.XXXX.org erstellt, damit der Dienst hinter dem Reverse Proxy (NginX) aus dem Netz erreichbar ist.

1. eine OAuth2 Application erstellen

Damit OAuth2 mit Drone und Gitea funktuiniert, muss man in Gitea eine “Anwendung” anlegen. Dieser Anwendung wird später die Erlaubnis gegeben, Gitea als Basis für die Git Repositories zu dienen. In den Einstellungen unter Settings –> Applications –> Manage OAuth2 Applications wird nun eine neue Anwendung erstellt.

Der Anwendung geben wir den Namen drone, damit eine Zuordnung später möglich ist. Die redirect URL setzt sich aus der Domain plus dem Path /login zusammen.

Feld Wert
Name drone
Redirect URL https://drone.XXXX.org/login

Mit einem Klick auf create application wird nun die Anwendung in Gitea angelegt.

Notieren Sie sofort die angezeigten Werte, da das Secret später nicht mehr angezeigt wird und bei Verlust ein neues Secret erstellt werden muss. Hier beispielhafte Werte die dann angezeigt werden:

Var Value
ClientID 3fdd3921-22f4-49ab-bcb1-2b15854e936f
Client Secret 6neDCDDVXya5eVCb4JJhNZGGGdG-nSoA3f8QKYyjamk=

Beenden Sie nun die Eingabe mit einem Klick auf den Button Save, um die Anwendung endgültig anzulegen.

2. Secret für die Kommunikation zwischen Drone und und dem Runner erstellen

Damit sich der Docker Runner mit Drone unterhält, muss ein zuvor generiertes Secret ausgetauscht werden. Mit openssl lässt sich ein Secret sehr einfach auf der Kommandozeile generieren. Der Aufruf von

openssl rand -hex 16

spuckt etwas wie

c014d553037b2ab672374684bed79fa0

aus. Dieses Secret muss dem Docker Container als RPC_SECRET übergeben werden (siehe unten).

3. Drone Container starten

Als Vorbereitung laden wir den Container mit docker pull runter.

docker pull drone/drone:1

Konfiguration

Der Docker Container benötigt einige Angaben, damit Drone mit dem Gitea zusammenarbeitet. Es werden die ClientID, ClientSecret und das Secret für den Docker Runner benötigt. Die Adresse des Gitea Server, sowie die Server Host Adresse und das Protokoll müssen angegeben werden. Alle weiteren Parameter sind Standard und bedürfen hier keiner weiteren Erklärung.

Die gesamte Konfiguration sieht dann wie folgt aus:

docker run \
  --volume=/var/lib/drone:/data \
  --env=DRONE_AGENTS_ENABLED=true \
  --env=DRONE_GITEA_SERVER=https://git.XXXX.org \
  --env=DRONE_GITEA_CLIENT_ID=3e2f5dd2-2704-11ea-9f11-00248cbd6e48 \
  --env=DRONE_GITEA_CLIENT_SECRET=6neDCDDVXya5eVCb4JJhNZGGGdG-nSoA3f8QKYyjamk= \
  --env=DRONE_RPC_SECRET=c014d553037b2ab672374684bed79fa0 \
  --env=DRONE_SERVER_HOST=drone.XXXX.org \
  --env=DRONE_SERVER_PROTO=https \
  --publish=80:80 \
  --restart=always \
  --detach=true \
  --name=drone \
  drone/drone:1

Achtung: Auch wenn über Port 80 nur HTTP geht, so muss als Protokoll HTTPS angegeben werden, da sonst die Redirect URI nicht korrekt ist und OAuth2 Login nicht funktioniert und eine Fehlermeldung innerhalb von Gitea angezeigt wird.

Grant Access

Durch Aufruf der Seite https://drone.XXXX.org wird man per Redirect auf den Gitea Server weitergeleitet. Hier muss man jetzt noch der Anwendung “drone” den Zugriff gewähren. Danach wird man auf die Drone Seite weitergeleitet, wo jetzt alle Repositories des Gitea Servers gelistet sein sollten.

4. Docker Runner

Drone benötigt, wie bei GitLab auch, Runner. Es gibt verschiedene Runner, die man nutzen kann, um ein Build durchzuführen. Wie unter https://docker-runner.docs.drone.io/installation/install_linux/ beschrieben, installieren wir nun noch einen Runner auf Docker Basis. Zunächst pullen wir den Runner mit

docker pull drone/drone-runner-docker:1

Den Runner konfigurieren

Der Runner benötigt nicht ganz so viele Parameter wie der Drone Container. Einige werden jedoch dennoch benötigt. Es muss der Host und das Protokoll für den Drone Server angegeben werden. Zusätzlich muss das vorher generierte Secret übergeben werden, so dass sich folgender Aufruf ergibt:

docker run -d \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -e DRONE_RPC_PROTO=https \
  -e DRONE_RPC_HOST=drone.XXXX.org \
  -e DRONE_RPC_SECRET=e16578d8ae6306175e49df94ad2c75e4 \
  -e DRONE_RUNNER_CAPACITY=2 \
  -e DRONE_RUNNER_NAME=${HOSTNAME} \
  -p 3000:3000 \
  --restart always \
  --name runner \
  drone/drone-runner-docker:1

Nach dem erfolgreichen Start pollt der Runner den Server https://drone.XXXX.org, welches mit docker logs runner sichtbar wird.

[test@manjaro-server ~]$ docker logs runner
time="2019-12-19T11:06:44Z" level=info msg="starting the server" addr=":3000"
time="2019-12-19T11:06:45Z" level=info msg="successfully pinged the remote server"
time="2019-12-19T11:06:45Z" level=info msg="polling the remote server" arch=amd64 capacity=2 endpoint="https://drone.XXXX.org" kind=pipeline os=linux type=docker

Nun steht der Runner bereit und wartet auf Aufträge die er abarbeiten kann.

Die Projektkonfiguration

Repository aktivieren

In den Settings des Repository muss dieses aktiviert werden, damit Drone auf Events reagieren kann.

Ein Drone YAML Skript erstellen

Drone ließt die Datei .drone.yml im Rootverzeichnis des Repositories aus und handelt nach den dort hinterlegten Anweisungen.

Hier ein sehr einfaches Skript, welches bei jeden Commit einen Test mit Gradle durchführt. Das Skript verwendet ein Archlinux als Basis und installiert Java nach, damit der Gradle Wrapper in dem Repository

kind: pipeline
name: default

steps:
- name: setup
  image: archlinux/base:latest
  commands:
    - echo 'Server = http://cache.XXXX.org:8080/$repo/os/$arch/' > /etc/pacman.d/mirrorlist
    - pacman -Sy --noprogressbar --noconfirm jdk11-openjdk
    - JAVA_HOME=/usr/lib/jvm/java-11-openjdk/ ./gradlew clean build test

Wichtig: –noprogressbar muss angegeben werden, da sonst der Drone Runner sofort das Skript mit SUCCESS beendet. Dieses scheint ein Fehler von Drone zu sein.

Für eine schnellere Verarbeitung der Kommandos habe ich einen lokalen Cache für Pacman eingerichtet. Der Cache ist unter der Domäne cache.XXXX.org auf dem Port zu erreichen. Das Docker Image ist bereits lokal von Docker gecacht und daher ist nun eine Installation der Java und Gradle Pakete innerhalb kurzer Zeit erledigt.

Nun wird Drone beim jedem Commit in das Repository einen eigenständigen Build anstoßen und das Badge entsprechend in Gitea aktualisieren.

Plugins

Für Drone.io gibt es jede Menge Plugins unter https://plugins.drone.io/

Alternativen

Es gibt bereits einen Fork von Drone, der im Wesentlichen eine Erweiterung für Windows Programme mit Cygwin darstellt. Der Fork tea-ci ist unter https://docs.tea-ci.org/usage/overview/ zu finden.

Den JOOQ Parser verwenden

Hier ein praktisches Beispiel wie man den Parser von JOOQ verwenden kann. Gegeben sei ein String der einen Select enthält.

select name from books

Dieser soll aber mit Hilfe vom der JOOQ DSL um ein LIMIT und ein OFFSET von jeweils 10 erweitert werden.

Zunächst muss der Parser den String in DSL Objekt parsen, welches dann mit der DSL entsprechend modifiziert werden kann.

dsl.selectFrom(dsl.parser().parse("select name from books"))
  .limit(10)
  .offset(10)
  .fetch();

Hinweis: Ein kleiner Nachteil ist, dass der Parser den SQL String verstehen muss. Zur Zeit werden noch nicht alle Feinheiten der diversen Dialekte unterstützt.

Weitere Einsatzmöglichkeiten

Da der Parser viele Dialekte versteht, kann man das SQL mit JOOQ transpellieren, dass heißt von den einem Dialekt in einen anderen transformieren.

Buildship does not support JDK 13

In einem Test wollte ich mit den aktuellen Spring Tool Suite die neuen Features von Java 13 testen. Also habe ich die sourceCompatibility in build.gradle kurzerhabnd auf 13 gesetzt. Doch Eclipse konnte das Project nicht bauen. Nach kurzem suchen im Netz bin ich auf das Issue 1196 gestoßen. Das Eclipse Buildship Plugin wird erst in der Version 3.1.3 JDK 13 erkennen und unterstützen.

sourceCompatibility = 11 // can't use JDK 13 as buildship 3.1.3. See https://github.com/eclipse/eclipse.jdt.ls/issues/1196

Maven Caching

Warum sollte man einen zentralen Cache verwenden? Nun, wenn man in einem Betrieb mit mehreren Mitarbeitern arbeitet, dann kann es unter Umständen schon zu Engpässen in der Bandbreite führen. Dieses gilt auch heute bei Modernen DSL Anschlüssen, da diese oftmals auch Telefonie bereitstellen und daher wegen QoS diese zusätzlich von der Verfügung stehenden Bandbreite abgezogen werden müssen.

Es gibt aber auch noch weitere Gründe warum man den Zugang zu dem Maven Repository zentralisieren sollte. Es kann sein, dass bestimmte Projekte sehr strenge Vorgaben an die zu verwendenden Dependencies machen. Dieses können zum Beispiel verwendete Lizenzen oder CVEs sein.

Nexus

Zunächst muss man in Nexus ein Maven 2 Proxy anlegen. Ich gehe hier davon aus, dass eine laufende Instanz von dem Nexus Server im internen Netz vorhanden ist. Der Nexus Server ist auch als Docker Image vorhanden und lässt sich in ein paar Minuten aufsetzen.

Proxy Einrichten

Wie bereits beschrieben muss zunächst ein Maven Proxy angelegt werden. Dazu meldet man sich als Administrator an und geht in den Einstellungen in die Repositories. Dort kann man mit create ein neues Repository anlegen. Wählen sie den Type Maven2(Proxy). Vergeben sie als Namen maven-central-proxy. Der Name ist dann Teil der URL unter dem dann im Nexus der Maven Proxy erreichbar ist.

Für die Upstream URL für das Maven Repository https://repo1.maven.org/maven2 ein.

Maven

In Maven kann man die Repositories in der settings.xml verwalten.

<settings>
  <mirrors>
    <mirror>
      <!--This sends everything else to /public -->
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>http://nexus.XXXX.de/repository/maven-central-proxy/</url>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
      <id>nexus</id>
      <!--Enable snapshots for the built in central repo to direct -->
      <!--all requests to nexus via the mirror -->
      <repositories>
        <repository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
     <pluginRepositories>
        <pluginRepository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <!--make the profile active all the time -->
    <activeProfile>nexus</activeProfile>
  </activeProfiles>

  <!-- set if non anonymous connection allowed -->
  <servers>
    <server>
      <id>nexus</id>
      <username>XXX</username>
      <password>YYY</password>
    </server>
  </servers>    
</settings>

Gradle

In Gradle hat man mehrere Möglichkeiten ein anderes Repository zu verwenden. Das Naheliegendste ist in dem build.gradle das Repository direkt anzugeben. Doch Gradle bietet noch mehr Wege, um hier einzugreifen.

In https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.dsl.RepositoryHandler.html wird beschrieben, dass das Maven Central Repository mit der URL https://repo.maven.apache.org/maven2/ verwendet wird. Diese lässt sich zentral für alle Projekte auf dem Host durch ein Skript ersetzen, welches ich in dem nächsten Abschnitt vorstelle.

Gradle Init Skript

Das Gralde Init-Skript wird ausgeführt, wenn man Gradle startet bzw. ein Task ausgeführt wird. Das Init-Skript kann eine Datei sein die init.gradle heißt und im Ordner ~/.gradle liegt oder es ist eine Datei die mit .gradle endet und in dem Verzeichnis ~/.gradle/init.d liegt.

Ich habe mit für letzteres entschieden und eine Datei maven.gradle in dem Verzeichnis ~/.gradle/init.d erstellt. Es muss zunächst die nexusUrl angepasst werden. Diese setzt sich aus der Server URL und dem Namen des Maven2 Proxy Repositories zusammen. Hier also maven-central-proxy. Das Skript filter Repositories die mit https://repo.maven.apache.org/maven2 heraus und fügt dafür die URL von dem Nexus Server ein. Das Ganze wird auf der Konsole geloggt, sodass man auch gleich erkennt welche Repositories in dem Projekt verwendet werden.

Das Skript maven.gradle:

//
// init.d/maven.gradle
// -------------------
//
// This replaces all occurance of mavenCentral repositories with the local Nexus repository
//

def nexusUrl = "https://nexus.XXXX.de/repository/maven-central-proxy"
def prefix = "> Init.d/maven.gradle :"

allprojects {
    buildscript {
        repositories {
            all { ArtifactRepository repo -> 
                if ((repo instanceof MavenArtifactRepository) && repo.url.toString().startsWith("https://repo.maven.apache.org/maven2")) {
                    project.logger.warn "${prefix}Buildscript Repository: ${repo.url} removed. Only $nexusUrl is allowed"
                    remove repo
                }
            }
            maven {
                url "$nexusUrl"
            }
        }
    }

    repositories {
        all { ArtifactRepository repo ->
            if ((repo instanceof MavenArtifactRepository) && repo.url.toString().startsWith("https://repo.maven.apache.org/maven2")) {
                project.logger.warn "${prefix}Repository: change ${repo.url} ---> $nexusUrl"
                remove repo
            } else {
               println "${prefix}Repository: " + repo.url.toString()
            }
        }
        maven { 
            url "$nexusUrl" 
        }
    }
}

Hinweis: Gradle missachtet hier die settings.xml. Wenn eine Authentifizierung am Nexus nötig ist (kein anonymous), dann müssen in den beiden maven-Blöcken jeweils ein Credentials Block eingefügt werden. Sonst erhält man ein 401 HTTP Error.

credentials {
  username "XXX"
  password "YYY"
} 

Gradle Build

Wenn man jetzt einen Build startet, dann kann man auf der Konsole erkennen, wie das Skript das Maven Central Repository, welches über mavenCentral() eingebunden wurde, ersetzt wird.

[sascha@Workstation TestServer]$ ./gradlew build
> Init.d/maven.gradle :Repository: https://nexus.XXXX.de/repository/maven-central-proxy

> Configure project :
> Init.d/maven.gradle :Repository: change https://repo.maven.apache.org/maven2/ ---> https://nexus.XXXX.de/repository/maven-central-proxy
> Init.d/maven.gradle :Repository: https://repo.spring.io/snapshot
> Init.d/maven.gradle :Repository: https://repo.spring.io/milestone

Portainer

Portainer ist eine WebAnwendung für die Verwaltung von Docker Containern. Portainer ist selbst als Docker Container zu erhalten und somit ist die Installation auch sehr einfach.

Das Remote API aktivieren

Für lokale Docker Installationen ist nichts weiter nötig, da Portainer direkt auf den Socket von dem Docker Daemon zugreift. Möchte man zusätzlich auch entfernte Docker Daemon ansteuern, so muss zunächst

sudo mkdir /etc/systemd/system/docker.service.d
sudo tilde /etc/systemd/system/docker.service.d/startup_options.conf

# /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376

Änderung bekanntmachen

Der Docker Daemon muss nun nur noch neu gestartet werden, dann kann Portainer konfiguriert werden.

sudo systemctl daemon-reload
sudo systemctl restart docker.service

Den Endpoint in Portainer hinzufügen

Nun, da wir den Port 2376 im Netzwerk bereitstellen, kann Portainer auf den Remote Dockerserver zugreifen.

Wireguard Netzwerk

Die prinzipielle Einrichtung von Wireguard habe ich in dem Artikel Wireguard beschrieben. Es ging um die die Point to Point Einrichtung mehrerer Clients die auf einen PC, der als Server fungiert, zugreifen. Die Clients konnten sich untereinander nicht sehen.

Im folgenden möchte ich beschreiben, wie man ein VPN Netz aufbaut in dem sich die Clients untereinander verbinden können. Damit Clients in dem privaten Netzwerk sich über Hostname ansprechen können, wird ein zentraler DNS Server eingerichtet. Hierfür verwende ich einen vorhandenen OpenWRT Router.

Aufbau des Netzwerkes

Das Netzwerk ist auf mehrere Standorte verteilt. Es gibt einen zentralen Linux PC der die Rolle des Wireguard Servers einnimmt. Die Rolle einnimmt, weil es per se keinen Wireguard Server gibt. Es ist die Konfiguration die ihn zur Zentrale werden lässt. Damit dieser erreichbar ist, ist ein DynDNS Dienst oder eine feste IP Adresse von Nöten. Dieses ist aber nicht Bestandteil dieses Artikels. Hier verwaise ich zum Beipsiel auf den Artikel DynDNS mit Duck DNS.

Der Server bekommt in dem VPN die Adresse 10.0.0.1. Der Server ist für das Routing unter den Clients verantwortlich. Das heißt er leitet eingehende Pakete an die anderen Clients weiter. Hierfür muss das IP Forwarding aktiviert und einfache Regeln für die FORWARD Chain von IPTABLES angelegt werden.

Die Clients stellen eine Reihe von Laptops, Workstations und weiteren Servern da. Die Client erhalten durchnummerierte IP Adressen beginnend mit 10.0.0.2/32. In meinem Setup sind es aktuell 6 weitere PCs die in dem VPN vorhanden sind.

Konfiguration von Wireguard

Im Folgenden werde ich das Schema wie Clients und der Server einzurichten sind erläutern. Wenn das Schema bekannt ist, dann ist es leicht weitere Clients hinzuzufügen.

Einrichtung auf dem Server

Das Schema ist eigentlich recht einfach. Es muss allerdings auf die Subnetzmaske bei der Angabe der IP Adressen genau geachtet werden. Die Server Adresse ist mit /24 und die Clients mit /32 anzugeben.

[Interface]
PrivateKey = PrivateKeyDesServers
ListenPort = 32768
Address = 10.0.0.1/24

# Allow routing between clients
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT

#
# Client 1
#
[Peer]
PublicKey = PublicKeyDesClient
AllowedIPs = 10.0.0.2/32

#
# Client 2
#
[Peer]
PublicKey = PublicKeyDesClient
AllowedIPs = 10.0.0.3/32

#
# Client 3
#
[Peer]
PublicKey = PublicKeyDesClient
AllowedIPs = 10.0.0.4/32

.
.
.

#
# Client 7
#
[Peer]
PublicKey = PublicKeyDesClient
AllowedIPs = 10.0.0.7/32

Einrichtung der Clients

Es ist hierbei wichtig zu wissen, dass nur ein Client das gesamte Subnetz beanspruchen darf! Sonst funktioniert das ganze nicht. Das heißt auf den Clients sind explizit die Adresse einzutragen mit denen der Client kommunizieren darf. Dieses macht aber nur bei kleineren Netzen einen Sinn.

[Interface]
PrivateKey = PrivateKeyDesClients
Address = 10.0.0.3/24
DNS = 10.0.0.7

[Peer]
PublicKey = PublicKeyDesServers
AllowedIPs = 10.0.0.1/32, 10.0.0.2/32, 10.0.0.5/32, 10.0.0.6/32, 10.0.0.7/32
EndPoint = myname.dyndns.org:32222
PersistentKeepalive = 25

Wichtig ist das alle Clients mit 10.0.0.X/32 angegeben werden.

IP Forwarding Aktivierung

Das allgemeine IP Forwarding bzw. auf Geräteebene aktivieren habe ich bereits in dem Wireguard Artikel beschreiben.

Firewall Regeln

Damit die Pakete nun auch an die Client weitergeleitet werden, muss in der FORWARD Chain das In-/Output auf dem Wireguard Interface zulassen. Dieses kann mit dem PostUp in der Konfiguration automatisch nach dem Starten der Verbindung durchgeführt werden.

PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT

Verbindungsaufbau

Für den automatischen Verbindungsaufbau muss die Option PersistentKeepalive gesetzt werden, damit ein Paket Verbindung öffnet.

Einrichtung von DNS

Auf dem Client habe ich die Konfiguration mit dem KDE NetworkManager durchgeführt. Bei der Wireguard Konfiguration habe ich nun die Adresse des DNS Servers eingetragen, der für das interne Netzt zuständig ist. Die Konfiguration der statische Hostnames beschriebt ein andere Artikel.

Testen der Namesauflösung

Auf http://openwrt/cgi-bin/luci/admin/network/dhcp unter Local Server mit /XXXX.duckdns.org/ Anfrage an die Domain nach außen unterbinden, da diese sonst von DuckDNS beantwortet werden würden und somit die IPV6 Adresse genommen wird, was zu Fehlern führt.

Spring Tool Suite

Mit dem neuen Release 4.3.0 von Spring Tool Suite, ist Pivotal wieder gut gelungen.

Neuerungen

Neben dem Update auf Eclipse 2019-06 (Release News) sind auch wieder jede Menge Bugfixes dabei (Changelog).

Änderungen in STS4

Theia ist nun in die Liste der offiziell unterstützten Editoren aufgenommen worden. Dafür wurde die Unterstützung für ATOM (ab Spring Tools 4.3.0) gestrichen und ist nun kein unterstützter Client mehr.

Spring Boot

  • Verbesserung: Die Startugeschwindigkeit wurde durch Optimierung der Classpath Meldungen verbessert
  • Verbesserung: Symbole werden neu erstellt, wenn ein abhängiger Typ sich ändert
  • Bugfix: Langsame Codevervollständigung (mehr als eine Sekunde) wurde behoben
  • Bugfix: Der Content-Assisten für Spring XML funktioniert nun auch wieder in VS Code und Theia Editoren
  • Bugfix: Ein ClassCast Exception in dem Spring Boot LanguageServer wurde behoben
  • Bugfix: Anonyme Inner Types hatten keine Boot Hints

Eclipse

  • Bugfix: Das schelle neustarten einer Anwendung im Boot Dashboard erzeugt keine Fehler mehr
  • Bugifx: Merkwürdige Exceptions beim Löschen einer CF Anwendung, wenn der SSH Tunnel noch aktiv war, wurde behoben