Dienstag, 25. August 2009

GWT Compiler beschleunigen

Der GWT-Compiler erzeugt aus dem Java-Quellcode für verschiedene Browser JavaScript und zwar für jede Sprache eine eigene Datei. Die Browser mit Kennungen sind:
  • ie6: IE 6 und 7
  • ie8: IE 8
  • gecko: Mozilla
  • gecko1_8: Firefox
  • safari: Safari
  • opera: Opera
Hat man jetzt 5 Sprachen, dann erzeugt der Compiler 6 * 5 = 30 JavaScript-Dateien. Dies dauert eine ganz schöne Weile. Während der normalen Entwicklung testet man üblicherweise nur mit einem Browser und in einer Sprache. Über die Modul-spezifische .gwt.xml kann man dies Einstellen:
  1. Folgenden Eintrag machen:
    <set-property name="user.agent" value="gecko1_8">
  2. Die Optionen
    <extend-property name="locale" values="de">
    auskommentieren.
Damit baut mein GWT-Projekt statt in 1,5 Minuten in 15 Sekunden, lohnt sich also. Nur vor einem Release nicht vergessen, alles wieder zurückzustellen, sonst gucken die Benutzer mit dem falschen Browser in die Röhre.

Freitag, 24. April 2009

Das schnellste Dateisystem für USB-Sticks

Ich habe mal getestet, wie schnell die unterschiedlichen Dateisysteme auf meinem Transcent 64 GB USB-Stick sind. Hierzu habe ich den Stick jeweils mit dem entsprechenden Dateisystem formatiert und dann mein NetBeans-Projektverzeichnis mit viel Quellcode und Bibliotheken auf den Stick kopiert und mit sync sichergestellt, dass es auch geschrieben war. Danach das Verzeichnis wieder gelöscht, wieder sync und ein zweites Mal draufkopiert. Hier das Ergebnis mit Zeiten in Minuten:Sekunden:






DateisystemKopieren 1LöschenKopieren 2
ReiserFS1:170:061:14
XFS2:200:272:15
EXT33:100:083:03
EXT23:130:193:26


Wie man sieht ist ReiserFS deutlich schneller als die anderen. Schade eigentlich, dass Hans Reiser jetzt im Gefängnis sitzt.

Dienstag, 31. März 2009

Glassfish, JDBC und DB/2

Der JDBC-Treiber von IBM für DB/2 hat es in sich: normale Verbindungen über den DriverManager funktionieren, aber der Zugriff über eine DataSource - was der Glassfish macht - bricht ab mit einer Fehlermeldung in der Art

Caused by: com.ibm.db2.jcc.a.SqlException: jcc10389122453.51.90
Beim Laden der nativen Bibliothek db2jcct2,
java.lang.UnsatisfiedLinkError: no db2jcct2 in java.library.path ist ein Fehler aufgetreten: ERRORCODE=-4472, SQLSTATE=null

Dies kommt daher, dass der Treiber standardmäßig als Typ-2-Treiber eine native Bibliothek nachladen will. Ich hab es selbst nach Kopieren von 36 MB Bibliotheken nur geschafft, dass er nicht mehr über fehlende Bibliotheken meckert, dafür aber mit einem Segmentation Fault abstürzt. Die Lösung für das Problem ist es dem Treiber zu sagen, dass er ein Typ-4-Treiber sein soll. Beim DriverManager reicht hierfür die Angabe des Ports in der URL. Bei der DataSource muss man die Methode "setType(4)" aufrufen:

DB2DataSource dB2DataSource = new com.ibm.db2.jcc.DB2DataSource();
dB2DataSource.setDriverType(4);
dB2DataSource.setServerName("192.168.1.194");
dB2DataSource.setPortNumber(50000);
dB2DataSource.setUser("XXX");
dB2DataSource.setPassword("YYY");
dB2DataSource.setDatabaseName("ZZZ");
dB2DataSource.setSysSchema("XYZ");
dB2DataSource.getConnection();

und schon klappt es. Im Glassfish ist dies folgendermaßen einzurichten: man wählt als Datenbank DB/2 aus, ändert die Datasource-Klasse in "com.ibm.db2.jcc.DB2XADataSource" und setzt als Type "javax.sql.XADataSource".



Bei den Properties muss man eine zusätzliche Property "driverType" einfügen und sie auf den Wert 4 setzen:


Hat man das gemacht, wir der Ping grün und das ganz ohne native Bibliotheken. Natürlich nicht vergessen, die Treiberdatei in das lib-Verzeichnis der Glassfish-Domain zu kopieren, damit er ihn überhaupt finden kann.

Freitag, 27. Februar 2009

mod_jk und SSL-Verbindungen hängen

Das Problem

Mehrere Probleme mit einer Ursache: Verbindungen von Apache über mod_jk zu einem JBoss (genauer zu dem darin enthaltenen Tomcat) hängen und SSL Verbindungen wollen sich einfach nicht aufbauen. Das Problem tritt manchmal auf, ein Apache-Neustart oder die Option "JkOptions +DisableReuse" scheinen manchmal zu helfen.

Die Ursache

Die Ursache ist aber eine ganz andere: fehlender Zufall! Linux stellt zwei Geräte zur verfügung, die Zufall liefern: /dev/random und /dev/urandom. Diese greifen beide auf eine Kernel-interne Entropie-Quelle zu, die sich aus recht unvorhersehbaren Ereignissen im System füllt, wie Festplattenzugriffe, Netzwerkaktivität, Tastatur- und Mauseingaben usw. Der Unterschied zwischen den beiden ist, dass /dev/random sich nur auf diese Ereignisse verlässt, während /dev/urandom auch einen Pseudozufallszahlengenerator (PRNG) verwendet. In der Praxis bedeutet dies, dass /dev/random "hängen" kann, wenn zuviel Zufall abgerufen wurde, während /dev/urandom immer ein Ergebnis liefert.

Man kann sich leicht anzeigen lassen, wieviel Zufall der Kernel gerade zur Verfügung hat:

cat /proc/sys/kernel/random/entropy_avail

Mit einem

cat /dev/random

kann man den Zufall auslesen. Der Befehl hängt recht schnell und kann mit Strg-C abgebrochen werden. Eventuell ist das Terminal dann verkonfiguriert, was man durch Eingabe von "reset" wieder korrigiert. Versucht man das Gleiche mit /dev/urandom, so erhält man ständig neue Werte.

Java verwendet standardmäßig /dev/random für java.secure.SecureRandom, was wiederum für SSL-Verbindungen u.ä. verwendet wird. JBoss Seam verwendet es für seine Session-IDs. Gibt es nicht genug Zufall, dann wartet Java auf /dev/random und deshalb teilweise recht lange, bis es weiter geht.

Die Lösung

Hängt ein Prozess aktuell, so muss man für mehr Zufall sorgen, indem man beispielsweise mit einem

ls -lR /

oder

hdparm -t /dev/sda

für Festplattenaktivität sorgt. Besser ist es, den Hänger gar nicht erst auftreten zu lassen. Hierzu muss man Java lediglich anweisen, statt /dev/random einfach /dev/urandom zu verwenden. Dies geht mit einer einfachen Kommandozeilenoption für die JVM:

-Djava.security.egd=file:/dev/urandom

oder direkt im Programmcode mit

System.setProperty("java.security.egd", "file:/dev/urandom");

Der Nachteil dieses Ansatzes ist, dass sich der Zufall von /dev/urandom leichter vorhersagen lässt und damit beispielsweise ein Angriff auf SSL-Verbindungen denkbar ist. Wer hier auf der sicheren Seite bleiben will, der muss über einen Hardware-Zufallszahlengenerator nachdenken. Ein leicht zu realisierender Ansatz ist ein Mikrofon vor einem Lüfter. Die damit aufgenommenen Samples aber noch etwas nachbearbeiten, aber das ist ein ganz anderes Thema.

Dienstag, 16. Dezember 2008

UTF-8 in HTML-Formularen

Bei einem HTML-Formular kann man den zu verwendenden Zeichensatz mit dem Parameter

accept-encoding="UTF-8"

auf UTF-8 stellen. Leider wird dieser Zeichensatz beim Abschicken des Formulars nicht mit übermittelt, so dass der Server raten muss, welcher Zeichensatz verwendet wurde. Und er rät immer "ISO-8859-1", so dass bei einem Formular mit UTF-8 die Umlaute falsch interpretiert werden. Abhilfe schafft der folgende Aufruf:

request.setCharacterEncoding("UTF-8");

er muss vor dem ersten request.getParameter() kommen, damit er funktioniert. Damit werden die Parameter als UTF-8 interpretiert und das Formular funktioniert.

Freitag, 21. November 2008

Tomcat, Glassfish auf Port 80

Unter Unix Betriebssystemen dürfen die Ports unter 1024 nicht von normalen Benutzern verwendet werden, sondern nur vom Benutzer root. Aus Sicherheitsgründen will man so wenig Prozesse wie möglich mit Root-Rechten laufen lassen, da eine Sicherheitslücke damit automatisch Vollzugriff auf das System bedeutet. Webserver wie Apache helfen sich dadurch, dass ein Prozess mit Root-Rechten auf dem Port 80 lauscht, die Anfragen aber von normalen Benutzer-Prozessen bearbeitet werden. Die Funktionalität des Benutzerwechsels eines Prozesses ist Java nicht möglich. Man kann es auch nicht über JNI tricksen, da Java Threads verwendet und die immer einem gemeinsamen Benutzer gehören.

Eine mögliche Lösung ist es, den Webserver auf einem hohen Port - zum Beispiel 8080 - laufen zu lassen und eine passende Umleitung einzurichten. Dies kann beispielsweise über mod_jk oder mod_proxy im Apache geschehen, oder über eine Umleitung über iptables:

iptables -t nat -A PREROUTING -i eth0 -p tcp \
--dport 80 -j REDIRECT --to-ports 8080


Beide Ansätze haben den Nachteil, dass der Tomcat oder Glassfish denkt, er wird über Port 8080 angesprochen. Dies kann zu Problemen führen, wenn komplette URLs ausgegeben werden, denn diese haben dann :8080 darin. mod_jk und Tomcat kennen als Abhilfe entsprechende Optionen ("proxy-port"), mit denen ein anderer Port mitgeteilt wird.

Es gibt aber auch eine bessere Möglichkeit, mit der ein beliebiges Java-Programm auf privilegierten Ports lauschen kann und trotzdem als normaler Benutzer läuft: authbind und privbind. Die Einrichtung ist sehr einfach; als Beispiel hier die Freigbe des Ports 80 für den Benutzer glassfish: authbind ist SUID-root und kann deshalb direkt vom Benutzer gestartet werden. Bei dem Versuch auf einem niedrigen Port zu lauschen prüft es, ob der Benutzer Schreibzugriff auf eine bestimmte Datei hat. In unserem Beispiel wäre dies die Datei

/etc/autbind/byport/80

Um diese anzulegen, müssen die folgenden Befehle als root eingegeben werden:

touch /etc/authbind/byport/80
chmod 500 /etc/authbind/byport/80
chown glassfish /etc/authbind/byport/80

Für privbind ist eine solche Konfiguration nicht notwendig. Da es nicht SUID-root ist, muss es von root direkt gestartet werden. Außerdem ist es nicht möglich, den Zugriff auf einen Port einzuschränken - es sind immer alle möglich.

Leider haben beide Programme eine große Einschränkung: sie funktionieren nicht mit IPv6 sondern nur mit IPv4. Java versucht standardmäßig auch IPv6 zu nutzen, was einen Fehler zur Folge hat. Dem Java-Prozess muss deshalb die Option
-Djava.net.preferIPv4Stack=true
mitgegeben werden. Beim Tomcat schreibt man dies in die Variable CATALINA_OPTS in der Datei setenv.sh/setenv.bat, die man ggf. noch erzeugen muss. Beim Glassfish kommt diese Option in die entsprechende domain.xml in den Abschnitt mit den JVM-Optionen:

<jvm-options>-Djava.net.preferIPv4Stack=true</jvm-options>

Gestartet wird beispielsweise der Glassfish bei Verwendung von authbind als Benutzer glassfish mit

authbind --deep asadmin start-domain domain1

Die Option --deep ist notwendig, da der Java-Befehl ein Skript ist, das den eigentlichen Befehl erst aufruft und hierbei die Rechte für den Port sonst verloren gehen. Will man privbind verwenden, so lautet der Befehl so:

sudo privbind -u glassfish $(which asadmin) start-domain domain1

Starte man den Befehl als Root, kann man das sudo weg lassen.

Dies Ausgabe ist in beiden Fällen diese:

Starting Domain domain1, please wait.

Log redirected to /opt/glassfish-v2ur2/domains/domain1/logs/server.log.

Redirecting output to /opt/glassfish-v2ur2/domains/domain1/logs/server.log

Domain domain1 is ready to receive client requests. Additional services are being started in background.

Domain [domain1] is running [Sun Java System Application Server 9.1_02 (build b04-fcs)] with its configuration and logs at: [/opt/glassfish-v2ur2/domains].

Admin Console is available at [http://localhost:4848].

Use the same port [4848] for "asadmin" commands.

User web applications are available at these URLs:
[http://localhost:80 https://localhost:443 ].

Following web-contexts are available:
[/web1 /__wstx-services ].

Standard JMX Clients (like JConsole) can connect to JMXServiceURL:
[service:jmx:rmi:///jndi/rmi://pckurt:8686/jmxrmi] for domain management purposes.

Domain listens on at least following ports for connections:
[80 443 4848 3700 3820 3920 8686 ].

Domain does not support application server clusters and other standalone instances.

Auf diesem Server wurde auch der SSL-Port von 8181 auf 443 umgestellt; hierfür ist bei Verwendung von authbind auch die entsprechende Datei notwendig.

Dienstag, 19. August 2008

Fix Imports im NetBeans unter KDE

Die Tastenkombination für Fix-Imports (Strg+Umschalt+I) funktioniert unter KDE nicht solange der Kopete diese Kombination für sich beantsprucht. Zum Ändern die Kontaktliste öffnen und dann

Einstellungen - Globale Kurzbefehle einrichten...

auswählen. Dort kann man dann die Kurzbefehle ändern oder deaktivieren.