to.etc.webapp.eventmanager
Class VpEventManager

java.lang.Object
  extended by to.etc.webapp.eventmanager.VpEventManager
All Implemented Interfaces:
java.lang.Runnable

public class VpEventManager
extends java.lang.Object
implements java.lang.Runnable

Overview

This is an implementation of an event manager. It will handle two forms of events:

Usage

De event manager wordt gebruikt om gebeurtenissen die plaatsvinden ergens in ViewPoint te melden aan geinteresseerde stukken code. Neem als voorbeeld de koppeling met Casper of met MOCA. Casper en MOCA willen alle wijzigingen die een ViewPoint gebruiker maakt op een werkbon zo snel mogelijk weten. Een "flut"methode om dit voor elkaar te krijgen is de methode die nu voor de Casper koppeling gebruikt wordt: laat om de paar seconden iets in de database "pollen" om te zien of er iets gewijzigd is. Dit is erg duur en niet herbruikbaar: voor ieder pakket wat dezelfde informatie nodig heeft moet opnieuw zo'n stuk geschreven worden wat pollt.

Het Event mechanisme gaat als alternatief hiervoor dienen, en wordt later verder uitgebouwd om SOA events mogelijk te maken vanuit ViewPoint. Met dit event mechanisme is de koppeling naar Casper op een veel eenvoudiger manier mogelijk, als volgt:

Dit mechanisme ontkoppelt code door gebeurtenissen te scheiden van het afhandelen van de gevolgen voor de verschillende koppelingen. Daarnaast is de koppeling goedkoop (geen polling*), herbruikbaar en interactief.

Events versus Notifications

Hierbij worden events gebruikt voor code wat altijd uitgevoerd moet worden en alleen op de server waarop het event optreed. Notifications dienen om VP servers onderling te synchroniseren, en notifications worden niet gegarandeerd afgeleverd rond server startup/shutdown.

Implementatie

De huidige implementatie is suboptimaal omdat nog steeds polling gebruikt wordt, maar ze is later in geval van problemen te vervangen mits alleen de publieke interface in deze class gebruikt wordt. Hoewel er gepolled wordt is de overhead tamelijk miniem omdat slechts een enkele polling gebruikt wordt voor alle notifications.

De huidige implementatie creeert een Oracle table RED_VP_EVENTS in de database. Deze table ziet er uit als:

      UPID        NUMERIC(20, 0) not null primary key,
      UTIME       DATE not null,
      EVNAME      VARCHAR(80) not null,
      SERVER      VARCHAR(80) not null,
      OBJ         BLOB
 
De UPID PK is monotoon stijgend gevuld vanuit de sequence RED_VP_EVENTS_SQ. Zowel tabel als sequence worden door de code zelf gecreeerd. Ieder event wat wordt gegenereerd door de code wordt als enkel record in deze tabel opgenomen.

Iedere ViewPoint applicatie-server in het cluster heeft een eigen instance van de EventManager singleton. Tijdens initialisatie start deze instance een thread die iedere 4 seconden een select doet op deze tabel: select ... from RED_VP_EVENTS where upid > :last:. Door telkens de laatst ingelezen UPID te onthouden is dit een erg goedkope query die via de index alleen de voor de server nieuwe events leest. Doordat alle servers dezelfde tabel lezen worden events die op server A gegenereerd worden ook door alle andere servers gezien, zodat alle servers de bijbehorende event listeners aanroepen.

Het aanroepen van de event listeners is normaliter asynchroon: de event polling thread roept ze pas aan wanneer de events in de database gezien worden. Dit geldt zelfs voor de server die de event genereert. Er bestaat echter wel een call waarmee de postende server synchroon de event handlers aanroept, dus wanneer de postEventSynchronous() call terugkeert dan zijn alle handlers aangeroepen geweest. Dit synchrone gedrag geldt echter allen voor de postende server; de andere servers voeren de handlers pas uit wanneer de polling thread het event in de database tegenkomt.

De code controleert dat een event slechts eenmaal uitgevoerd wordt door de UPID van het laatst geziene event steeds te bewaren. Dit voorkomt tevens dat de event tabel steeds in het geheel doorgelezen moet worden om nieuwe events te vinden.

Author:
Frits Jalvingh Created on Sep 12, 2006

Method Summary
 void addListener(java.lang.Class<?> cl, ListenerType lt, AppEventListener<?> listener)
           
 void addWeakListener(java.lang.Class<?> cl, ListenerType lt, AppEventListener<?> listener)
           
static VpEventManager getInstance()
          Get the instance.
static void initialize(javax.sql.DataSource ds, java.lang.String tableName)
           
 void postDelayedEvent(java.sql.Connection dbc, AppEventBase ae)
          Post an event asynchronously.
 void postDelayedEvent(java.sql.Connection dbc, java.util.List<AppEventBase> ae)
          Post a list of events asynchronously.
 void postEvent(java.sql.Connection dbc, AppEventBase ae)
          Post an event synchronously.
 void postEvent(java.sql.Connection dbc, java.util.List<? extends AppEventBase> aelist)
          Post all of the events in the list synchronously.
 void removeListener(java.lang.Class<?> cl, AppEventListener<?> listener)
          Remove a weak or normal listener from a map.
 void removeWeakListener(java.lang.Class<?> cl, AppEventListener<?> listener)
           
 void run()
          Thread entry.
 void start()
          Must be called after init to actually start handling events.
 void stop()
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

getInstance

public static VpEventManager getInstance()
Get the instance. If the instance has not yet initialized this waits until init has commenced. If init does not complete in 60 secs this aborts.

Returns:

initialize

public static void initialize(javax.sql.DataSource ds,
                              java.lang.String tableName)
                       throws java.lang.Exception
Throws:
java.lang.Exception

stop

public void stop()

start

public void start()
Must be called after init to actually start handling events.


run

public void run()
Thread entry.

Specified by:
run in interface java.lang.Runnable
See Also:
Runnable.run()

removeListener

public void removeListener(java.lang.Class<?> cl,
                           AppEventListener<?> listener)
Remove a weak or normal listener from a map.

Parameters:
cl -
listener -

addListener

public void addListener(java.lang.Class<?> cl,
                        ListenerType lt,
                        AppEventListener<?> listener)

addWeakListener

public void addWeakListener(java.lang.Class<?> cl,
                            ListenerType lt,
                            AppEventListener<?> listener)

removeWeakListener

public void removeWeakListener(java.lang.Class<?> cl,
                               AppEventListener<?> listener)

postEvent

public void postEvent(java.sql.Connection dbc,
                      AppEventBase ae)
               throws java.lang.Exception
Post an event synchronously. This posts the event, commits it (commiting the transaction on the connection!!) and calls all local handlers immediately. You may only call this version if all data pertaining to the event has been commited to the database or will be commited as a result of this call! If you do not the event may fire in other servers/listeners with stale data in the database; this will cause wrong results.

Parameters:
dbc -
ae -
Throws:
java.lang.Exception

postDelayedEvent

public void postDelayedEvent(java.sql.Connection dbc,
                             AppEventBase ae)
                      throws java.lang.Exception
Post an event asynchronously. The event gets added to the database but not commited, and no local listeners get called at this time. When the event gets commited the scanner will see it and call the local handlers. This call is typically done when an event needs to be commited lazily.

Parameters:
dbc -
ae -
Throws:
java.lang.Exception

postDelayedEvent

public void postDelayedEvent(java.sql.Connection dbc,
                             java.util.List<AppEventBase> ae)
                      throws java.lang.Exception
Post a list of events asynchronously. The event gets added to the database but not commited, and no local listeners get called at this time. When the event gets commited the scanner will see it and call the local handlers. This call is typically done when an event needs to be commited lazily.

Parameters:
dbc -
ae -
Throws:
java.lang.Exception

postEvent

public void postEvent(java.sql.Connection dbc,
                      java.util.List<? extends AppEventBase> aelist)
               throws java.lang.Exception
Post all of the events in the list synchronously. This posts the events, commits them, then calls all local handlers immediately. You may only call this version if all data pertaining to the events have been commited to the database or will be commited as a result of this call! If you do not the events may fire in other servers/listeners with stale data in the database; this will cause wrong results.

Parameters:
dbc -
aelist -
Throws:
java.lang.Exception