RESTful JavaServer Faces

Bereitstellung von Ressourcen über GET-Requests in JSF 2.0

Die Tatsache, dass es bis einschließlich JavaServer Faces 1.2 nicht möglich war, mit GET-Requests sinnvoll auf eine JSF-View zuzugreifen, machte eine JSF-Applikation zu einem geschlossenen System. Man konnte zwar einen Link auf eine JSF-Seite setzen, allerdings zeigte sie dann immer den initialen Zustand. Spezifische Inhalte waren von außen nicht referenzierbar. In JavaServer Faces 2.0 hat man begonnen, auch die Verwendung von GET-Requests zu ermöglichen. Damit kann jetzt auch ein Link auf eine JSF-View mit einem spezifischen Inhalt gesetzt werden, wenn das von der jeweiligen View explizit unterstützt wird. Um zu beurteilen, was die Unterstützung von GET-Requests aus der REST-Perspektive bedeutet, werden wir im Folgenden diesen neuen Mechanismus nutzen, um die Implementierung einer Ressource zu zeigen, die einer Ressource in einer REST-Architektur ähnelt. Als Beispiel dient eine Schnittstelle, die Benutzerdaten einer nicht näher spezifizierten Applikation zur Verfügung stellt. Der URI http://example.com/faces/app/users.xhtml
soll eine Liste aller Benutzer referenzieren. Eine Übersicht über die Daten eines einzelnen Benutzers erhält man, wenn man durch Anhängen des Query-Parameters userId die Identifikationsnummer des Benutzers spezifiziert. Den Benutzer mit der Nummer 1 könnte man beispielsweise über folgenden URI referenzieren: http://example.com/faces/app/users.xhtml?userId=1. Die Repräsentation dieser Ressource wird über die View users.xhtml bereitgestellt, wie Listing 1 zeigt.

Listing 1

Innerhalb des Tags <dataTable> wird über eine Liste mit Benutzerdaten iteriert, die durch die Backing Bean Users_Backing zur Verfügung gestellt wurde. Es wird eine Tabelle erzeugt, in der jeder angeforderte Benutzer durch eine Zeile repräsentiert wird. Schon in der Render-Response-Phase muss deshalb bekannt sein, welche Benutzerdaten für den Client relevant sind, d. h. es muss eine Verbindung zwischen GET-Request und der Managed Bean Users_Backing hergestellt werden. Das geschieht über das in JSF 2.0 hinzugekommene Tag <f:viewParam>. Das sorgt dafür, dass der Wert des Query-Parameters userId ausgelesen wird und der Eigenschaft userId der Managed Bean Users_Backing zugewiesen wird. Wenn kein entsprechender Query-Parameter übergeben wurde, wird <f:viewParam> ignoriert. In JSF 1.2 wurde bei einem Non-Postback-Request direkt nach dem Erzeugen des Komponentenbaums eine gerenderte View zum Client geschickt. Da ein GET-Request immer eine initiale Anfrage auf eine View ist, würden die Attribute einer referenzierten Managed Bean im bisherigen Verarbeitungsmodell also nicht aktualisiert. Um das für GET-Requests zu ermöglichen, wurde in JSF 2.0 für diese ein abweichender JSF-Lifecyle implementiert.

In dem modifizierten Verfahren wird zunächst die angeforderte View nach einem Metadatenbereich durchsucht. Dieser wird durch den umschließenden Tag <f:metadata> gekennzeichnet und enthält die View-Parameter. Wenn ein Metadatenbereich vorhanden ist, dann wird zunächst ein Komponentenbaum erzeugt, der nur die View-Parameter enthält. Auf dieser Grundlage werden dann alle Phasen des JSF-Lifecycle durchlaufen, d. h. die View-Parameter werden konvertiert, validiert und den entsprechenden Eigenschaften der referenzierten Managed Beans zugeordnet. Erst danach wird ein kompletter Komponentenbaum erzeugt und eine View gerendert. Die erwähnte Backing Bean Users_Backing wird in der Klasse Users implementiert (Listing 2)

Listing 2
@ManagedBean(name="Users_Backing")
@RequestScoped
public class Users {

  private Long userId;

  public void setUserId(Long userId) {
    this.userId = userId;
  }

  public Long getUserId() {
    return userId;
  }

  public List getUserList() {
    List userList = new ArrayList();
    UserManager userManager = new UserManager();
    if (userId != null) {
      User user = userManager.getUserById(userId);
      userList.add(user);
    } else {
      userList = userManager.getUserList();
    }
    return userList;
  }
}

Da für die Ressource genau ein möglicher Query-Parameter vorgesehen ist, verfügt auch die Klasse Users über genau ein entsprechendes Attribut. Über die vorhandenen Getter- und Setter-Methoden kann die Member-Variable userId gelesen und geändert werden kann. Die angeforderten Benutzerdaten werden durch die Methode getUserList zur Verfügung gestellt. Das Verhalten dieser Methode ist abhängig vom Inhalt von userId. Wenn die Member-Variable einen Wert enthält, wird nach einem Benutzer mit dieser Nummer gesucht und im Erfolgsfall eine Liste, die nur diesen Benutzer enthält, zurückgegeben. Wenn userId nicht gesetzt wurde, dann wird eine Liste mit allen Benutzern geliefert. Die Managed Bean wurde im Request Scope angesiedelt. So ist sichergestellt, dass die Member-Variable userId bei jedem Request zunächst leer ist, da immer ein neues Objekt erzeugt wird. Das vorgestellte Beispiel zeigt, dass es mit JSF 2.0 zumindest möglich ist, über GET Ressourcen zur Verfügung zu stellen. Ausgeliefert wird dabei konzeptionell nicht eine Repräsentation einer Ressource, sondern eine View, die Informationen über diese Ressource enthält.

Aber natürlich kann man die erzeugte View parsen und auf diese Weise die angeforderten Informationen extrahieren. Es ist aber nicht möglich, diese Ressource mit vertretbarem Aufwand in beliebigen Repräsentationsformaten anzubieten. Der GET-Request funktioniert, ohne dass auf dem Server Sitzungsinformationen vorliegen müssen, d. h. die Kommunikation kann in diesem Beispiel ohne Probleme statuslos sein. Das ermöglicht, dass URIs an andere Applikation weitergegeben und von ihnen unabhängig von einer spezifischen Session verwendet werden können. Das ist eine rudimentäre Hypermedia-Unterstützung, allerdings kaum im Sinne von REST, wo Hypermedia als sehr zentrales Element der Applikationssteuerung eingesetzt wird. Mit JSF 2.0 kann man also tatsächlich rudimentären Lesezugriff auf Applikationsinhalte in einem REST-Sinn zur Verfügung zu stellen. Da POST von JavaServer Faces auf sehr JSF-spezifische Weise verwendet wird und weitere HTTP-Methoden nicht unterstützt werden, können Ressourcen aber nicht in einem REST-Sinn erzeugt, modifiziert oder gelöscht werden.

Kommentare

Schreibe einen Kommentar

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