Mark Brouwer wrote:
As I said I believe there is room for multiple mechanisms that
complement each other so what I just did is I tried to add the
following method to a particular EventRegistration subclass part of the
future JSC spec and gave the semantics some thought and here I run into
a problem for which I need some help.
/**
* Returns the sequence number of the last remote event that has been
* ... by the event producer for this event registration.
*
* @return the sequence number ...
*
* @throws RemoteException
* @throws UnknownLeaseException if the lease associated with the event
* registration has expired or has been cancelled
*/
public abstract long getLastSequenceNumber()
throws RemoteException, UnknownLeaseException;
But what should the semantics be for this method. Often in the event
producer the generation of the events is decoupled from the actual
delivery.
One of the important things about event delivery design is to understand that
the delivery of these events into a remote system must be idempotent for there
to be any chance of a callback kind of delivery to be usable. This is one of
the reasons why I prefer to use streaming sockets from the server to the client.
If the socket closes from the clients perspective, it can coordinate a gathering
step with a reregistration to manage a non or semi-idempotent delivery system
better. Because it has a "moment in time" and a "moment in content" that
coincide, the client can better understand what needs to be recovered (if
anything) and how to integrate the new stream of events with the current data view.
I think the following encapsulates the things that I do in many cases, but have
never codified in this way.
public class Moment<T> {
private final T val;
public Moment( T val ) {
this.val = val;
}
public T get() {
return val;
}
}
public class IdempotentRemoteEvent<T> {
protected final Moment<T> moment;
public RemoteEvent( Moment<T> when ) {
moment = when;
}
public Moment<T> getMoment() {
return moment;
}
}
public interface RemoteListener<E> {
public void eventReceived( E event );
}
public interface RemoteEventManagment<E,T> {
public void addListener( RemoteListener<E> lis );
public void removeListener( RemoteListener<E> lis );
public E getEventAt( Moment<T> moment )
throws UnsupportedOperationException;
public E getEventsIn( List<Moment<T>> moments )
throws UnsupportedOperationException;
public Moment<T> getCurrent();
}
You might imagine there being Moment<Date> instances or Moment<Integer>
instances that are either time based or sequence based ordered events.
There would be events such as
public RegistrarEvent extends IdempotentRemoteEvent<Integer> {
public RegistrarEvent( int seq, ... ) {
super( seq );
}
public int getSequenceNo() {
return getMoment();
}
}
In managing eventing, ServiceRegistart would have
implements RemoteEventManagement<RegistrarEvent,Integer>
on its declaration, and then include the appropriate method definitions to allow
the recovery of any missing data.
This is not beyond about 10 minutes of thinking, so take it with a grain of
salt. But, this starts to illustrate what I've found to be important if missing
events is not acceptable.
In many cases, I have the client getting events on a socket stream that gets
reconnected on any failure. Then, in a completely unrelated thread of
execution, it periodically gets a new snapshot of the world. As Mark commented,
this is not always a perfect solution, because you do have to understand how the
latency involved in getting that snapshot interacts with the fact that your
snapshot may have been created before or after the next remote event received.
Gregg Wonderly