GWT Charting Bibliothek gflot und charts4j.

Alles fängt mit http://jquery.com/ an. Darauf baut auf http://code.google.com/p/flot/, eine JavaScript-Bib. für Chars:

http://code.google.com/p/gflot/ ist nun der GWT-Aufsatz auf flot.

Für eine Bar-Diagramm ist nur folgender Quellcode nötig:

final String[] MONTH_NAMES = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };

PlotModel model = new PlotModel();
PlotOptions plotOptions = new PlotOptions();

BarSeriesOptions barSeriesOptions = new BarSeriesOptions();
barSeriesOptions.setShow(true);
barSeriesOptions.setLineWidth(1);
barSeriesOptions.setBarWidth(1);
barSeriesOptions.setAlignment(BarAlignment.CENTER);

plotOptions.setDefaultBarsSeriesOptions(barSeriesOptions);
plotOptions.setLegendOptions(new LegendOptions().setShow(false));



// add tick formatter to the options

plotOptions.setXAxisOptions(new AxisOptions().setTicks(12).setTickFormatter(new TickFormatter() {

public String formatTickValue(double tickValue, Axis axis) {

if (tickValue > 0 && tickValue <= 12) {

return MONTH_NAMES[(int) (tickValue - 1)];

}

return "";

}

}));


// create a series

SeriesHandler handler = model.addSeries("Ottawa's Month Temperatures (Daily Average in &deg;C)", "blue");


// add data

handler.add(new DataPoint(1, -10.5));

handler.add(new DataPoint(2, -8.6));
handler.add(new DataPoint(3, -2.4));
handler.add(new DataPoint(4, 6));
handler.add(new DataPoint(5, 13.6));
handler.add(new DataPoint(6, 18.4));
handler.add(new DataPoint(7, 21));
handler.add(new DataPoint(8, 19.7));
handler.add(new DataPoint(9, 14.7));
handler.add(new DataPoint(10, 8.2));
handler.add(new DataPoint(11, 1.5));
handler.add(new DataPoint(12, -6.6));

// create the plot
SimplePlot plot = new SimplePlot(model, plotOptions);

// put it on a panel
FlowPanel panel = new FlowPanel();
panel.add(plot);

return panel;

Eine Alternative dazu ist http://code.google.com/p/charts4j/  bzw. http://code.google.com/p/charts4j/wiki/GWT_Port.

Dozer 5.0

Letzten Monat ist Dozer 5.0 veröffentlicht worden. Vor einem Jahr hatte ich schon über Dozer berichtet: http://www.tutego.de/blog/javainsel/2008/01/bean-bean-mapping-mit-dozer.html. Neu beim Java-Bean-Mapper sind:

  • Added Generics to public api
  • Switched to XSD instead of DTD
  • Simpler packaging
  • Upgraded 3rd party dependencies
  • Upgraded code to use jdk 1.5 features
  • Various feature requests and bug fixes

Auch die Namen sind vereinheitlicht worden, nun heißt es org.dozer, BeanFactory, CustomFieldMapper, usw.

Wie das Mapping über XML-Dateien beschrieben wird, klärt das Dokument http://dozer.sourceforge.net/documentation/mappings.html genauer. Auch wenn Dozer nun Java 5 unterstützt, beginnt Dozer nicht mit Annotationen, um die Mappings zu beschreiben.

Derby Datenbank endlich auch pur In-Memory

Andere Java-Datenbanken können es schon lange, Derby kann es nun auch: Daten nur im Speicher ohne File-Backup halten. Die Derby-URL für dieses Feature lautet dann jdbc:derby:memory:datenbank;create=true. (Warum eigentlich nich einfach nur „mem“?)

Zum Weiterlesen: http://blogs.sun.com/kah/entry/derby_10_5_preview_in, http://wiki.apache.org/db-derby/InMemoryBackEndPrimer. Die aktuelle Derby-Datenbank kann man unter http://db.apache.org/derby/releases/release-10.5.1.1.cgi beziehen.

Thema der Woche: Micro-Benchmarks

Lies zunächst die folgenden Artikel

Versuche dann mit http://jetm.void.fm/index.html die Frage zu klären, wie groß der Unterschied ist, wenn man Threads für eine Aufgabe per Hand startet oder aus einem Thread-Pool holt.

Thema der Woche: Gof-Pattern-Wiederholung

Theoretische Aufgaben:

  • Nenne zwei Nachteile, die Vererbung gegenüber Assoziation hat.
  • Gibt es seit Java 5 eine Alternative zu Marker-Interfaces? Wie sieht diese aus? Nenne Beispiele.

Praktische Aufgaben:

Thema der Woche: Matcher-Append-Replacement und Pig Latin

Die Pattern/Matcher erlauben nicht nur das Suchen und Ersetzen, sondern bieten noch weitere interessante Möglichkeiten.

Lösungen sind wieder gerne willkommen.

Google Web Toolkit 1.6, Google Plugin for Eclipse

Am 7.4.09 hat Google das Google Web Toolkit 1.6 veröffentlicht:

Zu den Neuerungen gehören unter anderem DatePicker und DateBox (http://gwt.google.com/samples/Showcase/Showcase.html#CwDatePicker) und ein LazyPanel. Viele Bug-Fixes wurden gemacht. Eine der größten Änderungen ist das Eventing: Event-Listener wurden durch Event-Handler ersetzt. Damit gibt es nur noch eine zentrale Stelle, an der die Events verwaltet werden und nicht mehr viele kleine Mini-Listen bei jedem Widget.

Das Google Plugin for Eclipse wird wohl die Standard-IDE werden und vermutlich Cypal verdrängen. Insbesondere da es laut Webseite bietet:

  • Recognition of inline JavaScript (JSNI): syntax highlighting, auto-indenting, Java Search and Refactoring integration
  • GWT compiler shortcuts and configuration UI
  • Wizards to create entry points, modules and HTML pages
  • Support for GWT JUnit tests

Die Doku für das Eclipse-Plugin ist unter http://code.google.com/intl/de/eclipse/docs/gwt.html. Laut der Beschreibung fehlt die schöne Möglichkeit, Stubs für RPC-Dienste aufzubauen; das bietet Cypal. Schade.

PS: tutego hat das GWT-Seminar für GWT 1.6 aktualisiert: http://www.tutego.de/seminare/java-schulung/GWT-Seminar-Google-Web-Toolkit-Schulung.html.

Projekte und Pakete aus den langtools

Achtung! Nur gültig in für JDK 6, in JDK 7 gibt es Umbenennungen.

Die langtools von Java sind die bekannten Kommandozeilenprogramme wie javac, jar, javap, … Die Quellen kann man sich zum Beispiel aus dem Mercurial Repository holen. Für Java 6 liefert http://hg.openjdk.java.net/jdk6/jdk6/langtools/ den Zugriff auf die Sourcen. Sie lassen sich auch in einem Rutsch (zip) beziehen. Im Verzeichis src/share/classes beginnen die Pakete:

  • com.sun.javadoc. Die Java Doclet-API, also nur Schnittstellen etwa um Pakete, Variablen oder Typen zu beschreiben
  • com.sun.mirror. Mirror API repräsentiert Konstrukte eines Java-Programms wie Klassen, Modifizierer. Anweisungen und Ausdrücke werden nicht repräsentiert. Wird in Java 7 verschwinden, denn dort gibt es mit javax.lang.model (seit Java 6) einen Ersatz. Die Mirror API wurde in Java 5 für das APT eingeführt, aber nie standardisiert
  • com.sun.source.tree. Repräsentiert den AST (abstract syntax trees) eines Java-Programms vom Java-Compiler. com.sun.source.util. Utility-Klassen für den AST
  • javax.annotation.processing. Um Annotation-Prozessoren zu beschreiben
  • javax.lang.model.Element, javax.lang.model.type, javax.lang.model.util. Deklarationen, für Typen, die Meta-Daten aus einem Java-Programm beschreiben, etwa Modifizierer oder Generics-Typen. In erster Linie für APT
  • com.sun.tools.apt.*. Annotation Processing Tool (apt). Greift auf javax.lang.model zurück
  • com.sun.tools.doclets.internal.toolkit. Implementierung des Standard-Doclets
  • com.sun.tools.javadoc. Implementierung des Schnittstellen aus com.sun.javadoc
  • com.sun.tools.javac.*. Java-Compiler
  • com.sun.tools.javah. C Header and Stub File Generator
  • sun.tools.javap. Der Java Class File Disassembler.

Wenn man ein Beispiel programmieren möchte, muss man tools.jar in den Klassenpfad aufnehmen. Dann kann man schon loslegen. So nutzt folgendes Programm die Datenstrukturen von javap, und ist somit der Java-Quellcode-Gegenspieler von Reflection.

import java.io.*; 
import sun.tools.javap.*;
public class MyJavap 
{ 
  static String filename = "bin/MyJavap.class";
  public static void main( String[] args ) throws FileNotFoundException 
  { 
    ClassData classData = new ClassData( new FileInputStream( filename ) );
    for ( FieldData field : classData.getFields() )
       System.out.println( field.getType() + " " + field.getName() );
    for ( MethodData method : classData.getMethods() )
      System.out.println( method.getName() + method.getParameters() ); 
  } 
}

Ausgabe:

java.lang.String filename 
<clinit>() 
<init>() 
main(java.lang.String[])

Die API um Programmcode zu beschreiben ist komplizierter. Folgendes Programm baut einen internen Baum auf und lässt ihn über die komfortablen print()-Funktionen ausgeben:

import javax.tools.DiagnosticCollector; 
import javax.tools.JavaCompiler; 
import javax.tools.JavaFileManager; 
import javax.tools.JavaFileObject; 
import javax.tools.StandardJavaFileManager; 
import javax.tools.ToolProvider;
import com.sun.tools.javac.code.Flags; 
import com.sun.tools.javac.code.TypeTags; 
import com.sun.tools.javac.tree.JCTree; 
import com.sun.tools.javac.tree.TreeMaker; 
import com.sun.tools.javac.tree.JCTree.JCAnnotation; 
import com.sun.tools.javac.tree.JCTree.JCBlock; 
import com.sun.tools.javac.tree.JCTree.JCClassDecl; 
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 
import com.sun.tools.javac.tree.JCTree.JCExpression; 
import com.sun.tools.javac.tree.JCTree.JCModifiers; 
import com.sun.tools.javac.tree.JCTree.JCStatement; 
import com.sun.tools.javac.tree.JCTree.JCTypeParameter; 
import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 
import com.sun.tools.javac.util.Context; 
import com.sun.tools.javac.util.List; 
import com.sun.tools.javac.util.ListBuffer; 
import com.sun.tools.javac.util.Name; 
import com.sun.tools.javac.util.Name.Table;
public class BuildSomeCode 
{ 
  public static void main( String[] args ) 
  { 
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); 
    StandardJavaFileManager fm = compiler.getStandardFileManager( diagnostics, null, null );
    Context context = new Context(); 
    context.put( JavaFileManager.class, fm );
    TreeMaker maker = TreeMaker.instance( context ); 
    Table table = new Table();
    ListBuffer<JCTree> methods = new ListBuffer<JCTree>();
    JCModifiers mmods = maker.Modifiers( Flags.PUBLIC | Flags.STATIC ); 
    Name mname = Name.fromString( table, "test" ); 
    JCExpression retType = maker.TypeIdent( TypeTags.VOID ); 
    ListBuffer<JCVariableDecl> params = new ListBuffer<JCVariableDecl>(); 
    ListBuffer<JCStatement> stmts = new ListBuffer<JCStatement>(); 
    JCBlock methodBody = maker.Block( 0, stmts.toList() ); 
    methods.append( maker.MethodDef( mmods, mname, retType, List.<JCTypeParameter> nil(), 
                                     params.toList(), List.<JCExpression> nil(), methodBody, null ) );
    JCModifiers cmods = maker.Modifiers( Flags.PUBLIC ); 
    Name cname = Name.fromString( table, "Test" ); 
    JCClassDecl classDef = maker.ClassDef( cmods, cname, List.<JCTypeParameter> nil(), null, 
                                           List.<JCExpression> nil(), methods.toList() );
    ListBuffer<JCTree> classDefs = new ListBuffer<JCTree>(); 
    classDefs.append( classDef );
    JCCompilationUnit compilationUnit = maker.TopLevel( List.<JCAnnotation> nil(), null, 
                                                        classDefs.toList() );
    System.out.println( compilationUnit ); 
  } 
}

Das ergibt:

public class Test { 
    public static void test() { 
    } 
}

Zum Weiterlesen:

Thema der Woche: NIO-Buffer

Java nutzt für Objekte den Heap-Speicher, kann aber Bytes auch in sogenannten direkten Puffern speichern. Das kann sehr effizient sein, da Kopieroperationen zwischen dem Heap-Speicher und dem internen Speichern vermieden werden.

EoD SQL hilft beim Mini-OR-Mapping aber nicht, um Girls zu kriegen

EoD SQL (Ease of Development) ist eine API, die über Annotationen SQL-Queries an Methoden erlaubt und diese dann zur Laufzeit ausführt. Ein Beispiel zeigt die Arbeitsweise am schnellsten:

 public interface UserQuery extends BaseQuery {
    @Select("SELECT * FROM users WHERE id = ?1")
    public User getUserById(long id);

    @Select("SELECT * FROM users")
    public DataSet<User> getAllUsers();

    @Update("UPDATE users SET user_name = ?{1.userName}, email_address = ?{1.emailAddress} " +
    "dob = ?{1.dob} WHERE id = ?{1.id}")
    public void updateUser(User user);

    @Update(sql = "INSERT INTO users (user_name, email_address, dob) VALUES " +
    "(?{1.userName}, ?{1.emailAddress}, ?{1.dob})",
    keys = GeneratedKeys.RETURNED_KEYS_FIRST_COLUMN)
    public User insertUser(User user);

}

Mit

UserQuery QUERY = QueryTool.getQuery(UserQuery.class);

wird dann ein Objekt erzeugt, was die Schnittstelle implementiert. Dann sind Aufrufe wir QUERY.getAllUsers() möglich

Zusammenfassung von der Webseite;

EoD SQL is a very small API that allows binding of SQL Queries to Java Objects. The API is designed to be compatible with the "Ease of Development" API that could be found in J2SE 1.6 beta, but not in the final release. The EoD API’s main principal is that it maps SQL ResultSet’s to objects, and not the other way around. Most OR mapping layers try to replicate the database structure in objects, where EoD SQL lets you write the SQL code, while it allows for easy access to Java Objects that map to the ResultSet.

Advantages to EoD SQL:

  • Super light weight
  • Very easy to work with
  • Generally well documented (tell me where it’s not)
  • More features than the original EoD RI
  • No compile-time anything needed
  • No XML, or other configuration files needed
  • Should work with any JDBC database
  • Lets you code whatever SQL you like
  • Object mapping is loose (extra columns in the results are ignored, as are extra fields in the data object)

What EoD SQL is not:

  • EoD SQL will not parse or validate your SQL in any way
  • EoD SQL is not a database or database connector, it sits on top of JDBC
  • EoD SQL will not get you girls

Google App Engine unterstützt Java

Google Plugin for EclipseNach Python unterstützt die GAP nun auch Java 6. Allerdings gibt es auch einige Besonderheiten:

  • Kein Dateisystem (somit keine sinnvollen java.io.File-Operationen), da Zugriff über den App Engine Datastore
  • Keine eigenen Threads
  • Kein AWT- und Swing-Funktionalität/Pakete. Dass man kein Fenster aufmachen kann ist klar, aber es können auch keine Bilder skaliert werden und einige Web-Frameworks nutzen Swing-Modelle wie TableModel. (Für die Bild-Operationen bietet Google eine eigene API: http://code.google.com/intl/de/appengine/docs/java/images/overview.html)
  • Eingeschränktes Reflection
  • Besonderer Klassenlader
  • (Natürlich) kein JNI
  • Nicht unterstützte Dinge werfen eine SecurityException

Nichts desto trotz laufen auch dyn. Sprachen wie Groovy und JRuby. Und es gibt ein Eclipse-Plugin für das Deployment (Google Plugin for Eclipse). Datenzugriff der AppEngine gibt es mit der API von JPA/JDO über http://www.datanucleus.org/products/accessplatform/ (früher JPOX), IMHO eine recht unbekannte Implementierung der Standards.

Zum Weiterlesen:

Thema der Woche: Java™ 6u10 – Java6u13

In der letzten Java™ 6 Updates sind einige Neuigkeiten hinzugekommen. Einige sind JavaFX geschuldet.

Java 6u14 ist noch nicht fertig. Es gibt nur ein „Java™ Platform, Standard Edition 6u14 Binary Snapshot Releases“. Die Anzahl der gefixten Bugs ist groß. Lese dazu  http://download.java.net/jdk6/ und http://tutorial.fyicenter.com/out.php?ID=3688.

Na endlich: JDK 7 feature list, build and integration schedule

Mark Reinhold schreibt auf seinem Blog:

At Devoxx back in December I presented a list of candidate features for JDK 7(video, interview); more recently, at FOSDEM, I discussed that list as well as the high-level schedule and the means by which we plan to deliver the release (slides, audio/video). The high-level schedule is now available on the JDK 7 Project page in theOpenJDK Community along with a detailed feature list, the near-term build and integration schedule, and the long-term, build-by-build calendar. The JDK 7 feature list, like that of any large software project, is provisional and subject to change based upon new information and new proposals. Who Many of the planned features will be implemented by Sun engineers; the rest will be contributed from outside Sun. We’ll shortly define a process by which additional features can be proposed—so long as you’re willing to write not just the code but also the necessary tests and specification material. There will also be a simpler, lighter-weight process for smaller changes. Where Regardless of who writes the code, the expectation is that all development will take place in the open, either in the JDK 7 Project, in some other OpenJDK Project, or elsewhere. What about the JCP? The JDK 7 Project is creating a prototype of what might—or might not—wind up in the Java SE 7 Platform Specification. When the SE 7 Platform JSR is submitted then the features under development in JDK 7 will be proposed for inclusion therein, except for those that are VM-level or implementation-specific.

Der Kalender gibt Ende Februar an.

M8 2010/02/12 – 2010/02/18 b95

Die Feature-List ist interessant aber ich denke, dass einiges im Detail noch offen ist:

  • JSR TBD: Small language enhancements (Project Coin)
  • JSR 296: Swing application framework
  • Swing updates

Einführung in den EventBus

Was ist EventBus?

  • Die Idee vom EventBus https://eventbus.dev.java.net/ ist schnell in einem Satz beschrieben:
    • Biete einen Publish/Subscribe-Ereignisdienst für Applikationen an, die innerhalb einer JVM laufen.
    • Anders als also JMS, funktioniert EventBus nur in einer JVM, aber nicht über Rechnergrenzen.
  • Ereignisbehandlung wird üblicherweise über Observer/Observable oder über Listener realisiert.
    • Listener sind aber lästig zu schreiben: Man benötigt XXXEventListener Schnittstellen und Implementierungen, addXXXListener(), removeXXXListener() und fireEventListener() Methoden und vielleicht XXXEvent-Klassen.
  • EventBus vereinfacht das und mit zwei Typen und drei Methoden ist ein erstes Beispiel programmiert.

Erstes Beispiel

 

import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.EventSubscriber;

class Observer
{
Observer()
{
EventBus.subscribe( Object.class, new EventSubscriber<Object>() {
@Override public void onEvent( Object evt ) {
System.out.println( evt );
}
} );
}
}

public class First
{
public static void main( String args[] )
{
new Observer();
EventBus.publish( „Hallo Welt“ );
}
}

Analyse

  • EventBus.publish( Object ) sendet ein Ereignis an alle Interessenten aus.
  • EventBus.subscribe( Class, EventSubscriber ) meldet für einen speziellen Klassentyp einen Listener an.
    • Das besondere: Es ist hierarchisch. Wir senden mit EventBus.publish( „Hallo“ ) einen String, aber da String eine Unterklasse von Object ist, bekommt unser „Alles“-Listener die Nachricht.
    • EventBus.unsubscribe() meldet den Interessenten wieder ab.
  • Der EventSubscriber ist generisch deklariert, so dass es bei EventSubscriber<Object> somit onEvent( Object evt ) heißt.

Hierarchien

  • Mit der Möglichkeit Event-Hierarchien zu bilden gibt es eine große Flexibilität.
  • So horcht folgendes auf alle IOException-Events: EventBus.subscribe( IOException.class, new EventSubscriber() { … EventBus.publish( new FileNotFoundException() );
  • Ist dieses Matchen nicht gewünscht, verwendet man statt EventBus.subscribe() die Methode EventBus.subscribeExactly().

Hierarchien bei Generischen Typen (1)

  • Bei Generics funktioniert ein .class nicht wie erwartet.
    • Es liefert System.out.println( new Holder<String>().getClass() ); und System.out.println( new Holder<StringBuffer>().getClass() ); immer nur class javax.xml.ws.Holder.
  • Was passiert bei EventBus.subscribe( Holder.class, new EventSubscriber() { … und EventBus.publish( new Holder<String>(„String value“) ); EventBus.publish( new Holder<Date>( new Date() ) );
  • In beiden Fällen wird der Listener benachrichtigt.

Hierarchien bei Generischen Typen (2)

  • Soll eine Trennung aufgrund des generischen Typs stattfinden, lässt sich auf ein java.lang.reflect.Type anmelden und Senden. Der Quellcode ist ein wenig komplex:

 

EventBus.subscribe( new TypeReference<Holder<Date>>(){}.getType(), new EventSubscriber<Object>() {
    @Override public void onEvent( Object evt ) {
      System.out.println( ((Holder)evt).value );
    }
  } );

EventBus.publish( new TypeReference<Holder<String>>(){}.getType(), new Holder<String>(„String value“) );
EventBus.publish( new TypeReference<Holder<Date>>(){}.getType(), new Holder<Date>( new Date() ) );

Jetzt wird nur noch das Datum empfangen und auf den Bildschirm kommt:

Thu Mar 26 14:28:15 CET 2009

Topics

  • Im Regelfall sind nicht alle Interessenten an allen Ereignissen interessiert.
  • Man kann nun verschiedene Ereignistypen definieren, doch wenn man etwa Strings verschicken möchte, ist es lästig, diesen String extra in in Ereignisobjekt zu verpacken.
  • Die Lösung sind Topics, also bestimmte Themen, zu denen man sich anmelden kann und zu denen man schicken kann.
  • Es ändern sich in der API zwei Dinge: Bei publish() ist der Topic anzugeben, bei subscribe() ist statt ein EventSubscriber ein EventTopicSubscriber nötig, da der Listener neben dem Event auch den Topic übergibt.

Beispiel mit Topics

 

EventBus.subscribe( "Error", new EventTopicSubscriber() {
@Override public void onEvent( String topic, Object evt ) { System.out.printf( "'%s' für Topic '%s'%n", evt, topic ); }
} );

EventBus.publish( „Error“, „Hallo Welt“ );

Liefert dann

‚Hallo Welt‘ für Topic ‚Error‘

Für mehrere Topics anmelden

  • Soll ein EventTopicSubscriber für mehrere Topics angemeldet werden, so kann man natürlich schreiben: EventTopicSubscriber ets = … EventBus.subscribe( topic1, ets ); EventBus.subscribe( topic2, ets );
  • Eine weitere subscribe()-Methode ist subscribe(java.util.regex.Pattern, EventTopicSubscriber).
    • Damit lassen sich leicht über reguläre Ausdrücke Gruppen bilden.

EventBus.subscribe( Pattern.compile( „Error|Warning“ ), new EventTopicSubscriber() { …

EventBus.publish( „Error“, „Hallo Welt“ );
EventBus.publish( „Info“, „Hallo Welt“ ); // Kommt nicht an
EventBus.publish( „Warning“, „Hallo Welt“ );

EventBus und AWT Event Dispatching Thread (EDT)

  • Setzt man in die onEvent()-Methode die Anweisung System.out.println( Thread.currentThread() ); // Thread[AWT-EventQueue-0,6,main] so findet man, dass der EventBus den Programmcode im AWT Event Thread abarbeitet.
  • Das ist natürlich gut für Aktionen, die an den Swing-Komponenten vorgenommen werden.
    • Zum Beispiel: Ein beliebiger Thread lädt Daten und möchte nach dem Laden eine Statuszeile aktualisieren. Schickt der Thread dann mit publish() ein Ereignis, wird der Programmcode vom Empfänger im EDT ausgeführt, sodass dort etwa ein setText() auf einem JLabel der Statuszeile erlaubt ist.
  • Auf der anderen Seite hat das zur Konsequenz, dass der Programmcode schnell sein muss, damit der EDT nicht zu lange blockiert wird.
  • Bei nicht-AWT-Anwendungen ist die Abarbeitung im EDT unsinnig

EventBus und SwingEventService

  • Der EventBus setzt den Programmcode standardmäßig in den EDT, kann ihn aber auch von einem anderen Thread abarbeiten lassen.
  • System.out.println( EventBus.getGlobalEventService() ); // org.bushe.swing.event.SwingEventService@173a10f
  • Standardmäßig nutzt EventBus also intern eine SwingEventService-Objekt.
  • Alternativ kann man schreiben: SwingEventService eventing = new SwingEventService(); eventing.subscribe( Object.class, new EventSubscriber() { @Override public void onEvent( Object evt ) { System.out.println( evt ); } } ); eventing.publish( „Hallo“ );

SwingEventService und ThreadSafeEventService

  • Neben dem SwingEventService gibt es den ThreadSafeEventService für eine Abarbeitung, die nicht im EDT stattfindet.
    • Beide Implementieren die Schnittstelle EventService. (Genaugenommen ist SwingEventService eine Unterklasse von ThreadSafeEventService.)
  • Man schreibt dann: EventService eventing = new ThreadSafeEventService(); eventing.subsribe(…); eventing.publish(…);

EventServiceLocator (1)

  • Den EventBus kann man nun so umstellen, dass er standardmäßig den ThreadSafeEventService nutzt.
    • Dazu wird intern ein EventServiceLocator eingesetzt.

    System.out.println( EventBus.getGlobalEventService() ); // org.bushe.swing.event.SwingEventService System.out.println( EventServiceLocator.getEventBusService() ); // org.bushe.swing.event.SwingEventService System.out.println( EventServiceLocator.getSwingEventService() ); // org.bushe.swing.event.SwingEventService

  • Der EventServiceLocator verwaltet also zwei unterschiedliche Event-Services.

EventServiceLocator (2)

try { EventServiceLocator.setEventService(EventServiceLocator.SERVICE_NAME_EVENT_BUS, new ThreadSafeEventService()); } catch ( EventServiceExistsException e ) { e.printStackTrace(); }

System.out.println( EventBus.getGlobalEventService() ); // org.bushe.swing.event.ThreadSafeEventService System.out.println( EventServiceLocator.getEventBusService() ); // org.bushe.swing.event.ThreadSafeEventService System.out.println( EventServiceLocator.getSwingEventService() ); // org.bushe.swing.event.SwingEventService

Achtung: Das muss an den Anfang bevor ein Event je den Bus sieht!

EventServiceLocator (3)

  • Wenn man nun Ereignisse über einen „normalen“ Thread bearbeitet haben möchte, schreibt man wie üblich EventBus.publish()/subscribe().
  • Sollen die Eventanweisungen in den EDT, schreibt man EventServiceLocator.getSwingEventService().publish()/subscribe().

Hängende Referenzen bei Listenern

  • Ein häufiges Problem bei Listenern insbesondere bei Swing-Anwendungen sind angemeldete Listener, für die der Interessent aber schon weg ist. Ein Szenario:
    • Ein Textfeld einer Maske meldet eine Listener an, um bei Modelländerungen die Daten darstellen zu können.
    • Das Model speichert den Listener und indirekt auch eine Referenz auf das Textfeld.
    • Die Maske verschwindet, somit auch der Interessent für Modelländerungen.
    • Da aber das Model den Listener und indirekt das Textfeld referenziert, kann der GC das Textfeld gar nicht freigeben.
    • Daher müssen Listener immer abgemeldet werden, oder…?

EventBus und WeakReference (1)

  • Damit Listener bei nicht mehr aktiven Horchern automatisch verschwinden, kann man WeakReferences einsetzen.
  • Eine WeakReference ist wie ein Proxy, der ein anderes Objekt ummantelt. Ist die WeakReference die einzige Referenz, die sich für das Objekt interessiert, so kann sie beim nächsten GC das Objekt wegräumen. Der Proxy wird dann ebenfalls nicht mehr benutzt.
  • Standardmäßig mantelt die EventBus.subscribe(XXX, EventSubscriber)-Methoden den EventSubscriber in eine WeakReference.
    • Wenn es also keinen starken Verweis auf den EventSubscriber mehr von außen gibt, wird der EventBus diesen Listener automatisch abmelden.

EventBus und WeakReference (2)

 

public class First
{
First()
{
  new Observer();
}

public static void main( String args[] )
{
new First();
// System.gc();
EventBus.publish( „Hallo Welt“ );
}
}

EventBus und WeakReference (3)

  • Der Konstruktor erzeugt einen Observer, der aber nicht referenziert wird. Nach dem der Konstruktor abgearbeitet wurde, ist das Exemplar Freiwild für den GC.
  • Wenn in main() System.gc() aufgerufen wird, wird aufgeräumt. Damit werden die WeakReferences entsort also auch der Listener beim EventBus abgemeldet.
  • Ist das System.gc() raus, steht immer noch „Hallo“, weil das Objekt noch da ist.
  • Soll EventBus keinen WeakReference-Behälter um den Listener bauen, so nutzt man subscribeStrongly(XXX, EventSubscriber) bzw. subscribeExactlyStrongly(XXX, EventSubscriber).

Was fehlt noch?

  • EventBus kann die Listener aufzählen
  • EventBus kann Veto
  • Listener können auch über Annotationen angemeldet werden
  • Timer können Events überwachen
  • Events können gechached werden, sodass spätere Anmelder die Events auch bekommen