Archiv der Kategorie Java

Featurities meets Fallstrick: Die Spring Security 3.0 Konfigurationsodyssey

Mit dem Majorrelease 3.0 wurde dem Modul Spring Security eine Menge von neuen Features angeignet. Spring Security ist die Modulkomposition, welches für das Java Framework Spring quasi die gesamte Authentifizierung, Autorisierung, Legitimierung jedwegiger Art ermöglicht.

Leider wurden mit dem Release 2.x auf 3.0 eine Reihe von API-Changes vollzogen. Zugegeben, die waren auch sicher alle sinnvoll, weil Komponenten wie die Authentifizierung weiter geteilt wurden und man somit wesentlich flexibler ist, neue Anforderungen zu ermöglichen (Baukastenprinzip). Aber die Dokumentation ist – gesamtheitlich betrachtet – irgendwie immer noch mies und oft nicht aktualisiert. Oder man findet im Internet einfach nur (alte) Beispiele.

Die http-Direktive

Im Namespace von Spring Security existiert das Tagelement http, mit welchem man kurze, knappe und verständliche Konfigurationen anlegen kann. Der Vorteil liegt klar auf der Hand: Man muss nicht alle Beans, Listener und Provider anlegen, denn das geschieht automatisch. Tja, wären da nicht ein paar Einschränkungen in der Funktionsvielfalt.

Konkretes Beispiel: Remember Me

Just wurde das Minor-Release 3.0.1 veröffentlicht, und nur wenige Tage später zu erfahren, dass Remember Me kaputt sei. Egal, fahren wir erstmal weiter mit 3.0.0.

Laut Dokumentation ist es am einfachsten, wenn man die Direktive remember-me (Security Namespace) innerhalb der http-Direktive (Security Namespace) verwendet. Ohne irgendeine Angabe wird ein stinknormales, Cookie basiertes Tokenverfahren ohne (echten) privaten Schlüssel verwendet. Reicht für den ersten Einsatz erstmal auf, soll ja erstmal funktionieren.

Fehlermöglichkeit 1a: Man loggt sich ein, und es passiert nichts (kein “RememberMe”-Cookie).
Lösung: Wenn man einen eigenen Auth-Filter einsetzt, muss man diesem auch den RememberMe-Service “setten”. Außerdem muss der SecurityChainFilter (web.xml!) auch auf die login-Seite verweisen. Es dürfen auch keine Filter bei der Konfigurierung von intercepted Urls (speziell hier: login, logout) gemacht werden.

Fehlermöglichkeit 1b: Es passiert noch immer nichts?
Lösung: Vielleicht wurde vergessen, einen Parameternamen für den Request zu setzen. Der Standardname ist ein typischer Springname, der natürlich unschön ist. Und den kann man nicht über die RememberMe-Direktive setzen, also muss man eh einen eigenen Service definieren. Bäm. Referenzierung geht dann zwar noch, aber für mehr ist die RememberMe-Direktive dann nicht mehr zu gebrauchen.

Fehlermöglichkeit 2a: Man besucht die Seite ohne Login, aber mit Cookie – und die Loginseite kommt (Log sagt kein gültiger Auth).
Lösung: Man kann der Log trauen, wenn sie zwar beim Einloggen nun einen Token ablegt (kann man zum Beispiel sehr einfach mit diesem Firefox-Addon inkl. Editor(!) verifizieren), dieses aber beim erneuten Besuchen der Seite (bzw. ohne JSPSESSION-Cookie) nicht verwendet bzw. wird nicht erkannt. Schlussendlich half u.a. das Umbenennen der Userservices-Bean in “userService”. Außerdem sollte die Loginseite keinen Filter/Access haben (s.o.) Lieber 2x prüfen!

Fehlermöglichkeit 2b: Es erscheint eine Ausnahme, dass der Key falsch sei.
Lösung: Dazu muss man wissen: Sobald  man eine individualisierte RememberMe-Konfiguration nutzt, wird auch der Key nicht mehr vernünftig auf alle Komponenten (Provider, Filter, Manager) gesetzt. Beim Anlegen wird also der eigene Key verwendet, beim Auslesen der Standardkey. Yes! (s.o.)

Fehlermöglichkeit 3: Man besucht die Seite ohne Login, aber mit Cookie – aber wie in 2 nur die Loginseite.
Lösung: Im Logger/Debugger kann man nun feststellen, dass zwar das Cookie gefunden wurde, das Token gefunden und validiert wurde aber dann keine Rechte existieren – aha? Wahrscheinlich fehlt im Provider noch ein zusätzliches Setting der Komponenten. Am besten von RememberMe Service/Filter/Provider jeweils alle möglichen Properties durchgehen. Jaja, wie gesagt.. ;)

Fehlermöglichkeit 4: Das Ausloggen (beispielsweise logout.html) hat nach Aktivierung von Rememberme plötzlich keine Auswirkungen mehr.
Lösung: Zwar wird die Seite gefunden, aber es wird kein “Logout” gemacht. Auch hier sollte man prüfen, ob ein SecurityChainFilter (web.xml) auch für die logout-Seite greift.

Fehlermöglichkeit 5: Das Besuchen der Seite wirft einen Fehler (ggf. “mit weißer Seite”), dass keine neue Session erstellt werden kann.
Lösung: Richtig, nach einem Request ist ja dann auch zu spät. Das Attribut create-session in der Direktive http sollte daher auf “ifRequeried” gestellt sein.

Fazit:

  • SecurityChainFilter immer prüfen
  • Intercepted Urls prüfen
  • RememberMe-Direktive innerhalb der http-Direktive ist quasi abgesehen von der services-ref unbrauchbar.

Anmerkung:

Natürlich kann man sich das Problem mit den SecurityChains vom Hals schaffen, indem man stupide ein /* filtert. Das hat jedoch zur Auswirkung, das Spring Security auch jeden verdammten Request anguckt; bei zusätzlichen (statischen) Inhalten wie Javascript, Stylesheets, Bildern, Flash u.ä. ist das ein Overhead, der unnötig ist.

Tags: , , ,

Tomcat, Eclipse und der Manager

Während es natürlich mit der Standard-Standalone-Variante keine Probleme macht, hat die Integration von Tomcat in Eclipse einen kleinen Nachteil: Dadurch, dass Eclipse neben den Webapps auch die komplette Konfiguration mitsamt der Module übernimmt, fehlt daher die Manager-Webapp. Häufig ist dies egal und zu vernachlässigen, aber falls man doch mal Zugang braucht, sei es zum (testweisen) Deployen, oder einfach einer Konfiguration außerhalb Eclipse:

Als erstes fügt man ein externes Webapp-Modul ein, der Suchpfad ist “${catalina_home}/webapp/manager”. Dabei sollte man beachten, dass scheinbar der Pfad ausgeschrieben werden muss (bzw. eine gültige Catalina-Home-Variable). Dieser Schritt hat der server.xml eine Zeile (meist gaaaanz unten) wie folgt hinzugefügt:

<Context docBase="YOURPATH/webapps/manager" path="/manager" reloadable="false"/>

Wie man sich gerne überzeugen lassen kann, funktioniert die Anwendung jedoch noch nicht. Der Grund: Der Tomcatmanager muss im priviligierten Modus gestartet werden, welcher zusätzlich eine Benutzerkennung erwartet. Zunächst muss die o.g. eingefügt Zeile wie folgt angepasst werden (hier wichtig: privileged=true).

<Context antiJARLocking="false" antiResourceLocking="false" docBase="YOURPATH/webapps/manager" path="/manager" privileged="true" reloadable="false"/>

Wie bereits erwähnt, erwartet der Manager einen authentifizierten Benutzer. Man kann sich das Leben aber einfach machen, und die vorgefertige tomcat-users.xml nutzen. Man kann die Einträge auskommentieren; wichtig ist jedoch, das mindestens ein Benutzer existiert, dem die Rollen admin und manager zugewiesen sind. Beispielsweise:

<user username="manager" password="manager" roles="admin,manager"/>

Soweit, so gut. Nicht vergessen sollte man jedoch das (Re)Publish des Servers, denn sonst deployt Eclipse die neuen Einstellungen nicht in das interne Tomcat-Home-Verzeichnis.

Der Manager findet sich dann – bei einer Standardinstallation – unter der Adresse http://localhost:8080/manager/html

Statistiken via Subversionhistory

Nach einem erfolgreichen Projekt ist es interessant, was denn so alles gelaufen ist. Bei der Verwendung von Subversion (oder natürlich auch einem anderen Versionskontrollsystem) ist es “leicht”, die History einfach nach den Taten zu analysieren und daraus Berichte zu erstellen. Einfach deswegen in Anführungszeichen, weil dies natürlich händisch gesehen zuviel Zeit beanspruchen würde.

Für diesen Fall gibt es StatSVN, ein kleines in Java entwickeltes Tool, welches für eine Working Copy die komplette History in einem HTML-Report (wahlweise auch XML/XDOC) visualisiert. Dabei werden sowohl Tabellen als auch Diagramme erstellt – genial Sache! Da komplett über Kommandozeile lauffähig, ist eine Integration in Ant/Maven kein Problem; ebensowenig in andere Buildscripts.

Nachdem man eine neue Working Copy erstellt hat (bevorzuge ich mal an dieser Stelle), reichen 2 Befehle zum Generieren:

svn log -v --xml [ort] > svn.log

erstellt eine XML-Log der Commits, dabei ist “Ort” natürlich optional und kann bspw. der Ort der Workingcopy sein (in Scripts)

java -jar statsvn.jar {working-copy-dir} {svn.log}

erstellt schließlich den Bericht. Wichtig sind dabei vor allem die Paratemer -output-dir, -threads. Die letzten beiden Parameter sind Pflicht.

Tatsächlich sollte man sich jedoch vorsehen, auf welchem Repository man es ausführt. Zwar lässt sich die Threadzahl (Standard 25) einstellen, aber es gibt auch Server, die damit absolut nicht klarkommen. Für diesen Fall empfehle ich das Spiegeln des kompletten Repositories auf die lokale Festplatte; dann muss ein neuer Checkout gemacht werden sowie zwischenzeitlich ein weiterer Sync der Repositories, aber das Generieren der Statistiken funktioniert wesentlich(!) schneller.

Mit dieser Anleitung ist ein Spiegel schnell eingerichtet, mittels dem letzten Befehl (svnsync sync…) wird das Repository auch später schnell gesynct, ähnlich einem svn update.

Java-Anwendungen mittels Ant-Script in eine Drop deployen

Im Anschluss an meine vorangegangenen Artikel über das automatische Builden und Deployen einer Java-Anwendung (als Jar, als Mac-Applikation und als Windows-Exe) folgt nun ein Howto, wie man eine Drop von drop.io automatisch aktualisieren kann.

Leider gibt es derzeit weder eine Java-API-Implementierung der drop.io API noch einen Ant-Build-Task, letzteres ist dann natürlich keine Überraschung. Daher verwende ich iOrb, ein drop.io command line interface, entwickelt in Ruby. Daher ist auf dem System Ruby zwingend erforderlich — für Alternativen bin ich selbstverständlich offen.

Nach einer beispiellos simplen Installation ist iorb als Command verfügbar. In meinem Falle beginnen die Deploy-Apps mit “application-”, daher reicht ein Pattern, um alle zu löschen.

<target name="deployToDropio" depends="makeAll">
  <!-- Delete all application assets -->
  <exec executable="iorb">
    <arg line="destroy ${drop.drop_name}:/application-*/ --force" />
  </exec>
  <!-- Upload new versions -->
  <exec executable="iorb">
    <arg line="add deploy/Application.jar --drop-name ${drop.drop_name}" />
  </exec>
  <exec executable="iorb">
    <arg line="add deploy/Application.exe --drop-name ${drop.drop_name}" />
  </exec>
  <!-- zip mac app dir -->
  <zip destfile="deploy/Application.app.zip" basedir="deploy/Application.app" />
  <exec executable="iorb">
    <arg line="add deploy/Application.app.zip --drop-name ${drop.drop_name}" />
  </exec>
</target>

Vor dem ersten Starten muss/will iOrb eine Profildatei in ~/.iorbrc anlegen. Dafür ist ein API-Token von drop.io notwendig.

Dort werden auch im Yaml-Format die angelegten Drops gespeichert. Falls man bereits im Vorfeld eine Drop angelegt hat, so kann man diese Datei auch später im einen Texteditor entsprechend bearbeiten. Dort werden auch die Passwörter und Tokens abgelegt.

Java-Anwendungen mit Exe-Wrapper

Bereits vor einigen Wochen hatte ich hier und dort beschrieben, wie man mit Eclipse/Ant seine Anwendung per Knopfdruck automatisiert deployen kann. Da es unter manchen Windows-Systemen zu Schwierigkeiten kommt, eine Jar-Datei zu starten (bspw. nicht verknüpft mit dem Jar-Launcher) empfiehlt es sich natürlich, für jene einen kleinen Exe-Wrapper zur Verfügung zu stellen. Launch4j bietet neben dieser Funktionalität auch diverse andere Tweaks: Splashscreen, Java-Download-Hinweis, JVM-Arguments und vieles mehr. Dabei gibt es Launch4j in zwei Modi: Im Gui-Modus lassen sich alle Einstellungen konfigurieren (und testen) und anschließend speichern, im Befehls-Modus wird eben eine solche Konfigurationsdatei erwartet.

Launch4j arbeitet selber mit Ant, daher ist die Integration in bestehende Build-Tasks sehr einfach. Wahlweise konfiguriert man die Optionen direkt im Hauptbuildscript oder verwendet ein eigenes Script, welches man durch die Gui erhält.

In dem eigenen Buildscript definiert man den Task etwa mit:

<taskdef name="launch4j"
    classname="net.sf.launch4j.ant.Launch4jTask"
    classpath="${launch4j.dir}/launch4j.jar :${launch4j.dir}/lib/xstream.jar" />

wobei launch4j.dir auf das gesamte Verzeichnis zeigt (Hinweis.. es werden mindestens bin, lib und conf benötigt).

Anschließend kann mit einem beherzten <launch4j configFile=”build_data/launch4j.xml” /> bereits der Prozess gestartet werden.

Beispielhafter Inhalt der build_data/launch4j.xml:

	<launch4jconfig>
		<dontwrapjar>false</dontwrapjar>
		<headertype>gui</headertype>
		<jar>../deploy/Application.jar</jar>
		<outfile>../deploy/Application.exe</outfile>
		<errtitle>Please download Java 6.</errtitle>
		<cmdline />
		<chdir />
		<priority>normal</priority>
		<downloadurl>http://java.com/download</downloadurl>
		<supporturl />
		<customprocname>false</customprocname>
		<stayalive>false</stayalive>
		<manifest />
		<icon>Application_32x32.ico</icon>
		<jre>
			<path />
			<minversion>1.6.0</minversion>
			<maxversion />
			<jdkpreference>preferJre</jdkpreference>
		</jre>
	</launch4jconfig>

Tab-Management in Java

Die im Rahmen unserer Diplomarbeit entstehende Anwendung verfügt unter anderem auch über ein TabbedPane, in welchem mehrere Panels mit Informationen angezeigt werden. TabbedPane, das ist in etwa die gleiche Art wie Tabs in einem Browser.

Erste Usability-Versuche zeigten, das gewisse Aktionen wie “Alle Tabs schließen” aus Organisationsgründen fehlen, also flugs ran.. wären da nicht ein paar Probleme.

Einerseits verfügt leider das Standard-TabbedPane nicht über einen “Schließen”-Knopf, so das man entweder zu obskuren Hacks oder auf die TabbedComponent-Erweiterung aus dem JDK6 angewiesen ist. Kurze Erläuterung: Damit kann man quasi den Tab-Header komplett selber zeichnen, also auch inkl. eigener Panels, Buttons, usw.

Andererseits gibt es natürlich kein Tab-Management. Es gibt kein “Aktuellen Tab schließen”, kein “Alle Tabs schließen” oder auch “Nächsten Tab wählen”. Weder in einer Menubar noch als Popupmenu via Kontextmenu auf den Tab-Header.

Also, selber bauen! Es gibt natürlich eine Reihe von Möglichkeiten, aber das beste Rezept erscheint mir eine Mischung aus:

  • Die TabbedPane-Komponente, die Änderungen mittels firePropertyChange weitergibt, vorgeschlagene Kandidaten sind: openTabItem(null, title), closeTabItem(title, null) und selectedTabItem(null, title). Damit lassen sich alle o.g. Aktionen auf das Menu spiegeln.
  • Die Menubar, welche die Liste der Einträge und die Aktionen bereitstellt. Die Aktionen werden durch eine externe Komponente (nennen wir sie mal Controller) ausgeführt.
  • Spezieller TabChangeListener, welcher die o.g. Aktionen abfängt und die Änderungen an die Menubar weitergibt.
  • Ein Popupmenu (für das Kontextmenu) schickt die Commands direkt an o.g. Controller.

tab-managament

Die Lösung ist derzeit in die Applikation eingefügt, aber ich plane, das ganze als autonomes Package demnächst zu extrahieren.

Java-Update für Java-Update?

In der Java-Dev-Mailingliste bei Apple häufen sich die Fehlermeldungen bezgl. der Menubar (verschwindet, Abstürze, u.ä.). Scheint so, als würde es da alsbald ein Update des Updates kommen.

Java-Swing-Komponenten als Bild / Snapshot

Für unsere Anwendung (ähm, halt Diplomarbeit ftw!) wollte ich eine Export-als-Bild-Funktion für die Anzeige des aktuellen Graphen einbauen. Dabei handelt es sich um eine JComponent, die über das JUNG-Framework dargestellt wird.

Während jetzt Apple-Benutzer leichtfertig über dieses Feature lachen können, ist dies für Hauptzielgruppe der Anwendung (also Windowsbenutzer) eine sinnvolle Funktion. Es hat sicherlich wenig Sinn, umständlich einen Komplett-Screenshot anfertigen zu müssen.. viel einfacher kann das Programm das ja selbst machen.
Es gibt eine Reihe von Suchtreffern in Google, die durchaus lesenswert sind.

Die Grundidee ist, dass man sich selbst ein Graphics-Objekt erstellt und dann die JComponent darauf arbeiten lässt. Im Falle von Swing ist dies idR vom Framework vorgegeben (nämlich für den Bildschirm), hier aber wollen wir ja ein eigenes Bild haben. Die Höhe und Breite ist in den meisten Fällen von der Komponente zu erfragen.
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

Anschließend brauchen wir das zugehörige Graphics-Objekt, denn damit arbeiten die Paint-Methoden. Clue Nummer 1: Man könnte in Versuchung kommen, und einfach getGraphics() zu nehmen – ist ja auch naheliegend. Tatsächlich erstellt das aber auch nur ein Graphics-Objekt, während createGraphics() ein Graphics2D-Objekt erstellt. Und in den meisten Fällen braucht man das auch.
Graphics2D g = bi.createGraphics();

Anschließend muss man sich entscheiden, welche Zeichenroutine man anstoßen will. Es gibt prinzipiell folgende Möglichkeiten: paint(Graphics), paintComponent(Graphics) und paintAll(Graphics). Sofern paintComponent() ausreicht, diese nutzen. Im Falle des JUNG-Graphen mit verschiedenen Subelementen und eigenem Renderer funktioniert jedoch nur paintAll(Graphics) auf allen System zuverlässig. Während paint(Graphics) unter MacOS und Linux noch funktioniert, produziert es unter Windows unschöne Ergebnisse.

Anschließend kann man mit dem statischen Aufruf ImageIO.write(bi, "png", file); das gesamte Bild als — hier PNG — speichern. Achtung: Dies ist eine JavaSE6-Klasse, in den Vorgängern benötigt man dann den Umweg über FileStream & Co.

[1] jung.sourceforge.net
[2] java.sun.com

Tags: , , , ,

Code-Coverage, Eclipse, jUnit

eclemma-1

Joa, es ist wieder Zeit für einen Artikel in der Java/DA-Reihe.

Als erstes und sehr einfach zu testendes Kriterium gilt beim Testen die Code Coverage. Eigentlich sehr simpel, denn es quantifiziert nur die Code-Coverage, also von wie vielen Zeilen Code (Instructions) tatsächlich wie viele (bzw. welche) auch ausgeführt werden. Da man dies natürlich gerne automatisiert machen möchte, sind Tests wie mit jUnit ideal. Aber, auch normale Anwendungen sind mitzutracen und den Codecoverage ist zu ermitteln.

eclemma-2

Für Eclipse gibt es dazu ein prima Plugin namens EclEmma. Und auch schnell über die Update-URL im Manager installiert. EclEmma dient dabei als eine Art Wrapper für verschiedene Launchtypen – also beispielsweise als normale Applikation oder einen jUnit-Test bzw eine ganze Suite (Plugins). Das Ergebnis wird nach Beendigung des Programms/Tests innerhalb von Eclipse in einer Baumstruktur dargestellt; sehr schön ist auch die grafische Hervorhebung direkt im Sourcecode. Alternativ kann man sich das Ergebnis auch als HTML exportieren lassen.

Tags: , , , , ,

Yaml, Yuml, ja was denn?

Für große Datenbanklayouts braucht man irgendwann Diagramme; die werden entweder mittels “ER-Syntax” oder im “modernen” UML entwickelt. Bei kleineren Projekten oder Veranschaulichungen kann so ein UML-Editor aber schnell zum exponentiellen Overhead werden, weil es sich einfach weder lohnt noch in der “Projektlage” jeder damit genug auskennt. In diese Richtung stoßt yuml.me, die mit Hilfe einer wirklich einfachen Syntax wunderschöne UML-Diagramme zaubern. Die sind zwar ggf. nicht 100% UML2-tauglich (bzw. haben nicht alle Funktionen des Standards), aber das Ergebnis ist durchaus sehenswert und für die meisten kleinen Dinge mehr als ausreichend.

Auf Seite der Konfiguration ist es natürlich blöd, dauernd mit diesen URLs zu hantieren. Aber, es gibt für alles eine Lösung: MMan nehme YAML. Sehr hübscher Syntax, fast ohne Overhead und damit viel Platz für das eigentlich Wesentliche: Informationen und Daten. Das Doctine Project, ein PHP Object Relational Mapper, nutzt zur Definition von Datenbankschemata auch eine YAML-Struktur (Beispiel).

Da ich weder Lust auf PHP coden noch das Aufsetzen eines Java-Servers hatte, habe ich eine kleine, feine Java-Anwendung geschrieben. Sie konvertiert ein DB-Schema in YAML in ein Bild (zeigt dieses an) und lässt auf Wunsch dieses Speichern.

Downloads (benötigt Java 1.6):

  1. Ausführbare Jar kAml.jar
  2. Dmg Imagefile alsAnwendung für Mac OS X

Tags: , , , ,