Archiv für die Kategorie ‘Java’

Quelle aller Worte: Internationalisierung mit GWT UIBinder

Samstag, 10. September 2011

Als Gott sah was beim Turmbau zu Babel passierte, entschied er sich die Sprache der Menschen zu teilen, so dass der Eine den Anderen nicht mehr verstehen möge. Ähnlich scheinen sich die Designer von GWT gefühlt haben, als sie die Internationalisierung von GWT 2.x entworfen haben.

Das Internationalisierungsprinzip von GWT ist an sich sehr einfach. Man nehme ein paar properties-Dateien, inkludiere das I18N-Modul und lasse sich die entsprechende Java-Datei generieren. Soweit so gut. Leider nicht so, wenn man den UIBinder verwenden möchte. Hier haben sich bereits diverse verzweifelte Entwickler einen “abgebrochen” um herauszufinden, welche Dateien an welcher Stelle liegen müssen, damit man in den jeweiligen ui.xml-Dateien eine vernünftige Übersetzung erhält.

Grundsätzlich gilt, dass Übersetzungs-Keys aus ui.xml-Dateien immer im Package com.google.gwt.i18n.client mit dem Namen LocalizableResource_[locale].properties liegen müssen. Diesen Umstand verdanken wir dem MessagesWriter, der vom UiBinderGenerator herangezogen wird. Dieser generiert zwar die Messages-Dateien für alle Keys, die sich in einer ui.xml-Datei befinden in eine eigene Messages-Klasse, importiert aber mittels

writer.write("import static com.google.gwt.i18n.client.LocalizableResource.*;");

ausschließlich die Übersetzungen aus LocalizableResource. Soweit so gut, möchte man meinen. Problematisch wird es dann, wenn man Übersetzungen sowohl im Java-Code, als auch im in den ui-Dateien haben will. Typisches Beispiel sind hier Tabellen vom Typ CellTable, deren Spalten normalerweise in den View-Klassen definiert werden. Will man hier nun sprachlich angepasste Spaltenköpfe haben, muss man diese mittels einer Messages-Klasse übersetzen.
In diesem Moment steht man vor der Frage: Möchte ich zwei Übersetzungsdateien verwalten, in denen ggf. Keys doppelt auftreten, oder möchte ich nur Eine für beide Mechanismen haben?

Ohne auf die unsäglichen und teilweise unpraktikablen Varianten einzugehen, die sich im Netz bereits befinden bzw. die kryptisch auf der GWT-Dokumentation vorgestellt wurde, stelle ich hier meine zwei Lösungen vor, die ich für sinnvoll erachte.

Variante 1: ui:with

Die klassische Variante. Man nehme einfach die existierenden Messages und importiere sie wie folgt in die ui.xml-Datei.

 <ui:with field='messages' type='com.my.app.MyMessages'/>
 <g:Label text="{messages.hello}" />

Diese Variante hat den Vorteil, dass man auf die komplette Bandbreite der Erweiterungen, die für Messages-Klassen bereitgestellt wurde zugreifen kann.

Variante 2: Customized Messages-Interface

In Variante 1 nutzen wir den Mechanismus, der eigentlich für den Java-Code gedacht war für den UiBinder. Natürlich geht es auch anders herum. Wir “kopieren” einfach den Mechanismus der MessagesWriter-Klasse. Wir bauen uns ein Interface und importieren die LocalizableResources.

package com.mypackage;

import static com.google.gwt.i18n.client.LocalizableResource.*;

public interface MyAppMessages extends Messages {
  @DefaultMessage("Example")
  String example();
}

Der GWT-Compiler greift hier einfach das Interface und verknüpft die Methoden mit den Keys aus der jeweiligen LocalizableResource-Datei. Damit lassen sich auch innerhalb von Java-Dateien die Keys aus LocalizableResource verwenden.

Achtung:
Leider gibt es nicht die Möglichkeit LocalizableResource-Dateien über den i18n-Aufruf in eine Klasse übersetzen zu lassen, da LocalizableResource bereits ein existierendes Interface ist und die Übersetzung zu einer sogenannten zyklischen Vererbung führt.

Fazit

Mit beiden Varianten lässt sich das bestehende Problem lösen, dass man nur einen Mechanismus zur Internationalisierung innerhalb von GWT-Projekten nutzen möchte. Welche der beiden zum Einsatz kommt hängt stark von der bestehenden Struktur und – was nicht zur vernachlässigen ist – von der Motivation des Entwicklers, welche Schritte für ihn in seinem Arbeitsprozess besser integriert werden können, ab. Während Variante 1 den Aufruf des i18n-Creators erfordert, muss in Variante 2 das Interface immer manuell angepasst werden.

Java + Groovy: Sample-Project PdfRenderer auf Github

Mittwoch, 29. Dezember 2010

Das Buch “Groovy in Action” führt den Begriff “Beauty through Brevity” ins Feld, der auch gern in Verbindung mit Scala oder Clojure einhergeht. Lässt man diese Sprachen für sich alleine mag das auch gelten, doch für viele Entwicklungen, die noch mit älterem Java-Code arbeiten, ist es wichtig, dass die Kombination verschiedener Sprachen ein stimmiges Gesamtbild abgibt – und hier lauern gern die ein oder anderen Stolperdrähte.

Unter https://github.com/Zigu/PdfRenderer habe ich ein älteres Projekt von mir in ein neues “Misch-Gewand” geworfen und geschaut ob es funktioniert. Und ja, das tut es – mit Einschränkungen.

Grundsätzlich kann man sagen, dass die Kombination Maven2 + Java + Groovy eine sehr angenehme Sache ist, wenngleich der Weg dorthin etwas dornig war. Insbesondere durch die maßlos veraltete Dokumentation von GMaven – ich verzichte bewusst auf eine Verlinkung – wodurch nicht klar war, wie einfach es eigentlich ist Maven dazu zu bringen auch Groovy-Code zu verarbeiten.

Vorsicht ist allerdings geboten beim Einsatz von Closures in Vererbungshierarchien. Es gibt in Groovy 1.7.6 noch ein Problem, wenn eine konkrete Kindklasse aufgerufen wird, deren abstrakte Oberklasse ein Closure enthält, dass private Felder dieser Elternklasse verwendet. Eine kurze Skizzierung des Problems:

abstract class A {
  private List _myList;
  private ListElementHandler _handler;

  public void handleList() {
    _myList.each{ _handler.do_something_with(it) }
  }
}

class B extends A { ... }

class C {
  public void call() {
    def b = new B()
    b.handleList()
  }
}

Leider kommt es im Zuge des Abarbeitung der Klasse C zu einer Exception, dass das Feld _handler nicht zur Klasse B gehört. Dieses Problem ist bereits in den Groovy-Bugtracker aufgenommen worden.

Trotz dieser kleineren Hürden, verlief die Umstrukturierung ziemlich glatt und am Ende entstand größtenteils ein stimmiges Gesamtbild mit einigen sehr angenehmen “Abkürzungen” dank Groovy.

Erster Blick auf GWT 2.1.1 Requestfactory

Donnerstag, 16. Dezember 2010

Vor einigen Tagen wurde der erste Release-Kandidat for GWT 2.1.1 veröffentlicht. Was dieser enthält kann man hier nachlesen.

Nachdem ich bereits über einen Workaround für GWT 2.1 bezüglich des RequestFactory-Einsatzes mit Spring gesprochen habe war für mich vor allem das neue RequestFactory-API interessant. Dieses hat jetzt in u.a. in der Service-Annotation einen locator mit dem man auf Klassen referenzieren kann, die keine statischen Methoden enthalten. Ein schönes Beispiel für den Einsatz findet sich in der GWT Google Group (zugehöriger Thread).

Der folgende Auszug ist aus dem eben genannten Thread.

public class SpringServiceLocator implements ServiceLocator {
        @Override
        public Object getInstance(Class<?> clazz) {
                HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
                ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(request.getSession().ge tServletContext());
                return context.getBean(clazz);
        }
}

“I annotate my RequestContext with the GWT @Service annotation (not the
Spring one). Example below:”

@Service(locator = SpringServiceLocator.class, value =
AddressService.class)
public interface AddressRequest extends RequestContext {
        // Get address by id
        Request<AddressProxy> get(Long id);
}

Clean Code und Refactoring – Das riecht aber komisch

Mittwoch, 08. Dezember 2010

Wer sich schonmal über einen Quellcode gesetzt hat – über den eigenen oder eines anderen Entwicklers – und sich nach kurzer Zeit gefühlt hat, er müsse mal kurz weggehen um frische Luft zu bekommen, der hat es wohl mit einem sogenannten “Code smell” zu tun.

“Code smell” ist ein Begriff, dem man – je länger man in der Softwareentwicklung unterwegs ist – mittlerweile kaum noch entgehen kann. Sei es nun im eigenen Projekt oder in der aktuellen Fachpresse. Für jene, denen dieser Begriff noch nicht geläufig ist, sei er wie folgt kurz erklärt:

Ein “Code smell” ist ein Symptom für tieferliegende Programm-Probleme, das sich durch unschönen bzw. verwirrenden Quellcode zeigt.

Eine konkretere Herleitung findet sich u.a. auf der entsprechenden Wikipedia-Seite. Wie die obige Erklärung bereits andeutet sind Code smells eine relativ subjektive Angelegenheit. Wann ist eine Methode zu lang, oder was sind angemessene Variablenbezeichner? Eine Antwort auf diese Fragen muss man am Ende vor allem für sich selbst finden.
Was ist jedoch zu tun, wenn man über solchen Code stolpert? Wie schreibe ich z.B. eine Methodensignatur so um, dass ich sicher sein kann, dass mein Code immer noch wie vorher funktioniert? Hierfür möchte ich 3 Bücher vorstellen, die man getrost als Leitfaden für Erkennung, Vermeidung und Aufräumaktionen verwenden kann.

Clean Code stellt viele Aspekte für unschönen Code vor, die man bei der täglichen Arbeit zwar bemerkt, aber bei denen man unter Umständen nicht immer weiss wie man sie besser machen kann. Der Autor gibt sich hierbei nicht den Anspruch des allumfassenden “So-und-nicht-anders”, sondern lässt Freiraum für eigene Entscheidungen.
Martin Fowler stellt verschiedene Refactoring-Mechanismen vor und erklärt die Motivation, die hinter jedem einzelnen steckt. Da es sich hierbei u.a. auch um sehr triviale Refactorings handelt, wirkt das Buch zeitweise etwas trocken.
Dies wird jedoch durch die Darstellung verschiedener “Bad smells in Code” teilweise wieder ausgeglichen.
Ähnlich aufgebaut wie das “Refactoring”-Buch werden hier verschiedene Code-Probleme anhand einfacher Beispiele erklärt und deren Lösung durch Patterns oder in Richtung eines Patterns vorgestellt.

Wer sich etwas konkreter mit Refactoring an sich beschäftigten will, seien diese Bücher ans Herz gelegt. Wer meint, er schreibe bereits guten Code, möge folgenden Auszug aus “Clean Code” selbst prüfen: Guten Code erkennt man an der Metrik der WTF/minute eines Code Reviewers.

Statische Schleusen – GWT 2.1 RequestFactory mit Spring

Freitag, 26. November 2010

Manchmal ist Vorfreude die schönste Freude, aber wenn die Vorfreude die einzige Freude ist, dann stimmt etwas nicht. So auch bei den heißersehnten RequestFactories von GWT 2.1. In Erwartung, dass damit der Zugriff auf Domain-Objekte/Entities stark vereinfacht werden soll, kam es – zumindest bei mir – zu einem jähen Erwachen, nachdem ich versuchte die RequestFactory auf ein Service-Klasse verweisen zu lassen, die von Spring verwaltet wird.

Die Entwickler der RequestFactory haben sich nämlich ein schönes Konstrukt in der Klasse ReflectionBasedOperationRegistry ausgedacht, welches es nur gestattet entweder statische Methode oder eine Methode der Entity selbst aufzurufen.

boolean isInstance = InstanceRequest.class.isAssignableFrom(requestMethod.getReturnType());
if (isInstance == Modifier.isStatic(domainMethod.getModifiers())) {
  throw new IllegalArgumentException("domain method " + domainMethod
          + " and interface method " + requestMethod
          + " don't match wrt instance/static");
}

Zur allgemeinen Verteidigung: in der kommenden Version GWT 2.1.1 wurde diese Einschränkung angeblich schon ausgebaut. Da zum aktuellen Zeitpunkt allerdings nicht klar ist, wann diese Version veröffentlicht wird, muss man sich mit anderen Mitteln behelfen. Eines dieser Mittel möchte ich kurz vorstellen.

Die Idee hinter dem ganzen ist es einen Adapter zu schreiben, der die notwendigen statischen Methoden für die RequestFactory bereitstellt und diese an den eigentlichen Service delegiert.
Ich gehe davon aus, dass der Leser mit dem Grundprinzip der GWT RequestFactory vertraut ist. Falls nicht, empfehle ich einen Blick auf diese Seite.

Als Erstes möchte ich die RequestFactory vorstellen:

public interface SampleRequestFactory extends RequestFactory {

    @Service(SampleRequestAdapter.class)
    interface SampleRequestContext extends RequestContext {
        Request<List<SampleEntityProxy>> findAll();
    }

    SampleRequestContext sampleRequestContext();
}

Für diejenigen, die sich mit Spring auskennen sei darauf hingewiesen, dass die obige Service-Annotation nicht die von Spring, sondern die von GWT ist.

Weiter im Text …

Der entsprechende Adapter könnte in etwa so aussehen.

public class SampleRequestAdapter {

    private static SampleService _sampleService;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        _sampleService = applicationContext.getBean(SampleService.class);
    }

    public static List<SampleEntity> findAll() {
        return _sampleService.findAll();
    }
}

Soweit, sogut. Bisher ist noch nichts Spannendes passiert. Dennoch ist die Luft bereits erfüllt mit “Entwickler-Magie”. Die offene Frage ist nämlich: Wie wird die Methode setApplicationContext() aufgerufen?

Die Lösung ist zwar nicht zwingend schön, aber wirkungsvoll:

  • Man nehme eine Klasse, die von Spring verwaltet wird.
  • Mache diese Klasse ApplicationContextAware.
  • Gebe dieser Klasse die Adapter als Parameter und rufe für alle die Setter-Methode auf.

Im Code-Gewand sähe dies so aus:

public class ApplicationContextProvider implements ApplicationContextAware {
    private List<Class> _requestAdapters;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (_requestAdapters != null) {
            for (Class requestAdapter : _requestAdapters) {
                try {
                    final Method setter = requestAdapter.getDeclaredMethod("setApplicationContext", ApplicationContext.class);
                    if (Modifier.isStatic(setter.getModifiers())) {
                        setter.invoke(requestAdapter, applicationContext);
                    }
                // ... exception handling code
            }
        }
    }

    public List<Class> getRequestAdapters() {
        return _requestAdapters;
    }

    public void setRequestAdapters(List<Class> requestAdapters) {
        _requestAdapters = requestAdapters;
    }
}

Abschließend muss der Provider selbstverständlich noch für Spring verfügbar gemacht werden.

<bean class="de.sampleproject.ApplicationContextProvider">
  <property name="requestAdapters">
    <list>
      <value>de.sampleproject.SampleRequestAdapter</value>
    </list>
  </property>
</bean>