Das Gradle Deploy Plugin

Das https://gradle-ssh-plugin.github.io/docs/ gradle plguin eigent sich um zum Beispiel eine Spring Boot Jar auf einen remote Server zu deployen.

Upload per SSH auf einen Server

Hier nun ein Beispiel, wie man eine Datei auf einen remote Server kopiert. Dazu muss zunächst der Server definiert werden. Im Anschluss wird ein Deploy Task definiert, der von dem bootJar Task abhängt.

/**
 * This deployment script uses ssh to copy the spring boot jar to a remote server. The credentials are stored
 * in the credential store plugin from nu.studer. 
 *
 * set user and password for deployment
 *
 * to set username
 * ./gradlew addCredentials --key deployUsername --value sascha
 *
 * to set password
 * read -s -p "Enter Password: "; ./gradlew addCredentials --key deployPassword --value $REPLY; unset REPLY
 */


// define remote server
remotes {
    server {
        host = 'server'
        user     = credentials.deployUsername
        password = credentials.deployPassword
    }
}

// copy jar to server
task deploy(dependsOn: bootJar) {
    doLast {
        ssh.run {
            session(remotes.server) {
                put from: bootJar.archivePath.path, into: '/somejar-0.0.1.jar'
            }
        }
    }
}

Weitere Möglichkeiten gradle-ssh Plugin

Das war hier natürlich nur ein sehr einfach gehaltenes Bespiel. Es lassen sich durch die zahlreichen Funktionen wie das Ausführen, Kopieren und vielen mehr fast alles realisieren.

DefaultNamingStrategy von Hibernate

Per default werden die Tabellen nach den Klassennamen und die Columns nach den Feldern benannt. Manchmal stört es einen oder es passt nicht zur der restlichen Struktur in der Datenbank.

Eine Custom NamingStrategy definieren

In diesem Beipsiel hat die Anwendung Tabellen die Über Hibernate verwaltet werden, sowie Tabellen die losgelöst davon im Schema vorhanden sind. Um diese Gruppen von Tabellen sauber zu trennen, kann man z.B. ein Prefix vor jeden Tabellenname einfügen und schon lassen sich schnell die Tabellen unterscheiden.

Tabellenname über @Table festelegen

Über die @Table Annoatation kann über den Parameter name der Tabellenname festgelegt werden. Dieses bietet natürlich größtmögliche flexibilität ist aber aufwändig, da jede Entität das angepasst werden muss.

@Table(name="hibernate_user")

Tabellenname über eine NamingStrategy festlegen

Wie oben beschrieben, wird es in den meisten Fällen nicht angebracht sein alles von Hand einzufügen. In Hibernate gibt es für den Zweck 2 Strategien, um die Generierung der Namen der Tabellen, Felder und Objekten zu beeinflussen. In diesem Beispiel interessiert uns die PhysicalnamingStrategy. Dazu implementieren wir das Interface PhysicalnamingStrategy wie folgt:

/**
 * Adds a prefix "hibernate_" to all tablenames.
 * @author sascha
 *
 */
public class PrefixNamingStrategy implements PhysicalNamingStrategy {

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return stringToIdentifier("hibernate_" + name);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
        return name;
    }

    /**
     * String to Identifier
     */
    private Identifier stringToIdentifier(String name) {
        return Identifier.toIdentifier(name);
    }

Als Prefix habe ich hier hibernate_ hinzugefügt. Alle anderen Bezeichnungen werden 1:1 übernommen.

Einbindung über eine Bean

Damit die Strategy auch angewandt wird, muss diese gesetzt werden. Das kann man über die Properties oder eine Bean machen. Hier zeige ich kurz die Bean Variante:

@Bean
public PhysicalNamingStrategy physicalNamingStrategy() {
    return new PrefixNamingStrategy();
}

Dependency Management unter SpringBoot 2

Fügt man als Dependency spring-boot-starter-jooq ein, dann werden alle Abhängigeiten sowie Jooq als Abhängigkeiten eingefügt. Soweit so gut. Aber was ist, wenn man eine neuere Version von Jooq (zum Beispiel aufgrund eines Bugs) verwenden möchte?

Override Version

Unter Gradle kann man mit ext [jooq.version] die zu verwendende Version vorgeben.

// Override supported Jooq version 3.11.9
ext['jooq.version'] = '3.11.11'

Nun wird die aktuelle Version von Jooq 3.11.11 verwendet.

Milestone von Spring Boot 2.2.0M1

Um neue Feature zu testen, kann man auf die Milestone Repositories zurückgreifen. Da das Spring Boot Framework über ein Plugin eingebunden wird, muss das dem PluginManagement mit geteilt werden. Dazu muss man in der settings.gradle das Snapshot und/oder Milestone Repository hinzufügen. Ansonsten wird Spring Boot 2.2.0M1 Version von gradle nicht gefunden.

Plugin

Folgende Änderungen an der settings.gradle Datei sind nötig, um die Milestone Variante zu finden…

pluginManagement {
    repositories {
        maven { url 'https://repo.spring.io/snapshot' }
        maven { url 'https://repo.spring.io/milestone' }
        gradlePluginPortal()
    }
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == 'org.springframework.boot') {
                useModule("org.springframework.boot:spring-boot-gradle-plugin:${requested.version}")
            }
        }
    }
}

Dependencies

Jetzt kann regulär die Version 2.2.0.M1 eingebunden werden.

plugins {
    id 'java'
    id 'eclipse'
    id 'org.springframework.boot' version '2.2.0.M1'
    id 'io.spring.dependency-management' version "1.0.7.RELEASE"
}

Spring Tool Suite 4.2.0

Hier kurz die wichtigsten Änderungen der neuen Version vom 28.03.2019.

  • Bislang gab es keine Möglichkeit der VM für den Language Server Procol process Parameter zu übergeben. Dieses kann notwendig sein, wenn zum Beispiel der Heap zu klein ist.

    -Dboot.ls.custom.vmargs=-Xmx2G

    Kann man den Heap für das LSP auf 2G anheben.

  • Stark verbesserte Performance. Der Cache der Symboleindizierung des LSP wird nun über Neustarts des LSPs hinweg gespeichert.

  • Die interne Kommunikation mit dem JDT wurde ersetztz, was auch zu einer erhöhten Performance durch zeit- und Speichersparmaßnahmen geführt hat.
  • Update auf Eclipse 2019-03.
  • In den Lauch-Configs wird JMX Unterstüzung nun per default gesetzt.
  • Ein out of sync Fehler bei den Live Hoover wurde behoben.
  • Verschiedene Fehler in der Navigation in den Live Bean wurde behoben.
  • Weitere kleinere Eclipse Probleme wurden behoben.

Die Ausgabe des Logos von Jooq verhindern

Beim Starten eines Queries wird normalerweise das Logo von Jooq ausgegeben. Das ist auch nicht weiter schlimm, es ist nur beim Debuggen der Anwendung manchmal ein wenig störend, weil es viel Platz auf der Konsole einnimmt.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@  @@        @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@        @@@@@@@@@@
@@@@@@@@@@@@@@@@  @@  @@    @@@@@@@@@@
@@@@@@@@@@  @@@@  @@  @@    @@@@@@@@@@
@@@@@@@@@@        @@        @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@        @@        @@@@@@@@@@
@@@@@@@@@@    @@  @@  @@@@  @@@@@@@@@@
@@@@@@@@@@    @@  @@  @@@@  @@@@@@@@@@
@@@@@@@@@@        @@  @  @  @@@@@@@@@@
@@@@@@@@@@        @@        @@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@  @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  Thank you for using jOOQ 3.11.9

Dank geht an dieser Stelle an Lukas Eder für dieses tolle Werkzeug.

Wie kann nun das Logo abgeschaltet werden? Dazu muss nur die Property org.jooq.no-logo auf true gesetzt werden. Ich löse das in dem ich eine Klasse JooqConfig mit einem Bean erstelle. Die Komponente wird so beim Component Scan gefunden und die Property gesetzt, sodass beim Starten kein Logo angezeigt wird.

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class JooqConfig {
    @Bean
    private void disableLogo() {
        System.setProperty("org.jooq.no-logo", "true");
    }
}

Credentials auslagern

Neben der Möglichkeit die Credentials mit dem Gradle Plugin Credentials zu verwalten, kann man auch zum Beispiel dieses in eine weitere Datei auszulagern. Hierfür bietet sich YAML an.

Hier die jooq.yaml

---
jdbc:
  password: secret
  username: usr

In dem Buildscript jooq.gradle lesen wir die Daten aus dem YAML ein. Das SnakeYaml nutze ich hierfür um die cfg zu definieren.

...
jooq {
  def cfg = new org.yaml.snakeyaml.Yaml().load(new File("" + rootProject.projectDir + "/jooq.yml").newInputStream())
...

Weiter unten im Abschnitt xxx können jetzt die Credentials aus dem jooq.yaml angegeben werden…

...
    jdbc {
      driver = 'org.mariadb.jdbc.Driver'
      url = 'jdbc:mariadb://server:3306/database'
      username = cfg.jdbc.username
      password = cfg.jdbc.password
    }
...

Projekt reactor

Das Spring Framework nutzt für den Starter spring-boot-starter-webflux das Projekt reaktor. Dieses verleiht dem Spring Framework reaktive Streams. Das Framework Reactor ist von Hause aus Multithreading fähig, so dass es dem Entwickler hier hilft.

Warum reaktiv?

Meine Anwendung ist doch asynchron und verwendet Threads! Warum soll sie jetzt auf einmal reaktiv sein? Was ist der Unterschied zwischen asynchron und reaktiv? Diese Fragen stellte ich mir auch. Nun ja, einer der Unterschiede ist, dass das Framework ein Backpreasure kennt. Dieser “Gegendruck” verhindert zum Beispiel das ein Subscriber mit den vom Publisher gelieferten Daten überfordert wird.

Mono und Flux

Mono hat genau 0 … 1 Elemente und Flux hat 0 … N Elemente.

Hot und Cold Streams

Bei Hot-Streams ist die Anzahl der Elemente unbekannt und fortwährend. Man stelle sich zum Beispiel eine Datenquelle wie einen Temperatursensor vor. Solange die Anwendung läuft, können wir Messwerte abfragen und das unendlich.

Nimmt man als Quelle für einen reaktiven Stream zum Beispiel eine Liste, dann ist die Anzahl der Elemente bekannt und endlich. In der Sprache von reaktor heißt das Cold-Stream.

Es gibt noch einen weiteren unterschied zwischen Hot- und Cold-Streams: Sind Elemente in einem Hot-Stream einmal konsumiert durch einen Subscriber, dann sind Sie weg. Schreibt man sich bei einem Cold-Stream ein zweites mal ein, dann werden erneut alle Elemente publiziert.

Dependencies

implementation('io.projectreactor:reactor-core')


dependencyManagement {
     imports {
          mavenBom "io.projectreactor:reactor-bom:Californium-RELEASE"
     }
}

Wie mache ich aus einem blockierenden Methodenaufruf ein reaktiven Stream?

Mit der Methode .fromCallable() von Mono kann man einen blockierende Aufruf als Lambda Funktion übergeben und erhält unmittelbar ein Mono Objekt zurück. Als kurzes Beispiel wird hier eine blockierende Methode getInfo() gewrappt.

/**
    * Wrap blocking service call getInfo
    * 
    * @param Long itemId
    * @return Mono<Info>
    */
private Mono<Info> getInfo(Long id) {
        Mono<Info> blockingWrapper = Mono.fromCallable(() -> { 
                return service.getInfo(id);
        });

        return blockingWrapper.subscribeOn(Schedulers.elastic());
}

Das ganze kann man jetzt in einen Stream aus id verpacken.

private Flux<Info> getInfos(Flux<Long> ids) {
        return ids.flatMap(id -> getInfo(id), 2); // max 2 concurrent
}

Begrenzung der Aufrufe

In dem letzten Abschnitt haben wir gesehen wie man einen blockierenden Aufruf wrappen kann. In reaktiven Streams ist es so, dass keine Daten produziert werden solange kein Subscriber vorhanden ist. Wenn jetzt ein Subscriber für den Flux von getInfos() aboniert, dann werden alle id parallel im Excecutor ThreadPool abgfragt. Dieses kann zum Beispiel bei teuren Funktionen wie zum Beispiel Rest Calls zu einer Überlastung führen. Daher muss hier ein weiterer Parameter in flatMap mit angegeben werden. Damit kann man die Anzahl der Concurrent Calls begrenzen. Hier in diesem Beispiel sind immer maximal 2 konkurierende Aufruf gleichzeitig.

Codepfade

In einem Projekt das für Eingangsdaten sehr viele unterschiedliche Parser verwenden muss, ist es nur natürlich das wenn dieses in einer Klasse geschehen würde, dass das sehr unübersichtlich wird. Daher ist es besser, wenn man die Ganzheit in kleinere separate Parserklassen splittet.

Vor Spring

Nach diesem Schema bin ich in dem Projekt auch vorgegangen und habe für jede Gruppe einen eigenen Parser geschrieben. Alle Parser implementieren das Interface Parser, sodass sich folgender Code ergab:

//
// Instantiate class and execute parse
//
try {
    Class<?> c = Class.forName("de.pfau.parser.Parser00001");

    try {
        Parser p = (Parser) c.getDeclaredConstructor().newInstance();
        autowireCapableBeanFactory.autowireBean(p);

        p.parse();
    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
        throw new Exception ...
    } 
} catch (ClassNotFoundException e) {
    log...
}

Da das Projekt noch aus einer Zeit stammt in der es noch nicht mit Spring kontakt hatte, kommt hier die newInstance() Methode zum Einsatz. Damit in den Klassen auch das Autowiring weiterhin funktioniert, muss nach der Verwendung von DI mit autowireCapableBeanFactory.autowireBean(p) Spring hierüber in Kenntnis gesetzt werden. Ansonsten haben die Autowired Felder null Werte.

Die Umsetzung mit Spring

Spring bringt hierfür Frist Class Support mit, da das grundlegend für das Dependency Injection ist.

Mit dem ClassPathScanningCandidateComponentProvider kann man den Classpath durchsuchen. Beim Instanzieren des Objektes kann ein Parameter useDefaultFilters angegeben werden. Setzt man ihn auf false, dann hat man eine leere Filterkette.

ClassPathScanningCandidateComponentProvider scanner =
        new ClassPathScanningCandidateComponentProvider(false);

Als nächstes fügen wir einen neuen Includefilter ein. Es gibt verschiedene Filter die eingefügt werden können (siehe TypeFilter). Für meine Zwecke eignet sich hier der RegexPatternTypeFilter. Als Parameter übergeben wir einen kompilierten RegEx Ausdruck, sodass Standard Java RegEx zum tragen kommt.

Die Klassen heißen alle ParserXXXXX. Wobei XXXXX für fünf stellige Dezimalzahl steht.

scanner.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile("de.pfau.parser.*Parser\\d{5}")));

Über die Methode scanner.findCandidateComponents kann man eine gefilterte Liste der Kandidaten erhalten. Der Rest ist identisch zur alten Version.

Damit das Autowiring von autowireCapableBeanFactory funktioniert, darf der Code nicht im Konstruktor ausgeführt werden. Hierzu verwende ich die @PostConstruct Annotation. Die Instanzen der Parser werden in einer HashMap gespeichert, um so später darauf zuzugreifen.

ClassPathScanningCandidateComponentProvider scanner =
        new ClassPathScanningCandidateComponentProvider(false);

scanner.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile("de.pfau.parser.*Parser\\d{5}")));

for (BeanDefinition bd : scanner.findCandidateComponents("de.pfau.parser")) {
    //
    // Instantiate parser and parse loaded document
    //
    try {
        var classname = bd.getBeanClassName();
        Class<?> c = Class.forName(classname);

        try {
            Parser p = (Parser) c.getDeclaredConstructor().newInstance();
            autowireCapableBeanFactory.autowireBean(p);
            map.put(classname, p);

            log.info("parser " + classname + " successfuly loaded");
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            throw new Exception...
        } 
    } catch (ClassNotFoundException e) {
        log.severe("could not load class");
    }
}

Hat man einen Web Server z.B. Tomcat im Classpath, dann startet Spring Boot diesen automatisch. Es kann aber sein, das man die selbe Codebasis für zwei Anwendungen benutzt. Es lässt sich das Starten des Webservers steuern. Zum einen über die application.properties und programmatisch.

Spring Boot 2.x

Hier über die Properties:
spring.main.web-application-type=none

oder

public static void main(String[] args) {
    new SpringApplicationBuilder(Application.class)
        .web(WebApplicationType.NONE) 
        .run(args);
}

Spring Boot 1.x

Ab 2.0 haben sie die Bezeichnungen geändert. Zuvor musste die web-environment auf false gesetzt werden.

spring.main.web-environment=false

Am 20.12.2018 wurde die aktuellste Version der Spring Tools Suite 4.1.0 veröffentlicht.

Changelog für Spring Tools Suite 4.1.0

Wie immer wurden Fehler behoben und Aktualisierungen eingefügt. Nennenswertes ist diesmal das Eclipse Update. Eclipse erscheint seit einiger Zeit in einem 3 monatigen Rhythmus und so war Eclipse bei der Aktualisierung auch mit dabei. Spring Tools Suite basiert jetzt auf Eclipse 2018-12.

Weitere Änderungen liste ich hier kurz auf:

Spring Boot

  • Mit diesem Release ist erstmals eine Unterstützung für die Autovervollständigung (content-assist) für Spring Data Repository definitionen mit dabei.
  • Die Live Hover Hyperlinks zu den Javatypen funktionieren auch für Projekte die Java 9 oder höher verwenden.
  • Es wurde ein Problem mit beendeten Anwendungen behoben, wenn diese mit JMX über einen SSH Tunnel verbunden sind.

Eclipse

  • Eclipse Javadoc Live Hover ist nun auch ohne eine Abhägigkeit zum spring-boot-actuator wieder verfügbar.
  • Java 11 Support ohne Update vom Eclipse Marketplace
  • Verbesserter Kompatibilitätscheck von JDK Versionen
  • Live Reueast von Reactiven WebFlux Anwendungen können nun auch im Boot Dashboard angezeigt werden.
  • Fehlerbeseitung beim kommentieren mit Hilfe des Shortcuts