Endlich ohne Verzögerungen mit JavaServer Faces programmieren

JSF ohne Warteschleife

Johannes Fiala

Dieser Artikel zeigt Ihnen, wie sie die häufigsten Entwicklungsbremsen abbauen und ohne Unterbrechungen und Wartezeiten arbeiten können. Das Warten auf den Server-/Applikationsneustart während der Frontend-Entwicklung sollte damit der Vergangenheit angehören.

Ganz viel JavaServer Faces gibt es auf der kommenden JAX in Mainz. Die Konferenzwoche beginnt mit einem JSF-2.0-Kickstart (Workshop) und geht weiter mit dem JSF Day am Donnerstag. Und zum Thema Seam bietet sich am Freitag der Workshop „Extending JEE 6 with Seam 3“ an.

Nicht vergessen: Morgen (24.März) endet der Early Bird!

Dieser Artikel identifiziert typische Bremsen bei der Entwicklung mit JSF und Seam und zeigt Ihnen Lösungen dafür auf. Alle gezeigten Features sind mit JSF 1.2 oder 2.0 und JBoss Seam 2.1 oder höher verwendbar. Folgende Themen werden behandelt:

  • Wie Sie Messages in messages.properties einfügen oder ändern, ohne die Applikation neustarten zu müssen (hierfür benötigen Sie JBoss Seam).
  • Wie Sie neue Navigation Cases hinzufügen, ohne die Applikation neu starten zu müssen (mit JSF 1.2/2.0).
  • Wie Sie mit den Eclipse JBoss Tools direkt aus Ihren Facelet-Dateien auf die deklarierten Beans und deren Attribute zugreifen können (hiefür benötigen Sie die Eclipse JBoss Tools).

Für JSF-Einsteiger wird eine Beispielapplikation bereitgestellt, die als Basis für eigene Applikationen genutzt werden kann. Fortgeschrittene JSF-Entwickler können den Quellcode analysieren und bei Bedarf verändern/erweitern.

1. Verwalten von Messages

Wie oft haben Sie schon neue Messages hinzugefügt oder geändert und sich geärgert, dass diese nicht im Frontend angezeigt werden? Standardmäßig werden diese erst nach einem Neustart der Applikation oder des Servers angezeigt. Durch den Einsatz des neuen ResourceBundleControls von Java JDK 1.6 ist es jedoch möglich, dass Sie die Messages in bestimmten Cache-Intervallen neu laden. Mit JBoss Seam können Sie einen Custom Resourceloader definieren, der die Messages während der Entwicklung nach einem frei definierbaren Intervall – z. B. 1 Sekunde – neu lädt. Damit können Sie die Messages ändern und sehen die Änderungen sofort am Bildschirm. Seam ermöglicht es, den Custom Resourceloader nur im Debug-Modus zu aktivieren. Dadurch können Sie den Resourceloader ausschließlich für die lokale Entwicklungsumgebung aktivieren. In der Produktionsumgebung können Sie hingegen den Standard-Resourceloader von Seam nutzen. Die Anregung dazu basiert auf einem Eintrag im Seam-Userforum von Vladimir Kovalyuk. Folgende Schritte sind dazu notwendig:

  • Erstellen einer Seam MessageFactory
  • Erstellen einer Map, die auf ein ResourceBundle Control zugreift
  • Erstellen eines ResourceBundle Controls, das auf die Messages-Datei zugreift
Schritt 1: Erstellen einer Seam MessageFactory

Eine MessageFactory wird wie folgt deklariert:

@Scope(ScopeType.STATELESS)
@BypassInterceptors
@Name("org.jboss.seam.international.messagesFactory")
@Install(precedence = APPLICATION, debug = true)
public class DynamicMessageFactory extends Messages {
 @Override
 protected Map createMap() {
  return new DynamicMessageMap();
 }
}

Durch das Setzen des Flags debug=true wird die MessageFactory nur im Seam-Debug-Modus aktiviert. Der Seam-Debug-Modus wird über die Datei seam.properties aktiviert: org.jboss.seam.core.init.debug=true.

Schritt 2: Erstellen einer Map, die auf ein ResourceBundle Control zugreift

Die DynamicMessageMap liest die Messages über die Methode getHotResourceBundle() aus. Der Zugriff auf das gewünschte ResourceBundle erfolgt in der Klasse FileResourceBundleControl (Listing 1).

public class DynamicMessageMap implements Map {
 public String get(Object key) {
  if (key instanceof String) {
   String resourceKey = (String) key;
   String resource = null;
ResourceBundle bundle = getHotResourceBundle();
   if (bundle != null) {
    try {
     resource = bundle.getString(resourceKey);
    } catch (MissingResourceException mre) {
     log.warn("Resource not found: " + resourceKey);
    }
   }
   return resource == null ? resourceKey : resource;
  } else {
   return null;
  }
 }

 private ResourceBundle getHotResourceBundle () {
  ResourceBundle bundle = ResourceBundle.getBundle("at.fwd.jsf_beispiel.config.messages", org.jboss.seam.core.Locale.instance(), Thread.currentThread().getContextClassLoader(), new FileResourceBundleControl());
  
  return bundle;
 }

 // weitere Methoden zur Implementierung des Interfaces Map.
}

Listing 1

Schritt 3: Erstellen eines ResourceBundle Controls, das auf die Messages-Datei zugreift

Die Klasse FileResourceBundleControl definiert zwei wichtige Einstellungen:

  • Über das Attribut cacheInterval wird definiert, wie häufig das ResourceBundle neu geladen wird
  • Die Methode newBundle wird immer aufgerufen, wenn das CacheInterval abgelaufen ist. In der Methode newBundle wird das ResourceBundle neu von der Festplatte gelesen (Listing 2).
public class FileResourceBundleControl extends ResourceBundle.Control {

 // Cache-Intervall in Millisekunden
 private static int cacheInterval = 1000;
 
 private static String PROPERTIES = "properties";

    public List getFormats(String baseName) {
        return Collections.singletonList(PROPERTIES);
    }

    public long getTimeToLive(String baseName,
            Locale locale) {
     return cacheInterval;
 }
 
 public boolean needsReload(String baseName,
         Locale locale,
         String format,
         ClassLoader loader,
         ResourceBundle bundle,
         long loadTime) {
  return true;
 }
  
    public ResourceBundle newBundle(String baseName, 
            Locale locale, String format, ClassLoader loader, 
            boolean reload)
                throws IllegalAccessException, 
                    InstantiationException, IOException {
// Zugriff auf das FileResourceBundle
}
}

Listing 2

Die drei Klassen sind alle Teil der Beispielapplikation und können von dort direkt in Ihre JSF-/Seam-Applikation übernommen werden. Nach Übernahme der Klassen erkennt Seam beim nächsten Neustart im Debug-Modus, dass die Messages über das FileResourceBundleControl eingelesen werden und lädt diese im Sekundentakt neu.

Geschrieben von
Johannes Fiala
Kommentare

Schreibe einen Kommentar

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