Nov
18

Performance-Messungen bei Hetzner mit/ohne Raid-Controller bzw. SAS

Da unser Entwicklungsserver bei Hetzner immer mal unter sehr hoher IO-Last leidet, stand ein Hardware-Upgrade an. Um zukünftig eine gute Entscheidungsgrundlage zu haben, habe ich auf dem neuen und dem alten System mal ein paar Messungen der IO-Performance durchgeführt.

Die Tests wurden jeweils im Rescue-System mittels bonnie++ ausgeführt. Der RAM wurde mittels Ram-Disk fast komplett gefüllt, um Caching-Effekte zu minimieren.

System 1: EQ5 mit 2x 1,5 TB SATA 7.200u/min im Software-Raid
System 2: EX6 mit 2x 3 TB SATA 7.200u/min im Hardware-Raid (Adaptec)
System 3: EX6 mit 2x 300 GB SAS 15.000u/min im Hardware-Raid (Adaptec)
System 4: EQ8 mit 120 GB SSD

Version 1.96 Sequential Output Sequential Input Random
Seeks
Concurrency Size Block Rewrite Block
K/sec % CPU K/sec % CPU K/sec % CPU /sec % CPU
System1 5 10000M 97955 11 64593 7 135381 8 296.2 5
Latency 422ms 548ms 116ms 113ms
System2 5 50000M 152502 8 96655 7 335732 12 451.1 13
Latency 730ms 573ms 139ms 195ms
System3 5 50000M 188181 11 122138 9 331598 13 687.5 20
Latency 463ms 400ms 143ms 116ms
System 4 5 28000M 212558 14 87064 9 279109 13 +++++ +++
Latency 328ms 3432ms 157ms 2177us

Fazit

Der Raid-Controller holt aus den fast identischen SATA-Festplatten deutlich mehr heraus. Die Geschwindigkeit der verteilten Zugriffe steigt aufgrund des Cachings und einer besseren Optimierung der Zugriffe auf die Festplatten deutlich. Durch den Einsatz der SAS-Festplatten lässt sich dieser Wert nochmals deutlich steigern, was in erster Linie an der mit der doppelten Drehgeschwindigkeit verbundenen reduzierten Zugriffszeit liegen dürfte. Das Ergebnis zeigt sich beispielweise im Rewrite-Test, bei dem Daten gleichzeitig gelesen und geschrieben werden.

Damit lohnt sich der Einsatz der SAS-Platten vor allem bei Servern mit vielen verteilten Zugriffen (vor allem Datenbanken und Systemen mit permanent hoher IO-Last). Eine preiswerte Alternative ist der Einsatz eines Controllers an den vorhandenen SATA-HDDs (diese müssen jedoch dazu neu formatiert und das System neu aufgespielt werden). Insbesondere wenn schon ein Flexi-Pack vorhanden ist, kann man mit 15 bzw. 13 € die Leistung des Servers deutlich steigern.

Zum Vergleich habe ich noch ein System mit SSD herangezogen. Währen die Schreibgeschwindigkeit über derer des SAS-Systems liegt, ist die Lesegeschwindigkeit geringer. Die kombinierte Lese/Schreibgeschwindigkeit liegt im guten Mittelfeld. Bei den zufälligen Zugriffen liegt die SSD in einem für bonnie++ nicht mehr messbaren (extrem guten) Bereich. Damit eignet sich eine SSD für den extrem verteilten Zugriff auf kleine Datenmengen (z.B. als Cache/Swap). Für großen Datenmengen ist sie aufgrund ihrer Größe und dem damit verbundenen Preis pro GB Daten nur bedingt geeignet. Es gilt zu bedenken, dass auch SSDs im Raid-Verbund verwendet werden sollten.

Überprüfung der Ergebnisse

Da es bei bonnie++ ggf. zu Verfälschungen der Ergebnisse durch Cache-Effekte kommen kann, wurden die Tests mit “dd” und “Direct I/O” überprüft:

Lesen: dd if=/dev/VolData2/SSDTest1 of=/dev/null bs=1M count=5000 iflag=direct
Schreiben: dd if=/dev/zero of=/dev/VolData2/SSDTest1 bs=1M count=5000 oflag=direct

Die Ergebnisse sind bis auf System 1 ähnlich den mit bonnie++ ermittelten:

System 1: Lesen 75 MB/s, Schreiben: 75 MB/s
System 2: Lesen 290 MB/s, Schreiben: 151 MB/s
System 3: Lesen 356 MB/s, Schreiben: 209 MB/s
System 4: Lesen 275 MB/s, Schreiben: 216 MB/s

Die Anzeige mittels “dstat” bei System 1 hat gezeigt, dass bei Direct I/O die Zugriffe nicht auf die beiden HDDs im Raid verteilt werden. Dies führt zu einem starken Leistungseinbruch. Interessant ist dies vor allem beim Einsatz von virtuellen Maschinen, da diese in der Regel via Direct I/O auf Container-Partitionen zugreifen (ein ähnliches Verhalten zeigt sich auch im produktiven Betrieb bei uns).

Update vom 9.2.2012

Heute haben wir einen neuen EX 4S aufgesetzt. Dieser hat im Gegensatz zum EX 6 keine “Enterprise SATA HDDs”, sondern ganz normale. Zum Einsatz kam wieder ein Hardware Raid-1 (Adaptec). Hier die Ergebnisse der Messung mit dd und Direct I/O:

System 5: Lesen 314 MB/s, Schreiben: 160 MB/s

Von den HDDs her ist das System mit System 2 vergleichbar, die Leistung ist aber noch ein klein wenig besser. Zum Vergleich noch die Werte einer im selben System verbauten einzelnen HDD, die nicht mit am Raid-Controller hängt (die Werte sind nicht 1:1 vergleichbar, da es sich um eine 1,5 TB HDD handelt).

einzelne HDD: Lesen 118 MB/s, Schreiben: 114 MB/s

Aug
04

Cross-Domain-Ajax POSTs mit JSONP und IFrames

Dieser Artikelt stellt eine Kombination aus IFrame- und JSONP-Kommunikation über Domaingrenzen hinweg vor, welche es erlaubt, Daten sicher (via POST) zu übertragen.

Anforderung

Für unser Produkt TeamProQ sollte es möglich sein, in eine Kundenwebseite ein Login-Formular einzubauen, welches einen direkten Login in TeamProQ ermöglicht. Schlägt der Login fehl, soll die Fehlermeldung in der Kundenwebseite angezeigt werden. Ist der Login erfolreich, wird zur Kundenwebseite umgeleitet. Der Kunde soll die Möglichkeit haben, das Formular selbst zu stylen.

Lösungsansätze

Die ursprünglich angedachte Lösung war, Login und Passwort per AJAX zu schicken und die Antwort auf der Kundenwebseite anzuzeigen. Leider blockieren die gängigen Browser Ajax-Requests über Domaingrenzen. Für die meisten Browser lässt sich dies über entsprechenden Header lösen. Der Internet Explorer ist da leider eine Ausnahme, so dass dieser Ansatz nicht weiter verfolgt wurde (dieser bietet eine Möglichkeit über das XDomainRequest-Objekt, da dies unterstützt jedoch keine Cookies, welche für den Login jedoch erforderlich sind).

Ein weiterer Ansatz ist die Verwendung von IFrames zur Kommunikation. Leider funktioniert dieser auch nicht mehr auf allen Browsern in alle Richtungen.

Eine gut funktionierende Technik für Cross-Domain-Ajax ist JSONP. Hierbei wird  die Anfrage dynamisch als SCRIPT-Tag in das aktuelle Dokument eingebunden, so dass die Antwort ausgewertet werden kann. Über Frameworks wie zum Beispiel JQuery kann dies vollständig transparent geschehen. Prinzipbedingt sind JSONP-Requests aber immer GET-Requests, d.h. ein so übermitteltes Passwort würde immer im Query-String und somit ggf. auch im Server-Log auftauchen.

Die Idee war nun JSONP mit IFrames so zu kombinieren, dass beliebige Daten via POST an den Server geschickt werden können und das Ergebnis ausgewertet werden kann.

Der Ablauf ist wie folgt:

  • Die Formulardaten werden über ein verstecktes IFrame mittels POST an unseren Server geschickt. Dieser setzt einen Cookie, welcher die Antwort enthält.
  • Im IFrame ist ein onLoad-Handler registriert. Dieser wird aufgerufen, wenn die (leere) Antwort vom Server da ist
  • Sobald die Antwort da ist, wird ein JSONP-Request an den Server geschickt. Der Server ließt die Antwort aus dem Cookie aus und liefert sie zurück.

Damit wurde senden des Requests (via IFrame) und empfangen der Antwort (via JSONP) getrennt. Im Ergebnis können die POST-Daten über Domaingrenzen hinweg in beide Richtungen transportiert werden.

Zu beachten ist noch, dass der Internet Explorer normalerweise keine Cookies von anderen Domains annimmt. Dies kann durch setzen eines P3P-Headers gelöst werden. In unserem Fall setzte ich P3P=”CP=TEST”, was kein gültiger P3P-Header ist, dem IE aber genügt , um die Cookies zu akzeptieren.

Erweiterungsmöglichkeiten

Da es im Beispiel nur einen definierten Request gibt, ist die Lösung recht einfach gehalten. Dennoch ist dies eine Möglichkeit, JSONP um POST-Requests zu erweitern. Wenn jedem Request eine eindeutige ID mitgeliefert wird, kann via JSONP die Antwort zu einem bestimmten Request abgefragt werden, so dass auch mehrere parallele Requests möglich sind. Die Antworten könnten statt in einem Cookie auch serverseitig in der Session gespeichert werden.

Jun
09

Erfahrungsbericht: Datenrettung mit ddrescue

Ausgangsbasis war eine defekte Notebook-HDD mit noch ungesicherten wichtigen Daten. Um möglichst viele Daten zu retten, sollte als erstes ein Image der Festplatte erstellt werden. Unter Linux tut man dies gewöhnlich mit “dd”, doch nach kurzem googeln stellte sich heraus, dass man defekte Festplatten nicht mit dd sichern sollte. Das empfohlene Tool ddrescue stellte sich als sehr geeignet heraus:

Die Festplatte (80GB) hatte einige defekte Sektoren. Zusätzlich schaltete sich die Festplatte nach ca. 10-30 Minuten einfach ab. Praktischer weise merkt sich ddrescue, wo es aufgehört hat und welche Sektoren es nicht lesen konnte und macht beim nächsten Start dort weiter, wo es aufgehört hat. Es gab zwei Datenpartitionen und eine Systempartition. Erstere waren am wichtigsten.

Als erstes wurde von allen Partitionen so viel wie möglich gelesen. Der Befehl

ddrescue -i0 -s50Gi /dev/sdd5 sdd5.img sdd5.log

liest maximal 50 GB vom angegebenen Device und schreibt es in ein Image. Das Log enthält Infos über den aktuellen Stand die beim nächsten Durchlauf automatisch verwendet werden. Die Partition war ca. 30GB groß, ddresue hört am Ende auf, wenn der Parameter -s größer ist als die tatsächliche Speicherkapazität. Nach einer Weile hat sich die HDD abgeschaltet, danach habe ich sie abgeklemmt, gewartet und den Befehl erneut ausgeführt, bis ddrescue am Ende der Platte angekommen ist. Anschließend wurde dieses Vorgehen für die anderen beiden Partitionen wiederholt:

ddrescue -i0 -s50Gi /dev/sdd6 sdd6.img sdd6.log
ddrescue -i0 -s50Gi /dev/sdd1 sdd1.img sdd1.log

Die Bilanz am Ende dieser Aktion:

sdd1: 40GB, davon 4,5MB fehlerhaft
sdd5: 30GB, davon 1,3MB fehlerhaft
sdd6: 8GB, keine Fehler

Nun konnte damit begonnen werden, noch so viel wie möglich aus der HDD herauszuholen. Begonnen wurde wieder mit sdd5:

ddrescue -r1 /dev/sdd5 sdd5 sdd5.log

Der Parameter -r1 weist ddrescue an, jeden fehlerhaften Sektor noch einmal zu lesen. Man kann auch höhere Werte oder 0 für unendlich oft angeben. Da sich die Festplatte nach ein paar Fehlversuchen immer abgeschaltet hat, wurde -r1 gewählt. Nachdem dieser Befehl 4-5 mal hintereinander ausgeführt wurde, hatte ddrescue auch die fehlerhaften Sektoren dieser Partition gelesen, so dass davon ein komplettes fehlerfreies Image vorlag.

Mit sdd1 hatte ich nicht so viel Glück, einige Sektoren ließen sich überhaupt nicht mehr lesen, so dass es bei ca. 4MB fehlerhaften Daten blieb.

Die Images ließen sich mit “ntfs-3g” mounten (ntfs-3g sdd1.img /mnt/c -o ro) und die Daten wegsichern. Da die defekten Sektoren auf der Systempartition waren, waren keine der zu sichernden Dateien betroffen, so dass im Endeffekt 100% der gewünschten Dateien gerettet werden konnen.

Aug
21

Adobe Flex: FLV video streaming

(This is an article from 2009 saved from the old wiki)

Simulating Streaming by using adobe’s VideoDisplay (Flex) or FLVPlayback (Flash) component

The Problem

  • The components from Adobe are based on flash.net.NetStream which handles streaming/progressive downloading of videos
  • While FLVPlayback comes with a complete skinable GUI, VideoDisplay consists only of a reactangular video display area.
  • Both components (as well as NetStream) supports progressive downloading so that the video can start as soon as a certain amount of data is loaded.
  • None of these components allow to „seek ahead“, i.e. to jump to a position of the video that is not already loaded
    • NetStream throws an Exception („InvalidTime“)
    • VideoDisplay ignores the seek request
    • FLVPlayback is not handling the exception and is not working anymore after the click – good example for the „quality“ of the components delivered with flash ^^

The Solution

Basically it’s very simple: When the user wants to jump to a area of the video that is not loaded, the video is paused. Then a new stream is requested from the server that begins on the requested position. This stream starts with a new flv header followed by the flv file starting at a file offset where a keyframe begins.

Implementing it

Server-Side

  • When a stream is requested without parameter simply deliver the flv file as stream
  • When a stream is requested with a „offset“ Parameter, deliver the header + the file stream beginning with the requested keyframe
  • „offset“ might be a file offset, a keyframe number or a time. A file offset where a keyframe begins, must be calculated using this offset. The easies way to do this is to inject keyframe metadata to the flv file and determine the nearest keyframe to the target position on the client side. The metadata contains the file offset as well so it can be send directly to the server
  private static final String CONTENT_TYPE_VIDEO_X_FLV = "video/x-flv";

  public static final byte[] FLV_HEADER = new byte[]{
          (byte) 0x46, (byte) 0x4C, (byte) 0x56,
          (byte) 0x01,
          (byte) 0x05,
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x09,
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x09
  };

  protected void serveFLV(HttpServletResponse aHttpServletResponse,
                          InputStream aFLVInputStream,
                          boolean aPartial)
                          throws IOException {
    try {
      // here we need to set proper content type for response
      aHttpServletResponse.setContentType(CONTENT_TYPE_VIDEO_X_FLV);

      OutputStream os = aHttpServletResponse.getOutputStream();

      // Part of FLV should be passed - so we need to add header there
      if (aPartial){
        os.write(FLV_HEADER);
      }

      // and here we just copy content of FLV to output stream
      copyStream(os, aFLVInputStream);
    }
    catch (Exception e){
    }
  }

Client-Side (Flex)

To be done

May
08

JAVA: SOAP: Apache CXF and unchecked exceptions

(This is an article from 2009 saved from the old wiki)

The handling of exceptions in JAX-WS is described at http://www.ibm.com/developerworks/webservices/library/ws-tip-jaxrpc.html. This documents describes the behaviour of Apache CXF (Version 2.2.3) when using the Java-first approach.

Normally a RuntimeExceptions and subclasses of these are converted to a „Fault“ without any further details (except the exception message). The original class name of the exception and any extra properties are lost. Checked exceptions and their properties are described in the WSDL. When they occur, the exception type all properties are added as detail to the soap fault. On the client side the correct exception is initialized, filled with the original properties and thrown.

Example of an unchecked exception:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Server</faultcode>
         <faultstring>Invalid credentials. Access denied.</faultstring>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>
Example of checked exception:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Server</faultcode>
         <faultstring>Invalid credentials. Access denied.</faultstring>
         <detail>
            <ns1:ServiceException xmlns:ns1="http://admin.service.ika.de/">
               <errorCode>LOGIN_FAILED</errorCode>
            </ns1:ServiceException>
         </detail>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

This in CXF behaviour is caused by the following things:

  • The service interface is inspected when the service description is generated. This information is used to build the WSDL. The method signature is read by the ReflectionServiceFactoryBean (normally it’s subclass JaxWsServiceFactoryBean) when executing the method „protected void initializeFaults(final InterfaceInfo service, final OperationInfo op, final Method method)“.
  • The implementation is inspected when a fault occurs to determine if the fault is FaultMode.CHECKED_APPLICATION_FAULT or FaultMode.UNCHECKED_APPLICATION_FAULT. This is done by the method invoker (normally a subclass of AbstractInvoker, default is JAXWSMethodInvoker). If the implementation method declares the thrown exception, it is handled as CHECKED_APPLICATION_FAULT

Only if the exception is known by the ServiceFactory and is considered to be a CHECKED_APPLICATION_FAULT, the type and all properties of the exception are send to the client. So if a developer declares a RuntimeException in the throws declaration of the service interface AND the implementation, it is handled exactly the same way as a checked exception.

Advantages of using unchecked remote exceptions

A checked exception must either be declared by a client method or be handled by it. If an application does exception handling on a very high level but remote calls on a low level, the exception must be declared everywhere. When the remote call is done in a method that overrides or implements an abstract method that not declares this exception, there’s no way for the implementing method to add the exception to the throws declaration. So it has either do deal with the exception or wrap it into a run time exception. Both makes client code more complex and unreadable.

Disadvantages of using unchecked remote exceptions

To make the exception work properly in, on the server side it has to be declared in the service interface and in the implementation. This is no problem for checked exceptions since they have to be declared in the implementation to allow that they are thrown and they have to be declared in the service interface to allow their declaration in the implementation. A run time exception can be thrown everywhere without declaration. So relying on these exceptions is very error prone (if the developer forgets to declare it on either the interface or implementation, it’s thrown as Fault on the client side).

Solutions to the problem above

One solution is to tell the service that any service method can throw certain kinds of RuntimeExceptions. So for every method these exceptions are described within the WSDL. The service must also know that when this exception occurs it should be handled as CHECKED_APPLICATION_FAULT. The easiest way to ensure both is to extend the ServiceFactory in two ways:

  • 1st extend initializeFaults to add the run time exceptions to all service methods. The code here lacks a check if this exceptions are not already added.
  • 2nd change the method invoker to handle these exceptions as CHECKED_APPLICATION_FAULT. Instead of implementing an own invoker, the original invoker is simlpy wrapped here and the message is changed if the cause of the fault is the expected run time exception.
import java.lang.reflect.Method;

import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.FaultMode;
import org.apache.cxf.service.invoker.Invoker;
import org.apache.cxf.service.model.InterfaceInfo;
import org.apache.cxf.service.model.OperationInfo;

public class MyJaxWsServiceFactoryBean extends JaxWsServiceFactoryBean
{
    @Override
    protected void initializeFaults(final InterfaceInfo service, final OperationInfo op, final Method method)
    {
        super.initializeFaults(service,op,method);
        // TODO: only add if not declared by the method!
        addFault(service, op, MyCustomRuntimeException.class);
    }

    @Override
    public void setInvoker(Invoker invoker)
    {
        super.setInvoker(new InvokerWrapper(invoker));
    }

    protected static class InvokerWrapper implements Invoker
    {
        protected final Invoker target;
        protected InvokerWrapper(Invoker target)
        {
            this.target=target;
        }

        @Override
        public Object invoke(Exchange exchange, Object o)
        {
            try
            {
                return target.invoke(exchange, o);
            }
            catch (Fault fault)
            {
                if (fault.getCause() instanceof MyCustomRuntimeException)
                {
                    exchange.getInMessage().put(FaultMode.class,FaultMode.CHECKED_APPLICATION_FAULT);
                }
                throw fault;
            }
        }
    }
}
Using the custom service factory:
	<import resource="classpath:META-INF/cxf/cxf.xml" />
	<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
	<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
	<jaxws:endpoint id="adminServiceEndpoint" implementor="#adminService" address="/admin">
		<jaxws:serviceFactory>
			<bean class="test.MyJaxWsServiceFactoryBean">
			</bean>
		</jaxws:serviceFactory>
	</jaxws:endpoint>

Apr
20

Adobe Flex: scaling applications

(This is an article from 2009 saved from the old wiki)

This article describes, how an application can be scaled to the window’s size by maintaining it’s aspect ratio. The base application window has a fixed size. All it’s content will be scaled if the browser window is bigger. If the window is smaller, scroll bars are applied.

  • create an application which is set to „fit to window“
  • add a canvas with the desired size (e.g. 800×600). This canvas will be the root container of the application
  • add a listener to the application for „initialize“ and „resize“ that recalculate the size of the canvas

Problems to solve:

  • The resulting scale factors may have very much fractional digits. The result is that lines (e.g. within tables) are not calculated correctly (and are sometimes shifted by 1 pixel). Solution: round the scale factor to e.g. 2 digits.
  • Popups (e.g. opened Alert.show()) are not scaled. Solution: TODO

Here’s a sample application that scales well:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="onResize()" resize="onResize()">

	<mx:Script>
		<![CDATA[
			protected var initialContentWidth:Number=0;
			protected var initialContentHeight:Number=0;
			protected var currentScaleFactor:Number=1;
			protected var scalePrecision:Number=100; // used to round the scale factor
			protected function onResize():void {
				if (screen==null || applicationRoot==null) return;
				if (initialContentWidth==0) {
					initialContentWidth=applicationRoot.width;
					initialContentHeight=applicationRoot.height;
				}

				currentScaleFactor=Math.min(screen.width/initialContentWidth,screen.height/initialContentHeight);
				if (currentScaleFactor<1) currentScaleFactor=1;
				else currentScaleFactor=Math.floor(currentScaleFactor*scalePrecision)/scalePrecision;
				applicationRoot.scaleX=currentScaleFactor;
				applicationRoot.scaleY=currentScaleFactor;
			}
		]]>
	</mx:Script>

	<mx:Canvas id="applicationRoot" width="800" height="600" horizontalScrollPolicy="off" verticalScrollPolicy="off">
		<mx:Button x="0" y="0" label="top left"/>
		<mx:Button right="0" bottom="0" label="bottom right"/>
	</mx:Canvas>

</mx:Application>

Mar
16

Compiling Java to JavaScript

(This is an article from 2009 saved from the old wiki)

There are serveral compilers out there which can compile Java to JavaScript. The advantage above writing JavaScript directly are:

  • compiled code with strong typechecking
  • availability of high quaility development environments
  • availability of WYSIWYG gui design tools
  • any many more

GWT

GWT (Google Web Toolkit, http://code.google.com/intl/de-DE/webtoolkit/) is the most popular Java to JavaScript compiler. Ist comes with a special browser (hosted-mode) which runs the Java code if it where javascript, allowing the developer to use standard java debugging tools. The compiler itself compiles C-like (building one big JavaScript library and stripping all unused methods and classes). Development can be done in the hosted-mode browser. Then the whole application can be compiled to JavaScript and tested in the target browser.

GWT comes with it’s own widget library that contains a small number of widgets (http://code.google.com/docreader/#p=google-web-toolkit-doc-1-5&s=google-web-toolkit-doc-1-5&t=DevGuideWidgetGallery).

There’s a free (LGPL) extension with many widgets at http://gwt-ext.com/. The problem ist that this is based on ExtJS which is now GPL+commercial. So the gwt-ext Widgets are based on an old version of ExtJS. The quiality of the widgets is (IMO) not really good.

There are many other extra widgets for GWT which are from different quality. Some are wrappers to JavaScript toolkits which allow to use java to write GUIs with them.

Java2Script

Java2Script (http://j2s.sourceforge.net/) is an eclipse plugin. Once enabled for a java project, it compiles every class to a JavaScript file. It comes with an own classloader implemented in JavaScript that loads all needed js files on demand.

Java2Script comes with an SWT implementation which allows to build JavaScipt-GUIs using SWT design tools and run SWT applications with minimal changes in the browser.

Because the only way to run the Java2Script compiler is the eclipse plugin, it’s hard to integrate Java2Script into a build environment like ant.

Qooxdoo Java-to-JavaScript compiler

Qooxdoo is a set of high quality JavaScript widgets. There’s a subproject called QWT (http://qooxdoo.org/contrib/project/qwt) with the goal to implement a GWT like environment for Qooxdoo.

QWT consists of a Java-to-JavaScript compiler, a prepared library of Qooxdoo componente (version 0.7.x) and some other tools.

The compiler seems to work fast and produces good JavaScript code. It can be used standalone and allows wrapping of any JavaScript class by simple create a stub Java class.

XMLVM

XMLVM (http://www.xmlvm.org/overview/) is an xml based cross complier. It decompiles Java code (unlike all other Java-to-JavaScript compilers which uses the java source code) and creates XML. Using XSLT it’s possible to create almost any language code from the result. The project contains examples for creating JavaScript code.

The approach of this project is very generic (it can produce .net code from Java and many other combinations). The resulting code is unreadable. E.g. because Java is stack-based and JavaScript not, a simple stack emulation must be written in JavaScript (examples are provieded).