JAX Blog

Zehn Must-have-
Java-Bibliotheken

Nützlich für den Alltag

Mar 9, 2021

Java-Entwickler können ein Lied davon singen: heruntergeladene Bibliotheken, die nicht funktionieren oder das Programm zum Absturz bringen. Andererseits kommen Entwickler um gewisse Java-Bibliotheken nicht herum, wenn sie Wert auf Komfort legen.

Es kann nicht schaden, über den Tellerrand zu blicken, um einen eleganteren Code zu schreiben. Denn reiner Java-Code kann verglichen mit Java-Bibliotheken zum Teil umständlich sein. Aus diesem Grund dreht sich hier alles um zehn Java-Bibliotheken, die das Coden erleichtern.

Apache Commons

Apache Commons ist eine Sammlung mehrerer Komponenten, die von der Apache Software Foundation entwickelt werden. Zu den bekanntesten Komponenten zählt sicherlich Apache Commons Lang, die das Basispaket Java.lang erweitert. Darin befinden sich Klassen, um die Zeit und das Datum zu formatieren (apache.lang.time). Eine weitere beliebte Klasse nennt sich StringEscapeUtils. Damit können Strings mit Escape-Zeichen versehen werden. Die Klasse StringUtils stellt hingegen mehrere Methoden zur Bearbeitung von Strings zur Verfügung.

Einen Haufen Zeit erspart die Apache-Commons-Configuration-Komponente. Damit lassen sich im Nu Konfigurationsdateien erstellen und mit Schlüssel-Wert-Paaren versehen. Auch das Parsen der Konfigurationsdateien lässt sich leicht bewerkstelligen.

Verschaffen Sie sich den Zugang zur Java-Welt mit unserem kostenlosen Newsletter!

Lohnenswert ist zudem die Apache-Commons-Crypto-Bibliothek, mit der sich mit wenig Aufwand und Code Variablen ver- oder entschlüsseln lassen. Eine weitere empfehlenswerte Komponente ist Apache Commons Numbers. Sie beinhaltet mehrere Klassen, um Zahlen gemäß der Zahlenart zu formatieren. So ermöglicht die Klasse Complex das Erstellen von komplexen Zahlen, während die Klasse Fraction das Rechnen mit Brüchen erlaubt.

CalendarFX

Wer einen Terminplaner für eine Arztpraxis oder ein Ticketsystem implementieren möchte, der wird sich sicherlich Gedanken um die Ausgabe von Kalendern und um das Festlegen eines Termins machen. Hierbei kann das Framework CalendarFX, das sich in eine JavaFX-Anwendung einbinden lässt, nützlich sein (Abb. 1). Die CalendarFX-Bibliothek wird zurzeit in der Version 8 angeboten.

 



Abb. 1: Das CalendarFX Framework ermöglicht dem Anwender eine interaktive Sitzung

 

Google Guava

Ähnlich wie Apache Commons hat sich Google das Ziel gesetzt, die Effizienz bei der Produktion zu steigern. Mit Google Guava lässt sich der Code reduzieren sowie die Lesbarkeit und die Geschwindigkeit beim Entwickeln steigern. Die Guava-Bibliotheken verfügen über Stringverarbeitung, Collections, Graphen, Hashcodes, Exception Handling, Multithreading sowie weitere Bibliotheken. Aktuell ist Google Guava in der Version 30.0 verfügbar.

 

Apache Log4j 2

Zu den fortgeschrittenen Logging-Bibliotheken zählt Log4j 2. Deren Leistung kann sich sehen lassen. Denn je mehr Threads der Computer hat, auf dem die Anwendung läuft, desto mehr kann geloggt werden. Log4j 2 gibt es derzeit in der Version 2.14.0. Listing 1 zeigt, wie Log4j 2 eine Klasse loggt. Darüber hinaus verfügt Log4j 2 über eine Debug-Funktion.

 
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Sailboat {
  private static final Logger logger = LogManager.getLogger(Sailboat.class);
    public void move () {
    logger.info("sailing");
   }
}

 

JFXtras

Bei JFXtras handelt es sich um eine Bibliothek für die JavaFX-Anwendung [5]. Diese Bibliothek stellt unter anderem Steuerelemente wie Accordion sowie diverse Datum- und Zeit-Picker zur Verfügung. Daneben gibt es Anzeigenelemente, die für ein Dashboard nützlich sein können. In Listing 2 wird eine JavaFX-Anwendung erstellt, die die Klasse MainView startet.

 
import javafx.application.Application;
import javafx.stage.*;

public class App extends Application
{
  public static void main (String[] args)
  {
     launch(args);
  }

    public void start(Stage primaryStage) {
       MainView mainView = new MainView();
       mainView.show(primaryStage);   
  }
}

 

Das Accordion (Abb. 2) wird in der Klasse MainView wie ein anderes JavaFX-Element vom Typ Node erzeugt und anschließend im VBox-Container gespeichert (Listing 3).

 
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import jfxtras.scene.control.AccordionPane;

public class MainView {
  private AccordionPane accordionPane;
  private VBox pane;
  public MainView() {
    setRootNode();
    three();
    createWindow();
 }
  public void createWindow() {
    this.pane = new VBox(10,this.accordionPane );
}
  public void setRootNode() {
    this.accordionPane = new AccordionPane();
    this.accordionPane.setPrefSize(800.0, 600.0);
  }

public void three() {
  this.accordionPane.addTab("test1", new Label("test 1"));
  this.accordionPane.addTab("test2", new Label("test 2"));
  this.accordionPane.addTab("test3", new Label("test 3"));
}
public void show(Stage stage) {
  Scene scene = new Scene(this.pane);
  stage.setTitle("jfxtras");
  stage.setScene(scene);
  stage.setResizable(true);
  stage.setMaximized(true);
  stage.initStyle(StageStyle.TRANSPARENT);
    stage.show();
 }
}

 



Abb. 2: Ohne ein CSS-Stylesheet sieht das Accordion ziemlich langweilig aus

 

BootstrapFX

Bootstrap gibt es nicht nur für Webseiten, sondern auch für JavaFX-Anwendungen. So beinhaltet BootstrapFX 0.2.4 jede Menge vordefinierte CSS-Klassen für die einzelnen JavaFX-Elemente. BootstrapFX muss lediglich in die JavaFX-Anwendung eingebunden werden. Anschließend lassen sich die JavaFX-Elemente durch Setzen von vordefinierten CSS-Klassen stylen. Listing 4 zeigt die Klasse MainView, die so ähnlich aufgebaut ist wie in Listing 3, nur dass hier der VBox-Container einen Button beherbergt (Abb. 3).

 

 
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import org.kordamp.bootstrapfx.scene.layout.Panel;

public class MainView {
  private Button button;
  private VBox pane;
  public MainView() {
    createButton();
    createWindow();
}
public void createWindow() {
  this.pane = new VBox(10,this.button );
}
public void createButton() {
  this.button = new Button("Click me!");
  this.button.getStyleClass().setAll("btn","btn-danger");
}
public void show(Stage stage) {
  Scene scene = new Scene(this.pane);
  scene.getStylesheets().add("bootstrapfx.css");
    stage.setTitle("Bootstrap");
    stage.setScene(scene);
    stage.setResizable(true);
    stage.setMaximized(true);
    stage.initStyle(StageStyle.TRANSPARENT);
    stage.setWidth(900);
    stage.setHeight(600);
    stage.show();
  }
}

 



Abb. 3: Das Aussehen des Buttons wird von Bootstrap festgelegt

 

Bouncy Castle

Bouncy Castle ist eine kostenlose Crypto-Bibliothek, mit der sich Text ver- und entschlüsseln lässt. Es verfügt über zahlreiche Klassen und Schnittstellen, die in der Javadoc-Dokumentation der Quelldatei erläutert werden. Eine Einführung in Bouncy Castle befindet sich in der Quelldatei unter bctls-jdk15on-167/docs/specifications.html. Seit November dieses Jahres ist Bouncy Castle in der Version 1.67 verfügbar.

Listing 5 zeigt, wie Text mit Bouncy Castle verschlüsselt wird. In diesem Beispiel entspricht die Länge der Variable KeyString der Länge des zu verschlüsselnden Texts, da es sonst zu einem Laufzeitfehler kommen würde.

 

 
import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.modes.*;
import org.bouncycastle.crypto.params.*;

public class CryptoTest {
  private static final String keyString = "keyfrase";
  private static final String inputString = "password";

  public BlockCipher createEngine() {
    BlockCipher engine = new DESEngine();
    return engine;
  }

  public BufferedBlockCipher createCipher(BlockCipher engine) {
    BufferedBlockCipher cipher = new PaddedBlockCipher(new CBCBlockCipher(engine));
    return cipher;
  }

  public void runExample() {
    BlockCipher engine = createEngine();
    BufferedBlockCipher cipher = createCipher(engine);
    byte[] key = keyString.getBytes();
    byte[] input = inputString.getBytes();
    cipher.init(true, new KeyParameter(key));
    byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
    int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
  try
  {
    cipher.doFinal(cipherText, outputLen);
  }
  catch (CryptoException ce)
    {
    System.err.println(ce);
    System.exit(1);
    }
  }
}

 

Netty

Client-/Serveranwendungen sind nicht nur schwierig zu programmieren, sondern beanspruchen zum Teil etliche Zeilen an Code. Das NIO-Client-Server-Framework Netty wurde speziell entwickelt, um das Erstellen von Client-/Serveranwendungen zu erleichtern. Im November 2020 erschien das neue Release 4.1.54.

Mit Netty lassen sich sowohl Blocking Sockets als auch Non-Blocking Sockets einsetzen. Ein Vorteil von Non-Blocking IO ist, dass viele Clients zur selben Zeit bedient werden. Außerdem kommen hierbei Channels sowie ByteBuffer-Objekte zum Einsatz. Der Blocking-Modus hingegen bevorzugt Streams sowie Bytearrays. Listing 6 zeigt einen Server mit Non-Blocking IO.

 

 
import java.net.SocketAddress;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
    
public class NettyServer {
  private int port;
  
  public NettyServer(int port) {
    this.port = port;
    System.out.println("server running on port: "+ port);
  }
  
  public void run() throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
      .channel(NioServerSocketChannel.class)
      .childHandler(new ChannelInitializer<SocketChannel>() {
        public void initChannel(SocketChannel ch) throws Exception {
          ch.pipeline().addLast(new NettyServerHandler());
          System.out.println("Client-Adress: "+ ch.remoteAddress().getHostString());
        }
      })
      .option(ChannelOption.SO_BACKLOG, 128)          
      .childOption(ChannelOption.SO_KEEPALIVE, true);
      ChannelFuture f = b.bind(this.port).sync();
      f.channel().closeFuture().sync();
    } finally {
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }
  
  public static void main(String[] args) throws Exception {
    int port = 8080;
    if (args.length > 0) {
       port = Integer.parseInt(args[0]);
    }

    new NettyServer(port).run();
  }
}

 

In Listing 6 wird der Server NettyServer durch die Klasse ChannelFuture an den Port gebunden, der im zugehörigen Konstruktor definiert ist:

ChannelFuture f = b.bind(this.port).sync();

Kanäle zu den Clients werden durch den Aufruf der Methode initChannel erstellt. Zusätzlich gibt die Methode initChannel die Adresse des Clients aus. Außerdem verarbeitet der Server die Clients gleichzeitig, indem Multithreading eingesetzt wird. Dafür ist die Klasse NioEventLoopGroup verantwortlich. Was genau bei Anfragen des Clients erfolgt, regelt der Handler, in diesem Beispiel der NettyServerHandler (Listing 7).

 

 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ((ByteBuf) msg).release();
  }

  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}

 

Sobald der Server Daten vom Client erhält, wird die Methode channelRead aufgerufen. Jedoch werden die Daten vom Client in Listing 7 nicht verarbeitet, sondern stattdessen verworfen. Die andere Methode, genannt exceptionCaught, kommt dann zum Einsatz, wenn die Verbindung zum Client abbricht. Sie macht daraufhin den Kanal zum Client dicht.

Der Client selbst wird in Listing 8 realisiert. Der Hostname und die Portnummer des Servers sind schon bekannt, sodass ein Kanal zum Server mittels der Klasse ChannelFuture erstellt wird.

 

 
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.bootstrap.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.*;
import io.netty.channel.*;
import io.netty.buffer.ByteBuf;

public class NettyClient {
  public static void main(String[] args) throws Exception {
    String host = "localhost";
    int port =8080;
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
      Bootstrap b = new Bootstrap();
      b.group(workerGroup);
      b.channel(NioSocketChannel.class);
      b.option(ChannelOption.SO_KEEPALIVE, true);
      b.handler(new ChannelInitializer<SocketChannel>() {
        public void initChannel(SocketChannel ch) throws Exception {
          ch.pipeline().addLast(new NettyClientHandler());
        }
      });
      ChannelFuture f = b.connect(host, port).sync();
      f.channel().closeFuture().sync();
    } finally {
      workerGroup.shutdownGracefully();
    }
  }
}

 

Das Verarbeiten der Daten, die der Server an den Client sendet, wird beim Client im Handler namens NettyClientHandler umgesetzt (Listing 9). Dabei gibt die Methode channelRead Nachrichten des Servers als String aus.

 

 
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.*;
import io.netty.buffer.ByteBuf;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf buffer = (ByteBuf) msg;
    try {
      for (int i = 0; i < buffer.capacity(); i ++) {
        byte b = buffer.getByte(i);
        System.out.println((char) b);
      }
      ctx.close();
    } finally {
       buffer.release();
    }
  }
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}

 

JAXB

Mit Jaxb lassen sich XML-Repräsentationen in Java-Objekte umwandeln und umgekehrt. Aktuell ist Jaxb in der Version 2.3.1 verfügbar. Angenommen, ihr braucht eine XML-Datei, die die eigene Musik-CD-Sammlung darstellt. Dann erstellt ihr zunächst eine Java-Klasse vom Typ AudioCD und legt die Attribute der AudioCD fest (Listing 10). Außerdem fügt ihr bei Attributen, die im XML-Dokument erscheinen sollen, XML-Annotationen @Xml … hinzu.

 

 
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlType( propOrder = { "artist", "title", "year", "songlist"} )
@XmlRootElement(name = "Audiocd")
public class AudioCD {
  private String title;
  private String artist;
  private int year;
  private ArrayList<String> songlist;
  
  @XmlElement(name = "Album")
  public String getTitle() {
    return title;
  }

  public void setTitle(String t) {
    this.title = t;
  }

  @XmlElement(name = "Artist")
  public String getArtist() {
    return artist;
  }

  public void setArtist(String i) {
    this.artist = i;
  }
   
  @XmlElement(name = "Year")
  public int getYear() {
    return year;
  }

  public void setYear(int j) {
    this.year = j;
  }

  @XmlElement(name = "Song")
  public void setSonglist(ArrayList<String> list) {
    this.songlist = list;
  }

  public ArrayList<String> getSonglist() {
    return songlist;
  }
}

 

In diesem Beispiel besteht die CD-Sammlung aus mehreren Audio-CDs. Um die Liste mit den Audio-CDs im XML-Dokument darzustellen, wird eine weitere Klasse namens MusicDB benötigt (Listing 11).

 

 
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "CD Collection")
public class MusicDB {

  private ArrayList<AudioCD> cdList;
  private String name;
  private String location;

  @XmlElement(name = "CD")
  public void setCdList(ArrayList<AudioCD> l) {
    this.cdList = l;
  }
  public ArrayList<AudioCD> getCdList() {
    return cdList;
  }

  @XmlElement(name = "Music Collection")
  public String getName() {
    return name;
  }

  
  public void setName(String name) {
    this.name = name;
  }

  @XmlElement(name = "Location")
  public String getLocation() {
    return location;
  }

  public void setLocation(String location) {
    this.location = location;
  }
}

 

Listing 12 zeigt, wie Audio-CDs in der CD-Sammlung als ArrayList mit dem Namen mdb gespeichert werden. Außerdem findet in Listing 12 die Konvertierung der Java-Objekte in ein XML-Dokument statt (Abb. 4).

 

 
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class MyMusicDB {
  private static final String MUSICDB_XML = "music-jaxb.xml";
  public static void main(String[] args) throws IOException {
    try {
ArrayList<AudioCD> cds = new ArrayList<AudioCD>();
AudioCD cd1 = new AudioCD();
cd1.setTitle("New Adventures in Hi-Fi");
cd1.setArtist("REM");
cd1.setYear(1996);
ArrayList<String> s1= new ArrayList<>();
s1.add("How the West Was Won");
s1.add("The Wake-Up Bomb");
s1.add("New Test Leper");
s1.add("Undertow");
s1.add("E-Bow the Letter");
s1.add("Leave");
s1.add("Departure");
s1.add("Bittersweet Me");
s1.add("Be Mine");
s1.add("Binky the Doormat");
s1.add("Zither");
s1.add("So Fast, So Numb");
s1.add("Low Desert");
s1.add("Electrolite");
cd1.setSonglist(s1);
cds.add(cd1);

AudioCD cd2 = new AudioCD();
cd2.setTitle("Waking Up");
cd2.setArtist("OneRepublic");
[...]
MusicDB mdb = new MusicDB();
mdb.setName("My Music Collection");
mdb.setLocation("Living Room");
mdb.setCdList(cds);

JAXBContext context = JAXBContext.newInstance(MusicDB.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(mdb, System.out);
m.marshal(mdb, new File(MUSICDB_XML));
}
    catch( JAXBException e )
    {
       e.printStackTrace();
    }
  }
}

 



Abb. 4: Nach der Konvertierung kann die XML-Darstellung der CD-Sammlung betrachtet werden

 

JSON Simple

Manchmal sind selbst einfache Tasks wie das Erstellen von JSON-Objekten in Java ziemlich kompliziert. Dank Googles Library lässt sich dieser Code vereinfachen. JSON Simple ist zurzeit in der Version 1.1.1 erhältlich, wurde jedoch seit 2012 nicht mehr aktualisiert. In Listing 13 ist zu sehen, wie ein JSON-Objekt erstellt und anschließend mit Daten befüllt wird.

 

 
import org.json.simple.JSONObject;

public void setJSONObjectParams() {
  JSONObject jObj = new JSONObject();
}

public void addToJson(JSONObject jObj, String key, String value){
  jObj.put(key,value);
}

 

Darüber hinaus kann JSON Simple JSON-Objekte parsen und JSON-Arrays erstellen. Die JSON-Objekte lassen sich verschachteln, indem das Wurzelelement selbst ein JSON-Objekt ist. Beim abzuspeichernden Schlüssel-Wert-Paar wird als Wert ein anderes JSON-Objekt eingefügt, wobei der Schlüssel einem String entspricht.

Alle News der Java-Welt:

Behind the Tracks

Agile, People & Culture
Teamwork & Methoden

Clouds & Kubernetes
Alles rund um Cloud

Core Java & Languages
Ausblicke & Best Practices

Data & Machine Learning
Speicherung, Processing & mehr

DevOps & CI/CD
Deployment, Docker & mehr

Microservices
Strukturen & Frameworks

Performance & Security
Sichere Webanwendungen

Serverside Java
Spring, JDK & mehr

Software-Architektur
Best Practices

Web & JavaScript
JS & Webtechnologien

Digital Transformation & Innovation
Technologien & Vorgehensweisen

Domain-driven Design
Grundlagen und Ausblick

Spring Ecosystem
Wissen in Spring-Technologien

Web-APIs
API-Technologie, Design und Management