Frage: Kann man alle Instanzen einer Klasse ermitteln?

Das geht nicht wirklich und wenn, dann nur mit großen Umwegen etwa über die Java Debugging API, mit der man sich an die JVM hängen kann. Auch ein Blick auf den Quellcode von jconsole und jmap/jhat helfen hier, weil die Tools genau das machen und Zahlen geben.

Der Objektgraf verändert sich ständig und so könnte man auch die Objekte stark referenzieren und den GC am Löschen hindern. Das würde zu einer großen Anzahl von Problemen führen. (Schwache Referenzen könnte das Problem abmildern, aber der GC muss hier Zusatzarbeit machen und die Laufzeit würde sich (messbar) verschlechtern.) Die Objekte sind ja immer eine Momentaufnahme. Da kämen ja Millionen von Objekten raus, wenn man etwa nach „Gib mir alle Strings“ fragt. Laufend ändert sich diese Menge.

Wenn der Nutzer diese Instanzen wirklich braucht, kann er sie an einer Objekt-Registry anmelden.

  • Von Hand kann man Exemplare etwa in eine Map<Class,List<?>> setzen. Das muss man nicht unbedingt manuell machen, sondern man kann hier einen Aspekt schreiben, der Bytecode in den Konstruktor für solch ein Anmelden einfügt. Dann ist das Abmelden aber noch so eine Sache. Das kann aber eine PhantomReference übernehmen und so meldet man das Objekt an der Registry wieder ab.
  • Wenn man Spring benutzt, deklariert ListableBeanFactory (implementiert etwa von XmlBeanFactory) eine Methode Map getBeansOfType(Class type) für genau diesen Fall.
  • Sind die Exemplare alle MBeans, ist es einfach, dann sind sie schon an einem Container registriert, und Query liefert sie sogar nach gewissen Kriterien.

Thema der Woche: Repository-Pattern in DDD und DAO

Zur Abstraktion von Zugriffen auf konkreten Datastores (oft eine relationale Datenbank) haben sich DAOs (http://java.sun.com/blueprints/corej2eepatterns/Patterns/DataAccessObject.html) etabliert. Im Domain Driven Design (DDD) gibt es etwas, das so aussieht wie ein DAO, und zu großer Diskussion in der Community führt.

Lassen sich hier schon Unterschiede herausstellen?

Verfolge den “Streit” über den Unterschied zwischen Repos/DAO in den Blogs

Fragen:

  • Was sagt Eric Evans zu der Diskussion? Kann ein Repo als DAO implementiert sein?
  • Stehen Repo und DAO beide auf der Ebene vom Domain-Model?
  • Warum sagen Kritiker, dass Repos eine zusätzliche (unnötige) Zwischenschicht sind?
  • Einige EJB-Advocates injizieren gerne bei Java EE 5 Anwendungen den Entity-Manager in den DAO bzw. sehen den EM als DAO selbst, den man dann in Session-Beans injiziert. Ist das ein Design im Sinne von DDD?

Optional. Wie sieht Paginierung aus? http://tech.groups.yahoo.com/group/domaindrivendesign/message/5795

Java Datastore API für die Google App Engine

Um in der Google Cloud Daten zu speichern bietet Google drei API an: JPA, JDO und eine Low-Level-API. Infos dazu gibt liefert http://code.google.com/intl/de/appengine/docs/java/datastore/. JPA und JDO basieren im Kern auf der Low-Level API, die auf die http://en.wikipedia.org/wiki/BigTable zurückgreift.

Für JPA und JDO gibt es selbst von Google viele Beispiele, aber die Low-Level-API ist nicht so gut dokumentiert und selbst das Beispielprogramm in der JavaDoc enthält Fehler. Zeit daher, ein sehr einfaches Beispiel mit einer 1:n Relationen anzugehen.

Im Mittelpunkt der API steht der DatastoreService, der ein bisschen an den EntityManager von JPA erinnert. Er bietet Methoden für die CRUD-Operationen. Mein Beispiel geht schon ein bisschen “pseudo-ORM” an die Aufgabe ran, einer Person Nachrichten zuordnen zu können:

Die Personen-Klasse:

package com.tutego.server.entity;

import java.util.ArrayList;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;

public class Person
{
  private final static String ENTITY_NAME = „Person“;
  Entity personEntity;

  public enum Gender
  {
    MALE, FEMALE
  }

  public Person()
  {
    personEntity = new Entity( ENTITY_NAME );
  }

  private Person( Key key )
  {
    try
    {
      personEntity = DatastoreServiceFactory.getDatastoreService().get( key );
    }
    catch ( EntityNotFoundException e )
    {
    }
  }

  private Person( Entity entity )
  {
    personEntity = entity;
  }

  public static Person get( Key key )
  {
    return new Person( key );
  }

  public void setUsername( String username )
  {
    personEntity.setProperty( „username“, username );
  }

  public String getUsername()
  {
    return personEntity.getProperty( „username“ ).toString();
  }

  public void setGender( Gender gender )
  {
    personEntity.setProperty( „gender“, gender.toString() );
  }

  public Gender getGender()
  {
    return Gender.valueOf( personEntity.getProperty( „gender“ ).toString() );
  }

  public Key put()
  {
    return DatastoreServiceFactory.getDatastoreService().put( personEntity );
  }

  public static void deleteAll()
  {
    Query deleteAllQuery = new Query( ENTITY_NAME );

    for ( Entity entity : DatastoreServiceFactory.getDatastoreService().prepare( deleteAllQuery ).asIterable() )
      DatastoreServiceFactory.getDatastoreService().delete( entity.getKey() );
  }

  private static List<Person> executeQuery( Query query )
  {
    List<Person> result = new ArrayList<Person>();
    for ( Entity entity : DatastoreServiceFactory.getDatastoreService().prepare( query ).asIterable() )
      result.add( new Person( entity ) );
    return result; 
  }

  public static List<Person> findAllPersons()
  {
    Query query = new Query( ENTITY_NAME );

    return executeQuery( query );
  }

  public static List<Person> findPersonByGender( Gender gender )
  {
    Query query = new Query( ENTITY_NAME );
    query.addFilter( „gender“, Query.FilterOperator.EQUAL, gender.toString() );

    return executeQuery( query );
  }

  @Override
  public String toString()
  {
    return String.format( „Person[%s,%s]“, getUsername(), getGender() );
  }
}

Die Nachrichten-Klasse:

package com.tutego.server.entity;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Query;

public class Message
{
  private final static String ENTITY_NAME = „Message“;

  private Entity messageEntity;

  public Message()
  {
    messageEntity = new Entity( ENTITY_NAME );
  }

  private Message( Key key )
  {
    try
    {
      messageEntity = DatastoreServiceFactory.getDatastoreService().get( key );
    }
    catch ( EntityNotFoundException e )
    {
    }
  }

  private Message( Entity entity )
  {
    messageEntity = entity;
  }

  public static Message get( Key key )
  {
    return new Message( key );
  }

  public void setText( String text )
  {
    messageEntity.setProperty( „text“, text );
  }

  public String getText()
  {
    return messageEntity.getProperty( „text“ ).toString();
  }

  public void setCreationTime( Date d )
  {
    messageEntity.setProperty( „creationtime“, „“ + d.getTime() );
  }

  public Date getCreationTime()
  {
    return new Date( Long.parseLong( messageEntity.getProperty( „creationtime“ ).toString() ) );
  }

  public void setReceiver( Person p )
  {
    messageEntity.setProperty( „person_fk“, p.personEntity.getKey() );
  }

  public Key put()
  {
    return DatastoreServiceFactory.getDatastoreService().put( messageEntity );
  }

  private static List<Message> executeQuery( Query query )
  {
    List<Message> result = new ArrayList<Message>();

    for ( Entity entity : DatastoreServiceFactory.getDatastoreService().prepare( query ).asIterable() )
      result.add( new Message( entity ) );
    return result; 
  }

  public static List<Message> findMessagesForPerson( Person p )
  {
    Query query = new Query( ENTITY_NAME );
    query.addFilter( „person_fk“, Query.FilterOperator.EQUAL, p.personEntity.getKey() );

    return executeQuery( query );
  }

  @Override
  public String toString()
  {
    return String.format( „Message[%s,%s]“, getCreationTime(), getText() );
  }
}

Getestet werden soll das ganze in einer einfachen Server-Funktion:

StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter( sw );

// Insert new entity

Person p1 = new Person();
p1.setUsername( „chris“ );
p1.setGender( Person.Gender.MALE );
Key key1 = p1.put();

out.println( „* Key für erste Person “ + p1 );
out.println( KeyFactory.keyToString( key1 ) );

Person p2 = new Person();
p2.setUsername( „pallas“ );
p2.setGender( Person.Gender.FEMALE );
p2.put();

Person p3 = new Person();
p3.setUsername( „tina“ );
p3.setGender( Person.Gender.FEMALE );
p3.put();

// Search for entity with a given key

Person p = Person.get( key1 );
out.println( „* Suche mit Schlüssel “ + key1 );
out.println( p.getUsername() );

// Query

List<Person> findAll = Person.findAllPersons();
out.println( „* Alle Personen“ );
out.println( findAll.toString() );

// Query

List<Person> females = Person.findPersonByGender( Gender.FEMALE );
out.println( „* Alle Frauen:“ );
out.println( females.toString() );

Message msg1 = new Message();
msg1.setText( „Hallo Maus“ );
msg1.setCreationTime( new Date(1) );
msg1.setReceiver( p );
msg1.put();

Message msg2 = new Message();
msg2.setText( „Hallo Ratte“ );
msg2.setCreationTime( new Date(2) );
msg2.setReceiver( p );
msg2.put();

out.println( „* Alle Nachrichten für “ + p );
out.println( Message.findMessagesForPerson( p ) );
out.println( „\n“ );

// Clean up

Person.deleteAll();

out.flush();

return sw.toString().replace( „\n“, „<br/>“ );

Als Ergebnis kommt HTML zurück, was der Client zum Testen anschauen kann.

Thema der Woche: Stripes, ein einfaches Web Presentation Framework

Die Listen http://java-source.net/open-source/web-frameworks und http://de.wikipedia.org/wiki/Liste_von_Webframeworks geben eine fast unendliche Aufzählung von Web-Frameworks an. Selbst bin ich ein Freund (je nach Anwendungsfall) von GWT, JSF 2.0 und Stripes.

Stripes is a presentation framework for building web applications using the latest Java technologies. The main driver behind Stripes is that web application development in Java is just too much work! It seems like every existing framework requires gobs of configuration. Struts is pretty feature-light and has some serious architectural issues (see Stripes vs. Struts for details). Others, like WebWork 2 and Spring-MVC are much better, but still require a lot of configuration, and seem to require you to learn a whole new language just to get started.

Stripes fällt die Kategorie der Action-orientierten Frameworks, wie Struts oder Spring MVC. Ein Front-Controller nimmt den Request entgegen und delegiert auf eine ActionBean-Klasse, die den Seitenfluss auf eine andere Zielseite steuert.

Aufgaben:

Trivia: Frederic Daoud ist der Autor vom Standard-Stripes Buch “Stripes: …and Java Web Development Is Fun Again”. Frederic und seine Frau Nadia haben ein zweites Kind bekommen und es Ruby genannt.

OpenJDK7 / JDK7 M4 Release

Die Ankündigung wurde unter http://blogs.sun.com/xiomara/entry/openjdk7_jdk7_release_milestone_4 gemacht. Interessant ist „For now JDK 7 is finally in sync with the JDK 6u14 updates.“ Dann wird es Zeit, dass jetzt mal die *wirklichen* Dinge implementiert werden und man nicht nur die Änderungen von Java 6 alle in Java 7 nachzieht. Bis auf NIO.2 ist hier noch nicht wirklich viel passiert. Und beim Project Coin wird viel diskutiert, aber bisher ohne Ergebnis im Java-Compiler.

GWT und sein HTML/DOM, alternativer GWT FlowPanel

Zum Layout von GWT-Komponenten ist es unerlässlich zu verstehen, was GWT für ein DOM erzeugt. Zum einen sind da Werkzeuge wie FireBug unerlässlich und zum Anderen kann man sich vorher schon bei http://javabyexample.wisdomplug.com/component/content/article/75.html informieren, was GWT für eine Struktur erzeugen wird.

Schnell entstehen bei GWT-Anwendungen eine Unzahl geschachtelter Tabellen. Sie sind für die Performance der Darstellung nicht unerheblich, denn wenn man das Fenster zum Beispiel in der Größe ändert, so müssen die ganzen Größeninformationen neu berechnet werden.

Um das HTML schlank zu halten, lässt sich auf alternative Container zurückgreifen. Wer zum Beispiel horizontal oder vertikal anordnen will, greift sofort zum HorizontalPanel bzw. VerticalPanel. Doch zur Umsetzung setzt eben GWT eine Tabelle ein. Wenn man nur zum Beispiel eine Zeile wie “blal@googlemail.com  | My favorites | Profile  | Sign out” aufbauen möchte, ist das HorizontalPanel unnötig und schwergewichtig. Es bietet sich an, eine neue Panel-Klasse zu nutzen, etwa wie sie etwa http://blog.sudhirj.com/2009/05/vertical-and-horizontal-flow-panels-in.html vorstellt. Etwas komprimiert:

import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;

public class HorizontalFlowPanel extends FlowPanel
{
  @Override
  public void add( Widget w )
  {
    w.getElement().getStyle().setProperty( „display“, „inline“ );
    super.add( w );
  }
}

Diese Implementierung führt nur zu einem <div>-Block statt einer <table>.

EventBus Version 1.3

Seit einem Monat gibt es EventBus 1.3. Die Webseite spricht im Wesentlichen von zwei Neuigkeiten:

  • The ability to control the order in which subscribers are called with the Priority interface and priority annotation parameters.
  • Support for annotated inner classes and other non-public members.
  • Mein Blog-Eintrag und Folien stellen das nette Projekt vor:

    Vergleich von HTML-Dateien mit Daisy Diff

    In meiner Seminarverwaltungssoftware können Trainer die Seminarbeschreibungen ändern. Natürlich möchte ich mitbekommen, welche Stellen geändert wurden und das am Liebsten hübsches aufbereitet. Da bin ich auf http://code.google.com/p/daisydiff/ gestoßen. Man kann entweder über die Kommandozeile arbeiten oder mit einer Hilfsklasse, wobei zwei Klassen einen ersten Anhaltspunkt für die API geben:

    Ein kleines Beispiel soll das Diff in HTML-Form in den Temp-Ordner C:\Users\CHRIST~1\AppData\Local\Temp\ schreiben:

    package com.tutego.traida;

    import java.awt.Desktop;
    import java.io.File;
    import java.io.FileWriter;

    import org.outerj.daisy.diff.Main;

    public class DaisyDiffDemo
    {
      public static void main( String[] args ) throws Exception
      {
        String html1 = „Eine zwei Polizei“;
        String html2 = „Eins zwei drei Polizei“;

        File fileIn = File.createTempFile( „daisyin“, „.html“ );
        File fileOut = File.createTempFile( „daisyout“, „.html“ );
        File fileDiff = File.createTempFile( „daisydiff“, „.html“ );
        new FileWriter( fileIn ).append( html1 ).close();
        new FileWriter( fileOut ).append( html2 ).close();

        String[] daisyDiffArgs = {
          fileIn.getAbsolutePath(), fileOut.getAbsolutePath(), „–file=“+fileDiff.getAbsolutePath()
        };
        Main.main( daisyDiffArgs );

        fileIn.deleteOnExit();
        fileOut.deleteOnExit();
        Desktop.getDesktop().open( fileDiff );
      }
    }

    Das generierte HTML greift auf allerlei Zeugs zurück. Damit die HTML-Datei gut angezeigt wird, einfach aus dem Zip die Order css, images und js in das temp-Verzeichnis C:\Users\CHRIST~1\AppData\Local\Temp kopieren. Dann sieht das Ergebnis so aus:

    sshot-1

    Mit GPL-Tools von COBOL nach Java migrieren

    Auf Jazoon09 (Präsentation: http://docs.google.com/Present?docid=dcc9m6z9_1524fzspccfp) wurde ein Projekt vorgestellt, wie 4 Millionen Zeilen COBOL erfolgreich nach Java konvertiert werden konnten. (infoQ http://www.infoq.com/news/2009/07/cobol-to-java hat die Links). Im Zentrum der Konvertierung steht der Konverter (83.000 Quellcodezeilen, fast 700 Klassen), das zusammen mit der Laufzeitbibliotheken (153k Zeilen, fast 900 Klassen) auf COBOL-Programme übersetzt und ausführt. Die COBOL-Masken werden in HTML übersetzt.

    Die GPL-Projekte sind unter http://code.google.com/p/naca/ zu finden (aber nicht im Source als SVN).

    Welches Projekt wird von Maven wie oft referenziert?

    Das sagt http://www.mvnbrowser.com/most-referenced.html. Die Liste ist interessant zu lesen. Vielleicht nicht ganz unerwartet ist ganz oben JUnit und log4j — das wird von nahezu allen Programmen referenziert. Interessant in der Liste finde ich,

    • das HSQLDB doch noch so oft (für Tests) gebraucht wird, aber Derby noch auf die erste Seite kommt,
    • Spring (schon) so weit oben seht,
    • CXF vor Axis und dem alten XFire steht,
    • JAXB so groß im Einsatz ist,
    • Plexus von CodeHaus in der Liste auf der ersten Seite steht (unter anderem von Maven2 genutztes IoC-Framwork und daher wohl so weit vorne),
    • dom4j populärer als JDOM ist,
    • Struts ist (ein wenig) wichtiger als WebWork aber weit abgeschlagen von JSF.

    Eher unbekannte Projekte sind:

    • http://classworlds.codehaus.org/
    • http://qdox.codehaus.org/
    • http://www.janino.net/

    IDE-Woche: Eclipse 3.5, NetBeans 6.7, IntelliJ 9 M1

    So viel IDE gab es noch nie. In einer Woche drei wichtige Neuerungen.

    JaQue Version 1.5: MS LINQ Expression Trees für Java

    Von der Webseite:

    JaQue provides an infrastructure for Microsoft LINQ like capabilities on Java platform. Using ASM, JaQue builds expression trees, which are used to build a query in domain specific technology or language, such as SQL. This way Java developers can focus on writing application logic in the language they know – Java, regardless what the underlying technology or language is in use. JaQue to Objects and JaQue to XML are currently supported and JaQue to JPA is under development.

    Ein Beispiel macht das praktisch deutlich:

    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hibernate");
      EntityManager em = emf.createEntityManager();
      JaqueEntityManager jem = new JaqueEntityManager(em);
      Queryable<Order> orders = jem.from(Order.class);

      System.out.println(count(where(new Predicate<Order>() {
            public Boolean invoke(Order t) throws Throwable {
                    return t.getOrderID() > 11000;
            }
      }, orders)));

    Wie bei LINQ soll eine eine API für die verschiedenen Datenquellen geben, also XML, Objekte, Datenbanken, … Für JPA schreibt die Seite “Currently it’s able to perform only simple SELECTs, JOINs and AGGREGATEs”. Das ist natürlich nicht viel. Insofern ist diese Lösung WEIT von den LINQ to SQL-Möglichkeiten entfernt.

    Der Hinweis der Webseite auf “Interoperability with Java Closures” ist natürlich hinfällig. Dadurch, dass Closures aber wegfallen, wird die ganze API viel aufgeblähter, als wenn man Closures hätte. Die Scala-Variante ist daher auch viel kürzer.

     

    PS: Die nächsten 2 Wochen gibt es keine Blog-Einträge, da ich ich Afrika bin.

    GWT-bezogene Videos von der Google I/O Konferenz 2009

    Im Google Code Blog werden einige Videos vorgestellt, die den aktuellen Zustand von GWT widerspiegeln. Etwa

    GWT Can Do What?!?! A Preview of Google Web Toolkit 2.0 (PDF):

     

    Und weitere:

    Google Wave – Powered by GWT

    Google Web Toolkit Architecture: Best Practices for Architecting your GWT App

    Measure in Milliseconds: Performance Tips for Google Web Toolkit

    Effective GWT: Developing a Complex, High- performance App with Google Web Toolkit

    The Story of your Compile: Reading the Tea Leaves of the GWT Compiler for an Optimized Future

    Progressively Enhance AJAX Applications with Google Web Toolkit and GQuery

    Building Applications with Google APIs

    Mit AjaxSwing Swing Anwendungen als Web-Anwendungen laufen lassen

    Das macht die Swing – AJAX bridge. Das SwingSet2 Demo ist schon mal ein guter Start. Die Entwickler sagen, dass das Demo ohne Quellcodemodifikationen läuft. Lauf FAQ laufen 95% der Anwendungen vernünfitg nach 30 Minuten Konfiguration. Die Vorraussetzungen für den Web-Server, das das hostet ist minimal: es läuft schon ab Java 1.2. Auch für den Client sind die Anforderungen wirklich gering: Internet Explorer 4+, FireFox 1.2+ bzw. jeder HTML 4.0 Browser. (Vielleich sieht daher das LaF von AjaxSwing standardmäßig so schrottig aus. Lässt sich aber alles anpassen.) Probleme kann es im Einzelfall etwa beim Eventing geben, wenn der Browser nicht alle Events weitergibt. Auch bei Multithreaded-Anwendungen kann es Probleme geben, wenn im AWT-Event-Thread neue Threads aufgemacht werden und die die Gui modifizieren (auch mit SwingUtilities.invokeLater/Now). Da AjaxSwing auch schon bei JComponent mit der Abbildung beginnt, laufen auch Bibliotheken wie SwingX. Mit einen SnapshotRenderer für „harte“ Fälle lässt sich auch serverseitig rendern und dann als JPG in die Seite einfügen.

    Thema der Woche: Pull-Parser/StAX

    Nicht immer können und müssen Objektgraphen von XML-Dateien – entweder pur als XML-DOM, oder gemappt über etwa JAXB – im Speicher gehalten werden. Muss nur einmal eine XML-Datei abgelaufen werden (natürlich können in diesem einen Durchlauf dann der XML-DOM aufgebaut werden), kann gut ein Pull-Parser eingesetzt werden.

    Aufgabe: Implementiere einen XHTML-Prüfer, der meldet, ob im img-Tag das Attribut alt gesetzt ist.

    Ext GWT Beispiel mit EditorGrid/GroupingView/GroupingStore

    sshot-1

    GroupingStore<BaseModel> store = new GroupingStore<BaseModel>();

    BaseModel c1 = new BaseModel();
    c1.set( „title“, „Java 1“ );
    c1.set( „category“, „JAVA“ );
    c1.set( „price“, 100 );
    store.add( c1 );

    BaseModel c2 = new BaseModel();
    c2.set( „title“, „Java 2“ );
    c2.set( „category“, „JAVA“ );
    c2.set( „price“, 200 );
    store.add( c2 );

    BaseModel c3 = new BaseModel();
    c3.set( „title“, „C#“ );
    c3.set( „category“, „.NET“ );
    c3.set( „price“, 100 );
    store.add( c3 );

    List<ColumnConfig> config = new ArrayList<ColumnConfig>();
    config.add( new ColumnConfig( „category“, „Kategorie“, 200 ) );
    config.add( new ColumnConfig( „title“, „Titel“, 100 ) );
    config.add( new ColumnConfig( „price“, „Preis“, 100 ) );

    store.groupBy( „category“ );

    final ColumnModel cm = new ColumnModel( config );

    GroupingView view = new GroupingView();
    view.setShowGroupedColumn( false );
    view.setForceFit( true );
    view.setGroupRenderer( new GridGroupRenderer()
    {
      public String render( GroupColumnData data )
      {
        String header = cm.getColumnById( data.field ).getHeader();
        String items = data.models.size() == 1 ? „Eintrag“ : „Einträge“;
        return header + “ “ + data.group + “ (“ + data.models.size() + “ “ + items + „)“;
      }
    } );

    EditorGrid<BaseModel> grid = new EditorGrid<BaseModel>( store, cm );
    grid.setView( view );
    grid.setBorders( true );
    grid.setAutoHeight( true ); 

    ContentPanel cp = new ContentPanel();
    cp.add( grid );
    cp.setHeight( 500 );
    RootPanel.get().add( cp );

    Will ich eigentlich RIA mit Ext GWT?

    Nach dem ich nun eine Woche eine Anwendung mit Ext GWT (GXT) zusammengebaut habe, kommen mir Zweifel, ob Ext GWT überhaupt das richtige für meinen Anwendungsfall ist. Einige Fragen in dem Zusammenhang:

    Ist Ext GWT 2.0 M2 gut gewählt?

    Ext GWT 2 gibt es noch gar nicht lange (http://extjs.com/blog/2009/05/20/ext-gwt-20-milestone-2-released/), neues M2 Release also vor 2 Wochen, und die Komponenten sind grundsätzlich toll. Auf der Demo Seite http://extjs.com/examples-dev/explorer.html#overview kann man sich das anschauen.

    Probleme: Ext GWT 2.0 hat in meinen Augen sehr vielen Änderungen in der API. Quellcode für Ext GWT 1.0 findet man im Netz, doch viele Beispiele lassen sich nicht ohne weiteres auf die 2er Version übertragen. Wer migrieren will/muss wird noch seinen Spaß bekommen. Einige Beispiele laufen in der Demo nicht (http://extjs.com/examples-dev/explorer.html#grouping), bei anderen fehlt der Quellcode im Demo. Da muss man dann die Quellen laden.

    Laut Ankündigung müsste GXT schon längst fertig sein.

    Was passt mir an Ext GWT nicht?

    • Die Dokumentation. Einfache Beispiele zu finden kostet viel Zeit. Es fehlt so etwas wie ein Java-Almanac für Ext GWT. Aus komplexen Szenarien kann man sich einfache Beispiele ableiten, von dort diese auf seinen Anwendungsfall übertragen. Selbst die Beispiele aus dem ShowCase könnten einfacher sein, denn der ShowCase nutzt interne “Demo-Datengeber-Klassen”. Hier wären in meinen Augen einfachere Beispiele gut gewesen, die wirklich eigenständig sind um schnell die wichtigsten Schnittstellen ablesen zu können.
    • Compiliere ich meine Programme, so lassen sich im Browser plötzlich keine Textfelder mehr selektieren und den Cursor sieht man nicht.
    • Das Layout-Handing. Da die Ext GWT Anwendungen auf eine “Bildschirmseite” kommen, und weniger auf einem beliebig langen HTML-Dokument, gibt es Probleme mit den Höhen. Manches mal sind die Elemente in einem Scroll-Container zu klein, dann zu groß. Ich habe viel Zeit damit verbracht, große Tabellen in den richtigen Größen auf den Container zu setzen.
    • Während das Orginal-GWT viele eher den typischen Web-Charakter hat, basiert Ext GWT auf dem Applikationsgedanken: (Popup-)Menüs in Web-Applikationen? Hm …
    • Anpassungen des Aussehens sind eine Qual. Die CSS-Datei gxt-all.css ist 6478 Zeilen lang, gxt-gray.css noch mal 443! Wer viel Zeit hat, kann einmal versuchen, den Font 2 Pixel größer zu setzen. Dazu müssen natürlich auch die Icons passen. Strg++ geht nicht einfach so.
    • Es “erfindet” GXT eigentlich alles neu: Widgets, Container, Layout-Manager, Grafik-Einbindung, …. Besonders blöd ist das bei den Grafiken, wo Google mit Proxies einen schönen Ansatz fährt.
    • Es gibt keinen Gui-Builder, für GWT aber schon.

    War Ext GWT eine gute Wahl, oder hätte es doch SmartGWT sein sollen?

    Das weiß ich nicht, denn mit SmartGWT habe ich nur einfache Beispiele programmiert. Ext GWT hat aber den gleichen RIA-Ansatz wie SmartGWT, und unterscheidet sich nicht großartig im Ansatz. Die GXT-Komponenten sehen auf jeden Fall super aus. Daher hat mich GXT ja auch so angezogen. Wer hübscher ist gewinnt 🙂 Die Komponenten sind fancy und meine Lieblinge sind http://extjs.com/examples-dev/explorer.html#advancedcharts (cool), http://extjs.com/examples-dev/explorer.html#gridplugins, http://extjs.com/examples-dev/explorer.html#grouping, http://extjs.com/examples-dev/explorer.html#filtertree, http://extjs.com/examples-dev/explorer.html#gridtogrid.

    Ist ein RIA-Framework wie GXT immer gut?

    Das ist eigentlich die zentrale Frage.

    • Die Standard-Komponenten von GWT sind etwas “einfach”. Natürlich nutzt GXT das GWT als Infrastruktur, aber hat sich in meinen Augen weit davon entfernt.
    • GXT versucht, alle Desktop-Komponenten in den Browser zu bekommen. Und so bekommen wir immer mehr und mehr tolle Komponenten. Das hat aber seinen Preis! Der generierte JavaScript-Code wird schnell astronomisch. Und die Anwendungen sehen eben nicht mehr so aus wie Webseiten. Das ist mal gewollt, mal nicht gewollt. Ein richtig und falsch gibt es nicht! Wer aber vollständige Applikationen im Web-Schreiben möchte, der bekommt mit GXT ein gutes Framework. Alles ist vorbereitet und das Look-and-Feel konsistent.
    • Änderungen des Aussehens sind aufwändig. Wenn man also HTML/CSS sehen und damit designen möchte, ist mit GXT nicht optimal bedient. (Als nächstes teste ich http://code.google.com/p/google-web-toolkit-incubator/wiki/UiBinder — wenn der denn mal im Trunk ist…) Soll ein (echte) Webdesigner eine GXT-Anwendung skinnen, wird der sicherlich nicht glücklich werden. Es ist eben nicht Design first.

    SwingX 1.0

    Ich habe ja schon nicht mehr damit gerechnet: SwingX hat es in den 1.0 Status geschafft. Die Fixes/Enhancements sind aber eigentlich minimal und die Freigabe hätte schon früher kommen können …

    Und wenn man liest, “From now on, the Java 5 compatibility will no longer be maintained.” heißt das nur, dass die kommenden Versionen mindestens Java 6 erwarten; SwingX 1.0 ist selbstverständlich unter Java 5 lauffähig.