|
MatchSet Iterator - GSIteratorThe GSIterator is based on the net.jini.space.MatchSet interface and provides the ability to exhaustively read through all of the Entries from a JavaSpaces service that match one or more templates. There are scenarios where the conventional read operation that returns a single entry object does not fit and there is a need to return a collection of entries from the space. Generally, an iterator should be used because returning all the Entries in one result sent back to the call would consume too many resources in the client or introduce too much latency before the first Entry could be processed. The iterator constructs a match set (a collection of Entry instances) that incrementally returns the necessary Entries. The GSIterator constructs a proxy object that can be used to access a match set created by a space. The match set will initially contain some population of Entries specified by the operation that created it. These Entries can be retrieved by calling the next method. A successful call to next will remove the returned Entry from the match set. Match sets can end up in one of two terminal states: exhausted or invalidated. A match set becomes exhausted or invalidated specified by the operation that created it under the following conditions:
Between the time a match set is created and the time it reaches a terminal state, entries may be added by the space. However, an Entry that is removed by a next call must not be added back to a match set (though if there is a distinct but equivalent Entry in the space it may be added). The space may also remove Entries independent of next calls. The conditions under which Entries will be removed independent of next calls or added after the initial creation of the match set are specified by the operation that created the match set. An active lease on a match set serves as a hint to the space that the client is still interested in the match set, and as a hint to the client that the match set is still functioning, i.e., if a match set is leased and the lease is active, GSIterator will maintain the match set and will not invalidate it. If the iterator lease expires or is canceled, GSIterator will invalidate the match set. Clients should not assume that the resources associated with a leased match set will be freed automatically if the match set reaches the exhausted state, but should explicitly cancel the lease. The GSIterator defines the matched set using the following parameters:
API - Constructor SummaryGSIterator(com.j_spaces.core.IJSpace space, java.util.Collection collectionTemplates) GSIterator with default Iterator Buffer size (100 entries), without History property, and Lease.FOREVER as the iterator lease. GSIterator(com.j_spaces.core.IJSpace space, java.util.Collection collectionTemplates, int bufferSize, boolean withHistory, long lease) GSIterator Constructor
InitializationWhen a GSIterator is created, a match set is formulated. The match set initially contains all of the Entries in the space that match one or more of the collection templates and are not locked by conflicting transactions (unless withHistory was set to false, i.e., no initial contents). Each element of the matched set will be returned at most once. hasNext(), next() and next(timeout)Calling hasNext() returns true if next returns a non-null element rather than throwing an exception. Calling next removes one element from the matched set and returns it to the caller. Calling next(timeout) blocks next. The iteration is said to be complete if the match set becomes empty or next calls limit (buffer size) has removed Entries from the match set. A next call returns null only if the iteration is complete. take() and Entry Lease ExpirationAn Entry may be, but is not required to be, removed from the match set without being returned by a next call if it is removed from the space or is locked by a conflicting transaction. GSIterator does not remove the Entry after it has been buffered. NotificationsGSIterator uses the NotifyDelegator to register each of the templates in the templates collection. If a matching Entry was written to the space after the match set was created, the Entry will be added to the match set. An Entry that was locked under a conflicting transaction before or after the match set was created and the lock was released before the iteration was complete will also be added to the match set. A matching that arrived from a notification event will interrupt any blocking next(timeout) operation. If a take operation was called or an Entry lease timeout, the Entry will be removed from the next iteration matched set. Iterator LeaseIn most cases, the iterator will be leased and the lease proxy object can be obtained by calling the getLease() method. Cancelling or letting the lease expire will destroy the iterator; thus no notifications from here on will be accounted for, and all subsequent calls to hasNext() will return false. If there is a lease associated with the iterator, clients should not assume that completing the iteration will destroy it and should instead call cancel or let the lease expire when the end of the iteration is reached. A lease renewal(timeout) is used to renew a lease for an additional period of time. This duration is not added to the original lease, but is used to determine a new expiration time for the existing lease. If a lease has expired or has been canceled, a renewal is not allowed. TransactionsIterating through the matched set does not lock the Entry. Entries that are under transaction and match the specified template will not be included as part of the matched set. snapshotThe snapshot method returns a snapshot of the Entry returned by the last next call (see section JS.2.6 of the JavaSpaces specification). If the last next call returned null or failed with an exception or error, the snapshot will throw an IllegalStateException. It is important to note that the GSIterator.snapshot(), unlike the JavaSpace.snapshot(), does not throw a RemoteException.
Example
The following code example invokes two threads – a writer and a reader thread. The writer thread writes Entries to the space. The reader thread uses an iterator that iterates through all the matching Entries that match the specified templates in the pre-defined collection. If no matches are found, the reader uses the next with blocking option that waits until either a new match is found or a timeout occurs. When a matched Entry is found, the iterator lease is renewed. When the next is timed out, the iterator is canceled. package com.j_spaces.examples.iterator; import java.util.ArrayList; import java.util.Collection; import net.jini.core.entry.Entry; import net.jini.core.lease.Lease; import com.j_spaces.core.IJSpace; import com.j_spaces.core.client.FinderException; import com.j_spaces.core.client.GSIterator; import com.j_spaces.core.client.SpaceFinder; public class Iterator { public static class SpaceWriter implements Runnable { public final static int NUM_OF_MESSAGES = 5; public final static int WRITE_CYCLES = 3; public final static long SLEEP_TIME = 5000; //5 sec IJSpace space; public SpaceWriter(IJSpace space) { this.space = space; } /** * Write entries to the space, one even one odd. * Sleep after each cycle of N messages. */ public void run() { int id = 0; try { //break loop after 5 cycles for (int i=0; i < WRITE_CYCLES; i++) { System.out.println("[ Writer ]tWriter Thread writing " + NUM_OF_MESSAGES + " messages"); for (int j=0; j < NUM_OF_MESSAGES; j++){ id = i*NUM_OF_MESSAGES +j; if (id%2==0) space.write( new Even(id), null, Lease.FOREVER ); else space.write( new Odd(id), null, Lease.FOREVER ); } System.out.println("[ Writer ]tWriter Thread sleeping for " + SLEEP_TIME/1000 + " seconds"); Thread.sleep(SLEEP_TIME); } System.out.println("[ Writer ]tWriter Thread finished " + WRITE_CYCLES + " write cycles of " + NUM_OF_MESSAGES + " messages. " + "n[ Writer ]tDone."); } catch(Exception e) { e.printStackTrace(); } } } public static class SpaceIterator implements Runnable { public final static long BLOCK_TIME = 5000; // 5 sec public final static long LEASE_TIME = 10000;// 10 sec IJSpace space; GSIterator gsIterator; public SpaceIterator(IJSpace space) { this.space = space; /* * build a collection of 2 templates: * An odd numbered template and * an even numbered template. * * You could also pass a collection with a null template. */ Collection col = new ArrayList(); col.add(new Even()); col.add(new Odd()); try { /* * create an iterator of the space with the * specified collection. * * The buffer window is the limit of entries * to store for each iteration. * * The with history flag indicates that this * iterator will be pre-filled with matching entries; * otherwise it will start iterating only on newly * arriving entries at the space. * * The lease is the life time of this iterator */ System.out.println( "[ Iterator ]tCreating Iterator over space for Collection:"); gsIterator = new GSIterator(space, // space ref. col, // templates collection 10, // buffer "window" size true, // with history true LEASE_TIME);// lease 10 seconds } catch (Exception e) { e.printStackTrace(); } } /** * Iterate through all the entries matching the specified templates * in the pre-defined collection. * * If no matches are found, try blocking the iterator until either * a new match is found or a timeout occurs. * - When found, renew the iterator lease. * - When timed out, cancel the iterator. */ public void run() { try { System.out.println("[ Iterator ]tIterator Thread reading messages"); while (true) { //lets iterate until we don't have any more matches while (gsIterator.hasNext()) { Entry entry = (Entry)gsIterator.next(); printEntry(entry); } System.out.println("[ Iterator ]tNo more entries to iterate. Blocking next for " + BLOCK_TIME/1000 + " seconds"); //lets block the iterator until a new match is found Entry entry = (Entry)gsIterator.next(BLOCK_TIME); if (entry != null){ System.out.println("[ Iterator ]tIterator un-blocked with a match"); printEntry(entry); //lets renew the iterator and keep iterating. gsIterator.renew(LEASE_TIME); } else { System.out.println("[ Iterator ]tIterate un-blocked without a match"); break; } /* NOTE * An equivalent implementation is a blocking loop: * -------------------------------------------------- * while (gsIterator.hasNext()) * { * Entry entry = (Entry)gsIterator.next(BLOCK_TIME); * printEntry(entry); * } */ } //An iteration was un-blocked without a match, //lets cancel the iterator gsIterator.cancel(); System.out.println("[ Iterator ]tIterator canceled."); } catch(Exception e) { e.printStackTrace(); } } /** * print the entry returned by the iteration * @param entry An entry matching a template from the collection */ public void printEntry(Entry entry) { if (entry instanceof Even) System.out.println("[ Iterator ]tIterator Thread read: " + entry + " - "+ ((Even)entry).tmplId); else System.out.println("[ Iterator ]tIterator Thread read: " + entry + " - "+ ((Odd)entry).tmplId); } } public static void main(String[] args) { if ( args.length != 1 ) { System.out.println("Usage: <URL>"); System.out.println("jini://lookup host/container name/JavaSpaces"); System.exit(1); } try { IJSpace space = (IJSpace)SpaceFinder.find( args[0] ); if ( space == null ) { System.out.println("t> Space not found: " + args[0]); System.exit(-1); } System.out.println("t> Space will now be set to run in FIFO mode."); space.setFifo(true); /** * Lets create one iterator thread and one * write thread. * * @see SpaceIterator#run() * @see SpaceWriter#run() */ Thread[] threads = new Thread[2]; threads[0] = new Thread(new Iterator.SpaceIterator(space)); threads[Look And Feel - ServiceGrid] = new Thread(new Iterator.SpaceWriter(space)); for (int i=0; i< threads.length; i++) threads[i].start(); for (int i=0; i< threads.length; i++) threads[i].join(); System.exit(0); } catch( FinderException ex ) { ex.printStackTrace(); System.out.println("t> Could not find space: " + args[0]); System.out.println("t> Please check that GigaSpaces Server is running."); } catch (Exception e){} } } |
(works on Firefox 2 and Internet Explorer 7)