Implementing a faceted search - Part One

First of all, I think I will continue this Blog in English. I saw in the logs, that a lot of users who find this Blog through a search engine, are coming from other countries than Germany. So it does not make sense to keep on posting in German.

Today I will blog about implementing a faceted search in Java. I guess most of you are not so familiar with what a facet search is all about. To be honest, I am not so familiar with all the nifty theory, linguistics and details myself. You have to imagine a faceted search, like a way to browse through a huge list of items. Every item in the list is attached with some metadata. This metadata is organized into facets or taxonomies and values or headings. An example facet could be „Age“ and the headings for that facet could be „13+“, „15-18“ or „21“. Some sample item, let's say a deck of poker card, could contain the heading „13+“. A good example is the eToys website I think. You start by seeing all the different facets and headings. Selecting one facet will narrow down you search result as well as the forward selections you can take. Faceted search is a very user-friendly way of browing a repository.

So how could you integrate a facet search into your own website? I did this more than 2 years ago for one of our websites. Back then, I came across a Java library called Facetmap. It was offering everything I needed to incorporate a facet search into a Servlet based web application. There were a lot of commercial tools out there, like Dieselpoint or Endeca. Unfortunately they are all very expansive. So I decided to buy a Gold license of Facetmap, which cost me around $700. After playing around with Facetmap for a while, I figured out that for our website, the free edition would have been totally sufficient. But it was good to buy it anyways as I got great implementation support and even access to the source code. Facetmap is not an open source library. Not even the Light version of Facetmap is.

Anyway, two years have passed now. So I thought it was time to try out the latest version of Facetmap, which is 2.1. Knowing, that Facetmap Light was quite powerful and sufficient for most of my websites, I wrote a small sample web application that would make use of the latest Facetmap version. Facetmap can be downloaded as a trial version and ships with a .war file. The file facetmapgold-2.1.war contains an example webapplication. However, most of the code is based on good old jsp and some taglibs, so I decided to base my test application on Apache Wicket, Google Guice and Maven 2.

In the first part of this guide, we should build the project so that everyone is at the same page. I created a pom.xml for Maven 2 that does most of the stuff for you. Download my project.zip file and extract it into any directory which will become your new project root that way. After extraction you find the pom.xml file in the root. Before you can use the project pom however, there is an additional step you have to perform. Unfortunately the Facetmap libraries are not available in any Maven 2 repository I know of. I took the Facetmap Light 2.0 .jar file, fixed the name and provided a custom .pom file, so that I could use Facetmap within my own local repository. All you have to do, is take this .zip file and extract it into the root of your local Maven 2 repository. Under Linux this would be the ~/.m2/repository directory. Under Windows I think it is C:/Documents and Settings//m2/repository.

Once you have extracted the .zip file to that location, double-check that you have the following jar file in your local Maven 2 repository: com/facetmap/facetmap/2.0/facetmap-2.0.jar

Now that you have done the prerequisites it's time to build the project. Go to your project root and type mvn-package. This will compile everything and create a .war file ready for use as a webapplication. My project depends on the following libraries (that are all handled through Maven):

apache-wicket
apache-wicket-guice
log4j
slf4j
commons-lang
jetty
testng
facetmap light



To make it easier, I added the Jetty plugin into the Maven 2 build file. Just type mvn jetty:run to fire up a local Servlet container on port 8080 and deploy our new .war file to that container. You can access the webapplication by opening http://localhost:8080/web



Sorry for the poor layout. In the left column you will find the different facets and their headings. In the right column you will see all the items that match your current selection. When opening the page for the first time, you will see all the movies in the result list until you have made a first selection by clicking one of the links.

I will get to the implementation details soon but let's create a Eclipse or IntelliJ project first. Stop Jetty by pressing Ctrl+C in the Maven console. Now type mvn idea:idea or mvn eclipse:eclipse to create your project files for either one of the two famous IDE's. You are now ready to open the project and look at some source code (which we will do in the next part).

Testbarkeit von Code testen

In einer kurzen Demonstration Session auf dem OOPSLA 2008 habe ich gestern ein interessantes Tool kennengelernt. Mit dem Testability Explorer von Google kann Java Code dahingehend untersucht werden, wie testable die Klassen sind. Der Testability Explorer wird von Misko Hevery von Google betreut, der auch die Session auf der OOPSLA gegeben hat.

Zusammengefasst ist der Testability Explorer ein Command Line Utility mit dem sich Packages innerhalb jar-Files scannen lassen. Für jede Klasse aus diesen Packages wird ein Score vergeben, der sich daran orientiert, wie gut sich die Klasse in einem Unit Test testen lässt. Anschliessend wird für alle Metriken ein HTML Report erstellt, in dem die vergebenen Scores und alle gefundenen Probleme einsehen lassen.

Der Score basiert nach Aussage von Hevery grob gesagt darauf, welche Anstrengungen notwendig sind um einzelne Methoden isoliert und mit Mock Objekten zu testen. In der Demonstration wurde die Funktionsweise des Testability Explorers an einem Servlet aus einer Bibliothek demonstriert, die Hevery selbst vor einigen Jahren implementiert hatte. In der Service Methode des Servlets, wurde der HttpServletRequest in einem grossen if-else Block und abhängig von verschiedenen Conditions an verschiedene andere Servlet Implementierungen delegiert. Dabei wurde jedesmal eine neue Instanz mit dem new Keyword erzeugt. Im ersten Testlauf hatte das angesprochene Servlet einen Testability Score von über 9000. Je höher der Score desto schwerer lässt sich der Code testen. Im ersten Schritt wurden alle lokalen Objekterzeugungen in Fields konvertiert und mehrere Constructors hinzugefügt um somit die Tür für beispielsweise Dependency Injection zu öffnen. Dieser Schritt brachte bereits eine deutliche Verbesserung der Testbarkeit.

In einem zweiten Refactoring wurde die Anzahl der statischen Methodenaufrufe minimiert, bzw. die Implementierung der aufgerufenen Klasse auf Instanzmethoden umgestellt. Gerade dann, wenn der Client gegen ein Interface arbeitet und dort Instanzmethoden aufruft, wird das Testen um einiges leichter. Ein weiteres Problem der Servlet Klasse war die Verwendung eines zu breiten Scopes der verwendeten Objekte. Es wurde ein Repository Objekt in das Servlet hineingegeben, auf dem dann eine getPool().getConnection() aufgerufen wurde um mit der Connection weiter zu arbeiten. Das Problem dabei ist, dass dabei sehr viel mehr Schritte notwenig sind um für das Connection Objekt einen Mock Ersatz bereitzustellen. Um eine MockConnection zu erhalten, müssen ebenfalls das Repository und der Pool ”gemocked” werden. Eine entsprechende Mitteilung lässt sich sehr schön im Testreport des Testability Explorers ablesen. Die Lösung für das Problem an dieser Stelle war die direkte Verwendung und Übergabe der Connection im Constructor anstelle des kompletten Repository Objekts.

Der Testability Explorer untersucht unter der Haube jedoch nicht nur die statischen Probleme im Sourcecode. Vielmehr wird der Java Bytecode untersucht um weiterhin Probleme durch inkorrekte Verwendung von statisch globalem Objekten zu finden. Wie das genau funktioniert, konnte Hevery jedoch aufgrund von Zeitmangel nicht vorstellen. Das Open Source Projekt wird auf code.google.com gehostet und steht jedem zur Verfügung. Das es noch nicht sehr weit verbreitet ist, kann man unter anderem daran erkennen, dass Plugins für IDE's und Continuous Integration Server fehlen. In einem unserer nächsten Projekte will ich den Testability Explorer jedoch austesten und in diesem Rahmen ein Hudson Plugin schreiben. Ich denke gerade in Projekten mit vielen Junior Entwicklern ohne ausreichende TDD Erfahrung, kann der Testability Explorer schon sehr früh Metriken liefern, wie gut sich der Code des Teams testen lässt. In der Regel stösst man nämlich erst sehr viel später auf Probleme der Testbarkeit. Nämlich dann wenn die Testabdeckung einzelner Klassen oder Pakete zu niedrig ist. Soweit wie Google zu gehen, die ein Check-In von Code zurückrollen, wenn die eingecheckten Klassen nicht testbar genug sind, werden wir in unserem Team aber nicht gehen.

Security – Philosophie, Patterns und praktische Beispiele

So oder so ähnlich würde der ins Deutsche übersetzte Titel meines ersten Tutorials von der OOPSLA 2008 lauten. In fast vier Stunden hat Munawar Hafiz von der Universität of Illinois, uns in die Welt der Security Patterns eingeführt. Hafiz arbeitet mit Ralph Johnson (der von der Gang of Four) zusammen und hat an etlichen Publikationen über Security Patterns mitgearbeitet.

In seinem Tutorial geht es darum, verschiedene Patterns für die Sicherheit in Software Applikationen klassifizieren und anwenden zu können. Insgesamt hat Hafiz auf seiner Webseite mehr als 90 Patterns beschrieben und klassifiziert. Das Tutorial ist in zwei Teile aufgeteilt. Im ersten Teil demonstriert er zunächst welchen Einfluss Security auf die Software Architektur hat. Das ganze wird am Beispiel von sendmail durchgegangen. Zunächst stellt er Probleme von sendmail vor und zeigt wie diese mit qmail und postfix besser implementiert wurden. Hierbei geht er leider zu sehr ins Detail wie ich finde. Zwar werden bereits im ersten Teil einige Sicherheits Pattern vorgestellt (chroot jail, compartmentalization, secure pre-forking, single threaded facade, checkpoint system, content dependent processing etc.) aber er stellt die Architektur der drei genannten Mail Transfer Agents zu detailliert vor. Die Zeit wird später im zweiten Teil des Tutorials fehlen.

Nach einer kurzen Pause, lässt Hafiz die Teilnehmer an einem kleinen Spiel teilnehmen. Er lässt zwei Gruppen bilden. Dann verteilt er Blätter, die ein paar kurze, fiktive Geschichten aus der realen Welt enthalten. Zum Beispiel geht es um ein Army Camp, das von einer Seite aus beschossen wird, von der man keinen Angriff vermutet hat oder ein Königreich das für jederman zugänglich war und dessen König plötzlich ermordet wurde. Für jede Geschichte bildet eine Gruppe jeweils die ”Angreifer” und eine Gruppe die ”Verteidiger”. Jede Geschichte schliesst mit einer Frage an die ”Attacker” und die ”Security Experts” ab, in etwa ”Warum ist es vorher zu keinem Angriff auf den König gekommen”? Die Thematik wird dann in die Welt der Software übertragen und ein Security Pattern vorgestellt. Leider bricht Hafiz bereits nach 5 Geschichten ab, da er meint dass sich auf dem letzten Tutorial auf der OOPSLA 2007 alle am Ende gelangweilt hätten. Ich finde diese ”Übung” jedoch auflockernd und lustig.

Anders als erwartet, werden die verleibenden Security Patterns, die im Handout Material in Slides abgedruckt sind, nicht vorgestellt. Sehr schade wie ich finde. Der Pattern Katalog von Hafiz beinhaltet sehr viele gute Patterns, die sich auch in die Welt von Java und J2EE übernehmen lassen. Stattdessen beendet er das Tutorial, indem er in 30 Minuten durch eine Unmenge an Slides zu Buffer Overflow und SQL Injection durchrennt. Beide Problematiken sind mir zumindest ausreichend bekannt. Ich würde dieses Tutorial an erfahrene Entwickler und Architekten nur bedingt weiterempfehlen. Es wird sehr umfangreich auf Mail Transfer Agents der Linux Welt eingegangen und die eingentlich spannenden Patterns aus dem Security Catalogue nur kurz angerissen.

Erster Tag auf der OOPSLA

Mit einem Tutorial hat die OOPSLA nun für mich gestern begonnen. Wir hatten uns bereits sehr früh am Morgen in der Hotel Lobby verabredet. Während unserer Zeit in Nashville schlafen wir direkt Downtown im Marriot Courtyard Hotel in der 4th Avenue. Obwohl dieses Mariot nach Aussage eines Kollegen nur 3 Sterne hat, sind die Zimmer sehr gut. Leider konnte ich nicht besonders gut schlafen, da es Samstag Nacht um das Hotel herum sehr laut ist. Aufgrund der vielen kleinen Bars und Nachtclubs in der Nähe, sind bis früh um vier Leute auf den Strassen unterwegs und es geht oft lauftstark zu. Aber gut.

Nach einem mittelmässigen und mit $14 zu teurem Frühstück im Hotel, ging es ab zur OOPSLA. Das Nashville Convention Center ist nur 5 min Fussweg vom Marriot Courtyard in der 4th Ave. entfernt. Nach Vorlage unserer ausgedruckten Registrierungsbestätigung, bekam jeder eine Tasche von der OOPSLA, ein Shirt, eine CD, diverses Material über die Konferenz sowie ein Badge mit dem Namen drauf und den ganzen kostenpflichtigen Tutorials, für die man sich angemeldet hat. Das program der OOPSLA ist relativ unübersichtlich aufbereitet wie ich finde. Es gibt Tutorials, die dreieinhalb Stunden dauern und kostenpflichtig sind. Es gibt die regulären Sessions während der Hauptkonferenz, die parallel angeboten werden und zwischen 45 und 60 Minuten dauern. Zusätzlich gibt es noch Demonstrations, Lightning Talks, Workshops und Onward Events. Die separat gebuchten Tutorials wiederzufinden, ist ja noch relativ einfach. Aber einen optimalen Zeitplan für alle Sessions und Demonstrations die man sehen will zu erstellen, ist die Hölle. Glücklicherweise werden alle Demonstrations an mehreren Tagen angeboten, so dass man die Möglichkeit hat, eine parallel laufende Demonstration noch einmal später zu sehen. Schön finde ich, dass man im Vergleich zur Java One spontan entscheiden kan, in welche Session man gehen will. Auf der Java One ist der Platz begrenzt und man muss sich vorher für Panels anmelden.

Die OOPSLA Konferenz ist deutlich kleiner als die Java One. Ich würde schätzen, dass rund 2.000 Entwickler, Architekten und Manager die OOPSLA besuchen. Auf der Java One tummeln sich im Schnitt zwischen 10.000 und 20.000 Besucher. In meinem ersten Tutorial waren nur 9 Zuhörer. Es geht eher familiär zu. Schön finde ich das Tutorial Lunch, dass allen Besuchern der Tutorials zugänglich ist. In einem grossen Ballsaal, wird ab 12 Uhr Mittag serviert. Es sind jeweils Tische für 10 bis 12 Personen eingedeckt. Man trifft also ”zwangsläufig” auf andere Teilnehmer zum socializing. Ein Frühstück mit Kaffee und Kuchn wird auf der OOPSLA ebenfalls angeboten. Die Atmosphäre ist sehr locker. Man kommt mit anderen Besuchern sehr schnell ins Gespräch. Die meisten Entwickler, Architekten oder Consultants kommen aus der Java Welt. Aber auch zu .NET, C, C++ oder anderen Sprachen werden Veranstaltungen angeboten. Schauen wir mal, wie die Tutorials und Sessions so ablaufen.

Auf zur OOPSLA

In dieser Woche bin ich auf einer Konferenz in Nashville mit dem lustigen Namen OOPSLA. Auf dieser Konferenz, die einmal im Jahr stattfindet, geht es hauptsächlich um Aspekte der objektorientierten Programmierung. Allerdings finde ich, dass die meisten Sessions Aspekte aus der Java Welt aufgreifen. Der Name Java Konferenz ist demnach nicht ganz falsch. OOPSLA kann von der Vielfalt der Java Talks und Sessions jedoch nicht anderen reinen Java Konferenzen wie Java One oder Javoxx mithalten.

Die eigentliche OOPSLA 2008 beginnt erst am Dienstag. Heute am Sonntag und morgen am Montag werden lediglich kostenpflichtige Tutorials angeboten. Diese dauern jeweils zirka 4 Stunden und ich hoffe, dass man dabei ein wenig in die Tiefe gehen wird. Meine Firma hat mich für 4 Tutorials meiner Wahl eingetragen. Es geht darin jeweils um Security Patterns allgemein, Spring OSGI, Google Guice und Google Berry sowie um effective Java. Ich werde darüber später ausführlich berichten. Von Dienstag bis Donnerstag gibt es dann die Hauptkonferenz. Dort finden sehr viele, einstündige Sessions zu diversen Themen statt. Jeder der den einmaligen Entry Fee für die OOPSLA bezahlt hat, kan daran teilnehmen.

Da es bisher noch nicht soviel über OOPSLA zu schreiben gibt, noch ein paar Anmerkungen zur Einreise in die USA. Unser Flug ging mit Continental Airlines von Stockholm nach Newark und von dort nach Nashville. Bereits in der Schlage des Abfertigungsschalters wird von der Airline eine erste Security Überprüfung vorgenommen. Da ich als Deutscher mit deutschem, bordeauxfarbenem Reisepass und von Schweden aus fliegend, etwas aus der Reihe fiel, kam ich besonders in das Visier Continental Airlines Security Mitarbeiter. In schneller Abfolge wurden diverse Fragen gestellt: ”Wer hat das Gepäck gepackt”, ”Wo befand sich das Gepäck seit dem”, ”Hat Ihnen jemand etwas mitgegeben”? Ich musste sogar meine schwedische ID-Kort zu meinem Reisepass dazu zeigen. Beides wurde ausführlicher als bei schwedischen Reisenden überprüft.

Genauso unfreundlich war das die US Border Control (Homeland Security). Man musste jeweils den rechten und linken Zeigefinger auf ein Pad legen und es wurde ein Bild mit der Webcam gemacht. Da ich vergessen hatte, die Rückseite meines Visa Waiver irgendwas auszufüllen, rief mich der Homeland Mitarbeiter lautstark zurück, dummerweise ohne dabei in meine Richtung zu schauen so dass ich einige Sekunden brauchte um zu realisieren, dass er mich meinte. Ich schätze nicht viel länger und er hätte mich unsanft zurückholen lassen.

Die letzte Neuerung für mich war dann die Security Kontrolle auf dem Newark Airport. Man muss wie in Deutschland alle Hosentaschen leeren und den Gürtel ablegen. Zusätzlich müssen die Schuhe ausgezogen werden. Dann muss man in eine Art Fotobox steigen, die den abschreckenden Namen Sentry II trägt. Darin wird man dann von allen Seiten mit Luftdruck angepustet um Spuren von Explosives zu entdecken. Eine Computerstimme sagt vorher ”Firing Gun” - irgendwie kontra-produktiv wie ich finde. Ein System zum Aufspüren von Waffen, dass Firing Gun sagt.

Buchkritik: Pro Java EE Spring Patterns


In regelmäßigen Abständen gibts vom Manager meiner Abteilung eine Email, ob denn irgendjemand Bücher bestellen will. Ich habe mich diesmal für ein neues Werk aus dem Apress Verlag mit dem imposanten Namen „Pro Java EE Spring Patterns“ entschieden. Rechtzeitig vor dem Urlaub auf Rhodos, fand das gut 300 Seiten dicke Buch den Weg auf meinen Schreibtisch.

Um es gleich vorweg zu nehmen, das Buch ist für Java Entwickler und Architekten mit Spring Erfahrung eher eine Enttäuschung. In den ersten beiden Kapiteln wird kurz auf die Entstehung der n-Tier Applikationen und des Spring Frameworks eingegangen. Wer keine Lust auf die x-te Einführung in Dependency Injection, Spring AOP und Spring MVC hat, kann gleich zu Kapitel 3 vorblättern. Die restlichen Kapitel 3 bis 6 beschreiben verschiedene Patterns aus dem Java EE und Spring Bereich. Jedes Kapitel geht dabei auf Entwicklungsmuster aus den Schichten Presentation,- Business und Integrations,- und Cross Cutting Concern Tier ein. Das finale Kapitel soll an einem fiktiven Applikationsbeispiel die Anwendung der verschiedenen Patterns demonstrieren. Dazu später mehr.

Die erste Enttäuschung ist Kapitel 3, welches Patterns aus der Präsentationsschicht beschreibt. Namentlich sind das Front Controller, Application Controller, Page Controller, Context Object, Intercepting Filter, View Helper, Composite View, Dispatcher View und Service to Worker. Der Aufbau und die Vorstellung jedes Patterns ist so aufgebaut, dass zunächst die Problemstellung anhand einer Java Anwendung aus dem Versicherungsbereich vorgestellt wird. An dieser Anwendung hat der Autor von „ Pro Java EE Spring Patterns“ wohl selbst mit entwickelt. Die Applikation scheint leider etwas an den Haaren herbei gezogen. Eine Handvoll JSP Dateien mit else-if Blöcken als Ersatz für verschiedene Controller Actions traue ich selbst der schlechtesten Web-Anwendung nicht zu. Wie dem auch sei, die e-Insurance Webapplication muss immer als schlechtes Beispiel herhalten, bevor mit Hilfe eines Patterns gezeigt wird, wie es besser mit J2EE und Spring geht.

Leider stellen die meisten der genannten Pattern der Präsentationsschucht dabei keine neuen Erkenntnisse dar, sondern lediglich die von Spring propagierte Vorgehensweise für die Implementierung. Das Page Controller und das View Helper Pattern ist also nichts weiter, als eine ganz normale Implementierung mit Spring MVC. Intercepting Filter stellt nicht invasive Möglichkeiten vor, mit einem Servlet Filter oder einem Spring MVC Interceptor Cross Cutting Concerns wie Tracing zu implementieren. Das Context Object Pattern propagiert die nicht direkte Verwendung von Klassen aus der Servlet API zur besseren Wiederverwendbarkeit der Controller Klassen. Auch keine neue Information. Für mich interessant sind lediglich Erwähnung und kurze Integrationsbeispiele mit zwei Opensymphony Produkten (Sitemesh und Clickstream) sowie Apache Tiles.

Weiter geht es mit der Business-Schicht in Kapitel 4. Hier werden die Pattern Service Locator, Business Delegate, Session Facade, Application Service und Business Interface vorgestellt. Leider auch wenig neue Informationen für Entwickler mit mehrjähriger Erfahrung im Java Bereich. Service Locator für enkapsulierte JNDI Zugriffe - ein alter Hut. Business Delegate als Proxy für Zugriffe einer Schicht in die jeweils andere, entspricht der Standard Implementierung mit Spring. Business Interface stellt eine Methode vor, um in EJB Remote Interface und Bean Implementierung synchron zu halten

Weitere Anwendungsfälle werden in Kapitel 5 für die Integrationsschicht vorgestellt. Dieses Kapitel ist völlig überflüssig. Es werden drei Pattern vorgestellt, die meiner Meinung nach seit Spring 1.x bekannt sind. Data Access Object (DAO), Procedure Access Object (das gleiche wie DAO nur mit Stored Procedures) sowie Web Service Broker (Implementierung eines Webservices auf Basis von Spring jeweils mit Apache Axis 2 und Burlap). Bereits hier sollte spätestens klar werden was „Pro Java EE Spring Patterns“ eigentlich ist. Man nehme die Spring Dokumentation, suche sich die besten Kapitel heraus und ordne jedem Kapitel ein Pattern aus der Java Welt zu. Das Ergebnis wird unter „Pro Java EE Spring Patterns“ in neuer Aufmachung verkauft. Spring Einsteiger sollten besser „Spring 2.0 im Einsatz“ von entwickler.press sowie „Expert Spring MVC und Web Flow“ von Apress lesen. Alte Hasen werden in „Pro Java EE Spring Patterns“ nicht wirklich viel neues entdecken.

Bleiben noch die letzten beiden Kapitel von „Pro Java EE Spring Patterns“ zu erwähnen. Kapitel 6 „Exploring Cross Cuting Design Patterns“ ist nichts weiter als ein Abriss über Acegi Security, AOP und die Implementierung von Transaction Handling mit Spring AOP. Kapitel 7 beschreibt eine fiktive Order Management Anwendung unter Verwendung einiger weniger der vorgestellten Anwendungsmuster. Das letzte Kapitel ist relativ kurz und beschäftigt sich hauptsächlich mit der agilen Planung der Projekt-Iterationen und der Einrichtung des Maven Projekts unter einer Eclipse basierten IDE.

Fazit: das Buch ist definitiv nicht für Entwickler mit Erfahrung im Bereich Spring. Die vorgestellten Patterns entsprechen zu großen Teilen der Art, wie man Spring Anwendungen standardmäßig implementiert. Anfänger lesen besser andere Einführungsbücher für das Spring Framework. „ Pro Java EE Spring Patterns“ ist für das Kennenlernen von Spring zu oberflächlich. Ein Buch das glücklicherweise mein Arbeitgeber bezahlt hat.

Einfaches Beispiel mit Spring Security und Spring MVC

Alles neu im Spring Webbereich. Nachdem ich zum letzten Mal vor rund einem Jahr mit Spring MVC und damals noch Acegi Security programmiert hatte, musste ich feststellen, dass sich eine Menge geändert hat. Meine Aufgabe heute bestand darin, einen Adminstrationsbereich der mit Spring MVC umgesetzt ist, mit einer Autentifizierung zu versehen.

Die Dokumentation, in der die neuen Features von Spring Security beschrieben werden, ist leider an einigen Stellen nicht detailliert genug. Ich habe es mit ausprobieren dennoch hinbekommen und will es hier kurz beschreiben.

Zunächst die Konfiguration der Spring MVC Controller. Die Konfiguration in der web.xml Datei sieht wie folgt aus.



<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
<servlet-name>spring-controller</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>spring-controller</servlet-name>
<url-pattern>/admin/*</url-pattern>
</servlet-mapping>



Da der gewählte Servlet-Name spring-controller lautet, wird eine Konfigurationsdatei mit Namen spring-controller-servlet.xml im WEB-INF Verzeichnis benötigt. Diese sieht bei mir wie folgt aus.



<beans xmlns="http://www.springframework.org/schema/beans" xsi="http://www.w3.org/2001/XMLSchema-instance" context="http://www.springframework.org/schema/context" p="http://www.springframework.org/schema/p" schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!-- Enable autowiring -->
<context:component-scan package="com.mini.biz.servlets">

<!-- Enabale @Controller annotations -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">

<!-- Enabale @RequestMapping annotations -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">

<!-- View resolver -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView">
<property name="prefix" value="/WEB-INF/view/">
<property name="suffix" value=".jsp">
</property>

</property>
</property></bean></bean></bean></context:component-scan></beans>



Es kommen drei neue Spring 2.5. Elemente darin vor. Das context:component-scan Element sorgt dafür, dass das angegebene Package gescannt wird um das Autowiring der Dependencies durchzuführen. Die DefaultAnnotationHandlerMapping bean ermöglicht das Nutzen der @Controller Annotations. Die AnnotationMethodHandlerAdapter bean ermöglicht das Nutzen der @RequestMapping Annotations. Die viewResolver Bean ist im „oldschool“ Spring MVC Style deklariert. Hier gibt es meiner Meinung nach bisher keine andere Möglichkeit. Meine Requests müssen also wie folgt aussehen: /admin/* damit sie vom Spring DispatcherServlet verarbeitet werden (siehe web.xml). Alles weitere dahinter, z.B. /admin/show.do muss von einem Controller abgearbeitet werden. Die entsprechenden Annotationen im Controller sehen also so aus:



@Controller
@RequestMapping("/show.do")
public class AdminShowController {



In meinem zu schützenden Admin Bereich gibt es 3 Ausgabeseiten, die auf /show.do, /edit.do und /pdf.do gemappt sind. Für einen formularbasierten Schutz, wird ein Login Formular benötigt. Ich führe dafür einen neuen Controller ein.



@Controller
@RequestMapping("/anmelden.do")
public class AdminLoginController
{
@RequestMapping(method = RequestMethod.GET)
public String getDefaultForm()
{
return "login";
}

@RequestMapping(value = "/process.do", method = RequestMethod.POST)
public String onSubmit()
{
return "login";
}
}



Per default reagiert der Controller also auf /admin/anmelden.do. Ich wollte eigentlich login.do als Mapping verwenden, aber irgendwie scheint das in Spring MVC bereits ein reservierter Handler zu sein, den man nicht selbst noch einmal vergeben kann. Der AdminLoginController gibt login als View Name zurück. In meiner Webapplikation muss es also eine Datei /WEB-INF/view/login.jsp geben, die das Login Formular enthält.

Kommen wir nun zur Spring Security Konfiguration. Zunächst müssen diese beiden Elemente in der web.xml dazukommen.



<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>



Alles unterhalb von /admin/ durchläuft damit zusätzlich die springSecurityFilterChain. Für die Konfiguration der Security Beans lege ich eine separate XML Datei an, die ich in der applicationContext.xml Datei einbinde. Meine securityBeans.xml sieht wie folgt aus:



<b:beans xmlns="http://www.springframework.org/schema/security" b="http://www.springframework.org/schema/beans" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">

<http config="true">
<intercept-url pattern="/admin/anmelden.do*" filters="none">
<intercept-url pattern="/**" access="ROLE_ADMIN">
<form-login page="/admin/anmelden.do" url="/admin/process.do">
</form-login>

<authentication-provider>
<user-service>
<user name="bob" password="bob" authorities="ROLE_ADMIN">
</user>
</user-service>

</authentication-provider>
</intercept-url></intercept-url></http></b:beans>



Die auf der Spring Security Webseite angegebene Namespace Konfiguration könnt ihr nicht 1:1 übernehmen, da es sonst zu einer Exception mit Ausgabe „namespace Invalid content was found“ kommt. Übernehmt einfach meine Namespace deklaration wenn ihr ein separates File habt. Alternativ kann man es auch so machen.



<beans xmlns="http://www.springframework.org/schema/beans" security="http://www.springframework.org/schema/security" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">

<security:http config="true">
<security:intercept-url pattern="/admin/anmelden.do*" filters="none">
<security:intercept-url pattern="/**" access="ROLE_ADMIN">
<security:form-login page="/admin/anmelden.do" url="/admin/process.do">
</security:form-login>

<security:authentication-provider>
<security:user-service>
<security:user name="bob" password="bob" authorities="ROLE_ADMIN">
</security:user>
</security:user-service>

</security:authentication-provider>
</security:intercept-url></security:intercept-url></security:http></beans>



Was macht diese Konfiguration? Zunächst wird die URL des Login Formulars /admin/anmelden.do komplett aus der Sicherung herausgenommen. Das ist notwendig damit man das Login Formular sehen kann ohne sich vorher zu authentifizieren. Mit /** werden dann alle URL's unterhalb von /admin geschützt und der Rolle ROLE_ADMIN zugewiesen. Jetzt kommt mit dem form-login Element das Herzstück. login-page=“/admin/anmelden.do“ legt die (eigene) URL des Login Formulars fest. default-target-url="/admin/show.do" ist die URL, an die nach erfolgreichem Login weitergeleitet wird.

Wichtig ist das login-processing-url Attribut. Es muss sich von login-page und default-target-url unterscheiden. Ist login-processing-url = login-page, dann wird der AbstractProcessingFilter von Spring Security nicht durchlaufen, da das Login Formular ungeschützt ist. Ist login-processing-url = default-target-url funktioniert zwar der Login, bei der Weiterleitung an die target URL wird aber erneut nach den Username und Password Parametern geschaut, die dann natürlich nicht vorhanden sind. Ich habe mich für login-processing-url="/admin/process.do" entschieden und diese neue URL im AdminLoginController auf POST Requests gemapped (siehe oben).

Das security:authentication-provider Element definiert einen einzelnen rudimentären Benutzer für die Rolle Admin. Username bob mit Passwort bob. Hier bietet Spring Security angeklügelte andere Mechanismen. Ich denke für eine sehr einfache Webanwendung reicht die Verwendung eines md5 oder sha Hashs in der Art:



<authentication-provider>
<password-encoder hash="sha">
<user-service>
<user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_ADMIN">
</user>
</user-service>
</password-encoder></authentication-provider>



Damit ist eigentlich alles bereits fertig. Die /WEB-INF/view/login.jsp Datei sieht vereinfacht so aus:



<form action="/stats/process.do" method="post">
Benutzername: <input name="j_username" type="text">
Passwort: <input name="j_password" type="password">
<input value="Login" type="submit">
</form>



Die beiden Eingabefelder müssen in der Standardkonfiguration j_username und j_password heissen.

Unit Tests mit Spring und asynchronem Verhalten

Wieder einmal ein Anwendungsfall aus dem aktuellen (privaten) Projekt. Wenn ein Benutzer seine Bestellung abschliesst, soll diese gespeichert, eine PDF-Rechnung generiert und eine Email mit Attachment versendet werden. Ich habe beschlossen, nach dem Speichern der Bestellung via Hibernate, dass Erstellen der Rechnung und den Email Versand in einen separaten Thread auszulagern, der asynchron ausgeführt wird. Dadurch kehrt die Webanwendung schneller wieder zum Benutzer zurück. PDF Erstellung und Email Versand dauern nämlich 3 bis 5 Sekunden. Unter Useability Gesichtspunkten also durchaus positiv.

Das Starten des Threads ist in eine Klasse OrderProcessor ausgelagert.



package com.mini.biz.order;

import com.mini.biz.tools.mail.EmailProcessor;
import com.mini.biz.fop.pdf.PdfGenerator;
import com.mini.biz.entities.cart.Cart;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;

/**
* The {@link OrderProcessor} handles further processing once an
* order has been saved in the system.
*/
public class OrderProcessor
{
@Autowired
private TaskExecutor _taskExecutor;

@Autowired
private PdfGenerator _pdfGenerator;

@Autowired
private EmailProcessor _emailProcessor;


public void processOrder(final Cart cart)
{
Runnable runnable = new Runnable()
{
public void run()
{
m_pdfGenerator.generate(cart);
m_emailProcessor.sentEmailForNewOrder(cart);
}
};
_taskExecutor.execute(runnable);
}
}



Auf die Details vom PdfGenerator und EmailProcessor gehe ich an dieser Stelle nicht weiter ein. Wichtig ist der TaskExecutor. Er ist wie folgt konfiguriert.



<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
</bean>



Leider nimmt der TaskExecutor nur Runnable Objekte entgegen. Ich persönlich finde das mit Java5 eingeführte Callable Interface schöner – aber gut.

Mit dem Spring Framework und TestNG lässt sich das alles wunderbar testen. Das eingesetzte Web Framework Apache Wicket lassen wir mal ausser Acht.



@Test
@ContextConfiguration(locations={"/applicationContext.xml"})
public class ApproveAndOrderPanelTest extends AbstractTestNGSpringContextTests
{



Hier wird im Spring 2.5 Stil der ApplicationContext referenziert. TestNG Tests müssen leider (im Gegensatz zu JUnit 4.x Tests) immer noch von AbstractTestNGSpringContextTests ableiten. Übrigens sollte man AbstractTestNGSpringContextTests nicht vor Spring 2.5.2 einsetzen, da dort ein wichtiger Bug behoben wurde, der auftritt wenn man die komplette TestNG Suite laufen lässt (AbstractTestNGSpringContextTests executes Spring default callbacks in any case (marked with "alwaysRun = true"). Die @Test Annotation gehört zu TestNG. Kommen wir zum Code der vor den Tests ausgeführt wird:



@BeforeClass
public void before()
{
// Not mentioned here: delete existing PDF File

// Start Wiser SMTP
_wiser = new Wiser();
_wiser.setPort(2525);
_wiser.start();

// Alter MailSenderBean
JavaMailSenderImpl mailSender = (JavaMailSenderImpl) _mailSender;
mailSender.setPort(2525);
mailSender.setHost("localhost");
}
}



Hier starte ich einen Dummy SMTP Server von den Subversion Jungs. Anschliessend „verbiege“ ich die MailSender Bean damit der Dummy SMTP Server verwendet wird. Achtung, wenn Ihr weitere Unit Tests innerhalb der Suite laufen lasst, denkt dran dass Spring Beans in der Regel Singletons sind. Das „Verbiegen“ einer Bean schlägt also auch auf Folgetests durch, wenn es nicht in einer tearDown Methode wieder rückgängig gemacht wird. Nun der eigentliche (start gekürzte) Test.



public void testPanel() throws InterruptedException
{
// not shown: set up mock Bestellung aka Cart

assertEquals(0, _wiser.getMessages().size());
File pdfFile = _pdfGenerator.getPdfFile(cart);
assertFalse(pdfFile.exists());

// not shown: use WicketTester and FormTester to submit HTML Form and trigger order processing

ThreadPoolTaskExecutor threadPoolTaskExecutor = (ThreadPoolTaskExecutor) _taskExecutor;
int activeThreads = threadPoolTaskExecutor.getActiveCount();
while (activeThreads > 0)
{
TimeUnit.SECONDS.sleep(3);
activeThreads = threadPoolTaskExecutor.getActiveCount();
}

pdfFile = _pdfGenerator.getPdfFile(cart);
assertTrue(pdfFile.exists());

assertEquals(1, _wiser.getMessages().size());
// not shown: check that email contains attachment
}



Zunächst überprüfe ich, dass keine PDF Datei existiert und keine Email in der Queue vorhanden ist. Dann wir, hier nicht aufgeführter Code ausgeführt um den OrderProcessor anzuwerfen, so dass dieser einen neuen Thread spawned. Der Thread läuft im Spring konfigurierten TaskExecutor, welcher in Wirklichkeit ein ThreadPoolTaskExecutor ist. Da der TestNG Test weiterläuft, muss ich warten bis alle Threads (in meinem Fall also genau ein einziger Thread) im ThreadPoolTaskExecutor beendet sind. Anschliessend überprüfe ich, ob ein PDF erzeugt und eine Email versendet wurde.

Bleibt nur noch das Aufräumen.



@AfterClass
public void after()
{
_wiser.stop();
}



So richtig mächtig ist das Ganze eigentlich erst unter dem Gesichtspunkt, dass es sich dank WicketTester und FormTester eigentlich gar nicht mehr um einen Unit Test, sondern um einen GUI-Test (Integrationstest) handelt – auch wenn dieser Code hier nicht aufgeführt ist. Apache Wicket wird mit Testklassen ausgeliefert, die Selenium oder Watir (von Layout Aspekten abgesehen) überflüssig machen.

Pojo to XML mit Spring, Castor und Hibernate

Ich baue gerade an eine Webanwendung, mit der es möglich sein soll, von einer Bestellung ein PDF Dokument zu erzeugen. Für die Generierung des PDF Dokuments habe ich Apache FOP als Technologie ausgewählt. In einem Schritt vor der Apache FOP wird zunächst die Bestellung, die auf Domain Ebene in die Klasse Cart gekapselt ist, in ein XML File umgewandelt. Das XML File wird dann mit einer zusätzlichen XSL Datei in XSL-FO umgewandelt und von Apache FOP weiterverarbeitet.

In diesem Posting soll es jedoch nicht um die PDF Generierung, sondern die Ûberführung der gespeicherten Cart Pojo's in XML Dateien gehen. Um möglichst wenig Arbeit mit der XML Erzeugung zu haben, verwende ich ein Marshalling Framework welches aus Java Objekten XML erstellt. Hier hat man die Auswahl zwischen verschiedenen Anbietern, z.B. Xstream, Jaxb, Xmlbeans, Castor etc. In einer Testphase habe ich verschiedene Frameworks ausprobiert und mich für Castor entschieden. Castor benötigt, wie in der Spring WS Dokumentation beschrieben, keine zusätzliche Konfiguration, Annotation whatsoever und kann sofort eingesetzt werden. Bereits während der Evaluierung des Marhalling Frameworks habe ich auf Spring WS gesetzt um gegen eine unabhängige API zu programmieren. Dadurch kann man im Idealfall das eingesetzte Framework schneller wechseln.

Diese Dependencies füge ich meiner Maven2 POM hinzu.



<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.xml.stream</groupId>
<artifactId>stax-api</artifactId>
<version>1.0-2</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm</artifactId>
<version>1.5.2</version>
</dependency>



Stax ist eine Implementierung von JSR173. Hier kann bestimmt auch eine andere API verwendet werden. Spring-OXM beinhaltet Klassen zur angesprochenen API Abstrahierung. Diese verwende ich, um meine Cart Objekte in XML Dateien zu überführen.



package com.mini.biz.tools.oxm;

import com.mini.biz.entities.cart.Cart;
import org.apache.wicket.util.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.oxm.Marshaller;

import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class CartMarshaller
{
@Autowired
private Marshaller m_marshaller;

public void toXml(Cart cart)
{
// todo: determine file path based on Cart using Strategy pattern
File xmlFile = new File("whatsoever");

if (!xmlFile.exists())
{
toXml(cart, xmlFile);
}
}

public void toXml(Cart cart, File xmlFile)
{
if (cart != null)
{
FileOutputStream os = null;
try
{
os = new FileOutputStream(xmlFile);
m_marshaller.marshal(cart, new StreamResult(os));
}
catch (IOException e)
{
// todo: add logging
}
finally
{
IOUtils.closeQuietly(os);
}
}
}
}



An der Stelle, wo das (XML) File aus dem Cart generiert wird, verwende ich normalerweise eine Hilfsklasse, da ich diese „Logik“ noch an anderen Stelle benötige. Erwähnenswert ist noch die mit Spring 2.5 eingeführte @Autowired Annotation. Folgende schlanke Spring Konfiguration ist notwendig.



<bean id="cartMarshaller" class="com.mini.biz.tools.oxm.CartMarshaller" />
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/>




Spring WS wird mit Marshaller Klassen für alle bekannten Frameworks (inklusive der oben genannten) ausgeliefert.

So weit so gut. Es gibt nur ein Problem. Die Pojo's, in meinem Fall ein Cart, werden mit Hibernate persistiert. Hibernate verwendet einen Proxy Mechanismus um das Lazy Loading Pattern zu implementieren. Wenn ich meinen Cart aus der Datenbank lade und an den CartMarshaller ergebe, dann wird keine Cart Instanz in XML umgewandelt, sondern ein CGLIBLazyInitializer. Es gibt 2 Dinge die jetzt passieren können. Entweder ist das erzeugte XML totaler Schrott oder es kommt zu einer Exception beim Marshalling irgendwelcher DataSource Objekte.

Der von Castor selbst vorgeschlagene Ansatz, die Proxy Interfaces in einer castor.properties Datei anzugeben, kommt für mich nicht in Frage. Erstens hatte ich Castor gerade gewählt, weil keine zusätzliche Konfiguration notwendig ist und Zweitens ist der CGLIBLazyInitializer eine Klasse und kein Interface. Hier verwende ich einen kleinen Trick um Hibernate davon abzuhalten, einen Proxy einzusetzen. Meine simple Cart Klasse wird als final deklariert.



public final class Cart implements Serializable



Damit ist zwar das Lazy Loading von Hibernate ausgehebelt, aber es gibt keine Probleme beim Marshalling mit Castor.