Event Handling
Überblick
Für den Austausch von Informationen zwischen lose gekoppelten Komponenten bietet jadice web toolkit einen eventbasierten Standardmechanismus. Dieser wird zum einen von diversen produkteigenen Komponenten verwendet, kann aber zum anderen auch out-of-the-box aus Integrationskomponenten heraus genutzt - und bei Bedarf auch erweitert - werden.
Das jadice web toolkit stellt für den Eventversand den JadiceEventBus und den NotificationEventBus bereit. Das über diese Busse laufende Event Handling wird in diesem Kapitel genauer erklärt.
Der JadiceEventBus
Der JadiceEventBus stellt eine wichtige Kernkomponente des jadice web toolkit dar. Hierüber laufen beispielsweise Events, welche über die Veränderung des sichtbaren Bereiches, den Wechsel des aktuellen Dokumentes, etc. informieren. Im folgenden Codebeispiel wird am Beispiel des LoadingEvents gezeigt, wie ein neuer EventHandler in den JadiceEventBus eingehängt werden kann:
JadiceEventBus.get().addHandler(LoadingEvent.getType(), new LoadingEvent.Handler(){
@Override
public void onChange(LoadingEvent event) {
...
}
});
Welche Events es gibt und über was diese genau informieren, wird im Folgenden erklärt.
-
Dieses Event informiert darüber, dass sich das aktuelle Dokument der PageView geändert hat. Die Source des Events ist i.d.R. die betroffene PageView. Des Weiteren wird das neue Dokument und das zuvor geöffnete Dokument übermittelt.
-
Dieses Event informiert darüber, ob gerade etwas geladen wird und wie der Zustand des Ladevorgangs ist. Standardmäßig wird es beim Kachelladen versendet, kann jedoch auch integrationsseitig z.B. beim Dokumentladen versendet werden. Es eignet sich sehr gut, um dem User Feedback über den aktuellen Zustand der Anwendung zu liefern. Das Event stellt mehrere Zustände des Ladevorgangs zur Verfügung: LOADING, FINISHED, CANCELED, FAILED. Neben dem Zustand kann das betroffene Objekt übermittelt werden, im Falle des Kachelladens wird hier das zu ladende Tile mitgegeben. Im Falle einer Fehlermeldung kann noch eine Fehlermessage mitgeliefert werden. Da der ErrorHandler des img-Tags im Falle der HTTP-Request-Kacheln keine Fehlerursache liefert, wird hier die vermutliche Fehlerursache ermittelt und übergeben. Die hierfür verwendete JavaScript-Funktion wird jedoch von Internet Explorer 11 nicht unterstützt und somit wird hier keine Fehlerursache übermittelt. In allen anderen Browsern wird der Fehlerstatuscode bzw. im Falle einer fehlenden Serververbindung die Meldung "no server connection" als Fehlerursache mitgegeben.
-
Dieses Event informiert darüber, dass sich das aktuelle Layout der PageView geändert hat. Die Source des Events ist i.d.R. die betroffene PageView. Des Weiteren wird das neue und alte PageLayout mitgeliefert.
-
Dieses Event informiert darüber, dass sich die aktuelle Seite der PageView geändert hat. Die Source des Events ist i.d.R. die betroffene PageView. Des Weiteren werden die Seitenindexe der alten und neuen aktuellen Seite, sowie die alte und neue aktuelle Seite selbst übermittelt.
-
Dieses Event informiert über Änderungen rund um Tools. Die möglichen Gründe für den Versand eines solchen Events sind "ACTIVATED" (Aktivschaltung eines Tools), "VALUE_CHANGED" (Änderung eines Wertes in einem Tool), "EXCLUSIVE" (ein Tool wird exclusive gesetzt), "ENABLED" (ein Tool wird enabled) oder "REGISTERED" ((De)Registrierung eines neuen Tools). Neben dem Grund wird das betroffene Tool und der neue und alte Wert übermittelt.
-
Dieses Event informiert darüber, dass sich der sichtbare Bereich der PageView geändert hat. Die Source des Events ist i.d.R. die betroffene PageView. Des Weiteren wird der neue und der alte sichtbare Bereich übermittelt.
-
Dieses Event informiert über Änderungen an den RenderSettings. Hierbei werden die betroffenen RenderControls, die RenderSettings, der Name des Wertes, der alte und der neue Wert und die Source (i.d.R. die betroffene PageView) übermittelt.
Nutzung eigener Events auf dem JadiceEventBus
Im folgenden Abschnitt wird anhand von Codebeispielen zum bestehenden LoadingEvent demonstriert, wie eigene Eventtypen eingeführt und genutzt werden können.
Event und EventHandler
Die Events, welche über den JadiceEventBus versendet werden, leiten alle von der abstrakten Klasse JadiceEvent ab. Die EventHandler extenden das Interface JadiceEventHandler. Die Handler werden hier üblicherweise als internes Interface in der Eventklasse bereitgestellt.
public final class LoadingEvent extends JadiceEvent<LoadingEvent.Handler> {
public enum LoadingState {
LOADING, FINISHED, CANCELED, FAILED;
}
public interface Handler extends JadiceEventHandler {
void onChange(LoadingEvent event);
}
private static Type<Handler> TYPE;
private LoadingState loadingState;
private Object loadingObject;
private String errorMessage;
public static Type<Handler> getType() {
return TYPE != null ? TYPE : (TYPE = new Type<Handler>());
}
public static <T> void fire(HasHandlers source, LoadingState loadingState, Object loadingObject,
String errorMessage) {
if (TYPE != null) {
LoadingEvent event = new LoadingEvent(source, loadingState, loadingObject, errorMessage);
source.fireEvent(event);
}
}
public LoadingEvent(HasHandlers source, LoadingState loadingState, Object loadingObject, String errorMessage) {
super(source);
this.loadingState = loadingState;
this.loadingObject = loadingObject;
this.errorMessage = errorMessage;
}
public LoadingState getLoadingState() {
return loadingState;
}
public Object getLoadingObject() {
return loadingObject;
}
public String getErrorMessage() {
return errorMessage;
}
@Override
protected void dispatch(Handler handler) {
handler.onChange(this);
}
@Override
public com.google.gwt.event.shared.GwtEvent.Type<Handler> getAssociatedType() {
return TYPE;
}
}
Eventversand über den JadiceEventBus
Events können über den JadiceEventBus auf zwei Arten versendet Werden. Möglichkeit eins ist, eine neue Instanz des Events zu erzeugen und diese direkt über den JadiceEventBus zu versenden:
JadiceEventBus.get().fireEvent(new LoadingEvent(source, LoadingState.FINISHED, object, null));
Die zweite Möglichkeit besteht darin, das Event über die Methode fire
des Events selbst zu versenden. Hier muss jedoch die übergebene Source
das Event an den JadiceEventBus weiter versenden. Dies ist z.B. bei der
PageView der Fall.
LoadingEvent.fire(source, LoadingState.FINISHED, object, null);
Der NotificationEventBus
Im Unterschied zum JadiceEventBus werden die über den NotificationEventBus verschickten Events dazu verwendet, um dem Benutzer verständliche, nicht-technische Informationen über den Zustand der Anwendung zu geben und damit die Usability zu erhöhen. Die zugehörigen Events teilen sich in ErrorNotificationEvents, InfoNotificationEvent, SearchStatusChangedEvents und SearchResultsEvents auf. In den Demoanwendungen werden diese unter anderem dafür verwendet, dem Benutzer Rückmeldung über den Ladezustand eines Dokuments oder den Zustand einer in Ausführung befindlichen Textsuche zu geben. Im folgenden Beispiel wird das Einhängen eines Handlers in den NotificationEventBus anhand des ErrorNotificationEvents gezeigt:
NotificationEventBus.Instance.get().addHandler(ErrorNotificationEvent.TYPE, new ErrorNotificationEventHandler() {
@Override
public void onErrorNotification(ErrorNotificationEvent event) {
...
}
});
-
InfoNotificationEvents liefern in der Regel rein informative Nachrichten wie z.B. dass das Laden eines Dokumentes gestartet oder abgeschlossen wird. Derzeit werden diese Events ausschließlich im Democode versendet.
-
ErrorNotificationEvents informieren darüber, dass ein Fehler aufgetreten ist z.B. beim Rendern von Annotationen. Im Regelfall wird hier noch eine aufgetretene Exception mitgeliefert. Im Gegensatz zu dem InfoNotificationEvent wird dieses Event an einigen Stellen außerhalb der Demos versendet.
-
Dieses Event informiert über den aktuellen Status der Textsuche (STARTED, CANCELED, FINISHED, FAILED, FAILED_TOO_MANY_RESULTS) und wird von den Klassen
RolloutSearch
und AdvancedSearch versendet. -
Dieses Event informiert über den aktuellen Fortschritt der Textsuche, also z.B. "3 von 6 Seiten durchsucht (50%)". Das Event wird genauso wie das SearchStatusChangedEvent von den Klassen
RolloutSearch
und AdvancedSearch versendet.
Nutzung eigener Events auf dem NotificationEventBus
Im folgenden Abschnitt wird anhand von Codebeispielen zum bestehenden ErrorNotificationEvent demonstriert, wie eigene Eventtypen eingeführt und genutzt werden können.
Event und EventHandler
Neben des ErrorNotificationEvents selbst wird zusätzlich ein dafür spezifischer EventHandler in Form eines Interfaces definiert. Event und EventHandler werden im selben Package abgelegt.
package com.jadice.web.util.notifications.client.events;
import com.google.gwt.event.shared.EventHandler;
public interface ErrorNotificationEventHandler extends EventHandler {
void onErrorNotification(ErrorNotificationEvent event);
}
Das ErrorNotificationEventHandler-Interface definiert die Methode onErrorNotification. Der zugehörige Eventconsumer implementiert dieses Interface und definiert dadurch das Verhalten beim Eingang eines ErrorNotificationEvents. Für jeden vorab am NotificationEventBus registrierten konkreten Consumer ruft der Bus beim Eingang eines ErrorNotificationEvents zur Laufzeit automatisch einmal dessen Methode onErrorNotification auf.
Die Implementierung des ErrorNotificationEvents sieht folgendermaßen aus:
package com.jadice.web.util.notifications.client.events;
import com.google.gwt.event.shared.GwtEvent;
public class ErrorNotificationEvent extends GwtEvent<ErrorNotificationEventHandler> {
private final String errorMessage;
private final Throwable throwable;
public static Type<ErrorNotificationEventHandler> TYPE = new Type<ErrorNotificationEventHandler>();
public ErrorNotificationEvent(String errorMessage, Throwable throwable) {
this.errorMessage = errorMessage;
this.throwable = throwable;
}
public Type<ErrorNotificationEventHandler> getAssociatedType() {
return TYPE;
}
protected void dispatch(ErrorNotificationEventHandler handler) {
handler.onErrorNotification(this);
}
public String getErrorMessage() {
return errorMessage;
}
public Throwable getStackTrace() {
return throwable;
}
}
Das ErrorNotificationEvent erweitert die GWT Basisklasse GwtEvent
;.
Jedes Event besitzt einen eindeutigen TYPE. Die Event Consumer
registrieren sich am Bus mit genau diesem TYPE und abonnieren dadurch
alle Events des jeweiligen Typs. Außerdem enthält das Event das Feld
errorMessage
, das die jeweilige Highlevel-Fehlermeldung speichert.
Zusätzlich kann das Event bei Bedarf ein gefangenes Throwable
mit
transportieren.
Consumer
Auf der Consumerseite wird das HandlerInterface ErrorNotificationEventHandler implementiert. In der Methode onErrorNotification wird dabei das Verhalten beim Eingang eines ErrorNotificationEvents definiert.
package com.levigo.jadice.web.demo.common.client.events;
import com.jadice.web.util.notifications.client.events.ErrorNotificationEvent;
import com.jadice.web.util.notifications.client.events.ErrorNotificationEventHandler;
...
public class PopupNotificationEventHandler implements InfoNotificationEventHandler, ErrorNotificationEventHandler {
private final DemoPopup popup;
private boolean active;
...
@Override public void onErrorNotification(ErrorNotificationEvent event) {
if (active) {
popup.addMessage(event.getErrorMessage(), true);
Throwable stackTrace = event.getStackTrace();
if (stackTrace != null) {
for (StackTraceElement element : stackTrace.getStackTrace()) {
popup.addMessage(element.toString(), true);
}
}
showForTimedDuration();
}
}
private void showForTimedDuration() {
popup.show();
popup.hideAndClearIn(displayDuration);
}
...
}
Über den Aufruf von event.getErrorMessage wird auf die Fehlermeldung des Events zugegriffen und diese anschließend dem Popup zur Anzeige übergeben. Falls das Event zusätzlich eine Throwable enthält wird dieser ebenfalls zur Anzeige gebracht.
In der Klasse ApplicationEntryPoint der Basicviewer Demo findet sich der Code zum Registrieren der Consumer am Bus. Für jeden unterschiedlichen Eventtyp, der abonniert werden soll, ist ein eigener Aufruf notwendig.
NotificationEventBus.Instance.get().addHandler(ErrorNotificationEvent.TYPE,
new PopupErrorNotificationEventHandler(popup));
Emitter
Ein neues Event kann wie im folgenden Beispiel aufgeführt über den NotificationEventBus versendet werden:
NotificationEventBus eventBus = NotificationEventBus.Instance.get();
...
eventBus.fireEvent(new
ErrorNotificationEvents(new ErrorNotificationEvents(message, error)));
Über die statische Methode NotificationEventBus.Instance.get wird eine Referenz auf den NotificationEventBus geholt und dann auf dem Busobjekt die Methode fireEvent zur Übergabe des Events an den Bus aufgerufen.
Nutzung eines eigenen Eventbusses
Für den Fall, dass eine eigene Implementierung des NotificationEventBus verwendet werden soll, kann das jadice web toolkit mit einem kundenspezifischen Eventbus konfiguriert werden. Über den Aufruf der Methode NotificationEventBus.Instance.set(NotificationEventBus bus) wird die Standardimplementierung transparent für die bereits beteiligten Kommunikationspartner ausgetauscht.