GWT 2.0.4 Release

Der GWT-Blog http://googlewebtoolkit.blogspot.com/2010/06/gwt-204-is-now-available.html kündigt ein kleines Update an, was unter anderem Probleme bei der Umsetzung des Shift-Operators behebt (Fehler in der JS-Engine von Safari):

basically the issue is that a right shift of a value that is internally stored as a double fails to correctly set the integer flag on the internal value at the end of the shift.

Noch zwei weitere Kleinigkeiten wurden angegangen:

Simple OpenOffice templating with the ODF toolkit

To open an OpenOffice text document and access the text you first have to download odfdom-0.8.5-binaries.zip (Homepage of ODF TOOLKIT) and add odfdom.jar to your classpath together with a recent Xerces implementation like xercesImpl-2.9.0.jar. Then you can start with Java:

File file = new File( "C:/Users/Christian/Desktop/TEMPLATE.odt" );
OdfTextDocument odf = (OdfTextDocument) OdfTextDocument.loadDocument( file );
OdfOfficeText contentRoot = odf.getContentRoot();

The OdfOfficeText gives access to the XML-structure of the document. If you print it out

System.out.println( contentRoot );

it looks like this:

<office:text>
    <office:forms form:apply-design-mode="false"
        form:automatic-focus="false"></office:forms>
    <text:variable-decls>

    </text:p>
</office:text>

With regular XML-DOM operations you can now modify the document.

To use a template you have to a) find a certain element you want to fill b) modify it and c) save the document.

a) For templates you can use user variables. If you set a variable it looks like this in XML:

<text:variable-set office:value-type="string" text:name="COURSETITLE">COURSETITLE</text:variable-set>

(Date and other formats look different!)

b) After your found the XML element either by hand or XPath, you can modify an entry with setNodeValue() or different method if the value is stored in attributes.

c) Save the modified document with odf.save( file );

A simple but complete example to read a document, set variables and write the modified ODT:

import java.io.File;
import java.util.*;
import org.odftoolkit.odfdom.doc.OdfTextDocument;
import org.odftoolkit.odfdom.doc.office.OdfOfficeText;
import org.w3c.dom.*;

public class OdtTemplate
{
  private OdfTextDocument odf;
  private Map<String, String> map = new HashMap<String, String>();

  public static void main( String[] args ) throws Exception
  {
    File file1 = new File( "C:/Users/Christian/Desktop/TEMPLATE.odt" );
    File file2 = new File( "C:/Users/Christian/Desktop/result.odt" );

    OdtTemplate template = new OdtTemplate();
    template.readOdt( file1 );
    template.setVariable( "COURSETITLE", "Donuts backen für Kernkraftfahrer" );
    template.setVariable( "COURSECODE", "DONUT" );
    template.setVariable( "CREATOR", "ull" );
    template.saveOdt( file2 );
  }

  public void readOdt( File file ) throws Exception
  {
    odf = (OdfTextDocument) OdfTextDocument.loadDocument( file );
  }

  public void setVariable( String key, String value )
  {
    map.put( key, value );
  }

  public void saveOdt( File file ) throws Exception
  {
    OdfOfficeText contentRoot = odf.getContentRoot();
    iteratorOverEveryVariableSet( contentRoot.getChildNodes() );
    odf.save( file );
  }

  private void iteratorOverEveryVariableSet( NodeList childNodes )
  {
    for ( int i = 0; i < childNodes.getLength(); i++ )
    {
      Node item = childNodes.item( i );

      if ( item instanceof NodeList )
        iteratorOverEveryVariableSet( item.getChildNodes() );

      if ( item.getNodeName().equals( "text:variable-set" ) )
      {
        String nodeValue = item.getAttributes().getNamedItem( "text:name" ).getNodeValue();
        item.getChildNodes().item( 0 ).setNodeValue( map.get( nodeValue ) );
      }
    }
  }
}

Gedärme: Event Bus für Google Guice

Zwei Konzepte sind für mich zentral: Dependency Injection und Events. Nach dem jetzt DI durch CDI standardisiert ist, steht das für einen Event Bus noch aus. (CDI-kann man aber so interpretieren.) Über eine Lösung habe ich schon geschrieben: http://www.tutego.de/blog/javainsel/2009/03/einfuhrung-in-den-eventbus/. Ein Event Bus sollte natürlich mit einem DI-Container harmonieren. Nutzer von Google Guice können das mit Guts (englisch für Gedärme aber auch Mut) tun: http://kenai.com/projects/guts/pages/Home. Die Doku http://guts.kenai.com/guts-events/apidocs/ zeigt wie es geht:

1. Modul aufbauen

Injector injector = Guice.createInjector(new EventModule(), new AbstractModule()
 {
     @Override protected void configure()
     {
         Events.bindChannel(binder(), Integer.class);
     }
 });

Wie man sieht, wird ein Ereignistyp (hier Integer) registriert. Der Kanal kann auch einen Namen tragen:

Events.bindChannel(binder(), Integer.class, "TOPIC1");

2. Der Kanal wird nun injziert und man kann Ereignisse versenden:

public class Supplier
 {
     @Inject Supplier(Channel<Integer> channel)
     {
         // Send an event to this channel (i.e. to all its consumers)
         channel.publish(10);
         ...
     }
     ...
 }

3. Zum Empfang:

 public class MyClass
 {
     @Consumes public void push(Integer event)
     {
         ...
     }
     ...
 }

SmartGWT 2.2 ist raus

Neues:

    • Selenium Support
    • Support for the prototype pattern to set global defaults without creating a separate hierarchy of subclasses. This is explained in more detail in the release announcement
    • DateRangeItem, MiniDateRangeItem and Relative Date support for DynamicForm
    • Dropdown multi-selects
    • Javadoc enhancements : Over 200 API’s have an overview and relevant Showcase sample in the @see link of the API docs
    • Numerous other fixes and enhancements.
    • Several new Showcase samples

Der Showcase zeigts: http://www.smartclient.com/smartgwt/showcase/. 270 Beispiele sind schon eine stolze Zahl.

 

 

private final ist doof bei JAXB

Eclipse kann beim Speichern Aktionen ausführen. So

  • räumt es automatisch die import-Anweisungen auf,
  • entfernt Weißraum am Ende einer Zeile,
  • setzt @Overide an überschriebenen Methoden

Zum Testen habe ich heute morgen eine Einstellung gesetzt, die mir Attribute, die final gemacht werden können, auch final macht. Fand ich nett.

Doch dann kam der Abend. Plötzlich initialisierte meine App keine JAXB Beans mehr ein. Es kostete mich 10 Minuten herauszufinden, dass diese blöde Einstellung das Problem verursacht. Denn wenn statt

@XmlRootElement( name = "course" ) 
@XmlAccessorType( XmlAccessType.FIELD ) 
public class Course 
{ 
  private String htmlkeywords = ""; 
  private String htmlhead = ""; 

steht

… 
public class Course 
{ 
  private final String htmlkeywords = ""; 
  private final String htmlhead = "";

dann wird JAXB die finalen Variablen natürlich nicht mehr initialisieren können. Meine Beans werden nur gelesen, sodass kein Setter die Variablen verändert – die Beans sind immutable. Die Eclipse Save Einstellung das final magisch dranzusetzen passt dazu nicht.

Tomcat 7 RC 2

Die Tomcat Entwicklung ist nicht die schnellste, aber immerhin ist jetzt der 2 RC unter http://people.apache.org/~markt/dev/tomcat-7/v7.0.0-RC2/ verfügbar. Es gab zuletzt mit der neuen Asnyc-Fähigkeit aus der Servlet 3.0 Spezifikation noch einige Probleme doch sonst sollte alles so laufen wie bisher. Eclipse 3.6 WTP wird auch fit gemacht für Java EE 6 und Servlet 3.0 — wollen wir hoffen, dass das ein produktives Pärchen wird.

Thema der Woche: java.util.Collections, java.util.Arrays

  • Was ist die Aufgabe der Klasse java.util.Collections und Arrays?
  • Gruppiere die Methoden der beiden Klassen.
  • Was ist die Funktion einer Gruppe und warum gibt es die Methoden? Finde zu jeder Methoden einer Gruppe ein Beispiel mit Hilfe einer Quellcodesuchmaschine (http://dev.koders.com/ oder http://www.google.com/codesearch) und nenne den Motivation hinter der Nutzung.
  • Welche Methoden sind in Java 5, Java 6 und Java 7 hinzugekommen. Kann man Gründe für den Zuwachs anführen?
  • Hat Google Collections hat eine weitere Utility-Klasse für Datenstrukturen? Wenn ja, was bietet sie? Wenn nicht, warum ist sie nicht nötig?

Einfaches Testen von Servlets mit string-test

Servlets zu schreiben gehört nun nicht gerade zu meinem Alltagsaufgaben, aber für ein JSON-Endpoint muss ein Servlet nun doch mit ins Boot. (REST-API geht leider nicht.) Einen Test dafür zu schreiben finde ich selbstverständlich, da ich geil auf grüne Balken bin (nein, nicht die Download-Balken). Es kommen für Servlet-Tests unterschiedliche Ansätze in Betracht, die sich grundsätzlich in 2 Gruppen einteilen lassen:

  • Tests mit einem echten (embedded) Servlet-Container
  • Mock-Tests

Dass ich ein Jetty-Fan bin ist vielleicht schon in früheren Posts rübergekommen, aber hier lasse ich meinen Lieblingsserver links liegen und gehe auf die Mock-Tests. Gut funktioniert für mich spring-test. Ein Beispiel.

Das Servlet:

package com.tutego.traida.server.service.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Raplet extends HttpServlet
{
  private static final long serialVersionUID = 6942655630840028053L;

  @Override
  protected void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException,
      IOException
  {
    resp.getWriter().print( "hello " + req.getParameter( "name" ) );
  }
}

Der Test:

package com.tutego.traida.server.service.servlet;

import javax.servlet.ServletException;
import org.junit.*;
import static org.junit.Assert.*;
import org.springframework.mock.web.*;

public class RapletTest
{
  private Raplet servlet = new Raplet();
  private MockHttpServletRequest request;
  private MockHttpServletResponse response;

  @Before
  public void before() throws ServletException
  {
    servlet.init( new MockServletConfig() );

    this.servlet = new Raplet();
    this.request = new MockHttpServletRequest();
    this.response = new MockHttpServletResponse();
  }

  @Test
  public void doGetTest() throws Exception
  {
    request.setParameter( "name", "tutego" );

    servlet.doGet( request, response );

    assertEquals( "hello tutego", response.getContentAsString() );
  }
}

Einfacher geht’s nun wirklich nicht mehr.

Zum Ablaufen des Tests packe in den Klassenpfad:

Zwar bin ich kein Spring Fan Boy, aber mit 2 Archivdateien brennt die Jar-Hölle nicht so heiß (die Abhängigkeiten sind übertrieben).

com.google.appengine.api.datastore.* Typen für GWT serialisieren

Wer Datentypen wie com.google.appengine.api.datastore.Text in seiner Bean nutzt kann die Datentypen serialisieren. Wenn man nun eine Bean mit zum Beispiel dem Typ Text nach GWT serialisiert, der wird bemerken, dass GWT diese Datastore-Datentypen nicht unterstützt. Nun reicht es nicht, eine Kopie von etwa der Klasse Text anzufertigen und in den Quellordner zu setzen, da die Klasse nicht im client-Paket ist. Stattdessen ist ein etwas anderes Vorgehen nötig. Denn um Java-Klassen auch außerhalb vom client-Paket platzieren zu können, ist in der XML-Datei in Eintrag wie der folgende nötig;

<super-source path=""/>

Um das für die Datastore Klassen nicht selbst schreiben zu müssen, können wir auf die Implementierung unter http://www.resmarksystems.com/code/ zurückgreifen. Wir

  1. setzen <inherits name="com.resmarksystems.AppEngineDataTypes"/> in unsere GWT-XML-Datei und
  2. laden http://www.resmarksystems.com/code/appengine-utils-client-1.0.jar und setzten das Jar in unsern lib-Ordner

Das war’s schon. Serverseitig muss nichts gemacht werden.

Erster Milestone von GWT 2.1

Und es gibt tolle Neuigkeiten. Zum einen Data Presentation Widgets (Widgets zur effizienten Darstellung großer Datenmelgen) zusammen mit einem MVP-Framework.

Aus dem GWT-Blog:

With GWT 2.1 Milestone 1, you are one step closer to being able to build business apps for the cloud as easily as ever. Using GWT’s new data presentation widgets and MVP framework, you can create great looking web apps that run fast, whether you’re interacting with 25 records or 25 million records. This is accomplished by designing the widgets to be extremely lightweight, using DIVs and HTML instead of composites and widgets, and an app framework that makes it easy to fetch only the data you need, when you need it.

To make building web apps even faster, you can now use VMware’s Spring Roo to create a functional app with a few commands. All of these tools are available, including the Google Plugin for Eclipse and App Engine SDK, within SpringSource Tool Suite (STS) — providing you an integrated development environment.

And since it’s written using GWT, your resulting app is an HTML5 app that is cloud ready from the beginning.

Hier zu den Release-Notes.

Thema der Woche: Gekonnt mappen mit Annotationen

  • Was haben http://jsefa.sourceforge.net/ und https://args4j.dev.java.net/ gemeinsam?
  • Schreibe ein Programm, welches von der Kommandozeile eine CSV-Datei erwartet. Hilfe bei fehlender Dateiangabe soll gegeben werden.
  • Die CSV-Datei soll eingelesen und als Tabelle in ein HTML-Dokument konvertiert werden. Kommandozeilenargumente bestimmen, ob die Ausgabe auf die Konsole geht oder in eine Datei. Nutze dafür args4j und jsefa.
  • Es gibt gewissen Standards für Kommandozeilenoptionen. Welche sind das und was sind Quellen.

Google App Engine Prerelease 1.3.4 SDK

Die Neuerungen in Kürze:

– Client side bulkloader available with the Python SDK that has a new  configuration syntax and wizard for easier import/export with the datastore. Can be used by enabling remote_api in your Java application
– Applications can now be configured to authenticate with OpenID by selecting the OpenID option when creating your application in the admin console. http://code.google.com/p/googleappengine/issues/detail?id=248. http://code.google.com/p/googleappengine/issues/detail?id=56
– New API to allow App Engine apps to act as OAuth service providers. ttp://code.google.com/p/googleappengine/issues/detail?id=919
– The version update check in the Java SDK now uses https.
– Allow full access to javax.el.* . http://code.google.com/p/googleappengine/issues/detail?id=3157
– Increased the timeout during deployment to 15 minutes
– Fixed an issue with JPA where an illegal cast exception was thrown during the fetch of integer fields
– MemcacheService.setNamespace() is deprecated in favor of
  MemcacheServiceFactory.getMemcacheManager(namespace)
– Support in the SDK for Java 1.5 is being deprecated. These warnings now appear  when starting the SDK

Sehr interessant ist die Remote-API (http://blog.notdot.net/2010/05/Behind-the-scenes-with-remote-api). Über Beispiele in Java werde ich Zukunft berichten.

Neue Job-Börse auch für Java-Entwickler

Wer auf der Suche nach einem neuen Job ist, kann bei webentwickler-jobs.de vorbeischauen. Die Jobbörse ist dediziert für IT-Personen und nicht breit für Softwareentenwickler, Betriebswirtschaftler, Sozialarbeiter und Kaffeekocher (wobei es einige gibt, die sagen, ITler vereinen alles…) Die Stellenanzeigen sind schön unterteilt in die Rubriken Java, Ruby on Rails, Python, PHP, Typo3, .NET und Perl. Im Bereich Java standen heute fast 350 Jobs. Die Seite und ist dazu noch werbefrei und kostenlos.

Buchkritik Pro Jakarta Commons

Harshad Oak, Apress, 1590592832, Februar 2004, 304 Seiten

Harshad Oak beschreibt in dem Buch die bekannten Jakarta Commons Bibliotheken. Nicht jede Funktion wird vorgestellt, sodass das Buch die JavaDoc nicht ersetzt. Eher versucht der Autor die Philosophie und Kernkomponenten zu vermitteln. Es gibt immer wieder komplett abgebildete Listings, die man direkt in die IDE einsetzen und mit den entsprechenden Libs im Klassenpfad direkt ausführen kann. Mich hätte gefreut, wenn hinter den println()-Zeilen gleich die Ausgabe steht, so muss nicht man immer unter das Listing schauen – das geht besser. Die Auswahl an Themen ist OK, nur sein Styleguide ist zum Teil etwas schwach, wenn er etwa Variablen iVowelCnt oder iTestLen nennt. Auch Zeilen wie

char[] aChars = new char[] { ‚a‘, ‚1‘, ‚c‘, ‚2‘, ‚e‘, ‚f‘, ‚g‘ };

überraschen mich (mal wieder). Und was ist das für eine Schreibweise in “SELECT UserName as uName, AgE FROM USER”?

Updates wären natürlich sehr schön. Java 5 spielt keine Rolle, und Java 1.4 wird als aktuell bezeichnet, sodass es NestableException einen größeren Raum einnimmt, als es müsste. An anderer Stelle gibt es den Satz “The Discovery component depends on JDK version 1.1.8 or higher”. Also wenn das nicht gegeben ist… Auch Enum könnte man sich natürlich mit Java 5 sparen. Aber das ist eine allgemeine Kritik auch an den Jakarta Commons, da kann der Autor nichts für. Heute könnte man eine Reihe on Verbesserungen vornehmen und auch Alternativen nennen. Im Bereich Date/Time etwa Joda-Time, bei Collections natürlich Google Collections, usw. Die Validator-Klasse macht mittlerweile Java Beans Validation, ein Standard von Java EE 6, quasi überflüssig und Struts Validator spielt heute keine Rolle mehr. Bei den BeanUtils wäre der Hinweise angebracht, dass man mit dynamisch generieten Bytecode schnellere Lösungen realisieren kann als über Reflection. Fürs Datenbank-Pooling gibt es auch Alternativen, sie wären gut im Kapitel 6 aufgelistet. Commons Logging ist auch nicht unproblematisch, eine tiefere Diskussion hätte der Autor bringen können. Und Thread-Pools könnten auch noch erwähnt werden. In Java 7 ersetzt Objects die Commons Klasse ObjectUtils. Zusammenfassung: Fortgeschrittene Entwickler dürften sich das Buch sparen können und kommen mit der API-Dokumentation – die sehr gut und mit vielen Beispielen bebildert ist – sehr weit. Einige der Commons-Bibliotheken sind zudem heute veraltet und spielen in neuesten Projekten keine große Rolle mehr. Die spannenden und aktuellen Themen kommen im Buch leider etwas zu flach weg.