Archiv der Kategorie: Allgemein

Offene Java/JavaScript-Seminare in 2018

tutego Schulungsraum


Java Grundlagen (›JAVA1‹)

22.–26. Oktober 2018 (KW 43), 12.–16. November 2018 (KW 46)



Java für Fortgeschrittene (›JAVA2‹)

8.–12. Oktober 2018 (KW 41), 26.–30. November 2018 (KW 48)



Spring Boot (›SPRINGBOOT‹)

15.–17. Oktober 2018 (KW 42), 5.–7. November 2018 (KW 45), 3.–5. Dezember 2018 (KW 49)



JavaScript für Web-Entwickler (›JAVASCRIPT‹)

15.–17. Oktober 2018 (KW 42), 5.–7. November 2018 (KW 45), 3.–5. Dezember 2018 (KW 49)

Eigene Doclets programmatisch ausführen – was ist neu in Java 10?

Das javadoc-Tool hat die Aufgabe, den Java-Quellcode auszulesen und die Javadoc-Kommentare zu extrahieren. Was dann mit den Daten passiert, ist die Aufgabe eines Doclets. Das Standard-Doclet von Oracle erzeugt die bekannte Struktur auf verlinkten HTML-Dateien, aber es lassen sich auch eigene Doclets schreiben, um etwa in Javadoc-Kommentaren enthaltene Links auf Erreichbarkeit zu prüfen oder UML-Diagramme aus den Typbeziehungen zu erzeugen. Hervorzuheben ist JDiff (http://javadiff.sourceforge.net/), was Differenzen in unterschiedlichen Versionen der Java-Dokumentation – wie neu hinzugefügte Methoden – erkennt und meldet.

Die Doclet-API ist Teil vom JDK, und deklariert Typen und Methoden, mit denen sich Module, Pakete, Klassen, Methoden usw. und deren Javadoc-Texte erfragen lassen. Allerdings gibt es zwei Doclet-APIs:

  • Im Paket javadoc.doclet (Modul jdk.javadoc) ist die aktuelle API.
  • Im Paket sun.javadoc ist die mittlerweile deprecated API, allerdings einfacher zu nutzen.

Beispiel

Im folgenden Beispiel wollen wir ein kleines Doclet schreiben, das Klassen, Attribute, Methoden und Konstruktoren ausgibt, die das Javadoc-Tag @since 10 tragen. So lässt sich leicht ermitteln, was in der Version Java 10 alles hinzugekommen ist. Doclets werden normalerweise von der Kommandozeile aufgerufen und dem javadoc-Tool übergeben, doch da es mit der Tool-Schnittstelle auch selbst geht, können wir ein Programm mit main(…)-Methode schreiben, das die Neuerungen auf der Konsole ausgibt.

package com.tutego.insel.tools;




import java.io.IOException;

import java.nio.file.*;

import java.util.Arrays;

import java.util.List;

import java.util.function.Predicate;

import java.util.stream.Collectors;

import javax.tools.*;

import com.sun.javadoc.*;




@SuppressWarnings( "deprecation" )

public class FindSinceTagsInJavadoc {




  public static class TestDoclet {




    public static boolean start( RootDoc root ) {

      Arrays.stream( root.classes() ).forEach( TestDoclet::processClass );

      return true;

    }




    private static void processClass( ClassDoc clazz ) {




      Predicate<Doc> hasSinceTag = doc -> Arrays.stream( doc.tags( "since" ) )

                                            .map( Tag::text ).anyMatch( "10"::equals );




      if ( hasSinceTag.test( clazz ) )

        System.out.printf( "%s -- Neuer Typ%n", clazz );




      Arrays.stream( clazz.fields() ).filter( hasSinceTag )

            .forEach( f -> System.out.printf( "%s -- Neues Attribut%n", f ) );




      Arrays.stream( clazz.methods() ).filter( hasSinceTag )

            .forEach( m -> System.out.printf( "%s -- Neue Methode%n", m ) );




      Arrays.stream( clazz.constructors() ).filter( hasSinceTag )

            .forEach( c -> System.out.printf( "%s -- Neuer Konstruktor%n", c ) );

    }

  }




  public static void main( String[] args ) throws IOException {




    Path zipfile = Paths.get( System.getProperty( "java.home" ), "lib/src.zip" );

    try ( FileSystem srcFs = FileSystems.newFileSystem( zipfile, null ) ) {

      Predicate<Path> filesToIgnore = p ->  p.toString().endsWith( ".java" ) &&

                                          ! p.toString().startsWith( "/javafx" ) &&

                                          ! p.toString().startsWith( "/jdk" ) &&

                                          ! p.toString().endsWith( "module-info.java" ) &&

                                          ! p.toString().endsWith( "package-info.java" );




      List<Path> paths = Files.walk( srcFs.getPath( "/" ) )

                              .filter( filesToIgnore ).collect( Collectors.toList() );




      DocumentationTool tool = ToolProvider.getSystemDocumentationTool();

      try ( StandardJavaFileManager fm = tool.getStandardFileManager( null, null, null ) ) {

        Iterable<? extends JavaFileObject> files = fm.getJavaFileObjectsFromPaths( paths );

        tool.getTask( null, fm, null, TestDoclet.class, List.of( "-quiet" ), files ).call();

      }

    }

  }

}

Unsere main(…)-Methode bindet zunächst das ZIP-Archiv vom JDK mit den Quellen als Dateisystem ein. Im nächsten Schritt durchsuchen wir das Dateisystem nach den passenden Quellcodedateien und sammeln sie in eine Liste von Pfaden. Jetzt kann das DocumentationTool mit unserem Doclet konfiguriert und aufgerufen werden. call() startet das Parsen aller Quellen und führt anschließend zum Aufruf von start(RootDoc) unseres Doclets.

In unserer start(…)-Methode laufen wir über alle ermittelten Typen und rufen dann processClass(ClassDoc) auf. Dort passiert der eigentliche Test. Die Metadaten kommen dabei über diverse XXXDoc-Typen. Ein Predicate zieht den Tag-Test heraus.

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)

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)

Iterator vom Stream besorgen

Sich einen Iterator von einem Stream geben zu lassen ist nützlich, weil dann der Zeitpunkt des Konsumierens vom Zeitpunkt des Stream-Aufbaus getrennt werden kann. Es ist nicht einfach mehrere Streams gleichzeitig abzulaufen, doch mit Iteratoren funktioniert das. Zudem lässt sich in Stream mit einer Generator-Funktion einfach aufbauen, in Iterator aber nicht.

Beispiel: Aus zwei Streams sollen jeweils alterrnierend das nächste Element konsumiert und ausgegeben werden.

Iterator<Integer> iterator1 = Stream.of( 1, 3, 5 ).iterator();

Iterator<Integer> iterator2 = Stream.of( 2, 4, 6, 7, 8 ).iterator();




while ( iterator1.hasNext() || iterator2.hasNext() ) {

  if ( iterator1.hasNext() )

    System.out.println( iterator1.next() );

  if ( iterator2.hasNext() )

    System.out.println( iterator2.next() );

}

Iterator ist ein Datentyp, der häufig in der Rückgabe verwendet wird, seltener als Parametertyp. Mit stream.iterator() ist es aber möglich, die Daten vom Stream genau an solchen Stellen zu übergeben. Einen Stream in einen Iterator zu konvertieren, um diesen dann mit hasNext()/next() abzulaufen, ist wenig sinnvoll, hierfür bietet sich genauso gut forEach(…) auf dem Stream an.

Stream ist nicht Iterable

Der Typ Stream bietet eine Methode iterator(), erweitert jedoch die Schnittstelle Iterable nicht. In der Javadoc ist bei iterator() die Bemerkung „terminale Operation“ vermerkt, denn der Iterator saugt den Stream leer, sodass ein zweiter iterator()-Aufruf auf einem Stream nicht möglich ist. Bei Klassen, die Iterable implementieren, muss ein Aufruf von iterator() beliebig oft möglich sein. Bei Streams ist das nicht gegeben, da die Streams selbst nicht für die Daten stehen wie eine Collection, die daher Iterable ist.

Iterator in Stream umwandeln

Ist auf der anderen Seite ein Iterator gegeben, lässt sich dieser nicht direkt in einen Stream bringen. Iteratoren können wie Streams unendlich sein, und es gibt keinen Weg, die Daten eines Iterators als Quelle zu benutzen. Natürlich ist es möglich, den Iterator abzulaufen und daraus einen neuen Stream aufzubauen, dann muss der Iterator aber endlich sein.

Beispiel: Die Methode ImageIO.getImageReadersBySuffix(String) liefert einen Iterator von ImageReader-Objekten – sie sollen über einen Strom zugänglich sein:

Builder<ImageReader> builder = Stream.builder();
 for ( Iterator<ImageReader> iter = ImageIO.getImageReadersBySuffix( "jpg" );
       iter.hasNext(); )
   builder.add( iter.next() );
 Stream<ImageReader> stream = builder.build();
 System.out.println( stream.count() );    // 1

Einen anderen Weg geht StreamSupport.

Ist eine alte Enumeration gegeben, hilft Collections.list(enumeration).stream(), denn list(…) liefert eine ArrayList mit allen Einträgen; list(Iterator) gibt es da hingegen nicht.

iterator() von BaseStream und PrimitiveIterator.OfXXX

Die Schnittstelle Stream deklariert keine abstrakte iterator()-Methode, sondern bekommt die Methode vom Obertyp BaseStream vererbt. BaseStream ist insgesamt Basistyp von:

  • Stream<T>, DoubleStream, IntStream, LongStream

Alle diese drei Typen haben damit iterator()-Methdoden, doch die Rückgaben sind unterschiedlich:

Typ iterator()-Methdoden und Rückgabe
Stream<T> Iterator<T> iterator()
DoubleStream PrimitiveIterator.OfDouble iterator()
IntStream PrimitiveIterator.OfInt iterator()
LongStream PrimitiveIterator.OfInt iterator()

Unterschiedliche Rückgaben der Iteratoren

PrimitiveIterator ist eine Schnittstelle aus dem Paket java.util mit eben drei inneren statischen Schnittstellen: OfDouble, OfInt und OfLong, die PrimitiveIterator erweiten. Jeder dieser drei inneren Typen hat eigene, aber symmetrische Methoden:

  • default void forEachRemaining(Consumer<? super WrapperTyp> action)
  • default void forEachRemaining(WrapperTypConsumer action)
  • default WrapperTyp next()
  • PrimitiverTyp nextPrimitiverTyp()

Statt WrapperTyp und PrimitiverTyp ist dann Double, Integer, Long und double, int, double einzusetzen.

Beispiel: Ein vom Stream abgeleiter Iterator besorgt Zahlen. Diese werden in einem anderen Stream eingesetzt.

PrimitiveIterator.OfInt counter =

  IntStream.iterate( 1, Math::incrementExact ).iterator();

Stream.of( "Telegram", "WhatsApp", "Facebook Messenger", "Insta" )

      .map( s -> counter.nextInt() + ". " + s )

      .forEach( System.out::println );

Java sollte „Inkubator“-Features bekommen.

Das bedeutet, es gibt Syntaxerweiterungen, aber die sind erst mal zum Testen.

http://openjdk.java.net/jeps/12

An incubating language or VM feature is a new feature of the Java SE Platform that is fully specified, fully implemented, and yet impermanent. It is available in a JDK feature release to provoke developer feedback based on real world use; this may lead to it becoming permanent in a future Java SE Platform.

In der Mailliste fragen die Eclipse-Bauer schon nach, ob das verpflichtend ist … es wäre blöd, wenn man sich bei den JDT viel Mühe gibt, und dann wird das doch nicht übernommen: http://mail.openjdk.java.net/pipermail/jdk-dev/2018-February/000642.html

JEP-12 ist allerdings noch nicht bestätigt.

 

JEP Draft für „Raw String Literals“ in Java

In der Diskussion ist der „Back-Tick“, sodass es so aussehen würde:

File Paths Example

Traditional String Literals Raw String Literals
Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar");
Runtime.getRuntime().exec(`"C:\Program Files\foo" bar"`);

Multi-line Example

Traditional String Literals Raw String Literals
String html = "<html>\n"
              "    <body>\n" +
              "		    <p>Hello World.</p>\n" +
              "    </body>\n" +
              "</html>\n";
String html = `<html>
                   <body>
                       <p>Hello World.</p>
                   </body>
               </html>
              `;

Regular Expression Example

Traditional String Literals Raw String Literals
System.out.println("this".matches("\\w\\w\\w\\w"));
System.out.println("this".matches(`\w\w\w\w`));

Output:

true

Polyglot Example

Traditional String Literals Raw String Literals
String script = "function hello() {\n" +
                "   print(\'\"Hello World\"\');\n" +
                "}\n" +
                "\n" +
                "hello();\n";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval(script);
String script = `function hello() {
                    print('"Hello World"');
                 }
hello();
            `

ScriptEngine engine = new ScriptEngineManager().getEngineByName(„js“); Object obj = engine.eval(script);

Output:

"Hello World"

Database Example

Traditional String Literals Raw String Literals
String query = "SELECT EMP_ID, LAST_NAME\n" +
               "FROM EMPLOYEE_TBL\n" +
               "WHERE CITY = 'INDIANAPOLIS'\n" +
               "ORDER BY EMP_ID, LAST_NAME;\n";
String query = `SELECT EMP_ID, LAST_NAME
                FROM EMPLOYEE_TB
                WHERE CITY = 'INDIANAPOLIS'
                ORDER BY EMP_ID, LAST_NAME;
               `;

Ich bin gespannt, ob das in Java 10 kommen wird. Ich dachte schon, dass Java 10 Release wird eher klein, doch zusammen mit den var-Variablen sind das zwei kleine, aber bedeutende Sprachänderungen.

Details unter http://openjdk.java.net/jeps/8196004.

Aktuelles zu Java 10

Nach Java 9 wird uns Java 10 beglücken. Die Zeitleiste sieht so aus:

2017/12/14 Rampdown Phase One
2018/01/11 All Tests Run
2018/01/18 Rampdown Phase Two
2018/02/22 Final Release Candidate
2018/03/20 General Availability

Java 10 ist laut http://openjdk.java.net/projects/jdk/10/ ab heute in der „Rampdown Phase One“ (http://openjdk.java.net/projects/jdk8/milestones#Rampdown_start):

  • Rampdown — Phases in which increasing levels of scrutiny are applied to incoming changes. In phase 1, only P1-P3 bugs can be fixed. In phase 2 only showstopper bugs can be fixed.

Neuerungen dir wir für Java 10 erwarten können, beschrieben im JEP:

286: Local-Variable Type Inference
296: Consolidate the JDK Forest into a Single Repository
304: Garbage-Collector Interface
307: Parallel Full GC for G1
310: Application Class-Data Sharing
312: Thread-Local Handshakes
313: Remove the Native-Header Generation Tool (javah)
314: Additional Unicode Language-Tag Extensions
316: Heap Allocation on Alternative Memory Devices
317: Experimental Java-Based JIT Compiler
319: Root Certificates
322: Time-Based Release Versioning

Es wird auch Java 10, Java 11, usw. heißen und nicht, wie vorher angedacht, Java 18, etc. nach Jahreszahlen. Aus dem http://openjdk.java.net/jeps/322:

  • $FEATURE is incremented every six months: The March 2018 release is JDK 10, the September 2018 release is JDK 11, and so forth.