Texte umbrechen (Word-Wrap)

Dafür gibt es unterschiedliche Varianten. Die beste ist noch, einen BreakIterator zu nutzen. NetBeans (OpenIDE) bringt hier Funktionen mit. Extrahiert und ohne Abhängigkeiten lässt sich das auf folgendes reduzieren:

import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;
public class StringUtils
{
/**
* Indent and wrap multi-line strings.
*
* @param original the original string to wrap
* @param width the maximum width of lines
* @param breakIterator algorithm for breaking lines
* @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
* @return the whole string with embedded newlines
*/
public static String wrapAndIndentString( String original, String indent, int width )
{
BreakIterator breakIterator = BreakIterator.getWordInstance();
    List<String> lines = wrapStringToArray( original, width, breakIterator, true );
StringBuffer retBuf = new StringBuffer();
    for ( String line : lines )
{
retBuf.append( indent );
retBuf.append( line );
retBuf.append( '\n' );
}
    return retBuf.toString();
}
  /**
* Wrap multi-line strings (and get the individual lines).
*
* @param original the original string to wrap
* @param width the maximum width of lines
* @param breakIterator breaks original to chars, words, sentences, depending on what instance you provide.
* @param removeNewLines if <code>true</code>, any newlines in the original string are ignored
* @return the lines after wrapping
*/
public static List<String> wrapStringToArray( String original, int width,
BreakIterator breakIterator, boolean removeNewLines )
{
if ( original.length() == 0 )
return Arrays.asList( original );
    String[] workingSet; 
    // substitute original newlines with spaces,
// remove newlines from head and tail
if ( removeNewLines )
{
original = original.trim();
original = original.replace( '\n', ' ' );
workingSet = new String[] { original };
}
else
{
StringTokenizer tokens = new StringTokenizer( original, "\n" ); // NOI18N
int len = tokens.countTokens();
workingSet = new String[len];
      for ( int i = 0; i < len; i++ )
workingSet[ i ] = tokens.nextToken();
}
    if ( width < 1 )
width = 1;
    if ( original.length() <= width )
return Arrays.asList( workingSet );
    widthcheck :
{
boolean ok = true;
      for ( int i = 0; i < workingSet.length; i++ )
{
ok = ok && (workingSet[ i ].length() < width);
        if ( !ok )
break widthcheck;
}
      return Arrays.asList( workingSet );
}
    ArrayList<String> lines = new ArrayList<String>(); 
    int lineStart = 0; // the position of start of currently processed line in
// the original string
    for ( int i = 0; i < workingSet.length; i++ )
{
if ( workingSet[ i ].length() < width )
lines.add( workingSet[ i ] );
else
{
breakIterator.setText( workingSet[ i ] );
        int nextStart = breakIterator.next();
int prevStart = 0;
        do
{
while ( ((nextStart - lineStart) < width) && (nextStart != BreakIterator.DONE) )
{
prevStart = nextStart;
nextStart = breakIterator.next();
}
          if ( nextStart == BreakIterator.DONE )
nextStart = prevStart = workingSet[ i ].length();
          if ( prevStart == 0 )
prevStart = nextStart;
          lines.add( workingSet[ i ].substring( lineStart, prevStart ) ); 
          lineStart = prevStart;
prevStart = 0;
} while ( lineStart < workingSet[ i ].length() );
        lineStart = 0;
}
}
    return lines;
}
}

Schade nur, dass GWT keinen BreakIterator für die Clientseite emuliert und selbst das Paket http://code.google.com/p/gwtx/ nicht. Schade …

gwtrpc-spring zur Einfachen Integration von Spring in GWT 1.6

GWT-SL bietet schon seit längerem die Möglichkeit, die RPC-Services durch Spring-Beans zu definieren. Ist Spring auf der Serverseite, so möchte man gerne Spring-POJOs als GWT-RPC-Service freigeben. GWT-SL ist aber relativ groß und mit http://code.google.com/p/gwtrpc-spring/ gibt es eine sehr schlanke Alternative, die nur aus zwei Klassen besteht. Das die Klassen, die beide im Quellcode unter 3 KB liegen, liegt daran, dass GWT in den neuen Versionen eine Integration RPC-Implementierungen vereinfacht. Die Hauptseite zeigt die 4 Schritten zur Integration anschaulich.

Noch besser beschreibt es allerdings http://devbright.com/2009/05/super-simple-gwt-spring-integration/; hier kann man gleich ein Archiv laden, mit allen Jar-Dateien und alles ist fertig. So läuft das Beispiel nach wenigen Minuten.

JUnit 4.6 ist raus

Seit längerem gibt es mal wieder in Update in JUnit. Das Projekt ist relativ fehlerfrei und daher ist das neue Release kein Bug-Fix-Release, sondern JUnit 4.6 integriert einige interessante Neuigkeiten.

MaxCore:

JUnit now includes a new experimental Core, `MaxCore`.  `MaxCore`
remembers the results of previous test runs in order to run new
tests out of order. `MaxCore` prefers new tests to old tests, fast
tests to slow tests, and recently failing tests to tests that last
failed long ago. There's currently not a standard UI for running
`MaxCore` included in JUnit, but there is a UI included in the JUnit
Max Eclipse plug-in.

Scheduling-Strategien für parallele Abarbeitung von Tests:

`JUnitCore` now includes an experimental method that allows you to
specify a model of the `Computer` that runs your tests. Currently,
the only built-in Computers are the default, serial runner, and two
runners provided in the `ParallelRunner` class:
`ParallelRunner.classes()`, which runs classes in parallel, and
`ParallelRunner.methods()`, which runs classes and methods in parallel.
This feature is currently less stable than MaxCore, and may be
merged with MaxCore in some way in the future.

Dann lassen sich Arrays mit Fließkommazahlen auch mit einem Delta vergleichen:

assertArrayEquals(new double[] {1.0, 2.0}, new double[] {1.0, 2.0}, 0.01);

Das tutego-JUnit-Seminar berücksichtigt diese Änderungen.

JavaNCSS (Source Measurement Suite) in der Version 30.51

JavaNCSS ist ein “A Source Measurement Suite for Java”, mit dem man Statistiken über Projekte bekommt. Das Release vom 9. Feb. 2009 trägt die Versionsnummer 30.51 und gehört damit den zu höchsten Versionsnummer, die ich je gesehen habe.

Die Ausgabe, hier etwa für das JDK 1.1.5 (nicht 1.5!), sieht so aus:

Output generated by JavaNCSS with Sun's JDK 1.1.5 java.* source tree

Nr. Classes Functions NCSS Package
1 3 11 376 .
2 4 38 95 java.applet
3 70 1232 7060 java.awt
4 6 25 94 java.awt.datatransfer
5 30 117 861 java.awt.event
6 14 137 1023 java.awt.image
7 27 117 196 java.awt.peer
8 27 201 1268 java.beans
9 73 716 4221 java.io
10 72 711 3327 java.lang
11 7 92 288 java.lang.reflect
12 2 105 760 java.math
13 33 282 1504 java.net
14 19 73 318 java.rmi
15 3 10 71 java.rmi.dgc
16 3 13 47 java.rmi.registry
17 23 95 412 java.rmi.server
18 28 193 827 java.security
19 8 30 53 java.security.acl
20 5 8 22 java.security.interfaces
21 18 352 923 java.sql
22 40 522 4657 java.text
23 103 108 722 java.text.resources
24 30 322 2472 java.util
25 19 170 1064 java.util.zip
26 1 51 1093 sun.tools.ttydebug
27 1 0 2 sunw.io
28 2 1 6 sunw.util
--------- --------- ---------
671 5732 33762 Total

Packages Classes Functions NCSS | per
---------------------------------------------------
28.00 671.00 5,732.00 33,762.00 | Project
23.96 204.71 1,205.79 | Package
8.53 50.32 | Class
5.89 | Function

NCSS steht für “Non Commenting Source Statements (NCSS)”.

Für die Statistiken gibt es auch einen Ant-Task. Desweiteren gibt es eine kleine Gui und ein SVG-Output.

gwt-connectors – Verbindungen zwischen Formen

gwt-connectors ist eine GWT-Bibliothek, um Formen miteinander zu verbinden.

Die Präsentation zeigt, wie das geht.

Wer’s selber ausprobieren möchte, schaut unter demo (IE, Firefox, Opera, Chrome). (Aber wie kann man nun die Verbindung wieder lösen …)

Der nötige Programmcode ist kurz:

// Create boundary panel
AbsolutePanel boundaryPanel = new AbsolutePanel();
boundaryPanel.setSize("600px", "400px");
RootPanel.get().add(boundaryPanel, 10, 10);
Diagram diagram = new Diagram(boundaryPanel);
boundaryPanel.add(new Label("Connectors example"), 10, 2);

// Add connectors
Connector connector1 = new Connector(50, 80, 100, 100);
connector1.showOnDiagram(diagram);

Connector connector2 = new Connector(350, 200, 270, 80);
connector2.showOnDiagram(diagram);

Connector connector3 = new Connector(450, 120, 500, 80);
connector3.showOnDiagram(diagram);

// Add some elements that can be connected
Label label = new Label("LABEL");
Image image = new Image("http://code.google.com/images/code_sm.png");
HTML html = new HTML("<b>HTML<br>ELEMENT</b>");

boundaryPanel.add(label, 50, 270);
boundaryPanel.add(image, 180, 250);
boundaryPanel.add(html, 450, 250);

Shape shapeForLabel = new Shape(label);
shapeForLabel.showOnDiagram(diagram);

Shape shapeForImage = new Shape(image);
shapeForImage.showOnDiagram(diagram);

Shape shapeForHtml = new Shape(html);
shapeForHtml.showOnDiagram(diagram);

Ich bin gespannt, wann es das erste UML-Tool mit GWT gibt. gwt-connectores basiert im Übrigen auf Fred Sauer’s gwt-dnd. (Dazu auch das Demo http://allen-sauer.com/com.allen_sauer.gwt.dnd.demo.DragDropDemo/DragDropDemo.html.)

Dazu passt auch die Ankündigung von http://googledocs.blogspot.com/2009/03/drawing-on-your-creativity-in-docs.html, ein Zeichenwerkzeug in Google Docs einzubetten:

http://www.googlewatchblog.de/2009/03/26/google-docs-drawing-veroeffentlicht/ hat dazu ebenfalls ein Bild parat:

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.

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:

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

Seminar-Werbung. Diese Weiterbildungen sind neu bei tutego

Alphanum Algorithm

Der Alphanum Algorithm bietet eine alternative (bessere?) Sortierung für Zeichenketten mit gemischten Zahlen an:

Traditional  Alphanum
z1.doc

z10.doc

z100.doc

z101.doc

z102.doc

z11.doc

z12.doc

z13.doc

z14.doc

z15.doc

z16.doc

z17.doc

z18.doc

z19.doc

z2.doc

z20.doc

z3.doc

z4.doc

z5.doc

z6.doc

z7.doc

z8.doc

z9.doc

z1.doc

z2.doc

z3.doc

z4.doc

z5.doc

z6.doc

z7.doc

z8.doc

z9.doc

z10.doc

z11.doc

z12.doc

z13.doc

z14.doc

z15.doc

z16.doc

z17.doc

z18.doc

z19.doc

z20.doc

z100.doc

z101.doc

z102.doc

Der Alg. ist für Java über den AlphanumComparator (LGPL) implementiert.

Fluent Interface-based API für Java Reflection: FEST-Reflect

FEST-Reflect is a Java library that provides a Fluent Interface-based API that simplifies the usage of Java Reflection, resulting in improved readability and type safety. It supports class loading, access to static inner classes constructors, methods and fields, and more!

Die Aufrufen sind dann hübsch kompakt:

Steht zur Compilezeit etwa

  String name = names.get(8);

wird mit FEST-Reflect daraus

  String name = method("get").withReturnType(String.class)
.withParameterTypes(int.class)
.in(names)
.invoke(8);

Ein paar weitere Beispiele:

  Person person = constructor().withParameterTypes(String.class)
.in(Person.class)
.newInstance("Yoda");

method("setName").withParameterTypes(String.class)
.in(person)
.invoke("Luke");

field("name").ofType(String.class)
.in(person)
.set("Anakin");

List<String> powers = field("powers").ofType(new TypeRef<List<String>>() {})
.in(jedi)
.get();

Dead Code Detector (DCD)

Von der Webseite https://dcd.dev.java.net/:

If you are a developer or a team leader, Dead Code Detector (DCD) simply finds never used code in your Java/JEE applications. Deleting dead code helps to reduce cost of maintenance and to improve quality of code, and as a side effect it improves code coverage metrics of tests by deleting code that can’t be tested.  Provide a directory of compiled classes, a jar or a war file in the UI and DCD lists suspects of dead code. Dead code found can be private, package-private and even protected or public. Unread local variables, self assignments, toString on String and useless initializations are also detected. Please remember that dead code found by DCD is just suspects. DCD can not detect that reflection or other is used: ask to people who know your application and don’t complain if you don’t. You can read the usage manual to know how to launch and use DCD or check the FAQ if you have questions. The search is very fast (2000 classes/s on warm files) and made by static analysis of compiled classes without running the application or loading classes. DCD uses the library ASM (licence open source, BSD) from ObjectWeb/OW2 to do the job.

Die letzte Version ist DCD 1.1 und vom 13.2.2009. Das Tool lässt sich auch über Java Web Start starten:

https://dcd.dev.java.net/screenshot.png

PHP mit Quercus + Tomcat + Eclipse Java Web-Projekt

  1. Lade das WAR-Archiv http://quercus.caucho.com/download/quercus-3.2.1.war von http://quercus.caucho.com/.
  2. Packe der WAR-Archiv aus.
  3. Lege in Eclipse ein Dynamic Web Project (etwa unter dem Namen php) an.
  4. Verbinde es mit Tomcat. (Tomcat kann über Eclipse neuerdings auch automatisch heruntergeladen werden.)
  5. Kopiere aus dem ausgepackten Quercus-WAR das WEB-INF in das WEB-INF vom Eclipse-Web-Projekt (3 Jars in lib und web.xml)
  6. Kopiere images und index.php aus dem ausgepackten Quercus-WAR in WebContent
  7. Starte Tomcat in der Eclipse-View
  8. Gehe im Browser auf http://localhost:8080/php/

JRuby 1.2.0RC1 Released

Aus den News JRuby 1.2.0RC1 Released:

JRuby 1.2.0RC1 has fixed many many compatibility issues and continued to improve general performance. Highlights:
– Improved Ruby 1.9 support (via –1.9)
  – Compiler now works
  – Almost all of the missing 1.9 methods have been added
– New experimental –fast flag which does more aggressive optimizations
– Large scale compiler and runtime cleanup and performance audit
– Parsing is 3-6x faster now.
– Preliminary android support (Ruboto)
– Rails pathname issue fixed on Windows
– Major bug triage since last release
– 1052 revisions since 1.1.6
– 232 bugs fixed since 1.1.6