automatisches Scaling von JavaFX2.0-Controls

Getestet habe ich mit folgendem Code-Block unter JavaFX2.1-Beta unter Mac OS X 10.7 und Java 6:

 HBox p = HBoxBuilder.create().children(btn, btn1).build();
 HBox.setHgrow(btn, Priority.NEVER);
 HBox.setHgrow(btn1, Priority.ALWAYS);

Folgende Liste enthält die JavaFX2-Controls, die ihre Grösse in einer HBox (oder anderem Layout, welches die enthaltenen Elemente skaliert) automatisch anpasst:

- javafx.scene.control.ListView - javafx.scene.control.Slider - javafx.scene.control.TableView - javafx.scene.control.TextField - javafx.scene.control.TreeView

Diese JavaFX2-Controls benötigen das explizite Setzen der maximalen Breite bzw. Höhe:

- javafx.scene.control.Button - javafx.scene.control.ProgressBar - javafx.scene.control.ToggleButton

Bei folgenden Gui-Controls macht Scanning keinen Sinn:

- javafx.scene.control.CheckBox - javafx.scene.control.Label - javafx.scene.text.Text

javafx.scene.control.TextArea ignoriert Hgrow komplett

Tipps und Tricks zu JavaFX2.0

Ich lerne gerade JavaFX2.0 kennen und sammle hier meine Erkenntnisse zu einzelnen Themen. Wieso JavaFX2.0? Hauptgrund sind zum Einen die Erwartungen der Benutzer, die mit einem simplen Formular und ein paar Bildchen nicht mehr zu begeistern sind und zum Anderen die unüberschaubare Anzahl von Web-Frameworks, die am Ende des Tages doch an Browser-Inkompatibilitäten scheitern.

Bei JavaFX2.0 geht es darum, die Produktivität zu erhöhen, in dem der Benutzer seine gewohnten Handgriffe schneller und zuverlässiger ausführen kann.

Dabei geht's um verschiedene Aspekte: Funktionalität, Zuverlässigkeit und Optik.

Los geht's mit Funktionalität:

JavaFX2.0 ist Java. Alles was in der Vergangenheit clientseitig funktioniert hat, ist immer noch korrekt. Dazu kommen starke Controls, wie WebView, TableView, TreeView und Toolbar und GUI-Elemente für die Organisation des GUIs, wie Accordion. Dazu gleich mehr ...

Zentral sind Properties und Bindings. Properties erinnern an getter-/setter-Methoden aus JavaBeans-Zeiten (also: gestern). Zusätzlich zur Basisfunktionalität der Kapselung bieten Properties Observable-Funktionalität und Support für Binding. Grundsätzlich können Properties über Binding verknüpft werden. Das bedeutet, dass bei der Änderung eines Properties andere Properties automatisch neu gesetzt (z.B. berechnet) werden. Mehr Infos hier. Beide Themen sind zentral für JavaFX.

Tipp zu Binding: für Layout nie Binding verwenden. Die Performance leidet und der Code wird unlesbar. Verwende die entsprechenden Panes für die optische Anordnung von GUI-Elementen. Optimal für Bindings sind die Datenstrukturen hinter Tabellen, Trees oder eigenen JavaFX2.0 GUI-Elementen. Der Callback in die GUI Aktualisierung entfällt, wenn Datenstrukturen direkt mit den zugehörigen Elementen verknüpft sind.

JavaFX2.0 kommt mit neuen Collections (List und Map-style), die einen über inhaltliche Änderungen informieren. Beide lassen sich von FXCollections aus herkömmlichen Collections erzeugen.

WebView

Hierbei handelt es sich um eine komplette webkit-basierte HTML-Engine inkl. JavaScript, die überall ins GUI integriert werden kann. Der Inhalt kann über eine URL oder direkt als HTML übergeben werden.

URL:

 WebView wv = new WebView();
 WebEngine we = wc.getEngine();
 we.load("http://...");

direkt als HTML:

 WebView wv = new WebView();
 WebEngine we = wc.getEngine();
 we.loadContent("...");

TableView

Im Unterschied zu Swing entfällt die Implementierung eines TableModels und die ganze optische Gestaltung ist in CSS ausgelagert. Somit beschränkt sich die Implementierung der TableView auf die Funktionalität.

Dank Generics können Tabellen einfacher gebaut werden als früher unter Swing. Dazu braucht man zuerst ein Model-Klasse, die eine Zeile der Tabelle repräsentiert. Dazu reicht im Prinzip ein POJO; in den Beispielen sieht man häufig Beans mit Properties. Dies hilft natürlich sehr, wenn man automagisch aktualisierte Werte braucht.

Wenn man das Model hat, definiert man Tabellenspalten und fügt sie der TableView hinzu. Im Unterschied zu früher ist Sortierung (auch nach mehreren Spalten [aktivierbar mit SHIFT] ) standardmässig integriert. Die Optik kann mit CSS angepasst werden.

TreeView

Ähnlich wie bei der TableView entfällt auch hier die explizite Model-Implementierung. Man baut einen Baum mit TreeItems mit der Methode node.getChildren().add(myNode). Als Label für ein TreeItem wird toString() auf dem enthaltenen Value verwendet.

ToolBar

Eine ToolBar bietet kaum Überraschungen. Es lassen sich Effekte auf eine ToolBar anwenden; sinnvoll ist dies nicht. Schön wäre es eine ToolBar detachable zu definieren. Dies scheint aber (noch) nicht zu gehen.

 

Nun zur Zuverlässigkeit:

Es lohnt sich Auswahllisten und Daten, die selten ändern oder gar historische Daten sind, lokal zu speichern (mindestens in memory, evtl. auch echt persistent). Sämtliche aus der Client-Server-Entwicklung bekannten Tricks sind hier nicht verkehrt. Einzig das Bauen von GUIs "auf Vorrat" lohnt sich weniger. Gegenüber Panel-basierten GUIs mit GridBagLayout ist die Performance-Steigerung bei JavaFX2.0 enorm.

Zum Schluss die Optik:

Keep it simple! Perfekte Farbkombinationen und Schatten werden nie entsprechend honoriert. Im Gegenteil! Die hier verwendete Zeit bekommt keine Budget-Position. Beim Bauen eines GUIs mit einfachen Layouts anfangen und Benutzer-Feedback abwarten. Mit JavaFX2.0 sind GUIs so schnell neu oder umgebaut und diese Arbeiten sind so non-invasiv, dass ein gewaltiges Investment in Optisches fehlgeleitet ist.

Layout

HBox und VBox sollten nicht sehr tief geschachtelt werden. Das kostet Performance. Wahrscheinlich hilft eine GridPane diese Verschachtelungen zu vermeiden. Verwende kein Binding damit ein GridPane sich der Grösse des Fensters anpasst.

Gradients

Farbübergänge lassen sich in JavaFX2 sehr einfach erzeugen. Es gibt zwei Klassen dafür: LinearGradient und RadialGradient mit dem jeweiligen Buildern dazu. Am Besten zu Enterprise GUIs passen üblicherweise dezente, lineare Farbübergänge im Graubereich (oben hell / unten etwas dunkler) auf Fensterebene. Mit einem dunkleren Farbübergang (oben dunkelgrau / unten schwarz) lässt sich ein Adobe Flash Effekt erzielen. Dies kann hilfreich sein, wenn man User dieser Plattform "abholen" will.

CSS

Einem GUI (egal ob browser-basiert oder window-basiert) liegt immer ein DOM-Baum zugrunde, der die Struktur dieses GUIs definiert. Will man nun einen Knotenpunkt (Node) innerhalb eines solchen DOMs finden, kann man entweder drüber iterieren (beginnend mit myScene.getRoot()) oder man sucht den Node direkt über seinen Namen:

 Node n = parentNode.lookup("...");

Dies setzt natürlich voraus, dass vorgängig mit myNode.setId("...") eine eindeutige Id vergeben wurde.

Diese Ids können dann in CSS-Files verwendet werden.

Hier ein paar Tricks, wie man einer Applikation einen einheitlichen, neuen Farbstil geben kann:

-fx-base:/* Basisfarbe für alle Komponenten */

-fx-text-base-color:/* Textfarbe */

-fx-accent: /* Standard Knöpfe ... */

-fx-focus-color: /*Farbe für die Anzeige des aktuellen Fokus */

-fx-mark: /* Farbe für Markierungen (CheckBoxen, Menüs, ...) */

Animationen

JavaFX2.0 ermöglicht es fast Alles irgendwie zu animieren. Aber man muss hier natürlich den Anwendungsfall im Auge behalten. Der Einsatz von Animationen in Spielen drängt sich ja meistens geradezu auf, aber in Business Applikationen ist davon abzuraten. Es mag die ersten paar Mal toll aussehen, aber schon nach kurzer Zeit nervt's den Benutzer. Es mag Fälle geben, in dem man den Benutzer mit einer Animation bspw. auf einen aktuell laufenden Prozess hinweisen möchte, aber im Zweifelsfall: Finger weg von Animationen!

Effekte

JavaFX bietet eine Vielzahl von Effekten. Hier gilt aber dasselbe, wie bei Animationen: nur mit besonders gutem Grund brauchen. Vielleicht eine Ausnahme sind Fade-Out und Fade-In. Sie können gut funktionieren, wenn sie dem Benutzer einen klaren Hinweis geben sollen (z.B."Ich brauche hier Input"). Aber dann sollten sie schnell sein (ca 250 ms) und konsistent verwendet werden.

Bei allen Aspekten rund um die Optik: eine kleine Bibliothek von vordefinierten Styles anlegen und diese dann entsprechend und konsequent verwenden. Das gibt ein einheitliches GUI. Unbedingt CSS einsetzen, damit die Optik später von einem CSS-Profi anpassbar ist. Niemand will im Java-Code die Optik pflegen.

JBoss 7 - Was ist neu?

JBoss ist ein sehr weit verbreiteter Application Server, der eigentlich nicht explizit vorgestellt werden muss. Fast jeder hatte irgendwann mal was damit zu tun. (Streng genommen ist JBoss kein Application Server, sondern Name einer Produktfamilie. Genauso wie WebSphere eigentlich kein spezifisches Produkt bezeichnet. Ich werde mir die Struktur der Produktfamilie in einem anderen Blog vornehmen)

Grundsätzlich gibt es die frei verfügbare Version von JBoss und die kostenpflichtige Fassung rund um JBoss EAP (Enterprise Application Plattform). Die freie Version ist EAP quasi immer einen technologischen Schritt voraus. Ein Blick auf JBoss AS 7 zeigt also was in EAP 6 kommen könnte. Da JBoss beim Wechsel auf Version 7 keinen evolutionären sondern einen revolutionären Schritt gemacht hat, möchte ich die wichtigsten Unterschiede hier zusammenfassen.

Es fängt damit an, dass AS7 praktisch komplett neu entwickelt wurde. Die etablierten Bausteine (z.B. Microcontainer) wurden ersetzt und durch eine grundsätzlich andere Architektur ersetzt. Zentraler Unterschied der neuen Architektur ist der modulare Aufbau, dank dem Komponenten erst bei Bedarf gestartet werden. In Profilen kann definiert werden, welche Komponenten anfänglich gestartet werden.

Das Modulsystem nennt sich JBoss Modules und ist ein eigenständiges Projekt, welches auch für andere Projekte eingesetzt werden kann (es geht in die Richtung der für Java 7 versprochenen Modullösung (JSR 294), welche nun bis Ende 2012 als Teil von Java 8 verschoben wurde). Unter Modul wird eine Komponente verstanden, die mit eigenen Ressourcen und Klassen daher kommt und Abhängigkeiten zu anderen Modulen hat. Diese Abhängigkeiten definieren, welche Funktionalitäten von Modul B Modul A sehen kann. Dies entspricht quasi einem 5. Visibility Level in Java, also einer Steigerung von 'public'. Dieser Ansatz löst massenweise Probleme: die Startup-Zeit und der memory footprint wurden massiv verringert. Viel wichtiger ist, dass Module jeweils mit eigenem ClassLoader kommen und die mühseligen ClassPath-Problematiken der Vergangenheit angehören.

JBoss 7 unterstützt Hibernate, Weld, Mojarra und viele weitere opensource Standards. JBoss 7 bietet JavaEE6 Support (Web-Profile; Full Profile folgt in 7.1 später dieses Jahr).

Eine weitere, gut sichtbare Änderung ist das neue GWT-basierte Admin-GUI. Es lässt sich darüber streiten, ob ein solches GUI notwendig war; aber besser aussehen tut's auf jeden Fall ;-)

Neu unterstützt JBoss AS 7 zwei Betriebsmodi: standalone und domain. Stand-alone entspricht ungefähr der bisherigen Betriebsweise. Dabei laufen alle Komponenten (Host Controller und einer oder mehrere managed Application Server auf derselben Maschine). Ein Deployment besteht aus dem Kopieren des WAR-Files nach /standalone/deployments. Dieses Verzeichnis wird vom Deployment Scanner überwacht. Via Filename-Anpassungen wird über den (Miss-)Erfolg von Deployments informiert (neue File-Endung '.deployed' oder '.failed'). Domain unterstützt die Verwaltung mehrerer gemanagte Server (auch auf unterschiedlichen Maschinen). Neben HC und AS kommt bei Domain auch noch ein Domain Controller zum Einsatz, der die einzelnen HCs überwacht. Die einzelnen Application Server werden üblicherweise in Servergruppen zusammengefasst, die betriebliche Funktionen haben (z.B. Test, Integration, Produktion). Diese Servergruppen können mehrere physikalische Server belegen. Applikationen werden in eine Domain geladen und können dann in den verschiedenen Umgebungen deployed werden (Achtung: Konfigurationsdetails wie DB, Log-Settings, ...). Ein Deployment via File copy ist nicht mehr möglich.

Eine weitere spannende Möglichkeit ist die Integration von JBoss in Unit und Integration Tests. Während in vielen Fällen Unit-Tests gar keinen laufenden Application Server mehr benötigen (Stichwort: POJO), gibt es doch noch einige Fälle in denen ein solcher notwendig wäre. Häufig scheitert es daran, dass "professionelle" Application Server nicht sinnvoll in Tests integriert werden können, da sie zu lange zum Starten brauchen, man nicht die entsprechenden Einstellungen mitgeben kann oder eine Integration in einen Testlauf einfach nicht machbar ist. Natürlich gibt es die Möglichkeiten Jetty für gewisse Tests einzubinden, aber der verhält sich wahrscheinlich leicht anders als das System, welches produktiv zum Einsatz kommt. Kurz: man will auf dem Server testen, welcher am Ende des Tages produktiv zum Einsatz kommen wird.

Last but not least, gibt es in JBoss 7 eine Kommandozeile (bin/jboss-admin.sh) für die Administration ohne WebGUI. Mittels Batch-Blöcken lassen sich mehrere Änderungen in einer Transaktion ausführen, um den Server nicht 'unterwegs' in inkonsistente Zustände zu bringen.

Entwickeln für NetBeans Plattform

Ich arbeite schon länger mit NetBeans, aber mit der Plattform für eigene Applikationen habe ich noch nie was gemacht. Es folgen meine Notizen für die ersten Schritte. NetBeans enthält in den Standard-Installation schon alles für die Entwicklung eigener client-seitiger Applikationen. Spannend: es ist auch möglich Maven-basierte NetBeans-Plugins zu schreiben. Aber ich probier's erst einmal klassisch:

Schritt 1

Neues Projekt erstellen: NetBeans Modules -> NetBeans Platform Application Nach einer Weile steht da ein Projekt, bestehend aus einem leeren Modulverzeichnis und einem Folder "Important Files". In letzterem ist ein ANT-Build File und drei Properties Files.

Schritt 2

Wenn ich das nun laufen lasse, wird ein zweites NetBeans gestartet. Die generierten Menüs sind recht umfassend für eine leere Applikation; die Taskleiste ist hingegen ziemlich leer.

Schritt 3

Irgendwie muss ich nun Funktionalität hinzufügen. Ein Rechts-Klick auf "Modules" scheint mir die Möglichkeit zu geben ein neues Modul zu bauen.

Schritt 4

Ich baue ein Login-Modul via "Add new ...". Zum neuen Modul kann ich ganz einfach ein Fenster hinzufügen und mit Mantisse das GUI bauen. Super einfach! Bevor ich nun weitermache, schaue ich mir die Videos auf http://platform.netbeans.org/tutorials/nbm-10-top-apis.html an.

Kommentare in Java-Klassen

Kommentare haben fast so einen schlechten Ruf wie Testklassen, dabei sind sie fast genauso wichtig wie diese. Schliesslich helfen beide dabei, wartbare Applikationen zu schreiben. Je nachdem ist man selbst der direkt Betroffene, wenn man in ein paar Monaten an dieser Applikation eine Anpassung machen muss und nicht mehr weiss, um welchen Spezialfall es sich hier genau handelt. Deshalb lohnt es sich hier ein bisschen Zeit zu investieren. Ich möchte kurz meine Erfahrungen zum Thema 'Sourcecode kommentieren' beschreiben.

Nie den Sourcecode 1:1 kommentieren

Es bringt nichts im Kommentar zu schreiben, was bereits programmiert wurde. Wieso sollte jemand folgendes lesen wollen?

//füge Adresse in Adressliste ein
adresslist.add(adress);

Das hilft niemandem weiter. Kommentare müssen einen Mehrwert liefern. Also macht es zum Beispiel Sinn vor einem komplexen Algorithmus dessen Namen in den Kommentar zu schreiben.

Bei Business-Funktionen ist es sinnvoll Referenzen in die Spezifikation oder ins Issue Management System in die Kommentare zu schreiben. Wenn aus der Spez oder dem Issue dieses Implementierungsdetail nicht hervorgeht und es sich um einen genügend komplexen Codeblock handelt, sollte hier direkt ein Kommentar stehen, was man hier programmiert.

Sprache der Kommentare

Wenn das Team deutsch spricht, alle Dokumente deutsch sind und der Sourcecode intern bleibt, dann ist es irr englisch zu kommentieren.

JavaDoc in Schnittstellen

Schnittstellen beinhalten Methoden, die von anderen Teilen der Applikation aufgerufen werden. Hier ist eine Dokumentation besonders wichtig, da IDE's die Beschreibungen bei der Arbeit mit diesen Schnittstellen darstellen.

Keine Namen

Es bringt nichts in Kommentaren den eigenen Namen hineinzuschreiben. Diese Information kommt aus dem Sourcecode Verwaltungssystem (SVN, CVS, ...).

Review

Wie "normaler" Code sollten auch Kommentare reviewt werden. Was nützt ein Kommentar den niemand sonst versteht?

Anleitung neuer Service mit PortalPack 3.0.5 Beta, Liferay 6.1 und NetBeans 7.0.1

Hier eine kurze Anleitung, wie man mit PortalPack 3.0.5 Beta, Liferay 6.1 und NetBeans 7.0.1 einen Service baut.

Offen ist noch die DB-Einbindung. Irgendwas stimmt da noch nicht :-/

Unter Tools-Server 'Liferay Portal Server' definieren. Dabei ist Auto Deploy Dir: ...\liferay-portal-6.1.0\deploy und 'Directory Deployment Enabled' gesetzt.

Neues Projekt: - Java Web -> Web Application - NEXT - Project Name. Events - NEXT - Server: Liferay Portal Server - NEXT - Portlet Support aktivieren - Portlet generieren lassen

- FINISH

Jetzt wird das service.xml File angelegt:

Auf dem Events-Projekt rechts klicken und New ... wählen. Den File Type Liferay Plugins -> Service Builder XML wählen. Der Filename ist 'eventsservice' und der Folder ist 'web'.

- FINISH

Jetzt wird der Editor für den eneuen Service automatisch geöffnet:

Der Package Path und der Namespace sind falsch. Also wechseln wir in die XML-Ansicht und passen beides an.

Nun ersetelle ich einen neuen Service für die Events. Dazu klicke ich auf Add (links neben dem Package Path) und erfasse den Enitity Namen 'Event' und die Tabelle 'eventtable' (die Namen haben keine implizite Abhängigkeit untereinander.

Nach dem Add steht die neue Entity 'Event' in der Tabelle links oben. Nun können Columns für ein Event erfasst werden:

Als nächstes könnte ein Finder für Zugriffe über eine oder mehrere der Eventspalten definiert werden. Das mach ich ein anderes Mal.

Als letzter Schritt wird der Service generiert: Klick auf Generate Services (oben links im Services-Editor). Dadurch werden viele Java-Klassen (Service, Datenklassen,...), XMLs und ein ...\Events\service\lib\Events-service.jar erstellt. Die Klassen in ch.osys.events.service.*  sollten nicht von Hand geändert werden, da sie durchs Generieren wieder überschrieben werden.

Die Files in ch.osys.events werden nicht überschrieben durch Generate Services.

Das generierte JAR wird normalerweise ins Portlet WAR integriert. Wenn man will, dass andere Projeket auch diesem Service nutzen können, kann man dies in den Preferences (oben rechts im Editor) setzen.

Jetzt fehlt nur noch das GUI.

Keep it simple:

 <%@page contentType="text/html"%>
 <%@page pageEncoding="UTF-8"%>

 <%-- Uncomment below lines to add portlet taglibs to jsp
 <%@ page import="javax.portlet.*"%>
 <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%> 

 <portlet:defineObjects />
 <%PortletPreferences prefs = renderRequest.getPreferences();%>
 --%>

 <%@ page import="javax.portlet.*"%>
 <%@ page import="ch.osys.events.service.service.*"%>
 <%@ page import="ch.osys.events.service.model.*"%>
 <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>

 <portlet:defineObjects />
 <%PortletPreferences prefs = renderRequest.getPreferences();%>

 <table>
  <form method="POST" action="<portlet:actionURL/>">
   <tr>
    <td>Event ID:</td>
    <td><Input type="text" name="id"/></td>
   </tr>
   <tr>
    <td>Datum:</td>
    <td><Input type="text" name="datum"/></td>
   </tr>
   <tr>
    <td>Titel:</td>
    <td><Input type="text" name="titel"/></td>
   </tr>
   <tr>
    <td><Input type="submit"/></td>
   </tr>
  </form>
 </table>
 <H3> Anzahl Events :
  <%
   out.println(EventLocalServiceUtil.getEventsCount());
  %>
 </H3>

Jetzt fehlt noch die Funktionalität hinter dem Submit-Knopf. Dazu füge ich folgendes in ...\Events\src\java\ch\osys\events\Events.java:

public void processAction(ActionRequest request, ActionResponse response) throws PortletException, IOException {
 try {
  long id = Long.parseLong(request.getParameter("id"));
  String titel = request.getParameter("titel");
  Event event = EventLocalServiceUtil.createEvent(id);
  event.setId(id);
  event.setTitel(titel);
  event.setDatum(new Date());
  EventLocalServiceUtil.addEvent(event);
  System.out.println("Added");
 } catch (Exception ex) {
  ex.printStackTrace();
 }
}

Fertig!

Nun hab ich noch das Problem mit der DB-Einbindung. Gemäss Fehlermeldung, ist der User nicht berechtigt die Tabelle anzulegen. Ich muss der Sache nachgehen, wenn ich wieder wacher bin (oder 'Zeit habe', was halt zuerst eintritt).