EJOE und XSLT – ein gutes Gespann?

Wer findet es nicht überflüssig, unnützen Quellcode erfordernd, imperformant und einfach irgendwie nervend, wenn man das Ergebnis einer Abfrage gegen einen Anwendungsserver nicht einfach in der Anzeige rendern kann, sondern zwischen Beans und diversen Java-XML-Document-Modellen hin-und-her-konvertieren muss?

In der Firma, für die ich arbeite, werden wir quasi täglich mit diesem Problem konfrontiert:
Wir verwenden (derzeit noch) einen sehr einfachen Anwendungsserver (ohne Bezug zu J2EE) für Backend-Anbindung und Geschäftslogik. Auf die darin bereitgestellten Funktionalitäten wird dann mittels eines auf StrutsCX aufsetzenden Frontend zugegriffen.

StrutsCX is Struts with XSLT – as alternative to JSP.
StrutsCX overcomes the limitations of the Struts Framework by enabling you to utilize XML, XSLT, and XPath technologies instead of its standard JavaServer Pages.

The StrutsCX-project about itself

Zwischen dem Frontend und dem Anwendungsserver werden hauptsächlich Businessobjekte (POJOs) ausgetauscht.
Diese Objekte liegen dabei in zwei unterschiedlichen Varianten vor:
Einmal für den Anwendungsserver mit gewissen zusätzlichen Eigenschaften um bspw. direkt im Datenbanklayer genutzt werden zu können (in diesem Fall Apaches Torque) und andererseits für das Frontend als simple Beans mit schlichten Setter/Getter-Signaturen. Die Umwandlung zwischen den beiden Formen wird automatisch im jeweiligen Serialisierungs- bzw. Deserialisierungsvorgang vorgenommen.

Eigentlich also eine ganz normale Multi-Tier-Geschichte, oder?

Nun das Problem liegt hierbei wie folgt: Um die Businessobjekte zwischen Frontent und Anwendungsserver auszutauschen, werden diese in eine übertragbare Form gebracht und dann jeweils wieder in die Objektform deserialisiert. Die übertragbare Form ist hierbei XML. Eigentlich also super passend für StrutsCX, wäre da nicht der Umstand, dass die Deserialisierung in Objektform durch uns nicht umgangen werden kann. Was folgt ist eine ziemlich nervige Kette:

  • Frontend erstellt Request aus “normalen” Java-Containern und Businessobjekten
  • Clientkomponente des Anwendungsserver benötigt XML, also wird der Request in einen XML-String transformiert
  • Der Server deserialisiert den Request
  • Server verarbeitet den Request und antwortet in XML
  • Frontend deserialisiert die Server-Response zu Objekten
  • Frontend konvertiert die Response-Objekte um zu JDOM
  • JDOM-Form wird an StrutsCX übergeben, welches dann die XML-Transformation durchführt

Oh, ja, wäre es nicht schön, wenn man die Serverantwort ohne zeitraubende Deserialisierung und nachfolgende Umwandlung direkt an StrutsCX übergeben könnte? Und hier schlagen wir die Brücke zu EJOE: Mit EJOE ist eben dies seit Version 0.4.0 problemlos möglich. Normalerweise geht EJOE zwar ähnlich vor wie obig genannter Anwendungsserver:

  • Client übergibt Request an EJOE (EJClient)
  • EJClient transformiert den Request in eine übertragbare Form (Serialiserung)
  • EJServer deserialisiert den Request
  • EJServer verarbeitet den Request und serialisiert die Response in eine übertragbare Form
  • EJClient deserialisiert die Response und händigt sie dem Aufrufer aus

Zum besseren Verständnis hier noch eine grafische Übersicht des EJOE-Ansatzes:

Seit Version 0.4.0 besteht die Möglichkeit in EJOE mit sogenannten Serialisierungsstrategien zu arbeiten. Diese Strategien dienen der Einflussnahme auf die Punkte an denen Serialisierung bzw. Deserialisierung erfolgt.

EJOE unterstützt derzeit die folgenden Serialialisierungstrategien:

  • EJConstants.ADAPTER_STRATEGY_DEFAULT
  • EJConstants.ADAPTER_STRATEGY_DIRECT
  • EJConstants.ADAPTER_STRATEGY_MIXED

Eine Erklärung aller drei Strategien steht auf der EJOE-Projektseite http://ejoe.sourceforge.net/de/strategies.html bereit.

Uns interessiert für dieses Tutorial die Strategie EJConstants.ADAPTER_STRATEGY_MIXED. Dabei wird vom EJClient anstelle deserialisierter Objekte die serialisierte Form einer Serverantwort zurückgeliefert. Das bedeutet schlicht und einfach: Wird für die Serialisierung ein auf XML-basierender SerializeAdapter (siehe SerializeAdapter – Warum Serialisierung so wichtig ist) wie EJOEs Standardadapter de.netseeker.ejoe.adapter.XStreamAdapter benutzt, dann erhält der Aufrufer die Serverantwort in Form von XML innerhalb eines java.nio.ByteBuffer zurück. Dieses XML kann dann beispielsweise in einer XML-Transformation weiterverwendet werden.

Um dies anhand eines Beispiels zu demonstrieren benötigen wir:

  • ein Businessobjekt (POJO)
  • ein XML-Stylesheet (*.xsl)
  • einen Server
  • einen ServerHandler
  • einen Client

Das Test-POJO

Für unseren Testfall ist ein quasi statisch vorbelegtes POJO komplett ausreichend. Unser Server wird auf (alle) Clientanfragen einfach immer eine neue Instanz dieses POJOs als Antwort liefern.

package de.netseeker.ejxslt;

public class TestPojo
{
	String	name			= "EJOE";
	String	description		= "EJOE is a lightweight remoting framework with focus on pluggable (de)serialization mechanisms. It offers a scaleable high-performance implementation of the request-process-response-pattern build around a modern multithreaded server architecture.";
	int		interception	= 2004;
	String	projectpage		= "http://ejoe.sourceforge.net";
	String	sfpage			= "http://sourceforge.net/projects/ejoe/";
	String	download		= "http://sourceforge.net/project/showfiles.php?group_id=116355&package_id=126425&release_id=511776";

	public TestPojo()
	{
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public String getDescription()
	{
		return description;
	}

	public void setDescription(String description)
	{
		this.description = description;
	}

	public int getInterception()
	{
		return interception;
	}

	public void setInterception(int interception)
	{
		this.interception = interception;
	}

	public String getProjectpage()
	{
		return projectpage;
	}

	public void setProjectpage(String projectpage)
	{
		this.projectpage = projectpage;
	}

	public String getSfpage()
	{
		return sfpage;
	}

	public void setSfpage(String sfpage)
	{
		this.sfpage = sfpage;
	}

	public String getDownload()
	{
		return download;
	}

	public void setDownload(String download)
	{
		this.download = download;
	}
}

Das XML-Stylesheet

Der Server

Das Erzeugen und Starten eines EJOE-Servers erweist sich als sehr einfache Aufgabe. Mit einer sehr geringen Anzahl von Quelltextzeilen steht bereits ein voll funktionsfähiger Server zur Verfügung.

package de.netseeker.ejxslt;

import java.io.IOException;
import de.netseeker.ejoe.EJServer;

public class ServerStarter
{
	public static void main(String[] args)
	{
		EJServer server = new EJServer(new PojoHandler());
		try {
			server.start();
		}
		catch (IOException e) {
			e.printStackTrace();
			System.exit(-1);
		}
	}
}

Der ServerHandler

EJOE bedingt immer die Benutzung eines sogenannten ServerHandlers. Dieser kann selbst erstellt werden oder aber man benutzt einen der vorgefertigten ServerHandler um per Remote-Reflection (Remote Method Invocation via serverseitiges Reflection) Funktionalitäten für die Clients zu veröffentlichen.
Eine vollständige Erklärung der unterschiedlichen Möglichkeiten steht unter http://ejoe.sourceforge.net/de/serverhandler.html bereit.
In unserem Beispiel benutzen wir einen selbst erstellten ServerHandler, welcher keinerlei Auswertung der Clientanfragen vornimmt, sondern lediglich immer ein neues Test-POJO als Serverantwort aufbereitet.

package de.netseeker.ejxslt;

import de.netseeker.ejoe.handler.ServerHandler;

public class PojoHandler implements ServerHandler
{
	public Object handle(Object obj) throws Exception
	{
		return new TestPojo();
	}
}

Der Testclient

Im Gegensatz zu den obigen Quellen müssen wir beim Testclient ein wenig mehr tun, da wir nach dem Ansprechen des EJOE-Servers noch eine XML-Transformation durchführen wollen. Der Testclient muss

  • Den EJOE-Client (EJClient) initialisieren
  • Eine Abfrage an den EJServer absenden
  • Die Serverantwort auf der Konsole ausgeben
  • Das XML-Stylesheet und die Serverantwort an einen javax.xml.transform.Transformer übergeben, welcher dann die XML-Transformation vornimmt
  • Das Ergebnis der Transformation auf der Konsole anzeigen und zusätzlich in einer Datei abspeichern
package de.netseeker.ejxslt;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import de.netseeker.ejoe.EJClient;
import de.netseeker.ejoe.EJConstants;
import de.netseeker.ejoe.io.ByteBufferInputStream;
import de.netseeker.ejoe.io.IOUtil;

public class TestClient
{
	//Erzeugen des EJOE-Clients, es wird (falls auf dem Classpath vorhanden)
	//automatisch XStream zur Serialisierung verwendet
	private EJClient	ejclient	= new EJClient("localhost", EJConstants.EJOE_PORT);

	public TestClient()
	{
		//EJOE-Client für ADAPTER_STRATEGY_MIXED konfigurieren
		ejclient.setAdapterStrategy(EJConstants.ADAPTER_STRATEGY_MIXED);
	}

	public static void main(String[] args)
	{
		TestClient client = new TestClient();
		String xsl = "Test.xsl";
		if (args.length > 0) {
			xsl = args[0];
		}
		client.demonstrateXmlTransformation(xsl);
	}

	public void demonstrateXmlTransformation(String xsl)
	{
		try {
			//EJClient liefert das Ergebnis innerhalb eines java.nio.ByteBuffer
			ByteBuffer result = (ByteBuffer) ejclient.execute(null);

			//de.netseeker.ejoe.io.IOUtil ist Bestandteil der EJOE-Distribution
			//und enthält einige Hilfsmethoden für den Umgang mit allerlei I/O-bezogenen Aufgaben
			System.out.println(IOUtil.decodeToString(result));

			//de.netseeker.ejoe.io.ByteBufferInputStream und de.netseeker.ejoe.io.ByteBufferOutputStream
			//sind ebenfalls Bestandteil der EJOE-Distribution und vereinfachen
			//das "dynamische" Lesen und Schreiben von ByteBuffern
			Source source = new StreamSource(new ByteBufferInputStream(result));

			Writer buffer = new StringWriter();
			FileWriter writer = new FileWriter("out.html");

			//kompilieren des Stylesheets
			Transformer transformer = TransformerFactory.newInstance().newTransformer(new StreamSource(new File(xsl)));
			//XSL-Transformierung der EJOE-Response
			transformer.transform(source, new StreamResult(buffer));

			String html = buffer.toString();
			//Anzeigen des Transformationsergebniss
			System.out.println(html);

			//Speichern des Transformationsergebniss in die Datei "out.html"
			try {
				writer.write(html);
			}
			finally {
				IOUtil.closeQuiet(writer);
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			System.exit(-1);
		}
		catch (TransformerConfigurationException e) {
			e.printStackTrace();
			System.exit(-1);
		}
		catch (TransformerFactoryConfigurationError e) {
			e.printStackTrace();
			System.exit(-1);
		}
		catch (TransformerException e) {
			e.printStackTrace();
			System.exit(-1);
		}
	}
}

Das Resultat

Bilder sprechen mehr als Worte. Hier das Ergebnis der Transformation:
Das Resultat

Fazit

EJOE und XSLT passen sehr gut zusammen. Vorausgesetzt man kennt sich ein wenig mit XSLT aus und stellt sicher, dass die EJOE-Clients einen XML-basierten SerializeAdapter verwenden, ist die direkte Einbettung von Serverantworten in ein XSLT-basiertes Frontend keinesfalls ein Problem.
(Eine entsprechende Übersicht der verfügbaren SerializeAdapter für EJOE steht unter http://ejoe.sourceforge.net/de/adapterlist.html bereit)

Wer ohnehin auf XSLT als Layouttechnologie setzt, erhält vom Gespann EJOE/XSLT eine einfache und performante Lösung ohne auf die Vorzüge einer mehrschichtigen Architektur (engl. multitier architecture) verzichten zu müssen.

>>Download Java und XSL Quelldateien des obigen Beispiels

EJOE ist ein Remoting Framework für Java. Es besteht aus Client- und Serverkomponente und unterstützt den Datenaustausch zwischen Client und Server via austauschbaren (De-)Serialisierungsmechanismen, z.B. XStream, Castor oder Standard Java Beans Serialisierung. Es handelt sich dabei um eine hochperformante und skalierbare Implementierung des Request-Process-Response Patterns auf Basis des java.nio Packages. EJOE kann je nach Verwendung den Kategorien Middleware, Remoting Framework, I/O Framework und Object Request Broker zugeordnet werden.
Mehr Informationen zu EJOE inklusive einem umfangreichem, deutschen Handbuch stehen auf der Projektseite http://ejoe.sourceforge.net/de/ bereit.

Bookmark and Share
No Comments
Juni 9, 2007 in EJOE

Leave a Reply

Using Gravatars in the comments - get your own and be recognized!

XHTML: These are some of the tags you can use: <a href=""> <b> <blockquote> <code> <em> <i> <strike> <strong>