Archives for posts with tag: Java

Das normale Vorgehen in GWT fuer die Erstellung einer Oberflaeche ist es, die Struktur der Oberflaeche mit GWT-Mitteln festzulegen (hart verdrahtet im Code oder mittels UIBuilder). Dies hat den Nachteil, dass die Oberflaeche dynamisch zur Laufzeit (per Javascript) aufgebaut wird. Dies fuehrt dazu, dass die Inhalte, der so erstellten Seite, nicht indizierbar sind fuer Suchmaschinen.

Ein andere Variante ist es, die HTML-Seiten serverseitig zu erstellen und GWT an die jeweiligen Element zu “kleben”. Die meisten GWT-GUI-Elemente besitzen eine statische “wrap( Element )” Methode, mit der man aus einem HTML-Element das passende GWT-GUI-Objekt erzeugen kann.

Nun ein einfaches Beispiel fuer ein Eingabefeld.

In der HTML-Datei findet sich folgende Zeile:

<input id=”username” type=”text” name=”signin_username” >

Im GWT-Code wird kann daraus wie folgt ein GWT TextBox erzeugt werden:

TextBox textboxUsername = TextBox.wrap( DOM.getElementById( “username” ) );

Verknuepfungspunkt ist id=”username” des HTML-Elements. textboxUsername entspricht nun dem passenden HTML-Element und es kann z.B. der eingebene Wert abgefragt werden.

Growl ist DAS Benachrichtigungssystem unterm Mac, wenn es darum geht, dem Nutzer über neue Mails, abgeschlossene Downloads oder neue Chatnachrichten zu informieren. Was ich bisher nicht wusste, man kann Growl auch über Java ansteuern und entsprechende Benachrichtigungen an Growl schicken.

Ich zeige anhand eines klitzekleinen Beispielprogramms, wie man Growl nutzen kann.

Zuerst muss man sich das SDK von http://growl.info/downloads_developers.php herunter laden. Darin enthalten ist die API-Dokumentation und eine Klasse (com.growl.Growl), die den Zugriff auf Growl wrappt. Falls das heruntergeladenen DMG nicht automatisch gemountet wird, dies nun machen. Unter Bindings/Java finden sich die für Java-Entwickler interessanten Dateien. Im Verzeichnis Bindings/Java/docs befindet sich die API-Dokumentation, die kann man sich lokal kopieren, ist aber nicht unbedingt notwendig. Unter Bindings/Java/Source befindet sich die Wrapper-Klasse für Growl, diese wird gleich benötigt.

In der IDE seines Vertrauens erstellt man nun ein neues Java-Projekt. In das Source-Verzeichnis kopiert man die Growl-Klasse (aus Bindings/Java/Source) inklusive der Verzeichnisse, also com markieren und per Copy & Paste ins Sourceverzeichnis kopieren. Im Normalfall sollte die IDE jetzt meckern, dass sie die Klassen und Packages (com.apple.cocoa…), die im Import-Teil der Growl-Klasse stehen, nicht auflösen kann. Dazu muss in den Klassenpfad des Projektes noch folgendes Verzeichnis mit aufgenommen werden: /System/Library/Java. Nun sollte es keinen Fehler mehr geben, die Import-Anweisung dürften aber noch markiert sein, da die verwendeten Klassen laut Apple deprecated sind.

Growl zeigt bei den Benachrichtigungen noch ein Symbol an, dies sollte in der Regel das Symbol der Anwendung sein, die die Benachrichtigung an Growl sendet. Dazu ist es in unserem kleinen Beispiel notwendig, eine PNG-Grafik mit 32 x 32 Pixeln zu erstellen. Im Codebeispiel wird angenommen, die Grafik ist über folgenden Pfad auf dem System erreichbar: /image/GrowlTest.png.

Hier nun das Codebeispiel, anschliessend noch ein paar Erklärungen zum Code:

package de.yellowshoes.growl.example;
import com.growl.Growl;
public class GrowlExample
    {
    public static void main(String[] args) throws Exception
        {
        String GROWL_EXAMPLE_NOTIFY_01 = "ExampleNotification_01";
        String[] notifications = { GROWL_EXAMPLE_NOTIFY_01 };
        Growl growl = new Growl( "MyApp", notifications, notifications );
        growl.register();
        growl.notifyGrowlOf( GROWL_EXAMPLE_NOTIFY_01,
                             "Ein Beispiel",
                             "Hier steht die Beschreibung des Events" );
        growl.notifyGrowlOf( GROWL_EXAMPLE_NOTIFY_01,
                             "/image/GrowlTest.png",
                             "Zweites Beispiel",
                             "Und noch eine Beschreibung" );
        }
   }

Die Variable GROWL_EXAMPLE_NOTIFY_01 bezeichnet einen möglichen Benachrichtigungsevent, der von Seiten der Applikation an Growl geschickt werden kann. Bei der Initialisierung der Wrapperklasse (Growl growl = new Growl….) übergibt man zuerst den Namen der Applikation (MyApp), die die Benachrichtigungen an Growl schicken wird, anschliessend einen Array mit allen möglichen Benachrichtigungsevents der Applikation und als dritten Parameter ein Array mit allen Benachrichtungsevents, die per default aktiviert sein sollen.

Das Abschicken der Benachrichtigungsevents (growl.notifyGrowlOf….) sollte eigentlich selbsterklärend sein. Der Unterschied besteht hier darin, das beim zweiten Aufruf das eigene Symbol mit angezeigt wird.

Eine Sammlung von Links zur Google App Engine for Java

Einleitung

Es kommt immer wieder vor, dass man keine speziellen ORM-Frameworks für den Zugriff auf Datenbank nutzt oder nutzen kann und zum direkten JDBC-Zugriff zurückgreifen muss oder will. Wie werden die entsprechenden SQL-Statements im Code hinterlegt? Meistens als Konstanten, manchmal wird das Statement auch dynamisch aufgebaut. Was mich immer wieder gestört hat bei der Variante mit den Konstanten, ist die Lesbarkeit der SQL-Statements innerhalb des Codes. Werden die SQL-Statements umfangreicher, so ist es kaum noch nachzuvollziehen, was innerhalb des Statements passiert….wenn Einrückungen und Klammerungen dann auch noch vernachlässigt werden ist es ganz aus. Daher habe ich mir jetzt eine Variante ausgedacht, die eine Lösung darstellt: auslagern der SQL-Statements in Textdateien, die innerhalb der Packages liegen und zur Laufzeit eingelesen werden.

Umsetzung

Die SQL-Statements sollen innerhalb der Klassen als “Konstanten” abgelegt sein, dazu werden sie zur Laufzeit beim Laden der Klasse durch den Classloader initialisiert. Hierzu wird statischer Code verwendet. Eine entsprechende Utitlityklasse, die sich um das Einlesen der Textdateien kümmert, wird hier als Code aufgeführt, ebenso Beispielcode, wie es genutzt werden kann.

package de.maicoda.utilities;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;

import org.apache.log4j.Logger;

public class ResourceReader
{
static Logger log = Logger.getLogger( ResourceReader.class );

/**
* Liest aus dem Classpath eine Resource ein und liefert den Inhalt als String zurück.
* Besonders gedacht zum Einlesen von Textdateien, die innerhalb von Packages liegen.
*
* @param resource Pfad und Name der Resource zusammen, z.B. de/sinform/dwh/Test.sql
* @return Inhalt der Resource als Text
*/
public static String readResourceAsString( String resource )
 {
 ClassLoader classLoader = ResourceReader.class.getClassLoader();
 LineNumberReader reader = new LineNumberReader( new InputStreamReader( classLoader.getResourceAsStream( resource ) ) );

 String zeile = null;
 StringBuffer puffer = new StringBuffer();

 try
    {
    if( reader.ready() )
       {
       while( ( zeile = reader.readLine() ) != null )
          {
          puffer.append( zeile );
          puffer.append( "\n" );
          }
       }
    }
 catch( IOException e )
    {
    log.error( e );
    }
 finally
    {
    if( reader != null )
       {
       try
          {
          reader.close();
          }
       catch( IOException e1 )
          {
          log.error( "Fehler beim Schliessen des Readers für die Datei " + resource, e1 );
          }
       }
    }

 return puffer.toString();
 }

/**
* Liest aus dem Classpath eine Resource ein und liefert den Inhalt als String zurück.
* Besonders gedacht zum Einlesen von Textdateien, die innerhalb von Packages liegen.
*
* @param pfad Pfad (Package), in dem die Resource liegt, z.B. de/sinform/dwh/
* @param resource Name der Resource, z.B. Test.sql
* @return Inhalt der Resource als Text
*/
public static String readResourceAsString( String pfad, String resource )
 {
 if( pfad.endsWith( "/" ) == false )
    {
    pfad += "/";
    }

 return readResourceAsString( pfad + resource );
 }

/**
* Liest aus dem Classpath eine Resource ein und liefert den Inhalt als String zurück.
* Besonders gedacht zum Einlesen von Textdateien, die innerhalb von Packages liegen.
*
* @param packagename   Referenz auf das Package der Resource
* @param resource Name der Resource, z.B. Test.sql
* @return Inhalt der Resource als Text
*/
public static String readResourceAsString( Package packagename, String resource )
 {
 String pfad = packagename.getName().replaceAll( "\\.", "/" ) + "/";
 return readResourceAsString( pfad, resource );
 }
}

Diese Utilitieklasse kann nun wie folgt genutzt werden. Dabei wird davon angenommen, dass die die Textdatei mit dem SQL-Code (NeuerKunde.sql) im gleichen Package liegt wie die Klasse SqlKunde.

private static final String sqlNeuerKunde = ResourceReader.readResourceAsString( SqlKunde.class.getPackage(), "NeuerKunde.sql" );

Das Ergebnis ist nun eine initialisierte Konstante sqlNeuerKunde, in der das entsprechende SQL-Statement zu finden ist. Die Initialisierung findet beim Laden der Klasse statt.

In einem Projekt bekam ich die folgende Fehlermeldung: java.util.zip.ZipException: Cp437

Diese Fehlermeldung wurde durch die Apache Ziptools geworfen, die im Package org.apache.tools.zip beheimatet sind. Genutzt werden sie, um eine Zip-Datei zu erstellen.
Suchen im Netz gaben keine konkreten Hinweise darauf, was genau das Problem sein könnte. Klar, Cp437 bringt man mit Codepages in Verbindung….aber was genau sagt einem die Fehlermeldung? Da es auch keinen StackTrace gab, musste ich tiefer Code debuggen, zu dem ich keinen Sourcecode habe…und siehe da, irgendwann war ich an der Stelle, an der der eigentliche Fehler auftrat. Jetzt tauchte als Fehler kurz folgende Meldung auf, bevor sie in eine ZipException gekapselt wurde: java.io.UnsupportedEncodingException: Cp437.
Mit der Information im Netz gesucht und den Hinweis gefunden, dass die Jar-Datei charsets.jar eingebunden werden muss. Diese findet sich im lib-Verzeichnis der JRE-Installation, ich hatte allerdings das JDK genutzt, dort ist die Datei nicht vorhanden.

Nach einbinden der charsets.jar in den Klassenpfad war der Fehler behoben.

Hier ein Vorschlag, wie man Logger (vielleicht) besser initialisieren kann.

In einem Projekt werden für bestimmte Tätigkeiten gewisse Geldbeträge berechnet. Es existiert nun eine webbasierte Auswertung, in der diese Tätigkeiten aufgeführt werden und die Geldbeträge aufgelistet werden. Am Ende der Liste werden die Geldbeträge summiert angezeigt. In bestimmten Konstellationen, die immer wieder reproduzierbar waren, stimmte der summierte Betrag am Ende der Auflistung nicht. Meistens ging es hierbei um einen Cent. Nach fleissigem Debuggen konnten wir die Stelle eingrenzen auf die Codezeile, in der die double-Werte summiert wurden. Bei bestimmten Beträgen kam eine um einige Kommastellen abweichende Zahl heraus. Also z.B. anstatt einem erwarteten 32,00 wurde ein 31,99 errechnet.

Da wir das Problem kurz vor Feierabend erst fanden, setzte ich mich abends nochmal privat an den Rechner und versuchte das Problem nachzustellen. Erfolgreich. Als Lösung fand sich dann die Verwendung der Klasse BigDecimal. Werden Berechnungen hiermit ausgeführt, konnte ich bis jetzt keine Probleme feststellen.

Nett sind auch die Kommentare zu dem entsprechenden Bugeintrag bei Sun zum dem Thema. Wenn man sich überlegt, dass dieses Problem vielen vielleicht überhaupt nicht auffällt und entsprechende Anwendungen im Bankenbereich mit Java und der Verwendung von double/float geschrieben sein könnten……

In einem konkreten Fall ist es vorgekommen, dass Tabellen in Hibernate nicht sauber gemappt werden konnten. So wurde jede Tabelle auf eine eigene Klasse gemappt. Mussten nun Datensätze über mehrere Tabellen verknüpft eingelesen werden, so wurde im ersten Ansatz die passenden Datensätze der jeweiligen Tabellen eingelesen und dann mittels Javacode miteinander verknüpft. Das Problem war dabei, dass die Einschränkungen (Where-Bedingungen) nicht unbedingt performance-orientiert umgesetzt werden konnten. Wenn man 300 IDs ermittelt und diese dann in eine IN-Bedingungen im Select einbinden muss ist dies nicht gerade performant. Dieser Ansatz ist alles andere als berauschend, also habe ich nochmal etwas nachgelesen und nun folgenden Ansatz gefunden.

Die Tabellen werden jetzt mittels HQL verknüpft und es werden die passenden Datensätze der Tabellen in einem Datensatz zurückgeliefert, eingebetet in eine Liste.

Angenommen wir haben drei Tabellen, deren Hibernateklassen den Namen der Tabellen haben, z.B. Tabelle1, Tabelle2 und Tabelle3. Die Tabellen sind über ID-Attribute miteinander verbunden, z.B. Tabelle1.tabelle1_id.

Möglicher Code könnte dann wie folgt aussehen :

String query_string = "from " +
                     "   Tabelle1 tab1, " +
                     "   Tabelle2 tab2, " +
                     "   Tabelle3 tab3 " +
                     "where " +
                     "   tab1.tabelle1_id = tab2.tabelle1_id " +
                     "and " +
                     "   tab2.tabelle2_id = tab3.tabelle2_id " +
                     "and " +
                     "   tab3.tabelle3_id = :tabelle3_id";

Query query = session.createQuery( query_string );
query.setTimestamp( "tabelle3_id", tab3_id );
query_resultset = query.list();

if( !query_resultset.isEmpty() )
  {
  daten1 = new Tabelle1[ query_resultset.size() ];
  daten2 = new Tabelle2[ query_resultset.size() ];
  daten3 = new Tabelle3[ query_resultset.size() ];

  int i = 0;

  for( Iterator iter = query_resultset.iterator(); iter.hasNext(); )
     {
     Object[] elemente = ( Object[] )iter.next();

     if( elemente.length == 3 )
        {
        daten1[ i ] = ( Tabelle1 )elemente[ 0 ];
        daten2[ i ] = ( Tabelle2 )elemente[ 1 ];
        daten3[ i ] = ( Tabelle3 )elemente[ 2 ];
        }

     i++;
  }

Sollen z.B. nicht die Daten alle im Select aufgeführten Tabellen zurückgeliefert werden, so kann der HQL-Select wie folgt ergänzt werden :

String query_string = "Select " +
                     "    tab1 " +
                     "from " +
                     "   Tabelle1 tab1, " +
                     "   Tabelle2 tab2, " +
                     "   Tabelle3 tab3 " +
                     "where " +
                     "   tab1.tabelle1_id = tab2.tabelle1_id " +
                     "and " +
                     "   tab2.tabelle2_id = tab3.tabelle2_id " +
                     "and " +
                     "   tab3.tabelle3_id = :tabelle3_id";

Jetzt wird nur Tabelle1 pro Datensatz zurückgeliefert.

Als Erinnerung fuer mich: Wenn beim Deployment einer Java-Webanwendung die Libraries mal wieder nicht mit ins Tomcat-Webapps-Verzeichnis kopiert werden, hilft ein Maven -> Update Project Configuration auf dem Projekt.

Growl ist DAS Benachrichtigungssystem unterm Mac, wenn es darum geht, dem Nutzer über neue Mails, abgeschlossene Downloads oder neue Chatnachrichten zu informieren. Was ich bisher nicht wusste, man kann Growl auch über Java ansteuern und entsprechende Benachrichtigungen an Growl schicken.

Ich zeige anhand eines klitzekleinen Beispielprogramms, wie man Growl nutzen kann.

Zuerst muss man sich das SDK von http://growl.info/downloads_developers.php herunter laden. Darin enthalten ist die API-Dokumentation und eine Klasse (com.growl.Growl), die den Zugriff auf Growl wrappt. Falls das heruntergeladenen DMG nicht automatisch gemountet wird, dies nun machen. Unter Bindings/Javafinden sich die für Java-Entwickler interessanten Dateien. Im Verzeichnis Bindings/Java/docs befindet sich die API-Dokumentation, die kann man sich lokal kopieren, ist aber nicht unbedingt notwendig. Unter Bindings/Java/Source befindet sich die Wrapper-Klasse für Growl, diese wird gleich benötigt.

In der IDE seines Vertrauens erstellt man nun ein neues Java-Projekt. In das Source-Verzeichnis kopiert man die Growl-Klasse (aus Bindings/Java/Source) inklusive der Verzeichnisse, also com markieren und per Copy & Paste ins Sourceverzeichnis kopieren. Im Normalfall sollte die IDE jetzt meckern, dass sie die Klassen und Packages (com.apple.cocoa…), die im Import-Teil der Growl-Klasse stehen, nicht auflösen kann. Dazu muss in den Klassenpfad des Projektes noch folgendes Verzeichnis mit aufgenommen werden: /System/Library/Java. Nun sollte es keinen Fehler mehr geben, die Import-Anweisung dürften aber noch markiert sein, da die verwendeten Klassen laut Apple deprecated sind.

Growl zeigt bei den Benachrichtigungen noch ein Symbol an, dies sollte in der Regel das Symbol der Anwendung sein, die die Benachrichtigung an Growl sendet. Dazu ist es in unserem kleinen Beispiel notwendig, eine PNG-Grafik mit 32 x 32 Pixeln zu erstellen. Im Codebeispiel wird angenommen, die Grafik ist über folgenden Pfad auf dem System erreichbar: /image/GrowlTest.png.

Hier nun das Codebeispiel, anschliessend noch ein paar Erklärungen zum Code:

package de.yellowshoes.growl.example;
import com.growl.Growl;
public class GrowlExample
    {
    public static void main(String[] args) throws Exception
        {
        String GROWL_EXAMPLE_NOTIFY_01 = "ExampleNotification_01";
        String[] notifications = { GROWL_EXAMPLE_NOTIFY_01 };
        Growl growl = new Growl( "MyApp", notifications, notifications );
        growl.register();
        growl.notifyGrowlOf( GROWL_EXAMPLE_NOTIFY_01, "Ein Beispiel", "Hier steht die Beschreibung des Events" );
        growl.notifyGrowlOf( GROWL_EXAMPLE_NOTIFY_01, "/image/GrowlTest.png", "Zweites Beispiel", "Und noch eine Beschreibung" );
        }
   }

Die Variable GROWL_EXAMPLE_NOTIFY_01 bezeichnet einen möglichen Benachrichtigungsevent, der von Seiten der Applikation an Growl geschickt werden kann. Bei der Initialisierung der Wrapperklasse (Growl growl = new Growl….) übergibt man zuerst den Namen der Applikation (MyApp), die die Benachrichtigungen an Growl schicken wird, anschliessend einen Array mit allen möglichen Benachrichtigungsevents der Applikation und als dritten Parameter ein Array mit allen Benachrichtungsevents, die per default aktiviert sein sollen.

Das Abschicken der Benachrichtigungsevents (growl.notifyGrowlOf….) sollte eigentlich selbsterklärend sein. Der Unterschied besteht hier darin, das beim zweiten Aufruf das eigene Symbol mit angezeigt wird.