Motivation:

We need feature events to draw changes as they occur, and keep caches in sync.

Contact:

~jeichar

Tracker:

http://jira.codehaus.org/browse/GEOT-1607

Tagline:

What changed where

This page represents the current plan; for discussion please check the tracker link above.

Problem

FeatureEvents have long been something missed in the GeoTools library; the uDig project has hacked a few DataStores into submission way back in GeoTools 2.2. Now that we have updated to GeoTools trunk it is time to talk about what we have learned and see what can be cleaned up. Conversely FeatureEvents have not been implemented by the various DataStore implementations; in part that is probably due to a lack of clarity and available test cases; we need to take implementation experience into account and provide an event notification system that can be implemented.

There are several "feature event" notification things around:

There are two other input reference points around the functionality of the "commit" step:

Distractions, Observations and Complaints:

Solution

Status

The window for feedback on this proposal has closed, the proposal has been accepted by default.

Initial discussion as part of an IRC meeting was mostly in agreement. Justin made the interesting observation that only one of the changes is significant to client code:

  • Change from FeatureWriter.next() notification to FeatureWriter.close()
  • Gabriel asked that the Set used for batch notification of be a weak hash set

Voting is open:

Community support:

{dynamictasklist:Impediments}{dynamictasklist}

Tasks

This section is used to make sure your proposal is complete (did you remember documentation?) and has enough paid or volunteer time lined up to be a success

 

no progress

(tick)

done

(error)

impeded

(warning)

lack mandate/funds/time

(question)

volunteer needed

  1. (tick) Deprecate CollectionListener / CollectionEvent - asking people to listen to the FeatureSource instead
  2. (tick) Introduce RefernecedEnvelope.EVERYTHING
  3. (tick) Add Filter to FeatureEvent
  4. (tick) Add Filter and Bounds "building" methods to FeatureEvent( update: Implemented as BatchFeatureEvent)
  5. (tick) Update the DataStore Tutorial section on FeatureEvents based on the ArcSDE experience
  6. (warning) Provide an example of using a listening with FeatureEvents in the user guide

API Changes

BEFORE

describe subject is being changed

class ReferencedEnvelope {
    ....
}
class FeatureEvent {
    ....
}

AFTER

Deprecate code that is orphaned (maybe done in GeoTools 2.4?):

interface FeatureCollection {
    /**
     * @deprecated Please use FeatureSource.addFeatureListener
     */
    void addListener(CollectionListener listener) throws NullPointerException;

    /**
     * @deprecated Please use FeatureSource.removeFeatureListener
     */
    void removeListener(CollectionListener listener) throws NullPointerException;
}

Expand ReferencedEnvelope to include the definition of everything:

class ReferencedEnvelope {
     static final RefernecedEnvelope EVERYTHING = new RefernecedEnvelope(){
          ... over ride methods as needed this is an infinite envelope of any CRS ...
     }
}

Add helper methods to FeatureEvent so DataStore implementors do not have to work so hard to build up a good change notification:

interface FeatureEvent {     
     int FEATURES_ADDED = 1;
     int ADDED = 1;
     
     int FEATURES_CHANGED = 0;
     int CHANGED = 0;
     
     int FEATURES_REMOVED = -1;
     int REMOVED = -1;

     public Filter getFilter();
     public ReferencedEnvelope getBounds();
}

class FeatureEventImpl {     

     private ReferencedEnvelope bounds;

     /** Filter here is the new part; the point of this proposal */
     private Filter filter;

     public Filter getFilter();

     /** Allow event to grab the bounds and Fid *before* anything is modified or removed */
     public void before( SimpleFeature feature );

     /** Indicate the provided feature was modified */
     public void modify( SimpleFeature feature );

     /** Indicate the provided feature was added */
     public void add( SimpleFeature feature );

     /** Indicate the provided feature was removed */
     public void remove( SimpleFeature feature );
}

Subclass to provide batch notification on Commit / Rollback. This is used by FeatureListenerManager to report a bit more detail on transaction commit() and rollback(). Right now these changes show up as a change event with no known bounds.

class BatchFeatureEvent {     
     public static in COMMIT = 5;
     public static in ROLLBACK = 6;

     /**
      * This is a weak hash set as we don't need to track
      * changes on indentifiers that are not being used
      * by the client to track selection.
      */
     WeakHashSet<FeatureId> fids;

     /** Will often return Filter.INCLUDE */
     public Filter getFilter();

     /** Will often return everything */
     public RefernecedEnvelope getBounds();

     /**
      * Indicate the provided feature was added.
      * Will be use to update internal state of bounds and
      * fids.
      */
     public void add( FetaureEvent change );

     /**
      * Used to update any FeatureId during a commit.
      */
     public void replaceFid( String tempFid, String actualFid );

     public void WeakHashSet<FetaureId> getCreatedFeatureIds();
}

With the following changes to javadocs documenting expected behaviour:

interface FeatureWriter {
   /**
    * Advance to the next feature - notice no event fired 
    */
   SimpleFeature next();
   /**
    * Modify (or add) the current feature - notice no event fired
    */
   void write();
   /**
    * We are finished with this FeatureWrtier; event fired summarizing the changes made.
    */
   void close();
}
interface FeatureStore extends FeatureSource {
     /**
      * Will issue a single FeatureEvent.
      */
     Set<String> addFeatures(FeatureCollection collection) throws IOException;  
    /**
     * Will issue a single FeatureEvent.
     */
    void removeFeatures(Filter filter) throws IOException;
    /**
     * Will issue a single FeatureEvent.
     */
    void modifyFeatures(AttributeDescriptor type, Object value, Filter filter)
        throws IOException;
    ...
}
class FeatureListenerManager {
     class BatchListener implements FeatureListener, Transaction.State {
          private BatchFeatureEvent batch;
          void setTransaction(Transaction transaction){
                batch = transaction == null ? null : new BatchFeatureEvent( transaction );
          }
          void changed(FeatureEvent featureEvent){
               if( batch != null ) batch.add( featureEvent );
          }
          /** Fire batch event to Transaction.AUTO_COMMIT */
          public synchronized void commit() throws IOException{
          }
          /** Fire batch event to Transaction.AUTO_COMMIT */
          public synchronized void rollback() throws IOException{
          }
     }
     ...
}

We also need to allow our FeatureIdImpl to be "updated" during the commit:

public class FeatureIdImpl implements FeatureId {
	String fid;
	String origionalFid;
        ...
	public void setID( String id ){
		if( origionalFid == null ){
			origionalFid = fid;
			fid = id;			
		}
		else {
			throw new IllegalStateException("You can only assign a real id once during a commit");
		}
	}
        ...
}
 

Documentation Changes

There has been little documentation thus far around the behaviour of events during editing; so in that sense our work is easy.