GWT 2.1 M3 verfügbar

Ankündigung siehe hier: http://googlewebtoolkit.blogspot.com/2010/08/gwt-21-milestone-3-is-now-available.html, download unter http://code.google.com/p/google-web-toolkit/downloads/detail?name=gwt-2.1.0.m3.zip.

Some key features included in this release are built-in history
support in Activities and Places, relationship management within
RequestFactory, and the ability to call instance methods on entities
themselves. The overarching goal was to nail down the API and deliver
on features and functionality that are vital to creating industry-
grade business apps.

Bei M2 gab es eine Reihe von Änderungen, die viele Komponenten deprecated machte – ohne wirklichen Ersatz. M3 werde ich aufspielen und schauen, wie es damit weitergeht. Die Doku für Activities/Places ist für M2 erbärmlich; hier kann Google noch nachbessern. Die Verknüpfung mit Roo ist mir auch noch schleierhaft, mal sehen, wie sich das entwickelt.

Text-Extraktion aus PDF-Dokumenten

Lösung mit iText:

String path = "lala.pdf";
PdfReader pdfReader = new PdfReader( path );

for ( int page = 1; page <= pdfReader.getNumberOfPages(); page++ )
{
  String textFromPage = PdfTextExtractor.getTextFromPage( pdfReader, page );
}

 

Lösung mit PDFBox:

String path = "lala.pdf";
PDDocument pdfDocument = PDDocument.load( path );
String textFromPage = new PDFTextStripper().getText( pdfDocument );
pdfDocument.close();

 

Das Ergebnis unterscheidet sich deutlich, die PDFBox gibt bessere Ergebnisse.

Sehr hübsche Swing Gauge und LCD-Komponenten

Gerrit Grunwald schrieb mit eine kurze EMail und machte mich auf seine Komponenten aufmerksam, die absolut heiß sind.

ishot-1.png ishot-3.png

ishot-2.png ishot-5.png

ishot-4.png ishot-10.png

ishot-11.png ishot-8.png

ishot-9.png Radar.png

ishot-6.png ishot-7.png

ishot-12.png

ishot-13.png

Altimeter.png Clock.png

Compass.pngLevel.png

 

 

Eine großartige Arbeit mit viele Liebe zum Detail. Was mir auch gefällt ist ein Konverter von Fireworks-Dateien in Java 2D Zeichenanweisungen.

PS: Wer noch weitere tolle Swing Bibs kennt, bitte vorstellen und teilen. Weitere von mir gibt es hier: http://www.tutego.de/java/additional-java-swing-components.htm

Commons Lang 3.0 in Beta und (endlich) angepasst auf Java 5

Aus http://commons.apache.org/lang/article3_0.html:

What’s new in Commons Lang 3.0?

Commons Lang 3.0 is (not yet) out, and the obvious question is: "So what? What’s changed?".

The big story

Lang is now Java 5 based. We’ve generified the API, moved certain APIs to support varargsand thrown out any features that are now supported by Java itself. We’ve removed the deprecated parts of the API and have also removed some features that were deemed weak or unnecessary. All of this means that Lang 3.0 is not backwards compatible.

To that end we have changed the package name, allowing Lang 3.0 to sit side-by-side with your previous version of Lang without any bad side effects. The new package name is the exciting and original org.apache.commons.lang3.

There are also, as you’d expect, new features, enhancements and bugs fixed.

The build

We built 3.0 using Maven 2.2.1 and Java 1.5. Needs confirmation before release of actual build details

Migrating from 2.x

Despite the label of backwards incompatibility, in the vast majority of cases the simple addition of a '3' to an import statement will suffice for your migration. For example, using Perl:

find . -type f -name ‚*.java‘ | xargs perl -pi -e ’s/org\.apache\.commons\.lang\./org.apache.commons.lang3./g‘

Maven users will need to update the groupId from commons-lang to org.apache.commons, and the artifactId from commons-lang to commons-lang3.

What’s gone?

Enum package – Java 5 provided enums out of the box, therefore we dispensed with both the deprecated enum package, and the enums package. Instead you should migrate over to the standard Java enum. An EnumUtils class has been born from the ashes of the old code, focused on providing additional functionality to the standard Java enum API.

NestedExceptions – In Java 1.4, the notion that all Throwables could be linked to a cause was introduced. In Lang we had provided a NestedException framework to support the same feature, and now that we’re jumping from Java 1.3 to Java 5 we are remove this feature. The deprecation section below covers one part of ExceptionUtils that remains until we are on Java 6, where the last remaining parts of the JDK appear to have embraced the new cause API.

JVMRandom – This class was introduced in Lang 2.0 to support a Random object built around the system seed. This proved to be both an uncommon use case and one with bugs and so was dropped.

StringEscapeUtils.escapeSql – This was a misleading method, only handling the simplest of possible SQL cases. As SQL is not Lang’s focus, it didn’t make sense to improve this method.

Various Exception classes were removed – the lesson in defining more semantically relevant exception classes is that you can keep on coming up with more and more new classes. Simpler to focus on using the main JDK classes.

The various Range classes in the math package were removed in favour of a new Range class.

All deprecated fields/methods/classes – with a new major version, all of the previously deprecated parts of the API could finally go away.

If you feel that something was unfairly taken away, please feel free to contact the list. In many cases the possibility exists to reintroduce code.

Deprecations

The lone deprecation in 3.0 is that of the notion of ‚cause method names‘ in ExceptionUtils. In Java 5.0 it is still just about needed to handle some JDK classes that have not been migrated to the getCause API. In Java 6.0 things appear to be resolved and we will remove the related methods in Lang 4.0.

New packages

Two new packages have shown up. org.apache.commons.lang3.concurrent, which unsurprisingly provides support classes for multi-threaded programming, and org.apache.commons.lang3.text.translate, which provides a pluggable API for text transformation.

TODO: Add examples

New classes

CharSequenceUtils

EnumUtils

Pair

Range

builder.Builder

exception.ContextedException

exception.CloneFailedException

reflect.ConstructorUtils

reflect.FieldUtils

reflect.MethodUtils

reflect.TypeUtils

text.WordUtils

New methods

What’s fixed in Lang 3.0?

Weitere Änderungen: http://commons.apache.org/lang/upgradeto3_0.html.

So und wann kommt nun Common Collections für Java 5?

Lebenszyklus in Google Guice einführen

Wenn man von Spring kommt ist man gewöhnt, dass es ein Lebenszyklus gibt insbesondere mit

  • @PostConstruct

Standardmäßig gibt es das mit Guice nicht und das ist ziemlich ungünstig, insbesondere wenn man Spring-Projekte migrieren möchte. (Siehe Diskussion http://code.google.com/p/google-guice/issues/detail?id=62)

Allerdings gibt es http://code.google.com/p/guiceyfruit/. Das bildet den Lebenszkylus, vorgeschrieben durch Annotationen (http://code.google.com/p/guiceyfruit/wiki/Annotations), nach. Es wird einfach als Modul mit hinzugenommen, etwa so:

Injector injector = Guice.createInjector( new Jsr250Module(), new MEINModule() );

Mit paketsichtbaren Methoden gibt es aber Probleme: Sie müssen public sein.

Erinnerung: iText ist Affero GPL Lizenz

Von Wikipedia:

In the end of 2009, iText version 5 is released under Affero GPL license. This license is drastically different from the previous license that iText had been

distributed under, in that it requires anyone using iText 5 under a free license to provide the users with the full source of their application. Projects that do not want to provide their source code are required to purchase a commercial license for a non-disclosed price or they cannot upgrade to iText 5. (Projects can continue using previous versions of iText under the more liberal license without any changes.)

Siehe auch:

Sieben XStream Tipps

1. XML-Prolog schreiben

Das kann XStream nicht, aber beim Lesen wird der XML-Prolog überlesen. Man muss den Prolog also von Hand in den Strom schreiben:

Writer w = new OutputStreamWriter(
new BufferedOutputStream(
new FileOutputStream( file ) ), „utf-8“ );
w.write( „<?xml version=\“1.0\“ encoding=\“utf-8\“?>\n“ );
xstream.toXML( value, w );
w.close();

2. Statt voll qualifizierte Klassenamen kann ein anderer Name mit alias() auf dem XStream-Objekt gesetzt werden.

xstream.alias( „Course“, Course.class );
xstream.alias( „Contact“, Contact.class );

3. Statt geschachtelte Elemente sollen gewisse Dinge in Attributen stehen:

xstream.useAttributeFor( TrainerCourse.class, „price“ );
xstream.useAttributeFor( TrainerCourse.class, „languages“ );

4. Statt Elemente von Sammlungen in ein eigenes Container-Element zu setzen, kann das auch ohne Überelement gesetzt werden:

xstream.addImplicitCollection( Trainer.class, „trainerCourses“ );

5. Gewisse Dinge sollen anders geschrieben werden. Konverter sind angebracht:

xstream.registerConverter( new TrainerCourseConverter() );
xstream.registerConverter( new YYYYMMDDConverter() );

Konverter können ganz eigenständig entwickelt werden (1. Bsp.), oder eine Basisklasse (2. Bsp.) vereinfacht das:

class TrainerCourseConverter implements Converter
{
@Override
public void marshal( Object value, HierarchicalStreamWriter writer, MarshallingContext arg2 )
{
TrainerCourse tc = (TrainerCourse) value;
writer.addAttribute( „courseCode“, c.getCourseCode() );
}

@Override
public Object unmarshal( HierarchicalStreamReader arg0, UnmarshallingContext arg1 )
{

}

@SuppressWarnings( „unchecked“ )
@Override
public boolean canConvert( Class clazz  )
{
return clazz.equals( TrainerCourse.class );
}
}

Zweiter einfacher Konverter:

class YYYYMMDDConverter extends AbstractSingleValueConverter
{
@SuppressWarnings( „unchecked“ )
public boolean canConvert( Class clazz )
{
return clazz.equals( Date.class );
}

@Override
public Object fromString( String str )
{
return StringUtils2.isEmpty( str ) ? null : DateUtils.parseDDMMYYYY( str );
}

@Override
public String toString( Object obj )
{
return obj == null ? „“ : DateUtils.formatDDMMYY( (Date) obj );
}
}

6. Elemente mit bekannten Namen sollen in einer CDATA-Umgebung geschrieben werden:

xstream = new XStream( new XppDriver() {
public HierarchicalStreamWriter createWriter( Writer out ) {
return new PrettyPrintWriter( out ) {
boolean isCDATA = false;
@SuppressWarnings( „unchecked“ ) @Override public void startNode( String name, Class clazz ) {
super.startNode( name, clazz );
isCDATA = name.equals( „contents“ ) || name.equals( „description“ );
}
@Override protected void writeText( QuickWriter writer, String text ) {
if ( isCDATA ) { writer.write( „<![CDATA[\n“ ); writer.write( text ); writer.write( „\n]]>“ ); }
else super.writeText( writer, text );
}
};
}
} );

7. XStream soll XML-Dateien selbst laden und speichern (http://xstream.codehaus.org/persistence-tutorial.html)

Verwaltet man viele kleine XML-Dokumenten, etwa Kontakte, kann XStream diese XML-Kontakt-Dateien automatisch anlegen, lesen und aktualisieren. Der Tick ist, dass man alle Daten in eine spezielle Map, Set oder List setzt. Die XStream-Implementierungen der add(), put(), get(), … Methoden sind so realisiert, dass die Daten beim add()/put() in die Datenstruktur und gleichzeitig XML-serialisiert als Datei ins Dateisystem kommen. In welches Verzeichnis sagt die FilePersistenceStrategy.

PersistenceStrategy strategy = new FilePersistenceStrategy(new File(„/tmp“));
Map  map= new XmlMap( strategy );

Die Dateinamen sind etwas merkwürdig, etwa long@12345676, aber dass ist kein Wunder, denn der Dateiname kodiert den Schlüssel, denn XStream muss ja bei einem get() etwa die XML-Datei wiederfinden.

jRTF = a new library for building RTF documents

jRTF is a simple library to generate RTF documents and to fill RTF template files. The syntax is compact and non-verbose which makes it to some kind of DSL for RTF documents. Its under the BSD license.

The Basics

Write a simple RTF document to a file:

Rtf.rtf().p( "Hello World" ).out( new FileWriter("out.rtf") );

Special RTF-characters like "{", "}", "\" are encoded automatically. "\t" will stay tab and "\n" will be converted to a new paragraph.

The static document() creates a new RTF document object and the p() method is short for "paragraph". The out() method finally writes the output to an Appendable (a Writer for example) and out() without arguments or toString() returns the RTF document as String. The p()method is quite flexible because you add as many parameters as you like. If during building or writing of the file some exceptions will occur they are all of type RtfException which itself is a RuntimeException. So I/O errors during writing will be wrapped in this RtfException.

Because jRTF makes heavy use of statics methods the programs can be very concise and compact with static imports. Let’s assume the following (static) imports for the next examples:

import static com.tutego.jrtf.Rtf.rtf;
import static com.tutego.jrtf.RtfDocfmt.*;
import static com.tutego.jrtf.RtfHeader.*;
import static com.tutego.jrtf.RtfInfo.*;
import static com.tutego.jrtf.RtfFields.*;
import static com.tutego.jrtf.RtfPara.*;
import static com.tutego.jrtf.RtfSectionFormatAndHeaderFooter.*;
import static com.tutego.jrtf.RtfText.*;
import static com.tutego.jrtf.RtfUnit.*;

Sections

A RTF document consists of paragraphs which itself are collected in sections. A section is some kind of mini document with own header, footer, margins, columns. Most documents consist of only one section.

The p() method of the Rtf class is just a façade for the following

rtf().section( p( "Hello World" ) ).out( new FileWriter("out.rtf") );

If there will be more sections they are accumulated this way:

rtf().section(xx).section(xx).section(xx).out(xx);

Paragraphs and Formattings

The following RTF document consists of several paragraphs and text formattings:

rtf().section(
   p( "first paragraph" ),
   p( tab(),
      "second par",
      bold( "with something in bold" ),
      text( "and" ),
      italic( underline( "italic underline" ) )    
    ) 
).out( out );

The declaration of the section method is:

  • Rtf section(RtfPara... paragraphs)

For building paragraphs the RtfPara class offers two useful static methods: p(RtfText...) and a convenience method p(Object...). So you either build a paragraph with a collection of RtfText objects or you pass a sequence of objects which will be converted to Strings (if the type is not already RtfText). null elements in the sequence will be ignored.

A String can be wrapped in a RtfText object via the static method text(String) of the RtfText class. There is also the vararg methodtext(Object...) with is the foundation for p(Object...).

Beside of using ordinary text there are a couple of special methods like tab(), bullet(), currentDateAbbreviated(), shortHyphen() and more.

Special Text and Pictures

A footnote (rtf() omitted for brevity):

p( text("Read this book"),
   footnote( "The joy of RTF" ),
   text(".") )

Using a field requires two text blocks: One for the format description and one for a value a reader can show (most recent calculated result of the field).

field( p("time \\@ \"dd.MM.yyyy\""),
       p("20.06.2010") )

Because there are so many different fields and time is a common case there is an extra class RtfFields with static utility methods. Insert a time field with a pattern like this: timeField("hh:mm:ss"). Or the number of pages for a header:

p( pageNumberField(), "von", sectionPagesField() )

Pictures are part of a paragraph. The source is given by an URL or InputStream. If the resource is not available a RtfException will be thrown during writing.

p( picture( getClass().getResource( "folder.png" ) )
   .size( 64, 64, PIXEL )
   .type( AUTOMATIC )
)

You can explicitly set the picture type to PNG or JPEG, but usually PictureType.AUTOMATIC will do the job.

Paragraph Formatting

It you want a paragraph with bullets at the beginning use ul() instead of p():

section(
  p( "first paragraph" ),
  ul( "bullet1"),
  ul( "bullet2"),
  p( "another paragraph" )
)

The p() methods return an RtfTextPara object which allows formatting the paragraph according to the builder pattern. Aligning a paragraph is done this way;

p( text("centered and indented") ).alignCentered().indentFirstLine( 0.25, RtfUnit.CM )

RtfUnit is an enum. RTF uses a quite unique measurement (twips) but with the enum there is no need to know anything about twips.

If you want to use tabs do this:

p( "1\t2\t3" ).tab( 3, CM )
              .tab( TabKind.CENTER, TabLead.DOTS, 9, CM )

Additional to p() there is a method pard() where the paragraph styles are not inherited to the following paragraph.

Tables

Tables are a bit tricky in RTF because there isn’t a concept of a table but just the concept of a row. In total a section can contain two different types of blocks: paragraphs and rows. While p() lets you insert a regular paragraph the method row() lets you insert a row.

p( "lala" ),
row( "Number", "Square" ),
row( 1, 1 ),
row( 2, 4 ),
p( "lulu" )

While the result type of p() is RtfTextPara the result type of row() is RtfRow. With RtfRow you style the hole row in a similar way you style the paragraph.

row(
  bold( "S" ), bold( "T" )
).bottomCellBorder().topCellBorder().cellSpace( 1, CM ),
row(
  "Good", "nice"
). cellSpace( 1, CM )

A Bit of Style

In order to use different fonts and colors a header has to precede the section:

rtf()
  .header(
    color( 0xff, 0, 0 ).at( 0 ),
    color( 0, 0xff, 0 ).at( 1 ),
    color( 0, 0, 0xff ).at( 2 ),
    font( "Calibri" ).at( 0 ) )
  .section(
        p( font( 1, "Second paragraph" ) ),
        p( color( 1, "green" ) )
  )
).out( out );

This header is setting 3 colors and one font. Every color and font is identified by an index. This index is used later to identify this color and font. The numbering starts with 0. If there is no font given, Times will be the default font at position 0.

Some formats and styles are bound to a section, like a header. Let’s set a header for all pages in that section:

section(
  headerOnAllPages(
    p( "Date:", currentDate() )
  ),
  p( "bla bla bla " )
)

Metadata (Info, Document Formattings)

A RTF document can have some associated meta data: in a header, info or document info block. You can set this on the rtf() object:

rtf()
  .info( author("christian"), title("without parental guidance") )
  .documentFormatting( defaultTab( 1, CM ),
                       revisionMarking() )
  .section("")
  .out( out );

Templating with jRTF

jRTF is not able to read and change existing RTF documents (also I encourage programmers to extend jRTF) but you can inject RtfTExt in slots. To do so prepare a RTF document and define "variables" with are framed in %%. If for example an address has to be written in in the RTF document put a definition like

%%ADDRESSLINE1%%

in the regular text. (Take care not to change the formattings in between. If jRTF is not substituting your variable open the RTF file and check if the variable is really in the format %%VARIABBLE%%.)

To substitute use the following jRTF API:

Rtf.template( new FileInputStream("template.rtf) )
   .inject( "ADDRESSLINE1", "tutego" )
   .inject( "ADDRESSLINE2", bold("Sonsbeck") )
   .out( FileOutputStream("out.rtf") );

The key is always of type String but the value argument for inject() goes to RtfText.text(Object). That means regular Strings or formatted RTF text is both fine.

jRTF Design Decisions

Several facts drove the design of jRTF.

  1. The main design of the API was driven by ease of use. Take a paragraph and text for example. They are represented by the classes RtfPara andRtfText. But most methods are overloaded and as an alternative to RtfPara/RtfText they simply accept a regular String for the common usage or even an sequence of objects with are gracefully converted.
  2. jRTF is able to run on the Google App Engine because classes like Color, Font, FileWriter and other black-listed classes aren’t used.
  3. It should not be possible to generate illegal RTF and the library should throw exceptions if strange things happen. (It could do better…)
  4. You will find three kinds of API styles
    1. Hierarchical style
    2. Varargs container style.
    3. Fluent interface style.

The document model of jRTF is strictly hierarchical. A header just can be add to the document but not to a text. A paragraph can just be part of a document but not part of a header, etc.

If more then one element has to be added to some kind of container varargs are preferred and no Collection classes, which are usually used in Java. E.g.: A section can contain several paragraphs, a paragraph several text parts,a document different headers, etc.

The fluent interface design has been usually chosen when an object can have different states: A paragraph can be aligned and centered, a font definition for the header can be set to bold and has a certain site, and so on.

When it comes to the method names of the fluent interface the prefix "no" and the postfix "on"/"off" are used. "on/off" is only used as a suffix when two different methods can toggle the status. So it’s "noLineNumbering()" and not "lineNumberingOff()" because you can’t toggle the line numbering back.

  1. jRTF builds an internal representation of a RTF document first and writes

it later to a stream. Currently jRTF don’t take advantage of that because it could for example accept a font or color definition during writing a text and insert automatically a header information which is written in the second pass.

What’s not supported and how YOU can help

jRTF is grown out of my own need to generate RTF documents. Some background about this project: http//www.tutego.de/ is a German training institute and we wanted to send a RTF in revision mode to every trainer on a regular basis with his contact details, availability and a table with his seminars. The jRTF template mechanism is used to prepare offers if a customers asks for a training. Later I added things because they were just easy to add: pictures, different formating (but does somebody double underline today?) So for me jRTF does the job and probably I will not add a lot more stuff until I need it.

So a lot of things are missing like nice table handling, bookmarks, comments/annotations, drawing objects, sub documents, all object/shape stuff, embedded fonts, GIF images, and a lot more. The current RTF 1.7 standard has too many control words (around 1400). Some of them are easy to add, some conceptual things are a little bit more work. If you are looking to a mature open source alternative take a look at iText RTF (http://itextrtf.sourceforge.net/).

You can help with

  • testing jRTF
  • finding out where jRTF generates wrong RTF and where exceptions should be thrown
  • adding more control words to jRTF to support more formattings/styles
  • add more field
  • uncomment the style support and make it work
  • make the table stuff work, introduce a RtfCell class
  • add GIF and WMF support
  • Builds an enum for different languages so that the hole document, a certain paragraph or text can be in different languages
  • abstract from font, style and color positions in header and introduce names, which are mapped to these positions

Furthermore some design decisions has to be made according to formattings: The problem now is that some formattings can appear at several places. Take paragraph formattings for example. They can appear in header definitions and also "local" with the paragraph itself. I started using a EnumSet for this formattings which have the big advantage that a format can be reused and building is quite nice. But for consistency with the other classes I changes to the builder style which has the disadvantage of heavy source duplication and the lack of reusing a certain style. This problem can be solved in three different ways:

  1. Continue with the builder style and bring people to using styles so that local paragraph formattings are not necessary.
  2. Offer either a builder style and a EnumSet style.
  3. Change to EnumSet and accept that there a two different API "styles".

SwingX 1.6.1 released

SwingX ist nicht gerade bekannt für eine Open Source Lib, die jeden Monat ein fettes Update raushaut. Doch nun gibt es wieder ein paar Änderungen, die SwingX auf die Version 1.6.1 bringt. Karl listet ein paar Neuerungen auf:

  • Added prompt support.
  • Added new Highlighters and HighlighterPredicates and improved the rendering API.
  • Lots of JXDatePicker and JXMonthView improvements.
  • More Mustang sorting improvements.
  • AutoComplete improvements.

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)
     {
         ...
     }
     ...
 }

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.

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.

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.

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.