Dazu wird ein Online-Auktionsportal namens Auctioner vergleichbar Ebay erstellt, das Funktionen für Produktübersicht, Produkteintragung und Bieten umfasst. Diese Features sollen allesamt in Portlets verpackt werden, so dass sich Benutzer ganz nach Belieben ihre Weboberfläche innerhalb unseres Auctioners zusammenstellen können. Abschließend werden wir neben der reinen HTML-Ausgabe auch ein WML-Portlet erstellen, mit dem man vom Handy aus Produkte ersteigern kann.
Dieser Artikel basiert auf einem komplexeren Jetspeed- und Turbine-Beispiel, das im Rahmen des Buches Web-Applikationen und Portale mit Apache-Frameworks [Portale] erstellt wurde; dort werden auch die Grundlagen zu Velocity, Turbine, Torque und Jetspeed ausführlich dargestellt.
Die Grundidee des Auctioner ist, dass die Auktionseigenschaften im Rahmen eines Portals als Webapplikation angeboten werden sollen. Die Funktionalität des Auctioners soll folgende Punkte umfassen:
- Anlegen von neuen Auktionen
- Betrachten verschiedener Auktionen, nach Kategorien sortiert
- Bieten bei einer ausgewählten Auktion
- Regelmäßige Überprüfung auf abgelaufene Auktionen; nach Ablauf eMail an Bieter und Verkäufer
Die Realisierung der Portaleigenschaften geschieht mit dem Jetspeed-Framework, welches bereits in früheren Ausgaben des Java Magazins vorgestellt wurde [Jetspeed JM01] [Jetspeed JM02]. Über Jetspeed wird intern damit auch Turbine und Torque verwendet [Turbine JM]. Für detaillierte Erklärungen dieser einzelnen Frameworks und Tools sei an deren Dokumentation, obige Java Magazin Artikel und natürlich das Buch verwiesen.
Um das Beispiel nachvollziehen zu können, benötigen Sie folgendes:
- Den Jetspeed-Quellcode (jetspeed-1.4b1-release-src.zip) [Jetspeed]
- Das Apache Build-Tool Ant [Ant]
- Einen J2EE-kompatiblen Servlet Container, z. B. Tomcat [Tomcat]
- Sofern der verwendete Servlet Container diese noch nicht enthält (beispielsweise Tomcat 3.x), die Java-Bibliothek activation.jar aus dem Javabeans Activation Framework (JAF) von Sun [JAF]. Dieses muss im Klassenpfad für die Webanwendung bereitliegen.
Auf Wunsch können Sie auch das erweiterte Beispiel (inklusive Produkt- und Auktionsansicht und Benutzerbewertungen) mitsamt Quellcode von der Buchhomepage [Webapps] als Source-Distribution oder als war-Archiv herunterladen, das dann nur noch in das Tomcat-Webapps-Verzeichnis hineinkopiert werden muss.
Projektaufbau: Kompilieren und Deployen
Zur Projekterstellung benutzen wir das Build-Tool Ant von Apache. Die Haupt-Build-Datei build.xml liegt im Unterverzeichnis /build. Nach eventuell gewünschten Namens- und Verzeichnisanpassungen in build/build.properties kann unter /build mitant deploy
Das Kompilieren und Deployen kann nun kurz getestet werden; anschließend sollten ein paar Designänderungen wie Einbinden eines eigenen Logos und Änderung von Kopf- und Fußzeile vorgenommen werden, um sich noch einmal kurz den Jetspeed-Aufbau vor Augen zu führen. Zusätzlich haben wir für die Beispielanwendung noch zwei HTML-Portlets mit Begrüßungstexten und ein RSS Portlet für einen dynamischen Newsfeed eingebunden, die das Portal später lebendiger aussehen lassen. Wir verzichten an dieser Stelle auf Beschreibung und Vorgehensweise zur Erstellung solcher Portlets und verweisen auf Dokumentation und ältere Jetspeed-Artikel im Java Magazin.
Die Datenbankschicht
Bevor wir die eigentlichen Auktionsportlets schreiben können, muss die Geschäftslogik erstellt werden. Zusätzlich zu dem Benutzer, der in der Tabelle TURBINE_USER von Turbine und Jetspeed verwaltet wird, benötigen wir zwei weitere Entitäten; das zugehörige Datenbankschema ist in Abpictureung 1 gezeigt:- Kategorie: Die verschiedenen zu ersteigernden Produkte werden in (nicht weiter hierarchisch geordnete) Kategorien eingeteilt. Eine Kategorie besteht neben einer eindeutigen ID aus einem Namen und einer Beschreibung, die im Auctioner angezeigt werden kann.
- Auktion: Die Auktion ist offensichtlich die zentrale Entität des Auctioner. Auktionen werden vom Verkäufer angelegt mit einem festen Ablaufdatum, einem Startgebot und einem Mindestinkrement. Bieter können Gebote abgeben. Sie können um mehr als den geforderten Betrag erhöhen; dann wird das aktuelle Gebot zwar nur um das Mindestinkrement erhöht, aber sobald andere Bieter erhöhen wollen, bietet der aktuelle Bieter bis zu seinem Höchstgebot mit - in eBay wird diese Eigenschaft als Bidagent bezeichnet. Ist eine Auktion abgelaufen, so wird sie als abgelaufen deklariert, aber dennoch weiter gespeichert, da sie für Bewertungen und eine Historie immer noch benötigt wird. Eine Auktion besteht neben Produktnamen, Beschreibung und weiteren Daten für die Gebotsverwaltung aus einem Verweis auf eine Kategorie, dem Ablaufdatum der Auktion und einem Flag Abgelaufen, das bei abgelaufenen Auktionen gesetzt wird, um bei einer Abfrage nach noch nicht abgelaufenen Auktionen einen aufwändigeren Datumsvergleich zu vermeiden. Die Verknüpfung einer Auktion mit dem Benutzerschema geschieht nicht über eine ID, da diese von Turbine aus standardmäßig nicht lesbar ist, sondern über den ebenfalls eindeutigen Loginnamen des Benutzers. Wir haben in der Auktion noch ein weiteres Feld namens Bild eingefügt, in dem Binärdaten wie beispielsweise ein Bild des Produktes gespeichert und angezeigt werden kann, was wir im Folgenden aber nicht verwenden werden.
Nachdem Torque für uns ein Grundgerüst an Objektmodell erstellt hat, ist es an uns, dieses mit Leben zu füllen entsprechend den obigen Anforderungen an ein Auktionshaus. Dazu werden Methoden zu den leeren Entitätsklassen AuctionerUser, Auktion und Kategorie hinzugefügt; hierbei ist zu beachten, dass bei erneutem Torqueaufruf diese nicht mehr erzeugt werden, es werden ausschließlich die Base*-Klassen überschrieben. Auch werden wir einige Funktionen zu den entsprechenden Peer-Klassen hinzufügen, in denen statische Methoden zum Auffinden, Persistieren und Löschen der Objekte aus der Datenbank enthalten sind.
Die neu hinzuzufügenden Methoden sind in Abpictureung 2 im Objektmodell gezeigt. Da in diesem Artikel der Hauptaugenmerk der Portleterstellung selbst liegt, werden wir die einzelnen Methoden hier nicht weiter erläutern - hierfür sei an deren Javadoc-Dokumentation verwiesen. Alternativ hätten wir übrigens die gesamte Funktionalität natürlich auch mit EJBs modellieren können.
Das erste Auktionsportlet
Nachdem das Geschäftsmodell steht, können wir passende Views, in unserem Fall Portlets, erstellen. Für die Programmierung des Auctioners haben wir uns für die Verwendung von Velocity-Portlets mit Action-Klassen entschieden. Velocity ist eine scriptbasierte Template Engine aus dem Jakarta Projekt [Velocity JM]. VelocityPortlets unterstützen den Programmierer bei der Anwendung des Model-View-Controlling Musters; sie benutzen Velocity (und intern Turbine) zur View-Erstellung. Die Portlets des Projekts bestehen also jeweils aus einem oder mehreren Velocity-Templates und einer Java-Action-Klasse, die die Controller-Funktionalität übernimmt. Das Modell wurde oben bereits erstellt.Unser erstes Portlet soll dem Anlegen neuer Auktionen durch die Benutzer dienen. Dazu registrieren wir das Portlet in einer neu anzulegenden Datei local-portlets.xreg unter webapp/WEB-INF/conf (Listing 1).
Listing 1: local-portlets.xreg (nach Anlegen eines VelocityPortlets)
<?xml version="1.0" encoding="UTF-8"?><registry><portlet-entry name="NewAuction" hidden="false" type="ref"parent="Velocity" application="false"><meta-info><title>Neue Auktion anlegen</title><description>Portlet, um neue Auktionen anzulegen</description></meta-info><parameter name="template" value="auctionnew-main"hidden="true"/><parameter name="action"value="portlets.NewAuctionPortletAction"hidden="true"/><parameter name="lieblingskategorie" value=""hidden="false"/><media-type ref="html"/></portlet-entry></registry>
type="ref" parent="Velocity"
module.packages=de.instantsolutions.auctioner.modules,org.apache.jetspeed.modules
Das Haupt-Velocity-Template zum Auktionen-Anlegen, auctionnew-main.vm, das im von Jetspeed eingestellten Portlet-Template-Pfad webapp/WEB-INF/templates/vm/portlets/html liegen muss, ist in Listing 2 gezeigt. Zum größeren Teil handelt es sich dabei um einfachen HTML-Code. Interessant sind folgende Punkte: Die Liste der Auktionen wird über eine Schleife dynamisch gebaut. Die Herkunft dieser Werte folgt weiter unten. Datei wird die Kategorie ausgewählt, die der Benutzer als seine Lieblingskategorie gekennzeichnet hat. Gespeichert ist sie als Paramter in der Portlet-Registry:
<parametername="lieblingskategorie" value="" hidden="false"/>
name="eventSubmit_doAddnew"
Listing 2: auctionnew-main.vm zum Anlegen einer neuen Auktion
<form action="$jlink" method="post">Produktname: <input name="produktName" size="20"/> <br/>Beschreibung: <input name="beschreibung" size="20"/> <br/>Kategorie:<select name="kategorieId" size="1">#foreach ($kategorie in $auctioner.kategorien)<option value="$kategorie.id"#if ($kategorie.name == $lieblingskategorie) selected #end>$kategorie.name</option>#end</select> <br/>Ablaufdatum:<input name="ablaufday" size="10" maxlength="10"value="$auctioner.tomorrow"/> (dd.mm.yyyy)<input name="ablauftime" size="5" maxlength="5"value="12.00"/> (hh.mm) <br/>Startgebot: <input name="gebot" value="1" size="20"/> <br/>Inkrement: <input name="inkrement" value="1" size="20"/><br/><input type="submit" name="eventSubmit_doAddnew"value="Anlegen"/></form>
Listing 3: NewAuctionPortletAction zum Anlegen einer neuen Auktion
packagede.instantsolutions.auctioner.modules.actions.portlets;import org.apache.jetspeed.modules.actions.portlets.VelocityPortletAction;import org.apache.jetspeed.portal.portlets.VelocityPortlet;import org.apache.turbine.util.RunData;import org.apache.velocity.context.Context;import de.instantsolutions.auctioner.om.Auktion;public class NewAuctionPortletActionextends VelocityPortletAction {private static final String LIEBLINGSKATEGORIE_KEY ="lieblingskategorie";protected void buildNormalContext(VelocityPortlet portlet,Context context, RunData data) {context.put(LIEBLINGSKATEGORIE_KEY,portlet.getPortletConfig().getInitParameter(LIEBLINGSKATEGORIE_KEY));}public void doMain(RunData data, Context context) {setTemplate(data, "auctionnew-main.vm");}public void doAddnew(RunData data, Context context)throws Exception {Auktion auktion = new Auktion();// klappt wegen alter Turbine2.2 beta noch nicht :(// data.getParameters().setProperties(auktion);auktion.setProduktName(data.getParameters().getString("produktName"));auktion.setBeschreibung(data.getParameters().getString("beschreibung"));auktion.setGebot(data.getParameters().getInt("gebot"));auktion.setInkrement(data.getParameters().getInt("inkrement"));auktion.setKategorieId(data.getParameters().getString("kategorieId"));auktion.setAblaufdatumstring(data.getParameters().getString("ablaufday"),data.getParameters().getString("ablauftime"));auktion.setVerkaeuferLogin(data.getUser().getUserName());auktion.save();setTemplate(data, "auctionnew-done.vm");}}
Am Ende der doAddnew-Methode wird ein neues Template namens auctionnew-done.vm gesetzt, das sehr einfach gehalten ist:
<font color="$!{skin.Color}">Neue Auktion angelegt!<form action="$jlink" method="post"><input type="submit" name="eventSubmit_doMain"value="Weiter" /></form></font>
Durch diese einfach gehaltene Template-Action-Interaktion wird ohne großen Aufwand die Anzeige von HTML und die Bearbeitung von Anfragen realisiert, was dem Pull-Modell aus Turbine entsprecht, nur ohne (vom Programmierer erstellbare) Screen-Klassen. Man kann in diesem vorhandenen Gerüst unmittelbar den eigentlichen Code schreiben. Dies wird durch Turbine noch zusätzlich erleichtert, da Torque einen einfachen Datenbankzugriff erlaubt - zum Speichern eines Objektes in der Datenbank genügt es, dessen Attribute zu setzen und die save()-Methode aufzurufen. Außerdem hat Turbine die gesamte Benutzerverwaltung durchgeführt - wir haben keinen Code für das Login schreiben müssen und können einfach den eingeloggten Benutzer abfragen.
Bevor wir dieses Portlet erfolgreich testen können, muss noch erklärt werden, woher die Variable $auctioner im Template aus Listing 1 kommt.
Das auctioner-Tool
Im obigen Velocity-Template wurde aus allen Kategorien eine Dropdownliste erstellt. Hierzu benötigt man eine java.util.List, die die Kategorien enthält und über die mit dem Velocity-Kommando #foreach (... in ...) iteriert werden kann. Natürlich könnte man in der Action-Klasse dieses Objekt anlegen und in den Context schreiben. Da wir ähnliche Funktionen jedoch noch in anderen Portlets benötigen, wollen wir sie an einer zentralen Stelle anlegen. Wir benutzen hierzu das Pull-Tool-Konzept von Turbine, das es ermöglicht, Klassen in der TurbineResources.properties zu registrieren, die dann in allen Contexts zu Verfügung stehen.Wir wollen ein Request-Tool schreiben, daher muss das Interface ApplicationTool implementiert werden; wir erinnern uns, dass Request-Tools bei Init dann das jeweilige RunData-Objekt des Requests von Turbine gesetzt bekommen, sodass auch auf User- und Formulardaten zugegriffen werden kann. Die Klasse de.instantsolutions.auctioner.tools.AuctionerTool muss dafür das Interface ApplicationTool implementieren; die Methode
public List getKategorien() throws TorqueException {return KategoriePeer.getKategorien();}
In den TurbineResources.properties registrieren wir diese Klasse als Pull-Tool mit Namen auctioner durch
tool.request.auctioner=de.instantsolutions.auctioner.tools.AuctionerTool
Damit ist das erste Portlet funktionsfähig. Nach dem Erstellen des .war-Files und dem Deployment können sie sich als Benutzer einloggen und das Portlet hinzufügen (bei den Standardbenutzern ist ja das Portlet noch nicht auf der Seite enthalten). Das Anlegen von Auktionen ist damit jetzt möglich.
Portlet zur Auktionsteilnahme
Natürlich wollen die Benutzer bei Auktionen auch bieten; dem dient das nächste Portlet. Das Schema bei der Erstellung ist das gleiche wie oben. Zuerst tragen wir ein VelocityPortlet mit Namen AllAuctions, Actionklasse portlets.AllAuctionsPortletAction und Haupttemplate auctionall-main in die local-portlets.xreg ein. Dieses Template ist in Listing 4 gezeigt.Listing 4: auctionall-main.vm zum Ansehen von Auktionen
#if ($auktionen)Produkte:#foreach ($auktion in $auktionen)$auktion.produktName$auktion.ablaufdatumformatted$auktion.gebot €<a href="$jlink.addPathInfo("eventSubmit_doDetail","true").addPathInfo("auktionid", $auktion.id)">Details</a> <br/>#end#elseKategorie auswählen: <br/>#foreach ($kategorie in $auctioner.kategorien)<a href="$jlink.addPathInfo("eventSubmit_doSetkategorie","true").addPathInfo("kategorie", $kategorie.id)">$kategorie.name</a>$kategorie.beschreibung<br/>#end#end
Falls bereits eine Kategorie ausgewählt wurde, wird entweder angezeigt, dass die Kategorie keine Auktionen enthält oder aber eine Liste der Auktionen der Kategorie. Bei dieser Liste werden die Auktionen wieder per JetspeedLink als Links gestaltet. Hier wird die AuktionId und eine Methode der Action-Klasse angegeben. Diese Methode sieht so aus:
public void doDetail(RunData data, Context context)throws Exception {if (data.getParameters().containsKey(AUKTIONID_KEY)) {auktion = AuktionPeer.retrieveByPK(new NumberKey(data.getParameters().getNumberKey(AUKTIONID_KEY).getBigDecimal()));auktion.checkForAbgelaufen();context.put(AUKTION_KEY, auktion);setTemplate(data, "auctionall-detail.vm");}}
Wie wird nun die temporäre Variable kategorie des Benutzers gesetzt? Hierfür dient die Methode doSetkategorie() der Action-Klasse, die aufgerufen wird, wenn der Benutzer eine Kategorie ausgewählt hat. Diese setzt durch
data.getUser().setTemp("kategorie",data.getParameters.getInt(KATEGORIE_KEY));
Listing 5: auctionall-detail.vm zum Ansehen einer einzelnen Auktion und zum Bieten
$auktion.produktName ($auktion.beschreibung)#if ($auktion.isabgelaufen)Die Auktion wurde bereits beendet.#else#if ($data.user.userName.equals("anon"))Zum Bieten muessen Sie sich einloggen.#else#if ($auktion.verkaeufer.loginName.equals($data.user.userName))Sie sind der Verkäufer.#else<form action="$jlink" method="post">#if ($toosmall) Sie haben zu wenig geboten. #end#if ($wronginkrement)Sie haben nicht um das gegebene Inkrement erhöht.#end#if ($belowmax)Sie wurden von $auktion.bieter.userNamesofort überboten.#endGebot abgeben:<input name="gebot" size="10" maxlength="10"value="$possgebot"/> € <br/><input type="hidden" name="auktionid"value="$auktion.id"/><input type="submit" name="eventSubmit_doBid"value="Abgeben" /></form>#end#end#end<a href="$jlink.addPathInfo("eventSubmit_doMain","true")">Main</a>
public void doBid(RunData data, Context context)throws Exception {Auktion auktion = AuktionPeer.retrieveByPK(new NumberKey(data.getParameters().getNumberKey(AUKTIONID_KEY).getBigDecimal()));if (data.getParameters().containsKey(GEBOT_KEY)) {int gebot = data.getParameters().getInt(GEBOT_KEY);if (gebot < auktion.getGebot() ||(gebot == auktion.getGebot() &&auktion.getBieterLogin() != null))context.put("toosmall", Boolean.TRUE);elseif ((gebot-auktion.getGebot()) %auktion.getInkrement() != 0)context.put("wronginkrement", Boolean.TRUE);elseif (!auktion.bid(gebot, AuctionerUserPeer.getFromUser(data.getUser())))context.put("belowmax", Boolean.TRUE);}buildAuktion(context, data, auktion);buildAuctionContext(context, data);setTemplate(data, "auctionall-detail.vm");}
Ablauf von Auktionen
Wenn eine Auktion ihr Enddatum überschritten hat, muss diese als abgeschlossen gespeichert werden. Da der Ablauf einer Auktion nicht durch eine Benutzeraktion bewirkt wird, sondern durch Zeitablauf, muss ein Timer benutzt werden. Turbine stellt hierfür den sogenannten Scheduler zur Verfügung, den wir verwenden wollen.Nun zur Erstellung des eigentlichen Jobs, den wir AblaufCheckJob nennen wollen. Da er ein Assembler ist, muss er im Modulpfad unter scheduledjobs liegen. Diese Klasse erweitert ScheduledJob; ähnlich wie in einer Thread-Klasse findet sich der eigentliche Code in der run()-Methode:
public void run( JobEntry job ) throws Exception {int anz = AuktionPeer.checkForAbgelaufen();Log.info("AblaufCheckJob: found " +Integer.toString(anz) + " abgelaufene Auktionen");}
Als Scheduler Service in Turbine benutzen wir den nicht-persisten TurbineNonPersistentSchedulerService. Dazu ersetzen wir SchedulerService in TurbineResources.properties durch
services.SchedulerService.classname=org.apache.turbine.services.schedule.TurbineNonPersistentSchedulerService
scheduler.jobs=AblaufCheckJobscheduler.job.AblaufCheckJob.ID=1scheduler.job.AblaufCheckJob.SECOND=0scheduler.job.AblaufCheckJob.MINUTE=5scheduler.job.AblaufCheckJob.HOUR=-1scheduler.job.AblaufCheckJob.WEEKDAY=-1scheduler.job.AblaufCheckJob.DAY_OF_MONTH=-1
Einrichten der Anwendung
Nun haben wir zwar zwei Portlets erstellt, aber davon ist eigentlich noch nicht viel zu merken. Natürlich kann man sich als Benutzer einloggen, und dann über Anpassen von HTML diese Portlets zu den bereits angezeigten hinzufügen (Abb. 4). Dennoch bekommt ein Benutzer, der die Seite besucht, die Standard-Jetspeed-Seite. Erst als eingeloggter Benutzer kann er sich das Auktionshaus mühsam zusammenstellen. Um die ganze Sache attraktiver zu machen, muss also als erstes eine schön aussehende Seite, die alle nötigen Portlets enhält, gestaltet werden. Diese Seite kann entweder als eingeloggter Benutzer per graphischem Interface oder direkt durch editieren der PSML-Dateien gestaltet werden [Jetspeed JM02]. Diese Seite muss dann ins webapp/WEB-INF/psml/anon und webapp/WEB-INF/psml/user/turbine kopiert werden. Damit erhalten wir einen Startpictureschirm, auf dem ein Benutzer noch ohne eingeloggt zu sein, bereits Kategorien nach Auktionen durchsuchen kann (Abb. 3).
WAP-Ausgabe
Bekanntlich kann man mit Jetspeed auch ohne größere Probleme Ausgaben in verschiedenen Formaten erzeugen - hierfür müssen nur die Portlets für die gewünschten Formate registriert sein und dann entsprechende Templates bereitstehen. Abschließend wollen wir dies exemplarisch am AllAuctionsPortlet vorführen. Dazu ergänzen wir diese Portletregistrierung um das neue Ausgabeformat, fügen also in local-portlets.xreg im Portlet-Tag von AllAuctions den Eintrag<media-type ref="wml"/>
Die neu erstellten WML-Portlets lassen sich nun nach Einloggen (in der HTML-Maske) durch Customize-WML hinzufügen; allerdings sind die ursprünglichen WML-PSML-Dateien in der derzeitigen Jetspeedversion fehlerhaft, es muss der FlowPortletController in
<controller name="FlowPortletController"/>
<controller name="ColumnController"/>
In Abpictureung 5 sind die fertigen WML-Seiten in einem WAP-Browser, hier dem WAP-Emulator des Nokia Mobile Internet Toolkit [Nokia] gezeigt. Zuerst muss man sich als Benutzer einloggen, dann erhält man eine Übersicht über alle Portlets. Wählt man das Auktionen-Portlet aus, so bekommt man die übliche Kategorienübersicht. Anschließend kann man in der Detailansicht sogar bei Auktionen über das Handy mitbieten.
Ausblick
Wir haben ein Auktionsportal erstellt, mit dem man Auktionen anlegen und bei Auktionen mit bieten kann. Nach der Modellierung der eigentlichen Geschäftslogik mit Torque wurden sukzessive die einzelnen Portlets, basierend auf dem VelocityPortlet, erstellt, zunächst mit einer HTML-View. Unter anderem haben wir dafür die Turbine Services Scheduler zur Abfrage nach abgelaufenen Auktionen und Pull-Tool zur problemlosen Bereitstellung von Daten im Velocity-Context benutzt. Schließlich haben wir angedeutet, dass als zweite View für ein Portlet eine WML-Darstellung erzeugt, so dass man auch per Handy bei Auktionen mitbieten kann.Viele weitere Funktionen ließen sich nun einbauen. In [Webapps] haben wir zusätzliche Funktionalitäten zum Bewerten von Benutzern nach erfolgreichen Auktionen hinzugefügt. Für solche Erweiterungen sowie detailliertere Erklärungen zu Jetspeed und Turbine sei an das Buch verwiesen, das demnächst im Software & Support Verlag erscheint [Portale]. Dennoch haben wir in dieser einfach gehaltenen Anwendung gesehen, wie Turbine den Programmierer bei der Aufteilung und Entwicklung von Modell- und Präsentationsschicht unterstützt, und Jetspeed dann das ganze in einen Portalrahmen verschnürt.
Übrigens finden Sie einige der Java Magazin-Artikel, auf die in diesem Beitrag verwiesen wird, unter www.javamagazin.de.
Links und Literatur
- [Ant] Apache Jakarta Ant, jakarta.apache.org/ant
- [Jetspeed] Apache Jakarta Jetspeed, jakarta.apache.org/jetspeed
- [Jetspeed JM01] Daniel Haischt, Jetspeed - Portallösungen der nächsten Generation, Java Magazin 1/2001, pp. 66-68.
- [Jetspeed JM02] Stefan Undorf, Dominik Neumann, Portalentwicklung mit Jetspeed, Java Magazin 7/2002, pp. 61-65.
- [Tomcat] Apache Jakarta Tomcat, jakarta.apache.org/tomcat
- [Turbine] Apache Jakarta Turbine, jakarta.apache.org/turbine
- [Turbine JM] Fabian Theis, Turbine - das Apache Web Application Framework, Java Magazin 6.2002, pp. 73-77 (Teil 1) und Java Magazin 7/2002, pp. 66-71 (Teil 2).
- [Velocity JM] Web Frameworks - Webmacro, Velocity und Cameleon im Vergleich, Java Magazin 3.2002, Seiten 34-40.
- [Maven] Turbine Maven, jakarta.apache.org/turbine/maven
- [Portale] Fabian Theis (Hrsg.), Web-Applikationen und Portale mit Apache-Frameworks, Software & Support Verlag, erscheint Ende 2002. www.entwickler.com/buecher/portale
- [eBay] eBay, eBay - Der weltweite Online-Marktplatz, www.ebay.de/
- [Nokia] Nokia, Nokia Mobile Internet Toolkit: Nokia, forum.nokia.com/main/1,35452,1_1_50,00.html
- [WML] Roland Ortner, SELFWML, Version 1.2, http://www.orderone.de/selfwml/
- [Hypersonic] hsqldb-Development Team, hsqldb - 100% Java Database, hsqldb.sourceforge.net/
- [JAF] Sun, JavaBeans Activation Framework, java.sun.com/products/javabeans/glasgow/jaf.html




