Portale und Portlets (2) – Public-Render-Parameter und Portlet-Events

Andy Bosch

Im Rahmen der Portal- und Portlets-Reihe stehen in diesem Artikel die ersten Neuheiten des aktuellen Portlet-Standards 2.0 auf dem Programm. Public-Render-Parameter und Portlet-Events waren lange gewünschte und geforderte Features, die nun endlich Einzug in die Spezifikation gefunden haben.

Der Portlet-Standard in der ersten Version (JSR-168) hat viele Anforderungen adressiert, die sich innerhalb des Portlets abspielen. Eine Kommunikation zwischen Portlets war jedoch so gut wie überhaupt nicht möglich. Es gab zwar eine Möglichkeit, über den Application Scope der Portlet-Session Daten innerhalb einer Portlet-Applikation auszutauschen, dies war jedoch maximal als Workaround zu betrachten. Portlets sind mittlerweile dem Kindesalter entwachsen. Man entwickelt nicht mehr lediglich Wetter-Portlets und Nachrichten-Teaser, sondern realisiert anspruchsvolle Portalprojekte. Damit wird zunehmend ein Austausch von Informationen zwischen Portlets sowie ein ausgefeiltes Eventing-Modell notwendig. Mit dem neuen JSR-286 stehen künftig zwei Möglichkeiten bereit, wie Daten und Events zwischen Portlets ausgetauscht werden können.

Bevor wir in die Tiefen der neuen Portletspezifikation hinabtauchen, wird zunächst ein Anwendungsfall betrachtet, der die neuen Möglichkeiten des JSR-286 veranschaulicht: Auf einer Portalseite eines Reiseveranstalters sind sowohl ein Wetter-Portlet als auch ein Portlet mit aktuellen Sightseeing-Empfehlungen vorhanden. Der Benutzer möchte natürlich das Wetter seines Reiseziels sehen und ändert im Edit-Modus die Adresse, auf die sich das Wetter bezieht. Personalisierung ist eines der wichtigsten Funktionen eines Portals. Nehmen wir der Einfachheit halber zunächst an, der Reiseveranstalter ist nur in Deutschland aktiv und der Benutzer kann lediglich die Postleitzahl eines Bestimmungsorts anpassen. Nachdem er diese Änderung vorgenommen hat, zeigt das Wetter-Portlet zwar die korrekte Wettervorhersage an, aber die Sightseeing-Empfehlungen kennen die im anderen Portlet eingegebene Postleitzahl nicht. Hier wäre es wünschenswert, wenn das Wetter-Portlet die neuen Erkenntnisse (die neue PLZ) an andere Portlets kommuniziert hätte bzw. eine Art „sessionpezifische, Portlet-übergreifende Variable“ gesetzt hätte. Genau an dieser Stelle kann zukünftig ein so genannter Public-Render-Parameter eingesetzt werden (Abb. 1).

Abb. 1: Portlets mit gemeinsamen Render-Parametern

Jetzt kommt eventuell der berechtigte Einwand, dass der Reiseveranstalter international agiert und daher eine komplexe Adresse anstelle einer einfachen Postleitzahl eingegeben und an andere Portlets kommuniziert werden sollte. Zudem soll nicht lediglich eine übergreifende Variable gesetzt, sondern vielmehr eine Art PLZGeändertEvent gefeuert werden, sodass explizit registrierte Portlets aktiv benachrichtigt werden. Hier kann künftig auf das Konzept der Portlet-Events zugegriffen werden. Damit ist es möglich, explizit Events zu erzeugen, die in einer eigens geschaffenen Lifecycle-Phase verarbeitet werden können. Im vorgestellten Beispiel kann sich für den Event einer Adressänderung das Portlet für die Sightseeing-Empfehlungen registrieren. Somit haben wir zwei Anwendungsfälle, die mit den neuen Features des JSR-286 abgebildet werden können.

Welchen Container soll ich nehmen?

Um die gezeigten Beispiele selbst testen zu können, ist natürlich ein JSR-286-kompatibler Portlet-Container notwendig. Da die Referenzimplementierung im Apache-Pluto-Projekt umgesetzt wurde, ist dieser Container für grundlegende Beispiele und Tests geeignet. Mittlerweile haben jedoch die meisten Portalhersteller schon eine JSR-286-Unterstützung mit an Bord.

Setzen von Public-Render-Parametern

Die Verwendung von Public-Render-Parametern ist insofern gelöst, als dass in der Programmierung zunächst keine gravierenden Unterschiede zu herkömmlichen Render-Parametern zu sehen sind. Analog zu (konventionellen) Render-Parametern kann in der Action-Phase ein Wert gesetzt werden, auf den in der folgenden Render-Phase zugegriffen werden kann. Der Unterschied, der aus einem Render- ein Public-Render-Parameter macht, liegt im Deskriptor portlet.xml. Erst mit diesen Angaben kann Portlet-übergreifend auf gesetzte Werte zugegriffen werden.

Listing 1: Setzen eines Public-Render-Parameters
public void processAction(ActionRequest request, ActionResponse response)
      throws PortletException, IOException {
   ...
   String requestPRP = request.getParameter( "zip" );
   response.setRenderParameter( "customerZip", requestPRP );
}

Listing 2: Public-Render-Parameter in portlet.xml
WeatherPortletde.jsfportlets.sample.portlet.WeatherPortletcustomerZip
...
customerZipjp:customerZip

In Listing 1 ist das Setzen eines Public-Render-Parameters analog zu normalen Render-Parametern realisiert. Der interessante Teil ist in Listing 2 abgebildet. Hier muss zunächst ein Public-Render-Parameter deklariert werden. Dies geschieht über einen eigenen Abschnitt auf Ebene der Portlet-Applikation. Eine Definition besteht aus einer eindeutigen ID, die im Beispiel auf customerZip gesetzt ist. Um eventuelle Namenskonflikte zu vermeiden, muss zudem ein QName (Qualified Name) angegeben werden. Pro Portlet kann anschließend eine Unterstützung für diesen Parameter eingetragen werden. Dies erfolgt über den Tag supported-public-render-parameter, eine Abfrage wie in Listing 3 abgebildet über request.getParameter(„customerZip“). Mit einer neuen portlet.xml können auch ältere JSR-168-Portlets umgestellt werden. Eine Änderung im Java-Sourcecode ist nicht zwingend notwendig.

Listing 3: Abfrage von (Public-)Render-Parametern
protected void doView(RenderRequest request, RenderResponse response)
   throws PortletException, IOException {
   ...   
   String customerZip = request.getParameter( "customerZip" );
   ...
}

Für Public-Render-Parameter gelten die gleichen Beschränkungen wie für (konventionelle) Render-Parameter. Der Wert kann nur ein String bzw. ein String-Array sein. Somit sind die Möglichkeiten begrenzt, komplexe Objekte können nicht ohne Weiteres global verfügbar gemacht werden. Genau hier kann jedoch mit Portlet-Events auf ein mächtigeres Konstrukt zugegriffen werden.

Verwendung von Portlet-Events

Zur Demonstration der Portlet-Events wird das obige Beispiel erweitert. Es wird eine umfassende Adresse in einem Portlet eingegeben, die via Event an andere Portlets versendet werden soll. Die Idee hinter Portlet-Events ist, dass ein Portlet bestimmte Events erzeugen und abfeuern kann, auf der anderen Seite können sich Portlets für bestimmte Events registrieren. Es findet somit keine direkte Kommunikation statt (schicke Event A genau zu Portlet B), sondern eine indirekte. Events werden gefeuert und jeder, der sich dafür registriert hat, wird benachrichtigt.

Um dies zu erreichen, wurde der Portlet Lifecycle nochmals erweitert. Es gibt eine eigene Phase, in der Events verarbeitet werden können: processEvent. Die Spezifikation besagt, dass die Phase beendet sein muss, bevor in eine Render-Phase übergegangen werden kann. Um die neue Phase abzubilden, kann ein weiteres Interface javax.portlet.EventPortlet implementiert werden. Erbt das eigene Portlet von GenericPortlet, kann eine Methode processEvent überschrieben werden. In dieser Phase werden empfangende Events verarbeitet. Das Versenden von Events erfolgt beispielsweise in der processAction-Methode.

Listing 4: Versenden von Events
public void processAction(ActionRequest request, ActionResponse response)
   throws PortletException, IOException {
   ...
   response.setEvent( new QName("http://www.jsf-portlets.net/portlet", "adress"), newAdress );
   ...
}

Listing 4 zeigt, wie ein Event adress versendet wird. Der Parameter newAdress ist ein komplexes Adressobjekt, das an den Event angehängt wird und an die interessierten Empfänger-Portlets gesendet werden soll (Listing 5).

Listing 5: Empfangen von Events
public void processEvent(EventRequest request, EventResponse response)
   throws PortletException, IOException {
   ...
   Adress newAdress = (Adress)request.getEvent().getValue();
   ...
}

In der Methode processEvent, die vom GenericPortlet überschrieben werden kann, kann das Adressobjekt aus dem Event wieder ausgelesen werden. Es wäre im Beispiel auch nicht falsch, wenn zunächst mittels request.getEvent().getQName() geprüft wird, ob es sich um den passenden Event handelt. Damit ein komplexes Objekt versendet werden kann, muss ein gültiges JAXB-Binding vorliegen, zudem muss die Klasse das Interface java.io.Serializable implementieren (Listing 6). Bei primitiven Datentypen als Payload des Events ist dies nicht notwendig, bei unserem Adressobjekt dagegen schon.

Listing 6: JAXB-Binding und Serializable
@XmlRootElement
public class AdressBean implements Serializable {
   private String firstname;
   private String lastname;
   private String city;
   ...

Aber auch bei Portlet-Events passiert ein großer Teil in der portlet.xml. Nachdem ein Event definiert wurde, können Portlets als Empfänger oder Sender (oder auch als beides) gekennzeichnet werden.

Listing 7: Definition von Events in der portlet.xml

   ...
   jp:adress
   ...
   jp:adressjp:adressde.jsfportlets.sample.model.AdressBean

Listing 7 zeigt, wie zunächst in einem Portlet ein supported-publishing-event definiert wird (für Portlets, die ein Event versenden), in einem anderen Portlet ein supported-processing-event (für Portlets, die ein Event empfangen). Der Event muss auf Applikationsebene natürlich definiert sein, auch hier wird wieder auf QNames zurückgegriffen.

Wo liegen die Unterschiede

Jetzt haben sie zwei Möglichkeiten kennengelernt, wie Portlets untereinander Daten austauschen bzw. Events verschicken können. Doch wann sollte welche Möglichkeit verwendet werden? Oder anders gefragt: Braucht man beide Techniken oder kann nicht alles über eine Art abgewickelt werden? Kurz beantwortet: Man braucht beide Lösungen, muss im Detail jedoch jeweils abwägen. Public-Render-Parameter einerseits sind eine Möglichkeit, Portlet-übergreifend auf gemeinsame Werte zuzugreifen. Portlets können sich somit synchronisieren. Die Beschränkung des JSR-168, dass über den Applicaton Scope der Session nur Daten innerhalb einer Portlet-Applikation ausgetauscht werden konnten, entfällt. Public-Render-Parameter können über Portlet-Applikationsgrenzen hinweg geteilt werden. Portlet-Events andererseits sind ein sehr mächtiges Konstrukt, speziell auch im Hinblick auf Composite Applications. Anwendungen können in separaten Portlets aufgebaut werden. Durch das neue Eventing-System kann eine Kommunikation untereinander erfolgen. Portlet-Events können zudem komplexe Objekte beinhalten und sind nicht auf einfache Strings beschränkt.

Fazit

Mit den zwei gezeigten neuen Features lässt sich künftig (endlich) standardkonform eine Inter-Portlet-Kommunikation realisieren. Mit Public-Render-Parametern existiert ein recht einfacher Mechanismus, um Portlet-übergreifend Daten zu setzen und auch wieder abzufragen. Portlet-Events stellen zudem eine weitere Möglichkeit dar, auch Events zwischen Portlets verschicken zu können. Das Konzept wurde dabei gleich so angelegt, dass eine Kaskadierung möglich ist. Insgesamt stellen die Erweiterungen somit sinnvolle und lang erwartete Funktionen dar.

Buch-Tipp

Passend zum Thema erscheint im Mai 2009 „Portlets und JavaServer Faces“ von Andy Bosch.

Andy Bosch ist freier Berater und Trainer für die Technologien Portlets und JavaServer Faces. Er ist Mitglied der Expert Group des JSR-301 sowie Betreiber der Plattformen www.jsf-forum.de und www.jsf-portlets.net. Zudem ist er Vorstand von SENS (Software Experts Network Stuttgart, www.SoftwareExperts.de).

Geschrieben von
Andy Bosch
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.