Das Produkt JSF Ext enthält eine ganze Reihe von JSF-Tags. Diese werden auf dieser Seite beschrieben.

Die Tags <e:load>, <e:unload>, <e:scope> und <e:scopes> werden bei JSF Ext Scopes beschrieben.

Inhalt#

Insert Tag#

Der Insert Tag kann Components in den Component Tree einfügen, anhand einer EL-Expression. Dies ist unter anderem nützlich, wenn man Composite Components baut. Also Components mit dem Namespace http://java.sun.com/jsf/composite/...

In der Composite Component gibt es nur den Tag <cc:insertChildren>, damit hat man keine detailierte Kontrolle, die Children werden alle an derselben Stelle eingefügt. Möchte man diese zum Beispiel durch ein SPAN-Tag wrappen, braucht man den Insert Tag.

Lösung: Man iteriert durch die Children mittels <c:forEach> und fügt diese an einer beliebigen Stelle mittel <e:insert> ein.

<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:cc="http://java.sun.com/jsf/composite"
	xmlns:e="http://java.sun.com/jsf/ext"
	xmlns:ext="http://java.sun.com/jsf/composite/ext"
>
	<cc:interface>
		<cc:attribute name="align" default="center"/>
	</cc:interface>
	<cc:implementation>
		<div style="margin-top: 5px; text-align: #{cc.attrs.align};">
			<c:forEach items="#{cc.children}" var="child">
				<h:panelGroup style="padding: 5px 5px 0 0;" rendered="#{child.rendered}">
					<e:insert component="#{child}"/>
				</h:panelGroup>
			</c:forEach>
		</div>
	</cc:implementation>
</html>

JavaScript ausgeben#

Behavior Tag#

Eine Neuerung bei JSF2 ist die Einführung von Behaviors, also das Generieren von Java-Script-Snippets aus Java heraus. Ein Behavior kann jeder Komponente hinzugefügt werden, die ClientBehaviorHolder implementiert.

In vielen Fällen hab man bereits Behavior-Components zu einem Tag hinzugefügt. Würde man dennoch das entsprechende Attribut verwenden (z.B. onclick bei commandButton), kann es zu Problemen kommen, wie die Reihenfolge der Snippets oder das "return false;"-Anhängsel.

Lösung bringt der Behavior-Tag von JSF Ext. Typisch ist zum Beispiel die Verwendung zusammen mit dem AJAX-Tag:

<h:commandButton value="X">
	<f:ajax/>
	<e:unload/>
	<e:behavior script="#{rich:component('popup-panel')}.hide(event);"/>
</h:commandButton>

Die Behavior-Snippets werden in der entsprechenden Reihenfolge produziert:

jsf.util.chain(this,event,'mojarra.ab(this,event,\'action\',0,0)','RichFaces.$(\'popup:form:popup-panel\').hide(event);');return false

Evaluate Tag#

Wenn mit Actions und Java-Script gearbeitet wird, kann eine Ausführung im Erfolgsfall des Form Submit sinnvoll sein. Dies kann durch den Evaluate-Tag erreicht werden, der einen ActionListener darstellt und entsprechendes Script ausführen kann.
<h:commandButton value="Submit">
    <f:ajax/>
    <e:evaluate script="alert('Success');"/>
</h:commandButton>

Zusätzlich ist der Evaluate-Tag ein ClientBahaviorHolder, sodass Behavior-Tags verwendet werden können, um das Script zu erzeugen.

Script Tag#

Der Tag <e:script> gibt Scripte beim AJAX-Rendering aus. Es ist keine Kopplung an Behaviors oder Actions erforderlich. Dadurch können Aktionen beim Rendern von Components aufgeführt werden, zum Beispiel ein Highlighting durch JavaScript:
<e:script script="ext.util.flash('some-tag', '#80ff80');"/>

#

Unterschied zwischen Behavior-, Evaluate- und Script-Tag:

BehaviorEvaluateScript
Zeitpunkt der AusführungDirekt durch den Browser-EventDurch den AJAX-Request an den Browser gesendetDurch das AJAX-Rendering der Component
Bedingungen für die AusführungKeine Server-Bedingungen möglich, nur Java-Script if-StatementsDas Script wird nur nach erfolgreichen Submit ausgeführt, also wenn die Validation erfolgreich warNur beim AJAX-Rendering, nicht beim Aufbau der Seite.
AnwendungszweckSteuerung des Browserverhaltens, Ein- und Ausblenden, Fading, dynamische Browserelemente, Client-BehaviorAbwickeln von Server-States, Popups öffnen und Schließen, Submits bestätigenSpezielle AJAX- und Push-Features, Highlighting, Informationen.

AJAX-Tag#

Der AJAX-Tag von JSF Ext erlaubt das Abschicken eines anderen Formulars. Der Tag <f:ajax> kann nur das aktuelle Formular abschicken, innerhalb der Action-Source in der er sich befindet.

Sources#

Manchmal ist es sinnvoll andere Sources abzuschicken, dafür stellt JSF Ext den Tag <e:ajax> zur Verfügung:
<h:form id="test-form">
	<h:panelGrid columns="3">
		<h:outputText value="Name"/>
		<h:inputText id="name" value="#{bean.name}" required="true"/>
		<h:message for="name"/>
	</h:panelGrid>
</h:form>
<h:form id="other-form">
	<h:commandButton id="tag-save" value="Tag Button">
		<e:ajax source=":test-form" render=":test-form"/>
	</h:commandButton>
</h:form>

Der Tag <e:ajax> ersetzt in diesem Fall den Tag <f:ajax>, er braucht nicht zusätzlich angegeben zu werden.

Action Source#

Der Tag kann auch Actions abschicken, wenn er an einfachen ClientBehaviorHolder gehängt wird, weil er selbst eine ActionSource darstellt:
<h:panelGrid>
    <f:ajax event="dblclick" action="#{bean.action}"/>
</h:panelGrid>

Client-Behaviors#

Der Tag <e:ajax> ist nicht nur ein ClientBehavior sonder auch selbst wieder ein ClientBehaviorHolder. Er unterstützt die vier AJAX-Events begin, complete, success und error, wobei das Default-Event success ist:
<h:graphicImage id="edit" value="edit.png">
	<e:ajax event="click">
            <f:setPropertyActionListener value="#{element}" target="#{editController.element}"/>
            <rich:componentControl target=":some-popup" operation="show"/>
	</e:ajax>
</h:commandButton>

DIV-Tag#

In JSF gibt es eine Reihe von Möglichkeiten, einen DIV- oder SPAN-Tag zu erzeugen, zum Beispiel durch <h:panelGroup>. Dabei handelt es sich jedoch um einfache Komponenten, die keine ClientBehaviorHolder sind, und daher kein <f:ajax>, <e:bahavior> und andere Behaviors unterstützen.

Der Tag <e:div> ist ein vollwertiger ClientBehaviorHolder:

	<e:div id="mouse-active" tabindex="0" onkeypress="alert((event || window.event).keyCode);">
		<ext:mouse-focus id="mouse-focus"/>
		<h1>TEST</h1>
	</e:div>

Damit wird das Erstellen von einfachen Komponenten mit wenig HTML-Code und flachen Komponenten-Bäumen deutlich vereinfacht.

Attribute-Tag#

Bei manchen Tags fehlen Attribute, wie zum Beispiel placeholder oder oncontextmenu. JSF-Tags reichen die Attribute nicht durch, daher besteht keine andere Möglichkeit diese Attribute hinzuzufügen. In den meisten Fällen können solche Attribute durch das Attribute-Tag trotzdem hinzugefügt werden:
    <h:inputText id="name" value="#{bean.name}">
        <e:attribute name="placeholder" value="Name eingeben..."/>
    </h:inputText>

Des Weiteren unterstützt der Attribut-Tag folgende Attribute:

NameBeschreibung
finalizerZeichen für den Abschluss eines Behaviors. Default ist, dieses Zeichen aufgrund einer Reihe von Heuristiken zu erraten. Dazu zählen Behaviors und Attribut-Namen wie z.B. "style" mit den Finalizern ";" und "". Jeder untergeordnete Behavior-, Part- und anderer Tag wird mit diesem Zeichen abgeschlossen.
delimiterZeichen für die Abtrennung untergeordneter Behavior-Tags usw. Im Gegensatz zum Finalizer wird dieses Zeichen nur zwischen den Behaviors eingefügt, nicht jedoch am Ende. Default ist ein einzelnes Whitespace.

Hinweis: Durch dieses Feature können Strings bequem zusammengesetzt werden, die mit Attributen "rendered" versehen sind. Untergeordnete Behaviors, wie das Part-Tag, können die Werte nochmal für einzelne Strings überschreiben.

Attribute-Behaviors#

Der Attribute-Tag ist ein ClientBehaviorHolder, es können also Behaviors darunter gehängt werden wie <e:behavior> oder sogar <e:ajax>. Dadurch ist es möglich, Behaviors für Attribute zu festzulegen, die eigentlich nicht dafür gedacht sind:
<h:panelGroup>
    <e:attribute name="oncontextmenu">
        <f:ajax listener="#{bean.render}" render=":some-menu"/>
    </e:attribute>
</h:panelGroup>

Zusammengesetzte Attribute#

Gelegentlich möchte man Attribute zusammen setzen, zum Beispiel um Styles in Composite-Tags zu rendern. Dies kann durch Kombination eines Attribute-Tags und mehreren Behavior-Tags erreicht werden:
<e:div id="#{cc.id}" styleClass="button-bar">
	<e:attribute name="style" renderEmpty="false">
		<e:behavior script="text-align: #{cc.attrs.align};" rendered="#{!empty cc.attrs.align}"/>
		<e:behavior script="background-color: #{cc.attrs.color};" rendered="#{!empty cc.attrs.color}"/>
	</e:attribute>
	...
</e:div>

Part-Attribute#

Zusammengesetzt Attribute können bequem durch <e:part> erzeugt werden:
<h:commandButton>
	<e:attribute name="value">
		<e:part value="This"/>
		<e:part value="is"/>
		<e:part value="a"/>
		<e:part value="composed"/>
		<e:part value="value"/>
	</e:attribute>
</h:commandButton>

Hinweis: Der Part-Tag kann hilfreich sein, wenn bedingte styleClass-Attribute auftreten. Entsprechende Teile können mit dem rendered-Attribut gesteuert werden, anstatt unübersichtliche EL-Expressions zu konstruieren.

Style-Attribute#

Da Style-Attribute besonders häufig auftreten und zusammengesetzt werden, gibt es dafür einen eigenen Tag:
<e:div>
    <e:attribute name="style">
	<e:style name="text-align" value="#{valueEdit.permission.field.type.align}"/>
	<e:style name="width" value="100%"/>
	<e:style name="margin-left" value="10px"/>
    </e:attribute>
</e:div>

Reference-Tag#

Achtung: Der Reference-Tag wird nicht mehr unterstützt.

Begründung: Der Reference-Tag wird seit längerem durch Scopes ersetzt. Diese sind wesentlich besser in JSF integriert, brauchen weniger Ressourcen und sind flexibler in der Anwendung.

New-Tag#

Der Tag <e:new> kann an jeder Stelle verwendet werden, an der auch <f:param> verwendet wird:
<e:load scopeId="test-scope">
    <e:new name="bean" type="com.intersult.test.Bean"/>
</e:load>

Es ist auch möglich, Properties der neuen Bean Werte zuzuweisen:

<e:new name="bean" type="com.intersult.test.Bean">
    <f:param name="param1" value="#{some-expression}/>
</e:new>

Hinweis: Es ist zu erwähnen, dass das Instantiieren von Beans durch den New-Tag möglicher Weise nicht die beste Praxis ist. Die erste Wahl beim Erzeugen von Beans sollte das Verwenden von Scope- und Factory-Annotations sein, entweder über JSF mit @ManagedBean oder über Spring mit @Component. An zweiter Wahl steht das Erzeugen im Java-Code, zum Beispiel mit Lazy-Initialization Pattern "if (bean == null) bean = new Bean();". <e:new> macht vor allem da Sinn, wo eine neue leere Bean erzeugt werden soll (z.B. Create Popup) und wo mehrere Parameter durch eine Bean übergeben werden sollen.

Set-Tag#

Eine gängige Praxis ist das Setzen von Java-Properties mittels <f:setPropertyActionListener>. Bei Composite Components können ebenfalls als ActionSource agieren:
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:e="http://java.sun.com/jsf/ext"
	xmlns:c="http://java.sun.com/jsp/jstl/core"
	xmlns:cc="http://java.sun.com/jsf/composite"
>
	<cc:interface>
		<cc:attribute name="label"/>
		<cc:attribute name="action" targets="link" method-signature="void action()"/>
		<cc:actionSource name="action" targets="link" default="true"/>
	</cc:interface>
	<cc:implementation>
		<h:commandLink id="link"
		    <h:outputText value="#{cc.attrs.label}"/>
		</h:commandLink>
	</cc:implementation>
</html>

Dieser Tag kann wie folgt verwendet werden:

<app:myLink label="Some Link">
    <e:set value="Some Value" target="#{bean.value}"/>
</app:myLink>

Beim Verwenden von <f:setPropertyActionListener> müsste man explizit mit einem Attribut for="action" die Action spezifizieren. Der Tag <e:set> macht dies automatisch, bei häufigem Verwenden sieht der kurze Name einfach ausdrucksvoller und aufgeräumter aus.

Label#

JSF Ext liefert einen Tag <e:outputLabel> mit, der auf Required-Attribute mit einem unterschiedlichen Style reagiert. Zusätzlich berücksichtigt werden die Annotations @NotNull und @NotEmpty auf den durch EL-Expressions gebundenen Bean-Klassen.

Die Verwendung ist identisch mit <h:outputLabel>:

<e:outputLabel for="username:username" value="#{messages['user.username']}"/>
<ext:input id="username">
	<h:inputText id="username" value="#{userRegister.user.username}"/>
</ext:input>

Dabei kommen die beiden CSS-Style-Klassen label-required und label-not-required zum Einsatz, die mit "font-weight: bold;" und "color: #a0a0a0;" vorbelegt sind. Diese können durch ein Benutzer-Stylesheet überschrieben werden.

Der <e:outputLabel> unterstützt das Attribut disabled, mit dem der Label als optional angezeigt werden kann, unabhängig von der tatsächlichen Required-Eigenschaft. Dies ist nützlich, wenn die zugehörige Input-Component ebenfalls Disabled wird.

Move-Listener#

Auf modernen Web-Seiten wird mehr und mehr mit Popups und Elementen gearbeitet, die mit der Maus verschoben werden können. Der Move-Listener ist ein Event-Generator, mit dem solche Eingriffe verarbeitet werden können:
<e:moveListener listener="#{dialog.moveListener}"/>

JSF Ext enthält eine Custom-Scoped Bean "dialog", in der das Ergebnis gespeichert werden kann. Im obigen Beispiel geschieht dies durch listener="#{dialog.moveListener}". Ein entsprechendes Popup kann dann durch position="#{dialog.position}" wieder positioniert werden.

Um einem Primefaces-Dialog einen Listener hinzuzufügen, verwendet man:

<e:moveListener listener="#{dialog.moveListener}"
	targetNode="document.getElementById('#{cc.clientId}:dialog').childNodes[0]"
	height="element.childNodes[1].offsetHeight - 16"
	width="element.childNodes[1].offsetWidth"/>

Init-Tag#

In einigen Situationen ist es hilfreich, wenn eine Bean an einer bestimmten Stelle im Component-Tree initialisiert werden kann. So kann beispielsweise ein HTTP-GET-Parameter verarbeitet werden oder ein Scope initialisiert. Dazu gibt es den Tag <e:init>:
<e:init action="#{testBean.init}"/>

Hinweis: Die Action-Methode kann mehrfach aufgerufen werden, wenn Redirects durchgeführt werden.

Der Tag kann innerhalb eines Scopes verwendet werden, um den Scope zu initialisieren, falls kein Laden mit <e:load> erfolgt ist.

<e:scope id="test" load="true">
    <e:init action="#{testBean.init}"/>
    ...
</e:scope>
public class TestBean {
    @ScopeValue
    private String test;
    
    public void init() {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        String test = externalContext.getRequestParameterMap().get("test");
        if (test != null)
            this.test = test;
    }
    ...
}

Hinweis: Der Load-Tag ist dabei weiterhin möglich.

Der Init-Tag ist eine ActionSource und kann damit auch ActionListener als Unterobjekte haben:

<e:init>
    <e:set value="#{value}" target="#{target}"/>
</e:init>
    

Otherwise-Tag#

Da ist mit dem <c:choose>, <c:when> und <c:otherwise> Konstrukt in JSF Probleme gibt, enthält JSF Ext eine neue Implementierung von Otherweise als Component <e:otherwise>. Diese Component ist einfacher in der Nutzung:
  • Als Basis der Choose-Liste kann eine beliebige Component verwendet werden
  • Child-Components werden mit rendered-Attributen versehen
  • Der Otherwise-Tag wird hinzugefügt und rendered dann, wenn keine der anderen Components gerendert wurde

Das Ganze sieht dann so aus:

<e:div>
    <h:selectBooleanCheckbox id="boolean" value="#{bean.value}" rendered="#{bean.type == 'boolean'}"/>
    ...
    <e:otherwise>
        <h:inputText id="text" value="#{bean.value}"/>
    </e:otherwise>
</e:div>

Render-Tag#

Der Tag <e:render> ist ein nützlicher Tag für dynamische Web-Seiten, in denen Teile automatisch refreshed werden. Bisher gibt man im Ajax-Tag eine Liste von Elementen an, die neu gerendered werden sollen.

Die alte Implementierung über Render-Attribute führt zu folgenden Verbesserungswünschen:

  • Dynamik: Zum Zeitpunkt des Ausführens der Render-Anweisung ist unklar, welche Bereiche überhaupt neu gerendered werden brauchen. Die Zielbereiche können über das Render-Attribut, Scopes und andere Mechanismen dynamisch ein- und ausgeblendet werden. Man möchte die Veränderung einer Struktur melden können, unabhängig von den zur Laufzeit tatsächlich abhängigen Elementen.
  • Weiterentwicklung: Nach Schreiben der Render-Anweisung wird die Anwendung weiterentwickelt. Zielbereiche zum Rendern können hinzukommen oder wegfallen, dies führt zu permanenter Pflege einer wachsenden Zahl von Render-Attributen in der Anwendung. Gewünscht ist, dass fertiger Code nicht permanent wieder angefasst werden braucht.
  • Modularisierung: Bei Modulen, die durch JAR-Dateien ins Projekt kommen, kann auf die XHTML-Seiten kein Einfluss genommen werden. Dort soll ebenfalls ein Rendering möglich sein.

Die Lösung sind Events und der Render-Tag. Typischer Weise werden Events durch den Code ausgelöst, wie etwa beim Login:

Event.instance().raise(EVENT_LOGIN, authentication.getPrincipal());

Dadurch werden Bereiche neu gerendered:

<h:form id="process-form" enctype="multipart/form-data" styleClass="ui-widget" style="margin-left: 10px;">
	<e:render event="intersult.subflow.Authenticator.login"/>
	<e:render event="intersult.subflow.Process.change"/>
	<e:render event="intersult.subflow.Process.select"/>
	
	<e:div rendered="#{!empty processDetails.process}">
		...
	</e:div>
</form>

Hinweis: Es können nur Bereiche neu gerendered werden, die bereits gerendered wurden. Dadurch können Render-Tags dadurch problemlos in Scopes und anderen dynamischen Berechen verwendet werden. Es ist völlig verträglich, wenn ein Abschnitt mit einem Render-Tag selbst nicht gerendered wurde, dieser wird dann einfach ignoriert.

Tipp: Soll ein Bereich durch ein Render-Tag ein- und ausgeblendet werden, platziert man den Render-Tag in die übergeordnete Component. So wird die Region auf jeden Fall gerendered, unabhängig vom Render-Zustand (Render-Attribut, <c:if> etc.) der innen liegenden Sektion.

Async-Tag#

Manchmal kann es von Vorteil sein, wenn der wichtigste Teil einer Seite sofort zur Verfügung steht. Andere Teile können dann nachgeladen werden. Dies trifft insbesondere zu, wenn eine langsame Verbindung besteht oder Teile der Seite mehr Zeit auf dem Server in Anspruch nehmen, etwa bei komplexen Datenbankanfragen oder Berechnungen.

Hier kann der Tag <e:async> verwendet werden, dieser lädt seinen Inhalt erst nach dem Rendern der Seite per AJAX nach:

	<script type="text/javascript">
		jsf.ajaxQueue = 10;
	</script>
	<c:forEach begin="1" end="20" var="index">
		<e:div style="height: 20px; background-color: #e0e0e0; margin-bottom: 3px;">
			<e:async>
				<h:outputText value="Async Content #{index}, sleep = #{async.sleep}"/>
			</e:async>
		</e:div>
	</c:forEach>

Und eine Klasse, die den Request verzögert:

@Component
@Scope(WebApplicationContext.SCOPE_REQUEST)
public class Async {
	private static Random random = new Random();
	
	public int getSleep() throws InterruptedException {
		int sleep = random.nextInt(3000);
		Thread.sleep(sleep);
		return sleep;
	}
}

Der Async-Tag unterstützt weiterhin die Attribute styleClass, style und das Facet "preview", mit dem ein Vorab-Content angezeigt werden kann:

    <e:async>
        <f:facet name="preview">
            <h:outputText value="Loading content..."/>
        </f:facet>
    </e:async>

For-Tag#

Der Tag <e:for> ist ein Schleifen-Tag mit erweiterten Möglichkeiten. Er kombiniert die Naming-Container Eigenschaft von <ui:repeat> mit der Flexibilität von <c:forEach> und ist darüber hinaus noch angenehm kurz.

Folgender XHTML-Code erzeugt eine Reihe von Knöpfen:

	<h:form id="form">
		<e:for id="for" value="#{table.list}" var="element">
			<h:commandButton id="button" value="#{element.text}" action="#{element.action}">
				<f:ajax/>
			</h:commandButton>
		</e:for>
	</h:form>

Das Attribut value kann dabei durch eine Collection, Map, Array, einzelnes Element oder einen Null-Wert versorgt werden.

Mit dem Attribut test können Elemente aus der Schleife gefiltert werden, entsprechend dem Syntax von <c:if test="...">.

Method Parameters#

Für Zwecke wie dem Laden eines Scopes mit <e:load> können Parameter mit <f:param> übergeben. Dies unterstützt allerdings nur Value-Expressions und keine Method-Expressions. Mit dem Tag <e:method-param> können auch Methoden übergeben werden. Dies geht allerdings nur, wenn EL 2.0 unterstützt wird.
    <e:load viewId="/dialogs/someDialog.xhtml">
        <e:method-param name="action" value="#{test.action}"/>
    </e:load>

Damit können parameterisierte Dialoge erstellt werden, deren Action-Elemente bestimmte Methoden ausführen.

Achtung: Method-Parameters können nur mit EL-API 2.0 verwendet werden. Dies bedeutet auch, dass sie nicht mit einer EL-Implementierung 2.0 verwendet werden können, auch wenn diese auf EL-API 1.0 basiert, wie die JBoss-Implementierung.

Action Component#

Manchmal kann es erforderlich sein, eine Method-Expression als Component zu übergeben oder mehrere Method-Expressions aufzurufen. Dafür kann der Tag <e:action> verwendet werden:
<h:commandButton value="Invoke Actions">
    <f:ajax/>
    <e:action listener="#{bean.action1}"/>
    <e:action listener="#{bean.action2}"/>
    <e:action listener="#{bean.action3}"/>
</h:commandButton>

AJAX Push#

Mit sogenannten Push können Informationen aktiv an den Browser übertragen werden. Vor allem ist es dadurch möglich, eine Vielzahl von Benutzern gleichzeitig über ein Ereignis zu informieren.

Der Tag <e:socket> nutzt Web-Sockets um Browser-Push durchzuführen. Dies ist eine sehr effiziente Implementierung mit geringer Verzögerung zwischen dem Versenden der Nachricht und dem Eintreffen im Browser, sowie Netzwerk und Ressourcen schonend. Dies ist möglich, wenn der Web-Server den Web-Standard 3.0 beherrscht (z.B. ab Tomcat 7). Sind Web-Sockets nicht verfügbar aufgrund eines Web-Servers kleiner als 3.0, weil der Browser dies nicht beherrscht oder ein dazwischen liegender Proxy es nicht unterstützt, wird die Comet-Engine verwendet, die Long-Polling durchführt.

Dazu ist folgende Dependency zum Projekt hinzuzufügen:

<dependency>
	<groupId>org.atmosphere</groupId>
	<artifactId>atmosphere-runtime</artifactId>
	<version>2.1.0-beta1</version>
</dependency>

Push-Tag#

Der Push-Tag ist die einfachste Art, AJAX-Push zu implementieren. Der Tag <e:push> stellt eine transparente Verbindung zu einem Push-Channel her. Der Inhalt des Tags wird auf alle Clients verteilt, die mit dem gleichen Channel verbunden sind:
	<h:form id="form">
		<h:commandButton value="Senden" action="#{pushTest.action}">
			<f:ajax render=":test:value"/>
		</h:commandButton>
	</h:form>
		
	<e:push id="test" direct="true">
		<h:panelGrid id="panel" columns="2">
			<h:outputText id="text" value="Test"/>
			<h:outputText id="value" value="#{pushTest.count}"/>
		</h:panelGrid>
	</e:push>

Erklärung: Der Tag ":test:value" wird durch eine einfache AJAX-Render-Anweisung auf allen verbundenen Clients neu gerendert.

Direct- und AJAX-Mode#

Der Push-Tag kann in zwei unterschiedlichen Modi betrieben werden: Direct und AJAX:

EigenschaftDirect ModeAJAX Mode
RenderingInhalt wird nur bei dem Client gerendert, der den Event ausgelöst hatInhalt wird für jeden Client gerendert
EffizienzEffizienter Modus, da das HTML-Update direkt an alle Clients geschickt werden kannRendering wird für jeden Client getrennt gerendert
InhaltInhalt ist für jeden Client identischInhalt kann für jeden Client individualisiert werden
DatenAnzeige von Daten die für mindestens eine Gruppe bestimmt oder öffentlich istAnzeige privater Daten möglich
AuslöserAuslöser ist ein Faces-EventAuslöser kann ein Faces-Event, Timer oder beliebiger anderer Event sein

Rendering#

Die beim Render-Attribut angegebenen Ids werden einzeln gerendert und nur der relevante Inhalt zum Client übertragen. Auch wenn sich innerhalb des Push-Tag große Mengen an Tags befinden, wird nur die relevante Änderung übertragen, sofern die Render-Anweisungen entsprechend ausgelegt sind.

Damit ist es beispielsweise möglich, eine Tabelle zu erzeugen und einzelne Zellen per Push upzudaten.

Channel#

Wird der Channel-Attribut nicht angegeben, wird die Client-Id der Component als Channel benutzt.

Warnung: Channel-Name und Push-Tag sollte für alle Clients übereinstimmen, damit die Inhalte übereinstimmen. Andernfalls kommt es zu sogenannten Cross-Talking - der Inhalt des Push-Tags ändert sich.

Socket-Tag#

Mit dem Socket-Tag ist eine fortgeschrittenere Integration von AJAX-Push in die Applikation möglich. Dazu wird einfach ein sogenannter Socket in die XHTML-Seite gesetzt:
<e:socket channel="/test">
	<f:ajax render=":push-form:chat"/>
</e:socket>

Auf der Java-Seite wird der Socket dann so angesprochen:

	PushContext.instance().push("/test");

Hinweis: In vielen Fällen reicht die reine Benachrichtigung des Client aus, weil dieser dann per AJAX-Call eine Server-Anfrage startet. Dies bietet den Vorteil, dass die Session und der FacesContext des entsprechenden Benutzers verfügbar ist.

Die Socket-Component unterstützt folgende Attribute und Behaviors:

AttributBeschreibung
channelDas Attribut wird benötigt und beschreibt den Kommunikationskanal zum Server. Indem der Server diesen Channel informiert, wird im Browser eine Nachricht ausgeliefert. Die Besonderheit am Channel ist, dass dieser von beliebig vielen Browsern d.h. eingeloggten Benutzern abonniert werden kann. Der Server braucht daher nicht zu wissen, wieviele Clients überhaupt die Nachricht empfangen werden.
onopenDer Event wird beim Öffnen des Channels ausgelöst. Der Client weiß so immer, ob er verbunden ist oder nicht. Dadurch kann dem Nutzer auch ein Verbindungsstatus angezeigt werden.
oncloseDas Gegenstück zu onopen, wird aufgerufen, wenn die Verbindung abbricht. Im normalen Produktivbetrieb wird diese Methode nur in Ausnahmefällen aufgerufen.
onmessageDies ist der eigentliche Push-Aufruf. Wenn am Server PushContext.push() aufgerufen wird, wird am Client dieses JavaScript-Behavior ausgelöst.
onerrorHier kann eine Fehlerbehandlung hinzugefügt werden, zum Beispiel dem Benutzer angezeigt werden, wenn ein Problem mit der Verbindung auftaucht.

Push-Context#

Auf der Server-Seite gibt es die Klasse com.intersult.jsf.push.PushContext. Um eine Nachricht zu einem Tag <e:socket channel="/test"> zu schicken, wird hier ein entsprechender Methodenaufruf durchgeführt:
    PushContext.instance().push("/test");

Push-Parameter#

Optional kann der Aufruf auch mit einem Parameter erfolgen, der wird dann JSON-Serialisiert und an den Client übertragen:
    PushContext.instance().push("/test", parameter);

Auf der Client-Seite kann der Parameter dann wie folgt entgegen genommen werden:

<e:socket channel="/test" onmessage="alert(response.responseBody);">

Load Balancer und Reverse Proxy#

Wird ein Load-Balancer verwendet, sind die Web-Sockets zu beachten. Bei Apache werden folgende Proxy-Einträge vor der eigentlichen Applikation gebraucht:
	ProxyPass /<context-path>/faces/javax.faces.resource/<channel> ws://<ziel>/<context-path>/faces/javax.faces.resource/<channel>
	ProxyPassReverse /<context-path>/faces/javax.faces.resource/<channel> ws://<ziel>/<context-path>/faces/javax.faces.resource/<channel>

Erklärung: Die Web-Socket-Protokolle WS werden vor den eigentlichen Proxy-Einträgen für die Application abgefangen und mit dem WS-Protokoll umgeleitet. In Apache 2.4 steht dafür das Modul mod_proxy_wstunnel.so zur Verfügung.

Resource-Tag#

JSF-Resources können mit <h:outputStylesheet> und <h:outputScript> ausgegeben werden, mit <h:graphicImage> als Image angezeigt. Einige weitere Tags unterstützen die Attribute "name" und "library". Darüber hinaus gibt es die Möglichkeit, Resource-URLs mit
#{resource['<library>:<name>']}

zu erzeugen oder ganz zu Fuß mit

#{request.contextPath}/faces/javax.faces.resource/<name>?ln=<library>

Möchte man einen Link erzeugen vom Schema <a href="...">, bietet JSF Ext eine schönere Lösung:

<e:resourceLink name="<name>" library="<library">
    <h:outputText value="Some link"/>
</e:resourceLink>

Hinweis: Wie der normale <h:outputLink> unterstützt <h:resourceLink> ebenfalls Parameter vom Typ <f:param> (und abgeleitete Parameter wie <f:method-param> und <f:new>).

Composite Tags#

JSF Ext enthält auch bereits einige Composite Tags, diese sind unter dem Namespace xmlns:ext="http://java.sun.com/jsf/composite/ext" verwendbar.

Busy Pointer#

Die Integration von AJAX in die Anwendung findet vom Browser zunächst transparent statt. Der Benutzer hat keinen Hinweis, dass gerade eine Server-Aktion erfolgt. Der Vorteil ist, dass der Benutzer mit dem Browser normal weiter arbeiten kann. In vielen Fällen ist dies auch möglich, da Operationen unabhängig voneinander ausgeführt werden können. Der Nachteil ist, dass der Benutzer keinen Hinweis erhält, dass die beabsichtigte Operation angelaufen ist.

In den Anwendungen werden daher unterschiedlichste Elemente in die Seite eingebaut, von rotierenden, blinkenden durch durchlaufenden Bildern bis zu Seiten abdunkelnden und sperrenden Elementen. Das übliche Verfahren von Anwendungen die Verarbeitung einer Operation zu zeigen, ist den Mauszeiger als Busy Pointer darzustellen.

Also wieso nicht auch im Browser bei JSF-Anwendungen den Busy Pointer aktivieren. Der Benutzer wird nicht abgelenkt durch zappelnde Bilder, es wird nichts gesperrt, da die Seite ja weiterhin funktional ist:

<ext:busyPointer/>

Optional kann das Attribut cursor angeben werde, das per default auf "wait" gesetzt ist.

Mouse Visibility#

Auf Seiten die sehr viele Elemente enthalten kann es die Übersicht verbessern, wenn Elemente erst bei Mouse-Over angezeigt werden. Durch reine CSS-Styles ist das meist nicht zu realisieren, weil das Mouse-Over-Element ein Parent-Element des anzuzeigenden Elements ist. Lösungen mit A-Tags, die man häufig findet, führen zu Nebenwirkungen wie Anchor-Links und ungewollten GET-Requests.

Eine Lösung ist der Tag <ext:mouse-visibility>:

<e:div>
	<h:outputText id="project" value="#{processDetails.process.project.name}"/>
	<ext:mouse-visibility for=":process-form:image"/>
	<h:graphicImage id="image" name="edit.gif" library="images/bitcons">
		<e:ajax event="click">
			<e:load scopeId=":projectEdit">
				<f:param name="project" value="#{project}"/>
			</e:load>
		</e:ajax>
	</h:graphicImage>
</e:div>

Darüber hinaus gibt es noch die Tags <ext:mouse-display> mit welcher der Display-Style eines Elements gesteuert werden kann. Besonders schick ist auch der <ext:mouse-fade>, mit der das Element ein- und ausgefadet wird.