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.