Autor für MySQL-Administrationshandbuch gesucht

Der www.rheinwerk-verlag.de ist auf der Suche nach einem Überarbeiter für das Administrationshandbuch zu MySQL (https://www.rheinwerk-verlag.de/mysql_3843/). Das Autorenteam des bestehenden Buchs findet für die anstehende Aktualisierung auf MySQL 8
nicht mehr die Zeit. Interessierte Autoren mögen sich bitte unter melden bei:

Christoph Meister, Lektor Computing
Rheinwerk Verlag GmbH | Rheinwerkallee 4 | 53227 Bonn
Telefon +49 228 42150-45
christoph . meister @ rheinwerk – verlag . de (Space entfernen)

Actuator in Spring Boot

Gesundheitzustand über den Actuator

Das Actuator-Modul von Spring Boot stellt HTTP-Endpunkte bereit, die Auskunft über den Zustand der Anwendung geben und es erlauben, die Anwendung zu beeinflussen.

Eigene Informationen über den „Gesundheitzustand“ lassen sich einbauen.

Die Informationen werden über REST veröffentlicht, können aber auch über JMX abgerufen werden.

Achtung: Actuator 1 und Actuator 2 unterscheiden sich deutlich!

Actuator in der POM

Einbinden wie üblich über die POM:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Da ganze funktioniert nur im Web-Modus, also muss org.springframework.boot:spring-boot-starter-web auch in der POM-Datei sein.

Starte man nun die Anwendung, kann man im Log die neuen Endpunkte sehen.

Aktivierung der Endpunkte

Damit ein Actuator Endpunkt über HTPP verfügbar ist, muss er enabled und exposed sein.

  • Die meisten Endpunkte sind standardmäßig deaktiviert.
  • Man kann nur actuator/health und actuator/health nutzen, und selbst da ist wenig Info vorhanden.

Um mehr Endpunkte freizuschalten setze man die die application.properties:

management.endpoints.web.exposure.include=*

Der Wildcard * aktiviert alle, alternativ zählt man kommasepariert auf.

Auswahl einiger Spring Boot Endpunkte

Die Endpunkte liegen alle eine Ebene unter http://localhost:8080/actuator:

/beans
Alle Spring-Beans
/env
Das Environment-Properties
/health
„health information“
/info
Applikationsinformationen
/loggers
Welche Log-Level für welche Pakete gelten
/metrics
Metriken über die aktuelle Anwendung, es muss eine weitere ID folgen, etwa metrics/http.server.requests
/threaddump
Thread-Dump

Management Endpoint URL

Natürlich lassen sich Dinge wie

  • Server-Port
  • URL-Basispfad
  • freigeschaltete Endpunkte
  • SSL-Verschlüsselung

konfigurieren.

Bin ich gesund?

Unter /health finden sich Informationen über den „Gesundheitszustand“ der Anwendung.

Der Status kann UP oder DOWN sein. Standardmäßig gibt es nur status

Authentifizierung

Bei einer nicht-authentifizierten Verbindung bekommt man nicht mehr zu sehen als den Status.

Um das zu ändern, müssen wir 1. in die POM mit aufnehmen:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Dann 2. in application.properties (zum Testen!):

management.endpoint.health.show-details=always
spring.security.user.name=user
spring.security.user.password=pass

Nach dem Neustart der Anwendung und Neuladen der Webseite erfolgt eine BASIC-Authentifizierung. Geben wir user/pass ein, sehen wir alles.

Nacktes Thymeleaf

Oftmals nutzen die Beispiele im Internet Spring, oder — noch spezieller — Thymeleaf wird als Template-Engine in Spring MVC eingesetzt, daher jetzt nackt, so einfach es geht.

In unsere POM kommt:

<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
 <groupId>org.thymeleaf</groupId>
 <artifactId>thymeleaf</artifactId>
 <version>3.0.9.RELEASE</version>
</dependency>

Wir schreiben das Template demo.html:

<!DOCTYPE html>
<html>
<body>
<p>Hey <span data-th-text="${name}">CHRISTIAN</span></p>
</body>
</html>

Und dann kann ein Java-Programm name füllen:

import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templateresolver.FileTemplateResolver;

public class ThymeleafDemo {

 public static void main( String[] args ) {

  FileTemplateResolver resolver = new FileTemplateResolver();
  resolver.setCharacterEncoding( "UTF-8" );

  TemplateEngine templateEngine = new TemplateEngine();
  templateEngine.setTemplateResolver( resolver );

  Context ctx = new Context();
  ctx.setVariable( "name", "tutego" );
 
  String result = templateEngine.process( "demo.html", ctx );
  System.out.println( result );
 }
}

 

Aktuelle offene Java-Seminare Q2/Q3 2018

Spring Boot (›SPRINGBOOT‹)

22.–24. Mai 2018 (KW 21), 11.–13. Juni 2018 (KW 24), 16.–18. Juli (KW 29)

Java Grundlagen (›JAVA1‹)

14.–18. Mai 2018 (KW 20), 25.–29. Juni 2018 (KW 26), 6.–10. August 2018 (KW 32), 17.–21. September 2018 (KW 38), 5.–9. November 2018 (KW 45)

Java für Fortgeschrittene (›JAVA2‹)

28. Mai bis 1. Juni 2018 (KW 22), 9.–13. Juni 2018 (KW 28), 20.–24. August 2018 (KW 34), 8.–12. Oktober 2018 (KW 41), 26.–30. November 2018 (KW 48)

Java für C++/C#-Programmierer

23.–27. April 2018 (KW 17), 11.–15. Juni 2018 (KW 24), 23.–27. Juli 2018 (KW 30), 3.–7. September 2018 (KW 36), 22.–26. Oktober 2018 (KW 43)

Nebenläufige Programmierung mit Threads (›JAVACON‹)

7.–8. Mai (KW 19), 14.–15. Juni (KW 24), 19.–20. Juli (KW 29)

Spring Retry

Das Spring Retry Projekt

Spring Retry ist ein Zusatzprojekt (https://github.com/spring-projects/spring-retry), um Codeblöcke wiederholt auszuführen, wenn sie zu einem Fehler führen.

  • Das ist nützlich beim Ansprechen von Remote-Diensten, die temporär nicht verfügbar sein können.

Als erstes müssen zwei Dependencies in unsere POM:

<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
</dependency>

@Retryable

Schreiben wir eine Bean, die zwei Anläufe braucht, eine Operation durchzuführen.

@Component
class Achiever {

  int tries = 1;

  @Retryable   
  public int tryIt() {   

    System.out.println( "Runde " + tries );
    tries++;

    if ( tries != 3 )
      throw new IllegalStateException( "Runde nicht 3" );

    return tries;
  }
}
Retryable drückt aus, das eine Operation bei Ausnahmen automatisch neu aufgerufen werden soll.
Die Methoden können eine Rückgabe haben, oder auch keine.

@EnableRetry

Damit Spring für die annotierten Methoden einen Proxy baut, müssen wir eine @Configuration mit @EnableRetry annotieren.

Wir können nun den Achiever injizieren:

@Autowired Achiever archiever;

Und die Methode ausführen:

System.out.println( "Vor dem Aufruf" );
int i = archiever.tryIt();
System.out.println( "Nach dem Aufruf ist i=" + i );

Auf der Konsole erscheint keine Exception. Nur:

Vor dem Aufruf
Runde 1
Runde 2
Nach dem Aufruf

Der Annotationsty Retryable

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {
	String interceptor() default "";
	Class<? extends Throwable>[] value() default {};
	Class<? extends Throwable>[] include() default {};
	Class<? extends Throwable>[] exclude() default {};
	String label() default "";
	boolean stateful() default false;
	int maxAttempts() default 3;              
	String maxAttemptsExpression() default "";
	Backoff backoff() default @Backoff();
	String exceptionExpression() default "";
}
Die Anzahl der Wiederholungen ist nicht unendlich, Spring stoppt standardmäßig bei 3.

Ausblick

Die Dokumentation unter https://github.com/spring-projects/spring-retry zeit weitere Möglichkeiten auf. Ein zusätzliches Beispiel:

@Retryable
public void tryIO() throws IOException {
  throw new IOException( LocalTime.now().toString() );
}

@Recover  
public void recover( IOException e ) {
  System.err.println( "Aarrrrg: " + e );
}
Die mit @Recover annotierte Methode wird am Ende einer nicht erfolgreichen Aufrufkette aufgerufen.

Caching in Spring Boot nutzen

Optimierung durch Caching

Bei Methoden, die idempotent sind, also zu einem Parameter immer den gleichen Wert liefern, kann Spring einen Cache zur Optimierung einsetzen.

  • Im Grunde ist der Cache ein Assoziativspeicher, der die Methodenparameter als Schlüssel nutzt.

Beispiele:

  • Berechnen einer Prüfsumme
  • Dateiinhalt
  • DNS-Cache

Achtung: Gewisse Cache-Inhalte können nach einer Zeit ungültig werden.

Caching in Spring

Um das Caching zu aktivieren annotiert man

  1. eine @Configuration mit @EnableCaching
  2. eine public (!!!) Methode mit @Cacheable und vergibt einen Cache-Namen.

@Cacheable-Beispiel (1/2)

@Component
class Hash {

  @Cacheable( "md5" )
  public byte[] md5( String text ) {

    System.out.println( "hash: " + text );
    try {
      MessageDigest md = MessageDigest.getInstance( "MD5" );
      return md.digest( text.getBytes( StandardCharsets.UTF_8 ) );
    }
    catch ( NoSuchAlgorithmException e ) {
      throw new IllegalStateException( e );
    }
  }
}

@Cacheable-Beispiel (2/2)

Wir können Hash injizieren

@Autowired
Hash hash;

und nutzen

byte[] md5_1 = hash.md5( "tutego" );
byte[] md5_2 = hash.md5( "tutego" );
System.out.println( Arrays.equals( md5_1, md5_2 ) ); // true

System.out.println( hash.getClass() ); // com.tutego.boot.basic.Hash$$EnhancerBySpringCGLIB$$4548965

Ausblick (1/2)

  • Spring generiert den Schlüssel zum Cache aus den Argumente. Man kann kann eigene KeyGenerator en verwenden.
  • Mit der SpEL lassen sich von Anfrageobejkte zum Beispiel die Schlüssel erfragen, etwa so @Cacheable(cacheNames="books", key="#isbn.rawNumber") .
  • Unter Umständen sollen große Objekte nicht in den Cache, hier kann man eine Bedingung angeben: @Cacheable(cacheNames="book", condition="#name.length() < 32")
  • Ist eine Methode mit @CachePut annotiert, kann man den Cache selbst füllen, bzw. Werte überschreiben.
  • Ist eine Methode mit @CacheEvict annotiert, etwa @CacheEvict(cacheNames="books", allEntries=true) public void clear() wird der Cache gelöscht.

Ausblick (2/2)

Das Spring Framework kann automatisch diverse Caching-Implementierung nutzen.

  • Standardmäßig ist es eine ConcurrentHashMap.
  • Unterstützt werden u.a.: EhCache 2.x, Hazelcast, Infinispan, Couchbase, Redis, Caffeine.
  • Spring erkennt anhand des Eintrags im Klassenpfad, was gewünscht ist.

In der application.[properties|yml] lassen sich dann Dinge wie Lebensdauer, Größe, etc. extern konfigurieren.

JMX in Spring Boot nutzen

Die Java Management Extensions (JMX)

  • Die Java Management Extensions (JMX) ist eine Spezifikation zur Verwaltung und Überwachung von Java-Anwendungen.
  • Teile der JMX Spezifikation sind ab Java 5 in der Java SE integriert.
  • Es gibt einen JMX Agent, an dem Managed Beans angemeldet werden.
  • Von außen lassen sich diese Managed Beans erfragen.
  • JConsole ist ein GUI-Programm im JDK, das zur Verwaltung von MBeans verwendet werden kann.

JMX in Spring Boot

Spring nutzt drei Annotationen, um Managed Beans mit ihren Zuständen und Operationen beim JMX Agent zu registrieren:

@ManagedResource
Definiert mit der Typ-Annotation die Managed Bean
@ManagedAttribute
Definiert ein Attribut (Setter/Getter)
@ManagedOperation
Definiert eine Operation

Beispiel-Managed-Bean

package com.tutego.boot.actuator;

import java.util.HashMap;
import java.util.Map;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.stereotype.Component;

@Component
@ManagedResource
public class StringMapManagedBean {
  private final Map<String, String> map = new HashMap<>();

  @ManagedAttribute
  public int getSize() {
    return map.size();
  }

  @ManagedOperation
  public String get( String key ) {
    return map.get( key );
  }


  @ManagedOperation
  public String put( String key, String value ) {
    return map.put( key, value );
  }

  public void remove( String key ) {
    map.remove( key );
  }
}

Annotationstyp @ManagedResource

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ManagedResource {

	@AliasFor("objectName")
	String value() default "";

	@AliasFor("value")
	String objectName() default "";

	String description() default "";

	int currencyTimeLimit() default -1;
	boolean log() default false;
	String logFile() default "";
	String persistPolicy() default "";
	int persistPeriod() default -1;
	String persistName() default "";
	String persistLocation() default "";
}

Springs eigene Managed Beans

Spring Boot veröffentlicht selbst diverse Managed Beans unter der Domäne org.springframework.boot.

  • org.springframework.boot:type=Endpoint,name=Health zum Beispiel für die Gesundheitsinformationen

Sie lassen sich abschalten mit management.endpoints.jmx.exposure.exclude=*.

Falls mehrere ApplicationContext exisistieren kommt es zum Namenskonflikt; der lässt sich mit management.endpoints.jmx.unique-names=true vermeiden.

Der Domain-Name lässt sich anpassen mit management.endpoints.jmx.domain.

JavaFX fliegt aus dem JDK

So schreibt es Oracle unter https://blogs.oracle.com/java-platform-group/the-future-of-javafx-and-other-java-client-roadmap-updates

Starting with JDK 11, Oracle is making JavaFX easier to adopt by making the technology available as a separate download, decoupled from the JDK. These changes clear the way for new contributors to engage in the open source OpenJFX community. Meanwhile, Oracle customers can benefit from continued commercial support for JavaFX in the Oracle JDK 8 through at least 2022.

With the Java Platform Module System in place since Java SE 9, it now more viable to decouple JavaFX from the JDK, in order to make it available as a separate download.  This will make it easier for developers using JavaFX to have more freedom and flexibility with the framework.  Moreover, with our focus on increasing the release cadence of OpenJDK, JavaFX needs to be able to move forward at a pace driven by the contributions from Oracle and others in the OpenJFX community. Oracle plans to implement this decoupling starting with Java 11 (18.9 LTS).

Warum ein Lambda-Ausdruck keine lokale Variablen/Parametervariablen beschreiben kann

Lambda-Ausdrücke können lokale Variablen nur lesen und nicht beschreiben. Der Grund hat etwas mit der Art und Weise zu tun, wo Variablen gespeichert werden: Objekt- und statische Variablen „leben“ auf dem Heap lokale Variablen und Parameter „leben“ auf dem Stack. Wenn nun Threads ins Spiel kommen ist es nicht unüblich, dass unterschiedliche Threads die Variablen vom Heap nutzen; dafür gibt es Synchronisationsmöglichkeiten. Allerdings kann ein Thread nicht auf lokale Variablen eines anderen Threads zugreifen, denn ein Thread hat erst einmal keinen Zugriff auf den Stack-Speicher eines anderen Threads. Grundsätzlich wäre das möglich, allerdings wollten die Oracle-Entwickler diesen Pfad nicht gehen. Beim Lesezugriff wird tatsächlich eine Kopie angelegt, sodass sie für einen anderen Thread sichtbar ist.

Die Einschränkung, dass äußere lokale Variablen von Lambda-Ausdrücken nur gelesen werden können, ist an sich etwas Gutes, denn die Beschränkung minimiert Fehler bei nebenläufiger Ausführung von Lambda-Ausdrücken: arbeiten mehrere Threads Lambda-Ausdrücke ab, und beschreiben diese eine lokale Variable, müsste andernfalls eine Thread-Synchronisation her.

Grundsätzlich verbietet das Schreiben von lokalen Variablen aus Lambda-Ausdrücken heraus aber nicht jede Programmiersprache. In C# kann ein Lambda-Ausdruck lokale Variablen beschreiben, sie leben dann nicht mehr auf dem Stack.